Notifications & Integrations
dsh-qq-bridge
xiaomengxinbb/dsh-qq-bridge · ★ 7
Two-way QQ bridge for DeepSeek Harness using QQ Bot API v2, with private and group chats backed by isolated, persistent agent sessions.
README body is shown in Chinese.
安装
dsh plugin --profile web add github:xiaomengxinbb/dsh-qq-bridgeGitHub 源码安装:首次需按提示配置 allowBuilds 构建授权后重试
For install and environment setup, plus plugin development tutorials, see DSH community docs ↗
README
Table of contents
将 QQ 接入 DeepSeek Harness 的双向桥插件——通过 QQ 官方机器人 API v2(私聊 + 群聊), 让你直接在 QQ 里驱动 DeepSeek Harness 的 Agent:每个 QQ 对话拥有独立、持久的隔离 Agent 会话, 像在 Web 里一样使用完整的工具链、模型切换与工作区。
✨ 特性
- 🔌 零依赖网关:QQ 官方 WebSocket 协议(token 预刷新 / 心跳假死检测 / 指数退避重连 / Resume 补发),仅用 Node 内置能力
- 🧊 隔离会话:每 QQ 对话 ↔ 一个持久 DSH Agent(
agents.create/resume),历史按 (对话, 工作区) 隔离,重启自动恢复 - ⚡ steering 插嘴:任务运行中继续发消息,立即注入下一步骤(DSH 原生)
- 📚 完整命令体系:
/help /status /model /thinking /new /sessions /resume /compact /stop /workspace+ 键盘按钮 - 🔐 首访审批:未授权用户自动生成审批码,管理员一键授权(支持普通用户/管理员两级)
- 🖼️ 多媒体:图片直入视觉模型、语音 ASR/STT、TXT/PDF 有界提取;安全下载(SSRF 防护)
- 📤 出站文件:Agent 可调用
qq_send_local_file把本地文件发回 QQ(白名单 + 硬链接/竞态防护) - 🗂️ 多工作区:QQ 侧
/workspace切换目录,会话历史按工作区隔离 - ✅ 实测可用:116 个单测 + 真实 QQ 沙箱文本闭环验证
移植自 pi-qq-bridge(Apache-2.0): 宿主无关模块(网关/路由/命令/媒体/格式化)原样复用;宿主绑定层(会话创建/工具/命令)改为 DSH 官方 API。
架构
QQ 平台 WS 事件
→ src/gateway/qq-gateway.ts(状态机/心跳/重连/Resume)
→ src/router.ts(去重 → 白名单/审批 → 命令 | FIFO 队列 → 隔离会话)
→ src/session/qq-session.ts(DSH 适配:ctx.agents.create/resume + followup/whenIdle)
→ 最终文本 → src/reply-formatter.ts(Markdown 分块 → 降级纯文本)→ QQApi 发送
| 模块 | 说明 |
|---|---|
src/gateway/ |
token 管理 / WS 网关 / REST 发送与上传(宿主无关,原样移植) |
src/session/ |
DSH 隔离会话:每 QQ 对话 ↔ 一个持久 DSH agent(sessionId qq-<hash>-<seq>,cwd = 桥工作区);注册表懒创建/回收/工作区切换 |
src/router.ts |
消息路由、steering 插嘴、回复预算(宿主无关) |
src/commands/ |
QQ 侧命令、授权矩阵、审批码、键盘(宿主无关) |
src/media/ |
附件安全下载/嗅探/提取/STT/出站媒体(宿主无关;图片经 ctx.attachments) |
src/core/ |
配置(schemaVersion 4 严格校验)/ 类型 / 错误码(宿主无关) |
关键宿主 API(详见 HOST-API.md):
- 会话:
ctx.agents.create({sessionId, meta:{cwd}, agentOptions, setup})/ctx.agents.resume({resumeSessionId}) - 运行:
agent.followup(createUserMessage(...))+agent.whenIdle()+ 事件摘要(官方范式,见 dsh-headless) - 插嘴/中止:
agent.steer/agent.cancel({kind:'user'}) - 模型:
ctx.agentDefaultModel+installModelSelection;ctx.llm.listProviders/listModels - 工具:
ctx.tools.register(defineTool(...))(agent 作用域,QQ 会话专属qq_send_local_file) - 命令:
ctx.commands.register(全局,Web UI 可见) - 图片:
ctx.attachments.saveImage→ ImageBlock
安装
开发/冒烟(dev profile,不碰运行中的 GUI)
# 1. 插件依赖(typescript/@types/node + unpdf)
cd ~/dsh-qq-bridge && pnpm install
# 2. dev profile(已存在 ~/.dsh/profiles/dev,bundles: dsh-base + dsh-headless)
dsh plugin --profile dev add ~/dsh-qq-bridge
# 3. 冒烟:headless 任务 + 插件 overlay
dsh --profile dev --patch ~/dsh-qq-bridge/dev-overlay.yml 'Reply with exactly: OK'
# 验证:qqbotdsh/.boot-marker 出现(apply 已执行)
挂载到 web profile(正式使用;需重启 dsh web)
dsh plugin --profile web add ~/dsh-qq-bridge
# 编辑 ~/.dsh/profiles/web/cordis.patch.yml 追加:
# - insert:
# - id: dsh-qq-bridge
# name: 'dsh-qq-bridge'
# 重启 dsh web(注意:这是你正在用的 GUI 服务器)
配置
cp config.example.json ~/.dsh/qq-bridge/config.json
chmod 600 ~/.dsh/qq-bridge/config.json
# 填入 appId / clientSecret;sandbox 保持 true
字段与 pi-qq-bridge 一致(schemaVersion 4):allowUsers / allowGroups / workspaces /
commands / sessions / replyFormat / progress / media / outboundMedia 等。
Windows 原生部署适配
在 Windows 原生(非 WSL)环境部署时注意以下三点(对应 PR #6/#8):
1. 工作区避开 C:/Users/...(含 Temp)
Windows 的 windows-acl 沙箱要求临时目录(temp root)在会话工作区之外;若默认工作区落在
C:/Users/<user>(其子目录通常包含 Temp),会触发沙箱冲突
(temp root must be outside the workspace)。
解决:在配置里显式指定一个非 home 的工作区,插件会自动优先选用它作为 QQ 会话的默认工作区:
{
"workspaces": [
{ "name": "default", "path": "" },
{ "name": "work", "path": "D:/dev/qq-workspace" } // 非 C:/Users 的目录
]
}
(插件逻辑:默认工作区优先取配置中第一个 path 非空且不以 c:/users/ 开头的 workspace。)
2. 出站文件 allowedRoots 白名单
qq_send_local_file 工具只允许发送 allowedRoots 列出的目录(OS 临时目录恒可用)。
Windows 路径请写绝对路径,例如:
{
"outboundMedia": {
"enabled": true,
"allowedRoots": ["D:/dev/qq-workspace", "C:/data/shared"]
}
}
注意:路径校验走 realpath,符号链接/硬链接会被拒绝;不允许用 C:/Users/... 作为出站根目录
(与沙箱策略冲突)。
3. sandbox 策略说明
- 会话沙箱工作目录 = 会话工作区(见第 1 点),写入仅限工作区内;越界写入需审批(审批桥见 PR #3)
windows-acl沙箱在 Windows 原生环境启用;WSL/Linux 下为普通目录权限模型- 若使用正式环境(
sandbox: false),以上沙箱限制由平台侧接管,出站文件仍需allowedRoots白名单
本地命令(Web 聊天里输入,注册于 ctx.commands)
| 命令 | 说明 |
|---|---|
/qqbot-start / /qqbot-stop |
启动/停止 QQ 网关 |
/qqbot-status |
网关/会话/队列/配置/锁状态 |
/qqbot-reconnect |
强制重连 |
/qqbot-requests |
待审批访问申请列表 |
/qqbot-approve <码> <user|admin> [--yes] |
批准申请(admin 需 --yes 二次确认) |
/qqbot-deny <码> |
拒绝申请(1h 冷却) |
/qqbot-revoke <openid> [--yes] |
撤销权限 |
/workspace [名称] | add <名称> <路径> | remove <名称> |
工作区管理 |
QQ 侧命令(发给机器人)
/help /status /last /model /thinking /new /sessions /resume
/name /compact /stop /workspace(管理命令需 commands.admins)
测试
npm run typecheck # tsc --noEmit
npm test # 116 个测试(node:test;网关测试用本地 mock QQ 平台,含真实 WS 协议)
真宿主自测(无需 QQ 凭据)
在纯 dsh-base 的 dev-int profile 里跑(不要用 headless profile——headless 任务完成后会关停整棵树, 与自测赛跑导致 agent 被 dispose):
# 一次性准备
dsh plugin --profile dev-int add ~/dsh-qq-bridge
# 每次验证
DSH_QQBRIDGE_SELFTEST=1 dsh --profile dev-int --patch ~/dsh-qq-bridge/dev-overlay.yml
cat ~/dsh-qq-bridge/.selftest-result.json # ok: true = 全链路通过
覆盖:agents.create(sessionId/cwd/setup)→ 真模型两轮调用 → 持久化 → 跨进程恢复 → newSession → resume → 命名。
全链路集成测试(mock QQ 平台 + 真 DSH 宿主 + 真模型)
无需 QQ 凭据即可验证完整业务闭环(WS 网关 ↔ 路由 ↔ 隔离会话 ↔ 模型 ↔ 回复):
# 一次性准备
cd ~/dsh-qq-bridge && pnpm install
cd ~ && dsh plugin --profile dev-int add ~/dsh-qq-bridge/scripts/integration-driver
# 每次验证(mock 固定端口 18432/18433)
QQBOT_CONFIG_PATH=~/dsh-qq-bridge/scripts/integration-config.json \
QQBOT_API_BASE=http://127.0.0.1:18432 \
QQBOT_TOKEN_URL=http://127.0.0.1:18432/app/getAppAccessToken \
dsh --profile dev-int --patch ~/dsh-qq-bridge/int-overlay.yml
cat ~/dsh-qq-bridge/.integration-result.json # ok: true = 闭环通过
覆盖:网关握手/心跳 → C2C 消息注入 → 白名单 → 队列 → 真 DSH 会话 → 真模型调用 →
Markdown 格式化 → 被动回复回传。测试期环境变量:QQBOT_CONFIG_PATH / QQBOT_API_BASE /
QQBOT_TOKEN_URL(mock 平台注入,不影响正式运行)。
🙏 致谢
本项目在设计与实现过程中参考了以下开源项目(协议处理、架构思路与安全设计深受启发),代码为独立实现:
- pi-agent-qqbot(Apache-2.0)— 原生会话绑定方案、被动回复预算(ReplyBudget)、网关所有权转移
- @xsqm/pi-qqbot(Apache-2.0)— 隔离 AgentSession 架构(本项目骨架来源)、附件安全下载管线、语义分块回复格式
- pi-qq-integration(MIT)— 轻量 WebSocket 客户端与 Access Token 管理参考
本插件直接移植自 pi-qq-bridge(Apache-2.0),其宿主无关模块(网关/路由/命令/媒体/格式化)原样复用。
状态与验证进度
- ✅ 沙箱 mock 全链路单测(116 个)
- ✅ dev profile 装载冒烟(apply/命令注册/网关生命周期)
- ✅ 真实 QQ 沙箱文本闭环(私聊 C2C:消息 → 隔离会话 → 模型回复 → 送达 QQ)
- ⚠️ 分片上传协议字段、op9 4009 行为、Markdown 拒绝特征——以上线实测为准
群聊支持状态(重要)
代码层面完整支持群聊(GROUP_AT_MESSAGE_CREATE 意图、allowGroups 白名单、群回复),
但沙箱环境无法实测:QQ 开放平台沙箱要求把测试群加入沙箱配置的白名单,
个人开发者账号无法在沙箱中配置群聊测试(平台限制,非代码问题)。
群聊接入正式环境的步骤:
- 机器人应用通过平台提审上线(
sandbox: false) - 把机器人拉入目标群
- 获取群 openid(机器人入群后,群内 @ 机器人一次,从网关日志的
[router] 入站 ... group=<openid>行取得) - 把群 openid 加入配置
allowGroups,重启桥 - 群内 @ 机器人即可对话
开发期开关(默认全部关闭)
| 环境变量 | 作用 |
|---|---|
DSH_QQBRIDGE_SELFTEST=1 |
apply 时运行真宿主自测(src/dev/self-test.ts,结果写 .selftest-result.json) |
DSH_QQBRIDGE_BOOT_MARKER=1 |
写开发期冒烟标记 .boot-marker |
QQBOT_DEBUG_START=1 |
启动诊断写 /tmp/qq-start-debug.log |
QQBOT_CONFIG_PATH / QQBOT_API_BASE / QQBOT_TOKEN_URL |
测试/集成环境覆盖(mock 平台) |
开发期 node_modules/@deepseek-ai 是指向 ~/.dsh/profiles/node_modules/@deepseek-ai 的符号链接(保证与宿主单一拷贝;发布版由 peerDependencies 解析)。