@ryantest/openclaw-qqbot 0.0.1

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.
Files changed (197) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +483 -0
  3. package/README.zh.md +478 -0
  4. package/bin/qqbot-cli.js +243 -0
  5. package/clawdbot.plugin.json +16 -0
  6. package/dist/index.d.ts +17 -0
  7. package/dist/index.js +26 -0
  8. package/dist/src/admin-resolver.d.ts +27 -0
  9. package/dist/src/admin-resolver.js +122 -0
  10. package/dist/src/api.d.ts +156 -0
  11. package/dist/src/api.js +599 -0
  12. package/dist/src/channel.d.ts +11 -0
  13. package/dist/src/channel.js +354 -0
  14. package/dist/src/config.d.ts +25 -0
  15. package/dist/src/config.js +161 -0
  16. package/dist/src/credential-backup.d.ts +31 -0
  17. package/dist/src/credential-backup.js +66 -0
  18. package/dist/src/gateway.d.ts +18 -0
  19. package/dist/src/gateway.js +1265 -0
  20. package/dist/src/image-server.d.ts +68 -0
  21. package/dist/src/image-server.js +462 -0
  22. package/dist/src/inbound-attachments.d.ts +58 -0
  23. package/dist/src/inbound-attachments.js +234 -0
  24. package/dist/src/known-users.d.ts +100 -0
  25. package/dist/src/known-users.js +263 -0
  26. package/dist/src/message-queue.d.ts +50 -0
  27. package/dist/src/message-queue.js +115 -0
  28. package/dist/src/onboarding.d.ts +10 -0
  29. package/dist/src/onboarding.js +203 -0
  30. package/dist/src/outbound-deliver.d.ts +48 -0
  31. package/dist/src/outbound-deliver.js +462 -0
  32. package/dist/src/outbound.d.ts +203 -0
  33. package/dist/src/outbound.js +1102 -0
  34. package/dist/src/proactive.d.ts +170 -0
  35. package/dist/src/proactive.js +399 -0
  36. package/dist/src/ref-index-store.d.ts +70 -0
  37. package/dist/src/ref-index-store.js +273 -0
  38. package/dist/src/reply-dispatcher.d.ts +35 -0
  39. package/dist/src/reply-dispatcher.js +311 -0
  40. package/dist/src/runtime.d.ts +3 -0
  41. package/dist/src/runtime.js +10 -0
  42. package/dist/src/session-store.d.ts +52 -0
  43. package/dist/src/session-store.js +254 -0
  44. package/dist/src/slash-commands.d.ts +71 -0
  45. package/dist/src/slash-commands.js +1179 -0
  46. package/dist/src/startup-greeting.d.ts +30 -0
  47. package/dist/src/startup-greeting.js +78 -0
  48. package/dist/src/stt.d.ts +21 -0
  49. package/dist/src/stt.js +70 -0
  50. package/dist/src/tools/channel.d.ts +16 -0
  51. package/dist/src/tools/channel.js +234 -0
  52. package/dist/src/tools/remind.d.ts +2 -0
  53. package/dist/src/tools/remind.js +247 -0
  54. package/dist/src/types.d.ts +175 -0
  55. package/dist/src/types.js +1 -0
  56. package/dist/src/typing-keepalive.d.ts +27 -0
  57. package/dist/src/typing-keepalive.js +64 -0
  58. package/dist/src/update-checker.d.ts +34 -0
  59. package/dist/src/update-checker.js +166 -0
  60. package/dist/src/user-messages.d.ts +8 -0
  61. package/dist/src/user-messages.js +8 -0
  62. package/dist/src/utils/audio-convert.d.ts +89 -0
  63. package/dist/src/utils/audio-convert.js +704 -0
  64. package/dist/src/utils/file-utils.d.ts +55 -0
  65. package/dist/src/utils/file-utils.js +150 -0
  66. package/dist/src/utils/image-size.d.ts +51 -0
  67. package/dist/src/utils/image-size.js +234 -0
  68. package/dist/src/utils/media-tags.d.ts +14 -0
  69. package/dist/src/utils/media-tags.js +164 -0
  70. package/dist/src/utils/payload.d.ts +112 -0
  71. package/dist/src/utils/payload.js +186 -0
  72. package/dist/src/utils/platform.d.ts +137 -0
  73. package/dist/src/utils/platform.js +390 -0
  74. package/dist/src/utils/text-parsing.d.ts +32 -0
  75. package/dist/src/utils/text-parsing.js +80 -0
  76. package/dist/src/utils/upload-cache.d.ts +34 -0
  77. package/dist/src/utils/upload-cache.js +93 -0
  78. package/index.ts +31 -0
  79. package/moltbot.plugin.json +16 -0
  80. package/node_modules/@eshaz/web-worker/LICENSE +201 -0
  81. package/node_modules/@eshaz/web-worker/README.md +134 -0
  82. package/node_modules/@eshaz/web-worker/browser.js +17 -0
  83. package/node_modules/@eshaz/web-worker/cjs/browser.js +16 -0
  84. package/node_modules/@eshaz/web-worker/cjs/node.js +219 -0
  85. package/node_modules/@eshaz/web-worker/index.d.ts +4 -0
  86. package/node_modules/@eshaz/web-worker/node.js +223 -0
  87. package/node_modules/@eshaz/web-worker/package.json +54 -0
  88. package/node_modules/@wasm-audio-decoders/common/index.js +5 -0
  89. package/node_modules/@wasm-audio-decoders/common/package.json +36 -0
  90. package/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderCommon.js +231 -0
  91. package/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderWorker.js +129 -0
  92. package/node_modules/@wasm-audio-decoders/common/src/puff/README +67 -0
  93. package/node_modules/@wasm-audio-decoders/common/src/puff/build_puff.js +31 -0
  94. package/node_modules/@wasm-audio-decoders/common/src/puff/puff.c +863 -0
  95. package/node_modules/@wasm-audio-decoders/common/src/puff/puff.h +35 -0
  96. package/node_modules/@wasm-audio-decoders/common/src/utilities.js +3 -0
  97. package/node_modules/@wasm-audio-decoders/common/types.d.ts +7 -0
  98. package/node_modules/mpg123-decoder/README.md +265 -0
  99. package/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js +185 -0
  100. package/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js.map +1 -0
  101. package/node_modules/mpg123-decoder/index.js +8 -0
  102. package/node_modules/mpg123-decoder/package.json +58 -0
  103. package/node_modules/mpg123-decoder/src/EmscriptenWasm.js +464 -0
  104. package/node_modules/mpg123-decoder/src/MPEGDecoder.js +200 -0
  105. package/node_modules/mpg123-decoder/src/MPEGDecoderWebWorker.js +21 -0
  106. package/node_modules/mpg123-decoder/types.d.ts +30 -0
  107. package/node_modules/silk-wasm/LICENSE +21 -0
  108. package/node_modules/silk-wasm/README.md +85 -0
  109. package/node_modules/silk-wasm/lib/index.cjs +16 -0
  110. package/node_modules/silk-wasm/lib/index.d.ts +70 -0
  111. package/node_modules/silk-wasm/lib/index.mjs +16 -0
  112. package/node_modules/silk-wasm/lib/silk.wasm +0 -0
  113. package/node_modules/silk-wasm/lib/utils.d.ts +4 -0
  114. package/node_modules/silk-wasm/package.json +39 -0
  115. package/node_modules/simple-yenc/.github/FUNDING.yml +1 -0
  116. package/node_modules/simple-yenc/.prettierignore +1 -0
  117. package/node_modules/simple-yenc/LICENSE +7 -0
  118. package/node_modules/simple-yenc/README.md +163 -0
  119. package/node_modules/simple-yenc/dist/esm.js +1 -0
  120. package/node_modules/simple-yenc/dist/index.js +1 -0
  121. package/node_modules/simple-yenc/package.json +50 -0
  122. package/node_modules/simple-yenc/rollup.config.js +27 -0
  123. package/node_modules/simple-yenc/src/simple-yenc.js +302 -0
  124. package/node_modules/ws/LICENSE +20 -0
  125. package/node_modules/ws/README.md +548 -0
  126. package/node_modules/ws/browser.js +8 -0
  127. package/node_modules/ws/index.js +13 -0
  128. package/node_modules/ws/lib/buffer-util.js +131 -0
  129. package/node_modules/ws/lib/constants.js +19 -0
  130. package/node_modules/ws/lib/event-target.js +292 -0
  131. package/node_modules/ws/lib/extension.js +203 -0
  132. package/node_modules/ws/lib/limiter.js +55 -0
  133. package/node_modules/ws/lib/permessage-deflate.js +528 -0
  134. package/node_modules/ws/lib/receiver.js +706 -0
  135. package/node_modules/ws/lib/sender.js +602 -0
  136. package/node_modules/ws/lib/stream.js +161 -0
  137. package/node_modules/ws/lib/subprotocol.js +62 -0
  138. package/node_modules/ws/lib/validation.js +152 -0
  139. package/node_modules/ws/lib/websocket-server.js +554 -0
  140. package/node_modules/ws/lib/websocket.js +1393 -0
  141. package/node_modules/ws/package.json +69 -0
  142. package/node_modules/ws/wrapper.mjs +8 -0
  143. package/openclaw.plugin.json +16 -0
  144. package/package.json +76 -0
  145. package/scripts/cleanup-legacy-plugins.sh +124 -0
  146. package/scripts/proactive-api-server.ts +369 -0
  147. package/scripts/send-proactive.ts +293 -0
  148. package/scripts/set-markdown.sh +156 -0
  149. package/scripts/test-sendmedia.ts +116 -0
  150. package/scripts/upgrade-via-alt-pkg.sh +307 -0
  151. package/scripts/upgrade-via-npm.ps1 +296 -0
  152. package/scripts/upgrade-via-npm.sh +301 -0
  153. package/scripts/upgrade-via-source.sh +774 -0
  154. package/skills/qqbot-channel/SKILL.md +263 -0
  155. package/skills/qqbot-channel/references/api_references.md +521 -0
  156. package/skills/qqbot-media/SKILL.md +56 -0
  157. package/skills/qqbot-remind/SKILL.md +149 -0
  158. package/src/admin-resolver.ts +140 -0
  159. package/src/api.ts +819 -0
  160. package/src/bot-logs-2026-03-21T11-21-47(2).txt +46 -0
  161. package/src/channel.ts +381 -0
  162. package/src/config.ts +187 -0
  163. package/src/credential-backup.ts +72 -0
  164. package/src/gateway.log +43 -0
  165. package/src/gateway.ts +1404 -0
  166. package/src/image-server.ts +539 -0
  167. package/src/inbound-attachments.ts +304 -0
  168. package/src/known-users.ts +353 -0
  169. package/src/message-queue.ts +169 -0
  170. package/src/onboarding.ts +274 -0
  171. package/src/openclaw-2026-03-21.log +3729 -0
  172. package/src/openclaw-plugin-sdk.d.ts +522 -0
  173. package/src/outbound-deliver.ts +552 -0
  174. package/src/outbound.ts +1266 -0
  175. package/src/proactive.ts +530 -0
  176. package/src/ref-index-store.ts +357 -0
  177. package/src/reply-dispatcher.ts +334 -0
  178. package/src/runtime.ts +14 -0
  179. package/src/session-store.ts +303 -0
  180. package/src/slash-commands.ts +1305 -0
  181. package/src/startup-greeting.ts +98 -0
  182. package/src/stt.ts +86 -0
  183. package/src/tools/channel.ts +281 -0
  184. package/src/tools/remind.ts +296 -0
  185. package/src/types.ts +183 -0
  186. package/src/typing-keepalive.ts +59 -0
  187. package/src/update-checker.ts +179 -0
  188. package/src/user-messages.ts +7 -0
  189. package/src/utils/audio-convert.ts +803 -0
  190. package/src/utils/file-utils.ts +167 -0
  191. package/src/utils/image-size.ts +266 -0
  192. package/src/utils/media-tags.ts +182 -0
  193. package/src/utils/payload.ts +265 -0
  194. package/src/utils/platform.ts +435 -0
  195. package/src/utils/text-parsing.ts +82 -0
  196. package/src/utils/upload-cache.ts +128 -0
  197. package/tsconfig.json +16 -0
@@ -0,0 +1,247 @@
1
+ // ========== JSON Schema ==========
2
+ const RemindSchema = {
3
+ type: "object",
4
+ properties: {
5
+ action: {
6
+ type: "string",
7
+ description: "操作类型。add=创建提醒, list=查看已有提醒, remove=删除提醒",
8
+ enum: ["add", "list", "remove"],
9
+ },
10
+ content: {
11
+ type: "string",
12
+ description: '提醒内容,如"喝水"、"开会"。action=add 时必填。',
13
+ },
14
+ to: {
15
+ type: "string",
16
+ description: "投递目标地址,取自上下文中 [QQBot] to= 的值。" +
17
+ "私聊格式:user_openid,群聊格式:group:group_openid。action=add 时必填。",
18
+ },
19
+ time: {
20
+ type: "string",
21
+ description: "时间描述。支持两种格式:\n" +
22
+ "1. 相对时间:如 \"5m\"(5分钟后)、\"1h\"(1小时后)、\"1h30m\"(1.5小时后)、\"2d\"(2天后)\n" +
23
+ "2. cron 表达式:如 \"0 8 * * *\"(每天8点)、\"0 9 * * 1-5\"(工作日9点)\n" +
24
+ "系统会自动判断:包含空格的视为 cron 表达式(周期提醒),否则视为相对时间(一次性提醒)。\n" +
25
+ "action=add 时必填。",
26
+ },
27
+ timezone: {
28
+ type: "string",
29
+ description: "时区,仅周期提醒(cron)时需要。默认 \"Asia/Shanghai\"。",
30
+ },
31
+ name: {
32
+ type: "string",
33
+ description: "提醒任务名称(可选)。默认自动从 content 截取前 20 字。",
34
+ },
35
+ jobId: {
36
+ type: "string",
37
+ description: "要删除的任务 ID。action=remove 时必填,先用 list 获取。",
38
+ },
39
+ },
40
+ required: ["action"],
41
+ };
42
+ // ========== 工具函数 ==========
43
+ function json(data) {
44
+ return {
45
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
46
+ details: data,
47
+ };
48
+ }
49
+ /**
50
+ * 解析相对时间字符串为毫秒数
51
+ * 支持格式:5m, 1h, 1h30m, 2d, 30s, 1d2h30m 等
52
+ */
53
+ function parseRelativeTime(timeStr) {
54
+ const s = timeStr.trim().toLowerCase();
55
+ // 纯数字 → 视为分钟
56
+ if (/^\d+$/.test(s)) {
57
+ return parseInt(s, 10) * 60_000;
58
+ }
59
+ let totalMs = 0;
60
+ let matched = false;
61
+ const regex = /(\d+(?:\.\d+)?)\s*(d|h|m|s)/g;
62
+ let match;
63
+ while ((match = regex.exec(s)) !== null) {
64
+ matched = true;
65
+ const value = parseFloat(match[1]);
66
+ const unit = match[2];
67
+ switch (unit) {
68
+ case "d":
69
+ totalMs += value * 86_400_000;
70
+ break;
71
+ case "h":
72
+ totalMs += value * 3_600_000;
73
+ break;
74
+ case "m":
75
+ totalMs += value * 60_000;
76
+ break;
77
+ case "s":
78
+ totalMs += value * 1_000;
79
+ break;
80
+ }
81
+ }
82
+ return matched ? Math.round(totalMs) : null;
83
+ }
84
+ /**
85
+ * 判断是否为 cron 表达式(包含空格且有 3~6 段)
86
+ */
87
+ function isCronExpression(timeStr) {
88
+ const parts = timeStr.trim().split(/\s+/);
89
+ return parts.length >= 3 && parts.length <= 6;
90
+ }
91
+ /**
92
+ * 自动生成任务名称
93
+ */
94
+ function generateJobName(content) {
95
+ const trimmed = content.trim();
96
+ const short = trimmed.length > 20 ? trimmed.slice(0, 20) + "…" : trimmed;
97
+ return `提醒: ${short}`;
98
+ }
99
+ /**
100
+ * 构建一次性提醒的 cron 工具参数
101
+ */
102
+ function buildOnceJob(params, delayMs) {
103
+ const atMs = Date.now() + delayMs;
104
+ const to = params.to;
105
+ const content = params.content;
106
+ const name = params.name || generateJobName(content);
107
+ return {
108
+ action: "add",
109
+ job: {
110
+ name,
111
+ schedule: { kind: "at", atMs },
112
+ sessionTarget: "isolated",
113
+ wakeMode: "now",
114
+ deleteAfterRun: true,
115
+ payload: {
116
+ kind: "agentTurn",
117
+ message: buildReminderPrompt(content),
118
+ deliver: true,
119
+ channel: "qqbot",
120
+ to,
121
+ },
122
+ },
123
+ };
124
+ }
125
+ /**
126
+ * 构建周期提醒的 cron 工具参数
127
+ */
128
+ function buildCronJob(params) {
129
+ const to = params.to;
130
+ const content = params.content;
131
+ const name = params.name || generateJobName(content);
132
+ const tz = params.timezone || "Asia/Shanghai";
133
+ return {
134
+ action: "add",
135
+ job: {
136
+ name,
137
+ schedule: { kind: "cron", expr: params.time.trim(), tz },
138
+ sessionTarget: "isolated",
139
+ wakeMode: "now",
140
+ payload: {
141
+ kind: "agentTurn",
142
+ message: buildReminderPrompt(content),
143
+ deliver: true,
144
+ channel: "qqbot",
145
+ to,
146
+ },
147
+ },
148
+ };
149
+ }
150
+ /**
151
+ * 构建提醒 payload 中的 AI prompt
152
+ */
153
+ function buildReminderPrompt(content) {
154
+ return (`你是一个暖心的提醒助手。请用温暖、有趣的方式提醒用户:${content}。` +
155
+ `要求:(1) 不要回复HEARTBEAT_OK (2) 不要解释你是谁 ` +
156
+ `(3) 直接输出一条暖心的提醒消息 (4) 可以加一句简短的鸡汤或关怀的话 ` +
157
+ `(5) 控制在2-3句话以内 (6) 用emoji点缀`);
158
+ }
159
+ /**
160
+ * 格式化延迟时间为人类可读文本
161
+ */
162
+ function formatDelay(ms) {
163
+ const totalSeconds = Math.round(ms / 1000);
164
+ if (totalSeconds < 60)
165
+ return `${totalSeconds}秒`;
166
+ const totalMinutes = Math.round(ms / 60_000);
167
+ if (totalMinutes < 60)
168
+ return `${totalMinutes}分钟`;
169
+ const hours = Math.floor(totalMinutes / 60);
170
+ const minutes = totalMinutes % 60;
171
+ if (minutes === 0)
172
+ return `${hours}小时`;
173
+ return `${hours}小时${minutes}分钟`;
174
+ }
175
+ // ========== 注册入口 ==========
176
+ export function registerRemindTool(api) {
177
+ api.registerTool({
178
+ name: "qqbot_remind",
179
+ label: "QQBot 定时提醒",
180
+ description: "创建、查询、删除 QQ 定时提醒。" +
181
+ "使用简单参数即可,无需手动构造 cron JSON。\n" +
182
+ "创建提醒:action=add, content=提醒内容, to=目标地址, time=时间\n" +
183
+ "查看提醒:action=list\n" +
184
+ "删除提醒:action=remove, jobId=任务ID(先 list 获取)\n" +
185
+ "时间格式示例:\"5m\"(5分钟后) \"1h\"(1小时后) \"0 8 * * *\"(每天8点)",
186
+ parameters: RemindSchema,
187
+ execute(_toolCallId, params) {
188
+ const p = params;
189
+ // ===== list =====
190
+ if (p.action === "list") {
191
+ return json({
192
+ _instruction: "请立即使用 cron 工具执行以下参数",
193
+ cronParams: { action: "list" },
194
+ });
195
+ }
196
+ // ===== remove =====
197
+ if (p.action === "remove") {
198
+ if (!p.jobId) {
199
+ return json({ error: "action=remove 时 jobId 为必填参数。请先用 action=list 获取 jobId。" });
200
+ }
201
+ return json({
202
+ _instruction: "请立即使用 cron 工具执行以下参数",
203
+ cronParams: { action: "remove", jobId: p.jobId },
204
+ });
205
+ }
206
+ // ===== add =====
207
+ if (!p.content) {
208
+ return json({ error: "action=add 时 content(提醒内容)为必填参数" });
209
+ }
210
+ if (!p.to) {
211
+ return json({ error: "action=add 时 to(目标地址)为必填参数,取自上下文 [QQBot] to= 的值" });
212
+ }
213
+ if (!p.time) {
214
+ return json({ error: "action=add 时 time(时间)为必填参数。示例:\"5m\"、\"1h30m\"、\"0 8 * * *\"" });
215
+ }
216
+ // 判断是 cron 表达式还是相对时间
217
+ if (isCronExpression(p.time)) {
218
+ // 周期提醒
219
+ const cronJob = buildCronJob(p);
220
+ return json({
221
+ _instruction: "请立即使用 cron 工具执行以下参数,然后回复用户提醒已设置",
222
+ cronParams: cronJob,
223
+ summary: `⏰ 周期提醒: "${p.content}" (${p.time}, tz=${p.timezone || "Asia/Shanghai"})`,
224
+ });
225
+ }
226
+ // 一次性提醒
227
+ const delayMs = parseRelativeTime(p.time);
228
+ if (!delayMs || delayMs <= 0) {
229
+ return json({
230
+ error: `无法解析时间 "${p.time}"。支持格式:` +
231
+ `相对时间如 "5m"、"1h"、"1h30m"、"2d";` +
232
+ `cron 表达式如 "0 8 * * *"(每天8点)`,
233
+ });
234
+ }
235
+ if (delayMs < 30_000) {
236
+ return json({ error: "提醒时间不能少于 30 秒" });
237
+ }
238
+ const onceJob = buildOnceJob(p, delayMs);
239
+ return json({
240
+ _instruction: "请立即使用 cron 工具执行以下参数,然后回复用户提醒已设置",
241
+ cronParams: onceJob,
242
+ summary: `⏰ ${formatDelay(delayMs)}后提醒: "${p.content}"`,
243
+ });
244
+ },
245
+ }, { name: "qqbot_remind" });
246
+ console.log("[qqbot-remind] Registered QQBot remind tool");
247
+ }
@@ -0,0 +1,175 @@
1
+ /**
2
+ * QQ Bot 配置类型
3
+ */
4
+ export interface QQBotConfig {
5
+ appId: string;
6
+ clientSecret?: string;
7
+ clientSecretFile?: string;
8
+ }
9
+ /**
10
+ * 解析后的 QQ Bot 账户
11
+ */
12
+ export interface ResolvedQQBotAccount {
13
+ accountId: string;
14
+ name?: string;
15
+ enabled: boolean;
16
+ appId: string;
17
+ clientSecret: string;
18
+ secretSource: "config" | "file" | "env" | "none";
19
+ /** 系统提示词 */
20
+ systemPrompt?: string;
21
+ /** 图床服务器公网地址 */
22
+ imageServerBaseUrl?: string;
23
+ /** 是否支持 markdown 消息(默认 true) */
24
+ markdownSupport: boolean;
25
+ config: QQBotAccountConfig;
26
+ }
27
+ /**
28
+ * QQ Bot 账户配置
29
+ */
30
+ export interface QQBotAccountConfig {
31
+ enabled?: boolean;
32
+ name?: string;
33
+ appId?: string;
34
+ clientSecret?: string;
35
+ clientSecretFile?: string;
36
+ dmPolicy?: "open" | "pairing" | "allowlist";
37
+ allowFrom?: string[];
38
+ /** 系统提示词,会添加在用户消息前面 */
39
+ systemPrompt?: string;
40
+ /** 图床服务器公网地址,用于发送图片,例如 http://your-ip:18765 */
41
+ imageServerBaseUrl?: string;
42
+ /** 是否支持 markdown 消息(默认 true,设为 false 可禁用) */
43
+ markdownSupport?: boolean;
44
+ /**
45
+ * @deprecated 请使用 audioFormatPolicy.uploadDirectFormats
46
+ * 可直接上传的音频格式(不转换为 SILK),向后兼容
47
+ */
48
+ voiceDirectUploadFormats?: string[];
49
+ /**
50
+ * 音频格式策略配置
51
+ * 统一管理入站(STT)和出站(上传)的音频格式转换行为
52
+ */
53
+ audioFormatPolicy?: AudioFormatPolicy;
54
+ /**
55
+ * 是否启用公网 URL 直传 QQ 平台(默认 true)
56
+ * 启用时:公网 URL 先直传给 QQ 开放平台的富媒体 API,平台自行拉取;失败后自动 fallback 到插件下载再 Base64 上传
57
+ * 禁用时:公网 URL 始终由插件先下载到本地,再以 Base64 上传(适用于 QQ 平台无法访问目标 URL 的场景)
58
+ */
59
+ urlDirectUpload?: boolean;
60
+ /**
61
+ * /bot-upgrade 指令返回的升级指引网址
62
+ * 默认: https://doc.weixin.qq.com/doc/w3_AKEAGQaeACgCNHrh1CbHzTAKtT2gB?scode=AJEAIQdfAAozxFEnLZAKEAGQaeACg
63
+ */
64
+ upgradeUrl?: string;
65
+ /**
66
+ * /bot-upgrade 指令的行为模式
67
+ * - "doc":展示升级文档链接(默认,安全模式)
68
+ * - "hot-reload":检测到新版本时直接执行 npm 升级脚本进行热更新
69
+ */
70
+ upgradeMode?: "doc" | "hot-reload";
71
+ }
72
+ /**
73
+ * 音频格式策略:控制哪些格式可跳过转换
74
+ */
75
+ export interface AudioFormatPolicy {
76
+ /**
77
+ * STT 模型直接支持的音频格式(入站:跳过 SILK→WAV 转换)
78
+ * 如果 STT 服务支持直接处理某些格式(如 silk/amr),可将其加入此列表
79
+ * 例如: [".silk", ".amr", ".wav", ".mp3", ".ogg"]
80
+ * 默认为空(所有语音都先转换为 WAV 再送 STT)
81
+ */
82
+ sttDirectFormats?: string[];
83
+ /**
84
+ * QQ 平台支持直传的音频格式(出站:跳过→SILK 转换)
85
+ * 默认为 [".wav", ".mp3", ".silk"](QQ Bot API 原生支持的三种格式)
86
+ * 仅当需要覆盖默认值时才配置此项
87
+ */
88
+ uploadDirectFormats?: string[];
89
+ /**
90
+ * 是否启用语音转码(默认 true)
91
+ * 设为 false 可在环境无 ffmpeg 时跳过转码,直接以文件形式发送
92
+ * 当禁用时,非原生格式的音频会 fallback 到 sendDocument(文件发送)
93
+ */
94
+ transcodeEnabled?: boolean;
95
+ }
96
+ /**
97
+ * 富媒体附件
98
+ */
99
+ export interface MessageAttachment {
100
+ content_type: string;
101
+ filename?: string;
102
+ height?: number;
103
+ width?: number;
104
+ size?: number;
105
+ url: string;
106
+ voice_wav_url?: string;
107
+ asr_refer_text?: string;
108
+ }
109
+ /**
110
+ * C2C 消息事件
111
+ */
112
+ export interface C2CMessageEvent {
113
+ author: {
114
+ id: string;
115
+ union_openid: string;
116
+ user_openid: string;
117
+ };
118
+ content: string;
119
+ id: string;
120
+ timestamp: string;
121
+ message_scene?: {
122
+ source: string;
123
+ /** ext 数组,可能包含 ref_msg_idx=REFIDX_xxx(引用的消息)和 msg_idx=REFIDX_xxx(自身索引) */
124
+ ext?: string[];
125
+ };
126
+ attachments?: MessageAttachment[];
127
+ }
128
+ /**
129
+ * 频道 AT 消息事件
130
+ */
131
+ export interface GuildMessageEvent {
132
+ id: string;
133
+ channel_id: string;
134
+ guild_id: string;
135
+ content: string;
136
+ timestamp: string;
137
+ author: {
138
+ id: string;
139
+ username?: string;
140
+ bot?: boolean;
141
+ };
142
+ member?: {
143
+ nick?: string;
144
+ joined_at?: string;
145
+ };
146
+ attachments?: MessageAttachment[];
147
+ }
148
+ /**
149
+ * 群聊 AT 消息事件
150
+ */
151
+ export interface GroupMessageEvent {
152
+ author: {
153
+ id: string;
154
+ member_openid: string;
155
+ };
156
+ content: string;
157
+ id: string;
158
+ timestamp: string;
159
+ group_id: string;
160
+ group_openid: string;
161
+ message_scene?: {
162
+ source: string;
163
+ ext?: string[];
164
+ };
165
+ attachments?: MessageAttachment[];
166
+ }
167
+ /**
168
+ * WebSocket 事件负载
169
+ */
170
+ export interface WSPayload {
171
+ op: number;
172
+ d?: unknown;
173
+ s?: number;
174
+ t?: string;
175
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,27 @@
1
+ /**
2
+ * 输入状态自动续期
3
+ * 在消息处理期间定时续发 "正在输入" 状态通知,确保用户持续看到 bot 在处理中。
4
+ * 仅 C2C 私聊有效(QQ 群聊 API 不支持输入状态通知)。
5
+ */
6
+ export declare const TYPING_INTERVAL_MS = 50000;
7
+ export declare const TYPING_INPUT_SECOND = 60;
8
+ export declare class TypingKeepAlive {
9
+ private readonly getToken;
10
+ private readonly clearCache;
11
+ private readonly openid;
12
+ private readonly msgId;
13
+ private readonly log?;
14
+ private readonly logPrefix;
15
+ private timer;
16
+ private stopped;
17
+ constructor(getToken: () => Promise<string>, clearCache: () => void, openid: string, msgId: string | undefined, log?: {
18
+ info: (msg: string) => void;
19
+ error: (msg: string) => void;
20
+ debug?: (msg: string) => void;
21
+ } | undefined, logPrefix?: string);
22
+ /** 启动定时续期(首次发送由调用方自行处理,这里只负责后续续期) */
23
+ start(): void;
24
+ /** 停止续期 */
25
+ stop(): void;
26
+ private send;
27
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * 输入状态自动续期
3
+ * 在消息处理期间定时续发 "正在输入" 状态通知,确保用户持续看到 bot 在处理中。
4
+ * 仅 C2C 私聊有效(QQ 群聊 API 不支持输入状态通知)。
5
+ */
6
+ import { sendC2CInputNotify } from "./api.js";
7
+ // 每 50 秒续发一次(QQ API input_second=60,留 10s 余量)
8
+ export const TYPING_INTERVAL_MS = 50_000;
9
+ export const TYPING_INPUT_SECOND = 60;
10
+ export class TypingKeepAlive {
11
+ getToken;
12
+ clearCache;
13
+ openid;
14
+ msgId;
15
+ log;
16
+ logPrefix;
17
+ timer = null;
18
+ stopped = false;
19
+ constructor(getToken, clearCache, openid, msgId, log, logPrefix = "[qqbot]") {
20
+ this.getToken = getToken;
21
+ this.clearCache = clearCache;
22
+ this.openid = openid;
23
+ this.msgId = msgId;
24
+ this.log = log;
25
+ this.logPrefix = logPrefix;
26
+ }
27
+ /** 启动定时续期(首次发送由调用方自行处理,这里只负责后续续期) */
28
+ start() {
29
+ if (this.stopped)
30
+ return;
31
+ this.timer = setInterval(() => {
32
+ if (this.stopped) {
33
+ this.stop();
34
+ return;
35
+ }
36
+ this.send().catch(() => { });
37
+ }, TYPING_INTERVAL_MS);
38
+ }
39
+ /** 停止续期 */
40
+ stop() {
41
+ this.stopped = true;
42
+ if (this.timer) {
43
+ clearInterval(this.timer);
44
+ this.timer = null;
45
+ }
46
+ }
47
+ async send() {
48
+ try {
49
+ const token = await this.getToken();
50
+ await sendC2CInputNotify(token, this.openid, this.msgId, TYPING_INPUT_SECOND);
51
+ this.log?.debug?.(`${this.logPrefix} Typing keep-alive sent to ${this.openid}`);
52
+ }
53
+ catch (err) {
54
+ try {
55
+ this.clearCache();
56
+ const token = await this.getToken();
57
+ await sendC2CInputNotify(token, this.openid, this.msgId, TYPING_INPUT_SECOND);
58
+ }
59
+ catch {
60
+ this.log?.debug?.(`${this.logPrefix} Typing keep-alive failed for ${this.openid}: ${err}`);
61
+ }
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * 版本检查器
3
+ *
4
+ * - triggerUpdateCheck(): gateway 启动时调用,后台预热缓存
5
+ * - getUpdateInfo(): 每次实时查询 npm registry,返回最新结果
6
+ *
7
+ * 使用 HTTPS 直接请求 npm registry API(不依赖 npm CLI),
8
+ * 支持多 registry fallback:npmjs.org → npmmirror.com,解决国内网络问题。
9
+ */
10
+ export interface UpdateInfo {
11
+ current: string;
12
+ /** 最佳升级目标(prerelease 用户优先 alpha,稳定版用户取 latest) */
13
+ latest: string | null;
14
+ /** 稳定版 dist-tag */
15
+ stable: string | null;
16
+ /** alpha dist-tag */
17
+ alpha: string | null;
18
+ hasUpdate: boolean;
19
+ checkedAt: number;
20
+ error?: string;
21
+ }
22
+ /** gateway 启动时调用,保存 log 引用 */
23
+ export declare function triggerUpdateCheck(log?: {
24
+ info: (msg: string) => void;
25
+ error: (msg: string) => void;
26
+ debug?: (msg: string) => void;
27
+ }): void;
28
+ /** 每次实时查询 npm registry */
29
+ export declare function getUpdateInfo(): Promise<UpdateInfo>;
30
+ /**
31
+ * 检查指定版本是否存在于 npm registry
32
+ * 用于 /bot-upgrade --version 的前置校验
33
+ */
34
+ export declare function checkVersionExists(version: string): Promise<boolean>;