Bundle
dsh-plugin-im-bridge
把 QQ / 微信等 IM 接成 DSH 的对话入口:手机上给机器人发消息,电脑上的 agent 干活(QQ 与微信可收发并接 agent,企微 / 飞书 / 钉钉目前只能收)。
- Source
- eighteentang
- License
- MIT
- Updated
- Updated 7 days ago
Readme
# dsh-plugin-im-bridge
把 **IM 机器人**接成 DSH 的对话入口:**手机上给机器人发消息,电脑上的 agent 干活。**
DSH 是一个能读写文件、执行命令、跑长任务的编码 agent。
这个插件让它在手机上有个"嘴"和"耳朵" —— 出门在外也能给它派活。
```
你的手机 你的电脑
┌──────────────┐ ┌───────────────────────────────┐
│ 给机器人 │ 平台服务器 │ DSH 客户端 │
│ 发一条消息 │ ──────────▶ │ └─ 本插件 │
│ │ │ └─ 专用会话(有全套工具) │
│ 收到回答 │ ◀────────── │ 改文件 / 跑命令 /… │
└──────────────┘ └───────────────────────────────┘
```
---
## 零、现在支持哪些平台
| 平台 | 状态 | 怎么连 |
|---|---|---|
| **QQ** | ✅ 完整可用 | 收发 + agent。要自己申请一个 QQ 机器人(见下) |
| **微信** | ✅ 完整可用 | 收发 + agent。走官方 iLink / ClawBot 通道,**扫码即可,不用申请凭据** |
| 企业微信 | ⚠️ 只能收 | 官方长连接已接;**回复帧格式官方未公开**,发不回去 |
| 飞书 | ⚠️ 只能收 | 入站已接;出站未实现,且要另装约 30 MB 的官方 SDK |
| 钉钉 | ⚠️ 只能收 | 入站已接;出站未实现(Stream 通道本身不能回复) |
**设置页里每个平台都如实标注完成度** —— 没做完的不会假装能用。
### 内核与平台无关,只有传输层是各平台专属的
```
共用内核:队列 / 专用会话 / 会话面板 / 权限预设 / 回复分派
↑ 这些一份代码,所有平台都用
传输层 :QQ(WebSocket 长连接)/ 微信(HTTP 长轮询)/ …各自一份
```
所以新接一个平台=只写它的传输层,内核不用动
(企微/飞书/钉钉的入站就是这么接上的)。
---
## 一、能做什么
| 场景 | 说明 |
|---|---|
| **远程派活** | 在手机上让电脑上的 agent 干活,回到电脑看结果 |
| **随手查** | 让它读某个文件、列目录、搜代码,结果直接回到手机上 |
| **长任务** | 发起一件事然后走开,回来在 DSH 里看完整过程 |
| **双向对话** | 不只是"发指令" —— 它会回话、会追问、会报告进度 |
| **发图 / 语音 / 文件** | 手机上发一张截图或文件 ✱,它会存在电脑桌面上并把路径交给 agent —— agent 能直接读那张图 |
> ✱ **当前平台覆盖**:QQ 支持图文 / 语音 / 文件的**收发**;
> 微信支持**收**(图片解密、语音取自带转写),出站待做。
> 附件落在 `<你的桌面>/im-bridge-media/<平台>/<日期>/`,文件名会做清洗。
**每个平台各有一条独立的专用会话**(QQ 和微信互不干扰),
也和你电脑上正在进行的会话互不干扰。
---
## 二、开始之前:准备凭据
**两条路,选一条就行:**
### 路线 A:微信(最省事,不用申请任何东西)
只要手机微信是 **8.0.70 或更高**,并且「我 → 设置 → 插件」里能看到
**「微信 ClawBot」**。连的时候会出现一个二维码,扫码即可。
> 这条路的凭据(`bot_token`)存在本机,**重启客户端不用重扫**。
### 路线 B:QQ(要自己申请,约 15 分钟)
**这一步只能你自己做**(要实名、要用你自己的 QQ 号)。
### 1. 注册并创建机器人
去 **https://q.qq.com** → 登录 → 创建机器人应用。
> ⚠ 需要**实名认证**。个人开发者可以创建,但要走审核。
### 2. 拿到 AppID 和 AppSecret
创建完成后,进机器人后台 → **「开发设置」** 页面,里面有:
```
AppID —— 一串数字,例如 1903121969
AppSecret —— 一串字母数字,**不要外传**
```
> **AppSecret 等于你机器人的密码。** 泄露了别人就能用你的机器人。
> 本插件把凭据存在**本机**(`~/.dsh/.credentials.yaml`),不会上传到任何地方。
### 3. 把你自己加进沙箱白名单(⚠️ 最容易漏、漏了会静默失败)
在机器人后台 → **「沙箱配置」** → 找到 **「消息列表单聊」** → **把你的 QQ 号加进去**。
**不加会怎样**:机器人**收不到任何消息,而且不报错** ——
你会以为插件坏了,其实消息根本没到。这是最常见的"装好了但没反应"。
### 4. 想上正式环境?多半不行(要知道为什么)
插件**默认走沙箱环境**,原因很实际:
| | 沙箱 | 正式 |
|---|---|---|
| 网关 | `sandbox.api.sgroup.qq.com` | `api.bot.qq.com` |
| 限制 | 只有白名单里的 QQ 号能用 | **要配置 IP 白名单** |
| 家用宽带 | ✅ 能用 | ❌ **没有固定公网 IP,过不去** |
**所以家用宽带就用沙箱** —— 你自己用完全够。要上线给很多人用,
得有固定公网 IP 的服务器。
---
## 三、安装插件
### 前提
已经装好 DSH 并能正常对话(能登录、能发消息)。
### 方式一:在 DSH 界面里装(推荐,不用命令行)
1. 打开 DSH 侧边栏的 **插件** 页
2. 点 **「添加插件」**
3. 在「包名或地址」里填**这个插件的 GitHub 地址**:
```sh
github:eighteentang/dsh-plugin-im-bridge#v1.2.7
```
4. 确认安装,然后**重启 DSH 客户端**
> ⚠ **必须填完整地址,不能只填 `dsh-plugin-im-bridge`。**
> 那个输入框的引导会提示"填插件包名" —— 但这个插件**没有发布到 npm**,
> 只发在 GitHub 上。只填包名会被拒(找不到该包)。
>
> 界面原文是「输入插件的包名、**GitHub 仓库地址**或本地目录路径」——
> 我们属于第二种。
> ⚠ **GitHub 地址不经过 npm 安装源**(这是 DSH 界面自己的提示):
> 「GitHub 地址和 .tgz 直链不经过安装源,需要本机能直接访问它或配置代理」。
> 所以如果卡住/超时,先确认这台机器**能访问 github.com**(或已配代理)。
>
> 如果你所在网络访问 GitHub 困难,就只能先用下面的命令行方式配好代理再装。
### 方式二:命令行安装(等价,适合脚本/远程)
DSH 自带插件管理命令:
```sh
dsh plugin --profile <你的profile名> add github:eighteentang/dsh-plugin-im-bridge#v1.2.7
```
> **`--profile` 的名字**:填**你自己的** profile 名 —— 看 `~/.dsh/profiles/`
> 下的目录名就是它。
>
> ⚠ `web` 是 **DSH 随附的模板名**,不必然是你的 profile 名。
> 用 `dsh --profile <名字> --from-default-profile <模板>` 新建过的 profile
> 会是别的名字(例如 `desktop`)。**写错名字 = 装到另一个 profile,当前客户端看不到。**
### 验证装上了
**重启后**(两种方式都要重启)DSH 客户端里应该出现:
```
· 设置 → 连接 IM ← 配置凭据的地方
· 侧边栏一个企鹅图标 ← 点开是 QQ 会话面板
```
**看不到?** 检查三件事:
1. 插件装到了**正确的 profile**(命令行:`dsh plugin --profile <名字> list`)
2. 客户端**真的重启了** —— 只禁用再启用不够,Node 的模块缓存会让它继续用旧代码
3. 界面装的:那一步是否真的提示成功(失败时界面会显示原因)
### 关于版本
上面两种方式都**推荐带 `#v1.2.7`** —— 不写标签装的是 `main` 的**最新提交**,
上游随时可能变。
```sh
# 装某个具体版本(把 v1.2.4 换成你要的)
github:eighteentang/dsh-plugin-im-bridge#v1.2.7
# 看有哪些版本可选
# https://github.com/eighteentang/dsh-plugin-im-bridge/tags
```
> ⚠ **DSH 界面里装的插件暂不支持自动更新**(界面原文):
> 「插件安装后,暂不支持自动更新。若需升级,请先卸载再安装新版」。
> 所以升级要手动重来一次 —— 换成新的 `#vX.Y.Z` 重装。
### ⚠️ 版本注意(微信用户)
> **请用 `v1.2.2` 或更新**。
> `v1.2.1` 及更早的版本发微信回复时**缺一个 `client_id` 字段** ——
> 表现是:日志显示发送成功(HTTP 200),但微信里**只有第一条消息出现**,
> 之后每条都收不到。`v1.2.2` 修的就是这个。
**每个版本改了什么**:见 [CHANGELOG.md](./CHANGELOG.md)。
### 升级
```sh
dsh plugin --profile <你的profile名> add github:eighteentang/dsh-plugin-im-bridge#v1.2.7 # 换成的版本号
```
然后**重启客户端**。
> **升级不会丢凭据。** 插件在启动时会自动把旧键名的凭据迁移到新键名
> (`credential-migrated` 那条日志就是它干的)。
>
> **但升级可能会丢会话历史** —— 如果新版本换了内部会话 id 前缀。
> 那种情况 `CHANGELOG.md` 里会写明。会话历史丢了不影响功能,
> 只是 QQ 那条线的上下文从头开始。
### 我装的是哪个版本?
看 profile 里那份 `package.json` 的 `dsh-plugin-im-bridge` 字段:
```sh
# 装了标签版 → 显示 github:eighteentang/dsh-plugin-im-bridge#v1.2.7
# 装了浮动版 → 显示 github:eighteentang/dsh-plugin-im-bridge
cat ~/.dsh/profiles/<profile名>/package.json
```
---
## 四、填凭据并连接
打开 **设置 → 连接 IM**,每个平台一行:开关、完成度、连接状态、凭据都在那一行里。
点「配置/测试」展开,填凭据,再点「保存并测试连接」。
**凭据保存在哪**:本机 `~/.dsh/.credentials.yaml`,记录名 `im-bridge/bot`。
**设置页不会回显 AppSecret** —— 这是有意的,它是密钥不是普通配置。
配好之后那页会显示「凭据已保存 · AppID 尾号 XXXX」,点「重新设置」才能改。
### 连接状态怎么看
设置页顶部和侧边栏面板都会显示状态。也可以直接查本机的状态接口:
```sh
curl http://127.0.0.1:8799/im-bridge/status
```
返回 `"connected": true` 和 `"bot": "你的机器人名"` 就对了。
**接口只监听回环地址(127.0.0.1)** —— 外面的机器访问不到。
---
## 五、试第一条消息
**QQ**:用你在第二章加进白名单的那个 QQ 号,给机器人发一条。
**微信**:直接在微信里给「微信 ClawBot」发一条。
```
帮我看看 D 盘有哪些文件
```
**正常的话**:它会真的去列目录,然后把结果回到你手机上。
**没反应?** 按这个顺序查:
| 症状 | 最可能的原因 |
|---|---|
| 完全没回复(QQ) | ① 你的 QQ 号**没加进沙箱白名单**(第二章路线 B 第 3 点);② 设置页显示未连接 → 凭据错 |
| 完全没回复(微信) | 设置页那行是否显示「已连接」;**且插件版本必须 ≥ `v1.2.2`** —— 更早的版本发微信回复缺一个字段,只有第一条能显示 |
| 回复"我没有工具" | 会话的工具没挂上 —— 看下面的「已知问题」 |
| 回复"没有 API key" | 模型路由没配对 —— 看下面的「模型配置」 |
| 回了一句就卡住 | 上一轮还在处理;等一会儿或再发一条 |
---
## 五·二、发图片 / 语音 / 文件
**直接在聊天里发就行**,不用打任何命令。
附件会落在**你的桌面**:
```
<桌面>/im-bridge-media/<平台>/<日期>/<文件名>
例如 C:\Users\你\Desktop\im-bridge-media\weixin\2026-10-13\照片.jpg
```
插件把**本地路径拼进给 agent 的消息**里,所以你可以直接说:
```
看看我刚发的那张图,告诉我上面写的什么
```
agent 会自己去读那个文件(它有 `read_image` 等工具)。
| 平台 | 收(你发给机器人) | 发(机器人回给你) |
|---|---|---|
| **QQ** | ✅ 图文 / 语音 / 文件 | ✅ 图文 / 语音 / 文件(单文件 ≤ 5 MB)|
| **微信** | ✅ 图片(自动解密)/ 语音(取自带转写)| ⏳ 出站待做 |
**几个已知边界(如实说)**:
- **QQ 单文件上限 5 MB** —— 超过会**明确报错**,不会硬传一个被截断的文件。
分片上传还没做。
- **微信语音不下载音频**,只取微信自带的转写文字(本机没有 ffmpeg,silk 转不了码)。
- **文件名会被清洗**(剥掉路径分隔符与 Windows 非法字符)——
附件名是对方给的,不能直接信。
- **图片解密失败不静默**:会在旁边留一份密文原件并记事件,方便事后查。
> 想换落地目录?目前写死在桌面。改的话在 `media.js` 的 `mediaDirFor`。
> (为什么不做成配置项:还没有"要换"的真实需求,多一个口子就多一处要维护。)
---
## 六、模型配置(可能需要你改)
插件让各平台的专用会话用**你 DSH 里设置的默认模型**。但有个坑:
DSH 内置的默认是 `deepseek-official`(**需要 `DEEPSEEK_API_KEY`**)。
如果你是用**账号登录**(没有 API key),插件会**回退到 `deepseek-account`**,
这通常是对的。
**如果机器人回"no API key for provider route"**,就在插件的配置里显式指定:
```yaml
# profile 的 cordis.patch.yml 里,im-bridge 那一行
- id: im-bridge
name: dsh-plugin-im-bridge
config:
agentProvider: deepseek-account
agentModel: deepseek-flash
```
| 配置项 | 说明 |
|---|---|
| `agentProvider` | 模型服务商。账号登录用 `deepseek-account` |
| `agentModel` | 模型 id,例如 `deepseek-flash` |
| `agentReasoningEffort` | 思考强度,例如 `high` / `max` |
| `workspace` | 各平台专用会话的工作目录(默认 DSH 的工作目录) |
| `sandbox` | 是否连 QQ 沙箱环境(默认 `true`;只管 QQ) |
| `permissionPreset` | 专用会话的权限预设(默认 `danger-full-access`) |
| `agentSessionId` | **覆盖 QQ 的专用会话 id**(默认留空 = 用内置值)|
### 什么时候要改 `agentSessionId`
模型会把**历史里关于自身能力的错误结论**继承下去 —— 那种情况下改代码没用,
**得换一个全新会话**(换上下文)让修好的能力真正生效。
⚠ **但绝不要改内置默认值那个思路**:默认值是**所有用户共享**的,改掉它等于让每个装这个
插件的人一起换会话、丢历史。正确做法是在**自己的 profile patch** 里设:
```yaml
- id: im-bridge
config:
agentSessionId: "im-bridge-dedicated-v2"
```
**换 id 是非破坏性的**:旧会话数据仍完整留在 `~/.dsh/sessions/.../im-bridge-dedicated-v1/`,
只是不再被 resume。想回去,把那一行删掉即可。代价是新会话读不到旧对话历史。
> ⚠ **必须重启客户端才生效**(插件代码不热加载)。
---
## 七、安全:这个插件权限很大,务必读完
### ⚠️ 它把电脑的操作能力交给了"能给机器人发消息的人"
装好之后,**能私聊你这个机器人的人,就能在你的电脑上执行命令**。
**默认配置是 `permissionPreset: danger-full-access`**(无需审批)——
因为专用会话是**无人值守**的:审批框弹在电脑桌面上,你在手机上根本看不到,
如果要用"每次审批",机器人会直接卡死。
**所以你必须做一件事**:去 QQ 开放平台确认**谁能私聊这个机器人**。
如果是公开可加的,任何找到它的人都能操作你的电脑。
### 收紧权限的办法
```yaml
- id: im-bridge
name: dsh-plugin-im-bridge
config:
# 只能读,不能改
permissionPreset: read-only
```
可能的取值:`read-only` / `workspace-write` / `danger-full-access`。
> **注意**:`workspace-write` 在部分机器上**沙箱初始化会失败**
> (`SetNamedSecurityInfoW failed (Win32 5)`),导致所有命令都跑不起来。
> 如果遇到,用 `danger-full-access` 或 `read-only`。
### 凭据安全
- AppSecret **不要贴到任何聊天里**(包括给 AI 看的对话)
- 泄露了就去对应平台的开发者后台**重置**(QQ 是 AppSecret;微信是重新扫码换 token)
- 凭据只存在本机 `~/.dsh/.credentials.yaml`
---
## 八、它是怎么工作的
理解这个能帮你排查问题。
```
QQ 开放平台
│ WebSocket 长连接(插件主动连出去,不需要公网 IP)
▼
im-bridge(Host 插件,跑在 DSH 进程里)
│ ① 收到消息 → 排队(避免两条消息合并成一个回合)
│ ② 交给一个**专用会话**处理
▼
专用会话(agent,带全套工具:读写文件 / 执行命令 / 搜索 /…)
│ ③ 干完活
▼
im-bridge → 回手机(各平台各自的回复通道)
```
**几个设计点**(都是踩坑踩出来的):
| 设计 | 为什么 |
|---|---|
| **消息排队** | agent 会把收件箱里的消息**合并处理** —— 两条消息只回一个。排队保证"一条消息一个回答" |
| **每个平台一条专用会话** | 各平台的线**互相独立**、也独立于你在 GUI 里用的会话。以 `origin: subagent` 创建,**不出现在会话列表里** |
| **看门狗** | 上一轮如果卡住(超过 45 秒),自动解封并把排队消息放出去 —— 否则会永久卡死 |
| **被动→主动回复回退** | QQ 的被动回复有 `msg_id` 限制,失效时自动改用主动推送(微信则是每条消息带 `context_token` 回复) |
| **凭据自动迁移** | 插件改过包名/凭据键名;升级时会自动把旧凭据搬过来,你不用重填 |
**状态日志**:`~/.dsh/im-bridge-status.log`(JSON 行格式,排查问题的第一手材料)
---
## 九、卸载
```sh
dsh plugin --profile <你的profile名> remove dsh-plugin-im-bridge
```
> **已知问题**:卸载后 `~/.dsh/profiles/<名字>/node_modules/` 下可能残留一个
> 名叫 `dsh-plugin-im-bridge` 的死链接(junction)。
> **不影响使用**(插件的加载清单已经清干净了),但可以手动删掉。
---
## 十、已知问题
| 问题 | 状态 |
|---|---|
| 用 `select` 绑 preset 会让 resume 后的会话**丢掉几乎全部工具**(能回话但干不了活) | 已修(改用 `mount`) |
| `workspace-write` 沙箱在部分机器上初始化失败,所有命令报 `grantWrite Win32 5` | 规避:用 `danger-full-access` 或 `read-only` |
| 卸载后 junction 残留 | 已知,不影响使用 |
| 需要人工审批的权限预设会让专用会话**卡死**(审批框在电脑上,手机看不到) | 默认已避开(用 `danger-full-access`) |
| 会话历史被"模型自己的错误结论"污染后,改代码也没用,得换会话 id | 已修(换代会话 id) |
---
## 十一、许可与版本
MIT —— 见 [LICENSE](./LICENSE)。
**当前版本**:见 [CHANGELOG.md](./CHANGELOG.md) 最上面那条。
**钉版本 / 升级**:见上面的「钉住版本(推荐)」一节。
**这个插件不是 DeepSeek 官方项目**,是第三方插件。
各平台的接口用法来自腾讯 / 飞书 / 钉钉的公开文档。
Install
dsh plugin --profile web add github:eighteentang/dsh-plugin-im-bridge
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-plugin-im-bridge from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.