Back to Aionui

设置页 → 远程连接 — Channels 渠道接入 (F-CHAN)

docs/prds/remote/channels/channels.md

2.1.1225.4 KB
Original Source

设置页 → 远程连接 — Channels 渠道接入 (F-CHAN)

本文档覆盖「设置 → 远程连接」页面 Channels Tab 的全部功能,包括渠道卡片总览、各 IM 渠道配置(Telegram/Lark/DingTalk/WeChat/WeCom)、Agent/模型选择、配对授权通用流程、扩展渠道支持。 基于静态代码分析和动态 UI 验证综合整理。

WebUI Tab 相关内容见 webui.md


(F-WEBUI-12) Channels 页面总览与渠道卡片结构 [已实现]

用户故事:作为用户,我希望在"远程连接"设置页面中浏览所有支持的 IM 渠道,了解每个渠道的状态,并展开配置面板。

正常流程(用户视角):

  1. 用户进入「设置 → 远程连接」页面,切换到 "Channels" Tab(桌面端);或直接看到 Channels 内容(非桌面端/WebUI 浏览器访问)
  2. 页面顶部显示标题"Channels"、功能描述文本("Connect Telegram, Lark, and DingTalk to interact with AionUi from IM apps."),以及 2 步引导提示
  3. 下方为渠道卡片列表(Arco Design Collapse),每个渠道一张可折叠卡片
  4. 卡片 header 包含:渠道图标/logo、渠道名、描述、连接状态徽标(Connected/未连接)、Switch 启用开关
  5. 默认所有卡片折叠(collapseKeys 默认 true);点击卡片 header 展开详细配置面板
  6. Switch 开关可直接启用/禁用渠道,无需展开卡片

渠道列表与排序

顺序渠道pluginId状态
1Telegramtelegram_defaultactive
2Lark / Feishulark_defaultactive
3DingTalkdingtalk_defaultactive
4WeChatweixin_defaultactive
5WeComwecom_defaultactive
6+扩展渠道动态(extensionMetaactive
末尾Slack-coming_soon
末尾Discord-coming_soon

说明:Slack/Discord 若已被扩展渠道实现(extensionTypeSet 包含 slack/discord),则隐藏对应 coming_soon 占位卡片。

异常情况

  • channel.getPluginStatus.invoke() 失败:仅记录日志,渠道状态保持初始值(未启用/未连接)
  • channel.pluginStatusChanged.on() 事件推送:实时更新对应渠道的状态(启用/连接/botUsername 等)

验收标准

  • 页面显示标题、描述和引导提示
  • 所有渠道按指定顺序排列
  • 卡片默认折叠,点击可展开/收起
  • Switch 开关可在卡片 header 直接操作
  • 渠道状态(Connected/未连接)实时更新
  • 非桌面端直接显示 Channels 内容(无 Tab 切换)
  • Slack/Discord 显示"coming soon"占位文案
  • 已被扩展实现的 coming_soon 渠道自动隐藏

(F-WEBUI-13) Telegram 渠道配置 [已实现]

用户故事:作为用户,我希望通过输入 Telegram Bot Token 连接 Telegram,并管理通过 Telegram 与 AionUi 交互的授权用户。

前置条件:用户已在 Telegram @BotFather 创建 Bot 并获取 Token

正常流程(用户视角):

  1. 展开 Telegram 卡片,显示配置面板
  2. Bot Token 输入:Password 类型输入框(240px 宽),带可见性切换,placeholder 123456:ABC-DEF...
  3. 点击"Test"按钮测试连接
  4. 测试成功:toast 提示"Connected! Bot: @xxx",自动启用插件(enablePlugin),刷新状态
  5. 启用成功后 header 的 Switch 变为 ON,状态徽标变为 Connected
  6. 下方出现"Next Steps"引导卡片(4 步操作指引),引导用户在 Telegram 搜索 Bot 发送消息触发配对
  7. 用户在 Telegram 发送消息后,"Pending Pairing Requests"区域实时出现配对请求(通过 channel.pairingRequested.on 事件推送)
  8. 点击"Approve"批准配对,用户移入"Authorized Users"列表
  9. 授权后 Token 输入框和 Test 按钮变为 disabled(tokenLocked tooltip:"请先关闭渠道并删除所有授权用户后才能修改配置")

异常情况

  • Token 为空点击 Test:warning toast "Please enter a bot token"
  • 测试失败(Token 无效/网络错误):error toast 显示后端错误信息或"Connection failed"
  • 启用无 Token(Switch 直接打开):warning toast "Please enter a bot token first"
  • 批准配对失败:error toast
  • 拒绝配对:info toast "Pairing rejected"
  • 撤销用户失败:error toast

验收标准

  • Bot Token 输入框支持 Password 可见性切换
  • Test 按钮测试成功后自动启用插件
  • 启用后显示 4 步操作引导
  • 实时接收并显示配对请求
  • 可批准/拒绝配对请求
  • 授权用户列表显示用户名、平台、授权时间
  • 可撤销已授权用户
  • 有授权用户时 Token 和 Test 按钮禁用
  • 配对请求和授权用户列表支持手动刷新

(F-WEBUI-14) Lark / 飞书渠道配置 [已实现]

用户故事:作为用户,我希望通过配置飞书应用凭据连接 Lark/飞书,与 AionUi 进行 IM 对话。

前置条件:用户已在飞书开放平台创建应用并获取 App ID 和 App Secret

正常流程(用户视角):

  1. 展开 Lark 卡片,显示配置面板
  2. 必填字段
    • App ID(Input,必填,红色 * 标记)
    • App Secret(Input.Password,必填,红色 * 标记)
  3. 可选字段(默认折叠,点击"Show optional fields"展开):
    • Encrypt Key(Input.Password)
    • Verification Token(Input.Password)
  4. 右下角有蓝色链接"飞书开放平台文档",打开 https://open.feishu.cn/document/develop-an-echo-bot/introduction
  5. 点击"Test & Connect"按钮测试连接
  6. 测试成功:toast "Connected to Lark API!",自动启用插件(传入 appId + appSecret + encryptKey? + verificationToken?)
  7. 配对/授权用户管理流程与 Telegram 相同(见 F-WEBUI-13)

异常情况

  • App ID 或 App Secret 为空点击 Test:warning toast "Please enter App ID and App Secret",空字段标红(touched 状态)
  • 凭据无效:error toast 显示后端错误信息
  • 启用无凭据(Switch 直接打开):warning toast "Please configure Lark credentials first"
  • 连接状态变化:pluginStatusChanged 事件实时更新(Connected/Error/Connecting 带颜色编码徽标)

验收标准

  • App ID 和 App Secret 为必填项,带红色 *
  • 可选字段默认折叠,可展开
  • Test & Connect 测试成功后自动启用
  • 飞书文档链接可正常打开
  • 空字段提交时表单标红
  • 配对/授权用户管理功能正常
  • 连接状态实时更新

(F-WEBUI-15) DingTalk 渠道配置 [已实现]

用户故事:作为用户,我希望通过配置钉钉应用凭据连接 DingTalk,与 AionUi 进行 IM 对话。

前置条件:用户已在钉钉开放平台创建应用并获取 Client ID 和 Client Secret

正常流程(用户视角):

  1. 展开 DingTalk 卡片,显示配置面板
  2. 必填字段
    • Client ID(Input,必填,红色 * 标记)
    • Client Secret(Input.Password,必填,红色 * 标记)
  3. 右下角有蓝色链接"DingTalk setup guide",打开 GitHub Wiki 配置指南
  4. 点击"Test & Connect"按钮测试连接
  5. 测试成功后自动启用插件
  6. 配对/授权用户管理流程与 Telegram 相同(见 F-WEBUI-13)

异常情况

  • 凭据为空:warning toast "Please configure DingTalk credentials first"
  • 测试/启用失败:error toast 显示错误信息

验收标准

  • Client ID 和 Client Secret 为必填项
  • Test & Connect 测试成功后自动启用
  • GitHub Wiki 链接可正常打开
  • 配对/授权用户管理功能正常

(F-WEBUI-16) WeChat 渠道配置(扫码登录) [已实现]

用户故事:作为用户,我希望通过微信扫码登录连接 WeChat 渠道,无需手动输入复杂凭据。

正常流程(用户视角):

  1. 展开 WeChat 卡片,显示配置面板
  2. 初始状态(idle):显示"Scan to Login"按钮
  3. 用户点击按钮,进入二维码加载状态(loading_qr
  4. Electron 桌面端
    • 调用 window.electronAPI.weixinLoginStart() 启动登录
    • 通过 weixinLoginOnQR 事件接收 QR 码(base64 data URL)
    • 显示 `` 标签渲染 QR 码(160x160px)
  5. WebUI 浏览器端
    • 通过 EventSource('/api/channel/weixin/login') SSE 接收事件
    • 通过 qr 事件接收 QR 码数据(raw ticket string)
    • 使用 QRCodeSVG 组件渲染 QR 码(160x160px)
  6. 用户用微信扫描二维码
  7. 扫描后状态变为 scanned:显示 Spin loading + "Scanned, waiting for confirmation..."
  8. 确认后自动调用 enableWeixinPlugin(accountId, botToken) 启用渠道
  9. 状态变为 connected:显示绿色对钩 + "Connected" + "Disconnect"按钮
  10. 配对/授权用户管理流程与 Telegram 相同(见 F-WEBUI-13)

登录状态机

idle → loading_qr → showing_qr → scanned → connected
  ↑         ↓              ↓          ↓
  ←─── (error/expired) ←───┴──────────┘

异常情况

  • QR 码过期/too many 尝试:warning toast "QR code expired, please try again",回到 idle
  • EventSource error 事件(WebUI):error toast "WeChat login failed",回到 idle
  • Electron IPC 错误(非 Aborted):error toast "WeChat login failed"
  • 用户取消(Aborted):静默回到 idle,不显示错误
  • enablePlugin 失败:error toast,回到 idle
  • Disconnect 失败:error toast

验收标准

  • Electron 端通过 IPC 接收 QR 码(base64 img)
  • WebUI 端通过 EventSource SSE 接收 QR 码(QRCodeSVG 渲染)
  • 扫码后显示"等待确认"loading 状态
  • 登录成功后显示 Connected 状态 + Disconnect 按钮
  • QR 码过期有明确提示,可重新发起
  • 断开连接(Disconnect)可正常禁用渠道
  • 组件卸载时 EventSource 正确关闭(无连接泄漏)

(F-WEBUI-17) WeCom 渠道配置(WebSocket 长连接) [已实现]

用户故事:作为用户,我希望通过配置企业微信机器人 ID 和密钥连接 WeCom 渠道,使用 WebSocket 长连接模式无需回调 URL。

前置条件:用户已在企业微信管理后台创建应用并获取 Bot ID 和 Secret

正常流程(用户视角):

  1. 展开 WeCom 卡片,显示配置面板
  2. 顶部橙色提示横幅说明:WeCom 使用 WebSocket 长连接模式,无需配置回调 URL
  3. 必填字段
    • Bot ID(Input,必填,红色 * 标记,有授权用户时 disabled)
    • Secret(Input.Password,必填,红色 * 标记,有授权用户时 disabled)
  4. 右下角有蓝色链接"企业微信开发文档",打开 https://developer.work.weixin.qq.com/document/path/101463
  5. 点击"Save & Enable"按钮(区别于其他渠道的 "Test & Connect")
  6. 保存并启用成功:toast "WeCom channel enabled",刷新状态
  7. 配对/授权用户管理流程与 Telegram 相同(见 F-WEBUI-13)

异常情况

  • Bot ID 或 Secret 为空:warning toast "Please enter Bot ID and Secret",字段标红
  • 启用无凭据(Switch 直接打开):warning toast "Please save Token and EncodingAESKey first"
  • 保存/启用失败:error toast 显示错误信息

验收标准

  • 顶部显示 WebSocket 长连接模式说明横幅
  • Bot ID 和 Secret 为必填项
  • Save & Enable 按钮一步保存并启用
  • 企业微信文档链接可正常打开
  • 有授权用户时凭据输入框禁用
  • 配对/授权用户管理功能正常

(F-WEBUI-18) 渠道 Agent 选择 [已实现]

用户故事:作为用户,我希望为每个 IM 渠道独立选择使用的 Agent,以便不同渠道使用不同的 AI 后端。

正常流程(用户视角):

  1. 每个渠道配置面板内有"Agent"选择行
  2. 点击 Dropdown 按钮展开 Agent 列表(来自 acpConversation.getAvailableAgents.invoke(),过滤掉 preset Agent)
  3. 默认选中"Gemini CLI"({ backend: 'gemini' }
  4. 选择新 Agent 后:
    • 本地状态更新
    • ConfigStorage.set('assistant.{platform}.agent', agent) 持久化
    • channel.syncChannelSettings.invoke({ platform, agent }) 同步到后端
    • toast "Agent switched successfully"
  5. 页面重新打开时从 ConfigStorage 恢复之前的选择

异常情况

  • Agent 列表加载失败:默认显示 "Gemini CLI" 作为唯一选项
  • 持久化失败:error toast "Failed to save"
  • syncChannelSettings 失败:仅 console.warn,不影响本地选择

验收标准

  • 每个渠道有独立的 Agent Dropdown
  • Agent 列表从后端加载,过滤 preset
  • 切换 Agent 有成功 toast
  • 选择持久化,重新打开页面恢复
  • Agent 列表为空时显示 "Gemini CLI" 兜底

(F-WEBUI-19) 渠道默认模型选择 [已实现]

用户故事:作为用户,我希望为每个 IM 渠道独立设置默认使用的 AI 模型,以便控制不同渠道的回复质量和速度。

前置条件:当前 Agent 为 Gemini 兼容类型(backend === 'gemini''aionrs'

正常流程(用户视角):

  1. 每个渠道配置面板内有"Default Model"选择行
  2. 使用 GeminiModelSelector 组件渲染模型下拉选择
  3. 选择模型后:
    • ConfigStorage.set('assistant.{platform}.defaultModel', { id, useModel }) 持久化
    • channel.syncChannelSettings.invoke({ platform, agent, model }) 同步到后端
    • toast "Model switched successfully"
  4. 页面重新打开时自动恢复保存的模型(useChannelModelSelection hook,最多重试 5 次匹配 provider)

条件渲染

  • Agent 为 Gemini/aionrs 类型:正常显示模型选择器
  • Agent 为其他类型(如 Custom Agent):显示灰色文案"Automatically follow the model when CLI is running",选择器禁用

异常情况

  • 保存的 Provider ID 已失效(provider 被删除):重试最多 5 次后放弃,使用默认模型
  • 模型保存失败:error toast "Failed to save model"

验收标准

  • 每个渠道有独立的模型选择
  • 仅 Gemini 兼容 Agent 显示模型选择器
  • 非 Gemini Agent 显示禁用状态 + 提示文案
  • 模型选择持久化,重新打开页面恢复
  • Provider 失效时有重试机制(最多 5 次)

(F-WEBUI-20) 渠道配对与用户授权(通用流程) [已实现]

用户故事:作为用户,我希望通过配对码机制控制哪些 IM 用户可以与我的 AionUi 交互,保证安全性。

适用渠道:Telegram、Lark、DingTalk、WeChat、WeCom(全部 5 个已实现渠道共享此流程)

正常流程(用户视角):

  1. 渠道启用且已连接后,IM 用户向 Bot 发送消息
  2. 首次消息触发配对请求,AionUi 通过 channel.pairingRequested.on() 实时推送到设置页面
  3. "Pending Pairing Requests"区域显示配对卡片:
    • 用户显示名(displayName,未知则显示"Unknown User")
    • 配对码(code)+ 复制按钮
    • 剩余时间(expiresAt,单位 min)
    • "Approve"按钮(主色调)+ "Reject"按钮(红色危险态)
  4. 点击 Approve:channel.approvePairing.invoke({ code }) → 成功 toast → 用户移入"Authorized Users"
  5. channel.userAuthorized.on() 实时推送,自动从 pending 列表移除并添加到 authorized 列表
  6. "Authorized Users"区域显示:
    • 用户显示名
    • 平台类型 + 授权时间
    • "Revoke access"删除按钮(红色危险态)
  7. 点击 Revoke:channel.revokeUser.invoke({ userId }) → 成功 toast → 用户从列表移除

凭据锁定机制:当 authorizedUsers.length > 0 时,凭据输入框和测试按钮变为 disabled,tooltip 提示"请先关闭渠道并删除所有授权用户后才能修改配置"。此规则适用于 Telegram(Token)、Lark(AppID/Secret)、DingTalk(ClientID/Secret)、WeCom(BotID/Secret)。

异常情况

  • 加载配对/授权列表失败:仅日志,空列表
  • 批准配对失败:error toast 显示后端错误
  • 拒绝配对失败:error toast
  • 撤销用户失败:error toast
  • 实时事件去重:已存在相同 code/id 的不重复添加

验收标准

  • 配对请求实时显示(无需手动刷新)
  • 可批准/拒绝配对请求
  • 批准后用户实时出现在授权列表
  • 可撤销已授权用户
  • 有授权用户时凭据/测试按钮禁用 + tooltip 提示
  • 配对请求显示剩余时间
  • 配对请求/授权列表均有手动刷新按钮
  • 空列表显示 Empty 占位组件

(F-WEBUI-21) 扩展渠道支持 [已实现]

用户故事:作为扩展开发者,我希望通过扩展 manifest 注册自定义 IM 渠道,使用动态表单配置凭据。

正常流程(用户视角/开发者视角):

  1. 扩展通过 extensionMeta 声明 credentialFieldsconfigFields,定义表单字段
  2. 页面加载时,扩展渠道从 getPluginStatus 返回的非内置类型中识别
  3. 扩展渠道卡片在 5 个内置渠道之后、coming_soon 渠道之前显示
  4. 卡片 header 显示蓝色 "ext" 徽标(isExtension: true
  5. 展开后根据 extensionMeta 的字段 schema 动态渲染表单

支持的字段类型

类型渲染组件说明
textInput普通文本输入
passwordInput[type=password]密码输入
selectSelect下拉选择,options 来自 schema
numberInputNumber数值输入
booleanSwitch开关切换

启用前校验:遍历 credentialFieldsrequired: true 的字段,若有未填写的(空字符串或 undefined),warning toast 提示"Please fill required field: {fieldLabel}"

特殊处理 — ext-wecom-bot 扩展:当扩展类型为 ext-wecom-bot 时,显示橙色提示横幅说明回调 URL(本机/局域网/公网),包含 WebUI 状态(localUrl/networkUrl)。

异常情况

  • 扩展无配置字段:显示 extensionMeta.description 或 "No extra configuration required."
  • 启用缺少必填字段:warning toast,不提交
  • 启用/禁用失败:error toast

验收标准

  • 扩展渠道在内置渠道之后显示
  • 卡片显示蓝色 "ext" 徽标
  • 5 种字段类型正确渲染
  • 必填字段校验生效
  • 字段默认值从 field.default 初始化
  • ext-wecom-bot 显示回调 URL 信息横幅

附录 A:IPC 通信链路(Channels Tab)

┌──────────────────────────────────────────────────────────────────────┐
│ ChannelModalContent.tsx (Channels Tab):                              │
│  渠道状态:                                                            │
│    ├─ channel.getPluginStatus.invoke() [加载全部渠道状态]             │
│    └─ channel.pluginStatusChanged.on() [实时监听状态变更]            │
│  渠道启停:                                                            │
│    ├─ channel.enablePlugin.invoke({ pluginId, config }) [启用]       │
│    ├─ channel.disablePlugin.invoke({ pluginId }) [禁用]              │
│    └─ channel.testPlugin.invoke({ pluginId, token, extraConfig? })   │
│  配对授权:                                                            │
│    ├─ channel.getPendingPairings.invoke() [加载待配对]                │
│    ├─ channel.getAuthorizedUsers.invoke() [加载已授权]                │
│    ├─ channel.approvePairing.invoke({ code }) [批准]                 │
│    ├─ channel.rejectPairing.invoke({ code }) [拒绝]                  │
│    ├─ channel.revokeUser.invoke({ userId }) [撤销]                   │
│    ├─ channel.pairingRequested.on() [实时配对请求]                    │
│    └─ channel.userAuthorized.on() [实时授权通知]                      │
│  Agent/模型:                                                          │
│    ├─ acpConversation.getAvailableAgents.invoke() [加载 Agent 列表]  │
│    ├─ channel.syncChannelSettings.invoke({ platform, agent, model }) │
│    └─ ConfigStorage.get/set('assistant.{platform}.agent/defaultModel')│
│  WeChat 登录:                                                         │
│    ├─ electronAPI.weixinLoginStart() [Electron IPC]                  │
│    ├─ electronAPI.weixinLoginOnQR/OnScanned/OnDone [IPC 事件]        │
│    └─ EventSource('/api/channel/weixin/login') [WebUI SSE]           │
└──────────────────────────────────────────────────────────────────────┘

附录 B:Toast 通知汇总

触发操作Toast 类型i18n Key
渠道插件启用成功successsettings.assistant.pluginEnabled / settings.{platform}.pluginEnabled
渠道插件禁用成功successsettings.assistant.pluginDisabled / settings.{platform}.pluginDisabled
渠道插件启用/禁用失败errorsettings.assistant.enableFailed / settings.{platform}.enableFailed
连接测试成功successsettings.assistant.connectionSuccess / settings.lark.connectionSuccess
连接测试失败errorsettings.assistant.connectionFailed / 后端错误信息
Token 为空测试warningsettings.assistant.tokenRequired
凭据为空(Lark/DingTalk/WeCom)warningsettings.{platform}.credentialsRequired
配对批准成功successsettings.assistant.pairingApproved
配对拒绝infosettings.assistant.pairingRejected
用户撤销成功successsettings.assistant.userRevoked
Agent 切换成功successsettings.assistant.agentSwitched
模型切换成功successsettings.assistant.modelSwitched
模型保存失败errorsettings.assistant.modelSaveFailed
WeChat 登录过期warningsettings.weixin.loginExpired
WeChat 登录失败errorsettings.weixin.loginError
WeCom 保存启用成功successsettings.wecom.pluginEnabled

附录 C:已知局限汇总

#功能点局限描述来源
1F-WEBUI-13Telegram Token 锁定机制依赖 authorizedUsers.length > 0,如果后端授权数据丢失但 Token 实际在用中,用户可能误修改 Token 导致断连R2 初稿
2F-WEBUI-16WeChat 扫码登录的 EventSource(WebUI 模式)在组件卸载时关闭,但 Electron IPC 模式中 weixinLoginStart 是 await Promise,组件卸载不会取消该 PromiseR2 初稿
3F-WEBUI-18Agent 列表通过 acpConversation.getAvailableAgents.invoke() 加载,每个渠道独立调用,无缓存共享(5 个渠道 = 5 次相同 IPC 调用)R2 初稿
4F-WEBUI-19useChannelModelSelection 恢复保存的 provider 时最多重试 5 次,provider 被删除后静默降级为默认模型,无用户提示R2 初稿
5F-WEBUI-20配对请求的 expiresAt 由后端生成,UI 显示剩余时间为 Math.ceil((expiresAt - Date.now()) / 60000),无自动倒计时刷新(显示值仅在组件渲染时计算)R2 初稿
6F-WEBUI-17WeCom "Save & Enable" 按钮 warning 提示 "Please save Token and EncodingAESKey first" 与实际字段名 (Bot ID / Secret) 不一致R2 初稿
7F-WEBUI-21扩展渠道字段值存储在组件 state(extensionFieldValues),页面离开后丢失;已启用的扩展字段值不从后端恢复显示R2 初稿