mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-22 16:35:20 +08:00
fix: bot routing setup and busy-session response checks (#2728)
Apply group response rules before busy-session steering. Initialize usable private-message and group-mention routes for new bots, including Wizard-created pipelines, and preserve member-configured rules across saves and retries. Fix empty pipeline configuration, refresh the processor sidebar after automatic creation, and show creation feedback. Align routing regressions and use Node 24 for the browser CI job. Correct the task's erroneous agent attribution while preserving every code tree and the original human contribution. Co-authored-by: multica-agent <[email protected]>
This commit is contained in:
co-authored by
multica-agent
parent
f8e32ef14f
commit
d13ae56ea5
@@ -23,6 +23,7 @@ import { UUID } from 'uuidjs';
|
||||
import DynamicFormComponent from '@/app/home/components/dynamic-form/DynamicFormComponent';
|
||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||
import { systemInfo } from '@/app/infra/http';
|
||||
import { useSidebarData } from '@/app/home/components/home-sidebar/SidebarDataContext';
|
||||
import { Agent, Bot } from '@/app/infra/entities/api';
|
||||
import { getAdapterDocUrl } from '@/app/infra/entities/adapter-docs';
|
||||
import {
|
||||
@@ -220,6 +221,7 @@ const BotForm = forwardRef<BotFormHandle, BotFormProps>(function BotForm(
|
||||
ref,
|
||||
) {
|
||||
const { t } = useTranslation();
|
||||
const { refreshPipelines } = useSidebarData();
|
||||
const formSchema = getFormSchema(t);
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
@@ -668,8 +670,30 @@ const BotForm = forwardRef<BotFormHandle, BotFormProps>(function BotForm(
|
||||
};
|
||||
|
||||
try {
|
||||
// A failed metadata refresh must not prevent creating the bot.
|
||||
const before = await httpClient.getPipelines().catch(() => null);
|
||||
const res = await httpClient.createBot(newBot);
|
||||
toast.success(t('bots.createSuccess'));
|
||||
const [created] = await Promise.all([
|
||||
httpClient.getBot(res.uuid).catch(() => null),
|
||||
refreshPipelines(),
|
||||
]);
|
||||
const automaticallyCreatedPipeline =
|
||||
before !== null &&
|
||||
created?.bot.event_bindings?.some(
|
||||
(binding) =>
|
||||
binding.event_pattern === 'message.received' &&
|
||||
binding.target_type === 'pipeline' &&
|
||||
!before.pipelines.some(
|
||||
(pipeline) => pipeline.uuid === binding.target_uuid,
|
||||
),
|
||||
);
|
||||
toast.success(
|
||||
t(
|
||||
automaticallyCreatedPipeline
|
||||
? 'bots.autoCreatedPipeline'
|
||||
: 'bots.createSuccess',
|
||||
),
|
||||
);
|
||||
onNewBotCreated(res.uuid);
|
||||
} catch (error) {
|
||||
const err = error as CustomApiError & {
|
||||
|
||||
@@ -12,6 +12,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { cn } from '@/lib/utils';
|
||||
import styles from './EventBindingsEditor.module.css';
|
||||
import { appendMessageRoutingTemplate } from './message-routing-template';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { UseFormReturn } from 'react-hook-form';
|
||||
import {
|
||||
@@ -144,17 +145,22 @@ interface EventBindingsEditorProps {
|
||||
}
|
||||
|
||||
type FilterField =
|
||||
'chat_type' | 'chat_id' | 'message_text' | 'message_element_types';
|
||||
| 'chat_type'
|
||||
| 'chat_id'
|
||||
| 'message_text'
|
||||
| 'message_element_types'
|
||||
| 'is_at_bot';
|
||||
type FilterOperator =
|
||||
'eq' | 'neq' | 'contains' | 'not_contains' | 'starts_with' | 'regex';
|
||||
|
||||
interface FilterRow {
|
||||
field: FilterField;
|
||||
operator: FilterOperator;
|
||||
value: string;
|
||||
value: string | boolean;
|
||||
}
|
||||
|
||||
const OPERATORS_BY_FIELD: Record<FilterField, FilterOperator[]> = {
|
||||
is_at_bot: ['eq', 'neq'],
|
||||
chat_type: ['eq', 'neq'],
|
||||
chat_id: ['eq', 'neq', 'contains', 'not_contains', 'regex'],
|
||||
message_text: [
|
||||
@@ -443,7 +449,7 @@ function samplePayloadForEvent(eventType: string): Record<string, unknown> {
|
||||
if (eventType === 'message.received') {
|
||||
return {
|
||||
message_text: 'Hello',
|
||||
chat_type: 'private',
|
||||
chat_type: 'person',
|
||||
chat_id: 'test-user',
|
||||
user_id: 'test-user',
|
||||
};
|
||||
@@ -727,7 +733,7 @@ function FilterConditionsPanel({
|
||||
next[i] = { ...next[i], ...patch };
|
||||
if ('field' in patch) {
|
||||
next[i].operator = OPERATORS_BY_FIELD[patch.field as FilterField][0];
|
||||
next[i].value = '';
|
||||
next[i].value = patch.field === 'is_at_bot' ? true : '';
|
||||
}
|
||||
onChange(next);
|
||||
}
|
||||
@@ -755,6 +761,9 @@ function FilterConditionsPanel({
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="is_at_bot">
|
||||
{t('bots.filterIsAtBot')}
|
||||
</SelectItem>
|
||||
<SelectItem value="chat_type">
|
||||
{t('bots.filterChatType')}
|
||||
</SelectItem>
|
||||
@@ -784,9 +793,22 @@ function FilterConditionsPanel({
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
{f.field === 'chat_type' ? (
|
||||
{f.field === 'is_at_bot' ? (
|
||||
<Select
|
||||
value={f.value}
|
||||
value={String(f.value)}
|
||||
onValueChange={(v) => update(i, { value: v === 'true' })}
|
||||
>
|
||||
<SelectTrigger className="h-8 w-[90px] text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="true">{t('bots.mentionYes')}</SelectItem>
|
||||
<SelectItem value="false">{t('bots.mentionNo')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
) : f.field === 'chat_type' ? (
|
||||
<Select
|
||||
value={String(f.value)}
|
||||
onValueChange={(v) => update(i, { value: v })}
|
||||
>
|
||||
<SelectTrigger className="h-8 w-[90px] text-xs">
|
||||
@@ -803,7 +825,7 @@ function FilterConditionsPanel({
|
||||
</Select>
|
||||
) : f.field === 'message_element_types' ? (
|
||||
<Select
|
||||
value={f.value}
|
||||
value={String(f.value)}
|
||||
onValueChange={(v) => update(i, { value: v })}
|
||||
>
|
||||
<SelectTrigger className="h-8 w-[100px] text-xs">
|
||||
@@ -825,7 +847,7 @@ function FilterConditionsPanel({
|
||||
? t('bots.ruleValueLauncherIdPlaceholder')
|
||||
: t('bots.ruleValueMessagePlaceholder')
|
||||
}
|
||||
value={f.value}
|
||||
value={String(f.value)}
|
||||
onChange={(e) => update(i, { value: e.target.value })}
|
||||
/>
|
||||
)}
|
||||
@@ -1600,6 +1622,18 @@ export default function EventBindingsEditor({
|
||||
}
|
||||
|
||||
function addBinding(eventPattern = 'message.received') {
|
||||
if (eventPattern === 'message.received') {
|
||||
const target =
|
||||
agentOptions.find((agent) => agent.kind === 'pipeline') ??
|
||||
agentOptions.find((agent) => agent.kind === 'agent');
|
||||
updateBindings(
|
||||
appendMessageRoutingTemplate(bindings, {
|
||||
target_type: target?.kind === 'pipeline' ? 'pipeline' : 'agent',
|
||||
target_uuid: target?.uuid ?? '',
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
updateBindings([
|
||||
...bindings,
|
||||
{
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { EventBinding } from '@/app/infra/entities/api';
|
||||
|
||||
/** Add explicit private/group routes, preserving all existing user routes. */
|
||||
export function appendMessageRoutingTemplate(
|
||||
bindings: EventBinding[],
|
||||
target: Pick<EventBinding, 'target_type' | 'target_uuid'>,
|
||||
): EventBinding[] {
|
||||
const routes: EventBinding[] = [
|
||||
{
|
||||
event_pattern: 'message.received',
|
||||
...target,
|
||||
priority: 0,
|
||||
enabled: true,
|
||||
filters: [{ field: 'chat_type', operator: 'eq', value: 'person' }],
|
||||
},
|
||||
{
|
||||
event_pattern: 'message.received',
|
||||
...target,
|
||||
priority: 0,
|
||||
enabled: true,
|
||||
filters: [
|
||||
{ field: 'chat_type', operator: 'eq', value: 'group' },
|
||||
{ field: 'is_at_bot', operator: 'eq', value: true },
|
||||
],
|
||||
},
|
||||
];
|
||||
const sameFilters = (a: EventBinding, b: EventBinding) => {
|
||||
const left = a.filters ?? [];
|
||||
const right = b.filters ?? [];
|
||||
return (
|
||||
left.length === right.length &&
|
||||
left.every((filter) =>
|
||||
right.some(
|
||||
(other) =>
|
||||
filter.field === other.field &&
|
||||
filter.operator === other.operator &&
|
||||
filter.value === other.value,
|
||||
),
|
||||
)
|
||||
);
|
||||
};
|
||||
return [
|
||||
...bindings,
|
||||
...routes.filter(
|
||||
(route) =>
|
||||
!bindings.some(
|
||||
(binding) =>
|
||||
binding.event_pattern === route.event_pattern &&
|
||||
sameFilters(binding, route),
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import type { Bot } from '@/app/infra/entities/api';
|
||||
import { appendMessageRoutingTemplate } from '@/app/home/bots/components/bot-form/message-routing-template';
|
||||
|
||||
export interface WizardDraft {
|
||||
adapter: string;
|
||||
botUuid: string | null;
|
||||
pipelineUuid: string | null;
|
||||
}
|
||||
|
||||
interface SetupClient {
|
||||
getBot(uuid: string): Promise<{ bot: Bot }>;
|
||||
createBot(bot: Bot): Promise<{ uuid: string }>;
|
||||
createPipeline(pipeline: {
|
||||
name: string;
|
||||
description: string;
|
||||
config: Record<string, unknown>;
|
||||
}): Promise<{ uuid: string }>;
|
||||
}
|
||||
|
||||
/** Keep successful resources in the draft so a retry never recreates them. */
|
||||
export async function prepareWizardBot(
|
||||
client: SetupClient,
|
||||
draft: WizardDraft,
|
||||
bot: Bot,
|
||||
pipelineName: string,
|
||||
) {
|
||||
if (draft.botUuid) {
|
||||
return {
|
||||
uuid: draft.botUuid,
|
||||
bot: (await client.getBot(draft.botUuid)).bot,
|
||||
};
|
||||
}
|
||||
if (!draft.pipelineUuid) {
|
||||
draft.pipelineUuid = (
|
||||
await client.createPipeline({
|
||||
name: pipelineName,
|
||||
description: bot.description ?? '',
|
||||
config: {},
|
||||
})
|
||||
).uuid;
|
||||
}
|
||||
const configuredBot: Bot = {
|
||||
...bot,
|
||||
event_bindings: appendMessageRoutingTemplate([], {
|
||||
target_type: 'pipeline',
|
||||
target_uuid: draft.pipelineUuid,
|
||||
}),
|
||||
};
|
||||
const { uuid } = await client.createBot(configuredBot);
|
||||
draft.botUuid = uuid;
|
||||
return { uuid, bot: configuredBot };
|
||||
}
|
||||
|
||||
/** Saving adapter settings must not replace or re-enable a member's routes. */
|
||||
export function wizardBindingsIfMissing(bot: Bot, pipelineUuid: string) {
|
||||
return bot.event_bindings?.length
|
||||
? {}
|
||||
: {
|
||||
event_bindings: appendMessageRoutingTemplate([], {
|
||||
target_type: 'pipeline',
|
||||
target_uuid: pipelineUuid,
|
||||
}),
|
||||
};
|
||||
}
|
||||
+48
-19
@@ -81,6 +81,11 @@ import {
|
||||
import OwnModelSetup, {
|
||||
type OwnModelSelection,
|
||||
} from '@/app/wizard/components/OwnModelSetup';
|
||||
import {
|
||||
prepareWizardBot,
|
||||
wizardBindingsIfMissing,
|
||||
type WizardDraft,
|
||||
} from '@/app/wizard/bot-setup';
|
||||
|
||||
type RunnerInstallProgress = { stage: InstallStage; percent: number };
|
||||
|
||||
@@ -153,6 +158,8 @@ export default function WizardPage() {
|
||||
);
|
||||
const [runnerConfig, setRunnerConfig] = useState<Record<string, unknown>>({});
|
||||
const [createdBotUuid, setCreatedBotUuid] = useState<string | null>(null);
|
||||
const botDraft = useRef<WizardDraft | null>(null);
|
||||
const botOperationPending = useRef(false);
|
||||
const [createdPipelineUuid, setCreatedPipelineUuid] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
@@ -699,10 +706,20 @@ export default function WizardPage() {
|
||||
// Creates a disabled bot using the adapter label as name.
|
||||
|
||||
const handleCreateBot = useCallback(async () => {
|
||||
if (!selectedAdapter) return;
|
||||
if (!selectedAdapter || botOperationPending.current) return;
|
||||
botOperationPending.current = true;
|
||||
setIsCreatingBot(true);
|
||||
|
||||
try {
|
||||
if (createdBotUuid) {
|
||||
const existing = (await httpClient.getBot(createdBotUuid)).bot;
|
||||
if (existing.adapter === selectedAdapter) {
|
||||
// Returning to the platform step must retain unsaved form values too.
|
||||
setCurrentStep(1);
|
||||
saveProgress({ step: 1 });
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Use adapter label as default bot name
|
||||
const adapter = adapters.find((a) => a.name === selectedAdapter);
|
||||
const defaultName = adapter
|
||||
@@ -726,9 +743,25 @@ export default function WizardPage() {
|
||||
adapter_config: initialConfig,
|
||||
enable: false,
|
||||
};
|
||||
const resp = await httpClient.createBot(bot);
|
||||
if (!botDraft.current || botDraft.current.adapter !== selectedAdapter) {
|
||||
botDraft.current = {
|
||||
adapter: selectedAdapter,
|
||||
botUuid: null,
|
||||
pipelineUuid: null,
|
||||
};
|
||||
}
|
||||
const resp = await prepareWizardBot(
|
||||
httpClient,
|
||||
botDraft.current,
|
||||
bot,
|
||||
`${defaultName} ${t('agents.pipelineType')}`,
|
||||
);
|
||||
setBotName(resp.bot.name);
|
||||
setAdapterConfig(
|
||||
(resp.bot.adapter_config ?? initialConfig) as Record<string, unknown>,
|
||||
);
|
||||
setCreatedBotUuid(resp.uuid);
|
||||
setCreatedPipelineUuid(null);
|
||||
setCreatedPipelineUuid(botDraft.current.pipelineUuid);
|
||||
|
||||
// Fetch runtime info to get webhook URL(s)
|
||||
try {
|
||||
@@ -752,7 +785,7 @@ export default function WizardPage() {
|
||||
selected_scenario: null,
|
||||
selected_adapter: selectedAdapter,
|
||||
created_bot_uuid: resp.uuid,
|
||||
created_pipeline_uuid: null,
|
||||
created_pipeline_uuid: botDraft.current.pipelineUuid,
|
||||
bot_saved: false,
|
||||
message_received: false,
|
||||
selected_runner: null,
|
||||
@@ -763,15 +796,18 @@ export default function WizardPage() {
|
||||
t('wizard.createError') + (apiErr?.msg ? `: ${apiErr.msg}` : ''),
|
||||
);
|
||||
} finally {
|
||||
botOperationPending.current = false;
|
||||
setIsCreatingBot(false);
|
||||
}
|
||||
}, [selectedAdapter, adapters, t, saveProgress]);
|
||||
}, [selectedAdapter, adapters, createdBotUuid, t, saveProgress]);
|
||||
|
||||
// ---- Save Bot Config & Enable (Step 1) ----
|
||||
// Updates the bot's adapter config and enables it.
|
||||
|
||||
const handleSaveBot = useCallback(async () => {
|
||||
if (!createdBotUuid || !selectedAdapter) return;
|
||||
if (!createdBotUuid || !selectedAdapter || botOperationPending.current)
|
||||
return;
|
||||
botOperationPending.current = true;
|
||||
setIsSavingBot(true);
|
||||
setBotSaved(false);
|
||||
let previewPipelineUuid = createdPipelineUuid;
|
||||
@@ -871,19 +907,11 @@ export default function WizardPage() {
|
||||
adapter_config: configToSave,
|
||||
enable: true,
|
||||
};
|
||||
if (previewPipelineUuid) {
|
||||
botUpdate.event_bindings = [
|
||||
{
|
||||
event_pattern: 'message.received',
|
||||
target_type: 'pipeline',
|
||||
target_uuid: previewPipelineUuid,
|
||||
filters: [],
|
||||
priority: 0,
|
||||
enabled: true,
|
||||
description: '',
|
||||
},
|
||||
];
|
||||
}
|
||||
const currentBot = (await httpClient.getBot(createdBotUuid)).bot;
|
||||
Object.assign(
|
||||
botUpdate,
|
||||
wizardBindingsIfMissing(currentBot, previewPipelineUuid),
|
||||
);
|
||||
|
||||
await httpClient.updateBot(createdBotUuid, botUpdate);
|
||||
setDefaultPreparationProgress(100);
|
||||
@@ -929,6 +957,7 @@ export default function WizardPage() {
|
||||
}
|
||||
toast.error(`${t('wizard.createError')}: ${getErrorMessage(err)}`);
|
||||
} finally {
|
||||
botOperationPending.current = false;
|
||||
setPreparingDefault(false);
|
||||
setIsSavingBot(false);
|
||||
}
|
||||
|
||||
@@ -951,6 +951,8 @@ const enUS = {
|
||||
saveSuccess: 'Saved successfully',
|
||||
saveError: 'Save failed: ',
|
||||
createSuccess: 'Created successfully. Continue configuring the bot',
|
||||
autoCreatedPipeline:
|
||||
'Automatically created a pipeline and bound message events',
|
||||
createError: 'Creation failed: ',
|
||||
deleteSuccess: 'Deleted successfully',
|
||||
deleteError: 'Delete failed: ',
|
||||
@@ -1018,7 +1020,8 @@ const enUS = {
|
||||
commonScenarios: 'Common scenarios',
|
||||
dragEventRoute: 'Drag route {{index}}',
|
||||
behaviorReplyMessages: 'Reply to messages',
|
||||
behaviorReplyMessagesDescription: 'Send incoming messages to a processor.',
|
||||
behaviorReplyMessagesDescription:
|
||||
'Handle private messages and group messages that mention this bot.',
|
||||
behaviorWelcomeMembers: 'Welcome new members',
|
||||
behaviorWelcomeMembersDescription:
|
||||
'Run a processor when someone joins a group.',
|
||||
@@ -1242,6 +1245,9 @@ const enUS = {
|
||||
'All conditions must match to trigger this binding. Leave empty to always trigger.',
|
||||
conditionsEmpty: 'No conditions — always triggers.',
|
||||
addFilter: 'Add condition',
|
||||
filterIsAtBot: 'Mentions this bot',
|
||||
mentionYes: 'Yes',
|
||||
mentionNo: 'No',
|
||||
filterChatType: 'Session type',
|
||||
filterChatId: 'Session ID',
|
||||
filterMessageText: 'Message text',
|
||||
|
||||
@@ -965,6 +965,8 @@ const esES = {
|
||||
saveSuccess: 'Guardado correctamente',
|
||||
saveError: 'Error al guardar: ',
|
||||
createSuccess: 'Creado correctamente. Configura el enrutamiento de eventos',
|
||||
autoCreatedPipeline:
|
||||
'Se ha creado un pipeline automáticamente y se han vinculado los eventos de mensajes',
|
||||
createError: 'Error al crear: ',
|
||||
deleteSuccess: 'Eliminado correctamente',
|
||||
deleteError: 'Error al eliminar: ',
|
||||
@@ -1034,7 +1036,7 @@ const esES = {
|
||||
dragEventRoute: 'Arrastrar ruta {{index}}',
|
||||
behaviorReplyMessages: 'Responder a mensajes',
|
||||
behaviorReplyMessagesDescription:
|
||||
'Envía los mensajes entrantes a un procesador.',
|
||||
'Procesa mensajes privados y mensajes de grupo que mencionen a este bot.',
|
||||
behaviorWelcomeMembers: 'Dar la bienvenida a nuevos miembros',
|
||||
behaviorWelcomeMembersDescription:
|
||||
'Ejecuta un procesador cuando alguien se une a un grupo.',
|
||||
@@ -1261,6 +1263,9 @@ const esES = {
|
||||
'Deben cumplirse todas las condiciones. Déjalo vacío para activar siempre.',
|
||||
conditionsEmpty: 'Sin condiciones: siempre se activa.',
|
||||
addFilter: 'Añadir condición',
|
||||
filterIsAtBot: 'Menciona a este bot',
|
||||
mentionYes: 'Sí',
|
||||
mentionNo: 'No',
|
||||
filterChatType: 'Tipo de sesión',
|
||||
filterChatId: 'ID de sesión',
|
||||
filterMessageText: 'Texto del mensaje',
|
||||
|
||||
@@ -952,6 +952,8 @@ const jaJP = {
|
||||
saveSuccess: '保存に成功しました',
|
||||
saveError: '保存に失敗しました:',
|
||||
createSuccess: '作成が完了しました。イベントルーティングを設定してください',
|
||||
autoCreatedPipeline:
|
||||
'パイプラインを自動作成し、メッセージイベントに関連付けました',
|
||||
createError: '作成に失敗しました:',
|
||||
deleteSuccess: '削除に成功しました',
|
||||
deleteError: '削除に失敗しました:',
|
||||
@@ -1020,7 +1022,7 @@ const jaJP = {
|
||||
dragEventRoute: 'ルート {{index}} をドラッグ',
|
||||
behaviorReplyMessages: '受信メッセージに返信',
|
||||
behaviorReplyMessagesDescription:
|
||||
'受信メッセージをプロセッサーに渡します。',
|
||||
'個別メッセージと、このボットにメンションしたグループメッセージを処理します。',
|
||||
behaviorWelcomeMembers: '新しいメンバーを歓迎',
|
||||
behaviorWelcomeMembersDescription:
|
||||
'グループへの参加時にプロセッサーを実行します。',
|
||||
@@ -1247,6 +1249,9 @@ const jaJP = {
|
||||
'すべての条件に一致した場合のみこのバインディングが発火します。空の場合は常に発火します。',
|
||||
conditionsEmpty: '条件なし。常に発火します。',
|
||||
addFilter: '条件を追加',
|
||||
filterIsAtBot: 'このボットへのメンション',
|
||||
mentionYes: 'はい',
|
||||
mentionNo: 'いいえ',
|
||||
filterChatType: 'セッションタイプ',
|
||||
filterChatId: 'セッション ID',
|
||||
filterMessageText: 'メッセージ本文',
|
||||
|
||||
@@ -960,6 +960,8 @@ const ruRU = {
|
||||
saveSuccess: 'Успешно сохранено',
|
||||
saveError: 'Ошибка сохранения: ',
|
||||
createSuccess: 'Успешно создано. Настройте маршрутизацию событий',
|
||||
autoCreatedPipeline:
|
||||
'Пайплайн автоматически создан и привязан к событиям сообщений',
|
||||
createError: 'Ошибка создания: ',
|
||||
deleteSuccess: 'Успешно удалено',
|
||||
deleteError: 'Ошибка удаления: ',
|
||||
@@ -1029,7 +1031,7 @@ const ruRU = {
|
||||
dragEventRoute: 'Перетащить маршрут {{index}}',
|
||||
behaviorReplyMessages: 'Отвечать на сообщения',
|
||||
behaviorReplyMessagesDescription:
|
||||
'Передавайте входящие сообщения обработчику.',
|
||||
'Обрабатывать личные сообщения и сообщения группы с упоминанием этого бота.',
|
||||
behaviorWelcomeMembers: 'Приветствовать новых участников',
|
||||
behaviorWelcomeMembersDescription:
|
||||
'Запускайте обработчик при вступлении участника в группу.',
|
||||
@@ -1259,6 +1261,9 @@ const ruRU = {
|
||||
'Для срабатывания должны выполняться все условия. Пустой список означает срабатывание всегда.',
|
||||
conditionsEmpty: 'Нет условий — срабатывает всегда.',
|
||||
addFilter: 'Добавить условие',
|
||||
filterIsAtBot: 'Упоминает этого бота',
|
||||
mentionYes: 'Да',
|
||||
mentionNo: 'Нет',
|
||||
filterChatType: 'Тип сеанса',
|
||||
filterChatId: 'ID сеанса',
|
||||
filterMessageText: 'Текст сообщения',
|
||||
|
||||
@@ -932,6 +932,7 @@ const thTH = {
|
||||
saveSuccess: 'บันทึกสำเร็จ',
|
||||
saveError: 'บันทึกล้มเหลว: ',
|
||||
createSuccess: 'สร้างสำเร็จ กรุณากำหนดเส้นทางเหตุการณ์',
|
||||
autoCreatedPipeline: 'สร้างไปป์ไลน์และผูกเหตุการณ์ข้อความโดยอัตโนมัติแล้ว',
|
||||
createError: 'สร้างล้มเหลว: ',
|
||||
deleteSuccess: 'ลบสำเร็จ',
|
||||
deleteError: 'ลบล้มเหลว: ',
|
||||
@@ -999,7 +1000,8 @@ const thTH = {
|
||||
commonScenarios: 'สถานการณ์ทั่วไป',
|
||||
dragEventRoute: 'ลากเส้นทาง {{index}}',
|
||||
behaviorReplyMessages: 'ตอบข้อความ',
|
||||
behaviorReplyMessagesDescription: 'ส่งข้อความขาเข้าไปยังตัวประมวลผล',
|
||||
behaviorReplyMessagesDescription:
|
||||
'ประมวลผลข้อความส่วนตัวและข้อความกลุ่มที่กล่าวถึงบอตนี้',
|
||||
behaviorWelcomeMembers: 'ต้อนรับสมาชิกใหม่',
|
||||
behaviorWelcomeMembersDescription: 'เรียกตัวประมวลผลเมื่อมีคนเข้ากลุ่ม',
|
||||
behaviorHandleDepartures: 'จัดการสมาชิกออกจากกลุ่ม',
|
||||
@@ -1213,6 +1215,9 @@ const thTH = {
|
||||
'ต้องตรงทุกเงื่อนไขจึงจะทำงาน เว้นว่างเพื่อให้ทำงานเสมอ',
|
||||
conditionsEmpty: 'ไม่มีเงื่อนไข — ทำงานเสมอ',
|
||||
addFilter: 'เพิ่มเงื่อนไข',
|
||||
filterIsAtBot: 'กล่าวถึงบอตนี้',
|
||||
mentionYes: 'ใช่',
|
||||
mentionNo: 'ไม่',
|
||||
filterChatType: 'ประเภทเซสชัน',
|
||||
filterChatId: 'ID เซสชัน',
|
||||
filterMessageText: 'ข้อความ',
|
||||
|
||||
@@ -949,6 +949,8 @@ const viVN = {
|
||||
saveSuccess: 'Lưu thành công',
|
||||
saveError: 'Lưu thất bại: ',
|
||||
createSuccess: 'Tạo thành công. Vui lòng cấu hình định tuyến sự kiện',
|
||||
autoCreatedPipeline:
|
||||
'Đã tự động tạo pipeline và liên kết các sự kiện tin nhắn',
|
||||
createError: 'Tạo thất bại: ',
|
||||
deleteSuccess: 'Xóa thành công',
|
||||
deleteError: 'Xóa thất bại: ',
|
||||
@@ -1017,7 +1019,8 @@ const viVN = {
|
||||
commonScenarios: 'Tình huống phổ biến',
|
||||
dragEventRoute: 'Kéo tuyến {{index}}',
|
||||
behaviorReplyMessages: 'Trả lời tin nhắn',
|
||||
behaviorReplyMessagesDescription: 'Gửi tin nhắn đến bộ xử lý.',
|
||||
behaviorReplyMessagesDescription:
|
||||
'Xử lý tin nhắn riêng và tin nhắn nhóm nhắc đến bot này.',
|
||||
behaviorWelcomeMembers: 'Chào mừng thành viên mới',
|
||||
behaviorWelcomeMembersDescription:
|
||||
'Chạy bộ xử lý khi có người tham gia nhóm.',
|
||||
@@ -1235,6 +1238,9 @@ const viVN = {
|
||||
'Phải khớp mọi điều kiện để kích hoạt. Để trống để luôn kích hoạt.',
|
||||
conditionsEmpty: 'Không có điều kiện — luôn kích hoạt.',
|
||||
addFilter: 'Thêm điều kiện',
|
||||
filterIsAtBot: 'Nhắc đến bot này',
|
||||
mentionYes: 'Có',
|
||||
mentionNo: 'Không',
|
||||
filterChatType: 'Loại phiên',
|
||||
filterChatId: 'ID phiên',
|
||||
filterMessageText: 'Nội dung tin nhắn',
|
||||
|
||||
@@ -896,6 +896,7 @@ const zhHans = {
|
||||
saveSuccess: '保存成功',
|
||||
saveError: '保存失败:',
|
||||
createSuccess: '创建成功,请继续配置机器人',
|
||||
autoCreatedPipeline: '已自动创建流水线并绑定消息处理事件',
|
||||
createError: '创建失败:',
|
||||
deleteSuccess: '删除成功',
|
||||
deleteError: '删除失败:',
|
||||
@@ -960,7 +961,8 @@ const zhHans = {
|
||||
commonScenarios: '常用场景',
|
||||
dragEventRoute: '拖动第 {{index}} 条路由',
|
||||
behaviorReplyMessages: '回复收到的消息',
|
||||
behaviorReplyMessagesDescription: '把收到的消息交给处理器。',
|
||||
behaviorReplyMessagesDescription:
|
||||
'私聊直接处理;群聊仅处理 @ 本机器人的消息。',
|
||||
behaviorWelcomeMembers: '欢迎新成员',
|
||||
behaviorWelcomeMembersDescription: '有人加入群组时运行处理器。',
|
||||
behaviorHandleDepartures: '处理成员离群',
|
||||
@@ -1163,6 +1165,9 @@ const zhHans = {
|
||||
conditionsDescription: '满足所有条件时才触发此绑定,不添加则无条件触发。',
|
||||
conditionsEmpty: '无条件,始终触发。',
|
||||
addFilter: '添加条件',
|
||||
filterIsAtBot: '是否 @ 本机器人',
|
||||
mentionYes: '是',
|
||||
mentionNo: '否',
|
||||
filterChatType: '会话类型',
|
||||
filterChatId: '会话 ID',
|
||||
filterMessageText: '消息文本',
|
||||
|
||||
@@ -893,6 +893,7 @@ const zhHant = {
|
||||
saveSuccess: '儲存成功',
|
||||
saveError: '儲存失敗:',
|
||||
createSuccess: '建立成功,請設定事件路由',
|
||||
autoCreatedPipeline: '已自動建立流水線並綁定訊息處理事件',
|
||||
createError: '建立失敗:',
|
||||
deleteSuccess: '刪除成功',
|
||||
deleteError: '刪除失敗:',
|
||||
@@ -958,7 +959,8 @@ const zhHant = {
|
||||
commonScenarios: '常用場景',
|
||||
dragEventRoute: '拖動第 {{index}} 條路由',
|
||||
behaviorReplyMessages: '回覆收到的訊息',
|
||||
behaviorReplyMessagesDescription: '把收到的訊息交給處理器。',
|
||||
behaviorReplyMessagesDescription:
|
||||
'私聊直接處理;群聊僅處理 @ 本機器人的訊息。',
|
||||
behaviorWelcomeMembers: '歡迎新成員',
|
||||
behaviorWelcomeMembersDescription: '有人加入羣組時運行處理器。',
|
||||
behaviorHandleDepartures: '處理成員離羣',
|
||||
@@ -1161,6 +1163,9 @@ const zhHant = {
|
||||
conditionsDescription: '滿足所有條件時才觸發此綁定,不添加則無條件觸發。',
|
||||
conditionsEmpty: '無條件,始終觸發。',
|
||||
addFilter: '添加條件',
|
||||
filterIsAtBot: '是否 @ 本機器人',
|
||||
mentionYes: '是',
|
||||
mentionNo: '否',
|
||||
filterChatType: '會話類型',
|
||||
filterChatId: '會話 ID',
|
||||
filterMessageText: '訊息文本',
|
||||
|
||||
Reference in New Issue
Block a user