@tencent-connect/openclaw-qqbot 1.7.2 → 2.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 (291) hide show
  1. package/README.md +25 -8
  2. package/README.zh.md +24 -8
  3. package/bin/qqbot-cli.js +5 -2
  4. package/dist/index.cjs +15549 -0
  5. package/dist/index.cjs.map +1 -0
  6. package/dist/index.d.cts +890 -0
  7. package/index.ts +56 -19
  8. package/openclaw.plugin.json +19 -3
  9. package/package.json +14 -23
  10. package/preload.cjs +17 -14
  11. package/scripts/link-sdk-core.cjs +133 -50
  12. package/scripts/upgrade-via-npm.sh +42 -11
  13. package/scripts/upgrade-via-source.sh +4 -4
  14. package/skills/qqbot-channel/SKILL.md +34 -12
  15. package/src/adapter/contract.ts +69 -0
  16. package/src/adapter/gateway.ts +61 -0
  17. package/src/adapter/index.ts +15 -0
  18. package/src/adapter/lint.ts +144 -0
  19. package/src/adapter/media.ts +112 -0
  20. package/src/adapter/pairing.ts +99 -0
  21. package/src/adapter/resolve.ts +333 -0
  22. package/src/adapter/setup.ts +55 -0
  23. package/src/adapter/webhook.ts +248 -0
  24. package/src/adapter/workspace.ts +29 -0
  25. package/src/bot-instance.ts +60 -0
  26. package/src/channel.ts +202 -413
  27. package/src/commands/bot-approve.ts +143 -0
  28. package/src/commands/bot-clear-storage.ts +114 -0
  29. package/src/commands/bot-group-always.ts +62 -0
  30. package/src/commands/bot-help.ts +40 -0
  31. package/src/commands/bot-logs.ts +248 -0
  32. package/src/commands/bot-me.ts +18 -0
  33. package/src/commands/bot-pairing.ts +57 -0
  34. package/src/commands/bot-ping.ts +33 -0
  35. package/src/commands/bot-streaming.ts +55 -0
  36. package/src/commands/bot-upgrade.ts +56 -0
  37. package/src/commands/bot-version.ts +41 -0
  38. package/src/commands/config-util.ts +96 -0
  39. package/src/commands/index.ts +49 -0
  40. package/src/config.ts +46 -19
  41. package/src/dispatch/body-assembler.ts +356 -0
  42. package/src/dispatch/ctx-builder.ts +110 -0
  43. package/src/dispatch/dispatch.ts +352 -0
  44. package/src/dispatch/envelope-builder.ts +112 -0
  45. package/src/dispatch/index.ts +2 -0
  46. package/src/{approval-handler.ts → features/approval-handler.ts} +40 -94
  47. package/src/features/approval-utils.ts +41 -0
  48. package/src/features/credential-backup.ts +82 -0
  49. package/src/features/history-store.ts +19 -0
  50. package/src/features/msgid-cache.ts +55 -0
  51. package/src/features/onboarding.ts +38 -0
  52. package/src/{proactive.ts → features/proactive.ts} +68 -49
  53. package/src/features/ref-index-store.ts +282 -0
  54. package/src/{update-checker.ts → features/update-checker.ts} +15 -35
  55. package/src/gateway/event-handlers.ts +241 -0
  56. package/src/gateway/index.ts +3 -0
  57. package/src/gateway/lifecycle.ts +221 -0
  58. package/src/gateway/middleware-setup.ts +139 -0
  59. package/src/gateway/qqbot-gateway.ts +342 -0
  60. package/src/middleware/access-control.ts +130 -0
  61. package/src/middleware/attachment.ts +314 -0
  62. package/src/middleware/policy-injector.ts +66 -0
  63. package/src/openclaw-plugin-sdk.d.ts +81 -10
  64. package/src/outbound/cron-scheduler.ts +130 -0
  65. package/src/outbound/debounce.ts +102 -0
  66. package/src/outbound/deliver-pipeline.ts +235 -0
  67. package/src/outbound/image-size.ts +123 -0
  68. package/src/outbound/index.ts +3 -0
  69. package/src/outbound/local-file-router.ts +145 -0
  70. package/src/outbound/media-send.ts +377 -0
  71. package/src/outbound/outbound-service.ts +222 -0
  72. package/src/outbound/reply-limiter.ts +128 -0
  73. package/src/outbound/sanitize.ts +32 -0
  74. package/src/outbound/streaming-controller.ts +228 -0
  75. package/src/outbound/target.ts +63 -0
  76. package/src/outbound/tts-provider.ts +84 -0
  77. package/src/request-context.ts +9 -8
  78. package/src/runtime.ts +31 -6
  79. package/src/setup/account-key.ts +41 -0
  80. package/src/setup/finalize.ts +110 -0
  81. package/src/setup/login.ts +197 -0
  82. package/src/setup/surface.ts +40 -0
  83. package/src/tools/platform.ts +149 -0
  84. package/src/tools/remind.ts +2 -0
  85. package/src/types-augment.d.ts +54 -0
  86. package/src/types.ts +51 -9
  87. package/src/utils/mention.ts +52 -0
  88. package/src/utils/pkg-version.ts +84 -47
  89. package/src/utils/platform.ts +31 -7
  90. package/src/utils/plugin-logger.ts +104 -0
  91. package/src/utils/ssrf-guard.ts +32 -2
  92. package/src/utils/stt.ts +137 -0
  93. package/src/utils/voice-text.ts +61 -0
  94. package/tsconfig.json +2 -1
  95. package/tsup.config.ts +57 -0
  96. package/dist/index.d.ts +0 -18
  97. package/dist/index.js +0 -27
  98. package/dist/src/admin-resolver.d.ts +0 -33
  99. package/dist/src/admin-resolver.js +0 -157
  100. package/dist/src/api.d.ts +0 -309
  101. package/dist/src/api.js +0 -923
  102. package/dist/src/approval-handler.d.ts +0 -47
  103. package/dist/src/approval-handler.js +0 -372
  104. package/dist/src/channel.d.ts +0 -29
  105. package/dist/src/channel.js +0 -528
  106. package/dist/src/config.d.ts +0 -60
  107. package/dist/src/config.js +0 -288
  108. package/dist/src/credential-backup.d.ts +0 -31
  109. package/dist/src/credential-backup.js +0 -66
  110. package/dist/src/deliver-debounce.d.ts +0 -74
  111. package/dist/src/deliver-debounce.js +0 -174
  112. package/dist/src/gateway.d.ts +0 -18
  113. package/dist/src/gateway.js +0 -2025
  114. package/dist/src/group-history.d.ts +0 -136
  115. package/dist/src/group-history.js +0 -226
  116. package/dist/src/image-server.d.ts +0 -87
  117. package/dist/src/image-server.js +0 -570
  118. package/dist/src/inbound-attachments.d.ts +0 -60
  119. package/dist/src/inbound-attachments.js +0 -248
  120. package/dist/src/known-users.d.ts +0 -100
  121. package/dist/src/known-users.js +0 -263
  122. package/dist/src/message-gating.d.ts +0 -53
  123. package/dist/src/message-gating.js +0 -107
  124. package/dist/src/message-queue.d.ts +0 -91
  125. package/dist/src/message-queue.js +0 -257
  126. package/dist/src/onboarding.d.ts +0 -10
  127. package/dist/src/onboarding.js +0 -203
  128. package/dist/src/outbound-deliver.d.ts +0 -48
  129. package/dist/src/outbound-deliver.js +0 -392
  130. package/dist/src/outbound.d.ts +0 -220
  131. package/dist/src/outbound.js +0 -975
  132. package/dist/src/proactive.d.ts +0 -170
  133. package/dist/src/proactive.js +0 -399
  134. package/dist/src/ref-index-store.d.ts +0 -101
  135. package/dist/src/ref-index-store.js +0 -298
  136. package/dist/src/reply-dispatcher.d.ts +0 -35
  137. package/dist/src/reply-dispatcher.js +0 -311
  138. package/dist/src/request-context.d.ts +0 -25
  139. package/dist/src/request-context.js +0 -37
  140. package/dist/src/runtime.d.ts +0 -3
  141. package/dist/src/runtime.js +0 -13
  142. package/dist/src/session-store.d.ts +0 -52
  143. package/dist/src/session-store.js +0 -254
  144. package/dist/src/slash-commands.d.ts +0 -82
  145. package/dist/src/slash-commands.js +0 -2233
  146. package/dist/src/startup-greeting.d.ts +0 -30
  147. package/dist/src/startup-greeting.js +0 -97
  148. package/dist/src/streaming.d.ts +0 -247
  149. package/dist/src/streaming.js +0 -899
  150. package/dist/src/stt.d.ts +0 -21
  151. package/dist/src/stt.js +0 -70
  152. package/dist/src/tools/channel.d.ts +0 -16
  153. package/dist/src/tools/channel.js +0 -231
  154. package/dist/src/tools/remind.d.ts +0 -2
  155. package/dist/src/tools/remind.js +0 -255
  156. package/dist/src/transport/index.d.ts +0 -10
  157. package/dist/src/transport/index.js +0 -9
  158. package/dist/src/transport/webhook-transport.d.ts +0 -67
  159. package/dist/src/transport/webhook-transport.js +0 -245
  160. package/dist/src/transport/webhook-verify.d.ts +0 -48
  161. package/dist/src/transport/webhook-verify.js +0 -98
  162. package/dist/src/types.d.ts +0 -462
  163. package/dist/src/types.js +0 -22
  164. package/dist/src/typing-keepalive.d.ts +0 -27
  165. package/dist/src/typing-keepalive.js +0 -64
  166. package/dist/src/update-checker.d.ts +0 -36
  167. package/dist/src/update-checker.js +0 -171
  168. package/dist/src/utils/audio-convert.d.ts +0 -98
  169. package/dist/src/utils/audio-convert.js +0 -783
  170. package/dist/src/utils/chunked-upload.d.ts +0 -68
  171. package/dist/src/utils/chunked-upload.js +0 -341
  172. package/dist/src/utils/file-utils.d.ts +0 -61
  173. package/dist/src/utils/file-utils.js +0 -172
  174. package/dist/src/utils/image-size.d.ts +0 -51
  175. package/dist/src/utils/image-size.js +0 -234
  176. package/dist/src/utils/media-send.d.ts +0 -158
  177. package/dist/src/utils/media-send.js +0 -502
  178. package/dist/src/utils/media-tags.d.ts +0 -14
  179. package/dist/src/utils/media-tags.js +0 -165
  180. package/dist/src/utils/payload.d.ts +0 -112
  181. package/dist/src/utils/payload.js +0 -186
  182. package/dist/src/utils/pkg-version.d.ts +0 -5
  183. package/dist/src/utils/pkg-version.js +0 -61
  184. package/dist/src/utils/platform.d.ts +0 -137
  185. package/dist/src/utils/platform.js +0 -390
  186. package/dist/src/utils/ssrf-guard.d.ts +0 -25
  187. package/dist/src/utils/ssrf-guard.js +0 -91
  188. package/dist/src/utils/text-parsing.d.ts +0 -31
  189. package/dist/src/utils/text-parsing.js +0 -74
  190. package/dist/src/utils/upload-cache.d.ts +0 -34
  191. package/dist/src/utils/upload-cache.js +0 -93
  192. package/node_modules/@eshaz/web-worker/LICENSE +0 -201
  193. package/node_modules/@eshaz/web-worker/README.md +0 -134
  194. package/node_modules/@eshaz/web-worker/browser.js +0 -17
  195. package/node_modules/@eshaz/web-worker/cjs/browser.js +0 -16
  196. package/node_modules/@eshaz/web-worker/cjs/node.js +0 -219
  197. package/node_modules/@eshaz/web-worker/index.d.ts +0 -4
  198. package/node_modules/@eshaz/web-worker/node.js +0 -223
  199. package/node_modules/@eshaz/web-worker/package.json +0 -54
  200. package/node_modules/@wasm-audio-decoders/common/index.js +0 -5
  201. package/node_modules/@wasm-audio-decoders/common/package.json +0 -36
  202. package/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderCommon.js +0 -231
  203. package/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderWorker.js +0 -129
  204. package/node_modules/@wasm-audio-decoders/common/src/puff/README +0 -67
  205. package/node_modules/@wasm-audio-decoders/common/src/puff/build_puff.js +0 -31
  206. package/node_modules/@wasm-audio-decoders/common/src/puff/puff.c +0 -863
  207. package/node_modules/@wasm-audio-decoders/common/src/puff/puff.h +0 -35
  208. package/node_modules/@wasm-audio-decoders/common/src/utilities.js +0 -3
  209. package/node_modules/@wasm-audio-decoders/common/types.d.ts +0 -7
  210. package/node_modules/mpg123-decoder/README.md +0 -265
  211. package/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js +0 -185
  212. package/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js.map +0 -1
  213. package/node_modules/mpg123-decoder/index.js +0 -8
  214. package/node_modules/mpg123-decoder/package.json +0 -58
  215. package/node_modules/mpg123-decoder/src/EmscriptenWasm.js +0 -464
  216. package/node_modules/mpg123-decoder/src/MPEGDecoder.js +0 -200
  217. package/node_modules/mpg123-decoder/src/MPEGDecoderWebWorker.js +0 -21
  218. package/node_modules/mpg123-decoder/types.d.ts +0 -30
  219. package/node_modules/silk-wasm/LICENSE +0 -21
  220. package/node_modules/silk-wasm/README.md +0 -85
  221. package/node_modules/silk-wasm/lib/index.cjs +0 -16
  222. package/node_modules/silk-wasm/lib/index.d.ts +0 -70
  223. package/node_modules/silk-wasm/lib/index.mjs +0 -16
  224. package/node_modules/silk-wasm/lib/silk.wasm +0 -0
  225. package/node_modules/silk-wasm/lib/utils.d.ts +0 -4
  226. package/node_modules/silk-wasm/package.json +0 -39
  227. package/node_modules/simple-yenc/.github/FUNDING.yml +0 -1
  228. package/node_modules/simple-yenc/.prettierignore +0 -1
  229. package/node_modules/simple-yenc/LICENSE +0 -7
  230. package/node_modules/simple-yenc/README.md +0 -163
  231. package/node_modules/simple-yenc/dist/esm.js +0 -1
  232. package/node_modules/simple-yenc/dist/index.js +0 -1
  233. package/node_modules/simple-yenc/package.json +0 -50
  234. package/node_modules/simple-yenc/rollup.config.js +0 -27
  235. package/node_modules/simple-yenc/src/simple-yenc.js +0 -302
  236. package/node_modules/ws/LICENSE +0 -20
  237. package/node_modules/ws/README.md +0 -548
  238. package/node_modules/ws/browser.js +0 -8
  239. package/node_modules/ws/index.js +0 -13
  240. package/node_modules/ws/lib/buffer-util.js +0 -131
  241. package/node_modules/ws/lib/constants.js +0 -19
  242. package/node_modules/ws/lib/event-target.js +0 -292
  243. package/node_modules/ws/lib/extension.js +0 -203
  244. package/node_modules/ws/lib/limiter.js +0 -55
  245. package/node_modules/ws/lib/permessage-deflate.js +0 -528
  246. package/node_modules/ws/lib/receiver.js +0 -706
  247. package/node_modules/ws/lib/sender.js +0 -602
  248. package/node_modules/ws/lib/stream.js +0 -161
  249. package/node_modules/ws/lib/subprotocol.js +0 -62
  250. package/node_modules/ws/lib/validation.js +0 -152
  251. package/node_modules/ws/lib/websocket-server.js +0 -554
  252. package/node_modules/ws/lib/websocket.js +0 -1393
  253. package/node_modules/ws/package.json +0 -69
  254. package/node_modules/ws/wrapper.mjs +0 -8
  255. package/scripts/postinstall-link-sdk.js +0 -170
  256. package/skills/qqbot-media/SKILL.md +0 -60
  257. package/src/admin-resolver.ts +0 -181
  258. package/src/api.ts +0 -1333
  259. package/src/credential-backup.ts +0 -72
  260. package/src/deliver-debounce.ts +0 -229
  261. package/src/gateway.ts +0 -2272
  262. package/src/group-history.ts +0 -328
  263. package/src/image-server.ts +0 -675
  264. package/src/inbound-attachments.ts +0 -321
  265. package/src/known-users.ts +0 -353
  266. package/src/message-gating.ts +0 -190
  267. package/src/message-queue.ts +0 -354
  268. package/src/onboarding.ts +0 -282
  269. package/src/outbound-deliver.ts +0 -486
  270. package/src/outbound.ts +0 -1179
  271. package/src/ref-index-store.ts +0 -412
  272. package/src/reply-dispatcher.ts +0 -334
  273. package/src/session-store.ts +0 -303
  274. package/src/slash-commands.ts +0 -2431
  275. package/src/startup-greeting.ts +0 -120
  276. package/src/streaming.ts +0 -1077
  277. package/src/stt.ts +0 -86
  278. package/src/tools/channel.ts +0 -275
  279. package/src/transport/index.ts +0 -11
  280. package/src/transport/webhook-transport.ts +0 -332
  281. package/src/transport/webhook-verify.ts +0 -119
  282. package/src/typing-keepalive.ts +0 -59
  283. package/src/utils/audio-convert.ts +0 -887
  284. package/src/utils/chunked-upload.ts +0 -483
  285. package/src/utils/file-utils.ts +0 -193
  286. package/src/utils/image-size.ts +0 -266
  287. package/src/utils/media-send.ts +0 -636
  288. package/src/utils/media-tags.ts +0 -183
  289. package/src/utils/payload.ts +0 -265
  290. package/src/utils/text-parsing.ts +0 -85
  291. package/src/utils/upload-cache.ts +0 -128
@@ -1,328 +0,0 @@
1
- /**
2
- * 群历史消息缓存
3
- *
4
- * 非@消息写入内存 Map,被@时一次性注入上下文后清空。
5
- * 自包含实现,不依赖 openclaw/plugin-sdk。
6
- */
7
-
8
- // ---------------------------------------------------------------------------
9
- // 常量
10
- // ---------------------------------------------------------------------------
11
-
12
- /** 历史上下文段落标签 */
13
- const HISTORY_CTX_START = "[上次回复后的聊天消息 - 作为上下文]";
14
- const HISTORY_CTX_END = "[当前消息 - 请回复此条]";
15
- /** 合并消息段落标签 */
16
- const MERGED_CTX_START = "[以下是合并消息 - 作为上下文]";
17
- const MERGED_CTX_END = "[当前消息 - 结合上下文回复]";
18
- /** 历史 Map 最大 key 数量(LRU 淘汰,防止无限增长) */
19
- const MAX_HISTORY_KEYS = 1000;
20
-
21
- // ---------------------------------------------------------------------------
22
- // 类型
23
- // ---------------------------------------------------------------------------
24
-
25
- /**
26
- * 附件摘要(统一格式)
27
- *
28
- * 兼容 ref-index-store 的 RefAttachmentSummary、群历史缓存、以及 gateway 当前消息动态上下文。
29
- * 所有场景的附件描述都通过 formatAttachmentTags() 统一格式化,确保标签风格一致。
30
- */
31
- export interface AttachmentSummary {
32
- /** 附件类型 */
33
- type: "image" | "voice" | "video" | "file" | "unknown";
34
- /** 文件名(如有) */
35
- filename?: string;
36
- /** 语音转录文本(入站:STT/ASR识别结果;出站:TTS原文本) */
37
- transcript?: string;
38
- /** 语音转录来源:stt=本地STT、asr=平台ASR、tts=TTS原文本、fallback=兜底文案 */
39
- transcriptSource?: "stt" | "asr" | "tts" | "fallback";
40
- /** 已下载到本地的文件路径 */
41
- localPath?: string;
42
- /** 在线来源 URL(公网图片/文件等) */
43
- url?: string;
44
- }
45
-
46
- /**
47
- * QQ 事件原始附件(来自 gateway 事件的通用字段子集)
48
- *
49
- * 多处需要将原始附件转换为 AttachmentSummary,统一此类型避免内联重复定义。
50
- */
51
- export interface RawAttachment {
52
- content_type: string;
53
- filename?: string;
54
- /** 语音 ASR 识别文本(QQ 事件内置) */
55
- asr_refer_text?: string;
56
- /** 附件 URL */
57
- url?: string;
58
- }
59
-
60
- /**
61
- * 根据 content_type 推断附件类型(统一判断逻辑,避免多处重复)
62
- */
63
- export function inferAttachmentType(contentType?: string): AttachmentSummary["type"] {
64
- const ct = (contentType ?? "").toLowerCase();
65
- if (ct.startsWith("image/")) return "image";
66
- if (ct === "voice" || ct.startsWith("audio/") || ct.includes("silk") || ct.includes("amr")) return "voice";
67
- if (ct.startsWith("video/")) return "video";
68
- if (ct.startsWith("application/") || ct.startsWith("text/")) return "file";
69
- return "unknown";
70
- }
71
-
72
- /**
73
- * 将原始附件数组转换为 AttachmentSummary 数组。
74
- *
75
- * 统一"原始附件 → 摘要"的映射逻辑,供历史记录缓存、合并消息格式化等场景复用。
76
- * 无附件时返回 undefined(而非空数组),与 HistoryEntry.attachments 的可选语义一致。
77
- */
78
- export function toAttachmentSummaries(attachments?: RawAttachment[]): AttachmentSummary[] | undefined {
79
- if (!attachments?.length) return undefined;
80
- return attachments.map((att) => ({
81
- type: inferAttachmentType(att.content_type),
82
- filename: att.filename,
83
- transcript: att.asr_refer_text || undefined,
84
- url: att.url || undefined,
85
- }));
86
- }
87
-
88
- /** @deprecated 使用 AttachmentSummary 代替 */
89
- export type HistoryAttachment = AttachmentSummary;
90
-
91
- export interface HistoryEntry {
92
- sender: string;
93
- body: string;
94
- timestamp?: number;
95
- messageId?: string;
96
- /** 富媒体附件摘要(图片/语音/视频/文件) */
97
- attachments?: AttachmentSummary[];
98
- }
99
-
100
- // ---------------------------------------------------------------------------
101
- // 消息内容格式化(表情解析 + mention 清理 + 附件标签)
102
- // ---------------------------------------------------------------------------
103
-
104
- /** formatMessageContent 入参 */
105
- export interface FormatMessageContentParams {
106
- content: string;
107
- /** 消息类型(group 时才做 mention 清理) */
108
- chatType?: string;
109
- mentions?: unknown[];
110
- attachments?: RawAttachment[];
111
- /** QQ 表情标签解析(<faceType=...> → 【表情: 中文名】) */
112
- parseFaceTags: (text: string) => string;
113
- /** mention @ 清理(移除 <@member_openid> 标记) */
114
- stripMentionText?: (text: string, mentions: unknown[]) => string;
115
- }
116
-
117
- /**
118
- * 格式化单条消息内容:表情标签解析 → mention 清理 → 附件标签拼接。
119
- *
120
- * 用于合并消息的逐条子消息格式化,将外部依赖(parseFaceTags / stripMentionText)
121
- * 通过参数注入,保持本模块自包含。
122
- */
123
- export function formatMessageContent(params: FormatMessageContentParams): string {
124
- let msgContent = params.parseFaceTags(params.content);
125
-
126
- if (params.chatType === "group" && params.mentions?.length && params.stripMentionText) {
127
- msgContent = params.stripMentionText(msgContent, params.mentions);
128
- }
129
-
130
- if (params.attachments?.length) {
131
- const attachmentDesc = formatAttachmentTags(toAttachmentSummaries(params.attachments));
132
- if (attachmentDesc) {
133
- msgContent = `${msgContent} ${attachmentDesc}`;
134
- }
135
- }
136
-
137
- return msgContent;
138
- }
139
-
140
- // ---------------------------------------------------------------------------
141
- // 附件标签格式化(全局统一)
142
- // ---------------------------------------------------------------------------
143
-
144
- /**
145
- * 将附件摘要格式化为统一的人类可读标签描述(供 AI 上下文注入)。
146
- *
147
- * 标签风格沿用框架的 MEDIA: 标签格式:
148
- * 有路径的附件 → MEDIA:path
149
- * 语音+转录 → MEDIA:path (内容: "transcript")
150
- * 无路径的语音 → [语音消息(内容: "transcript")]
151
- * 无路径无转录 → [图片] / [语音消息] / [视频] / [文件]
152
- *
153
- * 此函数是所有附件描述的 **唯一格式化入口**,确保引用消息、群历史缓存、
154
- * 当前消息动态上下文三处标签风格完全一致。
155
- */
156
- export function formatAttachmentTags(attachments?: AttachmentSummary[]): string {
157
- if (!attachments?.length) return "";
158
- const parts: string[] = [];
159
- for (const att of attachments) {
160
- const source = att.localPath || att.url;
161
- if (source) {
162
- // 有路径:使用 MEDIA: 标签
163
- if (att.type === "voice" && att.transcript) {
164
- parts.push(`MEDIA:${source}(内容: "${att.transcript}")`);
165
- } else {
166
- parts.push(`MEDIA:${source}`);
167
- }
168
- } else {
169
- // 无路径:使用描述性标签
170
- switch (att.type) {
171
- case "image":
172
- parts.push(`[图片${att.filename ? `: ${att.filename}` : ""}]`);
173
- break;
174
- case "voice":
175
- parts.push(att.transcript
176
- ? `[语音消息(内容: "${att.transcript}")]`
177
- : "[语音消息]");
178
- break;
179
- case "video":
180
- parts.push(`[视频${att.filename ? `: ${att.filename}` : ""}]`);
181
- break;
182
- case "file":
183
- parts.push(`[文件${att.filename ? `: ${att.filename}` : ""}]`);
184
- break;
185
- default:
186
- parts.push(`[附件${att.filename ? `: ${att.filename}` : ""}]`);
187
- }
188
- }
189
- }
190
- return parts.join("\n");
191
- }
192
-
193
- /** @deprecated 使用 formatAttachmentTags 代替 */
194
- export const formatHistoryAttachments = formatAttachmentTags;
195
-
196
- // ---------------------------------------------------------------------------
197
- // 内部工具
198
- // ---------------------------------------------------------------------------
199
-
200
- /**
201
- * LRU 淘汰:当 historyMap 的 key 数量超过阈值时,删除最早插入的 key。
202
- */
203
- function evictOldHistoryKeys<T>(historyMap: Map<string, T[]>, maxKeys: number = MAX_HISTORY_KEYS): void {
204
- if (historyMap.size <= maxKeys) return;
205
- const keysToDelete = historyMap.size - maxKeys;
206
- const iterator = historyMap.keys();
207
- for (let i = 0; i < keysToDelete; i++) {
208
- const key = iterator.next().value;
209
- if (key !== undefined) {
210
- historyMap.delete(key);
211
- }
212
- }
213
- }
214
-
215
- /**
216
- * 向指定 key 的历史列表追加一条记录,超出 limit 时从头部淘汰。
217
- * 同时刷新 key 的插入顺序(Map 迭代顺序 = 插入顺序),实现 LRU 语义。
218
- */
219
- function appendHistoryEntry(params: {
220
- historyMap: Map<string, HistoryEntry[]>;
221
- historyKey: string;
222
- entry: HistoryEntry;
223
- limit: number;
224
- }): HistoryEntry[] {
225
- const { historyMap, historyKey, entry } = params;
226
- if (params.limit <= 0) return [];
227
-
228
- const history = historyMap.get(historyKey) ?? [];
229
- history.push(entry);
230
- while (history.length > params.limit) {
231
- history.shift();
232
- }
233
- // 刷新插入顺序
234
- if (historyMap.has(historyKey)) {
235
- historyMap.delete(historyKey);
236
- }
237
- historyMap.set(historyKey, history);
238
- evictOldHistoryKeys(historyMap);
239
- return history;
240
- }
241
-
242
- // ---------------------------------------------------------------------------
243
- // 公开 API
244
- // ---------------------------------------------------------------------------
245
-
246
- /**
247
- * 记录一条待注入的历史消息(非@消息调用此函数)。
248
- * limit <= 0 或 entry 为空时不记录。
249
- */
250
- export function recordPendingHistoryEntry(params: {
251
- historyMap: Map<string, HistoryEntry[]>;
252
- historyKey: string;
253
- entry?: HistoryEntry | null;
254
- limit: number;
255
- }): HistoryEntry[] {
256
- if (!params.entry || params.limit <= 0) return [];
257
- return appendHistoryEntry({
258
- historyMap: params.historyMap,
259
- historyKey: params.historyKey,
260
- entry: params.entry,
261
- limit: params.limit,
262
- });
263
- }
264
-
265
- /**
266
- * 构建包含历史上下文的完整消息体(被@时调用)。
267
- * 如果没有累积的历史消息,直接返回 currentMessage 原文。
268
- */
269
- export function buildPendingHistoryContext(params: {
270
- historyMap: Map<string, HistoryEntry[]>;
271
- historyKey: string;
272
- limit: number;
273
- currentMessage: string;
274
- formatEntry: (entry: HistoryEntry) => string;
275
- lineBreak?: string;
276
- }): string {
277
- if (params.limit <= 0) return params.currentMessage;
278
-
279
- const entries = params.historyMap.get(params.historyKey) ?? [];
280
- if (entries.length === 0) return params.currentMessage;
281
-
282
- const lineBreak = params.lineBreak ?? "\n";
283
- const historyText = entries.map(params.formatEntry).join(lineBreak);
284
-
285
- return [
286
- HISTORY_CTX_START,
287
- historyText,
288
- "",
289
- HISTORY_CTX_END,
290
- params.currentMessage,
291
- ].join(lineBreak);
292
- }
293
-
294
- /**
295
- * 构建合并消息上下文(多条排队消息被合并时调用)。
296
- * 前置消息用 [合并消息开始]...[合并消息结束] 段落标签包裹,
297
- * 最后一条作为当前消息紧跟其后。
298
- * 如果只有一条消息,直接返回 currentMessage 原文。
299
- */
300
- export function buildMergedMessageContext(params: {
301
- precedingParts: string[];
302
- currentMessage: string;
303
- lineBreak?: string;
304
- }): string {
305
- const { precedingParts, currentMessage } = params;
306
- if (precedingParts.length === 0) return currentMessage;
307
-
308
- const lineBreak = params.lineBreak ?? "\n";
309
- return [
310
- MERGED_CTX_START,
311
- precedingParts.join(lineBreak),
312
- MERGED_CTX_END,
313
- currentMessage,
314
- ].join(lineBreak);
315
- }
316
-
317
- /**
318
- * 清空指定群的历史缓存(回复完成后调用)。
319
- * limit <= 0 表示功能已禁用,不做操作。
320
- */
321
- export function clearPendingHistory(params: {
322
- historyMap: Map<string, HistoryEntry[]>;
323
- historyKey: string;
324
- limit: number;
325
- }): void {
326
- if (params.limit <= 0) return;
327
- params.historyMap.set(params.historyKey, []);
328
- }