mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-09-29 21:06:41 +08:00
fix(bots): highlight unsupported processor events and show overlap
This commit is contained in:
@@ -19,8 +19,8 @@ import type {
|
||||
import { httpClient } from '@/app/infra/http';
|
||||
import { extractI18nObject } from '@/i18n/I18nProvider';
|
||||
import {
|
||||
eventPatternCovers,
|
||||
eventPatternLabel,
|
||||
processorEventCompatibility,
|
||||
} from '@/app/home/components/event-patterns/event-pattern-groups';
|
||||
import DynamicFormComponent from '@/app/home/components/dynamic-form/DynamicFormComponent';
|
||||
import { AuthenticatedPluginIcon } from '@/components/AuthenticatedPluginIcon';
|
||||
@@ -45,7 +45,7 @@ import {
|
||||
} from '@/components/ui/dialog';
|
||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
|
||||
|
||||
function EventCompatibilityWarning({
|
||||
function ProcessorEvents({
|
||||
patterns = [],
|
||||
supportedEvents,
|
||||
}: {
|
||||
@@ -53,28 +53,45 @@ function EventCompatibilityWarning({
|
||||
supportedEvents: string[];
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
// Legacy adapters only emit message.received when no events are declared.
|
||||
const supported = supportedEvents.length
|
||||
? supportedEvents
|
||||
: ['message.received'];
|
||||
const unsupported = [...new Set(patterns)].filter(
|
||||
(pattern) => !supported.some((event) => eventPatternCovers(event, pattern)),
|
||||
const { entries, matchingEvents } = processorEventCompatibility(
|
||||
patterns,
|
||||
supportedEvents,
|
||||
);
|
||||
if (!unsupported.length) return null;
|
||||
const incomplete = entries.some((entry) => !entry.supported);
|
||||
|
||||
return (
|
||||
<span
|
||||
role="status"
|
||||
className="mt-2 flex items-start gap-1.5 whitespace-normal text-xs text-amber-700 dark:text-amber-400"
|
||||
>
|
||||
<TriangleAlert className="mt-0.5 size-3.5 shrink-0" aria-hidden="true" />
|
||||
<span>
|
||||
{t('bots.pluginSubscriptions.incompleteEvents', {
|
||||
events: unsupported
|
||||
.map((pattern) => eventPatternLabel(pattern, t))
|
||||
.join(' · '),
|
||||
})}
|
||||
<span className="block text-xs">
|
||||
<span
|
||||
role="list"
|
||||
className="flex flex-wrap gap-x-2 gap-y-1 whitespace-normal"
|
||||
>
|
||||
{entries.map(({ pattern, supported }) => (
|
||||
<span
|
||||
role="listitem"
|
||||
key={pattern}
|
||||
title={pattern}
|
||||
className={`inline-flex items-center gap-1 ${supported ? 'text-muted-foreground' : 'text-amber-700 dark:text-amber-400'}`}
|
||||
>
|
||||
{!supported && (
|
||||
<TriangleAlert className="size-3.5 shrink-0" aria-hidden="true" />
|
||||
)}
|
||||
{eventPatternLabel(pattern, t)}
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
{incomplete && (
|
||||
<span
|
||||
role="status"
|
||||
className="mt-2 block whitespace-normal text-amber-700 dark:text-amber-400"
|
||||
>
|
||||
{t('bots.pluginSubscriptions.incompleteEvents', {
|
||||
events:
|
||||
matchingEvents
|
||||
.map((pattern) => eventPatternLabel(pattern, t))
|
||||
.join(' · ') || t('common.none'),
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -225,17 +242,7 @@ export default function PluginProcessorBindings({
|
||||
>
|
||||
{agent?.component_ref?.replace('plugin:', '')}
|
||||
</p>
|
||||
<p
|
||||
className="truncate text-xs text-muted-foreground"
|
||||
title={(agent?.supported_event_patterns ?? [])
|
||||
.map((pattern) => eventPatternLabel(pattern, t))
|
||||
.join(' · ')}
|
||||
>
|
||||
{(agent?.supported_event_patterns ?? [])
|
||||
.map((pattern) => eventPatternLabel(pattern, t))
|
||||
.join(' · ') || t('agents.eventProcessor.unavailable')}
|
||||
</p>
|
||||
<EventCompatibilityWarning
|
||||
<ProcessorEvents
|
||||
patterns={agent?.supported_event_patterns}
|
||||
supportedEvents={supportedEvents}
|
||||
/>
|
||||
@@ -374,12 +381,7 @@ export default function PluginProcessorBindings({
|
||||
<span className="block truncate text-sm text-muted-foreground">
|
||||
{agent.component_ref?.replace('plugin:', '')}
|
||||
</span>
|
||||
<span className="block truncate text-xs text-muted-foreground">
|
||||
{(agent.supported_event_patterns ?? [])
|
||||
.map((pattern) => eventPatternLabel(pattern, t))
|
||||
.join(' · ')}
|
||||
</span>
|
||||
<EventCompatibilityWarning
|
||||
<ProcessorEvents
|
||||
patterns={agent.supported_event_patterns}
|
||||
supportedEvents={supportedEvents}
|
||||
/>
|
||||
@@ -441,12 +443,7 @@ export default function PluginProcessorBindings({
|
||||
<span className="block truncate text-sm text-muted-foreground">
|
||||
{descriptor.plugin_author}/{descriptor.plugin_name}
|
||||
</span>
|
||||
<span className="block truncate text-xs text-muted-foreground">
|
||||
{descriptor.supported_event_patterns
|
||||
.map((pattern) => eventPatternLabel(pattern, t))
|
||||
.join(' · ')}
|
||||
</span>
|
||||
<EventCompatibilityWarning
|
||||
<ProcessorEvents
|
||||
patterns={descriptor.supported_event_patterns}
|
||||
supportedEvents={supportedEvents}
|
||||
/>
|
||||
|
||||
@@ -12,6 +12,34 @@ export function eventPatternCovers(supported: string, required: string) {
|
||||
);
|
||||
}
|
||||
|
||||
export function processorEventCompatibility(
|
||||
patterns: string[],
|
||||
supportedEvents: string[],
|
||||
) {
|
||||
// Legacy adapters only emit message.received when no events are declared.
|
||||
const supported = supportedEvents.length
|
||||
? supportedEvents
|
||||
: ['message.received'];
|
||||
const entries = [...new Set(patterns)]
|
||||
.map((pattern) => ({
|
||||
pattern,
|
||||
supported: supported.some((event) => eventPatternCovers(event, pattern)),
|
||||
}))
|
||||
.sort((left, right) => Number(right.supported) - Number(left.supported));
|
||||
const matchingEvents = [
|
||||
...new Set(
|
||||
patterns.flatMap((pattern) =>
|
||||
supported.flatMap((event) => {
|
||||
if (eventPatternCovers(pattern, event)) return [event];
|
||||
if (eventPatternCovers(event, pattern)) return [pattern];
|
||||
return [];
|
||||
}),
|
||||
),
|
||||
),
|
||||
];
|
||||
return { entries, matchingEvents };
|
||||
}
|
||||
|
||||
function eventPatternNamespace(pattern: string) {
|
||||
if (pattern === '*') return '*';
|
||||
return pattern.split('.')[0] || pattern;
|
||||
|
||||
@@ -357,7 +357,7 @@ const enUS = {
|
||||
bots: {
|
||||
pluginSubscriptions: {
|
||||
incompleteEvents:
|
||||
'This bot cannot fully cover these subscribed events: {{events}}. The processor may work only partially; you can still select and bind it.',
|
||||
'This bot only supports some of the subscribed events ({{events}}). The processor may not behave as intended in all cases.',
|
||||
description:
|
||||
'Automatically receive events declared by the plugin, independently of the routes above.',
|
||||
empty: 'No plugin processors are bound.',
|
||||
|
||||
@@ -363,7 +363,28 @@ const esES = {
|
||||
"Uses the Workspace owner's LangBot Account billing and credits.",
|
||||
},
|
||||
bots: {
|
||||
eventWildcard: 'Todos los eventos',
|
||||
eventNamespaceWildcard: '{{namespace}}.*',
|
||||
eventNames: {
|
||||
message_received: 'Mensaje recibido',
|
||||
message_edited: 'Mensaje editado',
|
||||
message_deleted: 'Mensaje eliminado',
|
||||
message_reaction: 'Reacción a un mensaje',
|
||||
feedback_received: 'Comentarios recibidos',
|
||||
friend_request_received: 'Solicitud de amistad recibida',
|
||||
friend_added: 'Amigo añadido',
|
||||
group_member_joined: 'Miembro unido al grupo',
|
||||
group_member_left: 'Miembro salió del grupo',
|
||||
group_member_banned: 'Miembro bloqueado',
|
||||
bot_invited_to_group: 'Bot invitado al grupo',
|
||||
bot_removed_from_group: 'Bot eliminado del grupo',
|
||||
bot_muted: 'Bot silenciado',
|
||||
bot_unmuted: 'Bot ya no silenciado',
|
||||
platform_specific: 'Evento específico de la plataforma',
|
||||
},
|
||||
pluginSubscriptions: {
|
||||
incompleteEvents:
|
||||
'Este bot solo admite algunos de los eventos suscritos ({{events}}). El procesador podría no comportarse como se espera en todos los casos.',
|
||||
description:
|
||||
'Recibe automáticamente los eventos declarados por el plugin, de forma independiente de las rutas anteriores.',
|
||||
empty: 'No hay procesadores vinculados.',
|
||||
|
||||
@@ -363,7 +363,7 @@ const jaJP = {
|
||||
bots: {
|
||||
pluginSubscriptions: {
|
||||
incompleteEvents:
|
||||
'このボットは次の監視イベントに完全には対応していません:{{events}}。一部の機能が動作しない可能性がありますが、選択して登録できます。',
|
||||
'このボットは監視対象の一部のイベント({{events}})にのみ対応しています。プロセッサーが完全には動作しない可能性があります。',
|
||||
description:
|
||||
'プラグインが宣言したイベントを自動で受信し、上のルートとは独立して実行します。',
|
||||
empty: 'プラグインプロセッサーは未登録です。',
|
||||
|
||||
@@ -360,7 +360,28 @@ const ruRU = {
|
||||
"Uses the Workspace owner's LangBot Account billing and credits.",
|
||||
},
|
||||
bots: {
|
||||
eventWildcard: 'Все события',
|
||||
eventNamespaceWildcard: '{{namespace}}.*',
|
||||
eventNames: {
|
||||
message_received: 'Получено сообщение',
|
||||
message_edited: 'Сообщение изменено',
|
||||
message_deleted: 'Сообщение удалено',
|
||||
message_reaction: 'Реакция на сообщение',
|
||||
feedback_received: 'Получен отзыв',
|
||||
friend_request_received: 'Получен запрос в друзья',
|
||||
friend_added: 'Друг добавлен',
|
||||
group_member_joined: 'Участник вступил в группу',
|
||||
group_member_left: 'Участник покинул группу',
|
||||
group_member_banned: 'Участник заблокирован',
|
||||
bot_invited_to_group: 'Бот приглашён в группу',
|
||||
bot_removed_from_group: 'Бот удалён из группы',
|
||||
bot_muted: 'Боту запрещено отправлять сообщения',
|
||||
bot_unmuted: 'Боту разрешено отправлять сообщения',
|
||||
platform_specific: 'Событие конкретной платформы',
|
||||
},
|
||||
pluginSubscriptions: {
|
||||
incompleteEvents:
|
||||
'Этот бот поддерживает только часть отслеживаемых событий ({{events}}). Обработчик может работать не во всех случаях так, как ожидается.',
|
||||
description:
|
||||
'Автоматически получает события, объявленные плагином, независимо от маршрутов выше.',
|
||||
empty: 'Обработчики плагинов не привязаны.',
|
||||
|
||||
@@ -347,7 +347,28 @@ const thTH = {
|
||||
"Uses the Workspace owner's LangBot Account billing and credits.",
|
||||
},
|
||||
bots: {
|
||||
eventWildcard: 'ทุกเหตุการณ์',
|
||||
eventNamespaceWildcard: '{{namespace}}.*',
|
||||
eventNames: {
|
||||
message_received: 'ได้รับข้อความ',
|
||||
message_edited: 'แก้ไขข้อความ',
|
||||
message_deleted: 'ลบข้อความ',
|
||||
message_reaction: 'การแสดงความรู้สึกต่อข้อความ',
|
||||
feedback_received: 'ได้รับข้อเสนอแนะ',
|
||||
friend_request_received: 'ได้รับคำขอเป็นเพื่อน',
|
||||
friend_added: 'เพิ่มเพื่อน',
|
||||
group_member_joined: 'สมาชิกเข้าร่วมกลุ่ม',
|
||||
group_member_left: 'สมาชิกออกจากกลุ่ม',
|
||||
group_member_banned: 'สมาชิกถูกแบน',
|
||||
bot_invited_to_group: 'บอตได้รับเชิญเข้ากลุ่ม',
|
||||
bot_removed_from_group: 'บอตถูกนำออกจากกลุ่ม',
|
||||
bot_muted: 'บอตถูกปิดสิทธิ์ส่งข้อความ',
|
||||
bot_unmuted: 'บอตได้รับสิทธิ์ส่งข้อความคืน',
|
||||
platform_specific: 'เหตุการณ์เฉพาะแพลตฟอร์ม',
|
||||
},
|
||||
pluginSubscriptions: {
|
||||
incompleteEvents:
|
||||
'บอตนี้รองรับเพียงบางเหตุการณ์ที่ตัวประมวลผลติดตาม ({{events}}) ตัวประมวลผลอาจทำงานได้ไม่ครบถ้วน',
|
||||
description:
|
||||
'รับเหตุการณ์ที่ปลั๊กอินประกาศไว้โดยอัตโนมัติ และทำงานแยกจากเส้นทางด้านบน',
|
||||
empty: 'ยังไม่ได้เชื่อมโยงตัวประมวลผลปลั๊กอิน',
|
||||
|
||||
@@ -356,7 +356,28 @@ const viVN = {
|
||||
"Uses the Workspace owner's LangBot Account billing and credits.",
|
||||
},
|
||||
bots: {
|
||||
eventWildcard: 'Tất cả sự kiện',
|
||||
eventNamespaceWildcard: '{{namespace}}.*',
|
||||
eventNames: {
|
||||
message_received: 'Nhận tin nhắn',
|
||||
message_edited: 'Tin nhắn được chỉnh sửa',
|
||||
message_deleted: 'Tin nhắn bị xóa',
|
||||
message_reaction: 'Cảm xúc cho tin nhắn',
|
||||
feedback_received: 'Nhận phản hồi',
|
||||
friend_request_received: 'Nhận lời mời kết bạn',
|
||||
friend_added: 'Đã thêm bạn',
|
||||
group_member_joined: 'Thành viên tham gia nhóm',
|
||||
group_member_left: 'Thành viên rời nhóm',
|
||||
group_member_banned: 'Thành viên bị cấm tham gia nhóm',
|
||||
bot_invited_to_group: 'Bot được mời vào nhóm',
|
||||
bot_removed_from_group: 'Bot bị xóa khỏi nhóm',
|
||||
bot_muted: 'Bot bị cấm gửi tin nhắn',
|
||||
bot_unmuted: 'Bot được phép gửi tin nhắn trở lại',
|
||||
platform_specific: 'Sự kiện riêng của nền tảng',
|
||||
},
|
||||
pluginSubscriptions: {
|
||||
incompleteEvents:
|
||||
'Bot hiện chỉ hỗ trợ một số sự kiện mà bộ xử lý theo dõi ({{events}}). Bộ xử lý có thể không hoạt động đầy đủ trong mọi trường hợp.',
|
||||
description:
|
||||
'Tự động nhận sự kiện do plugin khai báo, hoạt động độc lập với các tuyến ở trên.',
|
||||
empty: 'Chưa liên kết bộ xử lý plugin.',
|
||||
|
||||
@@ -342,7 +342,7 @@ const zhHans = {
|
||||
bots: {
|
||||
pluginSubscriptions: {
|
||||
incompleteEvents:
|
||||
'当前机器人无法完整支持以下监听事件:{{events}}。处理器的表现可能不完整。',
|
||||
'当前机器人仅支持部分事件({{events}}),处理器表现可能不完整。',
|
||||
description: '自动接收插件声明的事件,与上方事件路由独立执行。',
|
||||
empty: '尚未绑定插件处理器。',
|
||||
add: '添加插件处理器',
|
||||
|
||||
@@ -336,7 +336,28 @@ const zhHant = {
|
||||
"Uses the Workspace owner's LangBot Account billing and credits.",
|
||||
},
|
||||
bots: {
|
||||
eventWildcard: '全部事件',
|
||||
eventNamespaceWildcard: '{{namespace}}.*',
|
||||
eventNames: {
|
||||
message_received: '收到訊息',
|
||||
message_edited: '訊息已編輯',
|
||||
message_deleted: '訊息已刪除',
|
||||
message_reaction: '訊息表情回應',
|
||||
feedback_received: '收到回饋',
|
||||
friend_request_received: '收到好友請求',
|
||||
friend_added: '新增好友',
|
||||
group_member_joined: '成員加入群組',
|
||||
group_member_left: '成員離開群組',
|
||||
group_member_banned: '成員被封禁',
|
||||
bot_invited_to_group: '機器人被邀請加入群組',
|
||||
bot_removed_from_group: '機器人被移出群組',
|
||||
bot_muted: '機器人被禁言',
|
||||
bot_unmuted: '機器人被解除禁言',
|
||||
platform_specific: '平台特定事件',
|
||||
},
|
||||
pluginSubscriptions: {
|
||||
incompleteEvents:
|
||||
'目前機器人僅支援部分事件({{events}}),處理器表現可能不完整。',
|
||||
description: '自動接收外掛宣告的事件,與上方事件路由獨立執行。',
|
||||
empty: '尚未綁定外掛處理器。',
|
||||
add: '新增外掛處理器',
|
||||
|
||||
Reference in New Issue
Block a user