@zaofan/dsh-qqbot 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +308 -0
- package/README_EN.md +273 -0
- package/client/qqbot-settings.js +1463 -0
- package/cordis.patch.yml +36 -0
- package/dist/api/group-admin.d.ts +112 -0
- package/dist/api/group-admin.d.ts.map +1 -0
- package/dist/api/group-admin.js +179 -0
- package/dist/api/group-admin.js.map +1 -0
- package/dist/channel-tools.d.ts +14 -0
- package/dist/channel-tools.d.ts.map +1 -0
- package/dist/channel-tools.js +880 -0
- package/dist/channel-tools.js.map +1 -0
- package/dist/commands/help.d.ts +11 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +21 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/commands/index.d.ts +13 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +29 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/misc.d.ts +14 -0
- package/dist/commands/misc.d.ts.map +1 -0
- package/dist/commands/misc.js +39 -0
- package/dist/commands/misc.js.map +1 -0
- package/dist/commands/model.d.ts +7 -0
- package/dist/commands/model.d.ts.map +1 -0
- package/dist/commands/model.js +58 -0
- package/dist/commands/model.js.map +1 -0
- package/dist/commands/session.d.ts +10 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +26 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/status.d.ts +8 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +24 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/types.d.ts +12 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +2 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/config.d.ts +205 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +203 -0
- package/dist/config.js.map +1 -0
- package/dist/features/chat-ledger.d.ts +53 -0
- package/dist/features/chat-ledger.d.ts.map +1 -0
- package/dist/features/chat-ledger.js +185 -0
- package/dist/features/chat-ledger.js.map +1 -0
- package/dist/features/face-tags.d.ts +17 -0
- package/dist/features/face-tags.d.ts.map +1 -0
- package/dist/features/face-tags.js +79 -0
- package/dist/features/face-tags.js.map +1 -0
- package/dist/features/group-join-request.d.ts +65 -0
- package/dist/features/group-join-request.d.ts.map +1 -0
- package/dist/features/group-join-request.js +190 -0
- package/dist/features/group-join-request.js.map +1 -0
- package/dist/features/history-store.d.ts +8 -0
- package/dist/features/history-store.d.ts.map +1 -0
- package/dist/features/history-store.js +24 -0
- package/dist/features/history-store.js.map +1 -0
- package/dist/features/qq-approval.d.ts +78 -0
- package/dist/features/qq-approval.d.ts.map +1 -0
- package/dist/features/qq-approval.js +165 -0
- package/dist/features/qq-approval.js.map +1 -0
- package/dist/features/schedule-store.d.ts +62 -0
- package/dist/features/schedule-store.d.ts.map +1 -0
- package/dist/features/schedule-store.js +253 -0
- package/dist/features/schedule-store.js.map +1 -0
- package/dist/features/scheduler.d.ts +9 -0
- package/dist/features/scheduler.d.ts.map +1 -0
- package/dist/features/scheduler.js +146 -0
- package/dist/features/scheduler.js.map +1 -0
- package/dist/features/sticker-gate.d.ts +90 -0
- package/dist/features/sticker-gate.d.ts.map +1 -0
- package/dist/features/sticker-gate.js +391 -0
- package/dist/features/sticker-gate.js.map +1 -0
- package/dist/features/sticker-store.d.ts +158 -0
- package/dist/features/sticker-store.d.ts.map +1 -0
- package/dist/features/sticker-store.js +676 -0
- package/dist/features/sticker-store.js.map +1 -0
- package/dist/features/sticker-tagger.d.ts +10 -0
- package/dist/features/sticker-tagger.d.ts.map +1 -0
- package/dist/features/sticker-tagger.js +139 -0
- package/dist/features/sticker-tagger.js.map +1 -0
- package/dist/gateway/bootstrap.d.ts +12 -0
- package/dist/gateway/bootstrap.d.ts.map +1 -0
- package/dist/gateway/bootstrap.js +346 -0
- package/dist/gateway/bootstrap.js.map +1 -0
- package/dist/gateway/debounce.d.ts +40 -0
- package/dist/gateway/debounce.d.ts.map +1 -0
- package/dist/gateway/debounce.js +262 -0
- package/dist/gateway/debounce.js.map +1 -0
- package/dist/gateway/index.d.ts +3 -0
- package/dist/gateway/index.d.ts.map +1 -0
- package/dist/gateway/index.js +3 -0
- package/dist/gateway/index.js.map +1 -0
- package/dist/gateway/middleware-setup.d.ts +12 -0
- package/dist/gateway/middleware-setup.d.ts.map +1 -0
- package/dist/gateway/middleware-setup.js +156 -0
- package/dist/gateway/middleware-setup.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +187 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/attachment.d.ts +11 -0
- package/dist/middleware/attachment.d.ts.map +1 -0
- package/dist/middleware/attachment.js +17 -0
- package/dist/middleware/attachment.js.map +1 -0
- package/dist/middleware/media-history.d.ts +30 -0
- package/dist/middleware/media-history.d.ts.map +1 -0
- package/dist/middleware/media-history.js +54 -0
- package/dist/middleware/media-history.js.map +1 -0
- package/dist/middleware/sticker-capture.d.ts +17 -0
- package/dist/middleware/sticker-capture.d.ts.map +1 -0
- package/dist/middleware/sticker-capture.js +97 -0
- package/dist/middleware/sticker-capture.js.map +1 -0
- package/dist/model/index.d.ts +10 -0
- package/dist/model/index.d.ts.map +1 -0
- package/dist/model/index.js +9 -0
- package/dist/model/index.js.map +1 -0
- package/dist/model/model-resolver.d.ts +88 -0
- package/dist/model/model-resolver.d.ts.map +1 -0
- package/dist/model/model-resolver.js +151 -0
- package/dist/model/model-resolver.js.map +1 -0
- package/dist/model/prefs-store.d.ts +24 -0
- package/dist/model/prefs-store.d.ts.map +1 -0
- package/dist/model/prefs-store.js +126 -0
- package/dist/model/prefs-store.js.map +1 -0
- package/dist/model/settings-reader.d.ts +19 -0
- package/dist/model/settings-reader.d.ts.map +1 -0
- package/dist/model/settings-reader.js +76 -0
- package/dist/model/settings-reader.js.map +1 -0
- package/dist/model/types.d.ts +15 -0
- package/dist/model/types.d.ts.map +1 -0
- package/dist/model/types.js +5 -0
- package/dist/model/types.js.map +1 -0
- package/dist/session/idle-evictor.d.ts +18 -0
- package/dist/session/idle-evictor.d.ts.map +1 -0
- package/dist/session/idle-evictor.js +31 -0
- package/dist/session/idle-evictor.js.map +1 -0
- package/dist/session/index.d.ts +9 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +8 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/session-manager.d.ts +97 -0
- package/dist/session/session-manager.d.ts.map +1 -0
- package/dist/session/session-manager.js +624 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/session/types.d.ts +130 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +2 -0
- package/dist/session/types.js.map +1 -0
- package/dist/session/workspace-attach.d.ts +22 -0
- package/dist/session/workspace-attach.d.ts.map +1 -0
- package/dist/session/workspace-attach.js +21 -0
- package/dist/session/workspace-attach.js.map +1 -0
- package/dist/setup.d.ts +25 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +198 -0
- package/dist/setup.js.map +1 -0
- package/dist/shared/index.d.ts +7 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +7 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/scope.d.ts +14 -0
- package/dist/shared/scope.d.ts.map +1 -0
- package/dist/shared/scope.js +12 -0
- package/dist/shared/scope.js.map +1 -0
- package/dist/shared/send-helper.d.ts +11 -0
- package/dist/shared/send-helper.d.ts.map +1 -0
- package/dist/shared/send-helper.js +13 -0
- package/dist/shared/send-helper.js.map +1 -0
- package/dist/shared/utils.d.ts +28 -0
- package/dist/shared/utils.d.ts.map +1 -0
- package/dist/shared/utils.js +143 -0
- package/dist/shared/utils.js.map +1 -0
- package/dist/transport/attachment.d.ts +18 -0
- package/dist/transport/attachment.d.ts.map +1 -0
- package/dist/transport/attachment.js +106 -0
- package/dist/transport/attachment.js.map +1 -0
- package/dist/transport/chunker.d.ts +14 -0
- package/dist/transport/chunker.d.ts.map +1 -0
- package/dist/transport/chunker.js +78 -0
- package/dist/transport/chunker.js.map +1 -0
- package/dist/transport/events.d.ts +80 -0
- package/dist/transport/events.d.ts.map +1 -0
- package/dist/transport/events.js +62 -0
- package/dist/transport/events.js.map +1 -0
- package/dist/transport/inbound.d.ts +8 -0
- package/dist/transport/inbound.d.ts.map +1 -0
- package/dist/transport/inbound.js +292 -0
- package/dist/transport/inbound.js.map +1 -0
- package/dist/transport/index.d.ts +10 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +10 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/inject-rules.d.ts +24 -0
- package/dist/transport/inject-rules.d.ts.map +1 -0
- package/dist/transport/inject-rules.js +72 -0
- package/dist/transport/inject-rules.js.map +1 -0
- package/dist/transport/outbound-buffer.d.ts +56 -0
- package/dist/transport/outbound-buffer.d.ts.map +1 -0
- package/dist/transport/outbound-buffer.js +131 -0
- package/dist/transport/outbound-buffer.js.map +1 -0
- package/dist/transport/outbound.d.ts +30 -0
- package/dist/transport/outbound.d.ts.map +1 -0
- package/dist/transport/outbound.js +139 -0
- package/dist/transport/outbound.js.map +1 -0
- package/dist/transport/rich-media.d.ts +41 -0
- package/dist/transport/rich-media.d.ts.map +1 -0
- package/dist/transport/rich-media.js +104 -0
- package/dist/transport/rich-media.js.map +1 -0
- package/dist/transport/streaming-writer.d.ts +47 -0
- package/dist/transport/streaming-writer.d.ts.map +1 -0
- package/dist/transport/streaming-writer.js +110 -0
- package/dist/transport/streaming-writer.js.map +1 -0
- package/dist/transport/tool-presenter.d.ts +38 -0
- package/dist/transport/tool-presenter.d.ts.map +1 -0
- package/dist/transport/tool-presenter.js +121 -0
- package/dist/transport/tool-presenter.js.map +1 -0
- package/dist/types.d.ts +45 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +106 -0
- package/settings-host.js +927 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tencent Connect
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# @zaofan/dsh-qqbot
|
|
2
|
+
|
|
3
|
+
[](./LICENSE) -blue)
|
|
4
|
+
|
|
5
|
+
基于 [deepseek-harness](https://github.com/deepseek-ai/deepseek-harness) (dsh) 的 QQ Bot IM 插件**增强 fork**:将 QQ 消息平台作为 dsh agent 的前端协议驱动,并加入表情包图库、富媒体收发、定时任务、多实例人格、可视化设置面板等能力。
|
|
6
|
+
|
|
7
|
+
📦 仓库: [gcry13067381632-jpg/dsh-qqbot](https://github.com/gcry13067381632-jpg/dsh-qqbot)(fork 自 [tencent-connect/dsh-qqbot](https://github.com/tencent-connect/dsh-qqbot))
|
|
8
|
+
|
|
9
|
+
中文 | [English](./README_EN.md)
|
|
10
|
+
|
|
11
|
+
## 🐋 本 fork 增强版
|
|
12
|
+
|
|
13
|
+
**一句话**:让跑在 dsh 上的 QQ 机器人"活"起来——会存表情包、会挑图回你、到点主动开口、一个DSH还能同时开好几个不同性格的机器人号。
|
|
14
|
+
|
|
15
|
+
它是上游 [@tencent-connect/dsh-qqbot](https://github.com/tencent-connect/dsh-qqbot) 的增强 fork(改动不在上游源码里,升级/重装上游会被冲掉)。
|
|
16
|
+
|
|
17
|
+
### 装上之后,它能帮你……
|
|
18
|
+
|
|
19
|
+
**🤳 群里的表情包,自动变成它的收藏**
|
|
20
|
+
群里谁发了图,机器人自动存进本地图库(自动去重、分"待整理/收藏/回收站")。你只要说一句"发个开心点的图"——它自己搜库、自己挑、自己发,还会挑场合出手(可配置:冷场不发、刷屏限量、同图不连发)。
|
|
21
|
+
|
|
22
|
+
**⏰ 到点它会自己开口**
|
|
23
|
+
设置里告诉它"每天早 9 点去群里说早安",它就准点出现;跟它说"30 秒后提醒我去喝水",它到点真会来催你。
|
|
24
|
+
|
|
25
|
+
**🧑🤝🧑 一个电脑,多个人格同时在线**
|
|
26
|
+
傲娇系、元气系、高冷系……想开几个开几个:每个号独立 AppID、独立人格、独立工作目录(图库/定时/闸门全分开,互不串号)。新号不用翻教程——Web 页点「扫码绑定」,手机 QQ 扫一下,凭据自动填好。
|
|
27
|
+
|
|
28
|
+
**🛡️ 群主/群管理员的帮手:入群审批 + 禁言**
|
|
29
|
+
机器人为群管理员时,可在设置面板「⑥ QQ 群管理」开总开关:有人申请进群,机器人收实时事件并在群里提醒你,回一句"通过/拒绝"就审批;也能查禁言状态、把人禁言或解禁(全部走官方接口,出错给"人话"提示:不是管理员/不能禁群主等)。
|
|
30
|
+
|
|
31
|
+
**🧹 图库乱?让 AI 自己整理**
|
|
32
|
+
一句话"看看收藏里哪些图还没写介绍",它列出清单、自己补标签补描述,越用越懂你。
|
|
33
|
+
|
|
34
|
+
**🖥️ 不用碰配置文件,全在设置面板点**
|
|
35
|
+
机器人怎么回、能发什么图、什么时候主动说话、每个号用什么人格——都在一个面板里改,保存即生效(账号增减才需重启)。
|
|
36
|
+
|
|
37
|
+
### 📸 效果展示
|
|
38
|
+
|
|
39
|
+
图①:dsh 运行后台——思考过程、工具调用、Token 用量一目了然(配合「回复闸门 reply_gate」可让机器人自主判断该开口还是静默吃瓜);
|
|
40
|
+
图②:QQ 群里的抓鬼游戏互动——该回就回、该藏就藏,角色扮演全自动;
|
|
41
|
+
图③:斗图实战——机器人用自己收藏的表情包接招回击,图、文分开两条连发。
|
|
42
|
+
|
|
43
|
+

|
|
44
|
+
|
|
45
|
+

|
|
46
|
+
|
|
47
|
+

|
|
48
|
+
|
|
49
|
+
### 给开发者的话
|
|
50
|
+
- QQ 会话内可直接调用的标准工具:发图/撤图/查库/打标/查未整理/定时(`send_media`/`recall_message`/`list_stickers`/`sticker_tag`/`sticker_untagged`/`schedule_timer`/`schedule_cancel`…),会话按账号精确路由
|
|
51
|
+
- **群管理工具**(`group_join_requests`/`group_approve_join`/`group_mute_state`/`group_mute_member`…):入群审批与禁言,需机器人为该群管理员;对话内管当前群,web/非群会话用配置的 `manageGroup`
|
|
52
|
+
- **纯文本也能发图撤消息**:让ai再回复里写 `[MEDIA:image|图片路径或网址]` 就自动变成真图发出去(`voice`/`video`/`file` 同理);写 `[RECALL]` 撤回自己刚发的那条
|
|
53
|
+
- 会话归属、工作区挂载等宿主问题已按官方机制修好(移植上游 PR #21,幂等、全 fail-soft)
|
|
54
|
+
|
|
55
|
+
> 🛡️ 仓库**不含**任何机器人凭据、图库数据、日志与个人路径(发布前已清理)。AppID/AppSecret 请走环境变量或 Web 面板注入,**不要提交进 git**。
|
|
56
|
+
|
|
57
|
+
### 构建与部署
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm install # 安装依赖(peer 依赖由 dsh 宿主解析)
|
|
61
|
+
npm run build # 或: node node_modules/typescript/lib/tsc.js -p tsconfig.json
|
|
62
|
+
npm run check:package # 发布前自检(单包四项家当齐全)
|
|
63
|
+
pnpm pack # 打 tarball(供 dsh plugin add 安装)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
安装到 dsh profile 见下方「安装」小节(同样支持 `dsh plugin add` 与扫码引导)。
|
|
67
|
+
仓库根的 `install.ps1` 提供 Windows 一键安装(自动 pack 到无空格目录 → add → 重启提示)。
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 架构
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
QQ 用户 → QQ WebSocket → dsh-im-qqbot → ctx.agents → dsh agent loop → LLM
|
|
75
|
+
↑ │
|
|
76
|
+
└── session/event ──────────┘
|
|
77
|
+
(assistant reply → QQ sendMarkdown)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## 安装
|
|
81
|
+
|
|
82
|
+
> ⚠️ **必须装到 `web` profile**(`dsh web` 设置面板的宿主);装到别的 profile 只会得到没有设置面板的裸环境。
|
|
83
|
+
> 也不要 `add @tencent-connect/dsh-qqbot`——那会装上游官方版(无本 fork 增强功能)。
|
|
84
|
+
>
|
|
85
|
+
> ✅ **单包自含,装一个就全有**:QQ 机器人 + Web 可视化设置面板(host 桥 + 设置页 UI)都打包在
|
|
86
|
+
> 这一个包内——装完它,dsh Web「设置」里就会出现「QQ 机器人 (im-qqbot)」面板(多账号时每个实例各一页),
|
|
87
|
+
> **无需再单独安装 dsh-qqbot-settings**。
|
|
88
|
+
|
|
89
|
+
### 方式一(发布到 npm 后):一条命令
|
|
90
|
+
|
|
91
|
+
```powershell
|
|
92
|
+
npx @deepseek-ai/dsh plugin --profile web add @zaofan/dsh-qqbot
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
> 尚未发布到 npm 前,请用下面的方式二。
|
|
96
|
+
|
|
97
|
+
### 方式二:源码分发(当前推荐)
|
|
98
|
+
|
|
99
|
+
**Windows(一键脚本)**:
|
|
100
|
+
|
|
101
|
+
```powershell
|
|
102
|
+
git clone https://github.com/gcry13067381632-jpg/dsh-qqbot.git
|
|
103
|
+
cd dsh-qqbot
|
|
104
|
+
.\install.ps1 # 自动 install/build → pack → add tarball → 输出重启指引
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
> 若系统禁止运行脚本,改用:`powershell -ExecutionPolicy Bypass -File .\install.ps1`
|
|
108
|
+
|
|
109
|
+
**macOS / Linux(手动)**:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
git clone https://github.com/gcry13067381632-jpg/dsh-qqbot.git
|
|
113
|
+
cd dsh-qqbot
|
|
114
|
+
npm install && npm run build
|
|
115
|
+
pnpm pack --pack-destination /tmp
|
|
116
|
+
npx @deepseek-ai/dsh plugin --profile web add /tmp/zaofan-dsh-qqbot-0.4.0.tgz
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
> 💡 为什么打 tarball、而不是 `add` 源码目录?实测教训:
|
|
120
|
+
> ① 目录路径含空格时 Windows 会把参数在空格处拆碎(pnpm 报 `- isn't supported`);
|
|
121
|
+
> ② `add` 目录 = pnpm link(junction),插件无法按"代码位置"反推 profile → 扫码凭据落不了盘,只能走环境变量。
|
|
122
|
+
|
|
123
|
+
### 排障: npm 安装报 ERESOLVE(2026-09-06 移植上游 PR #42)
|
|
124
|
+
|
|
125
|
+
首次 `npm install` 可能报 `ERESOLVE could not resolve`——原因: `@deepseek-ai/dsh-tools`/`dsh-agent` 等 peer 依赖仍在 prerelease(-rc) 版本线,npm 7+ 严格解析拒绝不相交组合。**这是上游版本线问题,不是插件 bug**,两条绕过路:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npm install --legacy-peer-deps # 仅安装期解析策略, 不改运行行为
|
|
129
|
+
# 或: 装完依赖后手动 build + pack(peer 由 dsh 宿主解析, 不受影响)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
> 跟踪中: 上游 #37 根治后此段可删(版本线收敛后 npm 不再报错)。
|
|
133
|
+
|
|
134
|
+
### 首次启动与绑定
|
|
135
|
+
|
|
136
|
+
启动 `dsh web` 后,若未配置凭据会自动进入**扫码引导**:终端输出二维码 → 手机 QQ 扫码绑定 → 凭据自动保存,重启不丢(设置面板里也可随时「扫码绑定」/改账号)。
|
|
137
|
+
|
|
138
|
+

|
|
139
|
+
|
|
140
|
+
> **提示**:建议使用 `0.4.0` 以上版本扫码,支持点击链接在浏览器打开,避免部分终端二维码渲染错位的问题。
|
|
141
|
+
|
|
142
|
+
### 还没有 QQ 机器人?先注册一个(拿 AppID / AppSecret)
|
|
143
|
+
|
|
144
|
+
1. 打开 [QQ 开放平台](https://q.qq.com),用 QQ 号登录;
|
|
145
|
+
2. 进入「机器人」→「创建机器人」,填好名称、头像、简介;
|
|
146
|
+
3. 创建完成后在机器人详情页拿到 **AppID** 与 **AppSecret**;
|
|
147
|
+
4. 在 dsh Web → 设置 →「QQ 机器人」→「账号与预设」里填入并保存
|
|
148
|
+
(或设为环境变量 `QQBOT_APPID` / `QQBOT_SECRET`);
|
|
149
|
+
5. 按需在平台开通**单聊/群聊**消息权限(群聊一般需要提交用途审核)。
|
|
150
|
+
|
|
151
|
+
> 💡 更省事:机器人建好即可,首次启动直接**扫码绑定**,不用手抄凭据。
|
|
152
|
+
|
|
153
|
+
### 开发者:--patch 开发模式
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
export QQBOT_APPID="你的AppID" QQBOT_SECRET="你的AppSecret"
|
|
157
|
+
npx @deepseek-ai/dsh web --patch /path/to/dsh-qqbot/cordis.dev.yml
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## QQ 远程审批(可选)
|
|
161
|
+
|
|
162
|
+
当 Agent 的工具要访问**工作区之外**的位置时,dsh 会触发权限审批。开启后,机器人把审批请求**发到 QQ**(任务发起者的会话),你直接在 QQ 里放行/拒绝:
|
|
163
|
+
|
|
164
|
+
> ⚠️ **DSH 权限申请**
|
|
165
|
+
> 工具:pwsh
|
|
166
|
+
> 原因:需要访问工作区外路径
|
|
167
|
+
>
|
|
168
|
+
> 允许本次操作:`/approve A1B2C3`
|
|
169
|
+
> 拒绝本次操作:`/deny A1B2C3`
|
|
170
|
+
> 仅本次有效,120 秒后自动拒绝。
|
|
171
|
+
|
|
172
|
+
**启用**(二选一;保存即对新审批请求生效,无需重启):
|
|
173
|
+
- **Web 设置面板**:设置 →「QQ 机器人」→ ⑤ QQ 远程审批 → 勾选开启;
|
|
174
|
+
- 或 `cordis.patch.yml` 的实例 config 加两行后重启:
|
|
175
|
+
|
|
176
|
+
```yaml
|
|
177
|
+
- id: im-qqbot
|
|
178
|
+
config:
|
|
179
|
+
enableApprovals: true # 默认 false
|
|
180
|
+
approvalTimeoutMs: 120000 # 等待时长, 超时自动拒绝
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**安全边界**:验证码一次性;仅"任务发起者本人 + 同一会话"可批(群聊里其他人看到验证码也无效);只授权当前这一次操作;Agent 取消或 dsh 退出自动取消。
|
|
184
|
+
|
|
185
|
+
> 思路来源: wang-22-code/dsh-qqbot-bridge 的 QQ 审批设计(宿主 dsh `approval/request` 标准事件,官方 dsh-acp / Web 审批弹窗同款机制)。
|
|
186
|
+
|
|
187
|
+
## QQ 群管理(可选)
|
|
188
|
+
|
|
189
|
+
机器人**为群管理员**时,可开启群管理能力:实时接收「入群申请」并自动提醒主人、按申请审批入群、查询/设置群成员禁言。所有操作走腾讯官方 GroupOpenMsg 接口,错误信息已做"人话"映射(如 11703=机器人不是该群管理员、40103004=不能禁言群主/管理员、11255=群已注销)。
|
|
190
|
+
|
|
191
|
+
**能力总开关**:
|
|
192
|
+
|
|
193
|
+
- **Web 设置面板**: 设置 →「QQ 机器人」→ ⑥ QQ 群管理 → 勾选开启,并填/选「默认管理群」;
|
|
194
|
+
- 或 `cordis.patch.yml` 的实例 config 加配置后重启:
|
|
195
|
+
|
|
196
|
+
```yaml
|
|
197
|
+
- id: im-qqbot
|
|
198
|
+
config:
|
|
199
|
+
groupAdmin:
|
|
200
|
+
enabled: true
|
|
201
|
+
owners: [] # 主人 openid 白名单(空=不校验)
|
|
202
|
+
manageGroup: "群openid" # 对话内默认管理群(web/非群会话用; 群会话自动取当前群)
|
|
203
|
+
watchJoinRequests: true # 订阅入群申请事件(改后需重启: 涉及连接期 intents)
|
|
204
|
+
notifyInGroup: true # 收到申请时在群内发提醒
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
> ⚠️ `watchJoinRequests` 需要连接期注册 intents(GROUP_MEMBER_EVENT, 1<<24)——**改它必须重启**,不是 live 热改;且需官方对该机器人开放对应能力,否则连接可能被拒(4914/4915)。
|
|
208
|
+
|
|
209
|
+
**入群审批怎么用**: 事件到达 → bot 在群里发一条提醒(含申请人昵称/验证语)→ 你在对话里说"通过/拒绝"(AI 调 `group_approve_join`)→ 官方落库审批。也可以在设置面板「⑥ QQ 群管理 → 入群审批」页看待审批清单手动批。
|
|
210
|
+
|
|
211
|
+
**配置项**(Web 面板 ⑥ 可改, 见下表 `groupAdmin.*`)
|
|
212
|
+
|
|
213
|
+
## 配置项
|
|
214
|
+
|
|
215
|
+
| 配置 | 类型 | 默认值 | 说明 |
|
|
216
|
+
|------|------|--------|------|
|
|
217
|
+
| `appId` | string | **必填** | QQ Bot AppID(或通过 `QQBOT_APPID` 环境变量) |
|
|
218
|
+
| `appSecret` | string | **必填** | QQ Bot AppSecret(或通过 `QQBOT_SECRET` 环境变量) |
|
|
219
|
+
| `provider` | string | `deepseek-official` | LLM 提供商名称 |
|
|
220
|
+
| `model` | string | `deepseek-chat` | 模型名称 |
|
|
221
|
+
| `preset` | string | - | Agent preset id |
|
|
222
|
+
| `cwd` | string | `process.cwd()` | Agent 工作目录 |
|
|
223
|
+
| `requireMention` | boolean | `true` | 群聊是否需要 @bot 才触发 |
|
|
224
|
+
| `groupPrompt` | string | - | 群聊额外 system prompt |
|
|
225
|
+
| `directPrompt` | string | - | 私聊额外 system prompt |
|
|
226
|
+
| `textChunkLimit` | number | `4500` | 单条消息最大字符数 |
|
|
227
|
+
| `sessionIdleTimeout` | number | `1800000` | 会话闲置超时(ms),默认 30 分钟 |
|
|
228
|
+
| `debug` | boolean | `false` | 调试模式 |
|
|
229
|
+
| `groupAdmin.enabled` | boolean | `false` | 群管理总开关(需机器人为群管理员) |
|
|
230
|
+
| `groupAdmin.owners` | string[] | `[]` | 可操作群管理的主人 openid 白名单(空=不校验) |
|
|
231
|
+
| `groupAdmin.manageGroup` | string | `''` | 对话内默认管理群 openid(web/非群会话时用) |
|
|
232
|
+
| `groupAdmin.watchJoinRequests` | boolean | `false` | 订阅入群申请事件(改后需重启) |
|
|
233
|
+
| `groupAdmin.notifyInGroup` | boolean | `true` | 收到申请时在群内发提醒 |
|
|
234
|
+
|
|
235
|
+
> 🔧 新版 Web 面板把群管理单开成「⑥ QQ 群管理」卡片(入群审批/禁言/成员信息/黑名单), 与上方 `groupAdmin.*` 配置同一份数据。
|
|
236
|
+
|
|
237
|
+
## 内置命令
|
|
238
|
+
|
|
239
|
+
| 命令 | 说明 |
|
|
240
|
+
|------|------|
|
|
241
|
+
| `/bot-reset` | 重置当前会话(清除上下文) |
|
|
242
|
+
| `/bot-model` | 查看或切换模型 |
|
|
243
|
+
| `/bot-status` | 查看当前会话状态 |
|
|
244
|
+
| `/bot-help` | 查看所有指令 |
|
|
245
|
+
|
|
246
|
+
## 核心模块
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
src/
|
|
250
|
+
├── index.ts # Cordis 插件入口(async apply)
|
|
251
|
+
├── config.ts # 配置 Schema
|
|
252
|
+
├── types.ts # 全局类型定义
|
|
253
|
+
├── setup.ts # 凭据绑定(扫码)
|
|
254
|
+
├── transport/ # 传输层
|
|
255
|
+
│ ├── inbound.ts # QQ 入站消息 → agent.followup()
|
|
256
|
+
│ ├── outbound.ts # session/event → QQ sendMarkdown
|
|
257
|
+
│ ├── outbound-buffer.ts # 流式缓冲
|
|
258
|
+
│ └── chunker.ts # Markdown 文本切分
|
|
259
|
+
├── session/ # 会话管理层
|
|
260
|
+
│ ├── session-manager.ts # QQ peer → Agent 映射
|
|
261
|
+
│ └── idle-evictor.ts # 闲置回收
|
|
262
|
+
├── model/ # 模型路由层
|
|
263
|
+
│ ├── model-resolver.ts # 路由解析
|
|
264
|
+
│ ├── prefs-store.ts # per-peer 偏好持久化
|
|
265
|
+
│ └── settings-reader.ts # settings.yaml 只读
|
|
266
|
+
├── shared/ # 共享工具
|
|
267
|
+
│ ├── utils.ts # 通用函数
|
|
268
|
+
│ ├── scope.ts # scope/peer 提取
|
|
269
|
+
│ └── send-helper.ts # 分块发送
|
|
270
|
+
├── commands/ # 斜杠命令
|
|
271
|
+
└── typings/ # 外部模块声明
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## 会话路由
|
|
275
|
+
|
|
276
|
+
sessionKey: `qqbot:${appId}:${scope}:${peerId}`,由 SHA-256 确定性派生 SessionId,重启后可恢复。
|
|
277
|
+
|
|
278
|
+
解析策略:进程内复用 → 持久化恢复 → 全新创建。
|
|
279
|
+
|
|
280
|
+
## 设计原则
|
|
281
|
+
|
|
282
|
+
- **纯 Cordis 插件** — 遵循 dsh "Plugins, not loop changes" 原则
|
|
283
|
+
- **声明式依赖** — `inject = ['agents']`,不直接耦合其他插件
|
|
284
|
+
- **会话隔离** — 每个 QQ 私聊用户/群聊各一个独立 Agent
|
|
285
|
+
- **Preset 支持** — 可通过 `agent-presets` 服务挂载预设(工具集、prompt 等)
|
|
286
|
+
- **闲置回收** — 超时自动 dispose Agent,防止内存泄漏
|
|
287
|
+
- **Markdown 输出** — 回复以 Markdown 格式发送,支持代码块/表格感知切分
|
|
288
|
+
|
|
289
|
+
## 本地开发
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
# 安装依赖
|
|
293
|
+
pnpm install
|
|
294
|
+
|
|
295
|
+
# 构建
|
|
296
|
+
pnpm build
|
|
297
|
+
|
|
298
|
+
# 开发模式(watch)
|
|
299
|
+
pnpm dev
|
|
300
|
+
|
|
301
|
+
# 用 --patch 方式调试
|
|
302
|
+
export QQBOT_APPID="xxx" QQBOT_SECRET="xxx"
|
|
303
|
+
npx @deepseek-ai/dsh web --patch /path/to/dsh-qqbot/cordis.dev.yml
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## License
|
|
307
|
+
|
|
308
|
+
[MIT](./LICENSE)
|
package/README_EN.md
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# @zaofan/dsh-qqbot
|
|
2
|
+
|
|
3
|
+
An **enhanced fork** of the QQ Bot IM plugin for [deepseek-harness](https://github.com/deepseek-ai/deepseek-harness) (dsh): it drives the dsh agent loop with the QQ messaging platform as the frontend protocol, adding a local sticker library, rich media send/recall, scheduled tasks, multi-instance personas, and a visual settings panel.
|
|
4
|
+
|
|
5
|
+
📦 Repo: [gcry13067381632-jpg/dsh-qqbot](https://github.com/gcry13067381632-jpg/dsh-qqbot) (forked from [tencent-connect/dsh-qqbot](https://github.com/tencent-connect/dsh-qqbot))
|
|
6
|
+
|
|
7
|
+
[中文文档](./README.md) | English
|
|
8
|
+
|
|
9
|
+
## 🐋 This fork (enhanced edition)
|
|
10
|
+
|
|
11
|
+
**In one line**: bring your dsh-powered QQ bot to life — it auto-collects stickers from group chats, picks and sends the right one when the mood hits, speaks up on schedule, and lets you run several bots with different personalities from one computer.
|
|
12
|
+
|
|
13
|
+
This repo is an enhanced fork of [@tencent-connect/dsh-qqbot](https://github.com/tencent-connect/dsh-qqbot) (changes live outside the upstream source — reinstalling/upgrading upstream will wipe them).
|
|
14
|
+
|
|
15
|
+
### What it does for you
|
|
16
|
+
|
|
17
|
+
- **🤳 Group images become its sticker library automatically** — saved locally with dedup, organized into "to-sort / favorite / trash". Just say *"send something happy"* and it searches, picks and sends on its own — with optional guardrails (no posting into a cold chat, rate limits, no repeat stickers).
|
|
18
|
+
- **⏰ It speaks up on time** — schedule a daily greeting at 9:00, or tell it *"remind me to drink water in 30 seconds"* and it actually will.
|
|
19
|
+
- **🧑🤝🧑 Multiple bots, multiple personalities, one machine** — each bot has its own AppID, persona preset and working directory (sticker library / timers / gates fully isolated). Adding a bot is a phone-QR scan away from the Web panel — credentials are filled in automatically.
|
|
20
|
+
- **🛡️ Group admin helper: join-request approval + mute management** — when the bot is a group admin, enable it in the panel (⑥ QQ Group Admin): join requests arrive as real-time events with an in-group reminder (reply "approve/reject" to handle them), and you can inspect mute state or mute/unmute members — all through official APIs with human-readable errors (not an admin / cannot mute the owner, etc.).
|
|
21
|
+
- **🧹 Messy library? Let the AI tidy it** — ask *"which stickers still lack tags or descriptions"* and it lists them, then tags and describes them itself.
|
|
22
|
+
- **🖥️ No config-file surgery** — reply pacing, sticker gates, scheduled wake-ups, and per-bot personas are all editable in the settings panel; saving applies live (only adding/removing bots needs a restart).
|
|
23
|
+
|
|
24
|
+
### 📸 Showcase
|
|
25
|
+
|
|
26
|
+
Left: the dsh runtime backend — reasoning, tool calls and token usage are fully visible (paired with the `reply_gate` gate tool, the bot decides on its own whether to speak or stay silently idle);
|
|
27
|
+
Middle: real QQ group conversation — hide-and-seek role-play, replying when it should and staying quiet when it shouldn't;
|
|
28
|
+
Right: sticker-battle in action — the bot answers with stickers from its own library, image and text sent as separate messages.
|
|
29
|
+
|
|
30
|
+

|
|
31
|
+
|
|
32
|
+

|
|
33
|
+
|
|
34
|
+

|
|
35
|
+
|
|
36
|
+
### For developers
|
|
37
|
+
- Standard tools available inside QQ sessions: `send_media` / `recall_message` / `list_stickers` / `sticker_tag` / `sticker_untagged` / `schedule_timer` / `schedule_cancel` …, routed per bot account.
|
|
38
|
+
- **Group admin tools** (`group_join_requests` / `group_approve_join` / `group_mute_state` / `group_mute_member` …): join-request approval and mute management — requires the bot to be a group admin; inside a group session the current group is used, elsewhere the configured `manageGroup` applies.
|
|
39
|
+
- **Plain text can send media or recall messages**: writing `[MEDIA:image|path-or-url]` in a reply turns it into a real image message (`voice`/`video`/`file` work the same); a lone `[RECALL]` line recalls the bot's own last message.
|
|
40
|
+
- Host-level fixes (workspace session attachment, upstream PR #21) are included — idempotent and fully fail-soft.
|
|
41
|
+
|
|
42
|
+
> 🛡️ This repo contains **no** bot credentials, sticker data, logs or personal paths (cleaned before publishing). Inject AppID/AppSecret via env vars or the Web panel — **never commit them**.
|
|
43
|
+
|
|
44
|
+
### Build & deploy
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install # install deps (peer deps resolved by the dsh host)
|
|
48
|
+
node node_modules/typescript/lib/tsc.js -p tsconfig.json # or npm run build
|
|
49
|
+
# then copy dist/ over your dsh profile's
|
|
50
|
+
# node_modules/@tencent-connect/dsh-qqbot/dist/ and restart dsh
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
See the upstream "Installation" section below (`dsh plugin add` + QR onboarding both work).
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Architecture
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
QQ User → QQ WebSocket → dsh-im-qqbot → ctx.agents → dsh agent loop → LLM
|
|
61
|
+
↑ │
|
|
62
|
+
└── session/event ──────────┘
|
|
63
|
+
(assistant reply → QQ sendMarkdown)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
> ⚠️ **Install into the `web` profile** (host of the `dsh web` settings panel); other profiles get a bare
|
|
69
|
+
> environment with no settings UI. Do **not** `add @tencent-connect/dsh-qqbot` — that installs the upstream
|
|
70
|
+
> official version (without this fork's features).
|
|
71
|
+
>
|
|
72
|
+
> ✅ **Single package, everything included**: QQ bot + the Web settings panel (host bridge + settings UI) all
|
|
73
|
+
> ship in this one package — after install, dsh Web → Settings shows a "QQ bot (im-qqbot)" page (one per bot
|
|
74
|
+
> instance). **No separate dsh-qqbot-settings install needed.**
|
|
75
|
+
|
|
76
|
+
### Method 1 (after npm release): one command
|
|
77
|
+
|
|
78
|
+
```powershell
|
|
79
|
+
npx @deepseek-ai/dsh plugin --profile web add @zaofan/dsh-qqbot
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
> Until it is published to npm, use Method 2 below.
|
|
83
|
+
|
|
84
|
+
### Method 2: from source (recommended for now)
|
|
85
|
+
|
|
86
|
+
**Windows (one-click script)**:
|
|
87
|
+
|
|
88
|
+
```powershell
|
|
89
|
+
git clone https://github.com/gcry13067381632-jpg/dsh-qqbot.git
|
|
90
|
+
cd dsh-qqbot
|
|
91
|
+
.\install.ps1 # npm install/build -> pack -> add tarball -> prints restart steps
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
> If script execution is blocked: `powershell -ExecutionPolicy Bypass -File .\install.ps1`
|
|
95
|
+
|
|
96
|
+
**macOS / Linux (manual)**:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
git clone https://github.com/gcry13067381632-jpg/dsh-qqbot.git
|
|
100
|
+
cd dsh-qqbot
|
|
101
|
+
npm install && npm run build
|
|
102
|
+
pnpm pack --pack-destination /tmp
|
|
103
|
+
npx @deepseek-ai/dsh plugin --profile web add /tmp/zaofan-dsh-qqbot-0.4.0.tgz
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
> 💡 Why a tarball instead of `add <source dir>`? Lessons from real installs:
|
|
107
|
+
> ① a directory path containing spaces gets split at the spaces on Windows (pnpm reports `- isn't supported`);
|
|
108
|
+
> ② `add <dir>` becomes a pnpm link (junction), so the plugin can't locate the profile by its code location,
|
|
109
|
+
> and scanned credentials cannot be persisted (env-var-only fallback).
|
|
110
|
+
|
|
111
|
+
### Troubleshooting: npm install fails with ERESOLVE (backported from upstream PR #42)
|
|
112
|
+
|
|
113
|
+
A fresh `npm install` may fail with `ERESOLVE could not resolve`. Cause: peer deps like
|
|
114
|
+
`@deepseek-ai/dsh-tools` / `dsh-agent` are still on prerelease (-rc) version lines, and npm 7+
|
|
115
|
+
strict resolution rejects non-intersecting combinations. **This is an upstream version-line issue, not a plugin bug.** Two workarounds:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npm install --legacy-peer-deps # install-time resolution only; runtime behavior unchanged
|
|
119
|
+
# or: install deps, then build & pack manually (peers are resolved by the dsh host)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
> Tracked: once upstream #37 is fixed and version lines converge, this section can be removed.
|
|
123
|
+
|
|
124
|
+
### First launch & binding
|
|
125
|
+
|
|
126
|
+
Start `dsh web`. If credentials are missing, the **QR flow** starts automatically: a QR code is printed in the
|
|
127
|
+
terminal → scan it with the QQ mobile app → credentials are saved and survive restarts (you can also use
|
|
128
|
+
"QR bind" / edit accounts in the settings panel at any time).
|
|
129
|
+
|
|
130
|
+

|
|
131
|
+
|
|
132
|
+
> **Note**: Use `0.4.0` or later for browser-link scanning, which avoids QR code misalignment in some terminals.
|
|
133
|
+
|
|
134
|
+
### Don't have a QQ bot yet? Register one (get AppID / AppSecret)
|
|
135
|
+
|
|
136
|
+
1. Open the [QQ Open Platform](https://q.qq.com) and sign in with your QQ account;
|
|
137
|
+
2. Go to "Bot" → "Create Bot" and fill in name, avatar and description;
|
|
138
|
+
3. After creation, copy the **AppID** and **AppSecret** from the bot detail page;
|
|
139
|
+
4. Enter them in dsh Web → Settings → "QQ bot" → "Accounts & presets" and save
|
|
140
|
+
(or set env vars `QQBOT_APPID` / `QQBOT_SECRET`);
|
|
141
|
+
5. Enable the needed **single-chat / group-chat** message permissions on the platform
|
|
142
|
+
(group chat usually requires a use-case review).
|
|
143
|
+
|
|
144
|
+
> 💡 Easier: once the bot exists, just use the **QR scan bind** on first launch — no need to type credentials.
|
|
145
|
+
|
|
146
|
+
### For developers: --patch dev mode
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
export QQBOT_APPID="yourAppID" QQBOT_SECRET="yourAppSecret"
|
|
150
|
+
npx @deepseek-ai/dsh web --patch /path/to/dsh-qqbot/cordis.dev.yml
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Remote approval over QQ (optional)
|
|
154
|
+
|
|
155
|
+
When an Agent tool needs access **outside the workspace**, dsh raises a permission request. With this
|
|
156
|
+
feature on, the bot forwards the request to the **QQ conversation of the task's initiator**, and you
|
|
157
|
+
allow/deny right from QQ:
|
|
158
|
+
|
|
159
|
+
> ⚠️ **DSH permission request**
|
|
160
|
+
> Tool: pwsh
|
|
161
|
+
> Reason: needs access outside the workspace
|
|
162
|
+
>
|
|
163
|
+
> Allow this operation: `/approve A1B2C3`
|
|
164
|
+
> Deny this operation: `/deny A1B2C3`
|
|
165
|
+
> One-time only; auto-denied after 120 s.
|
|
166
|
+
|
|
167
|
+
**Enable** (either way; takes effect for new requests right after saving, no restart needed):
|
|
168
|
+
- **Web settings panel**: Settings → "QQ bot" → ⑤ QQ remote approval → tick it on;
|
|
169
|
+
- or add two lines to the instance `config` in `cordis.patch.yml`, then restart:
|
|
170
|
+
|
|
171
|
+
```yaml
|
|
172
|
+
- id: im-qqbot
|
|
173
|
+
config:
|
|
174
|
+
enableApprovals: true # default false
|
|
175
|
+
approvalTimeoutMs: 120000 # wait window; auto-deny on timeout
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Security boundaries**: one-time code; only the **initiator in the same conversation** may decide
|
|
179
|
+
(others in a group chat cannot approve even if they see the code); grants only the current operation;
|
|
180
|
+
auto-cancelled when the agent is cancelled or dsh exits.
|
|
181
|
+
|
|
182
|
+
> Idea source: QQ-approval design of wang-22-code/dsh-qqbot-bridge (host dsh `approval/request`
|
|
183
|
+
> standard event — the same wiring used by official dsh-acp and the Web approval dialog).
|
|
184
|
+
|
|
185
|
+
## Configuration
|
|
186
|
+
|
|
187
|
+
| Config | Type | Default | Description |
|
|
188
|
+
|------|------|--------|------|
|
|
189
|
+
| `appId` | string | **required** | QQ Bot AppID (or via `QQBOT_APPID` env var) |
|
|
190
|
+
| `appSecret` | string | **required** | QQ Bot AppSecret (or via `QQBOT_SECRET` env var) |
|
|
191
|
+
| `provider` | string | `deepseek-official` | LLM provider name |
|
|
192
|
+
| `model` | string | `deepseek-chat` | Model name |
|
|
193
|
+
| `preset` | string | - | Agent preset id |
|
|
194
|
+
| `cwd` | string | `process.cwd()` | Agent working directory |
|
|
195
|
+
| `requireMention` | boolean | `true` | Whether group messages require @bot to trigger |
|
|
196
|
+
| `groupPrompt` | string | - | Extra system prompt for group chats |
|
|
197
|
+
| `directPrompt` | string | - | Extra system prompt for direct chats |
|
|
198
|
+
| `textChunkLimit` | number | `4500` | Max chars per message |
|
|
199
|
+
| `sessionIdleTimeout` | number | `1800000` | Session idle timeout (ms), default 30 min |
|
|
200
|
+
| `debug` | boolean | `false` | Debug mode |
|
|
201
|
+
|
|
202
|
+
## Built-in Commands
|
|
203
|
+
|
|
204
|
+
| Command | Description |
|
|
205
|
+
|------|------|
|
|
206
|
+
| `/bot-reset` | Reset the current session (clear context) |
|
|
207
|
+
| `/bot-model` | View or switch model |
|
|
208
|
+
| `/bot-status` | View current session status |
|
|
209
|
+
| `/bot-help` | View all commands |
|
|
210
|
+
|
|
211
|
+
## Core Modules
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
src/
|
|
215
|
+
├── index.ts # Cordis plugin entry (async apply)
|
|
216
|
+
├── config.ts # Config schema
|
|
217
|
+
├── types.ts # Global types
|
|
218
|
+
├── setup.ts # Credential binding (QR)
|
|
219
|
+
├── transport/ # Transport layer
|
|
220
|
+
│ ├── inbound.ts # QQ inbound message → agent.followup()
|
|
221
|
+
│ ├── outbound.ts # session/event → QQ sendMarkdown
|
|
222
|
+
│ ├── outbound-buffer.ts # Streaming buffer
|
|
223
|
+
│ └── chunker.ts # Markdown chunking
|
|
224
|
+
├── session/ # Session management
|
|
225
|
+
│ ├── session-manager.ts # QQ peer → Agent mapping
|
|
226
|
+
│ └── idle-evictor.ts # Idle eviction
|
|
227
|
+
├── model/ # Model routing
|
|
228
|
+
│ ├── model-resolver.ts # Route resolution
|
|
229
|
+
│ ├── prefs-store.ts # Per-peer preference persistence
|
|
230
|
+
│ └── settings-reader.ts # settings.yaml read-only
|
|
231
|
+
├── shared/ # Shared utilities
|
|
232
|
+
│ ├── utils.ts # Common helpers
|
|
233
|
+
│ ├── scope.ts # scope/peer extraction
|
|
234
|
+
│ └── send-helper.ts # Chunked send
|
|
235
|
+
├── commands/ # Slash commands
|
|
236
|
+
└── typings/ # External module declarations
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Session Routing
|
|
240
|
+
|
|
241
|
+
sessionKey: `qqbot:${appId}:${scope}:${peerId}`, with the SessionId derived deterministically via SHA-256 so sessions survive restarts.
|
|
242
|
+
|
|
243
|
+
Resolution strategy: in-process reuse → persisted resume → fresh create.
|
|
244
|
+
|
|
245
|
+
## Design Principles
|
|
246
|
+
|
|
247
|
+
- **Pure Cordis plugin** — follows the dsh "Plugins, not loop changes" principle
|
|
248
|
+
- **Declarative dependencies** — `inject = ['agents']`, no direct coupling to other plugins
|
|
249
|
+
- **Session isolation** — one independent Agent per QQ direct user / group
|
|
250
|
+
- **Preset support** — mount presets (toolkits, prompts, etc.) via the `agent-presets` service
|
|
251
|
+
- **Idle eviction** — auto-dispose Agents on timeout to prevent memory leaks
|
|
252
|
+
- **Markdown output** — replies sent as Markdown with code-block/table-aware chunking
|
|
253
|
+
|
|
254
|
+
## Local Development
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
# Install dependencies
|
|
258
|
+
pnpm install
|
|
259
|
+
|
|
260
|
+
# Build
|
|
261
|
+
pnpm build
|
|
262
|
+
|
|
263
|
+
# Dev mode (watch)
|
|
264
|
+
pnpm dev
|
|
265
|
+
|
|
266
|
+
# Debug via --patch
|
|
267
|
+
export QQBOT_APPID="xxx" QQBOT_SECRET="xxx"
|
|
268
|
+
npx @deepseek-ai/dsh web --patch /path/to/dsh-qqbot/cordis.dev.yml
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## License
|
|
272
|
+
|
|
273
|
+
[MIT](./LICENSE)
|