diff --git a/internal/channels/feishu/bot.go b/internal/channels/feishu/bot.go index ca9dc5ec..ba4b23ca 100644 --- a/internal/channels/feishu/bot.go +++ b/internal/channels/feishu/bot.go @@ -143,13 +143,18 @@ func (c *Channel) handleMessageEvent(ctx context.Context, event *MessageEvent) { metadata["sender_open_id"] = sender.SenderID.OpenID } - // Build final content with group context (pending history + sender annotation). - if mc.ChatType == "group" && senderName != "" { - annotated := fmt.Sprintf("[From: %s]\n%s", senderName, content) - if c.historyLimit > 0 { - content = c.groupHistory.BuildContext(chatID, annotated, c.historyLimit) + // Annotate content with sender identity so the agent knows who is messaging. + if senderName != "" { + if mc.ChatType == "group" { + annotated := fmt.Sprintf("[From: %s]\n%s", senderName, content) + if c.historyLimit > 0 { + content = c.groupHistory.BuildContext(chatID, annotated, c.historyLimit) + } else { + content = annotated + } } else { - content = annotated + // DM: annotate with sender identity so the agent knows who is messaging. + content = fmt.Sprintf("[From: %s]\n%s", senderName, content) } } diff --git a/ui/web/src/i18n/locales/en/channels.json b/ui/web/src/i18n/locales/en/channels.json index dab90a19..0cd0d07a 100644 --- a/ui/web/src/i18n/locales/en/channels.json +++ b/ui/web/src/i18n/locales/en/channels.json @@ -278,6 +278,11 @@ "formBanner": "After creating, you'll authenticate via QR code and configure allowed users." } }, + "scopes": { + "title": "Required API Permissions", + "description": "Your Lark/Feishu app must have these scopes enabled in the Developer Console. Publish a new app version after adding permissions.", + "publishReminder": "After adding scopes, set Contact Range to \"All members\" and publish a new app version for changes to take effect." + }, "toast": { "created": "Channel created", "createdDesc": "{{name}} has been added", diff --git a/ui/web/src/i18n/locales/vi/channels.json b/ui/web/src/i18n/locales/vi/channels.json index 765b99c8..42eedcb7 100644 --- a/ui/web/src/i18n/locales/vi/channels.json +++ b/ui/web/src/i18n/locales/vi/channels.json @@ -278,6 +278,11 @@ "formBanner": "Sau khi tạo, bạn sẽ xác thực bằng mã QR và cấu hình người dùng được phép." } }, + "scopes": { + "title": "Quyền API cần thiết", + "description": "Ứng dụng Lark/Feishu cần bật các scope này trong Developer Console. Publish phiên bản mới sau khi thêm quyền.", + "publishReminder": "Sau khi thêm scope, đặt Contact Range thành \"All members\" và publish phiên bản mới để thay đổi có hiệu lực." + }, "toast": { "created": "Đã tạo channel", "createdDesc": "{{name}} đã được thêm", diff --git a/ui/web/src/i18n/locales/zh/channels.json b/ui/web/src/i18n/locales/zh/channels.json index 4af15557..360dbb6a 100644 --- a/ui/web/src/i18n/locales/zh/channels.json +++ b/ui/web/src/i18n/locales/zh/channels.json @@ -278,6 +278,11 @@ "formBanner": "创建后,您将通过二维码进行认证并配置允许的用户。" } }, + "scopes": { + "title": "所需API权限", + "description": "您的 Lark/飞书 应用需要在开发者控制台启用这些权限。添加权限后请发布新版本。", + "publishReminder": "添加权限后,将通讯录范围设置为「全部成员」,并发布新版本使更改生效。" + }, "toast": { "created": "Channel已创建", "createdDesc": "{{name}} 已添加", diff --git a/ui/web/src/pages/channels/channel-detail/channel-config-tab.tsx b/ui/web/src/pages/channels/channel-detail/channel-config-tab.tsx index 3cd17bfa..0325eb28 100644 --- a/ui/web/src/pages/channels/channel-detail/channel-config-tab.tsx +++ b/ui/web/src/pages/channels/channel-detail/channel-config-tab.tsx @@ -5,6 +5,7 @@ import { Button } from "@/components/ui/button"; import type { ChannelInstanceData } from "@/types/channel"; import { configSchema } from "../channel-schemas"; import { ChannelFields } from "../channel-fields"; +import { ChannelScopesInfo } from "../channel-scopes-info"; interface ChannelConfigTabProps { instance: ChannelInstanceData; @@ -85,6 +86,8 @@ export function ChannelConfigTab({ instance, onUpdate }: ChannelConfigTabProps) idPrefix="cd-cfg" /> + + {showWebhookUrl && (
diff --git a/ui/web/src/pages/channels/channel-instance-form-dialog.tsx b/ui/web/src/pages/channels/channel-instance-form-dialog.tsx index e91abdf3..8c44ce7a 100644 --- a/ui/web/src/pages/channels/channel-instance-form-dialog.tsx +++ b/ui/web/src/pages/channels/channel-instance-form-dialog.tsx @@ -23,6 +23,7 @@ import type { AgentData } from "@/types/agent"; import { slugify, isValidSlug } from "@/lib/slug"; import { credentialsSchema, configSchema, wizardConfig, type FieldDef } from "./channel-schemas"; import { ChannelFields } from "./channel-fields"; +import { ChannelScopesInfo } from "./channel-scopes-info"; import { wizardAuthSteps, wizardConfigSteps, wizardEditConfigs } from "./channel-wizard-registry"; import { TelegramGroupOverrides } from "./telegram-group-overrides"; import { CHANNEL_TYPES } from "@/constants/channels"; @@ -307,6 +308,8 @@ export function ChannelInstanceFormDialog({ )} + + {/* Auth status indicator (edit mode, channels with auth wizard step) */} {instance && wizard?.steps.includes("auth") && (
diff --git a/ui/web/src/pages/channels/channel-schemas.ts b/ui/web/src/pages/channels/channel-schemas.ts index 045353be..6bba6521 100644 --- a/ui/web/src/pages/channels/channel-schemas.ts +++ b/ui/web/src/pages/channels/channel-schemas.ts @@ -163,6 +163,34 @@ export const groupOverrideSchema: FieldDef[] = [ { key: "system_prompt", label: "System Prompt", type: "textarea", placeholder: "Additional system prompt for this group..." }, ]; +// --- Required API scopes per channel type --- +// Displayed as a help reference when creating/configuring a channel. + +export interface ScopeEntry { + scope: string; + note?: string; // e.g. "Range: All members" +} + +export const requiredScopes: Partial> = { + feishu: [ + { scope: "application:application:self_manage" }, + { scope: "application:bot.menu:write" }, + { scope: "cardkit:card:read" }, + { scope: "cardkit:card:write" }, + { scope: "contact:contact.base:readonly", note: "Range: All members" }, + { scope: "contact:user.base:readonly", note: "Range: All members" }, + { scope: "contact:user.employee_id:readonly", note: "Range: All members" }, + { scope: "event:ip_list" }, + { scope: "im:chat.members:bot_access" }, + { scope: "im:message" }, + { scope: "im:message.group_at_msg:readonly" }, + { scope: "im:message.p2p_msg:readonly" }, + { scope: "im:message:readonly" }, + { scope: "im:message:send_as_bot" }, + { scope: "im:resource" }, + ], +}; + // --- Post-create wizard configuration --- // Channels with multi-step create flows (e.g. auth then config). // Channels not listed here use the default single-step create. diff --git a/ui/web/src/pages/channels/channel-scopes-info.tsx b/ui/web/src/pages/channels/channel-scopes-info.tsx new file mode 100644 index 00000000..8ad5f244 --- /dev/null +++ b/ui/web/src/pages/channels/channel-scopes-info.tsx @@ -0,0 +1,54 @@ +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { ChevronDown, ChevronRight, Shield } from "lucide-react"; +import { requiredScopes } from "./channel-schemas"; + +interface ChannelScopesInfoProps { + channelType: string; +} + +export function ChannelScopesInfo({ channelType }: ChannelScopesInfoProps) { + const { t } = useTranslation("channels"); + const scopes = requiredScopes[channelType]; + const [expanded, setExpanded] = useState(false); + + if (!scopes || scopes.length === 0) return null; + + return ( +
+ + {expanded && ( +
+

+ {t("scopes.description")} +

+
+ {scopes.map((s) => ( +
+ {s.scope} + {s.note && ( + + — {s.note} + + )} +
+ ))} +
+

+ {t("scopes.publishReminder")} +

+
+ )} +
+ ); +}