mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-21 16:05:19 +08:00
fix(wecombot): improve event routing and sandbox attachment delivery
This commit is contained in:
@@ -90,6 +90,14 @@ const DEBUG_EVENT_DEFINITIONS: Record<string, DebugEventDefinition> = {
|
||||
messageField: 'text',
|
||||
},
|
||||
'message.deleted': deletion,
|
||||
'wecombot.enter_chat': {
|
||||
fields: [user],
|
||||
defaults: { chat_id: 'debug-user', chat_type: 'private', message_id: 'debug-entry', data: {} },
|
||||
},
|
||||
'wecombot.template_card_event': {
|
||||
fields: [user],
|
||||
defaults: { chat_id: 'debug-user', chat_type: 'private', message_id: 'debug-card-click', task_id: 'debug-card', event_key: 'confirm', card_type: 'button_interaction', selected_items: [], data: {} },
|
||||
},
|
||||
'wecomcs.enter_session': {
|
||||
fields: [user],
|
||||
defaults: { open_kfid: 'debug-kf', external_userid: 'debug-user', chat_id: 'debug-user|debug-kf', scene: 'debug', scene_param: '', welcome_code: '', wechat_channels: {} },
|
||||
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { UseFormReturn } from 'react-hook-form';
|
||||
import {
|
||||
@@ -30,6 +32,7 @@ import {
|
||||
Play,
|
||||
RefreshCw,
|
||||
Shield,
|
||||
Settings2,
|
||||
Trash2,
|
||||
UserCheck,
|
||||
UserMinus,
|
||||
@@ -495,6 +498,10 @@ function TargetCombobox({
|
||||
const [open, setOpen] = useState(false);
|
||||
const pipelineAllowed = isMessageEventPattern(binding.event_pattern);
|
||||
const targetType = binding.target_type || 'agent';
|
||||
const selectedTarget =
|
||||
targetType !== 'discard'
|
||||
? agentOptions.find((agent) => agent.uuid === binding.target_uuid)
|
||||
: undefined;
|
||||
|
||||
const current =
|
||||
targetType === 'discard'
|
||||
@@ -545,63 +552,92 @@ function TargetCombobox({
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
className="h-8 w-[200px] justify-between text-sm font-normal px-3"
|
||||
>
|
||||
<span className="truncate">{currentLabel()}</span>
|
||||
<ChevronsUpDown className="ml-1 h-3.5 w-3.5 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[240px] p-0" align="start">
|
||||
<Command>
|
||||
<CommandInput placeholder={t('bots.searchTarget')} className="h-8" />
|
||||
<CommandList>
|
||||
<CommandEmpty>{t('bots.noTargetFound')}</CommandEmpty>
|
||||
{agents.length > 0 && (
|
||||
<CommandGroup heading={t('bots.targetAgent')}>
|
||||
{agents.map((a) => (
|
||||
<CommandItem
|
||||
key={a.uuid}
|
||||
value={`agent:${a.uuid}:${a.name}`}
|
||||
onSelect={() => select(encodeTarget('agent', a.uuid || ''))}
|
||||
>
|
||||
<Bot className="mr-2 size-3.5 shrink-0" />
|
||||
<span className="truncate">{targetLabel(a)}</span>
|
||||
{current === encodeTarget('agent', a.uuid || '') && (
|
||||
<Check className="ml-auto size-3.5 shrink-0" />
|
||||
)}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
<div className="relative w-[200px] shrink-0">
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
className={cn(
|
||||
'h-8 w-full justify-between text-sm font-normal px-3',
|
||||
selectedTarget && 'pr-10',
|
||||
)}
|
||||
{pipelines.length > 0 && (
|
||||
<CommandGroup heading={t('bots.targetPipeline')}>
|
||||
{pipelines.map((a) => (
|
||||
<CommandItem
|
||||
key={a.uuid}
|
||||
value={`pipeline:${a.uuid}:${a.name}`}
|
||||
onSelect={() =>
|
||||
select(encodeTarget('pipeline', a.uuid || ''))
|
||||
}
|
||||
>
|
||||
<Workflow className="mr-2 size-3.5 shrink-0" />
|
||||
<span className="truncate">{targetLabel(a)}</span>
|
||||
{current === encodeTarget('pipeline', a.uuid || '') && (
|
||||
<Check className="ml-auto size-3.5 shrink-0" />
|
||||
)}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
)}
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
>
|
||||
<span className="truncate">{currentLabel()}</span>
|
||||
<ChevronsUpDown className="ml-1 h-3.5 w-3.5 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[240px] p-0" align="start">
|
||||
<Command>
|
||||
<CommandInput placeholder={t('bots.searchTarget')} className="h-8" />
|
||||
<CommandList>
|
||||
<CommandEmpty>{t('bots.noTargetFound')}</CommandEmpty>
|
||||
{agents.length > 0 && (
|
||||
<CommandGroup heading={t('bots.targetAgent')}>
|
||||
{agents.map((a) => (
|
||||
<CommandItem
|
||||
key={a.uuid}
|
||||
value={`agent:${a.uuid}:${a.name}`}
|
||||
onSelect={() => select(encodeTarget('agent', a.uuid || ''))}
|
||||
>
|
||||
<Bot className="mr-2 size-3.5 shrink-0" />
|
||||
<span className="truncate">{targetLabel(a)}</span>
|
||||
{current === encodeTarget('agent', a.uuid || '') && (
|
||||
<Check className="ml-auto size-3.5 shrink-0" />
|
||||
)}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
)}
|
||||
{pipelines.length > 0 && (
|
||||
<CommandGroup heading={t('bots.targetPipeline')}>
|
||||
{pipelines.map((a) => (
|
||||
<CommandItem
|
||||
key={a.uuid}
|
||||
value={`pipeline:${a.uuid}:${a.name}`}
|
||||
onSelect={() =>
|
||||
select(encodeTarget('pipeline', a.uuid || ''))
|
||||
}
|
||||
>
|
||||
<Workflow className="mr-2 size-3.5 shrink-0" />
|
||||
<span className="truncate">{targetLabel(a)}</span>
|
||||
{current === encodeTarget('pipeline', a.uuid || '') && (
|
||||
<Check className="ml-auto size-3.5 shrink-0" />
|
||||
)}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
)}
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
{selectedTarget && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
asChild
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="absolute right-1 top-1 size-6"
|
||||
>
|
||||
<Link
|
||||
to={`/home/agents?id=${encodeURIComponent(selectedTarget.uuid || '')}&tab=config`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label={t('bots.pluginSubscriptions.configure')}
|
||||
>
|
||||
<Settings2 className="size-3.5" />
|
||||
</Link>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{t('bots.pluginSubscriptions.configure')}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -390,9 +390,10 @@ const BotSessionMonitor = forwardRef<
|
||||
}, [selectedSessionId, messagePage, loadMessages]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingMessages || messageError) return;
|
||||
if (messages.length === 0 && toolCalls.length === 0) return;
|
||||
// Wait for DOM to render the new messages before scrolling
|
||||
requestAnimationFrame(() => {
|
||||
// The timeline is hidden until all message data has finished loading.
|
||||
const frame = requestAnimationFrame(() => {
|
||||
const container = messagesContainerRef.current;
|
||||
if (container) {
|
||||
const viewport = container.querySelector(
|
||||
@@ -402,7 +403,8 @@ const BotSessionMonitor = forwardRef<
|
||||
scrollTarget.scrollTop = scrollTarget.scrollHeight;
|
||||
}
|
||||
});
|
||||
}, [messages, toolCalls]);
|
||||
return () => cancelAnimationFrame(frame);
|
||||
}, [loadingMessages, messageError, messages, toolCalls]);
|
||||
|
||||
const parseMessageChain = (content: string): MessageChainComponent[] => {
|
||||
try {
|
||||
|
||||
@@ -1003,6 +1003,8 @@ const enUS = {
|
||||
eventWildcard: 'All events',
|
||||
eventNamespaceWildcard: '{{namespace}}.*',
|
||||
eventNames: {
|
||||
wecombot_enter_chat: "User entered bot chat",
|
||||
wecombot_template_card_event: "Template card interaction",
|
||||
wecomcs_enter_session: "Customer entered conversation",
|
||||
wecomcs_msg_send_fail: "Customer service message failed",
|
||||
wecomcs_servicer_status_change: "Representative availability changed",
|
||||
@@ -1025,6 +1027,8 @@ const enUS = {
|
||||
platform_specific: 'Platform-specific event',
|
||||
},
|
||||
eventDescriptions: {
|
||||
wecombot_enter_chat: "A user opened the bot chat. Use this event for welcome workflows.",
|
||||
wecombot_template_card_event: "A user clicked or submitted a template card. Managed form callbacks are handled separately.",
|
||||
wecomcs_enter_session: "A customer opened a service link; includes entry source and welcome code.",
|
||||
wecomcs_msg_send_fail: "WeChat reported an outgoing message failure, with message ID and failure type.",
|
||||
wecomcs_servicer_status_change: "A representative started, stopped or suspended service.",
|
||||
|
||||
@@ -1022,6 +1022,8 @@ const esES = {
|
||||
eventWildcard: 'Todos los eventos',
|
||||
eventNamespaceWildcard: '{{namespace}}.*',
|
||||
eventNames: {
|
||||
wecombot_enter_chat: "Usuario entró al chat del bot",
|
||||
wecombot_template_card_event: "Interacción con tarjeta",
|
||||
wecomcs_enter_session: "Cliente entró en la conversación",
|
||||
wecomcs_msg_send_fail: "Fallo de envío de atención al cliente",
|
||||
wecomcs_servicer_status_change: "Disponibilidad del agente modificada",
|
||||
@@ -1044,6 +1046,8 @@ const esES = {
|
||||
platform_specific: 'Evento específico de la plataforma',
|
||||
},
|
||||
eventDescriptions: {
|
||||
wecombot_enter_chat: "Un usuario abrió el chat del bot. Úsalo para flujos de bienvenida.",
|
||||
wecombot_template_card_event: "Un usuario pulsó o envió una tarjeta. Las respuestas de formularios gestionados se procesan por separado.",
|
||||
wecomcs_enter_session: "Un cliente abrió el enlace de atención; incluye origen y código de bienvenida.",
|
||||
wecomcs_msg_send_fail: "WeChat notificó un fallo de envío con el ID y el tipo de error.",
|
||||
wecomcs_servicer_status_change: "Un agente inició, detuvo o suspendió la atención.",
|
||||
|
||||
@@ -1008,6 +1008,8 @@ const jaJP = {
|
||||
eventWildcard: 'すべてのイベント',
|
||||
eventNamespaceWildcard: '{{namespace}}.*',
|
||||
eventNames: {
|
||||
wecombot_enter_chat: "ボットとのチャットに参加",
|
||||
wecombot_template_card_event: "テンプレートカード操作",
|
||||
wecomcs_enter_session: "顧客が会話に参加",
|
||||
wecomcs_msg_send_fail: "カスタマーサービスの送信失敗",
|
||||
wecomcs_servicer_status_change: "担当者の受付状態変更",
|
||||
@@ -1030,6 +1032,8 @@ const jaJP = {
|
||||
platform_specific: 'プラットフォーム固有イベント',
|
||||
},
|
||||
eventDescriptions: {
|
||||
wecombot_enter_chat: "ユーザーがボットのチャットを開きました。歓迎や案内のフローに使用できます。",
|
||||
wecombot_template_card_event: "ユーザーがカードをクリックまたは送信しました。管理対象フォームの応答は別途処理します。",
|
||||
wecomcs_enter_session: "顧客がリンクから会話を開きました。流入元と歓迎メッセージ用コードを含みます。",
|
||||
wecomcs_msg_send_fail: "WeChat が送信失敗を通知しました。メッセージ ID と失敗種別を含みます。",
|
||||
wecomcs_servicer_status_change: "担当者が受付を開始、停止、または一時停止しました。",
|
||||
|
||||
@@ -1018,6 +1018,8 @@ const ruRU = {
|
||||
eventWildcard: 'Все события',
|
||||
eventNamespaceWildcard: '{{namespace}}.*',
|
||||
eventNames: {
|
||||
wecombot_enter_chat: "Пользователь открыл чат бота",
|
||||
wecombot_template_card_event: "Взаимодействие с карточкой",
|
||||
wecomcs_enter_session: "Клиент вошёл в диалог",
|
||||
wecomcs_msg_send_fail: "Ошибка отправки сообщения поддержки",
|
||||
wecomcs_servicer_status_change: "Статус оператора изменён",
|
||||
@@ -1040,6 +1042,8 @@ const ruRU = {
|
||||
platform_specific: 'Событие конкретной платформы',
|
||||
},
|
||||
eventDescriptions: {
|
||||
wecombot_enter_chat: "Пользователь открыл чат бота. Событие подходит для сценариев приветствия.",
|
||||
wecombot_template_card_event: "Пользователь нажал или отправил карточку. Ответы управляемых форм обрабатываются отдельно.",
|
||||
wecomcs_enter_session: "Клиент открыл ссылку поддержки; событие содержит источник и код приветствия.",
|
||||
wecomcs_msg_send_fail: "WeChat сообщил об ошибке отправки с ID сообщения и типом ошибки.",
|
||||
wecomcs_servicer_status_change: "Оператор начал, остановил или приостановил приём обращений.",
|
||||
|
||||
@@ -986,6 +986,8 @@ const thTH = {
|
||||
eventWildcard: 'ทุกเหตุการณ์',
|
||||
eventNamespaceWildcard: '{{namespace}}.*',
|
||||
eventNames: {
|
||||
wecombot_enter_chat: "ผู้ใช้เข้าสู่แชตบอต",
|
||||
wecombot_template_card_event: "การโต้ตอบกับการ์ด",
|
||||
wecomcs_enter_session: "ลูกค้าเข้าสู่การสนทนา",
|
||||
wecomcs_msg_send_fail: "ส่งข้อความบริการลูกค้าไม่สำเร็จ",
|
||||
wecomcs_servicer_status_change: "สถานะเจ้าหน้าที่เปลี่ยนแปลง",
|
||||
@@ -1008,6 +1010,8 @@ const thTH = {
|
||||
platform_specific: 'เหตุการณ์เฉพาะแพลตฟอร์ม',
|
||||
},
|
||||
eventDescriptions: {
|
||||
wecombot_enter_chat: "ผู้ใช้เปิดแชตบอต ใช้สำหรับขั้นตอนต้อนรับและแนะนำ",
|
||||
wecombot_template_card_event: "ผู้ใช้คลิกหรือส่งการ์ด การตอบกลับแบบฟอร์มที่ระบบจัดการจะประมวลผลแยกกัน",
|
||||
wecomcs_enter_session: "ลูกค้าเปิดลิงก์บริการ พร้อมข้อมูลแหล่งที่มาและรหัสต้อนรับ",
|
||||
wecomcs_msg_send_fail: "WeChat แจ้งการส่งล้มเหลว พร้อม ID ข้อความและประเภทข้อผิดพลาด",
|
||||
wecomcs_servicer_status_change: "เจ้าหน้าที่เริ่ม หยุด หรือพักการให้บริการ",
|
||||
|
||||
@@ -1003,6 +1003,8 @@ const viVN = {
|
||||
eventWildcard: 'Tất cả sự kiện',
|
||||
eventNamespaceWildcard: '{{namespace}}.*',
|
||||
eventNames: {
|
||||
wecombot_enter_chat: "Người dùng vào cuộc trò chuyện với bot",
|
||||
wecombot_template_card_event: "Tương tác thẻ mẫu",
|
||||
wecomcs_enter_session: "Khách hàng vào hội thoại",
|
||||
wecomcs_msg_send_fail: "Gửi tin nhắn hỗ trợ thất bại",
|
||||
wecomcs_servicer_status_change: "Trạng thái nhân viên thay đổi",
|
||||
@@ -1025,6 +1027,8 @@ const viVN = {
|
||||
platform_specific: 'Sự kiện riêng của nền tảng',
|
||||
},
|
||||
eventDescriptions: {
|
||||
wecombot_enter_chat: "Người dùng mở cuộc trò chuyện với bot. Dùng cho quy trình chào mừng.",
|
||||
wecombot_template_card_event: "Người dùng nhấp hoặc gửi thẻ. Phản hồi biểu mẫu do hệ thống quản lý được xử lý riêng.",
|
||||
wecomcs_enter_session: "Khách hàng mở liên kết hỗ trợ; gồm nguồn truy cập và mã chào mừng.",
|
||||
wecomcs_msg_send_fail: "WeChat thông báo gửi thất bại, gồm ID tin nhắn và loại lỗi.",
|
||||
wecomcs_servicer_status_change: "Nhân viên bắt đầu, dừng hoặc tạm dừng tiếp nhận.",
|
||||
|
||||
@@ -946,6 +946,8 @@ const zhHans = {
|
||||
eventWildcard: '全部事件',
|
||||
eventNamespaceWildcard: '{{namespace}}.*',
|
||||
eventNames: {
|
||||
wecombot_enter_chat: "用户进入机器人会话",
|
||||
wecombot_template_card_event: "模板卡片交互",
|
||||
wecomcs_enter_session: "客户进入会话",
|
||||
wecomcs_msg_send_fail: "客服消息发送失败",
|
||||
wecomcs_servicer_status_change: "接待人员状态变更",
|
||||
@@ -968,6 +970,8 @@ const zhHans = {
|
||||
platform_specific: '平台特定事件',
|
||||
},
|
||||
eventDescriptions: {
|
||||
wecombot_enter_chat: "用户进入机器人会话,可用于欢迎和引导流程。",
|
||||
wecombot_template_card_event: "用户点击或提交模板卡片;已有交互表单的回调单独处理。",
|
||||
wecomcs_enter_session: "客户通过客服链接进入会话,可获取来源和欢迎语凭据。",
|
||||
wecomcs_msg_send_fail: "微信通知客服消息发送失败,包含消息 ID 和失败类型。",
|
||||
wecomcs_servicer_status_change: "接待人员开始、停止接待或暂时挂起。",
|
||||
|
||||
@@ -945,6 +945,8 @@ const zhHant = {
|
||||
eventWildcard: '全部事件',
|
||||
eventNamespaceWildcard: '{{namespace}}.*',
|
||||
eventNames: {
|
||||
wecombot_enter_chat: "使用者進入機器人會話",
|
||||
wecombot_template_card_event: "範本卡片互動",
|
||||
wecomcs_enter_session: "客戶進入會話",
|
||||
wecomcs_msg_send_fail: "客服訊息傳送失敗",
|
||||
wecomcs_servicer_status_change: "接待人員狀態變更",
|
||||
@@ -967,6 +969,8 @@ const zhHant = {
|
||||
platform_specific: '平台特定事件',
|
||||
},
|
||||
eventDescriptions: {
|
||||
wecombot_enter_chat: "使用者進入機器人會話,可用於歡迎和引導流程。",
|
||||
wecombot_template_card_event: "使用者點擊或提交範本卡片;已有互動表單的回呼單獨處理。",
|
||||
wecomcs_enter_session: "客戶透過客服連結進入會話,可取得來源和歡迎語憑證。",
|
||||
wecomcs_msg_send_fail: "微信通知客服訊息傳送失敗,包含訊息 ID 和失敗類型。",
|
||||
wecomcs_servicer_status_change: "接待人員開始、停止接待或暫時掛起。",
|
||||
|
||||
Reference in New Issue
Block a user