Skip to content

feat(channels): 新增 Discord 渠道接入 - #82

Open
aurevian-biz wants to merge 31 commits into
OpenBMB:mainfrom
aurevian-biz:feat/discord-channel
Open

aurevian-biz wants to merge 31 commits into
OpenBMB:mainfrom
aurevian-biz:feat/discord-channel

Conversation

@aurevian-biz

@aurevian-biz aurevian-biz commented Aug 7, 2026

Copy link
Copy Markdown

Summary

数字员工现在可以接入 Discord 作为第 5 个消息渠道,能力与既有渠道同级且更完整:原生斜杠命令、线程(含自动建线程)、批量发送与限流、历史回填、权限白名单、连接/typing 指示、语音投递、富媒体(embeds/附件)发送。此前仅支持微信、企业微信、飞书、钉钉——本 PR 让 Discord 从「不支持」变为一等渠道,且完全复用既有渠道无关内核(入站 staging、出站 outbox、身份服务、凭证生命周期、能力声明),未做内核结构性改动。

顺带补齐一条通用能力:渠道投递的附件会登记为「已发布交付物」,并新增 read_published_deliverable harness 能力,使历史产物可被后续会话检索复用。

能力矩阵

能力 现在可以做什么
原生斜杠命令 /employee /switch /current /help /bind 按 guild 同步;回调序列化为文本指令走 durable inbox,幂等/重试/审计自动复用
线程 入站按 thread 建会话、出站优先回 thread;白名单按父频道;自动建线程开关(默认关)在群聊 reply 前建线程,失败自动降级主频道
批量发送 TokenBucket 限流(容量 5 / 每 5s 补 1)+ 内存 BatchJob 状态机 + batch:{job}:{index} 幂等键
历史回填 REST 分页拉取历史,status=backfilled 落库不触发 agent,web 会话页合并展示
权限白名单 mode/guild/channel/user_ids + deny,deny 优先;越权入站落库 rejected 可审计
连接 / typing 连接状态(connected/reconnecting)+ 凭证失效判定;typing 每 8s 触发,双门禁
语音投递 能力默认关闭,ffmpeg 缺失时不声明(不虚报)
富媒体 embeds(≤10 裁剪校验)+ 文件(≤8MiB);harness 产物自动桥接为附件

关键设计决策

决策 选择 理由
能力协议 ChannelCapability StrEnum + 可选协议混入 存量渠道未实现即空集,自动降级,零侵入
运行模式 每绑定一线程 + 独立 event loop(5s reconcile) 规避 discord.py 2.x 模块级 event loop 隐患
命令回调 序列化为文本指令走 durable inbox 幂等/重试/审计免费复用,parse_command 单一事实来源
自动建线程 建线程成功立即 commit 持久化 thread_id 重试短路不重发;Permanent 失败降级 blocked
富媒体传递 payload_json 全链路传递,显式优先、harness 产物兜底 出站适配器统一处理 content/embeds/files/audio
交付物检索 投递附件登记 + read_published_deliverable 读回 历史产物跨会话可检索

验证

  • 后端:渠道相关 pytest 全绿;全库 1550+ passed;ruff 全过
  • 前端:vitest 26 files / 99 tests 全绿;npm run build(tsc + vite)与 i18n:check 均通过
  • 线上实证:真实 Bot Token 下 connected=True,Discord 消息 → AgentLoop 回复 delivered(群聊 + DM 双场景);harness 产物实际投递为 Discord 附件
  • Oracle 设计一致性审查:无 P0,P1 全部修复闭环

风险与说明

  • 需启用 Message Content Intent,否则群聊非 @bot 消息 content 为空(UI 已提示)
  • 语音能力依赖 ffmpeg,缺失时能力不声明、UI 置灰,不会虚报
  • 批处理为内存态,进程重启任务丢失(幂等键保证不重发);自动回填为后续演进
  • 自动建线程存在「创建成功但响应读取失败」的窄窗口会重试再建(Discord 允许重名,接受)
  • 本 PR 不含真实网关集成测试(需真实 token,已线上实证)

@hm1229
hm1229 requested a review from fadeoreo August 9, 2026 07:05
@fadeoreo

Copy link
Copy Markdown
Collaborator

@aurevian-biz
你好,我注意到 PR #82 中的 commit 作者信息显示为 sunxu heli_9902@hotmail.com,这个邮箱是我的邮箱,但我并未授权在你的 Git 提交中使用它。
请不要使用他人的邮箱作为 Git author,并请尽快修改这些 commit 的 author 信息后强制更新 PR。建议使用你自己 GitHub 账号绑定的邮箱,或者使用 GitHub 提供的 noreply 邮箱。
修改后请确认 PR 中不再出现我的邮箱。谢谢。

@aurevian-biz

Copy link
Copy Markdown
Author

我没有主观故意性,应该是提交时没修改git的帐号,周四我会修改后再次提交。

@fadeoreo

Copy link
Copy Markdown
Collaborator

好的,十分感谢

@aurevian-biz

Copy link
Copy Markdown
Author

已经将作者和提交者修改完成。

为 Discord 渠道 8 项功能扩展奠定共享地基:
- ChannelCapability 枚举 + ChannelCapabilityAdapter 可选协议, 存量渠道自动降级为空能力集
- ChannelDelivery 新增 payload_json/thread_id/batch_id/delivery_kind 字段
- ChannelInboundEvent 新增 thread_id/mention_user_ids/command 字段(信封 v2)
- SQLite 就地迁移 _migrate_channel_envelope_v2_schema, 幂等补列
- channel_capabilities_of 双防御(callable+isinstance) 保证向后兼容

本地验证: pytest 9 个新测试通过, ruff 通过
按 design-discord-channel-features.md 实现全部功能:
- 原生斜杠命令: commands.Bot+CommandTree 注册 5 命令, 回调走 durable inbox 管道复用幂等/重试
- 线程: 入站识别 is_thread/thread_id, 出站 target.thread_id 优先, 白名单按父频道
- 批处理: TokenBucket 限流(容量5/每5s补1) + BatchJob 状态机 + 幂等键 batch:{job}:{index}
- 回填: fetch_history REST 分页 + status=backfilled 不触发 agent + message_id 幂等 + web 合并可见
- 权限白名单: config_json.allowlist 六重 fence 校验, 拒绝落库 rejected 可审计
- typing: send_typing(每8s) + TypingManager 三进门禁(hasattr+能力声明+features 开关)
- 语音: VOICE 默认关闭, ffmpeg 缺失自动不声明, outbox delivery_kind==voice 分派
- 富媒体: embeds(≤10裁剪)+files(≤8MiB multipart)+payload_json 全链路传递+入站附件提取

另修复真实链路缺陷: ChannelInbound dataclass 无 .get() 致回填必崩,
新增 _backfill_message_dict 归一化; features.slash_commands/typing 开关接线。

本地验证: pytest 相关集合 1511 passed(5 预存/flaky 与本次无关), ruff 通过
- DiscordFeatureConfig: 8 功能开关(features) + 白名单编辑器(mode/ID 列表) + 回填触发按钮 + 批量发送面板(轮询进度)
- ChannelMessageAttachments: 会话消息附件卡片渲染(image/pdf/文件名)
- ChannelsPage 挂载功能配置 section 与附件渲染分支
- types: ChannelAllowlistConfig/ChannelFeatureFlags 等 6 个新类型, config_json 收紧为 ChannelBindingConfigJson
- i18n: 补充 33 条 Discord 功能区词条

本地验证: build 通过, vitest 26 files/97 tests 通过
agent 运行生成的 harness 工作区文件此前只在 web 端展示(harness_artifacts
元数据),渠道投递所需的 channel_payload.files 载荷无生产写入方,导致
Discord 渠道永远只收到纯文本回复。

在 stage_channel_delivery 中对 discord 渠道补齐桥接:读取工作区文件字节
(base64),受 8MiB 单文件上限约束,读取失败静默降级;仅 discord 渠道生效,
微信/飞书/钉钉/企微路径零影响;显式 channel_payload 优先于桥接。

新增 test_channel_harness_payload.py 覆盖构造/超限跳过/非 discord 不桥接/
文件缺失降级/显式载荷优先。
此前 Web 端提问从不外投,仅 assistant 回复经 outbox 回投 Discord,
方向不对称。新增 stage_user_message_mirror 登记 kind=user_mirror
投递,接入 harness_v2_engine.run 与定时任务草稿路径;仅 web 来源
且会话完整锚定渠道时触发,幂等按 message.id 去重,失败静默不
影响 Web 主流程,渠道来源不镜像以防回声。
- 显式 channel_payload 附件幂等镜像进 harness_artifacts(operation=channel_delivery),
  桥接路径产物天然已登记不重复,消除渠道与 artifact 账目脱节
- 新增 list_published_deliverables internal 能力,按 tenant+session 聚合并
  跨 task frame 检索历史已发布交付物(created_at 倒序,limit 截断)
数字员工此前只能通过 list_published_deliverables 发现历史已发布
交付物的元数据,但因 task frame 工作区隔离,无法读取旧任务产物的
真实内容,只能重复创建文档。新增 internal 能力 read_published_deliverable:
按 path 跨所有 task frame 匹配(可选 task_frame_id 消歧),经
harness_task_workspace_path + open_harness_artifact 读回真实文件内容,
base64 返回,超 max_bytes 截断并标记 truncated。

与 fix(channels): 渠道投递附件补登记搭配,补齐"发现→读取→复用"闭环。
- 解决 10 个文件的合并冲突,保留 discord 渠道功能并合并主线改动
- 修复合并回归:service_outbox voice 投递 elif 链断裂导致误触发 text send
- 修复 update_channel_binding_agents features 块缺失 db.commit()
- 保留 backfill 可视化、auto-thread、reaction 等 discord 分支逻辑
- 引入 main 的 default_handoff 人工处理、harness 改进、Feishu trace 等
@aurevian-biz aurevian-biz changed the title feat: 新增 Discord 渠道接入 feat(channels): 新增 Discord 渠道接入 Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants