Skip to content

Feat/substitute mode#401

Open
mahongchaodev wants to merge 4 commits into
deepcoldy:masterfrom
mahongchaodev:feat/substitute-mode
Open

Feat/substitute mode#401
mahongchaodev wants to merge 4 commits into
deepcoldy:masterfrom
mahongchaodev:feat/substitute-mode

Conversation

@mahongchaodev

@mahongchaodev mahongchaodev commented Jul 8, 2026

Copy link
Copy Markdown

背景

普通群里,用户经常会自然地 @ 某个负责人或值班人,而不是 @ 机器人。这个 PR 增加“替身模式”:当群消息 @ 到配置的替身对象时,botmux 可以把这条消息视为对当前 bot 的请求,由 bot 在同一个普通群会话里代答,常常配合个人知识库使用。

该功能适合搭配 Oncall 模式使用:Oncall 让群成员具备 canTalk,替身模式让群成员可以自然 @ 替身对象触发 bot 代答。

核心改动

  • 新增 per-bot 的 substituteMode 配置,用于声明替身目标。
  • 支持按 openId / userId / unionId 匹配飞书 @mention。
  • 配置结构保留 email 字段,但当前版本不按 email 匹配。
  • 普通群消息 @ 到替身目标时,会触发当前 bot 代答。
  • 替身触发强制使用 chat-scope,即整个普通群复用一个连续会话。
  • 替身模式遵守 canTalk:只有具备对话权的 sender @替身目标才会触发。
  • 新增群内命令 /substitute status|on|off。
    • 默认每个普通群开启替身触发。
    • /substitute off 只关闭当前群的替身触发。
    • 修改开关仍要求 owner/allowedUsers。
  • Dashboard Bot Defaults 增加“替身模式”配置区块。
    • 可启用/关闭全局替身配置。
    • 可编辑 targets JSON。
    • 可选择 disclosure 策略。
    • 文案提示推荐搭配 Oncall 模式使用。
  • 替身触发时,card-off 模式下“收到任务”的飞书 reaction 使用 Get。
    • 普通消息仍使用默认/自定义 receivedReactionEmoji。
    • 完成态仍使用 DONE 或 doneReactionEmoji。

权限与边界

  • 替身模式不绕过 canTalk。
  • /restart、/close、/cd、终端写链接、配置修改等敏感操作仍只允许 owner/allowedUsers。
  • 替身触发不会改变 canOperate 权限模型。
  • 以 / 开头的非 @bot 替身消息不会触发,避免 @替身 /close 这类消息绕过命令权限模型。
  • 当前群可以通过 @bot /substitute off 临时关闭替身触发。
  • Dashboard 的“关闭全局替身配置”是关闭 bot 级 substituteMode;群内 /substitute off 是关闭当前群,两者语义不同。

使用方式

Dashboard 或 bots.json 配置示例:

{
  "substituteMode": {
    "enabled": true,
    "disclosure": "prefix",
    "targets": [
      {
        "name": "马洪超",
        "userId": "ge897abb",
        "openId": "ou_8b8570da90e6694261be27662d9becf6",
        "unionId": "on_03f32fd6a6f361505d8a2a65a79455b3",
        "email": "[mahongchao.chaotian@bytedance.com](mailto:mahongchao.chaotian@bytedance.com)"
      }
    ]
  }
}

群内开关:

@bot /substitute status
@bot /substitute on
@bot /substitute off

推荐配置:

1. 在 Dashboard 给 bot 开启 Oncall 模式,绑定项目目录。
2. 在替身模式中配置替身目标 userId/openId/unionId。
3. 群成员 @替身对象 时,bot 会在群连续会话中代答。

实现要点

  • 新增 mention identity 解析,统一处理 open_id / user_id / union_id / app_id。
  • 在 Lark event dispatcher 中新增 substitute trigger 判定。
  • 命中替身触发后路由到普通群 chat-scope。
  • 将 substituteTrigger 元数据注入 prompt,模型知道当前是替身代答场景。
  • pending repo 路径保留 substituteTrigger,用户选仓库后首轮 prompt 仍包含替身上下文。
  • 新增 substitute-chat-toggle-store 保存每个 bot、每个群的替身开关状态。
  • 新增 substitute-mode-store 支持 Dashboard 原子写入 bots.json 并同步 daemon 内存。

验证

已通过:

  • TypeScript 检查:npx pnpm@9.5.0 exec tsc --noEmit
  • 相关单测:
    • substituteMode 配置解析
    • Dashboard bot payload
    • /substitute 命令
    • 群级 substitute 开关 store
    • 普通群替身触发路由
    • 非 canTalk sender 不触发
    • 富文本 inline @ 的 userId 匹配
    • substitute_trigger prompt 注入
    • 替身触发使用 Get reaction
    • 普通消息仍支持 receivedReactionEmoji / doneReactionEmoji 自定义
  • 完整构建:npx pnpm@9.5.0 build

注意事项

  • 该功能依赖飞书事件能投递“未 @bot、但 @ 到替身人”的普通群消息。实际租户需要确认应用消息事件权限/订阅覆盖此场景。
  • email 当前只保存不匹配,后续可补 email 到 user_id/open_id 的解析。
  • 群级 substitute toggle store 目前是简单本地 JSON 文件,多 daemon 并发修改时理论上可能 last-write-wins;当前风险较低,后续可按 appId 分文件或加文件锁。

@mahongchaodev mahongchaodev requested a review from deepcoldy as a code owner July 8, 2026 02:48
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.

1 participant