@tencent-connect/openclaw-qqbot 1.7.1 → 1.7.2
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/README.md +188 -3
- package/README.zh.md +190 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/src/api.d.ts +2 -0
- package/dist/src/api.js +16 -3
- package/dist/src/config.d.ts +5 -1
- package/dist/src/config.js +12 -2
- package/dist/src/gateway.js +131 -169
- package/dist/src/slash-commands.js +119 -3
- package/dist/src/tools/channel.js +1 -4
- package/dist/src/tools/remind.js +0 -1
- package/dist/src/transport/index.d.ts +10 -0
- package/dist/src/transport/index.js +9 -0
- package/dist/src/transport/webhook-transport.d.ts +67 -0
- package/dist/src/transport/webhook-transport.js +245 -0
- package/dist/src/transport/webhook-verify.d.ts +48 -0
- package/dist/src/transport/webhook-verify.js +98 -0
- package/dist/src/types.d.ts +19 -0
- package/dist/src/utils/audio-convert.js +37 -9
- package/index.ts +1 -0
- package/package.json +1 -1
- package/scripts/postinstall-link-sdk.js +44 -0
- package/scripts/upgrade-via-npm.sh +358 -62
- package/scripts/upgrade-via-source.sh +122 -85
- package/src/api.ts +18 -4
- package/src/config.ts +15 -2
- package/src/gateway.ts +135 -167
- package/src/onboarding.ts +8 -0
- package/src/slash-commands.ts +137 -3
- package/src/tools/channel.ts +1 -7
- package/src/tools/remind.ts +0 -2
- package/src/transport/index.ts +11 -0
- package/src/transport/webhook-transport.ts +332 -0
- package/src/transport/webhook-verify.ts +119 -0
- package/src/types.ts +22 -1
- package/src/typings/openclaw-webhook-ingress.d.ts +66 -0
- package/src/utils/audio-convert.ts +37 -9
package/README.md
CHANGED
|
@@ -36,7 +36,9 @@ Scan to join the QQ group chat
|
|
|
36
36
|
|
|
37
37
|
| Feature | Description |
|
|
38
38
|
|---------|-------------|
|
|
39
|
-
| 🔒 **Multi-Scene** | C2C private chat, group @
|
|
39
|
+
| 🔒 **Multi-Scene** | C2C private chat, group chat (@mention / autonomous dual mode) |
|
|
40
|
+
| 👥 **Group Fine-Tuning** | Per-group @trigger rules, tool policies, custom prompts, message filtering |
|
|
41
|
+
| 🌐 **Dual Transport** | WebSocket (default) or Webhook (HTTP callback) — switch via config |
|
|
40
42
|
| 🖼️ **Rich Media** | Send & receive images, voice, video, and files |
|
|
41
43
|
| 🎙️ **Voice (STT/TTS)** | Speech-to-text transcription & text-to-speech replies |
|
|
42
44
|
| 🔥 **One-Click Hot Upgrade** | Send `/bot-upgrade` in private chat to upgrade — no server login needed |
|
|
@@ -242,6 +244,22 @@ Manage the AI command execution approval policy. Supported subcommands:
|
|
|
242
244
|
|
|
243
245
|
`/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
246
|
|
|
247
|
+
#### `/bot-group-allways` — Group Response Mode Toggle
|
|
248
|
+
|
|
249
|
+
> **You**: `/bot-group-allways`
|
|
250
|
+
>
|
|
251
|
+
> **QQBot**: 🤖 Group autonomous mode: ❌ @mention required
|
|
252
|
+
|
|
253
|
+
Toggle group @trigger behavior at runtime — changes persist instantly, no restart needed:
|
|
254
|
+
|
|
255
|
+
| Subcommand | Description |
|
|
256
|
+
|------------|-------------|
|
|
257
|
+
| `/bot-group-allways on` | AI decides when to speak autonomously (no @ needed) |
|
|
258
|
+
| `/bot-group-allways off` | Only respond when @mentioned |
|
|
259
|
+
| `/bot-group-allways` (no arg) | View current setting |
|
|
260
|
+
|
|
261
|
+
> ⚠️ This command modifies the account-level `defaultRequireMention`. It has lower priority than per-group `groups.{groupId}.requireMention` settings.
|
|
262
|
+
|
|
245
263
|
---
|
|
246
264
|
|
|
247
265
|
## 🚀 Getting Started
|
|
@@ -408,13 +426,180 @@ openclaw message send --channel "qqbot" \
|
|
|
408
426
|
|
|
409
427
|
#### How It Works
|
|
410
428
|
|
|
411
|
-
- When `openclaw gateway` starts, all accounts with `enabled: true` launch their own WebSocket
|
|
429
|
+
- When `openclaw gateway` starts, all accounts with `enabled: true` launch their own connections (WebSocket or Webhook depending on `transport` config)
|
|
412
430
|
- Each account maintains an independent Token cache (isolated by `appId`), preventing cross-contamination
|
|
413
431
|
- Incoming message logs are prefixed with `[qqbot:accountId]` for easy debugging
|
|
414
432
|
|
|
415
433
|
---
|
|
416
434
|
|
|
417
|
-
###
|
|
435
|
+
### Webhook Transport Mode
|
|
436
|
+
|
|
437
|
+
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.
|
|
438
|
+
|
|
439
|
+
| | WebSocket (default) | Webhook |
|
|
440
|
+
|---|---|---|
|
|
441
|
+
| Connection | Plugin connects to QQ gateway | QQ platform POSTs to your server |
|
|
442
|
+
| Public IP | Not required | Required |
|
|
443
|
+
| Use case | Development, single instance | Production, horizontal scaling, Serverless |
|
|
444
|
+
| Session resume | Supported (RESUME) | Stateless, no resume needed |
|
|
445
|
+
| Signature | Built-in | Ed25519 auto-verified by plugin |
|
|
446
|
+
|
|
447
|
+
#### Configuration
|
|
448
|
+
|
|
449
|
+
```json
|
|
450
|
+
{
|
|
451
|
+
"channels": {
|
|
452
|
+
"qqbot": {
|
|
453
|
+
"appId": "111111111",
|
|
454
|
+
"clientSecret": "your-secret",
|
|
455
|
+
"transport": "webhook",
|
|
456
|
+
"webhook": {
|
|
457
|
+
"path": "/qqbot/webhook"
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
| Field | Default | Description |
|
|
465
|
+
|-------|---------|-------------|
|
|
466
|
+
| `transport` | `"websocket"` | `"websocket"` or `"webhook"` |
|
|
467
|
+
| `webhook.path` | `"/qqbot/webhook"` | HTTP path for receiving callbacks |
|
|
468
|
+
|
|
469
|
+
#### Platform Setup
|
|
470
|
+
|
|
471
|
+
1. Go to [QQ Open Platform](https://q.qq.com/) → Bot Settings → Message Receiving
|
|
472
|
+
2. Select **HTTP Callback**
|
|
473
|
+
3. Enter your callback URL: `https://your-domain.com/qqbot/webhook`
|
|
474
|
+
4. The platform sends an `op:13` validation request — the plugin handles it automatically
|
|
475
|
+
5. Once validated, all events will be POSTed to your endpoint
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
### Group Chat Configuration
|
|
480
|
+
|
|
481
|
+
The plugin provides flexible group chat controls, allowing you to customize trigger rules, tool permissions, and AI behavior per group.
|
|
482
|
+
|
|
483
|
+
#### @Mention Trigger Mode (`requireMention`)
|
|
484
|
+
|
|
485
|
+
By default, the bot **only responds when @mentioned** in a group. You can configure it to autonomously decide when to speak:
|
|
486
|
+
|
|
487
|
+
| Mode | Config Value | Behavior |
|
|
488
|
+
|------|-------------|----------|
|
|
489
|
+
| **@ only** | `true` (default) | Only messages that @mention the bot trigger a reply |
|
|
490
|
+
| **Autonomous** | `false` | AI decides on its own whether each message needs a reply — no @ required |
|
|
491
|
+
|
|
492
|
+
**Priority chain** (highest to lowest):
|
|
493
|
+
|
|
494
|
+
```
|
|
495
|
+
groups.{groupOpenid}.requireMention
|
|
496
|
+
> groups."*".requireMention
|
|
497
|
+
> account-level defaultRequireMention
|
|
498
|
+
> default value true
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
**Example:**
|
|
502
|
+
|
|
503
|
+
```json
|
|
504
|
+
{
|
|
505
|
+
"channels": {
|
|
506
|
+
"qqbot": {
|
|
507
|
+
// Account-level default for all groups
|
|
508
|
+
"defaultRequireMention": false,
|
|
509
|
+
|
|
510
|
+
"accounts": {
|
|
511
|
+
"default": {
|
|
512
|
+
"groups": {
|
|
513
|
+
"*": {
|
|
514
|
+
// Wildcard fallback for all groups
|
|
515
|
+
"requireMention": false
|
|
516
|
+
},
|
|
517
|
+
"GROUP_OPENID": {
|
|
518
|
+
// Per-group override — this group still requires @
|
|
519
|
+
"requireMention": true
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
> **Use cases:**
|
|
530
|
+
>
|
|
531
|
+
> - Work groups → `requireMention: true` — avoid AI chiming in on every casual message
|
|
532
|
+
> - Dedicated AI companion groups → `requireMention: false` — participate naturally like a real person
|
|
533
|
+
> - Use [`/bot-group-allways`](#bot-group-allways--group-response-mode-toggle) to toggle account-level defaults at runtime
|
|
534
|
+
|
|
535
|
+
#### Additional Group Config Fields
|
|
536
|
+
|
|
537
|
+
Besides `requireMention`, each group supports these settings:
|
|
538
|
+
|
|
539
|
+
| Field | Type | Default | Description |
|
|
540
|
+
|-------|------|---------|-------------|
|
|
541
|
+
| `ignoreOtherMentions` | `boolean` | `false` | If enabled, messages that @mention others but not the bot are silently dropped (not recorded, no AI trigger) |
|
|
542
|
+
| `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 |
|
|
543
|
+
| `prompt` | `string` | built-in default | Group-specific system prompt, appended after global systemPrompt |
|
|
544
|
+
| `historyLimit` | `number` | `50` | Cached group history message count |
|
|
545
|
+
|
|
546
|
+
**Full example with multiple groups:**
|
|
547
|
+
|
|
548
|
+
```json
|
|
549
|
+
{
|
|
550
|
+
"channels": {
|
|
551
|
+
"qqbot": {
|
|
552
|
+
"defaultRequireMention": false,
|
|
553
|
+
"accounts": {
|
|
554
|
+
"default": {
|
|
555
|
+
"groups": {
|
|
556
|
+
"*": {
|
|
557
|
+
"requireMention": true,
|
|
558
|
+
"toolPolicy": "restricted",
|
|
559
|
+
"ignoreOtherMentions": true
|
|
560
|
+
},
|
|
561
|
+
"WORK_GROUP_OPENID": {
|
|
562
|
+
"requireMention": true,
|
|
563
|
+
"toolPolicy": "none",
|
|
564
|
+
"prompt": "You are a work assistant. Only answer work-related questions."
|
|
565
|
+
},
|
|
566
|
+
"FRIEND_GROUP_OPENID": {
|
|
567
|
+
"requireMention": false,
|
|
568
|
+
"toolPolicy": "full",
|
|
569
|
+
"prompt": "You are a friend in the group. Chat casually and naturally."
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
#### Group Access Control (`groupPolicy`)
|
|
580
|
+
|
|
581
|
+
Control which groups are allowed via `groupPolicy`:
|
|
582
|
+
|
|
583
|
+
| Policy | Description |
|
|
584
|
+
|--------|-------------|
|
|
585
|
+
| `"open"` (default) | All groups are allowed |
|
|
586
|
+
| `"allowlist"` | Only groups in `groupAllowFrom` are allowed |
|
|
587
|
+
| `"disabled"` | Group chats are disabled entirely |
|
|
588
|
+
|
|
589
|
+
```json
|
|
590
|
+
{
|
|
591
|
+
"channels": {
|
|
592
|
+
"qqbot": {
|
|
593
|
+
"groupPolicy": "allowlist",
|
|
594
|
+
"groupAllowFrom": ["ALLOWED_GROUP_OPENID_1", "ALLOWED_GROUP_OPENID_2"]
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
> You can also use [**`/bot-group-allways`**](#bot-group-allways--group-response-mode-toggle) to toggle account-level defaults at runtime without restarting.
|
|
601
|
+
|
|
602
|
+
---
|
|
418
603
|
|
|
419
604
|
#### STT (Speech-to-Text) — Transcribe Incoming Voice Messages
|
|
420
605
|
|
package/README.zh.md
CHANGED
|
@@ -31,7 +31,9 @@
|
|
|
31
31
|
|
|
32
32
|
| 功能 | 说明 |
|
|
33
33
|
|------|------|
|
|
34
|
-
| 🔒 **多场景支持** | C2C
|
|
34
|
+
| 🔒 **多场景支持** | C2C 私聊、群聊(@提及 / 自主发言双模式) |
|
|
35
|
+
| 👥 **群聊精细管控** | 按群配置 @触发规则、工具权限、自定义提示词、消息过滤 |
|
|
36
|
+
| 🌐 **双传输模式** | WebSocket(默认)或 Webhook(HTTP 回调)— 配置切换 |
|
|
35
37
|
| 🖼️ **富媒体消息** | 支持图片、语音、视频、文件的收发 |
|
|
36
38
|
| 🎙️ **语音能力 (STT/TTS)** | 语音转文字自动转录 & 文字转语音回复 |
|
|
37
39
|
| 🔥 **一键热更新** | 私聊发送 `/bot-upgrade` 即可完成版本升级,无需登录服务器 |
|
|
@@ -237,6 +239,24 @@ AI 可直接发送视频,支持本地文件和公网 URL。
|
|
|
237
239
|
|
|
238
240
|
`/bot-clear-storage` 列出对话产生的文件以及下载的资源目录里的文件,使用`/bot-clear-storage -- force`确定删除。
|
|
239
241
|
|
|
242
|
+
#### `/bot-group-allways` — 群消息响应模式切换
|
|
243
|
+
|
|
244
|
+
> **你**:`/bot-group-allways`
|
|
245
|
+
>
|
|
246
|
+
> **QQBot**:🤖 群自主发言状态:❌ 仅被 @ 时回复
|
|
247
|
+
|
|
248
|
+
运行时动态切换群聊默认 @触发行为,修改即时持久化,无需重启:
|
|
249
|
+
|
|
250
|
+
| 子命令 | 说明 |
|
|
251
|
+
|--------|------|
|
|
252
|
+
| `/bot-group-allways on` | AI 自主判断何时发言(无需 @) |
|
|
253
|
+
| `/bot-group-allways off` | 仅在被 @ 时回复 |
|
|
254
|
+
| `/bot-group-allways`(无参数) | 查看当前设置 |
|
|
255
|
+
|
|
256
|
+
> ⚠️ 此指令修改账户级 `defaultRequireMention`,优先级低于具体群的 `groups.{groupId}.requireMention` 配置。
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
240
260
|
---
|
|
241
261
|
|
|
242
262
|
## 🚀 快速开始
|
|
@@ -403,13 +423,180 @@ openclaw message send --channel "qqbot" \
|
|
|
403
423
|
|
|
404
424
|
#### 工作原理
|
|
405
425
|
|
|
406
|
-
- 启动 `openclaw gateway` 后,所有 `enabled: true`
|
|
426
|
+
- 启动 `openclaw gateway` 后,所有 `enabled: true` 的账户会同时启动连接(WebSocket 或 Webhook,取决于 `transport` 配置)
|
|
407
427
|
- 每个账户独立维护 Token 缓存(基于 `appId` 隔离),互不干扰
|
|
408
428
|
- 接收消息时,日志会带上 `[qqbot:accountId]` 前缀方便排查
|
|
409
429
|
|
|
410
430
|
---
|
|
411
431
|
|
|
412
|
-
###
|
|
432
|
+
### Webhook 传输模式
|
|
433
|
+
|
|
434
|
+
默认情况下,插件通过 **WebSocket** 连接 QQ 平台(出站连接,无需公网 IP)。你也可以切换为 **Webhook** 模式,由 QQ 平台主动 POST 事件到你的 HTTP 端点。
|
|
435
|
+
|
|
436
|
+
| | WebSocket(默认) | Webhook |
|
|
437
|
+
|---|---|---|
|
|
438
|
+
| 连接方式 | 插件主动连接 QQ 网关 | QQ 平台 POST 到你的服务器 |
|
|
439
|
+
| 公网 IP | 不需要 | 需要 |
|
|
440
|
+
| 适用场景 | 开发调试、单实例部署 | 生产环境、水平扩展、Serverless |
|
|
441
|
+
| 会话恢复 | 支持 RESUME | 无状态,无需恢复 |
|
|
442
|
+
| 签名验证 | 平台内置 | 插件自动 Ed25519 验签 |
|
|
443
|
+
|
|
444
|
+
#### 配置方式
|
|
445
|
+
|
|
446
|
+
```json
|
|
447
|
+
{
|
|
448
|
+
"channels": {
|
|
449
|
+
"qqbot": {
|
|
450
|
+
"appId": "111111111",
|
|
451
|
+
"clientSecret": "your-secret",
|
|
452
|
+
"transport": "webhook",
|
|
453
|
+
"webhook": {
|
|
454
|
+
"path": "/qqbot/webhook"
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
| 字段 | 默认值 | 说明 |
|
|
462
|
+
|------|--------|------|
|
|
463
|
+
| `transport` | `"websocket"` | `"websocket"` 或 `"webhook"` |
|
|
464
|
+
| `webhook.path` | `"/qqbot/webhook"` | 接收回调的 HTTP 路径 |
|
|
465
|
+
|
|
466
|
+
#### 平台配置步骤
|
|
467
|
+
|
|
468
|
+
1. 登录 [QQ 开放平台](https://q.qq.com/) → 开发设置 → 消息接收方式
|
|
469
|
+
2. 选择 **HTTP 回调**
|
|
470
|
+
3. 填写回调 URL:`https://your-domain.com/qqbot/webhook`
|
|
471
|
+
4. 平台发送 `op:13` 验证请求,插件自动处理签名验证
|
|
472
|
+
5. 验证通过后,所有事件将以 POST 方式推送到该地址
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
### 群聊配置
|
|
477
|
+
|
|
478
|
+
插件提供灵活的群聊管控能力,支持按群定制触发规则、工具权限和 AI 行为策略。
|
|
479
|
+
|
|
480
|
+
#### @提及触发模式(requireMention)
|
|
481
|
+
|
|
482
|
+
默认情况下,群聊中**必须 @机器人**才会触发 AI 回复。你可以通过配置让 AI 自主判断是否需要发言:
|
|
483
|
+
|
|
484
|
+
| 模式 | 配置值 | 行为 |
|
|
485
|
+
|------|--------|------|
|
|
486
|
+
| **仅 @时回复** | `true`(默认) | 群消息中只有 @了机器人才会触发回复 |
|
|
487
|
+
| **自主发言** | `false` | AI 自主判断每条消息是否需要回复,无需 @ |
|
|
488
|
+
|
|
489
|
+
**优先级链**(从高到低):
|
|
490
|
+
|
|
491
|
+
```
|
|
492
|
+
具体群 groups.{groupOpenid}.requireMention
|
|
493
|
+
> 通配符 groups."*".requireMention
|
|
494
|
+
> 账户级 defaultRequireMention
|
|
495
|
+
> 默认值 true
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
**配置示例:**
|
|
499
|
+
|
|
500
|
+
```json
|
|
501
|
+
{
|
|
502
|
+
"channels": {
|
|
503
|
+
"qqbot": {
|
|
504
|
+
// 账户级:所有群的默认行为
|
|
505
|
+
"defaultRequireMention": false,
|
|
506
|
+
|
|
507
|
+
"accounts": {
|
|
508
|
+
"default": {
|
|
509
|
+
"groups": {
|
|
510
|
+
"*": {
|
|
511
|
+
// 通配符:所有群的兜底规则
|
|
512
|
+
"requireMention": false
|
|
513
|
+
},
|
|
514
|
+
"GROUP_OPENID": {
|
|
515
|
+
// 单群覆盖:这个群仍然需要 @
|
|
516
|
+
"requireMention": true
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
> **使用场景举例:**
|
|
527
|
+
>
|
|
528
|
+
> - 工作群设为 `requireMention: true` — 避免 AI 对每条闲聊都插嘴
|
|
529
|
+
> - 专属 AI 陪伴群设为 `requireMention: false` — 像真人一样自然参与对话
|
|
530
|
+
> - 通过 `/bot-group-allways on|off` 指令可在运行时动态切换账户级默认值
|
|
531
|
+
|
|
532
|
+
#### 其他群配置项
|
|
533
|
+
|
|
534
|
+
除 `requireMention` 外,每个群还支持以下配置:
|
|
535
|
+
|
|
536
|
+
| 字段 | 类型 | 默认值 | 说明 |
|
|
537
|
+
|------|------|--------|------|
|
|
538
|
+
| `ignoreOtherMentions` | `boolean` | `false` | 是否忽略 @了其他人但没 @机器人的消息。开启后这类消息直接丢弃,不记录历史、不触发 AI |
|
|
539
|
+
| `toolPolicy` | `"full" \| "restricted" \| "none"` | `"restricted"` | 群聊中 AI 可使用的工具范围。`full`=全部可用;`restricted`=限制敏感工具(如命令执行、文件操作);`none`=禁止所有工具调用 |
|
|
540
|
+
| `prompt` | `string` | 内置默认提示词 | 该群专属的系统提示词,会追加到全局 systemPrompt 之后 |
|
|
541
|
+
| `historyLimit` | `number` | `50` | 群历史消息缓存条数 |
|
|
542
|
+
|
|
543
|
+
**完整群配置示例:**
|
|
544
|
+
|
|
545
|
+
```json
|
|
546
|
+
{
|
|
547
|
+
"channels": {
|
|
548
|
+
"qqbot": {
|
|
549
|
+
"defaultRequireMention": false,
|
|
550
|
+
"accounts": {
|
|
551
|
+
"default": {
|
|
552
|
+
"groups": {
|
|
553
|
+
"*": {
|
|
554
|
+
"requireMention": true,
|
|
555
|
+
"toolPolicy": "restricted",
|
|
556
|
+
"ignoreOtherMentions": true
|
|
557
|
+
},
|
|
558
|
+
"WORK_GROUP_OPENID": {
|
|
559
|
+
"requireMention": true,
|
|
560
|
+
"toolPolicy": "none",
|
|
561
|
+
"prompt": "你是工作助手,只回答与工作相关的问题"
|
|
562
|
+
},
|
|
563
|
+
"FRIEND_GROUP_OPENID": {
|
|
564
|
+
"requireMention": false,
|
|
565
|
+
"toolPolicy": "full",
|
|
566
|
+
"prompt": "你是群里的朋友,轻松随意地聊天"
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
#### 群访问控制(groupPolicy)
|
|
577
|
+
|
|
578
|
+
通过 `groupPolicy` 控制哪些群允许机器人加入并接收消息:
|
|
579
|
+
|
|
580
|
+
| 策略 | 说明 |
|
|
581
|
+
|------|------|
|
|
582
|
+
| `"open"`(默认) | 所有群均可使用 |
|
|
583
|
+
| `"allowlist"` | 仅 `groupAllowFrom` 白名单中的群可使用 |
|
|
584
|
+
| `"disabled"` | 禁止所有群聊 |
|
|
585
|
+
|
|
586
|
+
```json
|
|
587
|
+
{
|
|
588
|
+
"channels": {
|
|
589
|
+
"qqbot": {
|
|
590
|
+
"groupPolicy": "allowlist",
|
|
591
|
+
"groupAllowFrom": ["ALLOWED_GROUP_OPENID_1", "ALLOWED_GROUP_OPENID_2"]
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
> 也可通过 [**`/bot-group-allways`** 指令](#bot-group-allways--群消息响应模式切换) 在运行时动态切换账户级默认值,无需重启。
|
|
598
|
+
|
|
599
|
+
---
|
|
413
600
|
|
|
414
601
|
#### STT(语音转文字)— 自动转录用户发来的语音消息
|
|
415
602
|
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/src/api.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export declare class ApiError extends Error {
|
|
|
27
27
|
/** 回包中的原始 message 字段(用于向用户展示兜底文案) */
|
|
28
28
|
bizMessage?: string | undefined);
|
|
29
29
|
}
|
|
30
|
+
export declare const API_BASE: string;
|
|
31
|
+
export declare const TOKEN_URL: string;
|
|
30
32
|
/** 由 setQQBotRuntime 调用,将 api.runtime.version 注入到 User-Agent */
|
|
31
33
|
export declare function setOpenClawVersion(version: string): void;
|
|
32
34
|
export declare function getPluginUserAgent(): string;
|
package/dist/src/api.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import { computeFileHash, getCachedFileInfo, setCachedFileInfo } from "./utils/upload-cache.js";
|
|
7
7
|
import { sanitizeFileName } from "./utils/platform.js";
|
|
8
|
+
import { resolveUserAgentSuffix } from "./config.js";
|
|
9
|
+
import { getQQBotRuntime } from "./runtime.js";
|
|
8
10
|
/** 默认使用 console,外部可通过 setApiLogger 注入框架 log */
|
|
9
11
|
let log = {
|
|
10
12
|
info: (msg) => console.log(msg),
|
|
@@ -38,8 +40,9 @@ export class ApiError extends Error {
|
|
|
38
40
|
this.name = "ApiError";
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
+
// 支持环境变量覆盖,用于私有化部署/测试环境
|
|
44
|
+
export const API_BASE = (process.env.QQBOT_BASE_URL?.replace(/\/+$/, "") || "https://api.sgroup.qq.com");
|
|
45
|
+
export const TOKEN_URL = `${process.env.QQBOT_TOKEN_BASE_URL?.replace(/\/+$/, "") || "https://bots.qq.com"}/app/getAppAccessToken`;
|
|
43
46
|
// ============ Plugin User-Agent ============
|
|
44
47
|
// 格式: QQBotPlugin/{version} (Node/{nodeVersion}; {os}; OpenClaw/{openclawVersion})
|
|
45
48
|
// 示例: QQBotPlugin/1.6.0 (Node/22.14.0; darwin; OpenClaw/2026.3.31)
|
|
@@ -53,7 +56,17 @@ export function setOpenClawVersion(version) {
|
|
|
53
56
|
_openclawVersion = version;
|
|
54
57
|
}
|
|
55
58
|
export function getPluginUserAgent() {
|
|
56
|
-
|
|
59
|
+
const base = `QQBotPlugin/${_pluginVersion} (Node/${process.versions.node}; ${os.platform()}; OpenClaw/${_openclawVersion})`;
|
|
60
|
+
let suffix = "";
|
|
61
|
+
try {
|
|
62
|
+
const rt = getQQBotRuntime();
|
|
63
|
+
// rt.config 是配置管理器,调用 .current() 获取实际配置数据
|
|
64
|
+
const cfgMgr = rt.config;
|
|
65
|
+
const cfg = typeof cfgMgr.current === "function" ? cfgMgr.current() : cfgMgr;
|
|
66
|
+
suffix = resolveUserAgentSuffix(cfg);
|
|
67
|
+
}
|
|
68
|
+
catch { /* runtime 未初始化时返回无后缀 UA */ }
|
|
69
|
+
return suffix ? `${base} ${suffix}` : base;
|
|
57
70
|
}
|
|
58
71
|
// 运行时配置
|
|
59
72
|
let currentMarkdownSupport = false;
|
package/dist/src/config.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export declare function resolveGroupAllowFrom(cfg: OpenClawConfig, accountId?: s
|
|
|
17
17
|
/** 检查指定群是否被允许(使用标准策略引擎) */
|
|
18
18
|
export declare function isGroupAllowed(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): boolean;
|
|
19
19
|
type ResolvedGroupConfig = Omit<Required<GroupConfig>, "prompt"> & Pick<GroupConfig, "prompt">;
|
|
20
|
-
/** 解析指定群配置(具体 groupOpenid > 通配符 "*" >
|
|
20
|
+
/** 解析指定群配置(具体 groupOpenid > 通配符 "*" > 账户级 defaultRequireMention > 硬编码默认值) */
|
|
21
21
|
export declare function resolveGroupConfig(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): ResolvedGroupConfig;
|
|
22
22
|
/** 解析群历史消息缓存条数 */
|
|
23
23
|
export declare function resolveHistoryLimit(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): number;
|
|
@@ -31,6 +31,10 @@ export declare function resolveIgnoreOtherMentions(cfg: OpenClawConfig, groupOpe
|
|
|
31
31
|
export declare function resolveToolPolicy(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): ToolPolicy;
|
|
32
32
|
/** 解析群名称(优先配置,fallback 为 openid 前 8 位) */
|
|
33
33
|
export declare function resolveGroupName(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* 解析 User-Agent 追加后缀(仅通道级:channels.qqbot.userAgentSuffix)
|
|
36
|
+
*/
|
|
37
|
+
export declare function resolveUserAgentSuffix(cfg: OpenClawConfig): string;
|
|
34
38
|
/**
|
|
35
39
|
* 列出所有 QQBot 账户 ID
|
|
36
40
|
*/
|
package/dist/src/config.js
CHANGED
|
@@ -81,14 +81,16 @@ export function isGroupAllowed(cfg, groupOpenid, accountId) {
|
|
|
81
81
|
allowlistMatched,
|
|
82
82
|
}).allowed;
|
|
83
83
|
}
|
|
84
|
-
/** 解析指定群配置(具体 groupOpenid > 通配符 "*" >
|
|
84
|
+
/** 解析指定群配置(具体 groupOpenid > 通配符 "*" > 账户级 defaultRequireMention > 硬编码默认值) */
|
|
85
85
|
export function resolveGroupConfig(cfg, groupOpenid, accountId) {
|
|
86
86
|
const account = resolveQQBotAccount(cfg, accountId);
|
|
87
87
|
const groups = account.config?.groups ?? {};
|
|
88
88
|
const wildcardCfg = groups["*"] ?? {};
|
|
89
89
|
const specificCfg = groups[groupOpenid] ?? {};
|
|
90
|
+
// 账户级默认值:defaultRequireMention 配置 > 硬编码默认 true
|
|
91
|
+
const accountDefaultRequireMention = account.config?.defaultRequireMention ?? DEFAULT_GROUP_CONFIG.requireMention;
|
|
90
92
|
return {
|
|
91
|
-
requireMention: specificCfg.requireMention ?? wildcardCfg.requireMention ??
|
|
93
|
+
requireMention: specificCfg.requireMention ?? wildcardCfg.requireMention ?? accountDefaultRequireMention,
|
|
92
94
|
ignoreOtherMentions: specificCfg.ignoreOtherMentions ?? wildcardCfg.ignoreOtherMentions ?? DEFAULT_GROUP_CONFIG.ignoreOtherMentions,
|
|
93
95
|
toolPolicy: specificCfg.toolPolicy ?? wildcardCfg.toolPolicy ?? DEFAULT_GROUP_CONFIG.toolPolicy,
|
|
94
96
|
name: specificCfg.name ?? wildcardCfg.name ?? DEFAULT_GROUP_CONFIG.name,
|
|
@@ -123,6 +125,13 @@ export function resolveGroupName(cfg, groupOpenid, accountId) {
|
|
|
123
125
|
const name = resolveGroupConfig(cfg, groupOpenid, accountId).name;
|
|
124
126
|
return name || groupOpenid.slice(0, 8);
|
|
125
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* 解析 User-Agent 追加后缀(仅通道级:channels.qqbot.userAgentSuffix)
|
|
130
|
+
*/
|
|
131
|
+
export function resolveUserAgentSuffix(cfg) {
|
|
132
|
+
const qqbot = cfg.channels?.qqbot;
|
|
133
|
+
return qqbot?.userAgentSuffix ? String(qqbot.userAgentSuffix).trim() : "";
|
|
134
|
+
}
|
|
126
135
|
function normalizeAppId(raw) {
|
|
127
136
|
if (raw === null || raw === undefined)
|
|
128
137
|
return "";
|
|
@@ -217,6 +226,7 @@ export function resolveQQBotAccount(cfg, accountId) {
|
|
|
217
226
|
systemPrompt: accountConfig.systemPrompt,
|
|
218
227
|
imageServerBaseUrl: accountConfig.imageServerBaseUrl || process.env.QQBOT_IMAGE_SERVER_BASE_URL,
|
|
219
228
|
markdownSupport: accountConfig.markdownSupport !== false,
|
|
229
|
+
userAgentSuffix: resolveUserAgentSuffix(cfg),
|
|
220
230
|
config: accountConfig,
|
|
221
231
|
};
|
|
222
232
|
}
|