@tencent-connect/openclaw-qqbot 1.6.3 → 1.6.4-alpha.qqchannel
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 +15 -57
- package/README.zh.md +15 -57
- package/clawdbot.plugin.json +1 -1
- package/dist/index.js +2 -0
- package/dist/src/api.d.ts +9 -0
- package/dist/src/api.js +14 -8
- package/dist/src/channel.d.ts +0 -8
- package/dist/src/channel.js +29 -11
- package/dist/src/gateway.js +134 -228
- package/dist/src/outbound.js +18 -17
- package/dist/src/ref-index-store.d.ts +1 -1
- package/dist/src/ref-index-store.js +3 -2
- package/dist/src/slash-commands.d.ts +1 -9
- package/dist/src/slash-commands.js +132 -134
- package/dist/src/tools/channel.d.ts +16 -0
- package/dist/src/tools/channel.js +234 -0
- package/dist/src/types.d.ts +2 -2
- package/dist/src/user-messages.d.ts +40 -5
- package/dist/src/user-messages.js +62 -5
- package/index.ts +2 -0
- package/moltbot.plugin.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/cleanup-legacy-plugins.sh +1 -1
- package/scripts/test-sendmedia.ts +116 -0
- package/scripts/upgrade-via-npm.sh +4 -105
- package/scripts/upgrade-via-source.sh +71 -114
- package/skills/qqbot-channel/SKILL.md +263 -0
- package/skills/qqbot-channel/references/api_references.md +521 -0
- package/skills/qqbot-cron/SKILL.md +0 -8
- package/src/api.ts +20 -8
- package/src/channel.ts +33 -12
- package/src/gateway.ts +122 -229
- package/src/openclaw-plugin-sdk.d.ts +39 -0
- package/src/outbound.ts +18 -17
- package/src/ref-index-store.ts +4 -3
- package/src/slash-commands.ts +139 -144
- package/src/tools/channel.ts +281 -0
- package/src/types.ts +2 -2
- package/src/user-messages.ts +70 -4
- package/src/update-checker.ts +0 -155
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
**Connect your AI assistant to QQ — private chat, group chat, and rich media, all in one plugin.**
|
|
12
12
|
|
|
13
|
-
### 🚀 Current Version: `v1.
|
|
13
|
+
### 🚀 Current Version: `v1.5.7`
|
|
14
14
|
|
|
15
15
|
[](./LICENSE)
|
|
16
16
|
[](https://bot.q.qq.com/wiki/)
|
|
@@ -99,7 +99,7 @@ If your main model supports vision (e.g. Tencent Hunyuan `hunyuan-vision`), AI c
|
|
|
99
99
|
>
|
|
100
100
|
> **QQBot**: Here you go! 🐱
|
|
101
101
|
|
|
102
|
-
AI
|
|
102
|
+
AI sends images via `<qqimg>path</qqimg>`. Supports local paths and URLs. Formats: jpg/png/gif/webp/bmp.
|
|
103
103
|
|
|
104
104
|
<img width="360" src="docs/images/4645f2b3a20822b7f8d6664a708529eb_720.jpg" alt="Image Generation Demo" />
|
|
105
105
|
|
|
@@ -109,7 +109,7 @@ AI can send images directly. Supports local paths and URLs. Formats: jpg/png/gif
|
|
|
109
109
|
>
|
|
110
110
|
> **QQBot**: *(sends a voice message)*
|
|
111
111
|
|
|
112
|
-
AI
|
|
112
|
+
AI sends voice via `<qqvoice>path</qqvoice>`. Formats: mp3/wav/silk/ogg. No ffmpeg required.
|
|
113
113
|
|
|
114
114
|
<img width="360" src="docs/images/21dce8bfc553ce23d1bd1b270e9c516c.jpg" alt="TTS Voice Demo" />
|
|
115
115
|
|
|
@@ -129,7 +129,7 @@ This capability depends on OpenClaw cron scheduling and proactive messaging. If
|
|
|
129
129
|
>
|
|
130
130
|
> **QQBot**: *(sends a .txt file)*
|
|
131
131
|
|
|
132
|
-
AI
|
|
132
|
+
AI sends files via `<qqfile>path</qqfile>`. Any format, up to 20MB.
|
|
133
133
|
|
|
134
134
|
<img width="360" src="docs/images/17cada70df90185d45a2d6dd36e92f2f_720.jpg" alt="File Sending Demo" />
|
|
135
135
|
|
|
@@ -139,63 +139,21 @@ AI can send files directly. Any format, up to 20MB.
|
|
|
139
139
|
>
|
|
140
140
|
> **QQBot**: *(sends a video)*
|
|
141
141
|
|
|
142
|
-
AI
|
|
142
|
+
AI sends videos via `<qqvideo>path</qqvideo>`. Supports local files and URLs. Large files (>5MB) auto-show upload progress.
|
|
143
143
|
|
|
144
144
|
<img width="360" src="docs/images/85d03b8a216f267ab7b2aee248a18a41_720.jpg" alt="Video Sending Demo" />
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
### Rich Media Tag Reference
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
| Tag | Direction | Notes |
|
|
149
|
+
|-----|-----------|-------|
|
|
150
|
+
| `<qqimg>path</qqimg>` | Send | jpg/png/gif/webp/bmp, local path or URL |
|
|
151
|
+
| `<qqvoice>path</qqvoice>` | Send | mp3/wav/silk/ogg, no ffmpeg required |
|
|
152
|
+
| `<qqfile>path</qqfile>` | Send | Any format, up to 20MB |
|
|
153
|
+
| `<qqvideo>path</qqvideo>` | Send | Local path or URL |
|
|
154
|
+
| Voice / File / Image | Receive | Auto-transcribe (STT), auto-download, or vision analysis |
|
|
149
155
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
#### `/bot-ping` — Latency Test
|
|
153
|
-
|
|
154
|
-
> **You**: `/bot-ping`
|
|
155
|
-
>
|
|
156
|
-
> **QQBot**: ✅ pong!⏱ Latency: 602ms (network: 602ms, plugin: 0ms)
|
|
157
|
-
|
|
158
|
-
Measures end-to-end latency from QQ server push to plugin response, broken down into network transport and plugin processing time.
|
|
159
|
-
|
|
160
|
-
<img width="360" src="docs/images/slash-ping.jpg" alt="Ping Demo" />
|
|
161
|
-
|
|
162
|
-
#### `/bot-version` — Version Info
|
|
163
|
-
|
|
164
|
-
> **You**: `/bot-version`
|
|
165
|
-
>
|
|
166
|
-
> **QQBot**: 🦞 Framework: OpenClaw 2026.3.13 (61d171a) / 🤖 Plugin: v1.6.2 / 🌟 GitHub repo
|
|
167
|
-
|
|
168
|
-
Shows framework version, plugin version, and a direct link to the official repository.
|
|
169
|
-
|
|
170
|
-
<img width="360" src="docs/images/slash-version.jpg" alt="Version Demo" />
|
|
171
|
-
|
|
172
|
-
#### `/bot-help` — Command List
|
|
173
|
-
|
|
174
|
-
> **You**: `/bot-help`
|
|
175
|
-
>
|
|
176
|
-
> **QQBot**: Lists all available slash commands with clickable shortcuts.
|
|
177
|
-
|
|
178
|
-
<img width="360" src="docs/images/slash-help.jpg" alt="Help Demo" />
|
|
179
|
-
|
|
180
|
-
#### `/bot-upgrade` — Upgrade Guide
|
|
181
|
-
|
|
182
|
-
> **You**: `/bot-upgrade`
|
|
183
|
-
>
|
|
184
|
-
> **QQBot**: 📌 Current version / ✅ Up to date / ⬆️ Upgrade guide / 🌟 GitHub repo
|
|
185
|
-
|
|
186
|
-
Shows current version, update status, upgrade guide link, and official repository.
|
|
187
|
-
|
|
188
|
-
<img width="360" src="docs/images/slash-upgrade.jpg" alt="Upgrade Demo" />
|
|
189
|
-
|
|
190
|
-
#### `/bot-logs` — Log Export
|
|
191
|
-
|
|
192
|
-
> **You**: `/bot-logs`
|
|
193
|
-
>
|
|
194
|
-
> **QQBot**: 📋 Logs packaged (~2000 lines), sending file... *(sends a .txt file)*
|
|
195
|
-
|
|
196
|
-
Exports the last ~2000 lines of gateway logs as a file for quick troubleshooting.
|
|
197
|
-
|
|
198
|
-
<img width="360" src="docs/images/slash-logs.jpg" alt="Logs Demo" />
|
|
156
|
+
> **Under the hood:** 30+ tag variant auto-correction, upload dedup caching, ordered queue delivery, and multi-layer audio format fallback.
|
|
199
157
|
|
|
200
158
|
---
|
|
201
159
|
|
|
@@ -421,7 +379,7 @@ STT supports two-level configuration with priority fallback:
|
|
|
421
379
|
- `provider` — references a key in `models.providers` to inherit `baseUrl` and `apiKey`
|
|
422
380
|
- `voice` — voice variant
|
|
423
381
|
- Set `enabled: false` to disable (default: `true`)
|
|
424
|
-
- When configured, AI can generate and send voice messages
|
|
382
|
+
- When configured, AI can use `<qqvoice>` tags to generate and send voice messages
|
|
425
383
|
|
|
426
384
|
---
|
|
427
385
|
|
package/README.zh.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
**让你的 AI 助手接入 QQ — 私聊、群聊、富媒体,一个插件全搞定。**
|
|
11
11
|
|
|
12
|
-
### 🚀 当前版本: `v1.
|
|
12
|
+
### 🚀 当前版本: `v1.5.7`
|
|
13
13
|
|
|
14
14
|
[](./LICENSE)
|
|
15
15
|
[](https://bot.q.qq.com/wiki/)
|
|
@@ -94,7 +94,7 @@ QQ 的引用事件通常只携带索引键(如 `REFIDX_xxx`),不直接返
|
|
|
94
94
|
>
|
|
95
95
|
> **QQBot**:画好啦!一只可爱的简笔小猫咪🐱🎨
|
|
96
96
|
|
|
97
|
-
AI
|
|
97
|
+
AI 通过 `<qqimg>路径</qqimg>` 发送图片,支持本地文件路径和网络 URL。格式:jpg/png/gif/webp/bmp。
|
|
98
98
|
|
|
99
99
|
<img width="360" src="docs/images/4645f2b3a20822b7f8d6664a708529eb_720.jpg" alt="发图片演示" />
|
|
100
100
|
|
|
@@ -104,7 +104,7 @@ AI 可直接发送图片,支持本地文件路径和网络 URL。格式:jpg/
|
|
|
104
104
|
>
|
|
105
105
|
> **QQBot**:*(发送一条语音消息)*
|
|
106
106
|
|
|
107
|
-
AI
|
|
107
|
+
AI 通过 `<qqvoice>路径</qqvoice>` 发送语音消息。格式:mp3/wav/silk/ogg,无需安装 ffmpeg。
|
|
108
108
|
|
|
109
109
|
<img width="360" src="docs/images/21dce8bfc553ce23d1bd1b270e9c516c.jpg" alt="发语音演示" />
|
|
110
110
|
|
|
@@ -124,7 +124,7 @@ AI 可直接发送语音消息。格式:mp3/wav/silk/ogg,无需安装 ffmpeg
|
|
|
124
124
|
>
|
|
125
125
|
> **QQBot**:*(发送 .txt 文件)*
|
|
126
126
|
|
|
127
|
-
AI
|
|
127
|
+
AI 通过 `<qqfile>路径</qqfile>` 发送文件。任意格式,最大 20MB。
|
|
128
128
|
|
|
129
129
|
<img width="360" src="docs/images/17cada70df90185d45a2d6dd36e92f2f_720.jpg" alt="发文件演示" />
|
|
130
130
|
|
|
@@ -134,63 +134,21 @@ AI 可直接发送文件。任意格式,最大 20MB。
|
|
|
134
134
|
>
|
|
135
135
|
> **QQBot**:*(发送视频)*
|
|
136
136
|
|
|
137
|
-
AI
|
|
137
|
+
AI 通过 `<qqvideo>路径</qqvideo>` 发送视频,支持本地文件和公网 URL。大文件(>5MB)自动提示上传进度。
|
|
138
138
|
|
|
139
139
|
<img width="360" src="docs/images/85d03b8a216f267ab7b2aee248a18a41_720.jpg" alt="发视频演示" />
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
### 富媒体标签参考
|
|
142
142
|
|
|
143
|
-
|
|
143
|
+
| 标签 | 方向 | 说明 |
|
|
144
|
+
|------|------|------|
|
|
145
|
+
| `<qqimg>路径</qqimg>` | 发送 | jpg/png/gif/webp/bmp,本地路径或 URL |
|
|
146
|
+
| `<qqvoice>路径</qqvoice>` | 发送 | mp3/wav/silk/ogg,无需 ffmpeg |
|
|
147
|
+
| `<qqfile>路径</qqfile>` | 发送 | 任意格式,最大 20MB |
|
|
148
|
+
| `<qqvideo>路径</qqvideo>` | 发送 | 本地路径或 URL |
|
|
149
|
+
| 语音 / 文件 / 图片 | 接收 | 自动转录(STT)、自动下载、或视觉分析 |
|
|
144
150
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
#### `/bot-ping` — 延迟测试
|
|
148
|
-
|
|
149
|
-
> **你**:`/bot-ping`
|
|
150
|
-
>
|
|
151
|
-
> **QQBot**:✅ pong!⏱ 延迟: 602ms(网络传输: 602ms,插件处理: 0ms)
|
|
152
|
-
|
|
153
|
-
测量从 QQ 服务器推送到插件响应的端到端延迟,细分网络传输和插件处理两段耗时。
|
|
154
|
-
|
|
155
|
-
<img width="360" src="docs/images/slash-ping.jpg" alt="Ping 演示" />
|
|
156
|
-
|
|
157
|
-
#### `/bot-version` — 版本信息
|
|
158
|
-
|
|
159
|
-
> **你**:`/bot-version`
|
|
160
|
-
>
|
|
161
|
-
> **QQBot**:🦞框架版本:OpenClaw 2026.3.13 (61d171a) / 🤖QQBot 插件版本:v1.6.2 / 🌟官方 GitHub 仓库
|
|
162
|
-
|
|
163
|
-
一目了然查看框架版本、插件版本,并可直接跳转官方仓库。
|
|
164
|
-
|
|
165
|
-
<img width="360" src="docs/images/slash-version.jpg" alt="Version 演示" />
|
|
166
|
-
|
|
167
|
-
#### `/bot-help` — 指令列表
|
|
168
|
-
|
|
169
|
-
> **你**:`/bot-help`
|
|
170
|
-
>
|
|
171
|
-
> **QQBot**:列出所有可用的斜杠指令及说明,指令可点击快速输入。
|
|
172
|
-
|
|
173
|
-
<img width="360" src="docs/images/slash-help.jpg" alt="Help 演示" />
|
|
174
|
-
|
|
175
|
-
#### `/bot-upgrade` — 升级指引
|
|
176
|
-
|
|
177
|
-
> **你**:`/bot-upgrade`
|
|
178
|
-
>
|
|
179
|
-
> **QQBot**:📌当前版本 / ✅当前已是最新版本 / ⬆️升级指引 / 🌟官方 GitHub 仓库
|
|
180
|
-
|
|
181
|
-
显示当前版本、更新状态、升级文档链接及官方仓库入口。
|
|
182
|
-
|
|
183
|
-
<img width="360" src="docs/images/slash-upgrade.jpg" alt="Upgrade 演示" />
|
|
184
|
-
|
|
185
|
-
#### `/bot-logs` — 日志导出
|
|
186
|
-
|
|
187
|
-
> **你**:`/bot-logs`
|
|
188
|
-
>
|
|
189
|
-
> **QQBot**:📋 日志已打包(约 2000 行),正在发送文件… *(发送 .txt 文件)*
|
|
190
|
-
|
|
191
|
-
导出最近约 2000 行网关日志为文件,方便快速排查问题。
|
|
192
|
-
|
|
193
|
-
<img width="360" src="docs/images/slash-logs.jpg" alt="Logs 演示" />
|
|
151
|
+
> **底层细节:** 30+ 种标签变体自动纠正、上传去重缓存、有序队列发送、音频格式多层降级。
|
|
194
152
|
|
|
195
153
|
---
|
|
196
154
|
|
|
@@ -416,7 +374,7 @@ STT 支持两级配置,按优先级查找:
|
|
|
416
374
|
- `provider` — 引用 `models.providers` 中的 key,自动继承 `baseUrl` 和 `apiKey`
|
|
417
375
|
- `voice` — 语音音色
|
|
418
376
|
- 设置 `enabled: false` 可禁用(默认:`true`)
|
|
419
|
-
- 配置后,AI
|
|
377
|
+
- 配置后,AI 可使用 `<qqvoice>` 标签生成并发送语音消息
|
|
420
378
|
|
|
421
379
|
---
|
|
422
380
|
|
package/clawdbot.plugin.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "OpenClaw QQ Bot",
|
|
4
4
|
"description": "QQ Bot channel plugin with message support, cron jobs, and proactive messaging",
|
|
5
5
|
"channels": ["qqbot"],
|
|
6
|
-
"skills": ["skills/qqbot-cron", "skills/qqbot-media"],
|
|
6
|
+
"skills": ["skills/qqbot-channel", "skills/qqbot-cron", "skills/qqbot-media"],
|
|
7
7
|
"capabilities": {
|
|
8
8
|
"proactiveMessaging": true,
|
|
9
9
|
"cronJobs": true
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
2
2
|
import { qqbotPlugin } from "./src/channel.js";
|
|
3
3
|
import { setQQBotRuntime } from "./src/runtime.js";
|
|
4
|
+
import { registerChannelTool } from "./src/tools/channel.js";
|
|
4
5
|
const plugin = {
|
|
5
6
|
id: "openclaw-qqbot",
|
|
6
7
|
name: "QQ Bot",
|
|
@@ -9,6 +10,7 @@ const plugin = {
|
|
|
9
10
|
register(api) {
|
|
10
11
|
setQQBotRuntime(api.runtime);
|
|
11
12
|
api.registerChannel({ plugin: qqbotPlugin });
|
|
13
|
+
registerChannelTool(api);
|
|
12
14
|
},
|
|
13
15
|
};
|
|
14
16
|
export default plugin;
|
package/dist/src/api.d.ts
CHANGED
|
@@ -81,6 +81,15 @@ export declare function sendChannelMessage(accessToken: string, channelId: strin
|
|
|
81
81
|
id: string;
|
|
82
82
|
timestamp: string;
|
|
83
83
|
}>;
|
|
84
|
+
/**
|
|
85
|
+
* 发送频道私信消息
|
|
86
|
+
* @param guildId - 私信会话的 guild_id(由 DIRECT_MESSAGE_CREATE 事件提供)
|
|
87
|
+
* @param msgId - 被动回复时必填
|
|
88
|
+
*/
|
|
89
|
+
export declare function sendDmMessage(accessToken: string, guildId: string, content: string, msgId?: string): Promise<{
|
|
90
|
+
id: string;
|
|
91
|
+
timestamp: string;
|
|
92
|
+
}>;
|
|
84
93
|
export declare function sendGroupMessage(accessToken: string, groupOpenid: string, content: string, msgId?: string): Promise<MessageResponse>;
|
|
85
94
|
export declare function sendProactiveC2CMessage(accessToken: string, openid: string, content: string): Promise<MessageResponse>;
|
|
86
95
|
export declare function sendProactiveGroupMessage(accessToken: string, groupOpenid: string, content: string): Promise<{
|
package/dist/src/api.js
CHANGED
|
@@ -58,12 +58,8 @@ const tokenFetchPromises = new Map();
|
|
|
58
58
|
export async function getAccessToken(appId, clientSecret) {
|
|
59
59
|
const normalizedAppId = String(appId).trim();
|
|
60
60
|
const cachedToken = tokenCacheMap.get(normalizedAppId);
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
const REFRESH_AHEAD_MS = cachedToken
|
|
64
|
-
? Math.min(5 * 60 * 1000, (cachedToken.expiresAt - Date.now()) / 3)
|
|
65
|
-
: 0;
|
|
66
|
-
if (cachedToken && Date.now() < cachedToken.expiresAt - REFRESH_AHEAD_MS) {
|
|
61
|
+
// 检查缓存:未过期 且 appId 未变化 时复用
|
|
62
|
+
if (cachedToken && Date.now() < cachedToken.expiresAt - 5 * 60 * 1000) {
|
|
67
63
|
return cachedToken.token;
|
|
68
64
|
}
|
|
69
65
|
// Singleflight: 如果当前 appId 已有进行中的 Token 获取请求,复用它
|
|
@@ -163,8 +159,7 @@ export function getTokenStatus(appId) {
|
|
|
163
159
|
if (!cached) {
|
|
164
160
|
return { status: "none", expiresAt: null };
|
|
165
161
|
}
|
|
166
|
-
const
|
|
167
|
-
const isValid = remaining > Math.min(5 * 60 * 1000, remaining / 3);
|
|
162
|
+
const isValid = Date.now() < cached.expiresAt - 5 * 60 * 1000;
|
|
168
163
|
return { status: isValid ? "valid" : "expired", expiresAt: cached.expiresAt };
|
|
169
164
|
}
|
|
170
165
|
/**
|
|
@@ -340,6 +335,17 @@ export async function sendChannelMessage(accessToken, channelId, content, msgId)
|
|
|
340
335
|
...(msgId ? { msg_id: msgId } : {}),
|
|
341
336
|
});
|
|
342
337
|
}
|
|
338
|
+
/**
|
|
339
|
+
* 发送频道私信消息
|
|
340
|
+
* @param guildId - 私信会话的 guild_id(由 DIRECT_MESSAGE_CREATE 事件提供)
|
|
341
|
+
* @param msgId - 被动回复时必填
|
|
342
|
+
*/
|
|
343
|
+
export async function sendDmMessage(accessToken, guildId, content, msgId) {
|
|
344
|
+
return apiRequest(accessToken, "POST", `/dms/${guildId}/messages`, {
|
|
345
|
+
content,
|
|
346
|
+
...(msgId ? { msg_id: msgId } : {}),
|
|
347
|
+
});
|
|
348
|
+
}
|
|
343
349
|
export async function sendGroupMessage(accessToken, groupOpenid, content, msgId) {
|
|
344
350
|
const msgSeq = msgId ? getNextMsgSeq(msgId) : 1;
|
|
345
351
|
const body = buildMessageBody(content, msgId, msgSeq);
|
package/dist/src/channel.d.ts
CHANGED
|
@@ -1,11 +1,3 @@
|
|
|
1
1
|
import { type ChannelPlugin } from "openclaw/plugin-sdk";
|
|
2
2
|
import type { ResolvedQQBotAccount } from "./types.js";
|
|
3
|
-
/** QQ Bot 单条消息文本长度上限 */
|
|
4
|
-
export declare const TEXT_CHUNK_LIMIT = 5000;
|
|
5
|
-
/**
|
|
6
|
-
* Markdown 感知的文本分块函数
|
|
7
|
-
* 委托给 SDK 内置的 channel.text.chunkMarkdownText
|
|
8
|
-
* 支持代码块自动关闭/重开、括号感知等
|
|
9
|
-
*/
|
|
10
|
-
export declare function chunkText(text: string, limit: number): string[];
|
|
11
3
|
export declare const qqbotPlugin: ChannelPlugin<ResolvedQQBotAccount>;
|
package/dist/src/channel.js
CHANGED
|
@@ -4,16 +4,34 @@ import { sendText, sendMedia } from "./outbound.js";
|
|
|
4
4
|
import { startGateway } from "./gateway.js";
|
|
5
5
|
import { qqbotOnboardingAdapter } from "./onboarding.js";
|
|
6
6
|
import { getQQBotRuntime } from "./runtime.js";
|
|
7
|
-
/** QQ Bot 单条消息文本长度上限 */
|
|
8
|
-
export const TEXT_CHUNK_LIMIT = 5000;
|
|
9
7
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* 支持代码块自动关闭/重开、括号感知等
|
|
8
|
+
* 简单的文本分块函数
|
|
9
|
+
* 用于预先分块长文本
|
|
13
10
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
function chunkText(text, limit) {
|
|
12
|
+
if (text.length <= limit)
|
|
13
|
+
return [text];
|
|
14
|
+
const chunks = [];
|
|
15
|
+
let remaining = text;
|
|
16
|
+
while (remaining.length > 0) {
|
|
17
|
+
if (remaining.length <= limit) {
|
|
18
|
+
chunks.push(remaining);
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
// 尝试在换行处分割
|
|
22
|
+
let splitAt = remaining.lastIndexOf("\n", limit);
|
|
23
|
+
if (splitAt <= 0 || splitAt < limit * 0.5) {
|
|
24
|
+
// 没找到合适的换行,尝试在空格处分割
|
|
25
|
+
splitAt = remaining.lastIndexOf(" ", limit);
|
|
26
|
+
}
|
|
27
|
+
if (splitAt <= 0 || splitAt < limit * 0.5) {
|
|
28
|
+
// 还是没找到,强制在 limit 处分割
|
|
29
|
+
splitAt = limit;
|
|
30
|
+
}
|
|
31
|
+
chunks.push(remaining.slice(0, splitAt));
|
|
32
|
+
remaining = remaining.slice(splitAt).trimStart();
|
|
33
|
+
}
|
|
34
|
+
return chunks;
|
|
17
35
|
}
|
|
18
36
|
export const qqbotPlugin = {
|
|
19
37
|
id: "qqbot",
|
|
@@ -34,7 +52,7 @@ export const qqbotPlugin = {
|
|
|
34
52
|
* blockStreaming: true 表示该 Channel 支持块流式
|
|
35
53
|
* 框架会收集流式响应,然后通过 deliver 回调发送
|
|
36
54
|
*/
|
|
37
|
-
blockStreaming:
|
|
55
|
+
blockStreaming: false,
|
|
38
56
|
},
|
|
39
57
|
reload: { configPrefixes: ["channels.qqbot"] },
|
|
40
58
|
// CLI onboarding wizard
|
|
@@ -187,9 +205,9 @@ export const qqbotPlugin = {
|
|
|
187
205
|
},
|
|
188
206
|
outbound: {
|
|
189
207
|
deliveryMode: "direct",
|
|
190
|
-
chunker:
|
|
208
|
+
chunker: chunkText,
|
|
191
209
|
chunkerMode: "markdown",
|
|
192
|
-
textChunkLimit:
|
|
210
|
+
textChunkLimit: 2000,
|
|
193
211
|
sendText: async ({ to, text, accountId, replyToId, cfg }) => {
|
|
194
212
|
console.log(`[qqbot:channel] sendText called — accountId=${accountId}, to=${to}, replyToId=${replyToId}, text.length=${text?.length ?? 0}`);
|
|
195
213
|
console.log(`[qqbot:channel] sendText text preview: ${text?.slice(0, 100)}${(text?.length ?? 0) > 100 ? "..." : ""}`);
|