@jerryliang122/openclaw-qqbot 1.0.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 +967 -0
- package/README.zh.md +790 -0
- package/dist/index.cjs +18072 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1222 -0
- package/index.ts +92 -0
- package/openclaw.plugin.json +38 -0
- package/package.json +69 -0
- package/preload.cjs +19 -0
- package/scripts/link-sdk-core.cjs +268 -0
- package/scripts/proactive-api-server.ts +369 -0
- package/scripts/send-proactive.ts +293 -0
- package/scripts/test-sendmedia.ts +116 -0
- package/skills/qqbot-channel/SKILL.md +285 -0
- package/skills/qqbot-channel/references/api_references.md +521 -0
- package/skills/qqbot-remind/SKILL.md +159 -0
- package/skills/qqbot-upgrade/SKILL.md +56 -0
- package/src/adapter/contract.ts +63 -0
- package/src/adapter/lint.ts +144 -0
- package/src/adapter/media.ts +40 -0
- package/src/adapter/pairing.ts +95 -0
- package/src/adapter/resolve.ts +255 -0
- package/src/adapter/setup.ts +13 -0
- package/src/adapter/webhook.ts +248 -0
- package/src/adapter/workspace.ts +21 -0
- package/src/agent-prompt-adapter.ts +26 -0
- package/src/bot-instance.ts +60 -0
- package/src/channel.ts +230 -0
- package/src/commands/bot-approve.ts +143 -0
- package/src/commands/bot-clear-storage.ts +114 -0
- package/src/commands/bot-group-always.ts +62 -0
- package/src/commands/bot-group-info.ts +48 -0
- package/src/commands/bot-help.ts +40 -0
- package/src/commands/bot-logs.ts +248 -0
- package/src/commands/bot-me.ts +18 -0
- package/src/commands/bot-pairing.ts +50 -0
- package/src/commands/bot-ping.ts +33 -0
- package/src/commands/bot-streaming.ts +55 -0
- package/src/commands/bot-upgrade.ts +56 -0
- package/src/commands/bot-version.ts +41 -0
- package/src/commands/config-util.ts +96 -0
- package/src/commands/index.ts +51 -0
- package/src/config.ts +403 -0
- package/src/constants.ts +6 -0
- package/src/dispatch/body-assembler.ts +308 -0
- package/src/dispatch/ctx-builder.ts +127 -0
- package/src/dispatch/dispatch.ts +667 -0
- package/src/dispatch/envelope-builder.ts +112 -0
- package/src/dispatch/index.ts +2 -0
- package/src/features/approval-capability.ts +302 -0
- package/src/features/approval-helpers.ts +271 -0
- package/src/features/approval-utils.ts +21 -0
- package/src/features/command-panel.ts +301 -0
- package/src/features/credential-backup.ts +74 -0
- package/src/features/group-mode-store.ts +79 -0
- package/src/features/history-store.ts +75 -0
- package/src/features/msgid-cache.ts +55 -0
- package/src/features/outbound-echo-store.ts +46 -0
- package/src/features/proactive-budget.ts +57 -0
- package/src/features/proactive.ts +549 -0
- package/src/features/question-helpers.ts +771 -0
- package/src/features/quota-manager.ts +173 -0
- package/src/features/ref-index-store.ts +289 -0
- package/src/features/secret-input-store.ts +118 -0
- package/src/features/secret-store-cli.ts +324 -0
- package/src/features/typing-refresh.ts +51 -0
- package/src/features/update-checker.ts +166 -0
- package/src/gateway/event-handlers.ts +456 -0
- package/src/gateway/index.ts +3 -0
- package/src/gateway/lifecycle.ts +236 -0
- package/src/gateway/middleware-setup.ts +173 -0
- package/src/gateway/qqbot-gateway.ts +458 -0
- package/src/gateway-adapter.ts +44 -0
- package/src/heartbeat-adapter.ts +57 -0
- package/src/message-adapter.ts +40 -0
- package/src/messaging-adapter.ts +78 -0
- package/src/middleware/access-control.ts +125 -0
- package/src/middleware/attachment.ts +373 -0
- package/src/middleware/inbound-guard.ts +102 -0
- package/src/middleware/policy-injector.ts +71 -0
- package/src/middleware/secret-capture.ts +161 -0
- package/src/middleware/typing.ts +110 -0
- package/src/openclaw-plugin-sdk.d.ts +543 -0
- package/src/outbound/chunker.ts +80 -0
- package/src/outbound/debounce.ts +102 -0
- package/src/outbound/deliver-pipeline.ts +235 -0
- package/src/outbound/index.ts +3 -0
- package/src/outbound/local-file-router.ts +145 -0
- package/src/outbound/media-send.ts +408 -0
- package/src/outbound/outbound-service.ts +298 -0
- package/src/outbound/reply-limiter.ts +139 -0
- package/src/outbound/sanitize.ts +32 -0
- package/src/outbound/streaming-controller.ts +332 -0
- package/src/outbound/target.ts +109 -0
- package/src/outbound-adapter.ts +323 -0
- package/src/plugin-base.ts +42 -0
- package/src/request-context.ts +50 -0
- package/src/runtime.ts +42 -0
- package/src/setup/account-key.ts +41 -0
- package/src/setup/finalize.ts +110 -0
- package/src/setup/login.ts +197 -0
- package/src/setup/surface.ts +40 -0
- package/src/status-adapter.ts +56 -0
- package/src/tools/platform.ts +149 -0
- package/src/tools/remind.ts +308 -0
- package/src/tools/secret-input.ts +185 -0
- package/src/types-augment.d.ts +54 -0
- package/src/types-plugin.ts +82 -0
- package/src/types.ts +620 -0
- package/src/typing-lifecycle.ts +182 -0
- package/src/utils/mention.ts +52 -0
- package/src/utils/pkg-version.ts +23 -0
- package/src/utils/platform.ts +459 -0
- package/src/utils/plugin-logger.ts +104 -0
- package/src/utils/ssrf-guard.ts +132 -0
- package/src/utils/stt.ts +150 -0
- package/src/utils/voice-text.ts +61 -0
- package/tsconfig.json +17 -0
- package/tsup.config.ts +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,967 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img width="120" src="https://img.shields.io/badge/🤖-QQ_Bot-blue?style=for-the-badge" alt="QQ Bot" />
|
|
4
|
+
|
|
5
|
+
# QQ Bot Channel Plugin for OpenClaw
|
|
6
|
+
|
|
7
|
+
**Independently maintained fork — framework-delegated group queueing, room-event ingestion for full-mode groups, and passive-first outbound delivery**
|
|
8
|
+
|
|
9
|
+
**Connect your AI assistant to QQ — private chat, group chat, and rich media, all in one plugin.**
|
|
10
|
+
|
|
11
|
+
### 🚀 Current Version: `v1.0.0`
|
|
12
|
+
|
|
13
|
+
[](./LICENSE)
|
|
14
|
+
[](https://bot.q.qq.com/wiki/)
|
|
15
|
+
[](https://github.com/jerryliang122/qqbot-openclaw)
|
|
16
|
+
[](https://nodejs.org/)
|
|
17
|
+
[](https://www.typescriptlang.org/)
|
|
18
|
+
[](https://github.com/jerryliang122/qqbot-openclaw)
|
|
19
|
+
|
|
20
|
+
<br/>
|
|
21
|
+
|
|
22
|
+
**[简体中文](README.zh.md) | English**
|
|
23
|
+
|
|
24
|
+
> This is an **independently maintained fork** with its own versioning (v1.x, decoupled from the upstream 2.x line). Upstream: [tencent-connect/openclaw-qqbot](https://github.com/tencent-connect/openclaw-qqbot).
|
|
25
|
+
>
|
|
26
|
+
> **Requirements**: OpenClaw `>= 2026.9.2` · Published to npm as [`@jerryliang122/openclaw-qqbot`](https://www.npmjs.com/package/@jerryliang122/openclaw-qqbot). See [CHANGELOG](CHANGELOG.md) for differences vs the old version and the upgrade guide.
|
|
27
|
+
|
|
28
|
+
Scan to join the QQ group chat
|
|
29
|
+
|
|
30
|
+
<img width="400" alt="QQ QR Code" src="./docs/images/developer-group.png" />
|
|
31
|
+
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## ✨ Features
|
|
37
|
+
|
|
38
|
+
| Feature | Description |
|
|
39
|
+
|---------|-------------|
|
|
40
|
+
| 🔄 **Group Queueing** | Group messages dispatch immediately; queueing/merging is delegated to the OpenClaw framework followup queue (collect mode) — active turns are never interrupted, bursts merge into one batch |
|
|
41
|
+
| 👁️ **Room Events** (opt-in) | In groups with full-message push mode, the bot can read every message like a normal member; un-@'d traffic arrives as passive room events (read-only — the AI speaks only via the proactive `message` tool) |
|
|
42
|
+
| 🔔 **Three Wake Modes** | @mention, name patterns (`mentionPatterns`, e.g. "沈处"), and quote-of-bot-message all trigger normal replies |
|
|
43
|
+
| 📤 **Passive-First Outbound** | Every send prefers passive reply (msg_id) to conserve the 1000/day proactive budget; quota-aware fallback never hard-fails |
|
|
44
|
+
| 🔒 **Multi-Scene** | C2C private chat, group chat (@mention / autonomous / room-event modes) |
|
|
45
|
+
| 👥 **Group Fine-Tuning** | Per-group @trigger rules, tool policies, custom prompts, history modes, queueing config, room-event policy |
|
|
46
|
+
| 🌐 **Dual Transport** | WebSocket (default) or Webhook (HTTP callback) — switch via config |
|
|
47
|
+
| 🖼️ **Rich Media** | Send & receive images, voice, video, and files |
|
|
48
|
+
| 🎙️ **Voice (STT/TTS)** | Speech-to-text transcription & text-to-speech replies |
|
|
49
|
+
| 🔄 **Update Check** | `/bot-upgrade` checks the npm registry for new versions and links the upgrade guide |
|
|
50
|
+
| ⏰ **Scheduled Push** | Proactive message delivery via scheduled tasks |
|
|
51
|
+
| 🔗 **URL Support** | Direct URL sending in private chat (no restrictions) |
|
|
52
|
+
| ⌨️ **Typing Indicator** | "Bot is typing..." status shown in real-time |
|
|
53
|
+
| 📝 **Markdown** | Full Markdown formatting support |
|
|
54
|
+
| 🛠️ **Commands** | Native OpenClaw command integration |
|
|
55
|
+
| 💬 **Quoted Context** | Parses the original message a user is replying to and injects it into AI context, so the model always knows exactly which message is being referenced |
|
|
56
|
+
| 📦 **Large File Support** | Auto chunked upload for large files (parallel upload with retry), up to 100 MB |
|
|
57
|
+
| 🔐 **Command Execution Approval** | AI requests approval via Inline Keyboard buttons before executing commands — tap to allow or deny |
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 📸 Feature Showcase
|
|
62
|
+
|
|
63
|
+
> **Note:** This plugin serves as a **message channel** only — it relays messages between QQ and OpenClaw. Capabilities like image understanding, voice transcription, drawing, etc. depend on the **AI model** you configure and the **skills** installed in OpenClaw, not on this plugin itself.
|
|
64
|
+
|
|
65
|
+
### 💬 Quoted Message Context
|
|
66
|
+
|
|
67
|
+
When a user quotes a message in QQ, the plugin automatically parses the quoted message content and injects it into the AI context, so the model clearly knows "which message the user is replying to" and gives more accurate responses. Supports text and media messages (image/voice/video/file), and works across devices.
|
|
68
|
+
|
|
69
|
+
<img width="360" src="docs/images/ref-msg.png" alt="Quoted Message Context Demo" />
|
|
70
|
+
|
|
71
|
+
### 🎙️ Voice Messages (STT)
|
|
72
|
+
|
|
73
|
+
With STT configured, the plugin automatically transcribes voice messages to text before passing them to AI. The whole process is transparent to the user — sending voice feels as natural as sending text.
|
|
74
|
+
|
|
75
|
+
> **You**: *(send a voice message)* "What's the weather like tomorrow in Shenzhen?"
|
|
76
|
+
>
|
|
77
|
+
> **QQBot**: Tomorrow (March 7, Saturday) Shenzhen weather forecast 🌤️ ...
|
|
78
|
+
|
|
79
|
+
<img width="360" src="docs/images/voice-stt.jpg" alt="Voice STT Demo" />
|
|
80
|
+
|
|
81
|
+
### 📄 File Understanding
|
|
82
|
+
|
|
83
|
+
Send any file to the bot — novels, reports, spreadsheets — AI automatically recognizes the content and gives an intelligent reply.
|
|
84
|
+
|
|
85
|
+
> **You**: *(send a TXT file of "War and Peace")*
|
|
86
|
+
>
|
|
87
|
+
> **QQBot**: Got it! You uploaded the Chinese version of "War and Peace" by Leo Tolstoy. This appears to be the opening of Chapter 1...
|
|
88
|
+
|
|
89
|
+
<img width="360" src="docs/images/file-understand.jpg" alt="File Understanding Demo" />
|
|
90
|
+
|
|
91
|
+
### 🖼️ Image Understanding
|
|
92
|
+
|
|
93
|
+
If your main model supports vision (e.g. Tencent Hunyuan `hunyuan-vision`), AI can understand images too. This is a general multimodal capability, not plugin-specific.
|
|
94
|
+
|
|
95
|
+
> **You**: *(send an image)*
|
|
96
|
+
>
|
|
97
|
+
> **QQBot**: Haha, so cute! Is that a QQ penguin in a lobster costume? 🦞🐧 ...
|
|
98
|
+
|
|
99
|
+
<img width="360" src="docs/images/image-understand.jpg" alt="Image Understanding Demo" />
|
|
100
|
+
|
|
101
|
+
### 🎨 Image Sending
|
|
102
|
+
|
|
103
|
+
> **You**: Draw me a cat
|
|
104
|
+
>
|
|
105
|
+
> **QQBot**: Here you go! 🐱
|
|
106
|
+
|
|
107
|
+
AI can send images directly. Supports local paths and URLs. Formats: jpg/png/gif/webp/bmp.
|
|
108
|
+
|
|
109
|
+
<img width="360" src="docs/images/image-send.jpg" alt="Image Generation Demo" />
|
|
110
|
+
|
|
111
|
+
### 🔊 Voice Sending
|
|
112
|
+
|
|
113
|
+
> **You**: Tell me a joke in voice
|
|
114
|
+
>
|
|
115
|
+
> **QQBot**: *(sends a voice message)*
|
|
116
|
+
|
|
117
|
+
AI can send voice messages directly. Formats: mp3/wav/silk/ogg. No ffmpeg required.
|
|
118
|
+
|
|
119
|
+
<img width="360" src="docs/images/voice-send.jpg" alt="TTS Voice Demo" />
|
|
120
|
+
|
|
121
|
+
### ⏰ Scheduled Reminder (Proactive Message)
|
|
122
|
+
|
|
123
|
+
> **You**: Remind me to eat in 5 minutes
|
|
124
|
+
>
|
|
125
|
+
> **QQBot**: confirms the reminder first, then proactively sends a voice + text reminder when time is up
|
|
126
|
+
|
|
127
|
+
This capability depends on OpenClaw cron scheduling and proactive messaging. If no reminder arrives, a common reason is QQ-side interception of bot proactive messages.
|
|
128
|
+
|
|
129
|
+
<img width="360" src="docs/images/reminder.jpg" alt="Scheduled Reminder Demo" />
|
|
130
|
+
|
|
131
|
+
### 📎 File Sending
|
|
132
|
+
|
|
133
|
+
> **You**: Extract chapter 1 of War and Peace and send it as a file
|
|
134
|
+
>
|
|
135
|
+
> **QQBot**: *(sends a .txt file)*
|
|
136
|
+
|
|
137
|
+
AI can send files directly, in any format.
|
|
138
|
+
|
|
139
|
+
<img width="360" src="docs/images/file-send.jpg" alt="File Sending Demo" />
|
|
140
|
+
|
|
141
|
+
Large file transfer is supported: images up to 20MB, videos up to 30MB, attachments up to 100MB, with a daily transfer limit of 2GB.
|
|
142
|
+
|
|
143
|
+
<img width="360" src="docs/images/large-file-transfer.jpg" alt="Large File Transfer Demo" />
|
|
144
|
+
|
|
145
|
+
### 🔐 Command Execution Approval
|
|
146
|
+
|
|
147
|
+
When the AI needs to execute a command, the plugin sends an approval request via QQ message with interactive buttons — tap **✅ Allow Once**, **⭐ Always Allow**, or **❌ Deny** to control whether the command runs.
|
|
148
|
+
|
|
149
|
+
Use the `/bot-approve` command to manage the approval mode (allowlist / off / strict).
|
|
150
|
+
|
|
151
|
+
<img width="360" src="docs/images/approve.png" alt="Command Execution Approval Demo" />
|
|
152
|
+
|
|
153
|
+
### 🎬 Video Sending
|
|
154
|
+
|
|
155
|
+
> **You**: Send me a demo video
|
|
156
|
+
>
|
|
157
|
+
> **QQBot**: *(sends a video)*
|
|
158
|
+
|
|
159
|
+
AI can send videos directly. Supports local files and URLs.
|
|
160
|
+
|
|
161
|
+
<img width="360" src="docs/images/video-send.jpg" alt="Video Sending Demo" />
|
|
162
|
+
|
|
163
|
+
> **Under the hood:** Upload dedup caching, ordered queue delivery, and multi-layer audio format fallback.
|
|
164
|
+
|
|
165
|
+
### 🛠️ Slash Commands
|
|
166
|
+
|
|
167
|
+
The plugin provides built-in slash commands that are intercepted before reaching the AI queue, giving instant responses for diagnostics and management.
|
|
168
|
+
|
|
169
|
+
#### `/bot-ping` — Latency Test
|
|
170
|
+
|
|
171
|
+
> **You**: `/bot-ping`
|
|
172
|
+
>
|
|
173
|
+
> **QQBot**: ✅ pong!⏱ Latency: 602ms (network: 602ms, plugin: 0ms)
|
|
174
|
+
|
|
175
|
+
Measures end-to-end latency from QQ server push to plugin response, broken down into network transport and plugin processing time.
|
|
176
|
+
|
|
177
|
+
<img width="360" src="docs/images/slash-ping.jpg" alt="Ping Demo" />
|
|
178
|
+
|
|
179
|
+
#### `/bot-version` — Version Info
|
|
180
|
+
|
|
181
|
+
> **You**: `/bot-version`
|
|
182
|
+
>
|
|
183
|
+
> **QQBot**: 🦞 Framework: OpenClaw 2026.9.2 / 🤖 Plugin: v1.0.0 / 🌟 GitHub repo
|
|
184
|
+
|
|
185
|
+
Shows framework version, plugin version, and a direct link to the official repository.
|
|
186
|
+
|
|
187
|
+
<img width="360" src="docs/images/slash-version.jpg" alt="Version Demo" />
|
|
188
|
+
|
|
189
|
+
#### `/bot-help` — Command List
|
|
190
|
+
|
|
191
|
+
> **You**: `/bot-help`
|
|
192
|
+
>
|
|
193
|
+
> **QQBot**: Lists all available slash commands with clickable shortcuts.
|
|
194
|
+
|
|
195
|
+
<img width="360" src="docs/images/slash-help.jpg" alt="Help Demo" />
|
|
196
|
+
|
|
197
|
+
#### `/bot-upgrade` — Version Check & Upgrade Guide
|
|
198
|
+
|
|
199
|
+
> **You**: `/bot-upgrade`
|
|
200
|
+
>
|
|
201
|
+
> **QQBot**: 📌 Current: v1.0.0 / 🆕 New version available / 📖 Upgrade guide link
|
|
202
|
+
|
|
203
|
+
Checks the installed version against the npm registry (`@jerryliang122/openclaw-qqbot`) and returns a link to the upgrade guide (repo CHANGELOG by default; override with `channels.qqbot.upgradeUrl`). Actual upgrading is done on the host via `openclaw plugins install` — see [Getting Started](#-getting-started).
|
|
204
|
+
|
|
205
|
+
<img width="360" src="docs/images/hot-update.jpg" alt="Upgrade Demo" />
|
|
206
|
+
|
|
207
|
+
#### `/bot-logs` — Log Export
|
|
208
|
+
|
|
209
|
+
> **You**: `/bot-logs`
|
|
210
|
+
>
|
|
211
|
+
> **QQBot**: 📋 Logs packaged (~2000 lines), sending file... *(sends a .txt file)*
|
|
212
|
+
|
|
213
|
+
Exports the last ~2000 lines of gateway logs as a file for quick troubleshooting.
|
|
214
|
+
|
|
215
|
+
<img width="360" src="docs/images/slash-logs.jpg" alt="Logs Demo" />
|
|
216
|
+
|
|
217
|
+
#### Usage Help
|
|
218
|
+
|
|
219
|
+
All commands support a `?` suffix to show usage:
|
|
220
|
+
|
|
221
|
+
> **You**: `/bot-upgrade ?`
|
|
222
|
+
>
|
|
223
|
+
> **QQBot**: 📖 /bot-upgrade usage: …
|
|
224
|
+
|
|
225
|
+
#### `/bot-approve` — Approval Configuration
|
|
226
|
+
|
|
227
|
+
> **You**: `/bot-approve`
|
|
228
|
+
>
|
|
229
|
+
> **QQBot**: 🔐 Command Execution Approval — Enable / Disable / Strict mode / Reset / View current config
|
|
230
|
+
|
|
231
|
+
Manage the AI command execution approval policy. Supported subcommands:
|
|
232
|
+
|
|
233
|
+
| Subcommand | Description |
|
|
234
|
+
|------------|-------------|
|
|
235
|
+
| `/bot-approve on` | Enable approval (allowlist mode, recommended) |
|
|
236
|
+
| `/bot-approve off` | Disable approval — commands execute directly |
|
|
237
|
+
| `/bot-approve always` | Strict mode — every execution requires approval |
|
|
238
|
+
| `/bot-approve reset` | Restore framework defaults |
|
|
239
|
+
| `/bot-approve status` | View current approval config |
|
|
240
|
+
|
|
241
|
+
#### `/bot-clear-storage` — Clear files generated through QQBot conversations and downloaded resources (stored on the host running OpenClaw)
|
|
242
|
+
|
|
243
|
+
`/bot-clear-storage` lists files generated by the conversation and files in the downloaded resources directory. Use `/bot-clear-storage --force` to confirm deletion.
|
|
244
|
+
|
|
245
|
+
#### `/bot-group-always` — Group Response Mode Toggle
|
|
246
|
+
|
|
247
|
+
> **You**: `/bot-group-always`
|
|
248
|
+
>
|
|
249
|
+
> **QQBot**: 🤖 Group autonomous mode: ❌ @mention required
|
|
250
|
+
|
|
251
|
+
Toggle group @trigger behavior at runtime — changes persist instantly, no restart needed:
|
|
252
|
+
|
|
253
|
+
| Subcommand | Description |
|
|
254
|
+
|------------|-------------|
|
|
255
|
+
| `/bot-group-always on` | AI decides when to speak autonomously (no @ needed) |
|
|
256
|
+
| `/bot-group-always off` | Only respond when @mentioned |
|
|
257
|
+
| `/bot-group-always` (no arg) | View current setting |
|
|
258
|
+
|
|
259
|
+
> ⚠️ This command modifies the account-level `defaultRequireMention`. It has lower priority than per-group `groups.{groupId}.requireMention` settings.
|
|
260
|
+
|
|
261
|
+
#### `/bot-group-info` — Group Push Mode & Effective Config (in-group)
|
|
262
|
+
|
|
263
|
+
> **You**: `/bot-group-info` *(sent in a group)*
|
|
264
|
+
>
|
|
265
|
+
> **QQBot**: 🤖 群信息 — push mode inference (AT / full), requireMention, queueing strategy, history mode, room-event policy, today's proactive message usage
|
|
266
|
+
|
|
267
|
+
Answers "why does this group have no context" diagnostics: the push mode is chosen by the **group owner** when adding the bot (AT only / AT + recent N / full), and this command shows what the plugin actually observes plus every effective config value.
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## 🚀 Getting Started
|
|
272
|
+
|
|
273
|
+
### Step 1 — Create a QQ Bot on the QQ Open Platform
|
|
274
|
+
|
|
275
|
+
1. Go to the [QQ Open Platform](https://q.qq.com/) and **scan the QR code with your phone QQ** to register / log in. If you haven't registered before, scanning will automatically complete the registration and bind your QQ account.
|
|
276
|
+
|
|
277
|
+
<img width="3246" height="1886" alt="Clipboard_Screenshot_1772980354" src="https://github.com/user-attachments/assets/d8491859-57e8-47e4-9d39-b21138be54d0" />
|
|
278
|
+
|
|
279
|
+
2. After scanning, tap **Agree** on your phone — you'll land on the bot configuration page.
|
|
280
|
+
3. Click **Create Bot** to create a new QQ bot.
|
|
281
|
+
|
|
282
|
+
<img width="720" alt="Create Bot" src="docs/images/create-robot.png" />
|
|
283
|
+
|
|
284
|
+
> ⚠️ The bot will automatically appear in your QQ message list and send a first message. However, it will reply "The bot has gone to Mars" until you complete the configuration steps below.
|
|
285
|
+
|
|
286
|
+
<img width="400" alt="Bot Say Hello" src="docs/images/bot-say-hello.jpg" />
|
|
287
|
+
|
|
288
|
+
4. Find **AppID** and **AppSecret** on the bot's page, click **Copy** for each, and save them somewhere safe (e.g., a notepad). **AppSecret is not stored in plaintext — if you leave the page without saving it, you'll have to regenerate a new one.**
|
|
289
|
+
|
|
290
|
+
<img width="720" alt="Find AppID and AppSecret" src="docs/images/find-appid-secret.png" />
|
|
291
|
+
|
|
292
|
+
> For a step-by-step walkthrough with screenshots, see the [official guide](https://cloud.tencent.com/developer/article/2626045).
|
|
293
|
+
|
|
294
|
+
### Step 2 — Install / Upgrade the Plugin
|
|
295
|
+
|
|
296
|
+
> **Note**: The unscoped npm name `openclaw-qqbot` belongs to the original upstream project — this fork publishes as the scoped package `@jerryliang122/openclaw-qqbot`. Requires OpenClaw >= 2026.9.2.
|
|
297
|
+
|
|
298
|
+
**Option A: Install from npm (Recommended)**
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
openclaw plugins install @jerryliang122/openclaw-qqbot
|
|
302
|
+
|
|
303
|
+
# Or a specific version
|
|
304
|
+
openclaw plugins install @jerryliang122/openclaw-qqbot@1.0.0
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Option B: Install from GitHub**
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
openclaw plugins install git+https://github.com/jerryliang122/qqbot-openclaw.git
|
|
311
|
+
|
|
312
|
+
# Or a specific release tag
|
|
313
|
+
openclaw plugins install git+https://github.com/jerryliang122/qqbot-openclaw.git#v1.0.0
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Option C: Install from Local Source**
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
# Clone the repo
|
|
320
|
+
git clone https://github.com/jerryliang122/qqbot-openclaw.git
|
|
321
|
+
cd openclaw-qqbot
|
|
322
|
+
|
|
323
|
+
# Build
|
|
324
|
+
npm install
|
|
325
|
+
npm run build
|
|
326
|
+
|
|
327
|
+
# Install to OpenClaw (method 1: link)
|
|
328
|
+
openclaw plugins link .
|
|
329
|
+
|
|
330
|
+
# Or install to OpenClaw (method 2: pack)
|
|
331
|
+
npm pack
|
|
332
|
+
openclaw plugins install ./openclaw-qqbot-1.0.0.tgz
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
**Upgrading from the old (upstream 2.x) version?** See the [CHANGELOG](CHANGELOG.md) — it lists every breaking change (removed config keys, env vars, and behaviors) and the migration table.
|
|
336
|
+
|
|
337
|
+
**Option C: Configure Credentials**
|
|
338
|
+
|
|
339
|
+
After installation, configure your QQ bot credentials:
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
# Via QR code (recommended)
|
|
343
|
+
openclaw channels login --channel qqbot
|
|
344
|
+
|
|
345
|
+
# Or manually
|
|
346
|
+
openclaw channels add --channel qqbot --token "AppID:AppSecret"
|
|
347
|
+
|
|
348
|
+
# Start / restart
|
|
349
|
+
openclaw gateway restart
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
> Environment variables `QQBOT_APPID` / `QQBOT_SECRET` are also supported.
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
### Step 3 — Test
|
|
356
|
+
|
|
357
|
+
Open QQ, find your bot, and send a message!
|
|
358
|
+
|
|
359
|
+
<div align="center">
|
|
360
|
+
<img width="500" alt="Chat Demo" src="https://github.com/user-attachments/assets/b2776c8b-de72-4e37-b34d-e8287ce45de1" />
|
|
361
|
+
</div>
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## ⚙️ Advanced Configuration
|
|
366
|
+
|
|
367
|
+
### Multi-Account Setup (Multi-Bot)
|
|
368
|
+
|
|
369
|
+
Run multiple QQ bots under a single OpenClaw instance.
|
|
370
|
+
|
|
371
|
+
#### Configuration
|
|
372
|
+
|
|
373
|
+
Edit `~/.openclaw/openclaw.json` and add an `accounts` field under `channels.qqbot`:
|
|
374
|
+
|
|
375
|
+
```json
|
|
376
|
+
{
|
|
377
|
+
"channels": {
|
|
378
|
+
"qqbot": {
|
|
379
|
+
"enabled": true,
|
|
380
|
+
"appId": "111111111",
|
|
381
|
+
"clientSecret": "secret-of-bot-1",
|
|
382
|
+
|
|
383
|
+
"accounts": {
|
|
384
|
+
"bot2": {
|
|
385
|
+
"enabled": true,
|
|
386
|
+
"appId": "222222222",
|
|
387
|
+
"clientSecret": "secret-of-bot-2"
|
|
388
|
+
},
|
|
389
|
+
"bot3": {
|
|
390
|
+
"enabled": true,
|
|
391
|
+
"appId": "333333333",
|
|
392
|
+
"clientSecret": "secret-of-bot-3"
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
**Notes:**
|
|
401
|
+
|
|
402
|
+
- The top-level `appId` / `clientSecret` is the **default account** (accountId = `"default"`)
|
|
403
|
+
- Each key under `accounts` (e.g. `bot2`, `bot3`) is the `accountId` for that bot
|
|
404
|
+
- Each account can independently configure `enabled`, `name`, `allowFrom`, `systemPrompt`, etc.
|
|
405
|
+
- You may also skip the top-level default account and only configure bots inside `accounts`
|
|
406
|
+
|
|
407
|
+
Add a second bot via CLI (if the framework supports the `--account` parameter):
|
|
408
|
+
|
|
409
|
+
```bash
|
|
410
|
+
openclaw channels add --channel qqbot --account bot2 --token "222222222:secret-of-bot-2"
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
#### Sending Messages to a Specific Account's Users
|
|
414
|
+
|
|
415
|
+
When using `openclaw message send`, specify which bot to use with the `--account` parameter:
|
|
416
|
+
|
|
417
|
+
```bash
|
|
418
|
+
# Send with the default bot (no --account = uses "default")
|
|
419
|
+
openclaw message send --channel "qqbot" \
|
|
420
|
+
--target "qqbot:c2c:OPENID" \
|
|
421
|
+
--message "hello from default bot"
|
|
422
|
+
|
|
423
|
+
# Send with bot2
|
|
424
|
+
openclaw message send --channel "qqbot" \
|
|
425
|
+
--account bot2 \
|
|
426
|
+
--target "qqbot:c2c:OPENID" \
|
|
427
|
+
--message "hello from bot2"
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
**Target Formats:**
|
|
431
|
+
|
|
432
|
+
| Format | Description |
|
|
433
|
+
|--------|-------------|
|
|
434
|
+
| `qqbot:c2c:OPENID` | Private chat (C2C) |
|
|
435
|
+
| `qqbot:group:GROUP_OPENID` | Group chat |
|
|
436
|
+
| `qqbot:channel:CHANNEL_ID` | Guild channel |
|
|
437
|
+
|
|
438
|
+
> ⚠️ **Important**: Each bot has its own set of user OpenIDs. An OpenID received by Bot A **cannot** be used to send messages via Bot B — this will result in a 500 error. Always use the matching bot's `accountId` to send messages to its users.
|
|
439
|
+
|
|
440
|
+
#### How It Works
|
|
441
|
+
|
|
442
|
+
- When `openclaw gateway` starts, all accounts with `enabled: true` launch their own connections (WebSocket or Webhook depending on `transport` config)
|
|
443
|
+
- Each account maintains an independent Token cache (isolated by `appId`), preventing cross-contamination
|
|
444
|
+
- Incoming message logs are prefixed with `[qqbot:accountId]` for easy debugging
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
### Webhook Transport Mode
|
|
449
|
+
|
|
450
|
+
By default, the plugin connects to QQ via **WebSocket** (outbound connection, no public IP required). You can switch to **Webhook** mode where QQ platform POSTs events to your HTTP endpoint.
|
|
451
|
+
|
|
452
|
+
| | WebSocket (default) | Webhook |
|
|
453
|
+
|---|---|---|
|
|
454
|
+
| Connection | Plugin connects to QQ gateway | QQ platform POSTs to your server |
|
|
455
|
+
| Public IP | Not required | Required |
|
|
456
|
+
| Use case | Development, single instance | Production, horizontal scaling, Serverless |
|
|
457
|
+
| Session resume | Supported (RESUME) | Stateless, no resume needed |
|
|
458
|
+
| Signature | Built-in | Ed25519 auto-verified by plugin |
|
|
459
|
+
|
|
460
|
+
#### Configuration
|
|
461
|
+
|
|
462
|
+
```json
|
|
463
|
+
{
|
|
464
|
+
"channels": {
|
|
465
|
+
"qqbot": {
|
|
466
|
+
"appId": "111111111",
|
|
467
|
+
"clientSecret": "your-secret",
|
|
468
|
+
"transport": "webhook",
|
|
469
|
+
"webhook": {
|
|
470
|
+
"path": "/qqbot/webhook"
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
| Field | Default | Description |
|
|
478
|
+
|-------|---------|-------------|
|
|
479
|
+
| `transport` | `"websocket"` | `"websocket"` or `"webhook"` |
|
|
480
|
+
| `webhook.path` | `"/qqbot/webhook"` | HTTP path for receiving callbacks |
|
|
481
|
+
|
|
482
|
+
#### Platform Setup
|
|
483
|
+
|
|
484
|
+
1. Go to [QQ Open Platform](https://q.qq.com/) → Bot Settings → Message Receiving
|
|
485
|
+
2. Select **HTTP Callback**
|
|
486
|
+
3. Enter your callback URL: `https://your-domain.com/qqbot/webhook`
|
|
487
|
+
4. The platform sends an `op:13` validation request — the plugin handles it automatically
|
|
488
|
+
5. Once validated, all events will be POSTed to your endpoint
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
### Group Chat Configuration
|
|
493
|
+
|
|
494
|
+
The plugin provides flexible group chat controls, allowing you to customize trigger rules, tool permissions, and AI behavior per group.
|
|
495
|
+
|
|
496
|
+
#### @Mention Trigger Mode (`requireMention`)
|
|
497
|
+
|
|
498
|
+
By default, the bot **only responds when @mentioned** in a group. You can configure it to autonomously decide when to speak:
|
|
499
|
+
|
|
500
|
+
| Mode | Config Value | Behavior |
|
|
501
|
+
|------|-------------|----------|
|
|
502
|
+
| **@ only** | `true` (default) | Only messages that @mention the bot trigger AI processing. Non-@ messages are still cached in history but don't trigger AI |
|
|
503
|
+
| **Autonomous** | `false` | AI decides on its own whether each message needs a reply — no @ required |
|
|
504
|
+
|
|
505
|
+
> **Important**: Even when `requireMention: true`, non-@ messages are **still cached** in the group history buffer. They just don't trigger AI processing.
|
|
506
|
+
|
|
507
|
+
**Priority chain** (highest to lowest):
|
|
508
|
+
|
|
509
|
+
```
|
|
510
|
+
groups.{groupOpenid}.requireMention
|
|
511
|
+
> groups."*".requireMention
|
|
512
|
+
> account-level defaultRequireMention
|
|
513
|
+
> default value true
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
**Example:**
|
|
517
|
+
|
|
518
|
+
```json
|
|
519
|
+
{
|
|
520
|
+
"channels": {
|
|
521
|
+
"qqbot": {
|
|
522
|
+
// Account-level default for all groups
|
|
523
|
+
"defaultRequireMention": false,
|
|
524
|
+
|
|
525
|
+
"accounts": {
|
|
526
|
+
"default": {
|
|
527
|
+
"groups": {
|
|
528
|
+
"*": {
|
|
529
|
+
// Wildcard fallback for all groups
|
|
530
|
+
"requireMention": false
|
|
531
|
+
},
|
|
532
|
+
"GROUP_OPENID": {
|
|
533
|
+
// Per-group override — this group still requires @
|
|
534
|
+
"requireMention": true
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
> **Use cases:**
|
|
545
|
+
>
|
|
546
|
+
> - Work groups → `requireMention: true` — avoid AI chiming in on every casual message
|
|
547
|
+
> - Dedicated AI companion groups → `requireMention: false` — participate naturally like a real person
|
|
548
|
+
> - Use [`/bot-group-always`](#bot-group-always--group-response-mode-toggle) to toggle account-level defaults at runtime
|
|
549
|
+
|
|
550
|
+
#### Additional Group Config Fields
|
|
551
|
+
|
|
552
|
+
Besides `requireMention`, each group supports these settings:
|
|
553
|
+
|
|
554
|
+
| Field | Type | Default | Description |
|
|
555
|
+
|-------|------|---------|-------------|
|
|
556
|
+
| `ignoreOtherMentions` | `boolean` | `false` | If enabled, messages that @mention others but not the bot are silently dropped (not recorded, no AI trigger) |
|
|
557
|
+
| `toolPolicy` | `"full" \| "restricted" \| "none"` | `"restricted"` | Tool scope available to AI in this group. `full`=all tools; `restricted`=sensitive tools restricted (e.g., command execution, file ops); `none`=no tool calls allowed |
|
|
558
|
+
| `prompt` | `string` | built-in default | Group-specific system prompt, appended after global systemPrompt |
|
|
559
|
+
| `historyLimit` | `number` | `20` | Cached group history message count (0 disables) |
|
|
560
|
+
| `historyMode` | `"clear" \| "rolling"` | `"clear"` | `clear`: wipe history after each reply (legacy). `rolling`: bot outbounds are recorded too, and history is trimmed to after the bot's last message (AI sees what it last said) |
|
|
561
|
+
| `unmentionedInbound` | `"user_request" \| "room_event"` | `"user_request"` | `room_event`: read all messages like a group member; un-@'d traffic becomes passive room events (see [Room Events](#room-events-for-full-mode-groups-unmentionedinbound--opt-in); requires full-push-mode group) |
|
|
562
|
+
| `coalesce` | `object` | `{enabled: true}` | Group queueing config (see [Group Message Queueing](#group-message-queueing-configuration-coalesce--groupcoalesce)) |
|
|
563
|
+
|
|
564
|
+
**Full example with multiple groups:**
|
|
565
|
+
|
|
566
|
+
```json
|
|
567
|
+
{
|
|
568
|
+
"channels": {
|
|
569
|
+
"qqbot": {
|
|
570
|
+
"defaultRequireMention": false,
|
|
571
|
+
"accounts": {
|
|
572
|
+
"default": {
|
|
573
|
+
"groups": {
|
|
574
|
+
"*": {
|
|
575
|
+
"requireMention": true,
|
|
576
|
+
"toolPolicy": "restricted",
|
|
577
|
+
"ignoreOtherMentions": true
|
|
578
|
+
},
|
|
579
|
+
"WORK_GROUP_OPENID": {
|
|
580
|
+
"requireMention": true,
|
|
581
|
+
"toolPolicy": "none",
|
|
582
|
+
"prompt": "You are a work assistant. Only answer work-related questions."
|
|
583
|
+
},
|
|
584
|
+
"FRIEND_GROUP_OPENID": {
|
|
585
|
+
"requireMention": false,
|
|
586
|
+
"toolPolicy": "full",
|
|
587
|
+
"prompt": "You are a friend in the group. Chat casually and naturally."
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
#### Group Access Control (`groupPolicy`)
|
|
598
|
+
|
|
599
|
+
Control which groups are allowed via `groupPolicy`:
|
|
600
|
+
|
|
601
|
+
| Policy | Description |
|
|
602
|
+
|--------|-------------|
|
|
603
|
+
| `"open"` (default) | All groups are allowed |
|
|
604
|
+
| `"allowlist"` | Only groups in `groupAllowFrom` are allowed |
|
|
605
|
+
| `"disabled"` | Group chats are disabled entirely |
|
|
606
|
+
|
|
607
|
+
```json
|
|
608
|
+
{
|
|
609
|
+
"channels": {
|
|
610
|
+
"qqbot": {
|
|
611
|
+
"groupPolicy": "allowlist",
|
|
612
|
+
"groupAllowFrom": ["ALLOWED_GROUP_OPENID_1", "ALLOWED_GROUP_OPENID_2"]
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
> You can also use [**`/bot-group-always`**](#bot-group-always--group-response-mode-toggle) to toggle account-level defaults at runtime without restarting.
|
|
619
|
+
|
|
620
|
+
---
|
|
621
|
+
|
|
622
|
+
### Group vs C2C Differential Handling
|
|
623
|
+
|
|
624
|
+
The plugin implements different message handling strategies for group and private chats:
|
|
625
|
+
|
|
626
|
+
#### Group Chat (Framework Queue Strategy)
|
|
627
|
+
|
|
628
|
+
- **All messages are processed** — nothing is dropped
|
|
629
|
+
- **Immediate dispatch** — messages go straight to the framework; no plugin-side waiting room
|
|
630
|
+
- **Queueing by the framework** — while a turn is active, subsequent messages queue behind it (`collect` mode: merged into one batch after the active turn finishes; 500ms debounce absorbs bursts, queue cap with overflow summarizing)
|
|
631
|
+
- **Never interrupts** — an active turn always completes; new messages wait their turn
|
|
632
|
+
- **SessionKey format**: `qqbot:{accountId}:group:{groupId}:coalescing` (legacy naming, kept for session continuity)
|
|
633
|
+
- **Admission strategy**: `exclusive` (framework durable-ingress convention) with **no abort signal** — interruption is structurally impossible
|
|
634
|
+
|
|
635
|
+
**Example behavior**:
|
|
636
|
+
|
|
637
|
+
```
|
|
638
|
+
User A: "Question 1" → Start processing
|
|
639
|
+
User B: "Question 2" → Queued in framework followup queue
|
|
640
|
+
User C: "Question 3" → Queued in framework followup queue
|
|
641
|
+
|
|
642
|
+
Question 1 completes → [Q2, Q3] drain as one merged batch → AI sees combined context, single reply
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
#### C2C Private Chat (Exclusive Strategy)
|
|
646
|
+
|
|
647
|
+
- **User can interrupt** — sending a new message cancels the previous one
|
|
648
|
+
- **Last message wins** — only the most recent message is processed
|
|
649
|
+
- **SessionKey format**: `qqbot:{accountId}:{userId}`
|
|
650
|
+
- **Admission strategy**: `exclusive` with abort signal — new message cancels old
|
|
651
|
+
|
|
652
|
+
**Example behavior**:
|
|
653
|
+
|
|
654
|
+
```
|
|
655
|
+
User A: "Question 1" → Start processing
|
|
656
|
+
User A: "Question 2" → Cancel Q1, start processing Q2
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
#### Why This Design?
|
|
660
|
+
|
|
661
|
+
- **In groups**: All user messages should be preserved and addressed; a group is multi-user, so interruption would steal one member's answer from another
|
|
662
|
+
- **In C2C**: Users can change their mind mid-conversation
|
|
663
|
+
- **Aligns with user expectations** in different chat contexts
|
|
664
|
+
|
|
665
|
+
---
|
|
666
|
+
|
|
667
|
+
### Group Message Queueing Configuration (`coalesce` / `groupCoalesce`)
|
|
668
|
+
|
|
669
|
+
Control how group messages are queued and merged when they arrive in quick succession:
|
|
670
|
+
|
|
671
|
+
```json
|
|
672
|
+
{
|
|
673
|
+
"channels": {
|
|
674
|
+
"qqbot": {
|
|
675
|
+
"groupCoalesce": {
|
|
676
|
+
"enabled": true
|
|
677
|
+
},
|
|
678
|
+
"accounts": {
|
|
679
|
+
"default": {
|
|
680
|
+
"groups": {
|
|
681
|
+
"GROUP_123": {
|
|
682
|
+
"coalesce": {
|
|
683
|
+
"enabled": false
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
| Field | Type | Default | Description |
|
|
695
|
+
|-------|------|---------|-------------|
|
|
696
|
+
| `enabled` | `boolean` | `true` | `true` → framework `collect` mode (queue + merge batches); `false` → `followup` mode (queue without merging — preserves the "disable merging" intent while never interrupting active turns). Queueing/merging is fully delegated to the OpenClaw followup queue |
|
|
697
|
+
|
|
698
|
+
**Priority chain**: `groups.{groupId}.coalesce` > `groupCoalesce` (account-level) > defaults
|
|
699
|
+
|
|
700
|
+
**When enabled** (framework `collect`):
|
|
701
|
+
|
|
702
|
+
- Messages during an active turn queue up and drain as one merged batch
|
|
703
|
+
- 500ms debounce absorbs rapid-fire bursts even on an idle group
|
|
704
|
+
- Queue overflow is summarized (never hard-dropped like the old buffer-full behavior)
|
|
705
|
+
- The AI sees combined context with per-sender attribution, single reply per batch
|
|
706
|
+
|
|
707
|
+
---
|
|
708
|
+
|
|
709
|
+
### Room Events for Full-Mode Groups (`unmentionedInbound`) — opt-in
|
|
710
|
+
|
|
711
|
+
> Requires the group owner to have set the group's push mode to **full message reception** (receive all messages). In AT-mode groups this setting has no observable effect (un-@'d messages never arrive).
|
|
712
|
+
|
|
713
|
+
By default, un-@'d group messages only land in the history buffer. With `unmentionedInbound: "room_event"`, the bot **reads every message like a normal group member**:
|
|
714
|
+
|
|
715
|
+
```json
|
|
716
|
+
{
|
|
717
|
+
"channels": {
|
|
718
|
+
"qqbot": {
|
|
719
|
+
"accounts": {
|
|
720
|
+
"default": {
|
|
721
|
+
"groups": {
|
|
722
|
+
"GROUP_OPENID": {
|
|
723
|
+
"unmentionedInbound": "room_event"
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
Behavior once enabled:
|
|
734
|
+
|
|
735
|
+
- **@mention / name pattern / quote-of-bot** → normal turn with full reply rights (wake modes, see below)
|
|
736
|
+
- **Everything else** → passive room event: the AI reads it as context, its natural text output is **not delivered** (structural silence — the framework doesn't even inject the NO_REPLY instruction), and it can only speak by proactively calling the `message` tool
|
|
737
|
+
- Room events never steer or interrupt an active turn — they queue behind it
|
|
738
|
+
- Room-event speech goes out through the passive-first outbound path (see below)
|
|
739
|
+
|
|
740
|
+
> ⚠️ **Cost note**: each room event runs one inference pass ("reading" the message). In an active group this adds real token spend — enable per group deliberately.
|
|
741
|
+
|
|
742
|
+
#### Wake Modes (what counts as "being addressed")
|
|
743
|
+
|
|
744
|
+
| Wake mode | Mechanism | Config |
|
|
745
|
+
|-----------|-----------|--------|
|
|
746
|
+
| @mention | `GROUP_AT_MESSAGE_CREATE` event / `mentions[].is_you` / content markers | built-in |
|
|
747
|
+
| **Name patterns** | Content matches a configured pattern (e.g. group members call the bot "沈处") | `agents.list.<id>.groupChat.mentionPatterns: ["沈处"]` — note this lives in the `agents` section, not `channels.qqbot`; effective in full-mode groups only |
|
|
748
|
+
| **Quote of bot message** | User quotes/replies to a bot outbound (resolved via ref-index) | built-in (`isImplicitMention`) |
|
|
749
|
+
|
|
750
|
+
Name-pattern false positives ("people talking *about* the bot") are handled gracefully: the turn runs with normal reply rights, and the LLM can output `NO_REPLY` to stay silent (the framework injects this guidance automatically — no prompt changes needed).
|
|
751
|
+
|
|
752
|
+
---
|
|
753
|
+
|
|
754
|
+
### Passive-First Outbound (protects the 1000/day proactive budget)
|
|
755
|
+
|
|
756
|
+
QQ Bot proactive messages (sent without msg_id) have a daily budget (~1000/day). The plugin prefers passive replies (msg_id) everywhere:
|
|
757
|
+
|
|
758
|
+
- Framework-provided `replyToId` is used when available; otherwise the freshest cached msg_id is attached (msgid-cache TTL matches the platform's passive window: 5min group / 30min c2c)
|
|
759
|
+
- Attaching a msg_id is **quota-aware**: the passive quota (5 replies per msg_id per 5min in groups) is atomically checked and consumed before the send — when exhausted, the send gracefully degrades to proactive instead of failing with platform error 40034128
|
|
760
|
+
- Residual proactive sends are counted per account per day (`/bot-group-info` shows usage; a warning is logged at 80% of the budget)
|
|
761
|
+
- Quiet groups (no message within the passive window) can only be reached proactively — that's a platform constraint, not a bug
|
|
762
|
+
|
|
763
|
+
---
|
|
764
|
+
|
|
765
|
+
### Group Rate Limiting (`rateLimit`) — enabled by default
|
|
766
|
+
|
|
767
|
+
Three-tier sliding-window throttling with conservative defaults (normal usage never hits them):
|
|
768
|
+
|
|
769
|
+
| Tier | Default | Keyed by |
|
|
770
|
+
|------|---------|----------|
|
|
771
|
+
| `perSender` | 20 msgs / min | sender openid |
|
|
772
|
+
| `perGroup` | 60 msgs / min | group openid (c2c falls back to sender) |
|
|
773
|
+
| `global` | 300 msgs / min | all messages |
|
|
774
|
+
|
|
775
|
+
```json
|
|
776
|
+
{
|
|
777
|
+
"channels": {
|
|
778
|
+
"qqbot": {
|
|
779
|
+
"rateLimit": {
|
|
780
|
+
"enabled": true,
|
|
781
|
+
"perSender": { "max": 20, "windowMs": 60000 },
|
|
782
|
+
"perGroup": { "max": 60, "windowMs": 60000 },
|
|
783
|
+
"global": { "max": 300, "windowMs": 60000 }
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
Rate-limited messages are dropped with an INFO log (no auto-reply, to avoid burning quota). Keep this enabled for room-event groups.
|
|
791
|
+
|
|
792
|
+
---
|
|
793
|
+
|
|
794
|
+
### Middleware Execution Order
|
|
795
|
+
|
|
796
|
+
The plugin processes messages through a carefully ordered middleware chain:
|
|
797
|
+
|
|
798
|
+
1. **Error Handler** — Catches exceptions at the outermost layer
|
|
799
|
+
2. **Message Filter** — Bot echo + message deduplication
|
|
800
|
+
3. **Inbound Guard** — Drops outbound echoes (c2c), duplicate pushes (30min window), contentless events
|
|
801
|
+
4. **Policy Injector** — Injects `ctx.state.policy` with dynamic config
|
|
802
|
+
5. **History Buffer** — Caches all group messages (including non-@; skipped for room-event groups)
|
|
803
|
+
6. **Access Control** — Dynamic pairing/allowlist checks
|
|
804
|
+
7. **Mention Gate** — Filters based on @mention rules (+ quote-of-bot implicit mention)
|
|
805
|
+
8. **Content Sanitizer** — Strips @markers, parses face tags
|
|
806
|
+
9. **Rate Limiter** — Three-layer throttling (enabled by default, see `rateLimit`)
|
|
807
|
+
10. **Slash Commands** — Intercepts `/bot-*` commands
|
|
808
|
+
11. **Secret Capture** (c2c only) — One-shot env-var secret input interception
|
|
809
|
+
12. **Typing Indicator** (C2C only) — Shows "typing..." status
|
|
810
|
+
13. **Quote Reference** — Parses quoted message context
|
|
811
|
+
14. **Attachment Processor** — Downloads/converts media
|
|
812
|
+
15. **Envelope Formatter** — Builds final message body
|
|
813
|
+
|
|
814
|
+
**Key points**:
|
|
815
|
+
|
|
816
|
+
- Inbound guard and history buffer run **before** mention gate → junk is dropped early, all real messages are cached
|
|
817
|
+
- Group messages dispatch immediately; the OpenClaw followup queue handles queueing/merging (`coalesce.enabled` selects `collect` vs `followup`)
|
|
818
|
+
- Typing indicator only runs for **C2C** messages
|
|
819
|
+
|
|
820
|
+
---
|
|
821
|
+
|
|
822
|
+
#### STT (Speech-to-Text) — Transcribe Incoming Voice Messages
|
|
823
|
+
|
|
824
|
+
STT supports two-level configuration with priority fallback:
|
|
825
|
+
|
|
826
|
+
| Priority | Config Path | Scope |
|
|
827
|
+
|----------|------------|-------|
|
|
828
|
+
| 1 (highest) | `channels.qqbot.stt` | Plugin-specific |
|
|
829
|
+
| 2 (fallback) | `tools.media.audio.models[0]` | Framework-level |
|
|
830
|
+
|
|
831
|
+
```json
|
|
832
|
+
{
|
|
833
|
+
"channels": {
|
|
834
|
+
"qqbot": {
|
|
835
|
+
"stt": {
|
|
836
|
+
"provider": "your-provider",
|
|
837
|
+
"model": "your-stt-model"
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
- `provider` — references a key in `models.providers` to inherit `baseUrl` and `apiKey`
|
|
845
|
+
- Set `enabled: false` to disable
|
|
846
|
+
- When configured, incoming voice messages are automatically converted (SILK→WAV) and transcribed
|
|
847
|
+
- `asrFallback` — platform ASR (`asr_refer_text`) participation switch. Unless explicitly set to `true`, QQ's built-in platform transcript is **discarded in all cases**: not used as a fallback when your STT fails or returns empty, and not used as the sole source when STT is not configured at all (voice messages then render as `[Voice message - transcription unavailable]`; the audio URL is still referenced via the `- Voice:` line). The flag is read from `channels.qqbot.stt.asrFallback` regardless of whether STT credentials resolve — `stt: { "asrFallback": true }` alone restores the legacy platform-transcript behavior:
|
|
848
|
+
|
|
849
|
+
```json
|
|
850
|
+
{
|
|
851
|
+
"channels": {
|
|
852
|
+
"qqbot": {
|
|
853
|
+
"stt": {
|
|
854
|
+
"provider": "your-provider",
|
|
855
|
+
"model": "your-stt-model",
|
|
856
|
+
"asrFallback": true
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
#### TTS (Text-to-Speech) — Send Voice Messages
|
|
864
|
+
|
|
865
|
+
| Priority | Config Path | Scope |
|
|
866
|
+
|----------|------------|-------|
|
|
867
|
+
| 1 (highest) | `channels.qqbot.tts` | Plugin-specific |
|
|
868
|
+
| 2 (fallback) | `messages.tts` | Framework-level |
|
|
869
|
+
|
|
870
|
+
```json
|
|
871
|
+
{
|
|
872
|
+
"channels": {
|
|
873
|
+
"qqbot": {
|
|
874
|
+
"tts": {
|
|
875
|
+
"provider": "your-provider",
|
|
876
|
+
"model": "your-tts-model",
|
|
877
|
+
"voice": "your-voice"
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
- `provider` — references a key in `models.providers` to inherit `baseUrl` and `apiKey`
|
|
885
|
+
- `voice` — voice variant
|
|
886
|
+
- Set `enabled: false` to disable (default: `true`)
|
|
887
|
+
- When configured, AI can generate and send voice messages
|
|
888
|
+
|
|
889
|
+
#### Streaming Replies — C2C private chat only
|
|
890
|
+
|
|
891
|
+
The bot can stream its reply progressively (typewriter effect) via QQ's streaming API. Group chats do not support streaming (platform constraint). Disabled unless configured.
|
|
892
|
+
|
|
893
|
+
```json
|
|
894
|
+
{
|
|
895
|
+
"channels": {
|
|
896
|
+
"qqbot": {
|
|
897
|
+
"streaming": {
|
|
898
|
+
"mode": "partial",
|
|
899
|
+
"sendMode": "stream"
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
| Field | Default | Description |
|
|
907
|
+
|-------|---------|-------------|
|
|
908
|
+
| `mode` | *(unset = off)* | `"partial"` enables streaming reception; `"off"` disables |
|
|
909
|
+
| `sendMode` | `"stream"` | `"stream"` — QQ streaming printer (typewriter; the delivered prefix is immutable, tail rewrites are merged into appends). `"static"` — accumulate while the model generates, then send one complete message at the end (no typewriter) |
|
|
910
|
+
|
|
911
|
+
- Streaming replies (`session.update` frames) still consume the passive-reply quota of the triggering message
|
|
912
|
+
- On stream errors the controller falls back to a single static message automatically
|
|
913
|
+
|
|
914
|
+
#### Typing Indicator — C2C private chat only
|
|
915
|
+
|
|
916
|
+
After receiving a private message, the bot shows "typing…" and renews it periodically while the AI is processing.
|
|
917
|
+
|
|
918
|
+
```json
|
|
919
|
+
{
|
|
920
|
+
"channels": {
|
|
921
|
+
"qqbot": {
|
|
922
|
+
"typing": {
|
|
923
|
+
"enabled": true,
|
|
924
|
+
"intervalMs": 20000
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
```
|
|
930
|
+
|
|
931
|
+
- `enabled` — enable/disable the indicator (default: `true`)
|
|
932
|
+
- `intervalMs` — renewal interval in milliseconds (default: `20000`). The QQ client clears the indicator when the user leaves and re-enters the chat; only a fresh push re-shows it, hence the periodic renewal. Values below `20000` are clamped to `20000` (QPS constraint)
|
|
933
|
+
- **Quota note**: typing notifications share the passive-reply quota of the user message they reply to (QQ Open Platform allows ~5 passive replies per message). Once the passive quota is exhausted, typing — just like reply messages — automatically falls back to proactive sending (no msg_id); renewal is never interrupted
|
|
934
|
+
- **Intermediate-message refresh**: when the bot sends a message (e.g. chain-of-thought intermediate output), the QQ client terminates the indicator; if the framework task is still running, the plugin renews the indicator 5 seconds after the message (still guarded by the 20s QPS spacing). After the final reply completes the task, no further refresh is sent
|
|
935
|
+
|
|
936
|
+
---
|
|
937
|
+
|
|
938
|
+
## 📚 Documentation & Links
|
|
939
|
+
|
|
940
|
+
- [Command Reference](docs/commands.md) — OpenClaw CLI commands
|
|
941
|
+
- [Changelog](CHANGELOG.md) — release notes
|
|
942
|
+
|
|
943
|
+
## 🤝 Contributors
|
|
944
|
+
|
|
945
|
+
This is a forked version. For contributors to the official version, see [tencent-connect/openclaw-qqbot](https://github.com/tencent-connect/openclaw-qqbot/graphs/contributors).
|
|
946
|
+
|
|
947
|
+
<a href="https://github.com/jerryliang122/qqbot-openclaw/graphs/contributors">
|
|
948
|
+
<img src="https://contrib.rocks/image?repo=jerryliang122/qqbot-openclaw" />
|
|
949
|
+
</a>
|
|
950
|
+
|
|
951
|
+
## 💖 Acknowledgements
|
|
952
|
+
|
|
953
|
+
- Original project: [tencent-connect/openclaw-qqbot](https://github.com/tencent-connect/openclaw-qqbot)
|
|
954
|
+
- Special thanks to [@sliverp](https://github.com/sliverp) for outstanding contributions to the original project!
|
|
955
|
+
- Thanks to [Tencent Cloud Lighthouse](https://cloud.tencent.com/product/lighthouse) for the deep collaboration.
|
|
956
|
+
|
|
957
|
+
<a href="https://cloud.tencent.com/product/lighthouse">
|
|
958
|
+
<img alt="Tencent Cloud Lighthouse" src="./docs/images/lighthouse-head.png" height="500" style="max-width:80%; height:auto;"/>
|
|
959
|
+
</a>
|
|
960
|
+
|
|
961
|
+
## ⭐ Star History
|
|
962
|
+
|
|
963
|
+
<div align="center">
|
|
964
|
+
|
|
965
|
+
[](https://www.star-history.com/#jerryliang122/qqbot-openclaw&type=date&legend=top-left)
|
|
966
|
+
|
|
967
|
+
</div>
|