@tencent-connect/openclaw-qqbot 1.0.0-alpha.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 +22 -0
- package/README.md +393 -0
- package/README.zh.md +390 -0
- package/bin/qqbot-cli.js +243 -0
- package/clawdbot.plugin.json +16 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +22 -0
- package/dist/src/api.d.ts +138 -0
- package/dist/src/api.js +523 -0
- package/dist/src/channel.d.ts +3 -0
- package/dist/src/channel.js +337 -0
- package/dist/src/config.d.ts +25 -0
- package/dist/src/config.js +156 -0
- package/dist/src/gateway.d.ts +18 -0
- package/dist/src/gateway.js +2315 -0
- package/dist/src/image-server.d.ts +62 -0
- package/dist/src/image-server.js +401 -0
- package/dist/src/known-users.d.ts +100 -0
- package/dist/src/known-users.js +263 -0
- package/dist/src/onboarding.d.ts +10 -0
- package/dist/src/onboarding.js +203 -0
- package/dist/src/outbound.d.ts +150 -0
- package/dist/src/outbound.js +1175 -0
- package/dist/src/proactive.d.ts +170 -0
- package/dist/src/proactive.js +399 -0
- package/dist/src/runtime.d.ts +3 -0
- package/dist/src/runtime.js +10 -0
- package/dist/src/session-store.d.ts +52 -0
- package/dist/src/session-store.js +254 -0
- package/dist/src/types.d.ts +145 -0
- package/dist/src/types.js +1 -0
- package/dist/src/utils/audio-convert.d.ts +73 -0
- package/dist/src/utils/audio-convert.js +645 -0
- package/dist/src/utils/file-utils.d.ts +46 -0
- package/dist/src/utils/file-utils.js +107 -0
- package/dist/src/utils/image-size.d.ts +51 -0
- package/dist/src/utils/image-size.js +234 -0
- package/dist/src/utils/media-tags.d.ts +14 -0
- package/dist/src/utils/media-tags.js +120 -0
- package/dist/src/utils/payload.d.ts +112 -0
- package/dist/src/utils/payload.js +186 -0
- package/dist/src/utils/platform.d.ts +126 -0
- package/dist/src/utils/platform.js +358 -0
- package/dist/src/utils/upload-cache.d.ts +34 -0
- package/dist/src/utils/upload-cache.js +93 -0
- package/index.ts +27 -0
- package/moltbot.plugin.json +16 -0
- package/node_modules/@eshaz/web-worker/LICENSE +201 -0
- package/node_modules/@eshaz/web-worker/README.md +134 -0
- package/node_modules/@eshaz/web-worker/browser.js +17 -0
- package/node_modules/@eshaz/web-worker/cjs/browser.js +16 -0
- package/node_modules/@eshaz/web-worker/cjs/node.js +219 -0
- package/node_modules/@eshaz/web-worker/index.d.ts +4 -0
- package/node_modules/@eshaz/web-worker/node.js +223 -0
- package/node_modules/@eshaz/web-worker/package.json +54 -0
- package/node_modules/@wasm-audio-decoders/common/index.js +5 -0
- package/node_modules/@wasm-audio-decoders/common/package.json +36 -0
- package/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderCommon.js +231 -0
- package/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderWorker.js +129 -0
- package/node_modules/@wasm-audio-decoders/common/src/puff/README +67 -0
- package/node_modules/@wasm-audio-decoders/common/src/puff/build_puff.js +31 -0
- package/node_modules/@wasm-audio-decoders/common/src/puff/puff.c +863 -0
- package/node_modules/@wasm-audio-decoders/common/src/puff/puff.h +35 -0
- package/node_modules/@wasm-audio-decoders/common/src/utilities.js +3 -0
- package/node_modules/@wasm-audio-decoders/common/types.d.ts +7 -0
- package/node_modules/mpg123-decoder/README.md +265 -0
- package/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js +185 -0
- package/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js.map +1 -0
- package/node_modules/mpg123-decoder/index.js +8 -0
- package/node_modules/mpg123-decoder/package.json +58 -0
- package/node_modules/mpg123-decoder/src/EmscriptenWasm.js +464 -0
- package/node_modules/mpg123-decoder/src/MPEGDecoder.js +200 -0
- package/node_modules/mpg123-decoder/src/MPEGDecoderWebWorker.js +21 -0
- package/node_modules/mpg123-decoder/types.d.ts +30 -0
- package/node_modules/silk-wasm/LICENSE +21 -0
- package/node_modules/silk-wasm/README.md +85 -0
- package/node_modules/silk-wasm/lib/index.cjs +16 -0
- package/node_modules/silk-wasm/lib/index.d.ts +70 -0
- package/node_modules/silk-wasm/lib/index.mjs +16 -0
- package/node_modules/silk-wasm/lib/silk.wasm +0 -0
- package/node_modules/silk-wasm/lib/utils.d.ts +4 -0
- package/node_modules/silk-wasm/package.json +39 -0
- package/node_modules/simple-yenc/.github/FUNDING.yml +1 -0
- package/node_modules/simple-yenc/.prettierignore +1 -0
- package/node_modules/simple-yenc/LICENSE +7 -0
- package/node_modules/simple-yenc/README.md +163 -0
- package/node_modules/simple-yenc/dist/esm.js +1 -0
- package/node_modules/simple-yenc/dist/index.js +1 -0
- package/node_modules/simple-yenc/package.json +50 -0
- package/node_modules/simple-yenc/rollup.config.js +27 -0
- package/node_modules/simple-yenc/src/simple-yenc.js +302 -0
- package/node_modules/ws/LICENSE +20 -0
- package/node_modules/ws/README.md +548 -0
- package/node_modules/ws/browser.js +8 -0
- package/node_modules/ws/index.js +13 -0
- package/node_modules/ws/lib/buffer-util.js +131 -0
- package/node_modules/ws/lib/constants.js +19 -0
- package/node_modules/ws/lib/event-target.js +292 -0
- package/node_modules/ws/lib/extension.js +203 -0
- package/node_modules/ws/lib/limiter.js +55 -0
- package/node_modules/ws/lib/permessage-deflate.js +528 -0
- package/node_modules/ws/lib/receiver.js +706 -0
- package/node_modules/ws/lib/sender.js +602 -0
- package/node_modules/ws/lib/stream.js +161 -0
- package/node_modules/ws/lib/subprotocol.js +62 -0
- package/node_modules/ws/lib/validation.js +152 -0
- package/node_modules/ws/lib/websocket-server.js +554 -0
- package/node_modules/ws/lib/websocket.js +1393 -0
- package/node_modules/ws/package.json +69 -0
- package/node_modules/ws/wrapper.mjs +8 -0
- package/openclaw.plugin.json +16 -0
- package/package.json +76 -0
- package/scripts/proactive-api-server.ts +356 -0
- package/scripts/pull-latest.sh +316 -0
- package/scripts/send-proactive.ts +273 -0
- package/scripts/set-markdown.sh +156 -0
- package/scripts/upgrade-and-run.sh +525 -0
- package/scripts/upgrade.sh +127 -0
- package/skills/qqbot-cron/SKILL.md +513 -0
- package/skills/qqbot-media/SKILL.md +194 -0
- package/src/api.ts +704 -0
- package/src/channel.ts +368 -0
- package/src/config.ts +182 -0
- package/src/gateway.ts +2459 -0
- package/src/image-server.ts +474 -0
- package/src/known-users.ts +353 -0
- package/src/onboarding.ts +274 -0
- package/src/openclaw-plugin-sdk.d.ts +483 -0
- package/src/outbound.ts +1301 -0
- package/src/proactive.ts +530 -0
- package/src/runtime.ts +14 -0
- package/src/session-store.ts +303 -0
- package/src/types.ts +153 -0
- package/src/utils/audio-convert.ts +738 -0
- package/src/utils/file-utils.ts +122 -0
- package/src/utils/image-size.ts +266 -0
- package/src/utils/media-tags.ts +134 -0
- package/src/utils/payload.ts +265 -0
- package/src/utils/platform.ts +404 -0
- package/src/utils/upload-cache.ts +128 -0
- package/tsconfig.json +16 -0
package/README.zh.md
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
**简体中文 | [English](README.md)**
|
|
4
|
+
|
|
5
|
+
<img width="120" src="https://img.shields.io/badge/🤖-QQ_Bot-blue?style=for-the-badge" alt="QQ Bot" />
|
|
6
|
+
|
|
7
|
+
# QQ Bot — OpenClaw 渠道插件
|
|
8
|
+
|
|
9
|
+
**让你的 AI 助手接入 QQ — 私聊、群聊、富媒体,一个插件全搞定。**
|
|
10
|
+
|
|
11
|
+
[](./LICENSE)
|
|
12
|
+
[](https://bot.q.qq.com/wiki/)
|
|
13
|
+
[](https://github.com/tencent-connect/openclaw-qqbot)
|
|
14
|
+
[](https://nodejs.org/)
|
|
15
|
+
[](https://www.typescriptlang.org/)
|
|
16
|
+
|
|
17
|
+
<br/>
|
|
18
|
+
|
|
19
|
+
扫描二维码加入群聊,一起交流
|
|
20
|
+
|
|
21
|
+
<img width="316" height="410" alt="QQ 群二维码" src="https://github.com/user-attachments/assets/d079ba89-ecd0-437f-9e66-92319801a325" />
|
|
22
|
+
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## ✨ 功能特性
|
|
28
|
+
|
|
29
|
+
| 功能 | 说明 |
|
|
30
|
+
|------|------|
|
|
31
|
+
| 🔒 **多场景支持** | C2C 私聊、群聊 @消息、频道消息、频道私信 |
|
|
32
|
+
| 🖼️ **富媒体消息** | 支持图片、语音、视频、文件的收发 |
|
|
33
|
+
| 🎙️ **语音能力 (STT/TTS)** | 语音转文字自动转录 & 文字转语音回复 |
|
|
34
|
+
| ⏰ **定时推送** | 支持定时任务触发后主动推送消息 |
|
|
35
|
+
| 🔗 **URL 无限制** | 私聊可直接发送 URL |
|
|
36
|
+
| ⌨️ **输入状态** | 实时显示"Bot 正在输入中…"状态 |
|
|
37
|
+
| 🔄 **热更新** | 支持 npm 方式安装和无缝热更新 |
|
|
38
|
+
| 📝 **Markdown** | 完整支持 Markdown 格式消息 |
|
|
39
|
+
| 🛠️ **原生命令** | 支持 OpenClaw 原生命令 |
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 📸 功能展示
|
|
44
|
+
|
|
45
|
+
> **说明:** 本插件仅作为**消息通道**,负责在 QQ 和 OpenClaw 之间传递消息。图片理解、语音转录、AI 画图等能力取决于你配置的 **AI 模型**以及在 OpenClaw 中安装的 **skill**,而非插件本身提供。
|
|
46
|
+
|
|
47
|
+
### 🎙️ 语音消息(STT)
|
|
48
|
+
|
|
49
|
+
配置 STT 后,插件会自动将语音转录为文字再交给 AI 处理。整个过程对用户完全透明——发语音就像发文字一样自然,AI 听得懂你在说什么。
|
|
50
|
+
|
|
51
|
+
> **你**:*(发送一段语音)*"明天深圳天气怎么样"
|
|
52
|
+
>
|
|
53
|
+
> **QQBot**:明天(3月7日 周六)深圳的天气预报 🌤️ ...
|
|
54
|
+
|
|
55
|
+
<img width="360" src="docs/images/fc7b2236896cfba3a37c94be5d59ce3e_720.jpg" alt="听语音演示" />
|
|
56
|
+
|
|
57
|
+
### 📄 文件理解
|
|
58
|
+
|
|
59
|
+
用户发文件给 AI,AI 同样能接住。不管是一本小说还是一份报告,AI 会自动识别文件内容并给出智能回复。
|
|
60
|
+
|
|
61
|
+
> **你**:*(发送《战争与和平》TXT 文件)*
|
|
62
|
+
>
|
|
63
|
+
> **QQBot**:收到!你上传了列夫·托尔斯泰的《战争与和平》中文版文本。从内容来看,这是第一章的开头……你想让我做什么?
|
|
64
|
+
|
|
65
|
+
<img width="360" src="docs/images/07bff56ab68e03173d2af586eeb3bcee_720.jpg" alt="AI理解用户发送的文件" />
|
|
66
|
+
|
|
67
|
+
### 🖼️ 图片理解
|
|
68
|
+
|
|
69
|
+
如果主模型支持视觉(如腾讯混元 `hunyuan-vision`),用户发图片 AI 也能看懂。这是多模态模型的通用能力,非插件专属功能。
|
|
70
|
+
|
|
71
|
+
> **你**:*(发送一张图片)*
|
|
72
|
+
>
|
|
73
|
+
> **QQBot**:哈哈,好可爱!这是QQ企鹅穿上小龙虾套装吗?🦞🐧 ...
|
|
74
|
+
|
|
75
|
+
<img width="360" src="docs/images/59d421891f813b0d3c0cbe12574b6a72_720.jpg" alt="图片理解演示" />
|
|
76
|
+
|
|
77
|
+
### 🎨 AI 画图
|
|
78
|
+
|
|
79
|
+
> **你**:画一只猫咪
|
|
80
|
+
>
|
|
81
|
+
> **QQBot**:画好啦!一只可爱的简笔小猫咪🐱🎨
|
|
82
|
+
|
|
83
|
+
AI 通过 `<qqimg>路径</qqimg>` 发送图片,支持本地文件路径和网络 URL。格式:jpg/png/gif/webp/bmp。
|
|
84
|
+
|
|
85
|
+
<img width="360" src="docs/images/4645f2b3a20822b7f8d6664a708529eb_720.jpg" alt="发图片演示" />
|
|
86
|
+
|
|
87
|
+
### 🔊 语音回复(TTS)
|
|
88
|
+
|
|
89
|
+
> **你**:给我讲一个笑话
|
|
90
|
+
>
|
|
91
|
+
> **QQBot**:*(发送一条语音消息)*
|
|
92
|
+
|
|
93
|
+
AI 通过 `<qqvoice>路径</qqvoice>` 发送语音消息。格式:mp3/wav/silk/ogg,无需安装 ffmpeg。
|
|
94
|
+
|
|
95
|
+
<img width="360" src="docs/images/21dce8bfc553ce23d1bd1b270e9c516c.jpg" alt="发语音演示" />
|
|
96
|
+
|
|
97
|
+
### 📎 文件发送
|
|
98
|
+
|
|
99
|
+
> **你**:战争与和平的第一章截取一下发文件给我
|
|
100
|
+
>
|
|
101
|
+
> **QQBot**:*(发送 .txt 文件)*
|
|
102
|
+
|
|
103
|
+
AI 通过 `<qqfile>路径</qqfile>` 发送文件。任意格式,最大 20MB。
|
|
104
|
+
|
|
105
|
+
<img width="360" src="docs/images/17cada70df90185d45a2d6dd36e92f2f_720.jpg" alt="发文件演示" />
|
|
106
|
+
|
|
107
|
+
### 🎬 视频发送
|
|
108
|
+
|
|
109
|
+
> **你**:发一个演示视频给我
|
|
110
|
+
>
|
|
111
|
+
> **QQBot**:*(发送视频)*
|
|
112
|
+
|
|
113
|
+
AI 通过 `<qqvideo>路径</qqvideo>` 发送视频,支持本地文件和公网 URL。大文件(>5MB)自动提示上传进度。
|
|
114
|
+
|
|
115
|
+
<img width="360" src="docs/images/85d03b8a216f267ab7b2aee248a18a41_720.jpg" alt="发视频演示" />
|
|
116
|
+
|
|
117
|
+
### 富媒体标签参考
|
|
118
|
+
|
|
119
|
+
| 标签 | 方向 | 说明 |
|
|
120
|
+
|------|------|------|
|
|
121
|
+
| `<qqimg>路径</qqimg>` | 发送 | jpg/png/gif/webp/bmp,本地路径或 URL |
|
|
122
|
+
| `<qqvoice>路径</qqvoice>` | 发送 | mp3/wav/silk/ogg,无需 ffmpeg |
|
|
123
|
+
| `<qqfile>路径</qqfile>` | 发送 | 任意格式,最大 20MB |
|
|
124
|
+
| `<qqvideo>路径</qqvideo>` | 发送 | 本地路径或 URL |
|
|
125
|
+
| 语音 / 文件 / 图片 | 接收 | 自动转录(STT)、自动下载、或视觉分析 |
|
|
126
|
+
|
|
127
|
+
> **底层细节:** 30+ 种标签变体自动纠正、上传去重缓存、有序队列发送、音频格式多层降级。
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 🚀 快速开始
|
|
132
|
+
|
|
133
|
+
### 第一步 — 在 QQ 开放平台创建机器人
|
|
134
|
+
|
|
135
|
+
1. 前往 [QQ 开放平台](https://q.qq.com/),用**手机 QQ 扫描页面二维码**即可注册/登录。若尚未注册,扫码后系统会自动完成注册并绑定你的 QQ 账号。
|
|
136
|
+
|
|
137
|
+
<img width="3246" height="1886" alt="Clipboard_Screenshot_1772980354" src="https://github.com/user-attachments/assets/d8491859-57e8-47e4-9d39-b21138be54d0" />
|
|
138
|
+
|
|
139
|
+
2. 手机 QQ 扫码后选择**同意**,即完成注册,进入 QQ 机器人配置页。
|
|
140
|
+
3. 点击**创建机器人**,即可直接新建一个 QQ 机器人。
|
|
141
|
+
|
|
142
|
+
<img width="1982" height="1316" alt="Clipboard_Screenshot_1772980440" src="https://github.com/user-attachments/assets/3ccb494d-6e4d-462c-9218-b4dfd43a254f" />
|
|
143
|
+
|
|
144
|
+
4. 在机器人页面中找到 **AppID** 和 **AppSecret**,分别点击右侧**复制**按钮,保存到记事本或备忘录中。**AppSecret 不支持明文保存,离开页面后再查看会强制重置,请务必妥善保存。**
|
|
145
|
+
|
|
146
|
+
<img width="1670" height="1036" alt="Clipboard_Screenshot_1772980413" src="https://github.com/user-attachments/assets/b898d171-5711-4d42-bc07-2de967b119ec" />
|
|
147
|
+
|
|
148
|
+
> 详细图文教程请参阅 [官方指南](https://cloud.tencent.com/developer/article/2626045)。
|
|
149
|
+
|
|
150
|
+
> ⚠️ 机器人创建后会自动出现在你的 QQ 消息列表中,并发送第一条消息。但在完成下面的配置之前,发消息会提示"该机器人去火星了",属于正常现象。
|
|
151
|
+
|
|
152
|
+
### 第二步 — 安装插件
|
|
153
|
+
|
|
154
|
+
**方式一:一键安装并启动(推荐)**
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
git clone https://github.com/tencent-connect/openclaw-qqbot.git && cd openclaw-qqbot
|
|
158
|
+
bash ./scripts/upgrade-and-run.sh --appid YOUR_APPID --secret YOUR_SECRET
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
脚本会自动完成:清理旧插件 → 安装依赖 → 注册插件 → 配置通道 → 启动服务。完成后可直接跳到[第四步](#第四步--启动并测试)。
|
|
162
|
+
|
|
163
|
+
**方式二:手动分步安装**
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
git clone https://github.com/tencent-connect/openclaw-qqbot.git && cd openclaw-qqbot
|
|
167
|
+
npm install --omit=dev
|
|
168
|
+
openclaw plugins install .
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 第三步 — 配置 OpenClaw
|
|
172
|
+
|
|
173
|
+
**方式一:通过 Wizard 配置(推荐)**
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
openclaw channels add --channel qqbot --token "AppID:AppSecret"
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**方式二:编辑配置文件**
|
|
180
|
+
|
|
181
|
+
编辑 `~/.openclaw/openclaw.json`:
|
|
182
|
+
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"channels": {
|
|
186
|
+
"qqbot": {
|
|
187
|
+
"enabled": true,
|
|
188
|
+
"appId": "你的 AppID",
|
|
189
|
+
"clientSecret": "你的 AppSecret"
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### 第四步 — 启动与测试
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
openclaw gateway
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
打开 QQ,找到你的机器人,发条消息试试!
|
|
202
|
+
|
|
203
|
+
<div align="center">
|
|
204
|
+
<img width="500" alt="聊天演示" src="https://github.com/user-attachments/assets/b2776c8b-de72-4e37-b34d-e8287ce45de1" />
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## ⚙️ 进阶配置
|
|
210
|
+
|
|
211
|
+
### 多账户配置(Multi-Bot)
|
|
212
|
+
|
|
213
|
+
支持在同一个 OpenClaw 实例下同时运行多个 QQ 机器人。
|
|
214
|
+
|
|
215
|
+
#### 配置方式
|
|
216
|
+
|
|
217
|
+
编辑 `~/.openclaw/openclaw.json`,在 `channels.qqbot` 下增加 `accounts` 字段:
|
|
218
|
+
|
|
219
|
+
```json
|
|
220
|
+
{
|
|
221
|
+
"channels": {
|
|
222
|
+
"qqbot": {
|
|
223
|
+
"enabled": true,
|
|
224
|
+
"appId": "111111111",
|
|
225
|
+
"clientSecret": "secret-of-bot-1",
|
|
226
|
+
|
|
227
|
+
"accounts": {
|
|
228
|
+
"bot2": {
|
|
229
|
+
"enabled": true,
|
|
230
|
+
"appId": "222222222",
|
|
231
|
+
"clientSecret": "secret-of-bot-2"
|
|
232
|
+
},
|
|
233
|
+
"bot3": {
|
|
234
|
+
"enabled": true,
|
|
235
|
+
"appId": "333333333",
|
|
236
|
+
"clientSecret": "secret-of-bot-3"
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**说明:**
|
|
245
|
+
|
|
246
|
+
- 顶层的 `appId` / `clientSecret` 是**默认账户**(accountId = `"default"`)
|
|
247
|
+
- `accounts` 下的每个 key(如 `bot2`、`bot3`)就是该账户的 `accountId`
|
|
248
|
+
- 每个账户都可以独立配置 `enabled`、`name`、`allowFrom`、`systemPrompt` 等字段
|
|
249
|
+
- 也可以不配顶层默认账户,只在 `accounts` 里配置所有机器人
|
|
250
|
+
|
|
251
|
+
通过 CLI 添加第二个机器人(如果框架支持 `--account` 参数):
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
openclaw channels add --channel qqbot --account bot2 --token "222222222:secret-of-bot-2"
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
#### 向指定账户的用户发送消息
|
|
258
|
+
|
|
259
|
+
使用 `openclaw message send` 发消息时,需要通过 `--account` 参数指定使用哪个机器人发送:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
# 使用默认机器人发送(不指定 --account 时自动使用 default)
|
|
263
|
+
openclaw message send --channel "qqbot" \
|
|
264
|
+
--target "qqbot:c2c:OPENID" \
|
|
265
|
+
--message "hello from default bot"
|
|
266
|
+
|
|
267
|
+
# 使用 bot2 发送
|
|
268
|
+
openclaw message send --channel "qqbot" \
|
|
269
|
+
--account bot2 \
|
|
270
|
+
--target "qqbot:c2c:OPENID" \
|
|
271
|
+
--message "hello from bot2"
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**Target 格式支持:**
|
|
275
|
+
|
|
276
|
+
| 格式 | 说明 |
|
|
277
|
+
|------|------|
|
|
278
|
+
| `qqbot:c2c:OPENID` | 私聊 |
|
|
279
|
+
| `qqbot:group:GROUP_OPENID` | 群聊 |
|
|
280
|
+
| `qqbot:channel:CHANNEL_ID` | 频道 |
|
|
281
|
+
|
|
282
|
+
> ⚠️ **注意**:每个机器人的用户 OpenID 是不同的。机器人 A 收到的用户 OpenID 不能用机器人 B 去发消息,否则会返回 500 错误。必须用对应机器人的 accountId 去给该机器人的用户发消息。
|
|
283
|
+
|
|
284
|
+
#### 工作原理
|
|
285
|
+
|
|
286
|
+
- 启动 `openclaw gateway` 后,所有 `enabled: true` 的账户会同时启动 WebSocket 连接
|
|
287
|
+
- 每个账户独立维护 Token 缓存(基于 `appId` 隔离),互不干扰
|
|
288
|
+
- 接收消息时,日志会带上 `[qqbot:accountId]` 前缀方便排查
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
### 语音能力配置(STT / TTS)
|
|
293
|
+
|
|
294
|
+
#### STT(语音转文字)— 自动转录用户发来的语音消息
|
|
295
|
+
|
|
296
|
+
STT 支持两级配置,按优先级查找:
|
|
297
|
+
|
|
298
|
+
| 优先级 | 配置路径 | 作用域 |
|
|
299
|
+
|--------|----------|--------|
|
|
300
|
+
| 1(highest) | `channels.qqbot.stt` | 插件专属 |
|
|
301
|
+
| 2(fallback) | `tools.media.audio.models[0]` | 框架级 |
|
|
302
|
+
|
|
303
|
+
```json
|
|
304
|
+
{
|
|
305
|
+
"channels": {
|
|
306
|
+
"qqbot": {
|
|
307
|
+
"stt": {
|
|
308
|
+
"provider": "your-provider",
|
|
309
|
+
"model": "your-stt-model"
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
- `provider` — 引用 `models.providers` 中的 key,自动继承 `baseUrl` 和 `apiKey`
|
|
317
|
+
- 设置 `enabled: false` 可禁用
|
|
318
|
+
- 配置后,用户发来的语音消息会自动转换(SILK→WAV)并转录为文字
|
|
319
|
+
|
|
320
|
+
#### TTS(文字转语音)— 机器人发送语音消息
|
|
321
|
+
|
|
322
|
+
| 优先级 | 配置路径 | 作用域 |
|
|
323
|
+
|--------|----------|--------|
|
|
324
|
+
| 1(highest) | `channels.qqbot.tts` | 插件专属 |
|
|
325
|
+
| 2(fallback) | `messages.tts` | 框架级 |
|
|
326
|
+
|
|
327
|
+
```json
|
|
328
|
+
{
|
|
329
|
+
"channels": {
|
|
330
|
+
"qqbot": {
|
|
331
|
+
"tts": {
|
|
332
|
+
"provider": "your-provider",
|
|
333
|
+
"model": "your-tts-model",
|
|
334
|
+
"voice": "your-voice"
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
- `provider` — 引用 `models.providers` 中的 key,自动继承 `baseUrl` 和 `apiKey`
|
|
342
|
+
- `voice` — 语音音色
|
|
343
|
+
- 设置 `enabled: false` 可禁用(默认:`true`)
|
|
344
|
+
- 配置后,AI 可使用 `<qqvoice>` 标签生成并发送语音消息
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## 🔄 升级
|
|
349
|
+
|
|
350
|
+
运行一键脚本即可升级并重启:
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
bash ./scripts/upgrade-and-run.sh
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
不传 `--appid` / `--secret` 参数时,脚本会自动读取 `~/.openclaw/openclaw.json` 中已有的配置。
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
# 首次配置或需要覆盖时
|
|
360
|
+
bash ./scripts/upgrade-and-run.sh --appid YOUR_APPID --secret YOUR_SECRET
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
<details>
|
|
364
|
+
<summary>完整选项</summary>
|
|
365
|
+
|
|
366
|
+
| 选项 | 说明 |
|
|
367
|
+
|------|------|
|
|
368
|
+
| `--appid <id>` | QQ 机器人 AppID |
|
|
369
|
+
| `--secret <secret>` | QQ 机器人 AppSecret |
|
|
370
|
+
| `--markdown <yes\|no>` | 是否启用 Markdown 消息格式(默认: no) |
|
|
371
|
+
| `-h, --help` | 显示帮助 |
|
|
372
|
+
|
|
373
|
+
也支持环境变量:`QQBOT_APPID`、`QQBOT_SECRET`、`QQBOT_TOKEN`(AppID:Secret)。
|
|
374
|
+
|
|
375
|
+
</details>
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## 📚 文档与链接
|
|
380
|
+
|
|
381
|
+
- [命令参考](docs/commands.md) — OpenClaw CLI 常用命令
|
|
382
|
+
- [更新日志](CHANGELOG.md) — 各版本变更记录
|
|
383
|
+
|
|
384
|
+
## ⭐ Star History
|
|
385
|
+
|
|
386
|
+
<div align="center">
|
|
387
|
+
|
|
388
|
+
[](https://www.star-history.com/#tencent-connect/openclaw-qqbot&type=date&legend=top-left)
|
|
389
|
+
|
|
390
|
+
</div>
|
package/bin/qqbot-cli.js
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* QQBot CLI - 用于升级和管理 QQBot 插件
|
|
5
|
+
*
|
|
6
|
+
* 用法:
|
|
7
|
+
* npx openclaw-qqbot upgrade # 升级插件
|
|
8
|
+
* npx openclaw-qqbot install # 安装插件
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { execSync } from 'child_process';
|
|
12
|
+
import { existsSync, readFileSync, writeFileSync, rmSync } from 'fs';
|
|
13
|
+
import { homedir } from 'os';
|
|
14
|
+
import { join, dirname } from 'path';
|
|
15
|
+
import { fileURLToPath } from 'url';
|
|
16
|
+
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
const __dirname = dirname(__filename);
|
|
19
|
+
|
|
20
|
+
// 获取包的根目录
|
|
21
|
+
const PKG_ROOT = join(__dirname, '..');
|
|
22
|
+
|
|
23
|
+
const args = process.argv.slice(2);
|
|
24
|
+
const command = args[0];
|
|
25
|
+
|
|
26
|
+
// 检测使用的是 clawdbot 还是 openclaw
|
|
27
|
+
function detectInstallation() {
|
|
28
|
+
const home = homedir();
|
|
29
|
+
if (existsSync(join(home, '.openclaw'))) {
|
|
30
|
+
return 'openclaw';
|
|
31
|
+
}
|
|
32
|
+
if (existsSync(join(home, '.clawdbot'))) {
|
|
33
|
+
return 'clawdbot';
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 需要清理的所有可能的插件 ID / 包名(原仓库 + 本仓库 + 框架推断名)
|
|
39
|
+
const PLUGIN_IDS = ['qqbot', 'openclaw-qq', '@sliverp/qqbot', '@tencent-connect/openclaw-qq', '@tencent-connect/qqbot', '@tencent-connect/openclaw-qqbot', 'openclaw-qqbot'];
|
|
40
|
+
// 可能的扩展目录名
|
|
41
|
+
const EXTENSION_DIR_NAMES = ['qqbot', 'openclaw-qq', 'openclaw-qqbot'];
|
|
42
|
+
|
|
43
|
+
// 清理旧版本插件,返回旧的 qqbot 配置
|
|
44
|
+
function cleanupInstallation(appName) {
|
|
45
|
+
const home = homedir();
|
|
46
|
+
const appDir = join(home, `.${appName}`);
|
|
47
|
+
const configFile = join(appDir, `${appName}.json`);
|
|
48
|
+
|
|
49
|
+
let oldQqbotConfig = null;
|
|
50
|
+
|
|
51
|
+
console.log(`\n>>> 处理 ${appName} 安装...`);
|
|
52
|
+
|
|
53
|
+
// 1. 先读取旧的 qqbot 配置(尝试所有可能的 channel key)
|
|
54
|
+
if (existsSync(configFile)) {
|
|
55
|
+
try {
|
|
56
|
+
const config = JSON.parse(readFileSync(configFile, 'utf8'));
|
|
57
|
+
if (config.channels?.qqbot) {
|
|
58
|
+
oldQqbotConfig = { ...config.channels.qqbot };
|
|
59
|
+
console.log('已保存旧的 qqbot 配置');
|
|
60
|
+
}
|
|
61
|
+
} catch (err) {
|
|
62
|
+
console.error('读取配置文件失败:', err.message);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 2. 删除所有可能的旧扩展目录
|
|
67
|
+
for (const dirName of EXTENSION_DIR_NAMES) {
|
|
68
|
+
const extensionDir = join(appDir, 'extensions', dirName);
|
|
69
|
+
if (existsSync(extensionDir)) {
|
|
70
|
+
console.log(`删除旧版本插件: ${extensionDir}`);
|
|
71
|
+
rmSync(extensionDir, { recursive: true, force: true });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 3. 清理配置文件中所有可能的插件 ID 相关字段
|
|
76
|
+
if (existsSync(configFile)) {
|
|
77
|
+
console.log('清理配置文件中的插件字段...');
|
|
78
|
+
try {
|
|
79
|
+
const config = JSON.parse(readFileSync(configFile, 'utf8'));
|
|
80
|
+
|
|
81
|
+
for (const id of PLUGIN_IDS) {
|
|
82
|
+
// 删除 channels.<id>
|
|
83
|
+
if (config.channels?.[id]) {
|
|
84
|
+
delete config.channels[id];
|
|
85
|
+
console.log(` - 已删除 channels.${id}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 删除 plugins.entries.<id>
|
|
89
|
+
if (config.plugins?.entries?.[id]) {
|
|
90
|
+
delete config.plugins.entries[id];
|
|
91
|
+
console.log(` - 已删除 plugins.entries.${id}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 删除 plugins.installs.<id>
|
|
95
|
+
if (config.plugins?.installs?.[id]) {
|
|
96
|
+
delete config.plugins.installs[id];
|
|
97
|
+
console.log(` - 已删除 plugins.installs.${id}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 删除 plugins.allow 中的 <id>
|
|
101
|
+
if (Array.isArray(config.plugins?.allow)) {
|
|
102
|
+
const before = config.plugins.allow.length;
|
|
103
|
+
config.plugins.allow = config.plugins.allow.filter((x) => x !== id);
|
|
104
|
+
if (config.plugins.allow.length !== before) {
|
|
105
|
+
console.log(` - 已删除 plugins.allow.${id}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
writeFileSync(configFile, JSON.stringify(config, null, 2));
|
|
111
|
+
console.log('配置文件已更新');
|
|
112
|
+
} catch (err) {
|
|
113
|
+
console.error('清理配置文件失败:', err.message);
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
console.log(`未找到配置文件: ${configFile}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return oldQqbotConfig;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 执行命令并继承 stdio
|
|
123
|
+
function runCommand(cmd, args = []) {
|
|
124
|
+
try {
|
|
125
|
+
execSync([cmd, ...args].join(' '), { stdio: 'inherit' });
|
|
126
|
+
return true;
|
|
127
|
+
} catch (err) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 升级命令
|
|
133
|
+
function upgrade() {
|
|
134
|
+
console.log('=== QQBot 插件升级脚本 ===');
|
|
135
|
+
|
|
136
|
+
let foundInstallation = null;
|
|
137
|
+
let savedConfig = null;
|
|
138
|
+
const home = homedir();
|
|
139
|
+
|
|
140
|
+
// 检查 openclaw
|
|
141
|
+
if (existsSync(join(home, '.openclaw'))) {
|
|
142
|
+
savedConfig = cleanupInstallation('openclaw');
|
|
143
|
+
foundInstallation = 'openclaw';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 检查 clawdbot
|
|
147
|
+
if (existsSync(join(home, '.clawdbot'))) {
|
|
148
|
+
const clawdbotConfig = cleanupInstallation('clawdbot');
|
|
149
|
+
if (!savedConfig) savedConfig = clawdbotConfig;
|
|
150
|
+
foundInstallation = 'clawdbot';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!foundInstallation) {
|
|
154
|
+
console.log('\n未找到 clawdbot 或 openclaw 安装目录');
|
|
155
|
+
console.log('请确认已安装 clawdbot 或 openclaw');
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
console.log('\n=== 清理完成 ===');
|
|
160
|
+
|
|
161
|
+
// 自动安装插件
|
|
162
|
+
console.log('\n[1/2] 安装新版本插件...');
|
|
163
|
+
runCommand(foundInstallation, ['plugins', 'install', 'openclaw-qqbot']);
|
|
164
|
+
|
|
165
|
+
// 自动配置通道(使用保存的 appId 和 clientSecret)
|
|
166
|
+
console.log('\n[2/2] 配置机器人通道...');
|
|
167
|
+
if (savedConfig?.appId && savedConfig?.clientSecret) {
|
|
168
|
+
const token = `${savedConfig.appId}:${savedConfig.clientSecret}`;
|
|
169
|
+
console.log(`使用已保存的配置: appId=${savedConfig.appId}`);
|
|
170
|
+
runCommand(foundInstallation, ['channels', 'add', '--channel', 'qqbot', '--token', `"${token}"`]);
|
|
171
|
+
|
|
172
|
+
// 恢复其他配置项(如 markdownSupport)
|
|
173
|
+
if (savedConfig.markdownSupport !== undefined) {
|
|
174
|
+
runCommand(foundInstallation, ['config', 'set', 'channels.qqbot.markdownSupport', String(savedConfig.markdownSupport)]);
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
console.log('未找到已保存的 qqbot 配置,请手动配置:');
|
|
178
|
+
console.log(` ${foundInstallation} channels add --channel qqbot --token "AppID:AppSecret"`);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
console.log('\n=== 升级完成 ===');
|
|
183
|
+
console.log(`\n可以运行以下命令前台运行启动机器人:`);
|
|
184
|
+
console.log(` ${foundInstallation} gateway stop && ${foundInstallation} gateway --port 18789 --verbose`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// 安装命令
|
|
188
|
+
function install() {
|
|
189
|
+
console.log('=== QQBot 插件安装 ===');
|
|
190
|
+
|
|
191
|
+
const cmd = detectInstallation();
|
|
192
|
+
if (!cmd) {
|
|
193
|
+
console.log('未找到 clawdbot 或 openclaw 安装');
|
|
194
|
+
console.log('请先安装 openclaw 或 clawdbot');
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
console.log(`\n使用 ${cmd} 安装插件...`);
|
|
199
|
+
runCommand(cmd, ['plugins', 'install', '@tencent-connect/openclaw-qqbot']);
|
|
200
|
+
|
|
201
|
+
console.log('\n=== 安装完成 ===');
|
|
202
|
+
console.log('\n请配置机器人通道:');
|
|
203
|
+
console.log(` ${cmd} channels add --channel qqbot --token "AppID:AppSecret"`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// 显示帮助
|
|
207
|
+
function showHelp() {
|
|
208
|
+
console.log(`
|
|
209
|
+
QQBot CLI - QQ机器人插件管理工具
|
|
210
|
+
|
|
211
|
+
用法:
|
|
212
|
+
npx openclaw-qqbot <命令>
|
|
213
|
+
|
|
214
|
+
命令:
|
|
215
|
+
upgrade 清理旧版本插件(升级前执行)
|
|
216
|
+
install 安装插件到 openclaw/clawdbot
|
|
217
|
+
|
|
218
|
+
示例:
|
|
219
|
+
npx openclaw-qqbot upgrade
|
|
220
|
+
npx openclaw-qqbot install
|
|
221
|
+
`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// 主入口
|
|
225
|
+
switch (command) {
|
|
226
|
+
case 'upgrade':
|
|
227
|
+
upgrade();
|
|
228
|
+
break;
|
|
229
|
+
case 'install':
|
|
230
|
+
install();
|
|
231
|
+
break;
|
|
232
|
+
case '-h':
|
|
233
|
+
case '--help':
|
|
234
|
+
case 'help':
|
|
235
|
+
showHelp();
|
|
236
|
+
break;
|
|
237
|
+
default:
|
|
238
|
+
if (command) {
|
|
239
|
+
console.log(`未知命令: ${command}`);
|
|
240
|
+
}
|
|
241
|
+
showHelp();
|
|
242
|
+
process.exit(command ? 1 : 0);
|
|
243
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "openclaw-qqbot",
|
|
3
|
+
"name": "OpenClaw QQ Bot",
|
|
4
|
+
"description": "QQ Bot channel plugin with message support, cron jobs, and proactive messaging",
|
|
5
|
+
"channels": ["qqbot"],
|
|
6
|
+
"skills": ["skills/qqbot-cron", "skills/qqbot-media"],
|
|
7
|
+
"capabilities": {
|
|
8
|
+
"proactiveMessaging": true,
|
|
9
|
+
"cronJobs": true
|
|
10
|
+
},
|
|
11
|
+
"configSchema": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"additionalProperties": false,
|
|
14
|
+
"properties": {}
|
|
15
|
+
}
|
|
16
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
+
declare const plugin: {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
configSchema: unknown;
|
|
7
|
+
register(api: OpenClawPluginApi): void;
|
|
8
|
+
};
|
|
9
|
+
export default plugin;
|
|
10
|
+
export { qqbotPlugin } from "./src/channel.js";
|
|
11
|
+
export { setQQBotRuntime, getQQBotRuntime } from "./src/runtime.js";
|
|
12
|
+
export { qqbotOnboardingAdapter } from "./src/onboarding.js";
|
|
13
|
+
export * from "./src/types.js";
|
|
14
|
+
export * from "./src/api.js";
|
|
15
|
+
export * from "./src/config.js";
|
|
16
|
+
export * from "./src/gateway.js";
|
|
17
|
+
export * from "./src/outbound.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
2
|
+
import { qqbotPlugin } from "./src/channel.js";
|
|
3
|
+
import { setQQBotRuntime } from "./src/runtime.js";
|
|
4
|
+
const plugin = {
|
|
5
|
+
id: "openclaw-qqbot",
|
|
6
|
+
name: "QQ Bot",
|
|
7
|
+
description: "QQ Bot channel plugin",
|
|
8
|
+
configSchema: emptyPluginConfigSchema(),
|
|
9
|
+
register(api) {
|
|
10
|
+
setQQBotRuntime(api.runtime);
|
|
11
|
+
api.registerChannel({ plugin: qqbotPlugin });
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
export default plugin;
|
|
15
|
+
export { qqbotPlugin } from "./src/channel.js";
|
|
16
|
+
export { setQQBotRuntime, getQQBotRuntime } from "./src/runtime.js";
|
|
17
|
+
export { qqbotOnboardingAdapter } from "./src/onboarding.js";
|
|
18
|
+
export * from "./src/types.js";
|
|
19
|
+
export * from "./src/api.js";
|
|
20
|
+
export * from "./src/config.js";
|
|
21
|
+
export * from "./src/gateway.js";
|
|
22
|
+
export * from "./src/outbound.js";
|