@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.
Files changed (120) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +967 -0
  3. package/README.zh.md +790 -0
  4. package/dist/index.cjs +18072 -0
  5. package/dist/index.cjs.map +1 -0
  6. package/dist/index.d.cts +1222 -0
  7. package/index.ts +92 -0
  8. package/openclaw.plugin.json +38 -0
  9. package/package.json +69 -0
  10. package/preload.cjs +19 -0
  11. package/scripts/link-sdk-core.cjs +268 -0
  12. package/scripts/proactive-api-server.ts +369 -0
  13. package/scripts/send-proactive.ts +293 -0
  14. package/scripts/test-sendmedia.ts +116 -0
  15. package/skills/qqbot-channel/SKILL.md +285 -0
  16. package/skills/qqbot-channel/references/api_references.md +521 -0
  17. package/skills/qqbot-remind/SKILL.md +159 -0
  18. package/skills/qqbot-upgrade/SKILL.md +56 -0
  19. package/src/adapter/contract.ts +63 -0
  20. package/src/adapter/lint.ts +144 -0
  21. package/src/adapter/media.ts +40 -0
  22. package/src/adapter/pairing.ts +95 -0
  23. package/src/adapter/resolve.ts +255 -0
  24. package/src/adapter/setup.ts +13 -0
  25. package/src/adapter/webhook.ts +248 -0
  26. package/src/adapter/workspace.ts +21 -0
  27. package/src/agent-prompt-adapter.ts +26 -0
  28. package/src/bot-instance.ts +60 -0
  29. package/src/channel.ts +230 -0
  30. package/src/commands/bot-approve.ts +143 -0
  31. package/src/commands/bot-clear-storage.ts +114 -0
  32. package/src/commands/bot-group-always.ts +62 -0
  33. package/src/commands/bot-group-info.ts +48 -0
  34. package/src/commands/bot-help.ts +40 -0
  35. package/src/commands/bot-logs.ts +248 -0
  36. package/src/commands/bot-me.ts +18 -0
  37. package/src/commands/bot-pairing.ts +50 -0
  38. package/src/commands/bot-ping.ts +33 -0
  39. package/src/commands/bot-streaming.ts +55 -0
  40. package/src/commands/bot-upgrade.ts +56 -0
  41. package/src/commands/bot-version.ts +41 -0
  42. package/src/commands/config-util.ts +96 -0
  43. package/src/commands/index.ts +51 -0
  44. package/src/config.ts +403 -0
  45. package/src/constants.ts +6 -0
  46. package/src/dispatch/body-assembler.ts +308 -0
  47. package/src/dispatch/ctx-builder.ts +127 -0
  48. package/src/dispatch/dispatch.ts +667 -0
  49. package/src/dispatch/envelope-builder.ts +112 -0
  50. package/src/dispatch/index.ts +2 -0
  51. package/src/features/approval-capability.ts +302 -0
  52. package/src/features/approval-helpers.ts +271 -0
  53. package/src/features/approval-utils.ts +21 -0
  54. package/src/features/command-panel.ts +301 -0
  55. package/src/features/credential-backup.ts +74 -0
  56. package/src/features/group-mode-store.ts +79 -0
  57. package/src/features/history-store.ts +75 -0
  58. package/src/features/msgid-cache.ts +55 -0
  59. package/src/features/outbound-echo-store.ts +46 -0
  60. package/src/features/proactive-budget.ts +57 -0
  61. package/src/features/proactive.ts +549 -0
  62. package/src/features/question-helpers.ts +771 -0
  63. package/src/features/quota-manager.ts +173 -0
  64. package/src/features/ref-index-store.ts +289 -0
  65. package/src/features/secret-input-store.ts +118 -0
  66. package/src/features/secret-store-cli.ts +324 -0
  67. package/src/features/typing-refresh.ts +51 -0
  68. package/src/features/update-checker.ts +166 -0
  69. package/src/gateway/event-handlers.ts +456 -0
  70. package/src/gateway/index.ts +3 -0
  71. package/src/gateway/lifecycle.ts +236 -0
  72. package/src/gateway/middleware-setup.ts +173 -0
  73. package/src/gateway/qqbot-gateway.ts +458 -0
  74. package/src/gateway-adapter.ts +44 -0
  75. package/src/heartbeat-adapter.ts +57 -0
  76. package/src/message-adapter.ts +40 -0
  77. package/src/messaging-adapter.ts +78 -0
  78. package/src/middleware/access-control.ts +125 -0
  79. package/src/middleware/attachment.ts +373 -0
  80. package/src/middleware/inbound-guard.ts +102 -0
  81. package/src/middleware/policy-injector.ts +71 -0
  82. package/src/middleware/secret-capture.ts +161 -0
  83. package/src/middleware/typing.ts +110 -0
  84. package/src/openclaw-plugin-sdk.d.ts +543 -0
  85. package/src/outbound/chunker.ts +80 -0
  86. package/src/outbound/debounce.ts +102 -0
  87. package/src/outbound/deliver-pipeline.ts +235 -0
  88. package/src/outbound/index.ts +3 -0
  89. package/src/outbound/local-file-router.ts +145 -0
  90. package/src/outbound/media-send.ts +408 -0
  91. package/src/outbound/outbound-service.ts +298 -0
  92. package/src/outbound/reply-limiter.ts +139 -0
  93. package/src/outbound/sanitize.ts +32 -0
  94. package/src/outbound/streaming-controller.ts +332 -0
  95. package/src/outbound/target.ts +109 -0
  96. package/src/outbound-adapter.ts +323 -0
  97. package/src/plugin-base.ts +42 -0
  98. package/src/request-context.ts +50 -0
  99. package/src/runtime.ts +42 -0
  100. package/src/setup/account-key.ts +41 -0
  101. package/src/setup/finalize.ts +110 -0
  102. package/src/setup/login.ts +197 -0
  103. package/src/setup/surface.ts +40 -0
  104. package/src/status-adapter.ts +56 -0
  105. package/src/tools/platform.ts +149 -0
  106. package/src/tools/remind.ts +308 -0
  107. package/src/tools/secret-input.ts +185 -0
  108. package/src/types-augment.d.ts +54 -0
  109. package/src/types-plugin.ts +82 -0
  110. package/src/types.ts +620 -0
  111. package/src/typing-lifecycle.ts +182 -0
  112. package/src/utils/mention.ts +52 -0
  113. package/src/utils/pkg-version.ts +23 -0
  114. package/src/utils/platform.ts +459 -0
  115. package/src/utils/plugin-logger.ts +104 -0
  116. package/src/utils/ssrf-guard.ts +132 -0
  117. package/src/utils/stt.ts +150 -0
  118. package/src/utils/voice-text.ts +61 -0
  119. package/tsconfig.json +17 -0
  120. package/tsup.config.ts +64 -0
package/src/types.ts ADDED
@@ -0,0 +1,620 @@
1
+ // ── QQ 消息类型常量(message_type 枚举值) ──
2
+ /** 普通文本消息 */
3
+ export const MSG_TYPE_TEXT = 0;
4
+ /** 引用(回复)消息 */
5
+ export const MSG_TYPE_QUOTE = 103;
6
+
7
+ /**
8
+ * QQ Bot 配置类型
9
+ */
10
+ export interface QQBotConfig {
11
+ appId: string;
12
+ clientSecret?: string;
13
+ clientSecretFile?: string;
14
+ }
15
+
16
+ /**
17
+ * 解析后的 QQ Bot 账户
18
+ */
19
+ export interface ResolvedQQBotAccount {
20
+ accountId: string;
21
+ name?: string;
22
+ enabled: boolean;
23
+ appId: string;
24
+ clientSecret: string;
25
+ secretSource: "config" | "file" | "env" | "none";
26
+ /** 系统提示词 */
27
+ systemPrompt?: string;
28
+ /** 是否支持 markdown 消息(默认 true) */
29
+ markdownSupport: boolean;
30
+ /** 是否自动同步 openclaw essential 原生指令到 QQ Bot 指令面板(默认 true) */
31
+ commandPanelNative: boolean;
32
+ /** User-Agent 尾部追加内容 */
33
+ userAgentSuffix: string;
34
+ config: QQBotAccountConfig;
35
+ }
36
+
37
+ /** 群消息策略:open=全响应 | allowlist=白名单 | disabled=不响应 */
38
+ export type GroupPolicy = "open" | "allowlist" | "disabled";
39
+
40
+ /** 工具策略:full=全部 | restricted=限制敏感工具 | none=禁止 */
41
+ export type ToolPolicy = "full" | "restricted" | "none";
42
+
43
+ /** 群消息合并配置 */
44
+ export interface GroupCoalesceConfig {
45
+ /**
46
+ * 是否启用消息合并(默认 true):
47
+ * - true:框架 collect 合并批处理
48
+ * - false:followup 排队不合并
49
+ * 排队/合并完全由 OpenClaw 框架的 followup 队列承担。
50
+ */
51
+ enabled?: boolean;
52
+ }
53
+
54
+ /** 指令面板配置 */
55
+ export interface CommandsConfig {
56
+ /**
57
+ * 是否把 openclaw essential 原生指令自动注册到 QQ Bot 指令面板(默认 true)。
58
+ * 面板项点击后指令文本填入输入框,以普通文本进入框架,走文本斜杠指令路由执行。
59
+ */
60
+ native?: boolean;
61
+ }
62
+
63
+ /** 单个群的配置 */
64
+ export interface GroupConfig {
65
+ /** 是否需要 @机器人才响应(默认 true) */
66
+ requireMention?: boolean;
67
+ /**
68
+ * 是否忽略 @了其他用户但没有 @机器人的消息(默认 false)。
69
+ * 开启后,消息中 @了其他人但未 @bot 时直接丢弃(不记录历史、不触发 AI)。
70
+ */
71
+ ignoreOtherMentions?: boolean;
72
+ /** 群聊中 AI 可使用的工具范围(默认 restricted) */
73
+ toolPolicy?: ToolPolicy;
74
+ /** 群名称 */
75
+ name?: string;
76
+ /** 群消息行为 PE(未配置时使用内置默认值) */
77
+ prompt?: string;
78
+ /** 群历史消息缓存条数(0 禁用,默认 20) */
79
+ historyLimit?: number;
80
+ /**
81
+ * 群历史清理时机(默认 clear):
82
+ * - clear:每次回复后整清(旧语义,"自上次回复以来的窗口")
83
+ * - rolling:回复后裁剪到最后一条 bot 出站之后(bot 发言也计入历史,
84
+ * 对齐 telegram selectAfterLastSelf 的滚动窗口语义)
85
+ */
86
+ historyMode?: 'clear' | 'rolling';
87
+ /**
88
+ * 未被 @ 的群消息入站策略(默认 user_request,仅全量模式群有实际意义):
89
+ * - user_request:维持现状——mentionGate 拦截未 @ 消息(只进历史,不上报)
90
+ * - room_event:门控放行全部消息,未被 @/称呼/引用的作为 room_event 被动
91
+ * 房间事件进框架(AI 只读上下文,不自动回复,想发言走主动 message 工具;
92
+ * 框架自动压制 typing/流式/steer)。成本提示:每条消息一次推理 pass。
93
+ */
94
+ unmentionedInbound?: 'user_request' | 'room_event';
95
+ /** 群消息合并配置(覆盖账号级配置) */
96
+ coalesce?: GroupCoalesceConfig;
97
+ }
98
+
99
+ /** 限流单层配置(滑动窗口) */
100
+ export interface RateLimitTierConfig {
101
+ /** 窗口内最大消息数 */
102
+ max: number;
103
+ /** 窗口时长(毫秒) */
104
+ windowMs: number;
105
+ }
106
+
107
+ /** 三层限流配置(sender / group / global) */
108
+ export interface RateLimitConfig {
109
+ /** 是否启用(默认 true;保守默认阈值,正常使用不会触发) */
110
+ enabled?: boolean;
111
+ /** 单发送者限流(默认 20 条/分钟) */
112
+ perSender?: RateLimitTierConfig;
113
+ /** 单群限流(默认 60 条/分钟,c2c 按 sender 归组) */
114
+ perGroup?: RateLimitTierConfig;
115
+ /** 全局限流(默认 300 条/分钟) */
116
+ global?: RateLimitTierConfig;
117
+ }
118
+
119
+ /** 消息接收传输方式 */
120
+ export type TransportMode = "websocket" | "webhook";
121
+
122
+ /** Webhook 传输配置 */
123
+ export interface WebhookTransportConfig {
124
+ /** 监听路径(默认 /qqbot/webhook) */
125
+ path?: string;
126
+ }
127
+
128
+ /**
129
+ * QQ Bot 账户配置
130
+ */
131
+ export interface QQBotAccountConfig {
132
+ enabled?: boolean;
133
+ name?: string;
134
+ appId?: string;
135
+ clientSecret?: string;
136
+ clientSecretFile?: string;
137
+ dmPolicy?: "open" | "pairing" | "allowlist" | "disabled";
138
+ allowFrom?: string[];
139
+ /** 消息接收传输方式:websocket(默认)| webhook */
140
+ transport?: TransportMode;
141
+ /** webhook 传输配置(transport="webhook" 时生效) */
142
+ webhook?: WebhookTransportConfig;
143
+ /** 群消息策略(默认 allowlist) */
144
+ groupPolicy?: GroupPolicy;
145
+ /** 群白名单(groupPolicy 为 allowlist 时生效) */
146
+ groupAllowFrom?: string[];
147
+ /** 群配置映射(按 groupOpenid 索引,"*" 为默认) */
148
+ groups?: Record<string, GroupConfig>;
149
+ /** 三层限流(sender/group/global,默认启用保守阈值) */
150
+ rateLimit?: RateLimitConfig;
151
+ /** 系统提示词,会添加在用户消息前面 */
152
+ systemPrompt?: string;
153
+ /** 是否支持 markdown 消息(默认 true,设为 false 可禁用) */
154
+ markdownSupport?: boolean;
155
+ /**
156
+ * 音频格式策略配置
157
+ * 统一管理入站(STT)和出站(上传)的音频格式转换行为
158
+ */
159
+ audioFormatPolicy?: AudioFormatPolicy;
160
+ /**
161
+ * 是否启用公网 URL 直传 QQ 平台(默认 true)
162
+ * 启用时:公网 URL 先直传给 QQ 开放平台的富媒体 API,平台自行拉取;失败后自动 fallback 到插件下载再 Base64 上传
163
+ * 禁用时:公网 URL 始终由插件先下载到本地,再以 Base64 上传(适用于 QQ 平台无法访问目标 URL 的场景)
164
+ */
165
+ urlDirectUpload?: boolean;
166
+ /**
167
+ * /bot-upgrade 指令返回的升级指引网址
168
+ * 默认: 本仓库 CHANGELOG(含各版本升级说明)
169
+ */
170
+ upgradeUrl?: string;
171
+ /**
172
+ * 群消息是否默认需要 @机器人才响应(默认 true)
173
+ * 优先级低于 groups.{groupId}.requireMention 和 groups."*".requireMention
174
+ * 设为 false 时,所有群默认无需 @ 即触发回复(仍可被群级配置覆盖)
175
+ */
176
+ defaultRequireMention?: boolean;
177
+ /**
178
+ * 群消息合并配置(账号级)
179
+ * 启用后,群聊中快速发送的多条消息会被合并处理,而不是取消之前的任务
180
+ * 与私聊的"插嘴"行为相反,群聊中所有消息都应该被处理
181
+ */
182
+ groupCoalesce?: GroupCoalesceConfig;
183
+ /**
184
+ * 出站消息合并回复(debounce)配置
185
+ * 当短时间内收到多次 deliver 时,将文本合并为一条消息发送,避免消息轰炸
186
+ */
187
+ deliverDebounce?: DeliverDebounceConfig;
188
+ /**
189
+ * "正在输入"指示器配置(仅 C2C 私聊生效)
190
+ */
191
+ typing?: TypingIndicatorConfig;
192
+ /**
193
+ * 指令面板配置(openclaw 原生指令 → QQ Bot 指令面板自动注册)
194
+ */
195
+ commands?: CommandsConfig;
196
+ /**
197
+ * 是否启用流式消息(默认 false)
198
+ * 启用后,AI 的回复会以流式形式逐步显示在 QQ 聊天中,
199
+ * 用户可以看到文字逐字出现的打字机效果。
200
+ *
201
+ * 兼容布尔值和对象格式,对齐框架 schema:
202
+ * - true / false 旧版布尔格式(自动转换为对象)
203
+ * - { mode: "partial" } 开启(对齐 StreamingMode.partial)
204
+ * - { mode: "off" } 关闭
205
+ *
206
+ * 注意:仅 C2C(私聊)支持流式消息 API。
207
+ *
208
+ * sendMode 控制文本下发通道(仅 mode="partial" 时生效):
209
+ * - "stream" QQ 流式打印机(默认):同一条消息内容不断变长,打字机效果
210
+ * - "static" 流结束时用一条普通 sendText 发送完整文本:无打字机,
211
+ * partial 接收逻辑(状态机/串行/去重)保持不变,仅替换下发通道。
212
+ * 适合不想要打字机效果、只想收到一条完整回复的场景。
213
+ */
214
+ streaming?: {
215
+ mode: 'partial' | 'off';
216
+ sendMode?: 'stream' | 'static';
217
+ };
218
+ /**
219
+ * STT (语音转文字) 配置
220
+ * 配置后,收到语音消息时会自动调用 STT 服务转录为文字
221
+ */
222
+ stt?: STTChannelConfig;
223
+ /**
224
+ * User-Agent 尾部追加内容(用于私有化部署标识等场景)
225
+ * 追加在 `QQBotPlugin/{version} (Node/{nodeVersion}; {os}; OpenClaw/{version})` 之后
226
+ */
227
+ userAgentSuffix?: string;
228
+ }
229
+
230
+ /**
231
+ * 出站消息合并回复配置
232
+ */
233
+ export interface DeliverDebounceConfig {
234
+ /**
235
+ * 是否启用合并回复(默认 true)
236
+ */
237
+ enabled?: boolean;
238
+ /**
239
+ * 合并窗口时长(毫秒),在此时间内的连续 deliver 会被合并
240
+ * 默认 1500ms
241
+ */
242
+ windowMs?: number;
243
+ /**
244
+ * 最大等待时长(毫秒),从第一条 deliver 开始计算,超过此时间强制发送
245
+ * 防止持续有新 deliver 导致一直不发送
246
+ * 默认 8000ms
247
+ */
248
+ maxWaitMs?: number;
249
+ /**
250
+ * 合并文本之间的分隔符
251
+ * 默认 "\n\n---\n\n"
252
+ */
253
+ separator?: string;
254
+ }
255
+
256
+ /**
257
+ * "正在输入"指示器配置
258
+ *
259
+ * QQ 客户端行为:退出聊天界面再进入后,指示器会消失,只有收到新的
260
+ * input_notify 推送才会重新显示,因此处理期间需要周期性续期。
261
+ *
262
+ * 配额说明:typing 通知与回复消息共享同一 msg_id 的被动回复配额
263
+ * (QQ 开放平台同一条消息被动回复上限约 5 条)。被动配额耗尽后,
264
+ * typing 与回复消息一样自动降级为主动发送(不带 msg_id),续期不中断。
265
+ *
266
+ * 中间消息:机器人发出消息(如思维链中间输出)后,QQ 客户端会终止
267
+ * 指示器显示。若框架任务仍在进行,插件会在消息发出 5 秒后补发一次
268
+ * 续期;若是最终回复(任务完成),则不再补发。
269
+ */
270
+ export interface TypingIndicatorConfig {
271
+ /**
272
+ * 是否启用指示器(默认 true)
273
+ */
274
+ enabled?: boolean;
275
+ /**
276
+ * 续期间隔(毫秒),默认 20000
277
+ * 受 QPS 限制,低于 20000 会被钳制到 20000
278
+ */
279
+ intervalMs?: number;
280
+ }
281
+
282
+ /**
283
+ * 音频格式策略:控制哪些格式可跳过转换
284
+ */
285
+ export interface AudioFormatPolicy {
286
+ /**
287
+ * STT 模型直接支持的音频格式(入站:跳过 SILK→WAV 转换)
288
+ * 如果 STT 服务支持直接处理某些格式(如 silk/amr),可将其加入此列表
289
+ * 例如: [".silk", ".amr", ".wav", ".mp3", ".ogg"]
290
+ * 默认为空(所有语音都先转换为 WAV 再送 STT)
291
+ */
292
+ sttDirectFormats?: string[];
293
+ /**
294
+ * QQ 平台支持直传的音频格式(出站:跳过→SILK 转换)
295
+ * 默认为 [".wav", ".mp3", ".silk"](QQ Bot API 原生支持的三种格式)
296
+ * 仅当需要覆盖默认值时才配置此项
297
+ */
298
+ uploadDirectFormats?: string[];
299
+ /**
300
+ * 是否启用语音转码(默认 true)
301
+ * 设为 false 可在环境无 ffmpeg 时跳过转码,直接以文件形式发送
302
+ * 当禁用时,非原生格式的音频会 fallback 到 sendDocument(文件发送)
303
+ */
304
+ transcodeEnabled?: boolean;
305
+ }
306
+
307
+ /**
308
+ * STT (语音转文字) 配置
309
+ */
310
+ export interface STTChannelConfig {
311
+ /** 是否启用 STT(默认 true,配置了 baseUrl+apiKey 即自动启用) */
312
+ enabled?: boolean;
313
+ /** STT 服务提供商 ID(对应 models.providers 中的 key,默认 "openai") */
314
+ provider?: string;
315
+ /** STT API 地址(如 https://api.openai.com/v1) */
316
+ baseUrl?: string;
317
+ /** STT API 密钥 */
318
+ apiKey?: string;
319
+ /** STT 模型名称(默认 "whisper-1") */
320
+ model?: string;
321
+ }
322
+
323
+ /**
324
+ * 富媒体附件
325
+ */
326
+ export interface MessageAttachment {
327
+ content_type: string; // 如 "image/png"
328
+ filename?: string;
329
+ height?: number;
330
+ width?: number;
331
+ size?: number;
332
+ url: string;
333
+ voice_wav_url?: string; // QQ 提供的 WAV 格式语音直链,有值时优先使用以避免 SILK→WAV 转换
334
+ asr_refer_text?: string; // QQ 事件内置 ASR 语音识别文本
335
+ }
336
+
337
+ /**
338
+ * C2C 消息事件
339
+ */
340
+ export interface C2CMessageEvent {
341
+ author: {
342
+ id: string;
343
+ union_openid: string;
344
+ user_openid: string;
345
+ };
346
+ content: string;
347
+ id: string;
348
+ timestamp: string;
349
+ message_scene?: {
350
+ source: string;
351
+ /** ext 数组,可能包含 ref_msg_idx=REFIDX_xxx(引用的消息)和 msg_idx=REFIDX_xxx(自身索引) */
352
+ ext?: string[];
353
+ };
354
+ attachments?: MessageAttachment[];
355
+ /** 消息类型,参见 MSG_TYPE_* */
356
+ message_type?: number;
357
+ /** 消息元素列表,引用消息时 [0] 为被引用的原始消息 */
358
+ msg_elements?: MsgElement[];
359
+ }
360
+
361
+ /**
362
+ * 频道 AT 消息事件
363
+ */
364
+ export interface GuildMessageEvent {
365
+ id: string;
366
+ channel_id: string;
367
+ guild_id: string;
368
+ content: string;
369
+ timestamp: string;
370
+ author: {
371
+ id: string;
372
+ username?: string;
373
+ bot?: boolean;
374
+ };
375
+ member?: {
376
+ nick?: string;
377
+ joined_at?: string;
378
+ };
379
+ attachments?: MessageAttachment[];
380
+ }
381
+
382
+ /** 消息元素结点,引用消息时 msg_elements[0] 为被引用的原始消息 */
383
+ export interface MsgElement {
384
+ /** 消息索引标识 */
385
+ msg_idx?: string;
386
+ /** 消息类型,参见 MSG_TYPE_* 常量 */
387
+ message_type?: number;
388
+ /** 文本内容 */
389
+ content?: string;
390
+ /** 附件列表 */
391
+ attachments?: MessageAttachment[];
392
+ /** 嵌套消息元素(引用消息场景下可能存在) */
393
+ msg_elements?: MsgElement[];
394
+ }
395
+
396
+ /**
397
+ * 群聊 AT 消息事件
398
+ */
399
+ export interface GroupMessageEvent {
400
+ author: {
401
+ id: string;
402
+ member_openid: string;
403
+ username?: string;
404
+ bot?: boolean;
405
+ };
406
+ content: string;
407
+ id: string;
408
+ timestamp: string;
409
+ group_id: string;
410
+ group_openid: string;
411
+ message_scene?: {
412
+ source: string;
413
+ ext?: string[];
414
+ };
415
+ attachments?: MessageAttachment[];
416
+ /** @提及列表 */
417
+ mentions?: Array<{
418
+ scope?: "all" | "single";
419
+ id?: string;
420
+ user_openid?: string;
421
+ member_openid?: string;
422
+ nickname?: string;
423
+ bot?: boolean;
424
+ /** 是否 @机器人自身 */
425
+ is_you?: boolean;
426
+ }>;
427
+ /** 消息类型,参见 MSG_TYPE_* */
428
+ message_type?: number;
429
+ /** 消息元素列表,引用消息时 [0] 为被引用的原始消息 */
430
+ msg_elements?: MsgElement[];
431
+ }
432
+
433
+ /**
434
+ * 按钮交互事件(INTERACTION_CREATE)
435
+ */
436
+ export interface InteractionEvent {
437
+ /** 事件 ID,用于回应交互(PUT /interactions/{id}) */
438
+ id: string;
439
+ /** 事件类型:11=消息按钮 12=单聊快捷菜单 */
440
+ type: number;
441
+ /** 场景:c2c / group / guild */
442
+ scene?: string;
443
+ /** 场景类型:0=频道 1=群聊 2=单聊 */
444
+ chat_type?: number;
445
+ /** 触发时间 RFC3339 */
446
+ timestamp?: string;
447
+ /** 频道 openid(仅频道场景) */
448
+ guild_id?: string;
449
+ /** 子频道 openid(仅频道场景) */
450
+ channel_id?: string;
451
+ /** 单聊用户 openid(仅 c2c 场景) */
452
+ user_openid?: string;
453
+ /** 群 openid(仅群聊场景) */
454
+ group_openid?: string;
455
+ /** 群内触发用户 openid(仅群聊场景) */
456
+ group_member_openid?: string;
457
+ version: number;
458
+ data: {
459
+ type: number;
460
+ resolved: {
461
+ /** 按钮 action.data 值 */
462
+ button_data?: string;
463
+ /** 按钮 id */
464
+ button_id?: string;
465
+ /** 操作用户 userid(仅频道场景) */
466
+ user_id?: string;
467
+ /** 自定义菜单 id(仅菜单场景) */
468
+ feature_id?: string;
469
+ /** 操作的消息 id(仅频道场景) */
470
+ message_id?: string;
471
+ /** 配置更新:群消息模式 "mention"=@机器人时激活 "always"=总是激活 */
472
+ require_mention?: string;
473
+ /** 配置更新:群消息策略 */
474
+ group_policy?: GroupPolicy;
475
+ /** 配置更新:@文本的名称提及BOT名,多个使用,分隔 */
476
+ mention_patterns?: string;
477
+ };
478
+ };
479
+ }
480
+
481
+ // ---- Keyboard 类型 ----
482
+
483
+ /**
484
+ * 按钮 Action 类型
485
+ * 0=跳转链接 1=回调型(INTERACTION_CREATE) 2=指令型(直接发文本) 3=mqqapi
486
+ */
487
+ export type KeyboardActionType = 0 | 1 | 2 | 3;
488
+
489
+ /** 按钮权限 */
490
+ export interface KeyboardPermission {
491
+ /** 0=全体 1=管理员 2=按钮指定 3=身份组 */
492
+ type: 0 | 1 | 2 | 3;
493
+ specify_role_ids?: string[];
494
+ specify_user_ids?: string[];
495
+ }
496
+
497
+ /** 二次确认弹窗 */
498
+ export interface KeyboardModal {
499
+ content: string;
500
+ confirm_text?: string;
501
+ cancel_text?: string;
502
+ }
503
+
504
+ /** 按钮 Action */
505
+ export interface KeyboardAction {
506
+ type: KeyboardActionType;
507
+ data?: string;
508
+ /** true = 点击后直接发出(Enter)*/
509
+ enter?: boolean;
510
+ /** 仅指令型(type=2):是否把指令发到输入框(reply=true)还是静默发出 */
511
+ reply?: boolean;
512
+ permission?: KeyboardPermission;
513
+ click_limit?: number;
514
+ unsupport_tips?: string;
515
+ modal?: KeyboardModal;
516
+ }
517
+
518
+ /** 按钮渲染数据 */
519
+ export interface KeyboardRenderData {
520
+ label: string;
521
+ visited_label?: string;
522
+ /** 0=灰色线框 1=蓝色线框 2=推荐回复专用 3=红色字体 4=蓝色背景 */
523
+ style?: 0 | 1 | 2 | 3 | 4;
524
+ }
525
+
526
+ /** 单个按钮 */
527
+ export interface KeyboardButton {
528
+ id: string;
529
+ render_data?: KeyboardRenderData;
530
+ action?: KeyboardAction;
531
+ group_id?: string;
532
+ }
533
+
534
+ /** 一行按钮 */
535
+ export interface KeyboardRow {
536
+ buttons: KeyboardButton[];
537
+ }
538
+
539
+ /** CustomKeyboard(自定义按钮内容) */
540
+ export interface CustomKeyboard {
541
+ rows: KeyboardRow[];
542
+ }
543
+
544
+ /** MessageKeyboard(keyboard / prompt_keyboard.keyboard 共用) */
545
+ export interface MessageKeyboard {
546
+ /** 模板 ID(与 content 二选一) */
547
+ id?: string;
548
+ /** 自定义内容 */
549
+ content?: CustomKeyboard;
550
+ }
551
+
552
+ /**
553
+ * Inline Keyboard(消息内嵌按钮,需平台审核)
554
+ * 发送字段:keyboard
555
+ * JSON: { "keyboard": { "id": "...", "content": { "rows": [...] } } }
556
+ */
557
+ export type InlineKeyboard = MessageKeyboard;
558
+
559
+ /**
560
+ * WebSocket 事件负载
561
+ */
562
+ export interface WSPayload {
563
+ op: number;
564
+ d?: unknown;
565
+ s?: number;
566
+ t?: string;
567
+ }
568
+
569
+
570
+
571
+ // ---- 流式消息常量 ----
572
+
573
+ /** 流式消息输入模式 */
574
+ export const StreamInputMode = {
575
+ /** 每次发送的 content_raw 替换整条消息内容 */
576
+ REPLACE: "replace",
577
+ } as const;
578
+ export type StreamInputMode = (typeof StreamInputMode)[keyof typeof StreamInputMode];
579
+
580
+ /** 流式消息输入状态 */
581
+ export const StreamInputState = {
582
+ /** 正文生成中 */
583
+ GENERATING: 1,
584
+ /** 正文生成结束(终结状态) */
585
+ DONE: 10,
586
+ } as const;
587
+ export type StreamInputState = (typeof StreamInputState)[keyof typeof StreamInputState];
588
+
589
+ /** 流式消息内容类型 */
590
+ export const StreamContentType = {
591
+ MARKDOWN: "markdown",
592
+ } as const;
593
+ export type StreamContentType = (typeof StreamContentType)[keyof typeof StreamContentType];
594
+
595
+ /**
596
+ * 流式消息请求体
597
+ * 对应 StreamReq proto
598
+ */
599
+ export interface StreamMessageRequest {
600
+ /** 输入模式 */
601
+ input_mode: StreamInputMode;
602
+ /** 输入状态 */
603
+ input_state: StreamInputState;
604
+ /** 内容类型 */
605
+ content_type: StreamContentType;
606
+ /** markdown 内容 */
607
+ content_raw: string;
608
+ /** 事件 ID */
609
+ event_id: string;
610
+ /** 原始消息 ID */
611
+ msg_id: string;
612
+ /** 流式消息 ID,首次发送后返回,后续分片需携带 */
613
+ stream_msg_id?: string;
614
+ /** 递增序号 */
615
+ msg_seq: number;
616
+ /** 同一条流式会话内的发送索引,从 0 开始,每次发送前递增;新流式会话重新从 0 开始 */
617
+ index: number;
618
+ }
619
+
620
+