@tencent-connect/openclaw-qqbot 1.7.1 → 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 (283) hide show
  1. package/README.md +206 -4
  2. package/README.zh.md +207 -4
  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 -18
  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 +400 -73
  13. package/scripts/upgrade-via-source.sh +126 -89
  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 +59 -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 +1 -1
  85. package/src/types-augment.d.ts +54 -0
  86. package/src/types.ts +71 -8
  87. package/src/typings/openclaw-webhook-ingress.d.ts +66 -0
  88. package/src/utils/mention.ts +52 -0
  89. package/src/utils/pkg-version.ts +84 -47
  90. package/src/utils/platform.ts +31 -7
  91. package/src/utils/plugin-logger.ts +104 -0
  92. package/src/utils/ssrf-guard.ts +32 -2
  93. package/src/utils/stt.ts +137 -0
  94. package/src/utils/voice-text.ts +61 -0
  95. package/tsconfig.json +2 -1
  96. package/tsup.config.ts +57 -0
  97. package/dist/index.d.ts +0 -17
  98. package/dist/index.js +0 -26
  99. package/dist/src/admin-resolver.d.ts +0 -33
  100. package/dist/src/admin-resolver.js +0 -157
  101. package/dist/src/api.d.ts +0 -307
  102. package/dist/src/api.js +0 -910
  103. package/dist/src/approval-handler.d.ts +0 -47
  104. package/dist/src/approval-handler.js +0 -372
  105. package/dist/src/channel.d.ts +0 -29
  106. package/dist/src/channel.js +0 -528
  107. package/dist/src/config.d.ts +0 -56
  108. package/dist/src/config.js +0 -278
  109. package/dist/src/credential-backup.d.ts +0 -31
  110. package/dist/src/credential-backup.js +0 -66
  111. package/dist/src/deliver-debounce.d.ts +0 -74
  112. package/dist/src/deliver-debounce.js +0 -174
  113. package/dist/src/gateway.d.ts +0 -18
  114. package/dist/src/gateway.js +0 -2063
  115. package/dist/src/group-history.d.ts +0 -136
  116. package/dist/src/group-history.js +0 -226
  117. package/dist/src/image-server.d.ts +0 -87
  118. package/dist/src/image-server.js +0 -570
  119. package/dist/src/inbound-attachments.d.ts +0 -60
  120. package/dist/src/inbound-attachments.js +0 -248
  121. package/dist/src/known-users.d.ts +0 -100
  122. package/dist/src/known-users.js +0 -263
  123. package/dist/src/message-gating.d.ts +0 -53
  124. package/dist/src/message-gating.js +0 -107
  125. package/dist/src/message-queue.d.ts +0 -91
  126. package/dist/src/message-queue.js +0 -257
  127. package/dist/src/onboarding.d.ts +0 -10
  128. package/dist/src/onboarding.js +0 -203
  129. package/dist/src/outbound-deliver.d.ts +0 -48
  130. package/dist/src/outbound-deliver.js +0 -392
  131. package/dist/src/outbound.d.ts +0 -220
  132. package/dist/src/outbound.js +0 -975
  133. package/dist/src/proactive.d.ts +0 -170
  134. package/dist/src/proactive.js +0 -399
  135. package/dist/src/ref-index-store.d.ts +0 -101
  136. package/dist/src/ref-index-store.js +0 -298
  137. package/dist/src/reply-dispatcher.d.ts +0 -35
  138. package/dist/src/reply-dispatcher.js +0 -311
  139. package/dist/src/request-context.d.ts +0 -25
  140. package/dist/src/request-context.js +0 -37
  141. package/dist/src/runtime.d.ts +0 -3
  142. package/dist/src/runtime.js +0 -13
  143. package/dist/src/session-store.d.ts +0 -52
  144. package/dist/src/session-store.js +0 -254
  145. package/dist/src/slash-commands.d.ts +0 -82
  146. package/dist/src/slash-commands.js +0 -2117
  147. package/dist/src/startup-greeting.d.ts +0 -30
  148. package/dist/src/startup-greeting.js +0 -97
  149. package/dist/src/streaming.d.ts +0 -247
  150. package/dist/src/streaming.js +0 -899
  151. package/dist/src/stt.d.ts +0 -21
  152. package/dist/src/stt.js +0 -70
  153. package/dist/src/tools/channel.d.ts +0 -16
  154. package/dist/src/tools/channel.js +0 -234
  155. package/dist/src/tools/remind.d.ts +0 -2
  156. package/dist/src/tools/remind.js +0 -256
  157. package/dist/src/types.d.ts +0 -443
  158. package/dist/src/types.js +0 -22
  159. package/dist/src/typing-keepalive.d.ts +0 -27
  160. package/dist/src/typing-keepalive.js +0 -64
  161. package/dist/src/update-checker.d.ts +0 -36
  162. package/dist/src/update-checker.js +0 -171
  163. package/dist/src/utils/audio-convert.d.ts +0 -98
  164. package/dist/src/utils/audio-convert.js +0 -755
  165. package/dist/src/utils/chunked-upload.d.ts +0 -68
  166. package/dist/src/utils/chunked-upload.js +0 -341
  167. package/dist/src/utils/file-utils.d.ts +0 -61
  168. package/dist/src/utils/file-utils.js +0 -172
  169. package/dist/src/utils/image-size.d.ts +0 -51
  170. package/dist/src/utils/image-size.js +0 -234
  171. package/dist/src/utils/media-send.d.ts +0 -158
  172. package/dist/src/utils/media-send.js +0 -502
  173. package/dist/src/utils/media-tags.d.ts +0 -14
  174. package/dist/src/utils/media-tags.js +0 -165
  175. package/dist/src/utils/payload.d.ts +0 -112
  176. package/dist/src/utils/payload.js +0 -186
  177. package/dist/src/utils/pkg-version.d.ts +0 -5
  178. package/dist/src/utils/pkg-version.js +0 -61
  179. package/dist/src/utils/platform.d.ts +0 -137
  180. package/dist/src/utils/platform.js +0 -390
  181. package/dist/src/utils/ssrf-guard.d.ts +0 -25
  182. package/dist/src/utils/ssrf-guard.js +0 -91
  183. package/dist/src/utils/text-parsing.d.ts +0 -31
  184. package/dist/src/utils/text-parsing.js +0 -74
  185. package/dist/src/utils/upload-cache.d.ts +0 -34
  186. package/dist/src/utils/upload-cache.js +0 -93
  187. package/node_modules/@eshaz/web-worker/LICENSE +0 -201
  188. package/node_modules/@eshaz/web-worker/README.md +0 -134
  189. package/node_modules/@eshaz/web-worker/browser.js +0 -17
  190. package/node_modules/@eshaz/web-worker/cjs/browser.js +0 -16
  191. package/node_modules/@eshaz/web-worker/cjs/node.js +0 -219
  192. package/node_modules/@eshaz/web-worker/index.d.ts +0 -4
  193. package/node_modules/@eshaz/web-worker/node.js +0 -223
  194. package/node_modules/@eshaz/web-worker/package.json +0 -54
  195. package/node_modules/@wasm-audio-decoders/common/index.js +0 -5
  196. package/node_modules/@wasm-audio-decoders/common/package.json +0 -36
  197. package/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderCommon.js +0 -231
  198. package/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderWorker.js +0 -129
  199. package/node_modules/@wasm-audio-decoders/common/src/puff/README +0 -67
  200. package/node_modules/@wasm-audio-decoders/common/src/puff/build_puff.js +0 -31
  201. package/node_modules/@wasm-audio-decoders/common/src/puff/puff.c +0 -863
  202. package/node_modules/@wasm-audio-decoders/common/src/puff/puff.h +0 -35
  203. package/node_modules/@wasm-audio-decoders/common/src/utilities.js +0 -3
  204. package/node_modules/@wasm-audio-decoders/common/types.d.ts +0 -7
  205. package/node_modules/mpg123-decoder/README.md +0 -265
  206. package/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js +0 -185
  207. package/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js.map +0 -1
  208. package/node_modules/mpg123-decoder/index.js +0 -8
  209. package/node_modules/mpg123-decoder/package.json +0 -58
  210. package/node_modules/mpg123-decoder/src/EmscriptenWasm.js +0 -464
  211. package/node_modules/mpg123-decoder/src/MPEGDecoder.js +0 -200
  212. package/node_modules/mpg123-decoder/src/MPEGDecoderWebWorker.js +0 -21
  213. package/node_modules/mpg123-decoder/types.d.ts +0 -30
  214. package/node_modules/silk-wasm/LICENSE +0 -21
  215. package/node_modules/silk-wasm/README.md +0 -85
  216. package/node_modules/silk-wasm/lib/index.cjs +0 -16
  217. package/node_modules/silk-wasm/lib/index.d.ts +0 -70
  218. package/node_modules/silk-wasm/lib/index.mjs +0 -16
  219. package/node_modules/silk-wasm/lib/silk.wasm +0 -0
  220. package/node_modules/silk-wasm/lib/utils.d.ts +0 -4
  221. package/node_modules/silk-wasm/package.json +0 -39
  222. package/node_modules/simple-yenc/.github/FUNDING.yml +0 -1
  223. package/node_modules/simple-yenc/.prettierignore +0 -1
  224. package/node_modules/simple-yenc/LICENSE +0 -7
  225. package/node_modules/simple-yenc/README.md +0 -163
  226. package/node_modules/simple-yenc/dist/esm.js +0 -1
  227. package/node_modules/simple-yenc/dist/index.js +0 -1
  228. package/node_modules/simple-yenc/package.json +0 -50
  229. package/node_modules/simple-yenc/rollup.config.js +0 -27
  230. package/node_modules/simple-yenc/src/simple-yenc.js +0 -302
  231. package/node_modules/ws/LICENSE +0 -20
  232. package/node_modules/ws/README.md +0 -548
  233. package/node_modules/ws/browser.js +0 -8
  234. package/node_modules/ws/index.js +0 -13
  235. package/node_modules/ws/lib/buffer-util.js +0 -131
  236. package/node_modules/ws/lib/constants.js +0 -19
  237. package/node_modules/ws/lib/event-target.js +0 -292
  238. package/node_modules/ws/lib/extension.js +0 -203
  239. package/node_modules/ws/lib/limiter.js +0 -55
  240. package/node_modules/ws/lib/permessage-deflate.js +0 -528
  241. package/node_modules/ws/lib/receiver.js +0 -706
  242. package/node_modules/ws/lib/sender.js +0 -602
  243. package/node_modules/ws/lib/stream.js +0 -161
  244. package/node_modules/ws/lib/subprotocol.js +0 -62
  245. package/node_modules/ws/lib/validation.js +0 -152
  246. package/node_modules/ws/lib/websocket-server.js +0 -554
  247. package/node_modules/ws/lib/websocket.js +0 -1393
  248. package/node_modules/ws/package.json +0 -69
  249. package/node_modules/ws/wrapper.mjs +0 -8
  250. package/scripts/postinstall-link-sdk.js +0 -126
  251. package/skills/qqbot-media/SKILL.md +0 -60
  252. package/src/admin-resolver.ts +0 -181
  253. package/src/api.ts +0 -1319
  254. package/src/credential-backup.ts +0 -72
  255. package/src/deliver-debounce.ts +0 -229
  256. package/src/gateway.ts +0 -2304
  257. package/src/group-history.ts +0 -328
  258. package/src/image-server.ts +0 -675
  259. package/src/inbound-attachments.ts +0 -321
  260. package/src/known-users.ts +0 -353
  261. package/src/message-gating.ts +0 -190
  262. package/src/message-queue.ts +0 -354
  263. package/src/onboarding.ts +0 -274
  264. package/src/outbound-deliver.ts +0 -486
  265. package/src/outbound.ts +0 -1179
  266. package/src/ref-index-store.ts +0 -412
  267. package/src/reply-dispatcher.ts +0 -334
  268. package/src/session-store.ts +0 -303
  269. package/src/slash-commands.ts +0 -2297
  270. package/src/startup-greeting.ts +0 -120
  271. package/src/streaming.ts +0 -1077
  272. package/src/stt.ts +0 -86
  273. package/src/tools/channel.ts +0 -281
  274. package/src/typing-keepalive.ts +0 -59
  275. package/src/utils/audio-convert.ts +0 -859
  276. package/src/utils/chunked-upload.ts +0 -483
  277. package/src/utils/file-utils.ts +0 -193
  278. package/src/utils/image-size.ts +0 -266
  279. package/src/utils/media-send.ts +0 -636
  280. package/src/utils/media-tags.ts +0 -183
  281. package/src/utils/payload.ts +0 -265
  282. package/src/utils/text-parsing.ts +0 -85
  283. package/src/utils/upload-cache.ts +0 -128
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts","../node_modules/ws/lib/constants.js","../node_modules/ws/lib/buffer-util.js","../node_modules/ws/lib/limiter.js","../node_modules/ws/lib/permessage-deflate.js","../node_modules/ws/lib/validation.js","../node_modules/ws/lib/receiver.js","../node_modules/ws/lib/sender.js","../node_modules/ws/lib/event-target.js","../node_modules/ws/lib/extension.js","../node_modules/ws/lib/websocket.js","../node_modules/ws/lib/stream.js","../node_modules/ws/lib/subprotocol.js","../node_modules/ws/lib/websocket-server.js","../src/adapter/resolve.ts","../src/adapter/setup.ts","../node_modules/qrcode-terminal/vendor/QRCode/QRMode.js","../node_modules/qrcode-terminal/vendor/QRCode/QR8bitByte.js","../node_modules/qrcode-terminal/vendor/QRCode/QRMath.js","../node_modules/qrcode-terminal/vendor/QRCode/QRPolynomial.js","../node_modules/qrcode-terminal/vendor/QRCode/QRMaskPattern.js","../node_modules/qrcode-terminal/vendor/QRCode/QRUtil.js","../node_modules/qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js","../node_modules/qrcode-terminal/vendor/QRCode/QRRSBlock.js","../node_modules/qrcode-terminal/vendor/QRCode/QRBitBuffer.js","../node_modules/qrcode-terminal/vendor/QRCode/index.js","../node_modules/qrcode-terminal/lib/main.js","../node_modules/@tencent-connect/qqbot-connector/dist/esm/qqbot-session.js","../node_modules/@tencent-connect/qqbot-connector/dist/esm/qr-connect.js","../node_modules/@tencent-connect/qqbot-connector/dist/esm/index.js","../src/setup/account-key.ts","../src/setup/finalize.ts","../index.ts","../src/channel.ts","../src/bot-instance.ts","../src/outbound/outbound-service.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/QQBot.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/middleware/types.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/types.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/utils/format.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/api/api-client.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/api/media-chunked.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/api/retry.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/api/routes.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/api/media.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/utils/file-utils.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/api/messages.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/api/token.ts","../node_modules/ws/wrapper.mjs","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/gateway/codec.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/gateway/constants.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/gateway/event-dispatcher.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/gateway/reconnect.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/gateway/gateway-connection.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/transport/webhook-verify.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/transport/webhook-server-node.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/transport/webhook.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/utils/upload-cache.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/streaming.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/middleware/message-filter.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/middleware/content-sanitizer.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/middleware/rate-limiter.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/middleware/concurrency-guard.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/middleware/mention-gate.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/middleware/quote-ref.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/middleware/envelope-formatter.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/middleware/slash-command.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/middleware/history-buffer.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/middleware/typing-indicator.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/middleware/error-handler.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/storage/kv-store.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/storage/session-adapter.ts","../src/outbound/target.ts","../src/outbound/reply-limiter.ts","../src/utils/pkg-version.ts","../src/features/ref-index-store.ts","../src/utils/platform.ts","../src/request-context.ts","../src/utils/plugin-logger.ts","../src/runtime.ts","../src/outbound/media-send.ts","../src/adapter/workspace.ts","../src/utils/ssrf-guard.ts","../src/outbound/local-file-router.ts","../src/setup/surface.ts","../src/setup/login.ts","../src/outbound/sanitize.ts","../src/gateway/lifecycle.ts","../src/gateway/qqbot-gateway.ts","../src/commands/bot-help.ts","../src/commands/bot-ping.ts","../src/features/update-checker.ts","../src/commands/bot-version.ts","../src/commands/bot-me.ts","../src/commands/config-util.ts","../src/commands/bot-upgrade.ts","../src/commands/bot-streaming.ts","../src/commands/bot-clear-storage.ts","../src/commands/bot-logs.ts","../src/commands/bot-approve.ts","../src/commands/bot-group-always.ts","../src/adapter/pairing.ts","../src/commands/bot-pairing.ts","../src/commands/index.ts","../src/middleware/attachment.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/utils/reply-limiter.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/utils/media-tags.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/utils/ref-index-store.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/utils/session-store.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/utils/text-parsing.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/utils/media-source.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/utils/image-size.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/utils/ffmpeg.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/utils/audio.ts","../src/utils/stt.ts","../src/utils/voice-text.ts","../src/adapter/media.ts","../src/dispatch/body-assembler.ts","../src/middleware/policy-injector.ts","../src/features/history-store.ts","../src/middleware/access-control.ts","../src/utils/mention.ts","../src/gateway/middleware-setup.ts","../src/dispatch/envelope-builder.ts","../src/outbound/deliver-pipeline.ts","../src/dispatch/ctx-builder.ts","../src/outbound/debounce.ts","../src/outbound/streaming-controller.ts","../src/dispatch/dispatch.ts","../src/adapter/gateway.ts","../src/features/approval-handler.ts","../src/features/proactive.ts","../src/features/msgid-cache.ts","../src/gateway/event-handlers.ts","../src/adapter/webhook.ts","../src/features/credential-backup.ts","../src/features/approval-utils.ts","../src/features/onboarding.ts","../src/tools/platform.ts","../src/tools/remind.ts","../src/adapter/contract.ts","../src/types.ts"],"sourcesContent":["import type { ResolvedQQBotAccount, QQBotAccountConfig, ToolPolicy, GroupConfig } from \"./types.js\";\nimport type { OpenClawConfig, GroupPolicy } from \"openclaw/plugin-sdk\";\n\n// ============ Agent-aware mentionPatterns 解析 ============\n\ntype AgentEntry = { id?: string; groupChat?: { mentionPatterns?: string[]; historyLimit?: number } };\n\n/**\n * 解析 mentionPatterns(agent → global → 空数组)\n *\n * 优先级:\n * 1. agents.list[agentId].groupChat.mentionPatterns\n * 2. messages.groupChat.mentionPatterns\n * 3. []\n */\nexport function resolveMentionPatterns(cfg: OpenClawConfig, agentId?: string): string[] {\n // 1. agent 级别\n if (agentId) {\n const agents = (cfg as Record<string, unknown>).agents as { list?: AgentEntry[] } | undefined;\n const entry = agents?.list?.find((a) => a.id?.trim().toLowerCase() === agentId.trim().toLowerCase());\n const agentGroupChat = entry?.groupChat;\n if (agentGroupChat && Object.hasOwn(agentGroupChat, \"mentionPatterns\")) {\n return agentGroupChat.mentionPatterns ?? [];\n }\n }\n // 2. 全局级别\n const globalGroupChat = (cfg as any)?.messages?.groupChat;\n if (globalGroupChat && typeof globalGroupChat === \"object\" && Object.hasOwn(globalGroupChat, \"mentionPatterns\")) {\n return (globalGroupChat as { mentionPatterns?: string[] }).mentionPatterns ?? [];\n }\n // 3. 空数组\n return [];\n}\n\nexport const DEFAULT_ACCOUNT_ID = \"default\";\n\n// 内联 evaluateMatchedGroupAccessForPolicy(openclaw dist 尚未导出,本地实现)\n\ntype MatchedGroupAccessReason = \"allowed\" | \"disabled\" | \"missing_match_input\" | \"empty_allowlist\" | \"not_allowlisted\";\n\ninterface MatchedGroupAccessDecision {\n allowed: boolean;\n groupPolicy: GroupPolicy;\n reason: MatchedGroupAccessReason;\n}\n\nfunction evaluateMatchedGroupAccessForPolicy(params: {\n groupPolicy: GroupPolicy;\n allowlistConfigured: boolean;\n allowlistMatched: boolean;\n requireMatchInput?: boolean;\n hasMatchInput?: boolean;\n}): MatchedGroupAccessDecision {\n if (params.groupPolicy === \"disabled\") {\n return { allowed: false, groupPolicy: params.groupPolicy, reason: \"disabled\" };\n }\n if (params.groupPolicy === \"allowlist\") {\n if (params.requireMatchInput && !params.hasMatchInput) {\n return { allowed: false, groupPolicy: params.groupPolicy, reason: \"missing_match_input\" };\n }\n if (!params.allowlistConfigured) {\n return { allowed: false, groupPolicy: params.groupPolicy, reason: \"empty_allowlist\" };\n }\n if (!params.allowlistMatched) {\n return { allowed: false, groupPolicy: params.groupPolicy, reason: \"not_allowlisted\" };\n }\n }\n return { allowed: true, groupPolicy: params.groupPolicy, reason: \"allowed\" };\n}\n\ninterface QQBotChannelConfig extends QQBotAccountConfig {\n /** HTTP/WebSocket User-Agent 追加后缀 */\n userAgentSuffix?: string;\n accounts?: Record<string, QQBotAccountConfig>;\n}\n\n// ============ 群消息策略 ============\n\nconst DEFAULT_GROUP_POLICY: GroupPolicy = \"open\";\n\n/** 群历史缓存条数默认值 */\nconst DEFAULT_GROUP_HISTORY_LIMIT = 20;\n\n/** 单条消息默认处理超时(0 = 不限制) */\nconst DEFAULT_PROCESSING_TIMEOUT_MS = 0;\n\nconst DEFAULT_GROUP_CONFIG: Omit<Required<GroupConfig>, \"prompt\"> = {\n requireMention: true,\n ignoreOtherMentions: false,\n toolPolicy: \"restricted\",\n name: \"\",\n historyLimit: DEFAULT_GROUP_HISTORY_LIMIT,\n};\n\n/** 默认群消息行为 PE(可通过配置覆盖) */\nconst DEFAULT_GROUP_PROMPT = [\n \"若发送者为机器人,仅在对方明确@你提问或请求协助具体任务时,以简洁明了的内容回复,\",\n \"避免与其他机器人产生抢答或多轮无意义对话。\",\n \"在群聊中优先让人类用户的消息得到响应,机器人之间保持协作而非竞争,确保对话有序不刷屏。\",\n].join(\"\");\n\n/** 解析群消息策略 */\nexport function resolveGroupPolicy(cfg: OpenClawConfig, accountId?: string): GroupPolicy {\n const account = resolveQQBotAccount(cfg, accountId);\n return account.config?.groupPolicy ?? DEFAULT_GROUP_POLICY;\n}\n\n/** 解析群白名单(统一转大写) */\nexport function resolveGroupAllowFrom(cfg: OpenClawConfig, accountId?: string): string[] {\n const account = resolveQQBotAccount(cfg, accountId);\n return (account.config?.groupAllowFrom ?? []).map((id) => String(id).trim().toUpperCase());\n}\n\n/** 检查指定群是否被允许(使用标准策略引擎) */\nexport function isGroupAllowed(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): boolean {\n const account = resolveQQBotAccount(cfg, accountId);\n const policy = account.config?.groupPolicy ?? DEFAULT_GROUP_POLICY;\n const allowList = (account.config?.groupAllowFrom ?? []).map((id) => String(id).trim().toUpperCase());\n const allowlistConfigured = allowList.length > 0;\n const allowlistMatched = allowList.some((id) => id === \"*\" || id === groupOpenid.toUpperCase());\n\n return evaluateMatchedGroupAccessForPolicy({\n groupPolicy: policy,\n allowlistConfigured,\n allowlistMatched,\n }).allowed;\n}\n\nexport type ResolvedGroupConfig = Required<GroupConfig>;\n\nexport function resolveGroupConfigFromAccount(account: ResolvedQQBotAccount, groupOpenid: string): ResolvedGroupConfig {\n const groups = account.config?.groups ?? {};\n const wildcardCfg = groups[\"*\"] ?? {};\n const specificCfg = groups[groupOpenid] ?? {};\n const accountDefaultRequireMention = account.config?.defaultRequireMention ?? DEFAULT_GROUP_CONFIG.requireMention;\n\n return {\n requireMention: specificCfg.requireMention ?? wildcardCfg.requireMention ?? accountDefaultRequireMention,\n ignoreOtherMentions: specificCfg.ignoreOtherMentions ?? wildcardCfg.ignoreOtherMentions ?? DEFAULT_GROUP_CONFIG.ignoreOtherMentions,\n toolPolicy: specificCfg.toolPolicy ?? wildcardCfg.toolPolicy ?? DEFAULT_GROUP_CONFIG.toolPolicy,\n name: specificCfg.name ?? wildcardCfg.name ?? DEFAULT_GROUP_CONFIG.name,\n prompt: specificCfg.prompt ?? wildcardCfg.prompt ?? DEFAULT_GROUP_PROMPT,\n historyLimit: specificCfg.historyLimit ?? wildcardCfg.historyLimit ?? DEFAULT_GROUP_CONFIG.historyLimit,\n };\n}\n\nexport function resolveGroupConfig(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): ResolvedGroupConfig {\n return resolveGroupConfigFromAccount(resolveQQBotAccount(cfg, accountId), groupOpenid);\n}\n\n/** 解析群历史消息缓存条数 */\nexport function resolveHistoryLimit(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): number {\n return Math.max(0, resolveGroupConfig(cfg, groupOpenid, accountId).historyLimit);\n}\n\n/** 解析群行为 PE(具体群 > \"*\" > 默认值) */\nexport function resolveGroupPrompt(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): string {\n return resolveGroupConfig(cfg, groupOpenid, accountId).prompt;\n}\n\n/** 解析群是否需要 @机器人才响应 */\nexport function resolveRequireMention(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): boolean {\n return resolveGroupConfig(cfg, groupOpenid, accountId).requireMention;\n}\n\n/** 解析群是否忽略 @了其他人(非 bot)的消息 */\nexport function resolveIgnoreOtherMentions(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): boolean {\n return resolveGroupConfig(cfg, groupOpenid, accountId).ignoreOtherMentions;\n}\n\n/** 解析群工具策略 */\nexport function resolveToolPolicy(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): ToolPolicy {\n return resolveGroupConfig(cfg, groupOpenid, accountId).toolPolicy;\n}\n\n/** 解析群名称(优先配置,fallback 为 openid 前 8 位) */\nexport function resolveGroupName(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): string {\n const name = resolveGroupConfig(cfg, groupOpenid, accountId).name;\n return name || groupOpenid.slice(0, 8);\n}\n\n/**\n * 解析 User-Agent 追加后缀(仅通道级:channels.qqbot.userAgentSuffix)\n */\nexport function resolveUserAgentSuffix(cfg: OpenClawConfig): string {\n const qqbot = cfg.channels?.qqbot as QQBotChannelConfig | undefined;\n return qqbot?.userAgentSuffix ? String(qqbot.userAgentSuffix).trim() : \"\";\n}\n\nfunction normalizeAppId(raw: unknown): string {\n if (raw === null || raw === undefined) return \"\";\n return String(raw).trim();\n}\n\n/**\n * 列出所有 QQBot 账户 ID\n */\nexport function listQQBotAccountIds(cfg: OpenClawConfig): string[] {\n const ids = new Set<string>();\n const qqbot = cfg.channels?.qqbot as QQBotChannelConfig | undefined;\n\n if (qqbot?.appId) {\n ids.add(DEFAULT_ACCOUNT_ID);\n }\n\n if (qqbot?.accounts) {\n for (const accountId of Object.keys(qqbot.accounts)) {\n if (qqbot.accounts[accountId]?.appId) {\n ids.add(accountId);\n }\n }\n }\n\n return Array.from(ids);\n}\n\n/**\n * 获取默认账户 ID\n */\nexport function resolveDefaultQQBotAccountId(cfg: OpenClawConfig): string {\n const qqbot = cfg.channels?.qqbot as QQBotChannelConfig | undefined;\n // 如果有默认账户配置,返回 default\n if (qqbot?.appId) {\n return DEFAULT_ACCOUNT_ID;\n }\n // 否则返回第一个配置的账户\n if (qqbot?.accounts) {\n const ids = Object.keys(qqbot.accounts);\n if (ids.length > 0) {\n return ids[0];\n }\n }\n return DEFAULT_ACCOUNT_ID;\n}\n\n/**\n * 解析单条消息处理超时时间(ms)。\n * 优先级:账户配置 > 环境变量 OPENCLAW_PROCESSING_TIMEOUT_MS > 默认\n * 返回 0 表示不限制超时。\n */\nexport function resolveProcessingTimeoutMs(\n accountConfig?: QQBotAccountConfig,\n): number {\n if (accountConfig?.processingTimeoutMs !== undefined) {\n return accountConfig.processingTimeoutMs;\n }\n const env = process.env.OPENCLAW_PROCESSING_TIMEOUT_MS;\n if (env) {\n const v = Number(env);\n if (!Number.isNaN(v) && v >= 0) return v;\n }\n return DEFAULT_PROCESSING_TIMEOUT_MS;\n}\n\n/**\n * 解析 QQBot 账户配置\n */\nexport function resolveQQBotAccount(\n cfg: OpenClawConfig,\n accountId?: string | null\n): ResolvedQQBotAccount {\n const resolvedAccountId = accountId ?? resolveDefaultQQBotAccountId(cfg);\n const qqbot = cfg.channels?.qqbot as QQBotChannelConfig | undefined;\n\n // 基础配置\n let accountConfig: QQBotAccountConfig = {};\n let appId = \"\";\n let clientSecret = \"\";\n let secretSource: \"config\" | \"file\" | \"env\" | \"none\" = \"none\";\n\n if (resolvedAccountId === DEFAULT_ACCOUNT_ID) {\n // 默认账户从顶层读取(展开所有字段,避免遗漏新增配置项)\n const { accounts: _accounts, ...topLevelConfig } = qqbot ?? {} as QQBotChannelConfig;\n accountConfig = {\n ...topLevelConfig,\n markdownSupport: qqbot?.markdownSupport ?? true,\n };\n appId = normalizeAppId(qqbot?.appId);\n } else {\n // 命名账户从 accounts 读取\n const account = qqbot?.accounts?.[resolvedAccountId];\n accountConfig = account ?? {};\n appId = normalizeAppId(account?.appId);\n }\n\n // 解析 clientSecret\n if (accountConfig.clientSecret) {\n clientSecret = accountConfig.clientSecret;\n secretSource = \"config\";\n } else if (accountConfig.clientSecretFile) {\n // 从文件读取(运行时处理)\n secretSource = \"file\";\n } else if (process.env.QQBOT_CLIENT_SECRET && resolvedAccountId === DEFAULT_ACCOUNT_ID) {\n clientSecret = process.env.QQBOT_CLIENT_SECRET;\n secretSource = \"env\";\n }\n\n // AppId 也可以从环境变量读取\n if (!appId && process.env.QQBOT_APP_ID && resolvedAccountId === DEFAULT_ACCOUNT_ID) {\n appId = normalizeAppId(process.env.QQBOT_APP_ID);\n }\n\n return {\n accountId: resolvedAccountId,\n name: accountConfig.name,\n enabled: accountConfig.enabled !== false,\n appId,\n clientSecret,\n secretSource,\n systemPrompt: accountConfig.systemPrompt,\n markdownSupport: accountConfig.markdownSupport !== false,\n userAgentSuffix: resolveUserAgentSuffix(cfg),\n processingTimeoutMs: resolveProcessingTimeoutMs(accountConfig),\n config: normalizeAccountConfig(accountConfig),\n };\n}\n\n/** 兼容旧版 streaming: boolean 格式 → { mode: \"partial\" | \"off\" },对齐框架 schema */\nfunction normalizeAccountConfig(raw: QQBotAccountConfig): QQBotAccountConfig {\n if (typeof (raw as any).streaming === 'boolean') {\n const { streaming, ...rest } = raw as any;\n return { ...rest, streaming: { mode: streaming ? 'partial' : 'off' } };\n }\n return raw;\n}\n\n/**\n * 应用账户配置\n */\nexport function applyQQBotAccountConfig(\n cfg: OpenClawConfig,\n accountId: string,\n input: { appId?: string; clientSecret?: string; clientSecretFile?: string; name?: string }\n): OpenClawConfig {\n const next = { ...cfg };\n\n if (accountId === DEFAULT_ACCOUNT_ID) {\n // 如果没有设置过 allowFrom,默认设置为 [\"*\"]\n const existingConfig = (next.channels?.qqbot as QQBotChannelConfig) || {};\n const allowFrom = existingConfig.allowFrom ?? [\"*\"];\n \n next.channels = {\n ...next.channels,\n qqbot: {\n ...(next.channels?.qqbot as Record<string, unknown> || {}),\n enabled: true,\n allowFrom,\n ...(input.appId ? { appId: input.appId } : {}),\n ...(input.clientSecret\n ? { clientSecret: input.clientSecret }\n : input.clientSecretFile\n ? { clientSecretFile: input.clientSecretFile }\n : {}),\n ...(input.name ? { name: input.name } : {}),\n },\n };\n } else {\n // 如果没有设置过 allowFrom,默认设置为 [\"*\"]\n const existingAccountConfig = (next.channels?.qqbot as QQBotChannelConfig)?.accounts?.[accountId] || {};\n const allowFrom = existingAccountConfig.allowFrom ?? [\"*\"];\n \n next.channels = {\n ...next.channels,\n qqbot: {\n ...(next.channels?.qqbot as Record<string, unknown> || {}),\n enabled: true,\n accounts: {\n ...((next.channels?.qqbot as QQBotChannelConfig)?.accounts || {}),\n [accountId]: {\n ...((next.channels?.qqbot as QQBotChannelConfig)?.accounts?.[accountId] || {}),\n enabled: true,\n allowFrom,\n ...(input.appId ? { appId: input.appId } : {}),\n ...(input.clientSecret\n ? { clientSecret: input.clientSecret }\n : input.clientSecretFile\n ? { clientSecretFile: input.clientSecretFile }\n : {}),\n ...(input.name ? { name: input.name } : {}),\n },\n },\n },\n };\n }\n\n return next;\n}\n","'use strict';\n\nconst BINARY_TYPES = ['nodebuffer', 'arraybuffer', 'fragments'];\nconst hasBlob = typeof Blob !== 'undefined';\n\nif (hasBlob) BINARY_TYPES.push('blob');\n\nmodule.exports = {\n BINARY_TYPES,\n CLOSE_TIMEOUT: 30000,\n EMPTY_BUFFER: Buffer.alloc(0),\n GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',\n hasBlob,\n kForOnEventAttribute: Symbol('kIsForOnEventAttribute'),\n kListener: Symbol('kListener'),\n kStatusCode: Symbol('status-code'),\n kWebSocket: Symbol('websocket'),\n NOOP: () => {}\n};\n","'use strict';\n\nconst { EMPTY_BUFFER } = require('./constants');\n\nconst FastBuffer = Buffer[Symbol.species];\n\n/**\n * Merges an array of buffers into a new buffer.\n *\n * @param {Buffer[]} list The array of buffers to concat\n * @param {Number} totalLength The total length of buffers in the list\n * @return {Buffer} The resulting buffer\n * @public\n */\nfunction concat(list, totalLength) {\n if (list.length === 0) return EMPTY_BUFFER;\n if (list.length === 1) return list[0];\n\n const target = Buffer.allocUnsafe(totalLength);\n let offset = 0;\n\n for (let i = 0; i < list.length; i++) {\n const buf = list[i];\n target.set(buf, offset);\n offset += buf.length;\n }\n\n if (offset < totalLength) {\n return new FastBuffer(target.buffer, target.byteOffset, offset);\n }\n\n return target;\n}\n\n/**\n * Masks a buffer using the given mask.\n *\n * @param {Buffer} source The buffer to mask\n * @param {Buffer} mask The mask to use\n * @param {Buffer} output The buffer where to store the result\n * @param {Number} offset The offset at which to start writing\n * @param {Number} length The number of bytes to mask.\n * @public\n */\nfunction _mask(source, mask, output, offset, length) {\n for (let i = 0; i < length; i++) {\n output[offset + i] = source[i] ^ mask[i & 3];\n }\n}\n\n/**\n * Unmasks a buffer using the given mask.\n *\n * @param {Buffer} buffer The buffer to unmask\n * @param {Buffer} mask The mask to use\n * @public\n */\nfunction _unmask(buffer, mask) {\n for (let i = 0; i < buffer.length; i++) {\n buffer[i] ^= mask[i & 3];\n }\n}\n\n/**\n * Converts a buffer to an `ArrayBuffer`.\n *\n * @param {Buffer} buf The buffer to convert\n * @return {ArrayBuffer} Converted buffer\n * @public\n */\nfunction toArrayBuffer(buf) {\n if (buf.length === buf.buffer.byteLength) {\n return buf.buffer;\n }\n\n return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length);\n}\n\n/**\n * Converts `data` to a `Buffer`.\n *\n * @param {*} data The data to convert\n * @return {Buffer} The buffer\n * @throws {TypeError}\n * @public\n */\nfunction toBuffer(data) {\n toBuffer.readOnly = true;\n\n if (Buffer.isBuffer(data)) return data;\n\n let buf;\n\n if (data instanceof ArrayBuffer) {\n buf = new FastBuffer(data);\n } else if (ArrayBuffer.isView(data)) {\n buf = new FastBuffer(data.buffer, data.byteOffset, data.byteLength);\n } else {\n buf = Buffer.from(data);\n toBuffer.readOnly = false;\n }\n\n return buf;\n}\n\nmodule.exports = {\n concat,\n mask: _mask,\n toArrayBuffer,\n toBuffer,\n unmask: _unmask\n};\n\n/* istanbul ignore else */\nif (!process.env.WS_NO_BUFFER_UTIL) {\n try {\n const bufferUtil = require('bufferutil');\n\n module.exports.mask = function (source, mask, output, offset, length) {\n if (length < 48) _mask(source, mask, output, offset, length);\n else bufferUtil.mask(source, mask, output, offset, length);\n };\n\n module.exports.unmask = function (buffer, mask) {\n if (buffer.length < 32) _unmask(buffer, mask);\n else bufferUtil.unmask(buffer, mask);\n };\n } catch (e) {\n // Continue regardless of the error.\n }\n}\n","'use strict';\n\nconst kDone = Symbol('kDone');\nconst kRun = Symbol('kRun');\n\n/**\n * A very simple job queue with adjustable concurrency. Adapted from\n * https://github.com/STRML/async-limiter\n */\nclass Limiter {\n /**\n * Creates a new `Limiter`.\n *\n * @param {Number} [concurrency=Infinity] The maximum number of jobs allowed\n * to run concurrently\n */\n constructor(concurrency) {\n this[kDone] = () => {\n this.pending--;\n this[kRun]();\n };\n this.concurrency = concurrency || Infinity;\n this.jobs = [];\n this.pending = 0;\n }\n\n /**\n * Adds a job to the queue.\n *\n * @param {Function} job The job to run\n * @public\n */\n add(job) {\n this.jobs.push(job);\n this[kRun]();\n }\n\n /**\n * Removes a job from the queue and runs it if possible.\n *\n * @private\n */\n [kRun]() {\n if (this.pending === this.concurrency) return;\n\n if (this.jobs.length) {\n const job = this.jobs.shift();\n\n this.pending++;\n job(this[kDone]);\n }\n }\n}\n\nmodule.exports = Limiter;\n","'use strict';\n\nconst zlib = require('zlib');\n\nconst bufferUtil = require('./buffer-util');\nconst Limiter = require('./limiter');\nconst { kStatusCode } = require('./constants');\n\nconst FastBuffer = Buffer[Symbol.species];\nconst TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]);\nconst kPerMessageDeflate = Symbol('permessage-deflate');\nconst kTotalLength = Symbol('total-length');\nconst kCallback = Symbol('callback');\nconst kBuffers = Symbol('buffers');\nconst kError = Symbol('error');\n\n//\n// We limit zlib concurrency, which prevents severe memory fragmentation\n// as documented in https://github.com/nodejs/node/issues/8871#issuecomment-250915913\n// and https://github.com/websockets/ws/issues/1202\n//\n// Intentionally global; it's the global thread pool that's an issue.\n//\nlet zlibLimiter;\n\n/**\n * permessage-deflate implementation.\n */\nclass PerMessageDeflate {\n /**\n * Creates a PerMessageDeflate instance.\n *\n * @param {Object} [options] Configuration options\n * @param {(Boolean|Number)} [options.clientMaxWindowBits] Advertise support\n * for, or request, a custom client window size\n * @param {Boolean} [options.clientNoContextTakeover=false] Advertise/\n * acknowledge disabling of client context takeover\n * @param {Number} [options.concurrencyLimit=10] The number of concurrent\n * calls to zlib\n * @param {Boolean} [options.isServer=false] Create the instance in either\n * server or client mode\n * @param {Number} [options.maxPayload=0] The maximum allowed message length\n * @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the\n * use of a custom server window size\n * @param {Boolean} [options.serverNoContextTakeover=false] Request/accept\n * disabling of server context takeover\n * @param {Number} [options.threshold=1024] Size (in bytes) below which\n * messages should not be compressed if context takeover is disabled\n * @param {Object} [options.zlibDeflateOptions] Options to pass to zlib on\n * deflate\n * @param {Object} [options.zlibInflateOptions] Options to pass to zlib on\n * inflate\n */\n constructor(options) {\n this._options = options || {};\n this._threshold =\n this._options.threshold !== undefined ? this._options.threshold : 1024;\n this._maxPayload = this._options.maxPayload | 0;\n this._isServer = !!this._options.isServer;\n this._deflate = null;\n this._inflate = null;\n\n this.params = null;\n\n if (!zlibLimiter) {\n const concurrency =\n this._options.concurrencyLimit !== undefined\n ? this._options.concurrencyLimit\n : 10;\n zlibLimiter = new Limiter(concurrency);\n }\n }\n\n /**\n * @type {String}\n */\n static get extensionName() {\n return 'permessage-deflate';\n }\n\n /**\n * Create an extension negotiation offer.\n *\n * @return {Object} Extension parameters\n * @public\n */\n offer() {\n const params = {};\n\n if (this._options.serverNoContextTakeover) {\n params.server_no_context_takeover = true;\n }\n if (this._options.clientNoContextTakeover) {\n params.client_no_context_takeover = true;\n }\n if (this._options.serverMaxWindowBits) {\n params.server_max_window_bits = this._options.serverMaxWindowBits;\n }\n if (this._options.clientMaxWindowBits) {\n params.client_max_window_bits = this._options.clientMaxWindowBits;\n } else if (this._options.clientMaxWindowBits == null) {\n params.client_max_window_bits = true;\n }\n\n return params;\n }\n\n /**\n * Accept an extension negotiation offer/response.\n *\n * @param {Array} configurations The extension negotiation offers/reponse\n * @return {Object} Accepted configuration\n * @public\n */\n accept(configurations) {\n configurations = this.normalizeParams(configurations);\n\n this.params = this._isServer\n ? this.acceptAsServer(configurations)\n : this.acceptAsClient(configurations);\n\n return this.params;\n }\n\n /**\n * Releases all resources used by the extension.\n *\n * @public\n */\n cleanup() {\n if (this._inflate) {\n this._inflate.close();\n this._inflate = null;\n }\n\n if (this._deflate) {\n const callback = this._deflate[kCallback];\n\n this._deflate.close();\n this._deflate = null;\n\n if (callback) {\n callback(\n new Error(\n 'The deflate stream was closed while data was being processed'\n )\n );\n }\n }\n }\n\n /**\n * Accept an extension negotiation offer.\n *\n * @param {Array} offers The extension negotiation offers\n * @return {Object} Accepted configuration\n * @private\n */\n acceptAsServer(offers) {\n const opts = this._options;\n const accepted = offers.find((params) => {\n if (\n (opts.serverNoContextTakeover === false &&\n params.server_no_context_takeover) ||\n (params.server_max_window_bits &&\n (opts.serverMaxWindowBits === false ||\n (typeof opts.serverMaxWindowBits === 'number' &&\n opts.serverMaxWindowBits > params.server_max_window_bits))) ||\n (typeof opts.clientMaxWindowBits === 'number' &&\n !params.client_max_window_bits)\n ) {\n return false;\n }\n\n return true;\n });\n\n if (!accepted) {\n throw new Error('None of the extension offers can be accepted');\n }\n\n if (opts.serverNoContextTakeover) {\n accepted.server_no_context_takeover = true;\n }\n if (opts.clientNoContextTakeover) {\n accepted.client_no_context_takeover = true;\n }\n if (typeof opts.serverMaxWindowBits === 'number') {\n accepted.server_max_window_bits = opts.serverMaxWindowBits;\n }\n if (typeof opts.clientMaxWindowBits === 'number') {\n accepted.client_max_window_bits = opts.clientMaxWindowBits;\n } else if (\n accepted.client_max_window_bits === true ||\n opts.clientMaxWindowBits === false\n ) {\n delete accepted.client_max_window_bits;\n }\n\n return accepted;\n }\n\n /**\n * Accept the extension negotiation response.\n *\n * @param {Array} response The extension negotiation response\n * @return {Object} Accepted configuration\n * @private\n */\n acceptAsClient(response) {\n const params = response[0];\n\n if (\n this._options.clientNoContextTakeover === false &&\n params.client_no_context_takeover\n ) {\n throw new Error('Unexpected parameter \"client_no_context_takeover\"');\n }\n\n if (!params.client_max_window_bits) {\n if (typeof this._options.clientMaxWindowBits === 'number') {\n params.client_max_window_bits = this._options.clientMaxWindowBits;\n }\n } else if (\n this._options.clientMaxWindowBits === false ||\n (typeof this._options.clientMaxWindowBits === 'number' &&\n params.client_max_window_bits > this._options.clientMaxWindowBits)\n ) {\n throw new Error(\n 'Unexpected or invalid parameter \"client_max_window_bits\"'\n );\n }\n\n return params;\n }\n\n /**\n * Normalize parameters.\n *\n * @param {Array} configurations The extension negotiation offers/reponse\n * @return {Array} The offers/response with normalized parameters\n * @private\n */\n normalizeParams(configurations) {\n configurations.forEach((params) => {\n Object.keys(params).forEach((key) => {\n let value = params[key];\n\n if (value.length > 1) {\n throw new Error(`Parameter \"${key}\" must have only a single value`);\n }\n\n value = value[0];\n\n if (key === 'client_max_window_bits') {\n if (value !== true) {\n const num = +value;\n if (!Number.isInteger(num) || num < 8 || num > 15) {\n throw new TypeError(\n `Invalid value for parameter \"${key}\": ${value}`\n );\n }\n value = num;\n } else if (!this._isServer) {\n throw new TypeError(\n `Invalid value for parameter \"${key}\": ${value}`\n );\n }\n } else if (key === 'server_max_window_bits') {\n const num = +value;\n if (!Number.isInteger(num) || num < 8 || num > 15) {\n throw new TypeError(\n `Invalid value for parameter \"${key}\": ${value}`\n );\n }\n value = num;\n } else if (\n key === 'client_no_context_takeover' ||\n key === 'server_no_context_takeover'\n ) {\n if (value !== true) {\n throw new TypeError(\n `Invalid value for parameter \"${key}\": ${value}`\n );\n }\n } else {\n throw new Error(`Unknown parameter \"${key}\"`);\n }\n\n params[key] = value;\n });\n });\n\n return configurations;\n }\n\n /**\n * Decompress data. Concurrency limited.\n *\n * @param {Buffer} data Compressed data\n * @param {Boolean} fin Specifies whether or not this is the last fragment\n * @param {Function} callback Callback\n * @public\n */\n decompress(data, fin, callback) {\n zlibLimiter.add((done) => {\n this._decompress(data, fin, (err, result) => {\n done();\n callback(err, result);\n });\n });\n }\n\n /**\n * Compress data. Concurrency limited.\n *\n * @param {(Buffer|String)} data Data to compress\n * @param {Boolean} fin Specifies whether or not this is the last fragment\n * @param {Function} callback Callback\n * @public\n */\n compress(data, fin, callback) {\n zlibLimiter.add((done) => {\n this._compress(data, fin, (err, result) => {\n done();\n callback(err, result);\n });\n });\n }\n\n /**\n * Decompress data.\n *\n * @param {Buffer} data Compressed data\n * @param {Boolean} fin Specifies whether or not this is the last fragment\n * @param {Function} callback Callback\n * @private\n */\n _decompress(data, fin, callback) {\n const endpoint = this._isServer ? 'client' : 'server';\n\n if (!this._inflate) {\n const key = `${endpoint}_max_window_bits`;\n const windowBits =\n typeof this.params[key] !== 'number'\n ? zlib.Z_DEFAULT_WINDOWBITS\n : this.params[key];\n\n this._inflate = zlib.createInflateRaw({\n ...this._options.zlibInflateOptions,\n windowBits\n });\n this._inflate[kPerMessageDeflate] = this;\n this._inflate[kTotalLength] = 0;\n this._inflate[kBuffers] = [];\n this._inflate.on('error', inflateOnError);\n this._inflate.on('data', inflateOnData);\n }\n\n this._inflate[kCallback] = callback;\n\n this._inflate.write(data);\n if (fin) this._inflate.write(TRAILER);\n\n this._inflate.flush(() => {\n const err = this._inflate[kError];\n\n if (err) {\n this._inflate.close();\n this._inflate = null;\n callback(err);\n return;\n }\n\n const data = bufferUtil.concat(\n this._inflate[kBuffers],\n this._inflate[kTotalLength]\n );\n\n if (this._inflate._readableState.endEmitted) {\n this._inflate.close();\n this._inflate = null;\n } else {\n this._inflate[kTotalLength] = 0;\n this._inflate[kBuffers] = [];\n\n if (fin && this.params[`${endpoint}_no_context_takeover`]) {\n this._inflate.reset();\n }\n }\n\n callback(null, data);\n });\n }\n\n /**\n * Compress data.\n *\n * @param {(Buffer|String)} data Data to compress\n * @param {Boolean} fin Specifies whether or not this is the last fragment\n * @param {Function} callback Callback\n * @private\n */\n _compress(data, fin, callback) {\n const endpoint = this._isServer ? 'server' : 'client';\n\n if (!this._deflate) {\n const key = `${endpoint}_max_window_bits`;\n const windowBits =\n typeof this.params[key] !== 'number'\n ? zlib.Z_DEFAULT_WINDOWBITS\n : this.params[key];\n\n this._deflate = zlib.createDeflateRaw({\n ...this._options.zlibDeflateOptions,\n windowBits\n });\n\n this._deflate[kTotalLength] = 0;\n this._deflate[kBuffers] = [];\n\n this._deflate.on('data', deflateOnData);\n }\n\n this._deflate[kCallback] = callback;\n\n this._deflate.write(data);\n this._deflate.flush(zlib.Z_SYNC_FLUSH, () => {\n if (!this._deflate) {\n //\n // The deflate stream was closed while data was being processed.\n //\n return;\n }\n\n let data = bufferUtil.concat(\n this._deflate[kBuffers],\n this._deflate[kTotalLength]\n );\n\n if (fin) {\n data = new FastBuffer(data.buffer, data.byteOffset, data.length - 4);\n }\n\n //\n // Ensure that the callback will not be called again in\n // `PerMessageDeflate#cleanup()`.\n //\n this._deflate[kCallback] = null;\n\n this._deflate[kTotalLength] = 0;\n this._deflate[kBuffers] = [];\n\n if (fin && this.params[`${endpoint}_no_context_takeover`]) {\n this._deflate.reset();\n }\n\n callback(null, data);\n });\n }\n}\n\nmodule.exports = PerMessageDeflate;\n\n/**\n * The listener of the `zlib.DeflateRaw` stream `'data'` event.\n *\n * @param {Buffer} chunk A chunk of data\n * @private\n */\nfunction deflateOnData(chunk) {\n this[kBuffers].push(chunk);\n this[kTotalLength] += chunk.length;\n}\n\n/**\n * The listener of the `zlib.InflateRaw` stream `'data'` event.\n *\n * @param {Buffer} chunk A chunk of data\n * @private\n */\nfunction inflateOnData(chunk) {\n this[kTotalLength] += chunk.length;\n\n if (\n this[kPerMessageDeflate]._maxPayload < 1 ||\n this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload\n ) {\n this[kBuffers].push(chunk);\n return;\n }\n\n this[kError] = new RangeError('Max payload size exceeded');\n this[kError].code = 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH';\n this[kError][kStatusCode] = 1009;\n this.removeListener('data', inflateOnData);\n\n //\n // The choice to employ `zlib.reset()` over `zlib.close()` is dictated by the\n // fact that in Node.js versions prior to 13.10.0, the callback for\n // `zlib.flush()` is not called if `zlib.close()` is used. Utilizing\n // `zlib.reset()` ensures that either the callback is invoked or an error is\n // emitted.\n //\n this.reset();\n}\n\n/**\n * The listener of the `zlib.InflateRaw` stream `'error'` event.\n *\n * @param {Error} err The emitted error\n * @private\n */\nfunction inflateOnError(err) {\n //\n // There is no need to call `Zlib#close()` as the handle is automatically\n // closed when an error is emitted.\n //\n this[kPerMessageDeflate]._inflate = null;\n\n if (this[kError]) {\n this[kCallback](this[kError]);\n return;\n }\n\n err[kStatusCode] = 1007;\n this[kCallback](err);\n}\n","'use strict';\n\nconst { isUtf8 } = require('buffer');\n\nconst { hasBlob } = require('./constants');\n\n//\n// Allowed token characters:\n//\n// '!', '#', '$', '%', '&', ''', '*', '+', '-',\n// '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~'\n//\n// tokenChars[32] === 0 // ' '\n// tokenChars[33] === 1 // '!'\n// tokenChars[34] === 0 // '\"'\n// ...\n//\n// prettier-ignore\nconst tokenChars = [\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31\n 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 47\n 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63\n 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79\n 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 95\n 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111\n 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 // 112 - 127\n];\n\n/**\n * Checks if a status code is allowed in a close frame.\n *\n * @param {Number} code The status code\n * @return {Boolean} `true` if the status code is valid, else `false`\n * @public\n */\nfunction isValidStatusCode(code) {\n return (\n (code >= 1000 &&\n code <= 1014 &&\n code !== 1004 &&\n code !== 1005 &&\n code !== 1006) ||\n (code >= 3000 && code <= 4999)\n );\n}\n\n/**\n * Checks if a given buffer contains only correct UTF-8.\n * Ported from https://www.cl.cam.ac.uk/%7Emgk25/ucs/utf8_check.c by\n * Markus Kuhn.\n *\n * @param {Buffer} buf The buffer to check\n * @return {Boolean} `true` if `buf` contains only correct UTF-8, else `false`\n * @public\n */\nfunction _isValidUTF8(buf) {\n const len = buf.length;\n let i = 0;\n\n while (i < len) {\n if ((buf[i] & 0x80) === 0) {\n // 0xxxxxxx\n i++;\n } else if ((buf[i] & 0xe0) === 0xc0) {\n // 110xxxxx 10xxxxxx\n if (\n i + 1 === len ||\n (buf[i + 1] & 0xc0) !== 0x80 ||\n (buf[i] & 0xfe) === 0xc0 // Overlong\n ) {\n return false;\n }\n\n i += 2;\n } else if ((buf[i] & 0xf0) === 0xe0) {\n // 1110xxxx 10xxxxxx 10xxxxxx\n if (\n i + 2 >= len ||\n (buf[i + 1] & 0xc0) !== 0x80 ||\n (buf[i + 2] & 0xc0) !== 0x80 ||\n (buf[i] === 0xe0 && (buf[i + 1] & 0xe0) === 0x80) || // Overlong\n (buf[i] === 0xed && (buf[i + 1] & 0xe0) === 0xa0) // Surrogate (U+D800 - U+DFFF)\n ) {\n return false;\n }\n\n i += 3;\n } else if ((buf[i] & 0xf8) === 0xf0) {\n // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx\n if (\n i + 3 >= len ||\n (buf[i + 1] & 0xc0) !== 0x80 ||\n (buf[i + 2] & 0xc0) !== 0x80 ||\n (buf[i + 3] & 0xc0) !== 0x80 ||\n (buf[i] === 0xf0 && (buf[i + 1] & 0xf0) === 0x80) || // Overlong\n (buf[i] === 0xf4 && buf[i + 1] > 0x8f) ||\n buf[i] > 0xf4 // > U+10FFFF\n ) {\n return false;\n }\n\n i += 4;\n } else {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Determines whether a value is a `Blob`.\n *\n * @param {*} value The value to be tested\n * @return {Boolean} `true` if `value` is a `Blob`, else `false`\n * @private\n */\nfunction isBlob(value) {\n return (\n hasBlob &&\n typeof value === 'object' &&\n typeof value.arrayBuffer === 'function' &&\n typeof value.type === 'string' &&\n typeof value.stream === 'function' &&\n (value[Symbol.toStringTag] === 'Blob' ||\n value[Symbol.toStringTag] === 'File')\n );\n}\n\nmodule.exports = {\n isBlob,\n isValidStatusCode,\n isValidUTF8: _isValidUTF8,\n tokenChars\n};\n\nif (isUtf8) {\n module.exports.isValidUTF8 = function (buf) {\n return buf.length < 24 ? _isValidUTF8(buf) : isUtf8(buf);\n };\n} /* istanbul ignore else */ else if (!process.env.WS_NO_UTF_8_VALIDATE) {\n try {\n const isValidUTF8 = require('utf-8-validate');\n\n module.exports.isValidUTF8 = function (buf) {\n return buf.length < 32 ? _isValidUTF8(buf) : isValidUTF8(buf);\n };\n } catch (e) {\n // Continue regardless of the error.\n }\n}\n","'use strict';\n\nconst { Writable } = require('stream');\n\nconst PerMessageDeflate = require('./permessage-deflate');\nconst {\n BINARY_TYPES,\n EMPTY_BUFFER,\n kStatusCode,\n kWebSocket\n} = require('./constants');\nconst { concat, toArrayBuffer, unmask } = require('./buffer-util');\nconst { isValidStatusCode, isValidUTF8 } = require('./validation');\n\nconst FastBuffer = Buffer[Symbol.species];\n\nconst GET_INFO = 0;\nconst GET_PAYLOAD_LENGTH_16 = 1;\nconst GET_PAYLOAD_LENGTH_64 = 2;\nconst GET_MASK = 3;\nconst GET_DATA = 4;\nconst INFLATING = 5;\nconst DEFER_EVENT = 6;\n\n/**\n * HyBi Receiver implementation.\n *\n * @extends Writable\n */\nclass Receiver extends Writable {\n /**\n * Creates a Receiver instance.\n *\n * @param {Object} [options] Options object\n * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether\n * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted\n * multiple times in the same tick\n * @param {String} [options.binaryType=nodebuffer] The type for binary data\n * @param {Object} [options.extensions] An object containing the negotiated\n * extensions\n * @param {Boolean} [options.isServer=false] Specifies whether to operate in\n * client or server mode\n * @param {Number} [options.maxBufferedChunks=0] The maximum number of\n * buffered data chunks\n * @param {Number} [options.maxFragments=0] The maximum number of message\n * fragments\n * @param {Number} [options.maxPayload=0] The maximum allowed message length\n * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or\n * not to skip UTF-8 validation for text and close messages\n */\n constructor(options = {}) {\n super();\n\n this._allowSynchronousEvents =\n options.allowSynchronousEvents !== undefined\n ? options.allowSynchronousEvents\n : true;\n this._binaryType = options.binaryType || BINARY_TYPES[0];\n this._extensions = options.extensions || {};\n this._isServer = !!options.isServer;\n this._maxBufferedChunks = options.maxBufferedChunks | 0;\n this._maxFragments = options.maxFragments | 0;\n this._maxPayload = options.maxPayload | 0;\n this._skipUTF8Validation = !!options.skipUTF8Validation;\n this[kWebSocket] = undefined;\n\n this._bufferedBytes = 0;\n this._buffers = [];\n\n this._compressed = false;\n this._payloadLength = 0;\n this._mask = undefined;\n this._fragmented = 0;\n this._masked = false;\n this._fin = false;\n this._opcode = 0;\n\n this._totalPayloadLength = 0;\n this._messageLength = 0;\n this._fragments = [];\n\n this._errored = false;\n this._loop = false;\n this._state = GET_INFO;\n }\n\n /**\n * Implements `Writable.prototype._write()`.\n *\n * @param {Buffer} chunk The chunk of data to write\n * @param {String} encoding The character encoding of `chunk`\n * @param {Function} cb Callback\n * @private\n */\n _write(chunk, encoding, cb) {\n if (this._opcode === 0x08 && this._state == GET_INFO) return cb();\n\n if (\n this._maxBufferedChunks > 0 &&\n this._buffers.length >= this._maxBufferedChunks\n ) {\n cb(\n this.createError(\n RangeError,\n 'Too many buffered chunks',\n false,\n 1008,\n 'WS_ERR_TOO_MANY_BUFFERED_PARTS'\n )\n );\n return;\n }\n\n this._bufferedBytes += chunk.length;\n this._buffers.push(chunk);\n this.startLoop(cb);\n }\n\n /**\n * Consumes `n` bytes from the buffered data.\n *\n * @param {Number} n The number of bytes to consume\n * @return {Buffer} The consumed bytes\n * @private\n */\n consume(n) {\n this._bufferedBytes -= n;\n\n if (n === this._buffers[0].length) return this._buffers.shift();\n\n if (n < this._buffers[0].length) {\n const buf = this._buffers[0];\n this._buffers[0] = new FastBuffer(\n buf.buffer,\n buf.byteOffset + n,\n buf.length - n\n );\n\n return new FastBuffer(buf.buffer, buf.byteOffset, n);\n }\n\n const dst = Buffer.allocUnsafe(n);\n\n do {\n const buf = this._buffers[0];\n const offset = dst.length - n;\n\n if (n >= buf.length) {\n dst.set(this._buffers.shift(), offset);\n } else {\n dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset);\n this._buffers[0] = new FastBuffer(\n buf.buffer,\n buf.byteOffset + n,\n buf.length - n\n );\n }\n\n n -= buf.length;\n } while (n > 0);\n\n return dst;\n }\n\n /**\n * Starts the parsing loop.\n *\n * @param {Function} cb Callback\n * @private\n */\n startLoop(cb) {\n this._loop = true;\n\n do {\n switch (this._state) {\n case GET_INFO:\n this.getInfo(cb);\n break;\n case GET_PAYLOAD_LENGTH_16:\n this.getPayloadLength16(cb);\n break;\n case GET_PAYLOAD_LENGTH_64:\n this.getPayloadLength64(cb);\n break;\n case GET_MASK:\n this.getMask();\n break;\n case GET_DATA:\n this.getData(cb);\n break;\n case INFLATING:\n case DEFER_EVENT:\n this._loop = false;\n return;\n }\n } while (this._loop);\n\n if (!this._errored) cb();\n }\n\n /**\n * Reads the first two bytes of a frame.\n *\n * @param {Function} cb Callback\n * @private\n */\n getInfo(cb) {\n if (this._bufferedBytes < 2) {\n this._loop = false;\n return;\n }\n\n const buf = this.consume(2);\n\n if ((buf[0] & 0x30) !== 0x00) {\n const error = this.createError(\n RangeError,\n 'RSV2 and RSV3 must be clear',\n true,\n 1002,\n 'WS_ERR_UNEXPECTED_RSV_2_3'\n );\n\n cb(error);\n return;\n }\n\n const compressed = (buf[0] & 0x40) === 0x40;\n\n if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {\n const error = this.createError(\n RangeError,\n 'RSV1 must be clear',\n true,\n 1002,\n 'WS_ERR_UNEXPECTED_RSV_1'\n );\n\n cb(error);\n return;\n }\n\n this._fin = (buf[0] & 0x80) === 0x80;\n this._opcode = buf[0] & 0x0f;\n this._payloadLength = buf[1] & 0x7f;\n\n if (this._opcode === 0x00) {\n if (compressed) {\n const error = this.createError(\n RangeError,\n 'RSV1 must be clear',\n true,\n 1002,\n 'WS_ERR_UNEXPECTED_RSV_1'\n );\n\n cb(error);\n return;\n }\n\n if (!this._fragmented) {\n const error = this.createError(\n RangeError,\n 'invalid opcode 0',\n true,\n 1002,\n 'WS_ERR_INVALID_OPCODE'\n );\n\n cb(error);\n return;\n }\n\n this._opcode = this._fragmented;\n } else if (this._opcode === 0x01 || this._opcode === 0x02) {\n if (this._fragmented) {\n const error = this.createError(\n RangeError,\n `invalid opcode ${this._opcode}`,\n true,\n 1002,\n 'WS_ERR_INVALID_OPCODE'\n );\n\n cb(error);\n return;\n }\n\n this._compressed = compressed;\n } else if (this._opcode > 0x07 && this._opcode < 0x0b) {\n if (!this._fin) {\n const error = this.createError(\n RangeError,\n 'FIN must be set',\n true,\n 1002,\n 'WS_ERR_EXPECTED_FIN'\n );\n\n cb(error);\n return;\n }\n\n if (compressed) {\n const error = this.createError(\n RangeError,\n 'RSV1 must be clear',\n true,\n 1002,\n 'WS_ERR_UNEXPECTED_RSV_1'\n );\n\n cb(error);\n return;\n }\n\n if (\n this._payloadLength > 0x7d ||\n (this._opcode === 0x08 && this._payloadLength === 1)\n ) {\n const error = this.createError(\n RangeError,\n `invalid payload length ${this._payloadLength}`,\n true,\n 1002,\n 'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH'\n );\n\n cb(error);\n return;\n }\n } else {\n const error = this.createError(\n RangeError,\n `invalid opcode ${this._opcode}`,\n true,\n 1002,\n 'WS_ERR_INVALID_OPCODE'\n );\n\n cb(error);\n return;\n }\n\n if (!this._fin && !this._fragmented) this._fragmented = this._opcode;\n this._masked = (buf[1] & 0x80) === 0x80;\n\n if (this._isServer) {\n if (!this._masked) {\n const error = this.createError(\n RangeError,\n 'MASK must be set',\n true,\n 1002,\n 'WS_ERR_EXPECTED_MASK'\n );\n\n cb(error);\n return;\n }\n } else if (this._masked) {\n const error = this.createError(\n RangeError,\n 'MASK must be clear',\n true,\n 1002,\n 'WS_ERR_UNEXPECTED_MASK'\n );\n\n cb(error);\n return;\n }\n\n if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16;\n else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64;\n else this.haveLength(cb);\n }\n\n /**\n * Gets extended payload length (7+16).\n *\n * @param {Function} cb Callback\n * @private\n */\n getPayloadLength16(cb) {\n if (this._bufferedBytes < 2) {\n this._loop = false;\n return;\n }\n\n this._payloadLength = this.consume(2).readUInt16BE(0);\n this.haveLength(cb);\n }\n\n /**\n * Gets extended payload length (7+64).\n *\n * @param {Function} cb Callback\n * @private\n */\n getPayloadLength64(cb) {\n if (this._bufferedBytes < 8) {\n this._loop = false;\n return;\n }\n\n const buf = this.consume(8);\n const num = buf.readUInt32BE(0);\n\n //\n // The maximum safe integer in JavaScript is 2^53 - 1. An error is returned\n // if payload length is greater than this number.\n //\n if (num > Math.pow(2, 53 - 32) - 1) {\n const error = this.createError(\n RangeError,\n 'Unsupported WebSocket frame: payload length > 2^53 - 1',\n false,\n 1009,\n 'WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH'\n );\n\n cb(error);\n return;\n }\n\n this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4);\n this.haveLength(cb);\n }\n\n /**\n * Payload length has been read.\n *\n * @param {Function} cb Callback\n * @private\n */\n haveLength(cb) {\n if (this._payloadLength && this._opcode < 0x08) {\n this._totalPayloadLength += this._payloadLength;\n if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) {\n const error = this.createError(\n RangeError,\n 'Max payload size exceeded',\n false,\n 1009,\n 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'\n );\n\n cb(error);\n return;\n }\n }\n\n if (this._masked) this._state = GET_MASK;\n else this._state = GET_DATA;\n }\n\n /**\n * Reads mask bytes.\n *\n * @private\n */\n getMask() {\n if (this._bufferedBytes < 4) {\n this._loop = false;\n return;\n }\n\n this._mask = this.consume(4);\n this._state = GET_DATA;\n }\n\n /**\n * Reads data bytes.\n *\n * @param {Function} cb Callback\n * @private\n */\n getData(cb) {\n let data = EMPTY_BUFFER;\n\n if (this._payloadLength) {\n if (this._bufferedBytes < this._payloadLength) {\n this._loop = false;\n return;\n }\n\n data = this.consume(this._payloadLength);\n\n if (\n this._masked &&\n (this._mask[0] | this._mask[1] | this._mask[2] | this._mask[3]) !== 0\n ) {\n unmask(data, this._mask);\n }\n }\n\n if (this._opcode > 0x07) {\n this.controlMessage(data, cb);\n return;\n }\n\n if (this._compressed) {\n this._state = INFLATING;\n this.decompress(data, cb);\n return;\n }\n\n if (data.length) {\n if (\n this._maxFragments > 0 &&\n this._fragments.length >= this._maxFragments\n ) {\n const error = this.createError(\n RangeError,\n 'Too many message fragments',\n false,\n 1008,\n 'WS_ERR_TOO_MANY_BUFFERED_PARTS'\n );\n\n cb(error);\n return;\n }\n\n //\n // This message is not compressed so its length is the sum of the payload\n // length of all fragments.\n //\n this._messageLength = this._totalPayloadLength;\n this._fragments.push(data);\n }\n\n this.dataMessage(cb);\n }\n\n /**\n * Decompresses data.\n *\n * @param {Buffer} data Compressed data\n * @param {Function} cb Callback\n * @private\n */\n decompress(data, cb) {\n const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];\n\n perMessageDeflate.decompress(data, this._fin, (err, buf) => {\n if (err) return cb(err);\n\n if (buf.length) {\n this._messageLength += buf.length;\n if (this._messageLength > this._maxPayload && this._maxPayload > 0) {\n const error = this.createError(\n RangeError,\n 'Max payload size exceeded',\n false,\n 1009,\n 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'\n );\n\n cb(error);\n return;\n }\n\n if (\n this._maxFragments > 0 &&\n this._fragments.length >= this._maxFragments\n ) {\n const error = this.createError(\n RangeError,\n 'Too many message fragments',\n false,\n 1008,\n 'WS_ERR_TOO_MANY_BUFFERED_PARTS'\n );\n\n cb(error);\n return;\n }\n\n this._fragments.push(buf);\n }\n\n this.dataMessage(cb);\n if (this._state === GET_INFO) this.startLoop(cb);\n });\n }\n\n /**\n * Handles a data message.\n *\n * @param {Function} cb Callback\n * @private\n */\n dataMessage(cb) {\n if (!this._fin) {\n this._state = GET_INFO;\n return;\n }\n\n const messageLength = this._messageLength;\n const fragments = this._fragments;\n\n this._totalPayloadLength = 0;\n this._messageLength = 0;\n this._fragmented = 0;\n this._fragments = [];\n\n if (this._opcode === 2) {\n let data;\n\n if (this._binaryType === 'nodebuffer') {\n data = concat(fragments, messageLength);\n } else if (this._binaryType === 'arraybuffer') {\n data = toArrayBuffer(concat(fragments, messageLength));\n } else if (this._binaryType === 'blob') {\n data = new Blob(fragments);\n } else {\n data = fragments;\n }\n\n if (this._allowSynchronousEvents) {\n this.emit('message', data, true);\n this._state = GET_INFO;\n } else {\n this._state = DEFER_EVENT;\n setImmediate(() => {\n this.emit('message', data, true);\n this._state = GET_INFO;\n this.startLoop(cb);\n });\n }\n } else {\n const buf = concat(fragments, messageLength);\n\n if (!this._skipUTF8Validation && !isValidUTF8(buf)) {\n const error = this.createError(\n Error,\n 'invalid UTF-8 sequence',\n true,\n 1007,\n 'WS_ERR_INVALID_UTF8'\n );\n\n cb(error);\n return;\n }\n\n if (this._state === INFLATING || this._allowSynchronousEvents) {\n this.emit('message', buf, false);\n this._state = GET_INFO;\n } else {\n this._state = DEFER_EVENT;\n setImmediate(() => {\n this.emit('message', buf, false);\n this._state = GET_INFO;\n this.startLoop(cb);\n });\n }\n }\n }\n\n /**\n * Handles a control message.\n *\n * @param {Buffer} data Data to handle\n * @return {(Error|RangeError|undefined)} A possible error\n * @private\n */\n controlMessage(data, cb) {\n if (this._opcode === 0x08) {\n if (data.length === 0) {\n this._loop = false;\n this.emit('conclude', 1005, EMPTY_BUFFER);\n this.end();\n } else {\n const code = data.readUInt16BE(0);\n\n if (!isValidStatusCode(code)) {\n const error = this.createError(\n RangeError,\n `invalid status code ${code}`,\n true,\n 1002,\n 'WS_ERR_INVALID_CLOSE_CODE'\n );\n\n cb(error);\n return;\n }\n\n const buf = new FastBuffer(\n data.buffer,\n data.byteOffset + 2,\n data.length - 2\n );\n\n if (!this._skipUTF8Validation && !isValidUTF8(buf)) {\n const error = this.createError(\n Error,\n 'invalid UTF-8 sequence',\n true,\n 1007,\n 'WS_ERR_INVALID_UTF8'\n );\n\n cb(error);\n return;\n }\n\n this._loop = false;\n this.emit('conclude', code, buf);\n this.end();\n }\n\n this._state = GET_INFO;\n return;\n }\n\n if (this._allowSynchronousEvents) {\n this.emit(this._opcode === 0x09 ? 'ping' : 'pong', data);\n this._state = GET_INFO;\n } else {\n this._state = DEFER_EVENT;\n setImmediate(() => {\n this.emit(this._opcode === 0x09 ? 'ping' : 'pong', data);\n this._state = GET_INFO;\n this.startLoop(cb);\n });\n }\n }\n\n /**\n * Builds an error object.\n *\n * @param {function(new:Error|RangeError)} ErrorCtor The error constructor\n * @param {String} message The error message\n * @param {Boolean} prefix Specifies whether or not to add a default prefix to\n * `message`\n * @param {Number} statusCode The status code\n * @param {String} errorCode The exposed error code\n * @return {(Error|RangeError)} The error\n * @private\n */\n createError(ErrorCtor, message, prefix, statusCode, errorCode) {\n this._loop = false;\n this._errored = true;\n\n const err = new ErrorCtor(\n prefix ? `Invalid WebSocket frame: ${message}` : message\n );\n\n Error.captureStackTrace(err, this.createError);\n err.code = errorCode;\n err[kStatusCode] = statusCode;\n return err;\n }\n}\n\nmodule.exports = Receiver;\n","/* eslint no-unused-vars: [\"error\", { \"varsIgnorePattern\": \"^Duplex\" }] */\n\n'use strict';\n\nconst { Duplex } = require('stream');\nconst { randomFillSync } = require('crypto');\nconst {\n types: { isUint8Array }\n} = require('util');\n\nconst PerMessageDeflate = require('./permessage-deflate');\nconst { EMPTY_BUFFER, kWebSocket, NOOP } = require('./constants');\nconst { isBlob, isValidStatusCode } = require('./validation');\nconst { mask: applyMask, toBuffer } = require('./buffer-util');\n\nconst kByteLength = Symbol('kByteLength');\nconst maskBuffer = Buffer.alloc(4);\nconst RANDOM_POOL_SIZE = 8 * 1024;\nlet randomPool;\nlet randomPoolPointer = RANDOM_POOL_SIZE;\n\nconst DEFAULT = 0;\nconst DEFLATING = 1;\nconst GET_BLOB_DATA = 2;\n\n/**\n * HyBi Sender implementation.\n */\nclass Sender {\n /**\n * Creates a Sender instance.\n *\n * @param {Duplex} socket The connection socket\n * @param {Object} [extensions] An object containing the negotiated extensions\n * @param {Function} [generateMask] The function used to generate the masking\n * key\n */\n constructor(socket, extensions, generateMask) {\n this._extensions = extensions || {};\n\n if (generateMask) {\n this._generateMask = generateMask;\n this._maskBuffer = Buffer.alloc(4);\n }\n\n this._socket = socket;\n\n this._firstFragment = true;\n this._compress = false;\n\n this._bufferedBytes = 0;\n this._queue = [];\n this._state = DEFAULT;\n this.onerror = NOOP;\n this[kWebSocket] = undefined;\n }\n\n /**\n * Frames a piece of data according to the HyBi WebSocket protocol.\n *\n * @param {(Buffer|String)} data The data to frame\n * @param {Object} options Options object\n * @param {Boolean} [options.fin=false] Specifies whether or not to set the\n * FIN bit\n * @param {Function} [options.generateMask] The function used to generate the\n * masking key\n * @param {Boolean} [options.mask=false] Specifies whether or not to mask\n * `data`\n * @param {Buffer} [options.maskBuffer] The buffer used to store the masking\n * key\n * @param {Number} options.opcode The opcode\n * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be\n * modified\n * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the\n * RSV1 bit\n * @return {(Buffer|String)[]} The framed data\n * @public\n */\n static frame(data, options) {\n let mask;\n let merge = false;\n let offset = 2;\n let skipMasking = false;\n\n if (options.mask) {\n mask = options.maskBuffer || maskBuffer;\n\n if (options.generateMask) {\n options.generateMask(mask);\n } else {\n if (randomPoolPointer === RANDOM_POOL_SIZE) {\n /* istanbul ignore else */\n if (randomPool === undefined) {\n //\n // This is lazily initialized because server-sent frames must not\n // be masked so it may never be used.\n //\n randomPool = Buffer.alloc(RANDOM_POOL_SIZE);\n }\n\n randomFillSync(randomPool, 0, RANDOM_POOL_SIZE);\n randomPoolPointer = 0;\n }\n\n mask[0] = randomPool[randomPoolPointer++];\n mask[1] = randomPool[randomPoolPointer++];\n mask[2] = randomPool[randomPoolPointer++];\n mask[3] = randomPool[randomPoolPointer++];\n }\n\n skipMasking = (mask[0] | mask[1] | mask[2] | mask[3]) === 0;\n offset = 6;\n }\n\n let dataLength;\n\n if (typeof data === 'string') {\n if (\n (!options.mask || skipMasking) &&\n options[kByteLength] !== undefined\n ) {\n dataLength = options[kByteLength];\n } else {\n data = Buffer.from(data);\n dataLength = data.length;\n }\n } else {\n dataLength = data.length;\n merge = options.mask && options.readOnly && !skipMasking;\n }\n\n let payloadLength = dataLength;\n\n if (dataLength >= 65536) {\n offset += 8;\n payloadLength = 127;\n } else if (dataLength > 125) {\n offset += 2;\n payloadLength = 126;\n }\n\n const target = Buffer.allocUnsafe(merge ? dataLength + offset : offset);\n\n target[0] = options.fin ? options.opcode | 0x80 : options.opcode;\n if (options.rsv1) target[0] |= 0x40;\n\n target[1] = payloadLength;\n\n if (payloadLength === 126) {\n target.writeUInt16BE(dataLength, 2);\n } else if (payloadLength === 127) {\n target[2] = target[3] = 0;\n target.writeUIntBE(dataLength, 4, 6);\n }\n\n if (!options.mask) return [target, data];\n\n target[1] |= 0x80;\n target[offset - 4] = mask[0];\n target[offset - 3] = mask[1];\n target[offset - 2] = mask[2];\n target[offset - 1] = mask[3];\n\n if (skipMasking) return [target, data];\n\n if (merge) {\n applyMask(data, mask, target, offset, dataLength);\n return [target];\n }\n\n applyMask(data, mask, data, 0, dataLength);\n return [target, data];\n }\n\n /**\n * Sends a close message to the other peer.\n *\n * @param {Number} [code] The status code component of the body\n * @param {(String|Buffer)} [data] The message component of the body\n * @param {Boolean} [mask=false] Specifies whether or not to mask the message\n * @param {Function} [cb] Callback\n * @public\n */\n close(code, data, mask, cb) {\n let buf;\n\n if (code === undefined) {\n buf = EMPTY_BUFFER;\n } else if (typeof code !== 'number' || !isValidStatusCode(code)) {\n throw new TypeError('First argument must be a valid error code number');\n } else if (data === undefined || !data.length) {\n buf = Buffer.allocUnsafe(2);\n buf.writeUInt16BE(code, 0);\n } else {\n const length = Buffer.byteLength(data);\n\n if (length > 123) {\n throw new RangeError('The message must not be greater than 123 bytes');\n }\n\n buf = Buffer.allocUnsafe(2 + length);\n buf.writeUInt16BE(code, 0);\n\n if (typeof data === 'string') {\n buf.write(data, 2);\n } else if (isUint8Array(data)) {\n buf.set(data, 2);\n } else {\n throw new TypeError('Second argument must be a string or a Uint8Array');\n }\n }\n\n const options = {\n [kByteLength]: buf.length,\n fin: true,\n generateMask: this._generateMask,\n mask,\n maskBuffer: this._maskBuffer,\n opcode: 0x08,\n readOnly: false,\n rsv1: false\n };\n\n if (this._state !== DEFAULT) {\n this.enqueue([this.dispatch, buf, false, options, cb]);\n } else {\n this.sendFrame(Sender.frame(buf, options), cb);\n }\n }\n\n /**\n * Sends a ping message to the other peer.\n *\n * @param {*} data The message to send\n * @param {Boolean} [mask=false] Specifies whether or not to mask `data`\n * @param {Function} [cb] Callback\n * @public\n */\n ping(data, mask, cb) {\n let byteLength;\n let readOnly;\n\n if (typeof data === 'string') {\n byteLength = Buffer.byteLength(data);\n readOnly = false;\n } else if (isBlob(data)) {\n byteLength = data.size;\n readOnly = false;\n } else {\n data = toBuffer(data);\n byteLength = data.length;\n readOnly = toBuffer.readOnly;\n }\n\n if (byteLength > 125) {\n throw new RangeError('The data size must not be greater than 125 bytes');\n }\n\n const options = {\n [kByteLength]: byteLength,\n fin: true,\n generateMask: this._generateMask,\n mask,\n maskBuffer: this._maskBuffer,\n opcode: 0x09,\n readOnly,\n rsv1: false\n };\n\n if (isBlob(data)) {\n if (this._state !== DEFAULT) {\n this.enqueue([this.getBlobData, data, false, options, cb]);\n } else {\n this.getBlobData(data, false, options, cb);\n }\n } else if (this._state !== DEFAULT) {\n this.enqueue([this.dispatch, data, false, options, cb]);\n } else {\n this.sendFrame(Sender.frame(data, options), cb);\n }\n }\n\n /**\n * Sends a pong message to the other peer.\n *\n * @param {*} data The message to send\n * @param {Boolean} [mask=false] Specifies whether or not to mask `data`\n * @param {Function} [cb] Callback\n * @public\n */\n pong(data, mask, cb) {\n let byteLength;\n let readOnly;\n\n if (typeof data === 'string') {\n byteLength = Buffer.byteLength(data);\n readOnly = false;\n } else if (isBlob(data)) {\n byteLength = data.size;\n readOnly = false;\n } else {\n data = toBuffer(data);\n byteLength = data.length;\n readOnly = toBuffer.readOnly;\n }\n\n if (byteLength > 125) {\n throw new RangeError('The data size must not be greater than 125 bytes');\n }\n\n const options = {\n [kByteLength]: byteLength,\n fin: true,\n generateMask: this._generateMask,\n mask,\n maskBuffer: this._maskBuffer,\n opcode: 0x0a,\n readOnly,\n rsv1: false\n };\n\n if (isBlob(data)) {\n if (this._state !== DEFAULT) {\n this.enqueue([this.getBlobData, data, false, options, cb]);\n } else {\n this.getBlobData(data, false, options, cb);\n }\n } else if (this._state !== DEFAULT) {\n this.enqueue([this.dispatch, data, false, options, cb]);\n } else {\n this.sendFrame(Sender.frame(data, options), cb);\n }\n }\n\n /**\n * Sends a data message to the other peer.\n *\n * @param {*} data The message to send\n * @param {Object} options Options object\n * @param {Boolean} [options.binary=false] Specifies whether `data` is binary\n * or text\n * @param {Boolean} [options.compress=false] Specifies whether or not to\n * compress `data`\n * @param {Boolean} [options.fin=false] Specifies whether the fragment is the\n * last one\n * @param {Boolean} [options.mask=false] Specifies whether or not to mask\n * `data`\n * @param {Function} [cb] Callback\n * @public\n */\n send(data, options, cb) {\n const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];\n let opcode = options.binary ? 2 : 1;\n let rsv1 = options.compress;\n\n let byteLength;\n let readOnly;\n\n if (typeof data === 'string') {\n byteLength = Buffer.byteLength(data);\n readOnly = false;\n } else if (isBlob(data)) {\n byteLength = data.size;\n readOnly = false;\n } else {\n data = toBuffer(data);\n byteLength = data.length;\n readOnly = toBuffer.readOnly;\n }\n\n if (this._firstFragment) {\n this._firstFragment = false;\n if (\n rsv1 &&\n perMessageDeflate &&\n perMessageDeflate.params[\n perMessageDeflate._isServer\n ? 'server_no_context_takeover'\n : 'client_no_context_takeover'\n ]\n ) {\n rsv1 = byteLength >= perMessageDeflate._threshold;\n }\n this._compress = rsv1;\n } else {\n rsv1 = false;\n opcode = 0;\n }\n\n if (options.fin) this._firstFragment = true;\n\n const opts = {\n [kByteLength]: byteLength,\n fin: options.fin,\n generateMask: this._generateMask,\n mask: options.mask,\n maskBuffer: this._maskBuffer,\n opcode,\n readOnly,\n rsv1\n };\n\n if (isBlob(data)) {\n if (this._state !== DEFAULT) {\n this.enqueue([this.getBlobData, data, this._compress, opts, cb]);\n } else {\n this.getBlobData(data, this._compress, opts, cb);\n }\n } else if (this._state !== DEFAULT) {\n this.enqueue([this.dispatch, data, this._compress, opts, cb]);\n } else {\n this.dispatch(data, this._compress, opts, cb);\n }\n }\n\n /**\n * Gets the contents of a blob as binary data.\n *\n * @param {Blob} blob The blob\n * @param {Boolean} [compress=false] Specifies whether or not to compress\n * the data\n * @param {Object} options Options object\n * @param {Boolean} [options.fin=false] Specifies whether or not to set the\n * FIN bit\n * @param {Function} [options.generateMask] The function used to generate the\n * masking key\n * @param {Boolean} [options.mask=false] Specifies whether or not to mask\n * `data`\n * @param {Buffer} [options.maskBuffer] The buffer used to store the masking\n * key\n * @param {Number} options.opcode The opcode\n * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be\n * modified\n * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the\n * RSV1 bit\n * @param {Function} [cb] Callback\n * @private\n */\n getBlobData(blob, compress, options, cb) {\n this._bufferedBytes += options[kByteLength];\n this._state = GET_BLOB_DATA;\n\n blob\n .arrayBuffer()\n .then((arrayBuffer) => {\n if (this._socket.destroyed) {\n const err = new Error(\n 'The socket was closed while the blob was being read'\n );\n\n //\n // `callCallbacks` is called in the next tick to ensure that errors\n // that might be thrown in the callbacks behave like errors thrown\n // outside the promise chain.\n //\n process.nextTick(callCallbacks, this, err, cb);\n return;\n }\n\n this._bufferedBytes -= options[kByteLength];\n const data = toBuffer(arrayBuffer);\n\n if (!compress) {\n this._state = DEFAULT;\n this.sendFrame(Sender.frame(data, options), cb);\n this.dequeue();\n } else {\n this.dispatch(data, compress, options, cb);\n }\n })\n .catch((err) => {\n //\n // `onError` is called in the next tick for the same reason that\n // `callCallbacks` above is.\n //\n process.nextTick(onError, this, err, cb);\n });\n }\n\n /**\n * Dispatches a message.\n *\n * @param {(Buffer|String)} data The message to send\n * @param {Boolean} [compress=false] Specifies whether or not to compress\n * `data`\n * @param {Object} options Options object\n * @param {Boolean} [options.fin=false] Specifies whether or not to set the\n * FIN bit\n * @param {Function} [options.generateMask] The function used to generate the\n * masking key\n * @param {Boolean} [options.mask=false] Specifies whether or not to mask\n * `data`\n * @param {Buffer} [options.maskBuffer] The buffer used to store the masking\n * key\n * @param {Number} options.opcode The opcode\n * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be\n * modified\n * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the\n * RSV1 bit\n * @param {Function} [cb] Callback\n * @private\n */\n dispatch(data, compress, options, cb) {\n if (!compress) {\n this.sendFrame(Sender.frame(data, options), cb);\n return;\n }\n\n const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];\n\n this._bufferedBytes += options[kByteLength];\n this._state = DEFLATING;\n perMessageDeflate.compress(data, options.fin, (_, buf) => {\n if (this._socket.destroyed) {\n const err = new Error(\n 'The socket was closed while data was being compressed'\n );\n\n callCallbacks(this, err, cb);\n return;\n }\n\n this._bufferedBytes -= options[kByteLength];\n this._state = DEFAULT;\n options.readOnly = false;\n this.sendFrame(Sender.frame(buf, options), cb);\n this.dequeue();\n });\n }\n\n /**\n * Executes queued send operations.\n *\n * @private\n */\n dequeue() {\n while (this._state === DEFAULT && this._queue.length) {\n const params = this._queue.shift();\n\n this._bufferedBytes -= params[3][kByteLength];\n Reflect.apply(params[0], this, params.slice(1));\n }\n }\n\n /**\n * Enqueues a send operation.\n *\n * @param {Array} params Send operation parameters.\n * @private\n */\n enqueue(params) {\n this._bufferedBytes += params[3][kByteLength];\n this._queue.push(params);\n }\n\n /**\n * Sends a frame.\n *\n * @param {(Buffer | String)[]} list The frame to send\n * @param {Function} [cb] Callback\n * @private\n */\n sendFrame(list, cb) {\n if (list.length === 2) {\n this._socket.cork();\n this._socket.write(list[0]);\n this._socket.write(list[1], cb);\n this._socket.uncork();\n } else {\n this._socket.write(list[0], cb);\n }\n }\n}\n\nmodule.exports = Sender;\n\n/**\n * Calls queued callbacks with an error.\n *\n * @param {Sender} sender The `Sender` instance\n * @param {Error} err The error to call the callbacks with\n * @param {Function} [cb] The first callback\n * @private\n */\nfunction callCallbacks(sender, err, cb) {\n if (typeof cb === 'function') cb(err);\n\n for (let i = 0; i < sender._queue.length; i++) {\n const params = sender._queue[i];\n const callback = params[params.length - 1];\n\n if (typeof callback === 'function') callback(err);\n }\n}\n\n/**\n * Handles a `Sender` error.\n *\n * @param {Sender} sender The `Sender` instance\n * @param {Error} err The error\n * @param {Function} [cb] The first pending callback\n * @private\n */\nfunction onError(sender, err, cb) {\n callCallbacks(sender, err, cb);\n sender.onerror(err);\n}\n","'use strict';\n\nconst { kForOnEventAttribute, kListener } = require('./constants');\n\nconst kCode = Symbol('kCode');\nconst kData = Symbol('kData');\nconst kError = Symbol('kError');\nconst kMessage = Symbol('kMessage');\nconst kReason = Symbol('kReason');\nconst kTarget = Symbol('kTarget');\nconst kType = Symbol('kType');\nconst kWasClean = Symbol('kWasClean');\n\n/**\n * Class representing an event.\n */\nclass Event {\n /**\n * Create a new `Event`.\n *\n * @param {String} type The name of the event\n * @throws {TypeError} If the `type` argument is not specified\n */\n constructor(type) {\n this[kTarget] = null;\n this[kType] = type;\n }\n\n /**\n * @type {*}\n */\n get target() {\n return this[kTarget];\n }\n\n /**\n * @type {String}\n */\n get type() {\n return this[kType];\n }\n}\n\nObject.defineProperty(Event.prototype, 'target', { enumerable: true });\nObject.defineProperty(Event.prototype, 'type', { enumerable: true });\n\n/**\n * Class representing a close event.\n *\n * @extends Event\n */\nclass CloseEvent extends Event {\n /**\n * Create a new `CloseEvent`.\n *\n * @param {String} type The name of the event\n * @param {Object} [options] A dictionary object that allows for setting\n * attributes via object members of the same name\n * @param {Number} [options.code=0] The status code explaining why the\n * connection was closed\n * @param {String} [options.reason=''] A human-readable string explaining why\n * the connection was closed\n * @param {Boolean} [options.wasClean=false] Indicates whether or not the\n * connection was cleanly closed\n */\n constructor(type, options = {}) {\n super(type);\n\n this[kCode] = options.code === undefined ? 0 : options.code;\n this[kReason] = options.reason === undefined ? '' : options.reason;\n this[kWasClean] = options.wasClean === undefined ? false : options.wasClean;\n }\n\n /**\n * @type {Number}\n */\n get code() {\n return this[kCode];\n }\n\n /**\n * @type {String}\n */\n get reason() {\n return this[kReason];\n }\n\n /**\n * @type {Boolean}\n */\n get wasClean() {\n return this[kWasClean];\n }\n}\n\nObject.defineProperty(CloseEvent.prototype, 'code', { enumerable: true });\nObject.defineProperty(CloseEvent.prototype, 'reason', { enumerable: true });\nObject.defineProperty(CloseEvent.prototype, 'wasClean', { enumerable: true });\n\n/**\n * Class representing an error event.\n *\n * @extends Event\n */\nclass ErrorEvent extends Event {\n /**\n * Create a new `ErrorEvent`.\n *\n * @param {String} type The name of the event\n * @param {Object} [options] A dictionary object that allows for setting\n * attributes via object members of the same name\n * @param {*} [options.error=null] The error that generated this event\n * @param {String} [options.message=''] The error message\n */\n constructor(type, options = {}) {\n super(type);\n\n this[kError] = options.error === undefined ? null : options.error;\n this[kMessage] = options.message === undefined ? '' : options.message;\n }\n\n /**\n * @type {*}\n */\n get error() {\n return this[kError];\n }\n\n /**\n * @type {String}\n */\n get message() {\n return this[kMessage];\n }\n}\n\nObject.defineProperty(ErrorEvent.prototype, 'error', { enumerable: true });\nObject.defineProperty(ErrorEvent.prototype, 'message', { enumerable: true });\n\n/**\n * Class representing a message event.\n *\n * @extends Event\n */\nclass MessageEvent extends Event {\n /**\n * Create a new `MessageEvent`.\n *\n * @param {String} type The name of the event\n * @param {Object} [options] A dictionary object that allows for setting\n * attributes via object members of the same name\n * @param {*} [options.data=null] The message content\n */\n constructor(type, options = {}) {\n super(type);\n\n this[kData] = options.data === undefined ? null : options.data;\n }\n\n /**\n * @type {*}\n */\n get data() {\n return this[kData];\n }\n}\n\nObject.defineProperty(MessageEvent.prototype, 'data', { enumerable: true });\n\n/**\n * This provides methods for emulating the `EventTarget` interface. It's not\n * meant to be used directly.\n *\n * @mixin\n */\nconst EventTarget = {\n /**\n * Register an event listener.\n *\n * @param {String} type A string representing the event type to listen for\n * @param {(Function|Object)} handler The listener to add\n * @param {Object} [options] An options object specifies characteristics about\n * the event listener\n * @param {Boolean} [options.once=false] A `Boolean` indicating that the\n * listener should be invoked at most once after being added. If `true`,\n * the listener would be automatically removed when invoked.\n * @public\n */\n addEventListener(type, handler, options = {}) {\n for (const listener of this.listeners(type)) {\n if (\n !options[kForOnEventAttribute] &&\n listener[kListener] === handler &&\n !listener[kForOnEventAttribute]\n ) {\n return;\n }\n }\n\n let wrapper;\n\n if (type === 'message') {\n wrapper = function onMessage(data, isBinary) {\n const event = new MessageEvent('message', {\n data: isBinary ? data : data.toString()\n });\n\n event[kTarget] = this;\n callListener(handler, this, event);\n };\n } else if (type === 'close') {\n wrapper = function onClose(code, message) {\n const event = new CloseEvent('close', {\n code,\n reason: message.toString(),\n wasClean: this._closeFrameReceived && this._closeFrameSent\n });\n\n event[kTarget] = this;\n callListener(handler, this, event);\n };\n } else if (type === 'error') {\n wrapper = function onError(error) {\n const event = new ErrorEvent('error', {\n error,\n message: error.message\n });\n\n event[kTarget] = this;\n callListener(handler, this, event);\n };\n } else if (type === 'open') {\n wrapper = function onOpen() {\n const event = new Event('open');\n\n event[kTarget] = this;\n callListener(handler, this, event);\n };\n } else {\n return;\n }\n\n wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute];\n wrapper[kListener] = handler;\n\n if (options.once) {\n this.once(type, wrapper);\n } else {\n this.on(type, wrapper);\n }\n },\n\n /**\n * Remove an event listener.\n *\n * @param {String} type A string representing the event type to remove\n * @param {(Function|Object)} handler The listener to remove\n * @public\n */\n removeEventListener(type, handler) {\n for (const listener of this.listeners(type)) {\n if (listener[kListener] === handler && !listener[kForOnEventAttribute]) {\n this.removeListener(type, listener);\n break;\n }\n }\n }\n};\n\nmodule.exports = {\n CloseEvent,\n ErrorEvent,\n Event,\n EventTarget,\n MessageEvent\n};\n\n/**\n * Call an event listener\n *\n * @param {(Function|Object)} listener The listener to call\n * @param {*} thisArg The value to use as `this`` when calling the listener\n * @param {Event} event The event to pass to the listener\n * @private\n */\nfunction callListener(listener, thisArg, event) {\n if (typeof listener === 'object' && listener.handleEvent) {\n listener.handleEvent.call(listener, event);\n } else {\n listener.call(thisArg, event);\n }\n}\n","'use strict';\n\nconst { tokenChars } = require('./validation');\n\n/**\n * Adds an offer to the map of extension offers or a parameter to the map of\n * parameters.\n *\n * @param {Object} dest The map of extension offers or parameters\n * @param {String} name The extension or parameter name\n * @param {(Object|Boolean|String)} elem The extension parameters or the\n * parameter value\n * @private\n */\nfunction push(dest, name, elem) {\n if (dest[name] === undefined) dest[name] = [elem];\n else dest[name].push(elem);\n}\n\n/**\n * Parses the `Sec-WebSocket-Extensions` header into an object.\n *\n * @param {String} header The field value of the header\n * @return {Object} The parsed object\n * @public\n */\nfunction parse(header) {\n const offers = Object.create(null);\n let params = Object.create(null);\n let mustUnescape = false;\n let isEscaping = false;\n let inQuotes = false;\n let extensionName;\n let paramName;\n let start = -1;\n let code = -1;\n let end = -1;\n let i = 0;\n\n for (; i < header.length; i++) {\n code = header.charCodeAt(i);\n\n if (extensionName === undefined) {\n if (end === -1 && tokenChars[code] === 1) {\n if (start === -1) start = i;\n } else if (\n i !== 0 &&\n (code === 0x20 /* ' ' */ || code === 0x09) /* '\\t' */\n ) {\n if (end === -1 && start !== -1) end = i;\n } else if (code === 0x3b /* ';' */ || code === 0x2c /* ',' */) {\n if (start === -1) {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n\n if (end === -1) end = i;\n const name = header.slice(start, end);\n if (code === 0x2c) {\n push(offers, name, params);\n params = Object.create(null);\n } else {\n extensionName = name;\n }\n\n start = end = -1;\n } else {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n } else if (paramName === undefined) {\n if (end === -1 && tokenChars[code] === 1) {\n if (start === -1) start = i;\n } else if (code === 0x20 || code === 0x09) {\n if (end === -1 && start !== -1) end = i;\n } else if (code === 0x3b || code === 0x2c) {\n if (start === -1) {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n\n if (end === -1) end = i;\n push(params, header.slice(start, end), true);\n if (code === 0x2c) {\n push(offers, extensionName, params);\n params = Object.create(null);\n extensionName = undefined;\n }\n\n start = end = -1;\n } else if (code === 0x3d /* '=' */ && start !== -1 && end === -1) {\n paramName = header.slice(start, i);\n start = end = -1;\n } else {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n } else {\n //\n // The value of a quoted-string after unescaping must conform to the\n // token ABNF, so only token characters are valid.\n // Ref: https://tools.ietf.org/html/rfc6455#section-9.1\n //\n if (isEscaping) {\n if (tokenChars[code] !== 1) {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n if (start === -1) start = i;\n else if (!mustUnescape) mustUnescape = true;\n isEscaping = false;\n } else if (inQuotes) {\n if (tokenChars[code] === 1) {\n if (start === -1) start = i;\n } else if (code === 0x22 /* '\"' */ && start !== -1) {\n inQuotes = false;\n end = i;\n } else if (code === 0x5c /* '\\' */) {\n isEscaping = true;\n } else {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n } else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3d) {\n inQuotes = true;\n } else if (end === -1 && tokenChars[code] === 1) {\n if (start === -1) start = i;\n } else if (start !== -1 && (code === 0x20 || code === 0x09)) {\n if (end === -1) end = i;\n } else if (code === 0x3b || code === 0x2c) {\n if (start === -1) {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n\n if (end === -1) end = i;\n let value = header.slice(start, end);\n if (mustUnescape) {\n value = value.replace(/\\\\/g, '');\n mustUnescape = false;\n }\n push(params, paramName, value);\n if (code === 0x2c) {\n push(offers, extensionName, params);\n params = Object.create(null);\n extensionName = undefined;\n }\n\n paramName = undefined;\n start = end = -1;\n } else {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n }\n }\n\n if (start === -1 || inQuotes || code === 0x20 || code === 0x09) {\n throw new SyntaxError('Unexpected end of input');\n }\n\n if (end === -1) end = i;\n const token = header.slice(start, end);\n if (extensionName === undefined) {\n push(offers, token, params);\n } else {\n if (paramName === undefined) {\n push(params, token, true);\n } else if (mustUnescape) {\n push(params, paramName, token.replace(/\\\\/g, ''));\n } else {\n push(params, paramName, token);\n }\n push(offers, extensionName, params);\n }\n\n return offers;\n}\n\n/**\n * Builds the `Sec-WebSocket-Extensions` header field value.\n *\n * @param {Object} extensions The map of extensions and parameters to format\n * @return {String} A string representing the given object\n * @public\n */\nfunction format(extensions) {\n return Object.keys(extensions)\n .map((extension) => {\n let configurations = extensions[extension];\n if (!Array.isArray(configurations)) configurations = [configurations];\n return configurations\n .map((params) => {\n return [extension]\n .concat(\n Object.keys(params).map((k) => {\n let values = params[k];\n if (!Array.isArray(values)) values = [values];\n return values\n .map((v) => (v === true ? k : `${k}=${v}`))\n .join('; ');\n })\n )\n .join('; ');\n })\n .join(', ');\n })\n .join(', ');\n}\n\nmodule.exports = { format, parse };\n","/* eslint no-unused-vars: [\"error\", { \"varsIgnorePattern\": \"^Duplex|Readable$\", \"caughtErrors\": \"none\" }] */\n\n'use strict';\n\nconst EventEmitter = require('events');\nconst https = require('https');\nconst http = require('http');\nconst net = require('net');\nconst tls = require('tls');\nconst { randomBytes, createHash } = require('crypto');\nconst { Duplex, Readable } = require('stream');\nconst { URL } = require('url');\n\nconst PerMessageDeflate = require('./permessage-deflate');\nconst Receiver = require('./receiver');\nconst Sender = require('./sender');\nconst { isBlob } = require('./validation');\n\nconst {\n BINARY_TYPES,\n CLOSE_TIMEOUT,\n EMPTY_BUFFER,\n GUID,\n kForOnEventAttribute,\n kListener,\n kStatusCode,\n kWebSocket,\n NOOP\n} = require('./constants');\nconst {\n EventTarget: { addEventListener, removeEventListener }\n} = require('./event-target');\nconst { format, parse } = require('./extension');\nconst { toBuffer } = require('./buffer-util');\n\nconst kAborted = Symbol('kAborted');\nconst protocolVersions = [8, 13];\nconst readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];\nconst subprotocolRegex = /^[!#$%&'*+\\-.0-9A-Z^_`|a-z~]+$/;\n\n/**\n * Class representing a WebSocket.\n *\n * @extends EventEmitter\n */\nclass WebSocket extends EventEmitter {\n /**\n * Create a new `WebSocket`.\n *\n * @param {(String|URL)} address The URL to which to connect\n * @param {(String|String[])} [protocols] The subprotocols\n * @param {Object} [options] Connection options\n */\n constructor(address, protocols, options) {\n super();\n\n this._binaryType = BINARY_TYPES[0];\n this._closeCode = 1006;\n this._closeFrameReceived = false;\n this._closeFrameSent = false;\n this._closeMessage = EMPTY_BUFFER;\n this._closeTimer = null;\n this._errorEmitted = false;\n this._extensions = {};\n this._paused = false;\n this._protocol = '';\n this._readyState = WebSocket.CONNECTING;\n this._receiver = null;\n this._sender = null;\n this._socket = null;\n\n if (address !== null) {\n this._bufferedAmount = 0;\n this._isServer = false;\n this._redirects = 0;\n\n if (protocols === undefined) {\n protocols = [];\n } else if (!Array.isArray(protocols)) {\n if (typeof protocols === 'object' && protocols !== null) {\n options = protocols;\n protocols = [];\n } else {\n protocols = [protocols];\n }\n }\n\n initAsClient(this, address, protocols, options);\n } else {\n this._autoPong = options.autoPong;\n this._closeTimeout = options.closeTimeout;\n this._isServer = true;\n }\n }\n\n /**\n * For historical reasons, the custom \"nodebuffer\" type is used by the default\n * instead of \"blob\".\n *\n * @type {String}\n */\n get binaryType() {\n return this._binaryType;\n }\n\n set binaryType(type) {\n if (!BINARY_TYPES.includes(type)) return;\n\n this._binaryType = type;\n\n //\n // Allow to change `binaryType` on the fly.\n //\n if (this._receiver) this._receiver._binaryType = type;\n }\n\n /**\n * @type {Number}\n */\n get bufferedAmount() {\n if (!this._socket) return this._bufferedAmount;\n\n return this._socket._writableState.length + this._sender._bufferedBytes;\n }\n\n /**\n * @type {String}\n */\n get extensions() {\n return Object.keys(this._extensions).join();\n }\n\n /**\n * @type {Boolean}\n */\n get isPaused() {\n return this._paused;\n }\n\n /**\n * @type {Function}\n */\n /* istanbul ignore next */\n get onclose() {\n return null;\n }\n\n /**\n * @type {Function}\n */\n /* istanbul ignore next */\n get onerror() {\n return null;\n }\n\n /**\n * @type {Function}\n */\n /* istanbul ignore next */\n get onopen() {\n return null;\n }\n\n /**\n * @type {Function}\n */\n /* istanbul ignore next */\n get onmessage() {\n return null;\n }\n\n /**\n * @type {String}\n */\n get protocol() {\n return this._protocol;\n }\n\n /**\n * @type {Number}\n */\n get readyState() {\n return this._readyState;\n }\n\n /**\n * @type {String}\n */\n get url() {\n return this._url;\n }\n\n /**\n * Set up the socket and the internal resources.\n *\n * @param {Duplex} socket The network socket between the server and client\n * @param {Buffer} head The first packet of the upgraded stream\n * @param {Object} options Options object\n * @param {Boolean} [options.allowSynchronousEvents=false] Specifies whether\n * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted\n * multiple times in the same tick\n * @param {Function} [options.generateMask] The function used to generate the\n * masking key\n * @param {Number} [options.maxBufferedChunks=0] The maximum number of\n * buffered data chunks\n * @param {Number} [options.maxFragments=0] The maximum number of message\n * fragments\n * @param {Number} [options.maxPayload=0] The maximum allowed message size\n * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or\n * not to skip UTF-8 validation for text and close messages\n * @private\n */\n setSocket(socket, head, options) {\n const receiver = new Receiver({\n allowSynchronousEvents: options.allowSynchronousEvents,\n binaryType: this.binaryType,\n extensions: this._extensions,\n isServer: this._isServer,\n maxBufferedChunks: options.maxBufferedChunks,\n maxFragments: options.maxFragments,\n maxPayload: options.maxPayload,\n skipUTF8Validation: options.skipUTF8Validation\n });\n\n const sender = new Sender(socket, this._extensions, options.generateMask);\n\n this._receiver = receiver;\n this._sender = sender;\n this._socket = socket;\n\n receiver[kWebSocket] = this;\n sender[kWebSocket] = this;\n socket[kWebSocket] = this;\n\n receiver.on('conclude', receiverOnConclude);\n receiver.on('drain', receiverOnDrain);\n receiver.on('error', receiverOnError);\n receiver.on('message', receiverOnMessage);\n receiver.on('ping', receiverOnPing);\n receiver.on('pong', receiverOnPong);\n\n sender.onerror = senderOnError;\n\n //\n // These methods may not be available if `socket` is just a `Duplex`.\n //\n if (socket.setTimeout) socket.setTimeout(0);\n if (socket.setNoDelay) socket.setNoDelay();\n\n if (head.length > 0) socket.unshift(head);\n\n socket.on('close', socketOnClose);\n socket.on('data', socketOnData);\n socket.on('end', socketOnEnd);\n socket.on('error', socketOnError);\n\n this._readyState = WebSocket.OPEN;\n this.emit('open');\n }\n\n /**\n * Emit the `'close'` event.\n *\n * @private\n */\n emitClose() {\n if (!this._socket) {\n this._readyState = WebSocket.CLOSED;\n this.emit('close', this._closeCode, this._closeMessage);\n return;\n }\n\n if (this._extensions[PerMessageDeflate.extensionName]) {\n this._extensions[PerMessageDeflate.extensionName].cleanup();\n }\n\n this._receiver.removeAllListeners();\n this._readyState = WebSocket.CLOSED;\n this.emit('close', this._closeCode, this._closeMessage);\n }\n\n /**\n * Start a closing handshake.\n *\n * +----------+ +-----------+ +----------+\n * - - -|ws.close()|-->|close frame|-->|ws.close()|- - -\n * | +----------+ +-----------+ +----------+ |\n * +----------+ +-----------+ |\n * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING\n * +----------+ +-----------+ |\n * | | | +---+ |\n * +------------------------+-->|fin| - - - -\n * | +---+ | +---+\n * - - - - -|fin|<---------------------+\n * +---+\n *\n * @param {Number} [code] Status code explaining why the connection is closing\n * @param {(String|Buffer)} [data] The reason why the connection is\n * closing\n * @public\n */\n close(code, data) {\n if (this.readyState === WebSocket.CLOSED) return;\n if (this.readyState === WebSocket.CONNECTING) {\n const msg = 'WebSocket was closed before the connection was established';\n abortHandshake(this, this._req, msg);\n return;\n }\n\n if (this.readyState === WebSocket.CLOSING) {\n if (\n this._closeFrameSent &&\n (this._closeFrameReceived || this._receiver._writableState.errorEmitted)\n ) {\n this._socket.end();\n }\n\n return;\n }\n\n this._readyState = WebSocket.CLOSING;\n this._sender.close(code, data, !this._isServer, (err) => {\n //\n // This error is handled by the `'error'` listener on the socket. We only\n // want to know if the close frame has been sent here.\n //\n if (err) return;\n\n this._closeFrameSent = true;\n\n if (\n this._closeFrameReceived ||\n this._receiver._writableState.errorEmitted\n ) {\n this._socket.end();\n }\n });\n\n setCloseTimer(this);\n }\n\n /**\n * Pause the socket.\n *\n * @public\n */\n pause() {\n if (\n this.readyState === WebSocket.CONNECTING ||\n this.readyState === WebSocket.CLOSED\n ) {\n return;\n }\n\n this._paused = true;\n this._socket.pause();\n }\n\n /**\n * Send a ping.\n *\n * @param {*} [data] The data to send\n * @param {Boolean} [mask] Indicates whether or not to mask `data`\n * @param {Function} [cb] Callback which is executed when the ping is sent\n * @public\n */\n ping(data, mask, cb) {\n if (this.readyState === WebSocket.CONNECTING) {\n throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');\n }\n\n if (typeof data === 'function') {\n cb = data;\n data = mask = undefined;\n } else if (typeof mask === 'function') {\n cb = mask;\n mask = undefined;\n }\n\n if (typeof data === 'number') data = data.toString();\n\n if (this.readyState !== WebSocket.OPEN) {\n sendAfterClose(this, data, cb);\n return;\n }\n\n if (mask === undefined) mask = !this._isServer;\n this._sender.ping(data || EMPTY_BUFFER, mask, cb);\n }\n\n /**\n * Send a pong.\n *\n * @param {*} [data] The data to send\n * @param {Boolean} [mask] Indicates whether or not to mask `data`\n * @param {Function} [cb] Callback which is executed when the pong is sent\n * @public\n */\n pong(data, mask, cb) {\n if (this.readyState === WebSocket.CONNECTING) {\n throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');\n }\n\n if (typeof data === 'function') {\n cb = data;\n data = mask = undefined;\n } else if (typeof mask === 'function') {\n cb = mask;\n mask = undefined;\n }\n\n if (typeof data === 'number') data = data.toString();\n\n if (this.readyState !== WebSocket.OPEN) {\n sendAfterClose(this, data, cb);\n return;\n }\n\n if (mask === undefined) mask = !this._isServer;\n this._sender.pong(data || EMPTY_BUFFER, mask, cb);\n }\n\n /**\n * Resume the socket.\n *\n * @public\n */\n resume() {\n if (\n this.readyState === WebSocket.CONNECTING ||\n this.readyState === WebSocket.CLOSED\n ) {\n return;\n }\n\n this._paused = false;\n if (!this._receiver._writableState.needDrain) this._socket.resume();\n }\n\n /**\n * Send a data message.\n *\n * @param {*} data The message to send\n * @param {Object} [options] Options object\n * @param {Boolean} [options.binary] Specifies whether `data` is binary or\n * text\n * @param {Boolean} [options.compress] Specifies whether or not to compress\n * `data`\n * @param {Boolean} [options.fin=true] Specifies whether the fragment is the\n * last one\n * @param {Boolean} [options.mask] Specifies whether or not to mask `data`\n * @param {Function} [cb] Callback which is executed when data is written out\n * @public\n */\n send(data, options, cb) {\n if (this.readyState === WebSocket.CONNECTING) {\n throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');\n }\n\n if (typeof options === 'function') {\n cb = options;\n options = {};\n }\n\n if (typeof data === 'number') data = data.toString();\n\n if (this.readyState !== WebSocket.OPEN) {\n sendAfterClose(this, data, cb);\n return;\n }\n\n const opts = {\n binary: typeof data !== 'string',\n mask: !this._isServer,\n compress: true,\n fin: true,\n ...options\n };\n\n if (!this._extensions[PerMessageDeflate.extensionName]) {\n opts.compress = false;\n }\n\n this._sender.send(data || EMPTY_BUFFER, opts, cb);\n }\n\n /**\n * Forcibly close the connection.\n *\n * @public\n */\n terminate() {\n if (this.readyState === WebSocket.CLOSED) return;\n if (this.readyState === WebSocket.CONNECTING) {\n const msg = 'WebSocket was closed before the connection was established';\n abortHandshake(this, this._req, msg);\n return;\n }\n\n if (this._socket) {\n this._readyState = WebSocket.CLOSING;\n this._socket.destroy();\n }\n }\n}\n\n/**\n * @constant {Number} CONNECTING\n * @memberof WebSocket\n */\nObject.defineProperty(WebSocket, 'CONNECTING', {\n enumerable: true,\n value: readyStates.indexOf('CONNECTING')\n});\n\n/**\n * @constant {Number} CONNECTING\n * @memberof WebSocket.prototype\n */\nObject.defineProperty(WebSocket.prototype, 'CONNECTING', {\n enumerable: true,\n value: readyStates.indexOf('CONNECTING')\n});\n\n/**\n * @constant {Number} OPEN\n * @memberof WebSocket\n */\nObject.defineProperty(WebSocket, 'OPEN', {\n enumerable: true,\n value: readyStates.indexOf('OPEN')\n});\n\n/**\n * @constant {Number} OPEN\n * @memberof WebSocket.prototype\n */\nObject.defineProperty(WebSocket.prototype, 'OPEN', {\n enumerable: true,\n value: readyStates.indexOf('OPEN')\n});\n\n/**\n * @constant {Number} CLOSING\n * @memberof WebSocket\n */\nObject.defineProperty(WebSocket, 'CLOSING', {\n enumerable: true,\n value: readyStates.indexOf('CLOSING')\n});\n\n/**\n * @constant {Number} CLOSING\n * @memberof WebSocket.prototype\n */\nObject.defineProperty(WebSocket.prototype, 'CLOSING', {\n enumerable: true,\n value: readyStates.indexOf('CLOSING')\n});\n\n/**\n * @constant {Number} CLOSED\n * @memberof WebSocket\n */\nObject.defineProperty(WebSocket, 'CLOSED', {\n enumerable: true,\n value: readyStates.indexOf('CLOSED')\n});\n\n/**\n * @constant {Number} CLOSED\n * @memberof WebSocket.prototype\n */\nObject.defineProperty(WebSocket.prototype, 'CLOSED', {\n enumerable: true,\n value: readyStates.indexOf('CLOSED')\n});\n\n[\n 'binaryType',\n 'bufferedAmount',\n 'extensions',\n 'isPaused',\n 'protocol',\n 'readyState',\n 'url'\n].forEach((property) => {\n Object.defineProperty(WebSocket.prototype, property, { enumerable: true });\n});\n\n//\n// Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes.\n// See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface\n//\n['open', 'error', 'close', 'message'].forEach((method) => {\n Object.defineProperty(WebSocket.prototype, `on${method}`, {\n enumerable: true,\n get() {\n for (const listener of this.listeners(method)) {\n if (listener[kForOnEventAttribute]) return listener[kListener];\n }\n\n return null;\n },\n set(handler) {\n for (const listener of this.listeners(method)) {\n if (listener[kForOnEventAttribute]) {\n this.removeListener(method, listener);\n break;\n }\n }\n\n if (typeof handler !== 'function') return;\n\n this.addEventListener(method, handler, {\n [kForOnEventAttribute]: true\n });\n }\n });\n});\n\nWebSocket.prototype.addEventListener = addEventListener;\nWebSocket.prototype.removeEventListener = removeEventListener;\n\nmodule.exports = WebSocket;\n\n/**\n * Initialize a WebSocket client.\n *\n * @param {WebSocket} websocket The client to initialize\n * @param {(String|URL)} address The URL to which to connect\n * @param {Array} protocols The subprotocols\n * @param {Object} [options] Connection options\n * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether any\n * of the `'message'`, `'ping'`, and `'pong'` events can be emitted multiple\n * times in the same tick\n * @param {Boolean} [options.autoPong=true] Specifies whether or not to\n * automatically send a pong in response to a ping\n * @param {Number} [options.closeTimeout=30000] Duration in milliseconds to wait\n * for the closing handshake to finish after `websocket.close()` is called\n * @param {Function} [options.finishRequest] A function which can be used to\n * customize the headers of each http request before it is sent\n * @param {Boolean} [options.followRedirects=false] Whether or not to follow\n * redirects\n * @param {Function} [options.generateMask] The function used to generate the\n * masking key\n * @param {Number} [options.handshakeTimeout] Timeout in milliseconds for the\n * handshake request\n * @param {Number} [options.maxBufferedChunks=1048576] The maximum number of\n * buffered data chunks\n * @param {Number} [options.maxFragments=131072] The maximum number of message\n * fragments\n * @param {Number} [options.maxPayload=104857600] The maximum allowed message\n * size\n * @param {Number} [options.maxRedirects=10] The maximum number of redirects\n * allowed\n * @param {String} [options.origin] Value of the `Origin` or\n * `Sec-WebSocket-Origin` header\n * @param {(Boolean|Object)} [options.perMessageDeflate=true] Enable/disable\n * permessage-deflate\n * @param {Number} [options.protocolVersion=13] Value of the\n * `Sec-WebSocket-Version` header\n * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or\n * not to skip UTF-8 validation for text and close messages\n * @private\n */\nfunction initAsClient(websocket, address, protocols, options) {\n const opts = {\n allowSynchronousEvents: true,\n autoPong: true,\n closeTimeout: CLOSE_TIMEOUT,\n protocolVersion: protocolVersions[1],\n maxBufferedChunks: 1024 * 1024,\n maxFragments: 128 * 1024,\n maxPayload: 100 * 1024 * 1024,\n skipUTF8Validation: false,\n perMessageDeflate: true,\n followRedirects: false,\n maxRedirects: 10,\n ...options,\n socketPath: undefined,\n hostname: undefined,\n protocol: undefined,\n timeout: undefined,\n method: 'GET',\n host: undefined,\n path: undefined,\n port: undefined\n };\n\n websocket._autoPong = opts.autoPong;\n websocket._closeTimeout = opts.closeTimeout;\n\n if (!protocolVersions.includes(opts.protocolVersion)) {\n throw new RangeError(\n `Unsupported protocol version: ${opts.protocolVersion} ` +\n `(supported versions: ${protocolVersions.join(', ')})`\n );\n }\n\n let parsedUrl;\n\n if (address instanceof URL) {\n parsedUrl = address;\n } else {\n try {\n parsedUrl = new URL(address);\n } catch {\n throw new SyntaxError(`Invalid URL: ${address}`);\n }\n }\n\n if (parsedUrl.protocol === 'http:') {\n parsedUrl.protocol = 'ws:';\n } else if (parsedUrl.protocol === 'https:') {\n parsedUrl.protocol = 'wss:';\n }\n\n websocket._url = parsedUrl.href;\n\n const isSecure = parsedUrl.protocol === 'wss:';\n const isIpcUrl = parsedUrl.protocol === 'ws+unix:';\n let invalidUrlMessage;\n\n if (parsedUrl.protocol !== 'ws:' && !isSecure && !isIpcUrl) {\n invalidUrlMessage =\n 'The URL\\'s protocol must be one of \"ws:\", \"wss:\", ' +\n '\"http:\", \"https:\", or \"ws+unix:\"';\n } else if (isIpcUrl && !parsedUrl.pathname) {\n invalidUrlMessage = \"The URL's pathname is empty\";\n } else if (parsedUrl.hash) {\n invalidUrlMessage = 'The URL contains a fragment identifier';\n }\n\n if (invalidUrlMessage) {\n const err = new SyntaxError(invalidUrlMessage);\n\n if (websocket._redirects === 0) {\n throw err;\n } else {\n emitErrorAndClose(websocket, err);\n return;\n }\n }\n\n const defaultPort = isSecure ? 443 : 80;\n const key = randomBytes(16).toString('base64');\n const request = isSecure ? https.request : http.request;\n const protocolSet = new Set();\n let perMessageDeflate;\n\n opts.createConnection =\n opts.createConnection || (isSecure ? tlsConnect : netConnect);\n opts.defaultPort = opts.defaultPort || defaultPort;\n opts.port = parsedUrl.port || defaultPort;\n opts.host = parsedUrl.hostname.startsWith('[')\n ? parsedUrl.hostname.slice(1, -1)\n : parsedUrl.hostname;\n opts.headers = {\n ...opts.headers,\n 'Sec-WebSocket-Version': opts.protocolVersion,\n 'Sec-WebSocket-Key': key,\n Connection: 'Upgrade',\n Upgrade: 'websocket'\n };\n opts.path = parsedUrl.pathname + parsedUrl.search;\n opts.timeout = opts.handshakeTimeout;\n\n if (opts.perMessageDeflate) {\n perMessageDeflate = new PerMessageDeflate({\n ...opts.perMessageDeflate,\n isServer: false,\n maxPayload: opts.maxPayload\n });\n opts.headers['Sec-WebSocket-Extensions'] = format({\n [PerMessageDeflate.extensionName]: perMessageDeflate.offer()\n });\n }\n if (protocols.length) {\n for (const protocol of protocols) {\n if (\n typeof protocol !== 'string' ||\n !subprotocolRegex.test(protocol) ||\n protocolSet.has(protocol)\n ) {\n throw new SyntaxError(\n 'An invalid or duplicated subprotocol was specified'\n );\n }\n\n protocolSet.add(protocol);\n }\n\n opts.headers['Sec-WebSocket-Protocol'] = protocols.join(',');\n }\n if (opts.origin) {\n if (opts.protocolVersion < 13) {\n opts.headers['Sec-WebSocket-Origin'] = opts.origin;\n } else {\n opts.headers.Origin = opts.origin;\n }\n }\n if (parsedUrl.username || parsedUrl.password) {\n opts.auth = `${parsedUrl.username}:${parsedUrl.password}`;\n }\n\n if (isIpcUrl) {\n const parts = opts.path.split(':');\n\n opts.socketPath = parts[0];\n opts.path = parts[1];\n }\n\n let req;\n\n if (opts.followRedirects) {\n if (websocket._redirects === 0) {\n websocket._originalIpc = isIpcUrl;\n websocket._originalSecure = isSecure;\n websocket._originalHostOrSocketPath = isIpcUrl\n ? opts.socketPath\n : parsedUrl.host;\n\n const headers = options && options.headers;\n\n //\n // Shallow copy the user provided options so that headers can be changed\n // without mutating the original object.\n //\n options = { ...options, headers: {} };\n\n if (headers) {\n for (const [key, value] of Object.entries(headers)) {\n options.headers[key.toLowerCase()] = value;\n }\n }\n } else if (websocket.listenerCount('redirect') === 0) {\n const isSameHost = isIpcUrl\n ? websocket._originalIpc\n ? opts.socketPath === websocket._originalHostOrSocketPath\n : false\n : websocket._originalIpc\n ? false\n : parsedUrl.host === websocket._originalHostOrSocketPath;\n\n if (!isSameHost || (websocket._originalSecure && !isSecure)) {\n //\n // Match curl 7.77.0 behavior and drop the following headers. These\n // headers are also dropped when following a redirect to a subdomain.\n //\n delete opts.headers.authorization;\n delete opts.headers.cookie;\n\n if (!isSameHost) delete opts.headers.host;\n\n opts.auth = undefined;\n }\n }\n\n //\n // Match curl 7.77.0 behavior and make the first `Authorization` header win.\n // If the `Authorization` header is set, then there is nothing to do as it\n // will take precedence.\n //\n if (opts.auth && !options.headers.authorization) {\n options.headers.authorization =\n 'Basic ' + Buffer.from(opts.auth).toString('base64');\n }\n\n req = websocket._req = request(opts);\n\n if (websocket._redirects) {\n //\n // Unlike what is done for the `'upgrade'` event, no early exit is\n // triggered here if the user calls `websocket.close()` or\n // `websocket.terminate()` from a listener of the `'redirect'` event. This\n // is because the user can also call `request.destroy()` with an error\n // before calling `websocket.close()` or `websocket.terminate()` and this\n // would result in an error being emitted on the `request` object with no\n // `'error'` event listeners attached.\n //\n websocket.emit('redirect', websocket.url, req);\n }\n } else {\n req = websocket._req = request(opts);\n }\n\n if (opts.timeout) {\n req.on('timeout', () => {\n abortHandshake(websocket, req, 'Opening handshake has timed out');\n });\n }\n\n req.on('error', (err) => {\n if (req === null || req[kAborted]) return;\n\n req = websocket._req = null;\n emitErrorAndClose(websocket, err);\n });\n\n req.on('response', (res) => {\n const location = res.headers.location;\n const statusCode = res.statusCode;\n\n if (\n location &&\n opts.followRedirects &&\n statusCode >= 300 &&\n statusCode < 400\n ) {\n if (++websocket._redirects > opts.maxRedirects) {\n abortHandshake(websocket, req, 'Maximum redirects exceeded');\n return;\n }\n\n req.abort();\n\n let addr;\n\n try {\n addr = new URL(location, address);\n } catch (e) {\n const err = new SyntaxError(`Invalid URL: ${location}`);\n emitErrorAndClose(websocket, err);\n return;\n }\n\n initAsClient(websocket, addr, protocols, options);\n } else if (!websocket.emit('unexpected-response', req, res)) {\n abortHandshake(\n websocket,\n req,\n `Unexpected server response: ${res.statusCode}`\n );\n }\n });\n\n req.on('upgrade', (res, socket, head) => {\n websocket.emit('upgrade', res);\n\n //\n // The user may have closed the connection from a listener of the\n // `'upgrade'` event.\n //\n if (websocket.readyState !== WebSocket.CONNECTING) return;\n\n req = websocket._req = null;\n\n const upgrade = res.headers.upgrade;\n\n if (upgrade === undefined || upgrade.toLowerCase() !== 'websocket') {\n abortHandshake(websocket, socket, 'Invalid Upgrade header');\n return;\n }\n\n const digest = createHash('sha1')\n .update(key + GUID)\n .digest('base64');\n\n if (res.headers['sec-websocket-accept'] !== digest) {\n abortHandshake(websocket, socket, 'Invalid Sec-WebSocket-Accept header');\n return;\n }\n\n const serverProt = res.headers['sec-websocket-protocol'];\n let protError;\n\n if (serverProt !== undefined) {\n if (!protocolSet.size) {\n protError = 'Server sent a subprotocol but none was requested';\n } else if (!protocolSet.has(serverProt)) {\n protError = 'Server sent an invalid subprotocol';\n }\n } else if (protocolSet.size) {\n protError = 'Server sent no subprotocol';\n }\n\n if (protError) {\n abortHandshake(websocket, socket, protError);\n return;\n }\n\n if (serverProt) websocket._protocol = serverProt;\n\n const secWebSocketExtensions = res.headers['sec-websocket-extensions'];\n\n if (secWebSocketExtensions !== undefined) {\n if (!perMessageDeflate) {\n const message =\n 'Server sent a Sec-WebSocket-Extensions header but no extension ' +\n 'was requested';\n abortHandshake(websocket, socket, message);\n return;\n }\n\n let extensions;\n\n try {\n extensions = parse(secWebSocketExtensions);\n } catch (err) {\n const message = 'Invalid Sec-WebSocket-Extensions header';\n abortHandshake(websocket, socket, message);\n return;\n }\n\n const extensionNames = Object.keys(extensions);\n\n if (\n extensionNames.length !== 1 ||\n extensionNames[0] !== PerMessageDeflate.extensionName\n ) {\n const message = 'Server indicated an extension that was not requested';\n abortHandshake(websocket, socket, message);\n return;\n }\n\n try {\n perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);\n } catch (err) {\n const message = 'Invalid Sec-WebSocket-Extensions header';\n abortHandshake(websocket, socket, message);\n return;\n }\n\n websocket._extensions[PerMessageDeflate.extensionName] =\n perMessageDeflate;\n }\n\n websocket.setSocket(socket, head, {\n allowSynchronousEvents: opts.allowSynchronousEvents,\n generateMask: opts.generateMask,\n maxBufferedChunks: opts.maxBufferedChunks,\n maxFragments: opts.maxFragments,\n maxPayload: opts.maxPayload,\n skipUTF8Validation: opts.skipUTF8Validation\n });\n });\n\n if (opts.finishRequest) {\n opts.finishRequest(req, websocket);\n } else {\n req.end();\n }\n}\n\n/**\n * Emit the `'error'` and `'close'` events.\n *\n * @param {WebSocket} websocket The WebSocket instance\n * @param {Error} The error to emit\n * @private\n */\nfunction emitErrorAndClose(websocket, err) {\n websocket._readyState = WebSocket.CLOSING;\n //\n // The following assignment is practically useless and is done only for\n // consistency.\n //\n websocket._errorEmitted = true;\n websocket.emit('error', err);\n websocket.emitClose();\n}\n\n/**\n * Create a `net.Socket` and initiate a connection.\n *\n * @param {Object} options Connection options\n * @return {net.Socket} The newly created socket used to start the connection\n * @private\n */\nfunction netConnect(options) {\n options.path = options.socketPath;\n return net.connect(options);\n}\n\n/**\n * Create a `tls.TLSSocket` and initiate a connection.\n *\n * @param {Object} options Connection options\n * @return {tls.TLSSocket} The newly created socket used to start the connection\n * @private\n */\nfunction tlsConnect(options) {\n options.path = undefined;\n\n if (!options.servername && options.servername !== '') {\n options.servername = net.isIP(options.host) ? '' : options.host;\n }\n\n return tls.connect(options);\n}\n\n/**\n * Abort the handshake and emit an error.\n *\n * @param {WebSocket} websocket The WebSocket instance\n * @param {(http.ClientRequest|net.Socket|tls.Socket)} stream The request to\n * abort or the socket to destroy\n * @param {String} message The error message\n * @private\n */\nfunction abortHandshake(websocket, stream, message) {\n websocket._readyState = WebSocket.CLOSING;\n\n const err = new Error(message);\n Error.captureStackTrace(err, abortHandshake);\n\n if (stream.setHeader) {\n stream[kAborted] = true;\n stream.abort();\n\n if (stream.socket && !stream.socket.destroyed) {\n //\n // On Node.js >= 14.3.0 `request.abort()` does not destroy the socket if\n // called after the request completed. See\n // https://github.com/websockets/ws/issues/1869.\n //\n stream.socket.destroy();\n }\n\n process.nextTick(emitErrorAndClose, websocket, err);\n } else {\n stream.destroy(err);\n stream.once('error', websocket.emit.bind(websocket, 'error'));\n stream.once('close', websocket.emitClose.bind(websocket));\n }\n}\n\n/**\n * Handle cases where the `ping()`, `pong()`, or `send()` methods are called\n * when the `readyState` attribute is `CLOSING` or `CLOSED`.\n *\n * @param {WebSocket} websocket The WebSocket instance\n * @param {*} [data] The data to send\n * @param {Function} [cb] Callback\n * @private\n */\nfunction sendAfterClose(websocket, data, cb) {\n if (data) {\n const length = isBlob(data) ? data.size : toBuffer(data).length;\n\n //\n // The `_bufferedAmount` property is used only when the peer is a client and\n // the opening handshake fails. Under these circumstances, in fact, the\n // `setSocket()` method is not called, so the `_socket` and `_sender`\n // properties are set to `null`.\n //\n if (websocket._socket) websocket._sender._bufferedBytes += length;\n else websocket._bufferedAmount += length;\n }\n\n if (cb) {\n const err = new Error(\n `WebSocket is not open: readyState ${websocket.readyState} ` +\n `(${readyStates[websocket.readyState]})`\n );\n process.nextTick(cb, err);\n }\n}\n\n/**\n * The listener of the `Receiver` `'conclude'` event.\n *\n * @param {Number} code The status code\n * @param {Buffer} reason The reason for closing\n * @private\n */\nfunction receiverOnConclude(code, reason) {\n const websocket = this[kWebSocket];\n\n websocket._closeFrameReceived = true;\n websocket._closeMessage = reason;\n websocket._closeCode = code;\n\n if (websocket._socket[kWebSocket] === undefined) return;\n\n websocket._socket.removeListener('data', socketOnData);\n process.nextTick(resume, websocket._socket);\n\n if (code === 1005) websocket.close();\n else websocket.close(code, reason);\n}\n\n/**\n * The listener of the `Receiver` `'drain'` event.\n *\n * @private\n */\nfunction receiverOnDrain() {\n const websocket = this[kWebSocket];\n\n if (!websocket.isPaused) websocket._socket.resume();\n}\n\n/**\n * The listener of the `Receiver` `'error'` event.\n *\n * @param {(RangeError|Error)} err The emitted error\n * @private\n */\nfunction receiverOnError(err) {\n const websocket = this[kWebSocket];\n\n if (websocket._socket[kWebSocket] !== undefined) {\n websocket._socket.removeListener('data', socketOnData);\n\n //\n // On Node.js < 14.0.0 the `'error'` event is emitted synchronously. See\n // https://github.com/websockets/ws/issues/1940.\n //\n process.nextTick(resume, websocket._socket);\n\n websocket.close(err[kStatusCode]);\n }\n\n if (!websocket._errorEmitted) {\n websocket._errorEmitted = true;\n websocket.emit('error', err);\n }\n}\n\n/**\n * The listener of the `Receiver` `'finish'` event.\n *\n * @private\n */\nfunction receiverOnFinish() {\n this[kWebSocket].emitClose();\n}\n\n/**\n * The listener of the `Receiver` `'message'` event.\n *\n * @param {Buffer|ArrayBuffer|Buffer[])} data The message\n * @param {Boolean} isBinary Specifies whether the message is binary or not\n * @private\n */\nfunction receiverOnMessage(data, isBinary) {\n this[kWebSocket].emit('message', data, isBinary);\n}\n\n/**\n * The listener of the `Receiver` `'ping'` event.\n *\n * @param {Buffer} data The data included in the ping frame\n * @private\n */\nfunction receiverOnPing(data) {\n const websocket = this[kWebSocket];\n\n if (websocket._autoPong) websocket.pong(data, !this._isServer, NOOP);\n websocket.emit('ping', data);\n}\n\n/**\n * The listener of the `Receiver` `'pong'` event.\n *\n * @param {Buffer} data The data included in the pong frame\n * @private\n */\nfunction receiverOnPong(data) {\n this[kWebSocket].emit('pong', data);\n}\n\n/**\n * Resume a readable stream\n *\n * @param {Readable} stream The readable stream\n * @private\n */\nfunction resume(stream) {\n stream.resume();\n}\n\n/**\n * The `Sender` error event handler.\n *\n * @param {Error} The error\n * @private\n */\nfunction senderOnError(err) {\n const websocket = this[kWebSocket];\n\n if (websocket.readyState === WebSocket.CLOSED) return;\n if (websocket.readyState === WebSocket.OPEN) {\n websocket._readyState = WebSocket.CLOSING;\n setCloseTimer(websocket);\n }\n\n //\n // `socket.end()` is used instead of `socket.destroy()` to allow the other\n // peer to finish sending queued data. There is no need to set a timer here\n // because `CLOSING` means that it is already set or not needed.\n //\n this._socket.end();\n\n if (!websocket._errorEmitted) {\n websocket._errorEmitted = true;\n websocket.emit('error', err);\n }\n}\n\n/**\n * Set a timer to destroy the underlying raw socket of a WebSocket.\n *\n * @param {WebSocket} websocket The WebSocket instance\n * @private\n */\nfunction setCloseTimer(websocket) {\n websocket._closeTimer = setTimeout(\n websocket._socket.destroy.bind(websocket._socket),\n websocket._closeTimeout\n );\n}\n\n/**\n * The listener of the socket `'close'` event.\n *\n * @private\n */\nfunction socketOnClose() {\n const websocket = this[kWebSocket];\n\n this.removeListener('close', socketOnClose);\n this.removeListener('data', socketOnData);\n this.removeListener('end', socketOnEnd);\n\n websocket._readyState = WebSocket.CLOSING;\n\n //\n // The close frame might not have been received or the `'end'` event emitted,\n // for example, if the socket was destroyed due to an error. Ensure that the\n // `receiver` stream is closed after writing any remaining buffered data to\n // it. If the readable side of the socket is in flowing mode then there is no\n // buffered data as everything has been already written. If instead, the\n // socket is paused, any possible buffered data will be read as a single\n // chunk.\n //\n if (\n !this._readableState.endEmitted &&\n !websocket._closeFrameReceived &&\n !websocket._receiver._writableState.errorEmitted &&\n this._readableState.length !== 0\n ) {\n const chunk = this.read(this._readableState.length);\n\n websocket._receiver.write(chunk);\n }\n\n websocket._receiver.end();\n\n this[kWebSocket] = undefined;\n\n clearTimeout(websocket._closeTimer);\n\n if (\n websocket._receiver._writableState.finished ||\n websocket._receiver._writableState.errorEmitted\n ) {\n websocket.emitClose();\n } else {\n websocket._receiver.on('error', receiverOnFinish);\n websocket._receiver.on('finish', receiverOnFinish);\n }\n}\n\n/**\n * The listener of the socket `'data'` event.\n *\n * @param {Buffer} chunk A chunk of data\n * @private\n */\nfunction socketOnData(chunk) {\n if (!this[kWebSocket]._receiver.write(chunk)) {\n this.pause();\n }\n}\n\n/**\n * The listener of the socket `'end'` event.\n *\n * @private\n */\nfunction socketOnEnd() {\n const websocket = this[kWebSocket];\n\n websocket._readyState = WebSocket.CLOSING;\n websocket._receiver.end();\n this.end();\n}\n\n/**\n * The listener of the socket `'error'` event.\n *\n * @private\n */\nfunction socketOnError() {\n const websocket = this[kWebSocket];\n\n this.removeListener('error', socketOnError);\n this.on('error', NOOP);\n\n if (websocket) {\n websocket._readyState = WebSocket.CLOSING;\n this.destroy();\n }\n}\n","/* eslint no-unused-vars: [\"error\", { \"varsIgnorePattern\": \"^WebSocket$\" }] */\n'use strict';\n\nconst WebSocket = require('./websocket');\nconst { Duplex } = require('stream');\n\n/**\n * Emits the `'close'` event on a stream.\n *\n * @param {Duplex} stream The stream.\n * @private\n */\nfunction emitClose(stream) {\n stream.emit('close');\n}\n\n/**\n * The listener of the `'end'` event.\n *\n * @private\n */\nfunction duplexOnEnd() {\n if (!this.destroyed && this._writableState.finished) {\n this.destroy();\n }\n}\n\n/**\n * The listener of the `'error'` event.\n *\n * @param {Error} err The error\n * @private\n */\nfunction duplexOnError(err) {\n this.removeListener('error', duplexOnError);\n this.destroy();\n if (this.listenerCount('error') === 0) {\n // Do not suppress the throwing behavior.\n this.emit('error', err);\n }\n}\n\n/**\n * Wraps a `WebSocket` in a duplex stream.\n *\n * @param {WebSocket} ws The `WebSocket` to wrap\n * @param {Object} [options] The options for the `Duplex` constructor\n * @return {Duplex} The duplex stream\n * @public\n */\nfunction createWebSocketStream(ws, options) {\n let terminateOnDestroy = true;\n\n const duplex = new Duplex({\n ...options,\n autoDestroy: false,\n emitClose: false,\n objectMode: false,\n writableObjectMode: false\n });\n\n ws.on('message', function message(msg, isBinary) {\n const data =\n !isBinary && duplex._readableState.objectMode ? msg.toString() : msg;\n\n if (!duplex.push(data)) ws.pause();\n });\n\n ws.once('error', function error(err) {\n if (duplex.destroyed) return;\n\n // Prevent `ws.terminate()` from being called by `duplex._destroy()`.\n //\n // - If the `'error'` event is emitted before the `'open'` event, then\n // `ws.terminate()` is a noop as no socket is assigned.\n // - Otherwise, the error is re-emitted by the listener of the `'error'`\n // event of the `Receiver` object. The listener already closes the\n // connection by calling `ws.close()`. This allows a close frame to be\n // sent to the other peer. If `ws.terminate()` is called right after this,\n // then the close frame might not be sent.\n terminateOnDestroy = false;\n duplex.destroy(err);\n });\n\n ws.once('close', function close() {\n if (duplex.destroyed) return;\n\n duplex.push(null);\n });\n\n duplex._destroy = function (err, callback) {\n if (ws.readyState === ws.CLOSED) {\n callback(err);\n process.nextTick(emitClose, duplex);\n return;\n }\n\n let called = false;\n\n ws.once('error', function error(err) {\n called = true;\n callback(err);\n });\n\n ws.once('close', function close() {\n if (!called) callback(err);\n process.nextTick(emitClose, duplex);\n });\n\n if (terminateOnDestroy) ws.terminate();\n };\n\n duplex._final = function (callback) {\n if (ws.readyState === ws.CONNECTING) {\n ws.once('open', function open() {\n duplex._final(callback);\n });\n return;\n }\n\n // If the value of the `_socket` property is `null` it means that `ws` is a\n // client websocket and the handshake failed. In fact, when this happens, a\n // socket is never assigned to the websocket. Wait for the `'error'` event\n // that will be emitted by the websocket.\n if (ws._socket === null) return;\n\n if (ws._socket._writableState.finished) {\n callback();\n if (duplex._readableState.endEmitted) duplex.destroy();\n } else {\n ws._socket.once('finish', function finish() {\n // `duplex` is not destroyed here because the `'end'` event will be\n // emitted on `duplex` after this `'finish'` event. The EOF signaling\n // `null` chunk is, in fact, pushed when the websocket emits `'close'`.\n callback();\n });\n ws.close();\n }\n };\n\n duplex._read = function () {\n if (ws.isPaused) ws.resume();\n };\n\n duplex._write = function (chunk, encoding, callback) {\n if (ws.readyState === ws.CONNECTING) {\n ws.once('open', function open() {\n duplex._write(chunk, encoding, callback);\n });\n return;\n }\n\n ws.send(chunk, callback);\n };\n\n duplex.on('end', duplexOnEnd);\n duplex.on('error', duplexOnError);\n return duplex;\n}\n\nmodule.exports = createWebSocketStream;\n","'use strict';\n\nconst { tokenChars } = require('./validation');\n\n/**\n * Parses the `Sec-WebSocket-Protocol` header into a set of subprotocol names.\n *\n * @param {String} header The field value of the header\n * @return {Set} The subprotocol names\n * @public\n */\nfunction parse(header) {\n const protocols = new Set();\n let start = -1;\n let end = -1;\n let i = 0;\n\n for (i; i < header.length; i++) {\n const code = header.charCodeAt(i);\n\n if (end === -1 && tokenChars[code] === 1) {\n if (start === -1) start = i;\n } else if (\n i !== 0 &&\n (code === 0x20 /* ' ' */ || code === 0x09) /* '\\t' */\n ) {\n if (end === -1 && start !== -1) end = i;\n } else if (code === 0x2c /* ',' */) {\n if (start === -1) {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n\n if (end === -1) end = i;\n\n const protocol = header.slice(start, end);\n\n if (protocols.has(protocol)) {\n throw new SyntaxError(`The \"${protocol}\" subprotocol is duplicated`);\n }\n\n protocols.add(protocol);\n start = end = -1;\n } else {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n }\n\n if (start === -1 || end !== -1) {\n throw new SyntaxError('Unexpected end of input');\n }\n\n const protocol = header.slice(start, i);\n\n if (protocols.has(protocol)) {\n throw new SyntaxError(`The \"${protocol}\" subprotocol is duplicated`);\n }\n\n protocols.add(protocol);\n return protocols;\n}\n\nmodule.exports = { parse };\n","/* eslint no-unused-vars: [\"error\", { \"varsIgnorePattern\": \"^Duplex$\", \"caughtErrors\": \"none\" }] */\n\n'use strict';\n\nconst EventEmitter = require('events');\nconst http = require('http');\nconst { Duplex } = require('stream');\nconst { createHash } = require('crypto');\n\nconst extension = require('./extension');\nconst PerMessageDeflate = require('./permessage-deflate');\nconst subprotocol = require('./subprotocol');\nconst WebSocket = require('./websocket');\nconst { CLOSE_TIMEOUT, GUID, kWebSocket } = require('./constants');\n\nconst keyRegex = /^[+/0-9A-Za-z]{22}==$/;\n\nconst RUNNING = 0;\nconst CLOSING = 1;\nconst CLOSED = 2;\n\n/**\n * Class representing a WebSocket server.\n *\n * @extends EventEmitter\n */\nclass WebSocketServer extends EventEmitter {\n /**\n * Create a `WebSocketServer` instance.\n *\n * @param {Object} options Configuration options\n * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether\n * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted\n * multiple times in the same tick\n * @param {Boolean} [options.autoPong=true] Specifies whether or not to\n * automatically send a pong in response to a ping\n * @param {Number} [options.backlog=511] The maximum length of the queue of\n * pending connections\n * @param {Boolean} [options.clientTracking=true] Specifies whether or not to\n * track clients\n * @param {Number} [options.closeTimeout=30000] Duration in milliseconds to\n * wait for the closing handshake to finish after `websocket.close()` is\n * called\n * @param {Function} [options.handleProtocols] A hook to handle protocols\n * @param {String} [options.host] The hostname where to bind the server\n * @param {Number} [options.maxBufferedChunks=1048576] The maximum number of\n * buffered data chunks\n * @param {Number} [options.maxFragments=131072] The maximum number of message\n * fragments\n * @param {Number} [options.maxPayload=104857600] The maximum allowed message\n * size\n * @param {Boolean} [options.noServer=false] Enable no server mode\n * @param {String} [options.path] Accept only connections matching this path\n * @param {(Boolean|Object)} [options.perMessageDeflate=false] Enable/disable\n * permessage-deflate\n * @param {Number} [options.port] The port where to bind the server\n * @param {(http.Server|https.Server)} [options.server] A pre-created HTTP/S\n * server to use\n * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or\n * not to skip UTF-8 validation for text and close messages\n * @param {Function} [options.verifyClient] A hook to reject connections\n * @param {Function} [options.WebSocket=WebSocket] Specifies the `WebSocket`\n * class to use. It must be the `WebSocket` class or class that extends it\n * @param {Function} [callback] A listener for the `listening` event\n */\n constructor(options, callback) {\n super();\n\n options = {\n allowSynchronousEvents: true,\n autoPong: true,\n maxBufferedChunks: 1024 * 1024,\n maxFragments: 128 * 1024,\n maxPayload: 100 * 1024 * 1024,\n skipUTF8Validation: false,\n perMessageDeflate: false,\n handleProtocols: null,\n clientTracking: true,\n closeTimeout: CLOSE_TIMEOUT,\n verifyClient: null,\n noServer: false,\n backlog: null, // use default (511 as implemented in net.js)\n server: null,\n host: null,\n path: null,\n port: null,\n WebSocket,\n ...options\n };\n\n if (\n (options.port == null && !options.server && !options.noServer) ||\n (options.port != null && (options.server || options.noServer)) ||\n (options.server && options.noServer)\n ) {\n throw new TypeError(\n 'One and only one of the \"port\", \"server\", or \"noServer\" options ' +\n 'must be specified'\n );\n }\n\n if (options.port != null) {\n this._server = http.createServer((req, res) => {\n const body = http.STATUS_CODES[426];\n\n res.writeHead(426, {\n 'Content-Length': body.length,\n 'Content-Type': 'text/plain'\n });\n res.end(body);\n });\n this._server.listen(\n options.port,\n options.host,\n options.backlog,\n callback\n );\n } else if (options.server) {\n this._server = options.server;\n }\n\n if (this._server) {\n const emitConnection = this.emit.bind(this, 'connection');\n\n this._removeListeners = addListeners(this._server, {\n listening: this.emit.bind(this, 'listening'),\n error: this.emit.bind(this, 'error'),\n upgrade: (req, socket, head) => {\n this.handleUpgrade(req, socket, head, emitConnection);\n }\n });\n }\n\n if (options.perMessageDeflate === true) options.perMessageDeflate = {};\n if (options.clientTracking) {\n this.clients = new Set();\n this._shouldEmitClose = false;\n }\n\n this.options = options;\n this._state = RUNNING;\n }\n\n /**\n * Returns the bound address, the address family name, and port of the server\n * as reported by the operating system if listening on an IP socket.\n * If the server is listening on a pipe or UNIX domain socket, the name is\n * returned as a string.\n *\n * @return {(Object|String|null)} The address of the server\n * @public\n */\n address() {\n if (this.options.noServer) {\n throw new Error('The server is operating in \"noServer\" mode');\n }\n\n if (!this._server) return null;\n return this._server.address();\n }\n\n /**\n * Stop the server from accepting new connections and emit the `'close'` event\n * when all existing connections are closed.\n *\n * @param {Function} [cb] A one-time listener for the `'close'` event\n * @public\n */\n close(cb) {\n if (this._state === CLOSED) {\n if (cb) {\n this.once('close', () => {\n cb(new Error('The server is not running'));\n });\n }\n\n process.nextTick(emitClose, this);\n return;\n }\n\n if (cb) this.once('close', cb);\n\n if (this._state === CLOSING) return;\n this._state = CLOSING;\n\n if (this.options.noServer || this.options.server) {\n if (this._server) {\n this._removeListeners();\n this._removeListeners = this._server = null;\n }\n\n if (this.clients) {\n if (!this.clients.size) {\n process.nextTick(emitClose, this);\n } else {\n this._shouldEmitClose = true;\n }\n } else {\n process.nextTick(emitClose, this);\n }\n } else {\n const server = this._server;\n\n this._removeListeners();\n this._removeListeners = this._server = null;\n\n //\n // The HTTP/S server was created internally. Close it, and rely on its\n // `'close'` event.\n //\n server.close(() => {\n emitClose(this);\n });\n }\n }\n\n /**\n * See if a given request should be handled by this server instance.\n *\n * @param {http.IncomingMessage} req Request object to inspect\n * @return {Boolean} `true` if the request is valid, else `false`\n * @public\n */\n shouldHandle(req) {\n if (this.options.path) {\n const index = req.url.indexOf('?');\n const pathname = index !== -1 ? req.url.slice(0, index) : req.url;\n\n if (pathname !== this.options.path) return false;\n }\n\n return true;\n }\n\n /**\n * Handle a HTTP Upgrade request.\n *\n * @param {http.IncomingMessage} req The request object\n * @param {Duplex} socket The network socket between the server and client\n * @param {Buffer} head The first packet of the upgraded stream\n * @param {Function} cb Callback\n * @public\n */\n handleUpgrade(req, socket, head, cb) {\n socket.on('error', socketOnError);\n\n const key = req.headers['sec-websocket-key'];\n const upgrade = req.headers.upgrade;\n const version = +req.headers['sec-websocket-version'];\n\n if (req.method !== 'GET') {\n const message = 'Invalid HTTP method';\n abortHandshakeOrEmitwsClientError(this, req, socket, 405, message);\n return;\n }\n\n if (upgrade === undefined || upgrade.toLowerCase() !== 'websocket') {\n const message = 'Invalid Upgrade header';\n abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);\n return;\n }\n\n if (key === undefined || !keyRegex.test(key)) {\n const message = 'Missing or invalid Sec-WebSocket-Key header';\n abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);\n return;\n }\n\n if (version !== 13 && version !== 8) {\n const message = 'Missing or invalid Sec-WebSocket-Version header';\n abortHandshakeOrEmitwsClientError(this, req, socket, 400, message, {\n 'Sec-WebSocket-Version': '13, 8'\n });\n return;\n }\n\n if (!this.shouldHandle(req)) {\n abortHandshake(socket, 400);\n return;\n }\n\n const secWebSocketProtocol = req.headers['sec-websocket-protocol'];\n let protocols = new Set();\n\n if (secWebSocketProtocol !== undefined) {\n try {\n protocols = subprotocol.parse(secWebSocketProtocol);\n } catch (err) {\n const message = 'Invalid Sec-WebSocket-Protocol header';\n abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);\n return;\n }\n }\n\n const secWebSocketExtensions = req.headers['sec-websocket-extensions'];\n const extensions = {};\n\n if (\n this.options.perMessageDeflate &&\n secWebSocketExtensions !== undefined\n ) {\n const perMessageDeflate = new PerMessageDeflate({\n ...this.options.perMessageDeflate,\n isServer: true,\n maxPayload: this.options.maxPayload\n });\n\n try {\n const offers = extension.parse(secWebSocketExtensions);\n\n if (offers[PerMessageDeflate.extensionName]) {\n perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);\n extensions[PerMessageDeflate.extensionName] = perMessageDeflate;\n }\n } catch (err) {\n const message =\n 'Invalid or unacceptable Sec-WebSocket-Extensions header';\n abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);\n return;\n }\n }\n\n //\n // Optionally call external client verification handler.\n //\n if (this.options.verifyClient) {\n const info = {\n origin:\n req.headers[`${version === 8 ? 'sec-websocket-origin' : 'origin'}`],\n secure: !!(req.socket.authorized || req.socket.encrypted),\n req\n };\n\n if (this.options.verifyClient.length === 2) {\n this.options.verifyClient(info, (verified, code, message, headers) => {\n if (!verified) {\n return abortHandshake(socket, code || 401, message, headers);\n }\n\n this.completeUpgrade(\n extensions,\n key,\n protocols,\n req,\n socket,\n head,\n cb\n );\n });\n return;\n }\n\n if (!this.options.verifyClient(info)) return abortHandshake(socket, 401);\n }\n\n this.completeUpgrade(extensions, key, protocols, req, socket, head, cb);\n }\n\n /**\n * Upgrade the connection to WebSocket.\n *\n * @param {Object} extensions The accepted extensions\n * @param {String} key The value of the `Sec-WebSocket-Key` header\n * @param {Set} protocols The subprotocols\n * @param {http.IncomingMessage} req The request object\n * @param {Duplex} socket The network socket between the server and client\n * @param {Buffer} head The first packet of the upgraded stream\n * @param {Function} cb Callback\n * @throws {Error} If called more than once with the same socket\n * @private\n */\n completeUpgrade(extensions, key, protocols, req, socket, head, cb) {\n //\n // Destroy the socket if the client has already sent a FIN packet.\n //\n if (!socket.readable || !socket.writable) return socket.destroy();\n\n if (socket[kWebSocket]) {\n throw new Error(\n 'server.handleUpgrade() was called more than once with the same ' +\n 'socket, possibly due to a misconfiguration'\n );\n }\n\n if (this._state > RUNNING) return abortHandshake(socket, 503);\n\n const digest = createHash('sha1')\n .update(key + GUID)\n .digest('base64');\n\n const headers = [\n 'HTTP/1.1 101 Switching Protocols',\n 'Upgrade: websocket',\n 'Connection: Upgrade',\n `Sec-WebSocket-Accept: ${digest}`\n ];\n\n const ws = new this.options.WebSocket(null, undefined, this.options);\n\n if (protocols.size) {\n //\n // Optionally call external protocol selection handler.\n //\n const protocol = this.options.handleProtocols\n ? this.options.handleProtocols(protocols, req)\n : protocols.values().next().value;\n\n if (protocol) {\n headers.push(`Sec-WebSocket-Protocol: ${protocol}`);\n ws._protocol = protocol;\n }\n }\n\n if (extensions[PerMessageDeflate.extensionName]) {\n const params = extensions[PerMessageDeflate.extensionName].params;\n const value = extension.format({\n [PerMessageDeflate.extensionName]: [params]\n });\n headers.push(`Sec-WebSocket-Extensions: ${value}`);\n ws._extensions = extensions;\n }\n\n //\n // Allow external modification/inspection of handshake headers.\n //\n this.emit('headers', headers, req);\n\n socket.write(headers.concat('\\r\\n').join('\\r\\n'));\n socket.removeListener('error', socketOnError);\n\n ws.setSocket(socket, head, {\n allowSynchronousEvents: this.options.allowSynchronousEvents,\n maxBufferedChunks: this.options.maxBufferedChunks,\n maxFragments: this.options.maxFragments,\n maxPayload: this.options.maxPayload,\n skipUTF8Validation: this.options.skipUTF8Validation\n });\n\n if (this.clients) {\n this.clients.add(ws);\n ws.on('close', () => {\n this.clients.delete(ws);\n\n if (this._shouldEmitClose && !this.clients.size) {\n process.nextTick(emitClose, this);\n }\n });\n }\n\n cb(ws, req);\n }\n}\n\nmodule.exports = WebSocketServer;\n\n/**\n * Add event listeners on an `EventEmitter` using a map of <event, listener>\n * pairs.\n *\n * @param {EventEmitter} server The event emitter\n * @param {Object.<String, Function>} map The listeners to add\n * @return {Function} A function that will remove the added listeners when\n * called\n * @private\n */\nfunction addListeners(server, map) {\n for (const event of Object.keys(map)) server.on(event, map[event]);\n\n return function removeListeners() {\n for (const event of Object.keys(map)) {\n server.removeListener(event, map[event]);\n }\n };\n}\n\n/**\n * Emit a `'close'` event on an `EventEmitter`.\n *\n * @param {EventEmitter} server The event emitter\n * @private\n */\nfunction emitClose(server) {\n server._state = CLOSED;\n server.emit('close');\n}\n\n/**\n * Handle socket errors.\n *\n * @private\n */\nfunction socketOnError() {\n this.destroy();\n}\n\n/**\n * Close the connection when preconditions are not fulfilled.\n *\n * @param {Duplex} socket The socket of the upgrade request\n * @param {Number} code The HTTP response status code\n * @param {String} [message] The HTTP response body\n * @param {Object} [headers] Additional HTTP response headers\n * @private\n */\nfunction abortHandshake(socket, code, message, headers) {\n //\n // The socket is writable unless the user destroyed or ended it before calling\n // `server.handleUpgrade()` or in the `verifyClient` function, which is a user\n // error. Handling this does not make much sense as the worst that can happen\n // is that some of the data written by the user might be discarded due to the\n // call to `socket.end()` below, which triggers an `'error'` event that in\n // turn causes the socket to be destroyed.\n //\n message = message || http.STATUS_CODES[code];\n headers = {\n Connection: 'close',\n 'Content-Type': 'text/html',\n 'Content-Length': Buffer.byteLength(message),\n ...headers\n };\n\n socket.once('finish', socket.destroy);\n\n socket.end(\n `HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\\r\\n` +\n Object.keys(headers)\n .map((h) => `${h}: ${headers[h]}`)\n .join('\\r\\n') +\n '\\r\\n\\r\\n' +\n message\n );\n}\n\n/**\n * Emit a `'wsClientError'` event on a `WebSocketServer` if there is at least\n * one listener for it, otherwise call `abortHandshake()`.\n *\n * @param {WebSocketServer} server The WebSocket server\n * @param {http.IncomingMessage} req The request object\n * @param {Duplex} socket The socket of the upgrade request\n * @param {Number} code The HTTP response status code\n * @param {String} message The HTTP response body\n * @param {Object} [headers] The HTTP response headers\n * @private\n */\nfunction abortHandshakeOrEmitwsClientError(\n server,\n req,\n socket,\n code,\n message,\n headers\n) {\n if (server.listenerCount('wsClientError')) {\n const err = new Error(message);\n Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError);\n\n server.emit('wsClientError', err, socket, req);\n } else {\n abortHandshake(socket, code, message, headers);\n }\n}\n","/**\n * Runtime Adapters — 一次 resolve,全程复用。\n *\n * Capability Probe 模式:按候选 API 路径探测,选出第一个可用函数。\n * 对每个 runtime API 维护一个\"候选列表\"(最新在前),未来 API 改名时只需 +1 行。\n *\n * 调用方通过 `resolveRuntimeAdapters(runtime)` 获取适配层对象,\n * 后续所有 dispatch / channel 代码只使用 adapters 上的方法。\n */\n\nimport type { PluginRuntime } from 'openclaw/plugin-sdk';\nimport type { PluginLogger } from '../utils/plugin-logger.js';\n\n// ── 类型 ──\n\nexport interface RuntimeAdapters {\n /** 入站事件处理(自动适配 inbound.run / turn.run) */\n inboundRun: ((params: any) => Promise<any>) | null;\n /** 回复分发(带 block buffer) */\n dispatchReply: ((params: any) => Promise<any>) | null;\n /** Agent 路由解析 */\n resolveAgentRoute: ((params: any) => any) | null;\n /** 构建入站上下文(自动适配 inbound.buildContext / reply.finalizeInboundContext) */\n buildInboundContext: ((params: any) => any) | null;\n /** Session store path 解析 */\n resolveStorePath: ((storeConfig: any, opts: any) => string) | null;\n /** Session 记录 */\n recordInboundSession: ((params: any) => Promise<void>) | null;\n /** 格式化 envelope(自动适配 formatAgentEnvelope / formatInboundEnvelope) */\n formatEnvelope: ((params: any) => string) | null;\n /** 解析 envelope format options */\n resolveEnvelopeFormatOptions: ((cfg: any) => any) | null;\n /** Markdown 文本分块 */\n chunkMarkdownText: ((text: string, limit: number) => string[]) | null;\n /** 远程媒体保存(图片/语音下载) */\n saveRemoteMedia: ((opts: { url: string; subdir?: string; originalFilename?: string }) => Promise<{ path: string } | null>) | null;\n /** 获取当前配置快照 */\n getConfig: (() => any) | null;\n /**\n * 持久化配置变更(高低版本兼容)。\n *\n * 内部优先级:\n * 1. mutateConfigFile(新版 API,原子操作,支持 afterWrite 策略)\n * 2. writeConfigFile(旧版 API,整体覆盖)\n *\n * 调用方只需传入完整 config 对象,不必关心框架版本差异。\n */\n /**\n * 持久化配置变更(高低版本兼容)。\n *\n * 接受一个 mutator 回调,接收当前 config 对象(可变),\n * 可直接修改或返回新对象。适配器负责确保写入和热重载。\n */\n persistConfig: ((mutator: (cfg: any) => any) => Promise<void>) | null;\n /** openclaw 版本 */\n version: string;\n}\n\n// ── Probe 辅助 ──\n\ntype ProbePath = string[];\n\n/**\n * 从 runtime 对象沿路径探测函数。返回 bound function 或 null。\n */\nfunction probeFunction(rt: any, paths: ProbePath[]): ((...args: any[]) => any) | null {\n for (const path of paths) {\n let target = rt;\n let parent = rt;\n for (let i = 0; i < path.length; i++) {\n parent = target;\n target = target?.[path[i]];\n if (target === undefined || target === null) break;\n }\n if (typeof target === 'function') {\n // bind 到 parent(倒数第二层),确保 this 上下文正确\n return target.bind(parent);\n }\n }\n return null;\n}\n\n/**\n * 从 runtime 对象沿路径探测值(非函数也可)。返回值或 null。\n */\nfunction probeValue(rt: any, paths: ProbePath[]): any {\n for (const path of paths) {\n let target = rt;\n for (const key of path) {\n target = target?.[key];\n if (target === undefined || target === null) break;\n }\n if (target !== undefined && target !== null) return target;\n }\n return null;\n}\n\n// ── 主入口 ──\n\n/**\n * 一次性 resolve 所有 runtime adapter。\n *\n * 通常在 gateway ready 后(首条消息到来前)调用一次,结果缓存复用。\n * 返回 null 的字段表示该能力不可用,调用方按需降级或走 fallback。\n */\nexport function resolveRuntimeAdapters(\n rt: PluginRuntime,\n log?: PluginLogger,\n): RuntimeAdapters {\n const version = (rt as any).version ?? 'unknown';\n\n const inboundRun = probeFunction(rt, [\n ['channel', 'inbound', 'run'], // current (2026-05+)\n ['channel', 'turn', 'run'], // legacy (removed 2026-05-27)\n ]);\n\n const dispatchReply = probeFunction(rt, [\n ['channel', 'reply', 'dispatchReplyWithBufferedBlockDispatcher'],\n ]);\n\n const resolveAgentRoute = probeFunction(rt, [\n ['channel', 'routing', 'resolveAgentRoute'],\n ]);\n\n // 构建入站上下文:新 API 优先,低版本 fallback 到 deprecated finalizeInboundContext\n // 两个 API 签名不同,通过 wrapper 统一为 buildInboundContext(params) 接口\n const rawBuildContext = probeFunction(rt, [\n ['channel', 'inbound', 'buildContext'],\n ]);\n const rawFinalizeContext = !rawBuildContext\n ? probeFunction(rt, [['channel', 'reply', 'finalizeInboundContext']])\n : null;\n\n const buildInboundContext: RuntimeAdapters['buildInboundContext'] = rawBuildContext\n ? (params) => rawBuildContext(params)\n : rawFinalizeContext\n ? (params) => {\n // 将统一参数转换为旧 API 的 rawCtxPayload 格式\n const isCommand = params.access?.commands?.authorized ?? false;\n const rawCtx = {\n Body: params.message.body,\n BodyForAgent: params.message.bodyForAgent,\n RawBody: params.message.rawBody,\n CommandBody: params.message.commandBody ?? params.message.rawBody,\n CommandSource: isCommand ? 'text' : undefined,\n CommandTurn: params.command ?? undefined,\n CommandAuthorized: isCommand,\n From: params.from,\n To: params.reply.to,\n SessionKey: params.route.routeSessionKey,\n AccountId: params.route.accountId ?? params.accountId,\n ChatType: params.conversation.kind,\n GroupSystemPrompt: params.conversation.label,\n SenderId: params.sender.id,\n SenderName: params.sender.name,\n Provider: params.provider ?? params.channel,\n Surface: params.surface ?? params.channel,\n MessageSid: params.messageId,\n Timestamp: params.timestamp ?? Date.now(),\n OriginatingChannel: params.channel,\n OriginatingTo: params.reply.originatingTo ?? params.reply.to,\n ...params.extra,\n };\n return rawFinalizeContext(rawCtx);\n }\n : null;\n\n const resolveStorePath = probeFunction(rt, [\n ['channel', 'session', 'resolveStorePath'],\n ]);\n\n const recordInboundSession = probeFunction(rt, [\n ['channel', 'session', 'recordInboundSession'],\n ]);\n\n // 格式化 envelope:新 API 优先,低版本 fallback 到 deprecated formatInboundEnvelope\n const formatEnvelope = probeFunction(rt, [\n ['channel', 'reply', 'formatAgentEnvelope'], // current (2026-06+)\n ['channel', 'reply', 'formatInboundEnvelope'], // deprecated,低版本兼容\n ]);\n\n const resolveEnvelopeFormatOptions = probeFunction(rt, [\n ['channel', 'reply', 'resolveEnvelopeFormatOptions'],\n ]);\n\n const chunkMarkdownText = probeFunction(rt, [\n ['channel', 'text', 'chunkMarkdownText'],\n ]);\n\n const saveRemoteMedia = probeFunction(rt, [\n ['channel', 'media', 'saveRemoteMedia'],\n ]);\n\n const getConfig = probeFunction(rt, [\n ['config', 'current'],\n ]) ?? probeFunction(rt, [\n ['getConfig'],\n ]) ?? probeFunction(rt, [\n ['config', 'loadConfig'],\n ]);\n\n // config 持久化:优先 mutateConfigFile(新版,原子操作),fallback writeConfigFile(旧版)\n const rawMutateConfig = probeFunction(rt, [['config', 'mutateConfigFile']]);\n const rawWriteConfig = probeFunction(rt, [['config', 'writeConfigFile']]);\n\n const persistConfig: RuntimeAdapters['persistConfig'] = rawMutateConfig\n ? async (mutator: (cfg: any) => any) => {\n // 新版 API:mutate 回调接收当前 config,可直接修改或返回新对象\n await rawMutateConfig({\n afterWrite: 'hot-reload',\n mutate: mutator,\n });\n }\n : rawWriteConfig && getConfig\n ? async (mutator: (cfg: any) => any) => {\n // 旧版 API:先读取当前 config → 应用 mutator → 整体写入\n const current = JSON.parse(JSON.stringify(getConfig()));\n mutator(current);\n await rawWriteConfig(current);\n }\n : null;\n\n // 日志汇总\n const resolved = [\n inboundRun && 'inboundRun',\n dispatchReply && 'dispatchReply',\n resolveAgentRoute && 'resolveAgentRoute',\n buildInboundContext && 'buildInboundContext',\n resolveStorePath && 'resolveStorePath',\n recordInboundSession && 'recordInboundSession',\n formatEnvelope && 'formatEnvelope',\n chunkMarkdownText && 'chunkMarkdownText',\n saveRemoteMedia && 'saveRemoteMedia',\n getConfig && 'getConfig',\n persistConfig && `persistConfig(${rawMutateConfig ? 'mutate' : 'write'})`,\n ].filter(Boolean);\n\n log?.info(\n `[qqbot:adapter] openclaw=${version} resolved ${resolved.length} adapters: ${resolved.join(', ')}`,\n );\n\n return {\n inboundRun,\n dispatchReply,\n resolveAgentRoute,\n buildInboundContext,\n resolveStorePath,\n recordInboundSession,\n formatEnvelope,\n resolveEnvelopeFormatOptions,\n chunkMarkdownText,\n saveRemoteMedia,\n getConfig,\n persistConfig,\n version,\n };\n}\n\n// ── 全局缓存(所有消费者共享同一份 adapters) ──\n\nlet _cachedAdapters: RuntimeAdapters | null = null;\nlet _cachedRuntimeRef: WeakRef<PluginRuntime> | null = null;\n\n/**\n * 获取 RuntimeAdapters 的全局缓存版本。\n *\n * - 自动基于 runtime 实例的 WeakRef 判断是否需要重新 resolve\n * - 如果 runtime 被替换(框架热更新、二次 register),自动刷新\n * - 所有消费者(dispatch / channel / middleware)共用一份,避免重复 probe\n *\n * @param rt 当前 PluginRuntime 实例\n * @param log 可选日志(仅在首次 resolve 或 runtime 变更时输出)\n */\nexport function getAdapters(\n rt: PluginRuntime,\n log?: PluginLogger,\n): RuntimeAdapters {\n const cached = _cachedRuntimeRef?.deref();\n if (cached === rt && _cachedAdapters) {\n return _cachedAdapters;\n }\n // runtime 引用变化(首次 / 热更新)→ 重新 resolve\n _cachedAdapters = resolveRuntimeAdapters(rt, log);\n _cachedRuntimeRef = new WeakRef(rt);\n return _cachedAdapters;\n}\n\n/**\n * 持久化配置(auth.login 场景,兼容高低版本)。\n *\n * 探测顺序:\n * 1. config.mutateConfigFile — 新版原子操作\n * 2. config.writeConfigFile — 旧版整体覆盖\n * 3. writeConfigFile — 更旧的顶层 API\n */\nexport async function persistAuthConfig(\n runtime: Record<string, unknown>,\n cfg: Record<string, unknown>,\n afterWrite = 'restart',\n): Promise<void> {\n const config: Record<string, unknown> | undefined = runtime.config as any;\n\n if (typeof config?.mutateConfigFile === 'function') {\n await (config.mutateConfigFile as Function)({\n mutate: () => cfg,\n afterWrite,\n });\n return;\n }\n if (typeof config?.writeConfigFile === 'function') {\n await (config.writeConfigFile as Function)(cfg);\n return;\n }\n if (typeof runtime.writeConfigFile === 'function') {\n await (runtime.writeConfigFile as Function)(cfg);\n return;\n }\n\n // 最后兜底:裸写文件\n const { homedir } = await import('node:os');\n const { join } = await import('node:path');\n const { writeFileSync } = await import('node:fs');\n writeFileSync(join(homedir(), '.openclaw', 'openclaw.json'), JSON.stringify(cfg, null, 2) + '\\n', 'utf-8');\n}\n\n/**\n * 强制清除 adapter 缓存(仅测试用)\n */\nexport function _resetAdaptersCache(): void {\n _cachedAdapters = null;\n _cachedRuntimeRef = null;\n}\n\n","/**\n * Setup 工具动态加载(plugin-sdk/setup + plugin-sdk/setup-tools)\n *\n * 新版本 openclaw 有这些导出,旧版本可能缺失。\n * 不可用时提供最小降级实现。\n */\nimport { createRequire } from 'node:module';\n\nconst req = createRequire(__filename);\n\nlet _setup: typeof import('openclaw/plugin-sdk/setup') | null | undefined;\nlet _tools: typeof import('openclaw/plugin-sdk/setup-tools') | null | undefined;\n\nfunction loadSetup() {\n if (_setup !== undefined) return _setup;\n try { _setup = req('openclaw/plugin-sdk/setup'); } catch { _setup = null; }\n return _setup;\n}\n\nfunction loadTools() {\n if (_tools !== undefined) return _tools;\n try { _tools = req('openclaw/plugin-sdk/setup-tools'); } catch { _tools = null; }\n return _tools;\n}\n\nexport type { ChannelSetupWizard } from 'openclaw/plugin-sdk/setup';\n\nexport const DEFAULT_ACCOUNT_ID = 'default';\n\nexport function createStandardChannelSetupStatus(\n ...args: Parameters<NonNullable<typeof _setup>['createStandardChannelSetupStatus']>\n): ReturnType<NonNullable<typeof _setup>['createStandardChannelSetupStatus']> {\n const mod = loadSetup();\n if (mod) return mod.createStandardChannelSetupStatus(...args);\n return {\n channelLabel: args[0]?.channelLabel ?? 'QQ Bot',\n configuredLabel: 'Configured',\n unconfiguredLabel: 'Not configured',\n resolveConfigured: () => false,\n } as unknown as ReturnType<NonNullable<typeof _setup>['createStandardChannelSetupStatus']>;\n}\n\nexport function setSetupChannelEnabled(\n ...args: Parameters<NonNullable<typeof _setup>['setSetupChannelEnabled']>\n): void {\n loadSetup()?.setSetupChannelEnabled?.(...args);\n}\n\nexport function formatDocsLink(\n ...args: Parameters<NonNullable<typeof _tools>['formatDocsLink']>\n): ReturnType<NonNullable<typeof _tools>['formatDocsLink']> {\n const mod = loadTools();\n if (mod) return mod.formatDocsLink(...args);\n return (args[1] ? `${args[1]}: ${args[0]}` : args[0]) as ReturnType<NonNullable<typeof _tools>['formatDocsLink']>;\n}\n","module.exports = {\n MODE_NUMBER : 1 << 0,\n MODE_ALPHA_NUM : 1 << 1,\n MODE_8BIT_BYTE : 1 << 2,\n MODE_KANJI : 1 << 3\n};\n","var QRMode = require('./QRMode');\n\nfunction QR8bitByte(data) {\n\tthis.mode = QRMode.MODE_8BIT_BYTE;\n\tthis.data = data;\n}\n\nQR8bitByte.prototype = {\n\n\tgetLength : function() {\n\t\treturn this.data.length;\n\t},\n\t\n\twrite : function(buffer) {\n\t\tfor (var i = 0; i < this.data.length; i++) {\n\t\t\t// not JIS ...\n\t\t\tbuffer.put(this.data.charCodeAt(i), 8);\n\t\t}\n\t}\n};\n\nmodule.exports = QR8bitByte;\n","var QRMath = {\n\n\tglog : function(n) {\n\t\n\t\tif (n < 1) {\n\t\t\tthrow new Error(\"glog(\" + n + \")\");\n\t\t}\n\t\t\n\t\treturn QRMath.LOG_TABLE[n];\n\t},\n\t\n\tgexp : function(n) {\n\t\n\t\twhile (n < 0) {\n\t\t\tn += 255;\n\t\t}\n\t\n\t\twhile (n >= 256) {\n\t\t\tn -= 255;\n\t\t}\n\t\n\t\treturn QRMath.EXP_TABLE[n];\n\t},\n\t\n\tEXP_TABLE : new Array(256),\n\t\n\tLOG_TABLE : new Array(256)\n\n};\n\t\nfor (var i = 0; i < 8; i++) {\n\tQRMath.EXP_TABLE[i] = 1 << i;\n}\nfor (var i = 8; i < 256; i++) {\n\tQRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4]\n\t\t^ QRMath.EXP_TABLE[i - 5]\n\t\t^ QRMath.EXP_TABLE[i - 6]\n\t\t^ QRMath.EXP_TABLE[i - 8];\n}\nfor (var i = 0; i < 255; i++) {\n\tQRMath.LOG_TABLE[QRMath.EXP_TABLE[i] ] = i;\n}\n\nmodule.exports = QRMath;\n","var QRMath = require('./QRMath');\n\nfunction QRPolynomial(num, shift) {\n\tif (num.length === undefined) {\n\t\tthrow new Error(num.length + \"/\" + shift);\n\t}\n\n\tvar offset = 0;\n\n\twhile (offset < num.length && num[offset] === 0) {\n\t\toffset++;\n\t}\n\n\tthis.num = new Array(num.length - offset + shift);\n\tfor (var i = 0; i < num.length - offset; i++) {\n\t\tthis.num[i] = num[i + offset];\n\t}\n}\n\nQRPolynomial.prototype = {\n\n\tget : function(index) {\n\t\treturn this.num[index];\n\t},\n\t\n\tgetLength : function() {\n\t\treturn this.num.length;\n\t},\n\t\n\tmultiply : function(e) {\n\t\n\t\tvar num = new Array(this.getLength() + e.getLength() - 1);\n\t\n\t\tfor (var i = 0; i < this.getLength(); i++) {\n\t\t\tfor (var j = 0; j < e.getLength(); j++) {\n\t\t\t\tnum[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i) ) + QRMath.glog(e.get(j) ) );\n\t\t\t}\n\t\t}\n\t\n\t\treturn new QRPolynomial(num, 0);\n\t},\n\t\n\tmod : function(e) {\n\t\n\t\tif (this.getLength() - e.getLength() < 0) {\n\t\t\treturn this;\n\t\t}\n\t\n\t\tvar ratio = QRMath.glog(this.get(0) ) - QRMath.glog(e.get(0) );\n\t\n\t\tvar num = new Array(this.getLength() );\n\t\t\n\t\tfor (var i = 0; i < this.getLength(); i++) {\n\t\t\tnum[i] = this.get(i);\n\t\t}\n\t\t\n\t\tfor (var x = 0; x < e.getLength(); x++) {\n\t\t\tnum[x] ^= QRMath.gexp(QRMath.glog(e.get(x) ) + ratio);\n\t\t}\n\t\n\t\t// recursive call\n\t\treturn new QRPolynomial(num, 0).mod(e);\n\t}\n};\n\nmodule.exports = QRPolynomial;\n","module.exports = {\n\tPATTERN000 : 0,\n\tPATTERN001 : 1,\n\tPATTERN010 : 2,\n\tPATTERN011 : 3,\n\tPATTERN100 : 4,\n\tPATTERN101 : 5,\n\tPATTERN110 : 6,\n\tPATTERN111 : 7\n};\n","var QRMode = require('./QRMode');\nvar QRPolynomial = require('./QRPolynomial');\nvar QRMath = require('./QRMath');\nvar QRMaskPattern = require('./QRMaskPattern');\n\nvar QRUtil = {\n\n PATTERN_POSITION_TABLE : [\n [],\n [6, 18],\n [6, 22],\n [6, 26],\n [6, 30],\n [6, 34],\n [6, 22, 38],\n [6, 24, 42],\n [6, 26, 46],\n [6, 28, 50],\n [6, 30, 54], \n [6, 32, 58],\n [6, 34, 62],\n [6, 26, 46, 66],\n [6, 26, 48, 70],\n [6, 26, 50, 74],\n [6, 30, 54, 78],\n [6, 30, 56, 82],\n [6, 30, 58, 86],\n [6, 34, 62, 90],\n [6, 28, 50, 72, 94],\n [6, 26, 50, 74, 98],\n [6, 30, 54, 78, 102],\n [6, 28, 54, 80, 106],\n [6, 32, 58, 84, 110],\n [6, 30, 58, 86, 114],\n [6, 34, 62, 90, 118],\n [6, 26, 50, 74, 98, 122],\n [6, 30, 54, 78, 102, 126],\n [6, 26, 52, 78, 104, 130],\n [6, 30, 56, 82, 108, 134],\n [6, 34, 60, 86, 112, 138],\n [6, 30, 58, 86, 114, 142],\n [6, 34, 62, 90, 118, 146],\n [6, 30, 54, 78, 102, 126, 150],\n [6, 24, 50, 76, 102, 128, 154],\n [6, 28, 54, 80, 106, 132, 158],\n [6, 32, 58, 84, 110, 136, 162],\n [6, 26, 54, 82, 110, 138, 166],\n [6, 30, 58, 86, 114, 142, 170]\n ],\n\n G15 : (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0),\n G18 : (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0),\n G15_MASK : (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1),\n\n getBCHTypeInfo : function(data) {\n var d = data << 10;\n while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {\n d ^= (QRUtil.G15 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) ) ); \n }\n return ( (data << 10) | d) ^ QRUtil.G15_MASK;\n },\n\n getBCHTypeNumber : function(data) {\n var d = data << 12;\n while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {\n d ^= (QRUtil.G18 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) ) ); \n }\n return (data << 12) | d;\n },\n\n getBCHDigit : function(data) {\n\n var digit = 0;\n\n while (data !== 0) {\n digit++;\n data >>>= 1;\n }\n\n return digit;\n },\n\n getPatternPosition : function(typeNumber) {\n return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];\n },\n\n getMask : function(maskPattern, i, j) {\n \n switch (maskPattern) {\n \n case QRMaskPattern.PATTERN000 : return (i + j) % 2 === 0;\n case QRMaskPattern.PATTERN001 : return i % 2 === 0;\n case QRMaskPattern.PATTERN010 : return j % 3 === 0;\n case QRMaskPattern.PATTERN011 : return (i + j) % 3 === 0;\n case QRMaskPattern.PATTERN100 : return (Math.floor(i / 2) + Math.floor(j / 3) ) % 2 === 0;\n case QRMaskPattern.PATTERN101 : return (i * j) % 2 + (i * j) % 3 === 0;\n case QRMaskPattern.PATTERN110 : return ( (i * j) % 2 + (i * j) % 3) % 2 === 0;\n case QRMaskPattern.PATTERN111 : return ( (i * j) % 3 + (i + j) % 2) % 2 === 0;\n\n default :\n throw new Error(\"bad maskPattern:\" + maskPattern);\n }\n },\n\n getErrorCorrectPolynomial : function(errorCorrectLength) {\n\n var a = new QRPolynomial([1], 0);\n\n for (var i = 0; i < errorCorrectLength; i++) {\n a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0) );\n }\n\n return a;\n },\n\n getLengthInBits : function(mode, type) {\n\n if (1 <= type && type < 10) {\n\n // 1 - 9\n\n switch(mode) {\n case QRMode.MODE_NUMBER : return 10;\n case QRMode.MODE_ALPHA_NUM : return 9;\n case QRMode.MODE_8BIT_BYTE : return 8;\n case QRMode.MODE_KANJI : return 8;\n default :\n throw new Error(\"mode:\" + mode);\n }\n\n } else if (type < 27) {\n\n // 10 - 26\n\n switch(mode) {\n case QRMode.MODE_NUMBER : return 12;\n case QRMode.MODE_ALPHA_NUM : return 11;\n case QRMode.MODE_8BIT_BYTE : return 16;\n case QRMode.MODE_KANJI : return 10;\n default :\n throw new Error(\"mode:\" + mode);\n }\n\n } else if (type < 41) {\n\n // 27 - 40\n\n switch(mode) {\n case QRMode.MODE_NUMBER : return 14;\n case QRMode.MODE_ALPHA_NUM : return 13;\n case QRMode.MODE_8BIT_BYTE : return 16;\n case QRMode.MODE_KANJI : return 12;\n default :\n throw new Error(\"mode:\" + mode);\n }\n\n } else {\n throw new Error(\"type:\" + type);\n }\n },\n\n getLostPoint : function(qrCode) {\n \n var moduleCount = qrCode.getModuleCount();\n var lostPoint = 0;\n var row = 0; \n var col = 0;\n\n \n // LEVEL1\n \n for (row = 0; row < moduleCount; row++) {\n\n for (col = 0; col < moduleCount; col++) {\n\n var sameCount = 0;\n var dark = qrCode.isDark(row, col);\n\n for (var r = -1; r <= 1; r++) {\n\n if (row + r < 0 || moduleCount <= row + r) {\n continue;\n }\n\n for (var c = -1; c <= 1; c++) {\n\n if (col + c < 0 || moduleCount <= col + c) {\n continue;\n }\n\n if (r === 0 && c === 0) {\n continue;\n }\n\n if (dark === qrCode.isDark(row + r, col + c) ) {\n sameCount++;\n }\n }\n }\n\n if (sameCount > 5) {\n lostPoint += (3 + sameCount - 5);\n }\n }\n }\n\n // LEVEL2\n\n for (row = 0; row < moduleCount - 1; row++) {\n for (col = 0; col < moduleCount - 1; col++) {\n var count = 0;\n if (qrCode.isDark(row, col ) ) count++;\n if (qrCode.isDark(row + 1, col ) ) count++;\n if (qrCode.isDark(row, col + 1) ) count++;\n if (qrCode.isDark(row + 1, col + 1) ) count++;\n if (count === 0 || count === 4) {\n lostPoint += 3;\n }\n }\n }\n\n // LEVEL3\n\n for (row = 0; row < moduleCount; row++) {\n for (col = 0; col < moduleCount - 6; col++) {\n if (qrCode.isDark(row, col) && \n !qrCode.isDark(row, col + 1) && \n qrCode.isDark(row, col + 2) && \n qrCode.isDark(row, col + 3) && \n qrCode.isDark(row, col + 4) && \n !qrCode.isDark(row, col + 5) && \n qrCode.isDark(row, col + 6) ) {\n lostPoint += 40;\n }\n }\n }\n\n for (col = 0; col < moduleCount; col++) {\n for (row = 0; row < moduleCount - 6; row++) {\n if (qrCode.isDark(row, col) &&\n !qrCode.isDark(row + 1, col) &&\n qrCode.isDark(row + 2, col) &&\n qrCode.isDark(row + 3, col) &&\n qrCode.isDark(row + 4, col) &&\n !qrCode.isDark(row + 5, col) &&\n qrCode.isDark(row + 6, col) ) {\n lostPoint += 40;\n }\n }\n }\n\n // LEVEL4\n \n var darkCount = 0;\n\n for (col = 0; col < moduleCount; col++) {\n for (row = 0; row < moduleCount; row++) {\n if (qrCode.isDark(row, col) ) {\n darkCount++;\n }\n }\n }\n \n var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;\n lostPoint += ratio * 10;\n\n return lostPoint; \n }\n\n};\n\nmodule.exports = QRUtil;\n","module.exports = {\n\tL : 1,\n\tM : 0,\n\tQ : 3,\n\tH : 2\n};\n\n","var QRErrorCorrectLevel = require('./QRErrorCorrectLevel');\n\nfunction QRRSBlock(totalCount, dataCount) {\n\tthis.totalCount = totalCount;\n\tthis.dataCount = dataCount;\n}\n\nQRRSBlock.RS_BLOCK_TABLE = [\n\n\t// L\n\t// M\n\t// Q\n\t// H\n\n\t// 1\n\t[1, 26, 19],\n\t[1, 26, 16],\n\t[1, 26, 13],\n\t[1, 26, 9],\n\t\n\t// 2\n\t[1, 44, 34],\n\t[1, 44, 28],\n\t[1, 44, 22],\n\t[1, 44, 16],\n\n\t// 3\n\t[1, 70, 55],\n\t[1, 70, 44],\n\t[2, 35, 17],\n\t[2, 35, 13],\n\n\t// 4\t\t\n\t[1, 100, 80],\n\t[2, 50, 32],\n\t[2, 50, 24],\n\t[4, 25, 9],\n\t\n\t// 5\n\t[1, 134, 108],\n\t[2, 67, 43],\n\t[2, 33, 15, 2, 34, 16],\n\t[2, 33, 11, 2, 34, 12],\n\t\n\t// 6\n\t[2, 86, 68],\n\t[4, 43, 27],\n\t[4, 43, 19],\n\t[4, 43, 15],\n\t\n\t// 7\t\t\n\t[2, 98, 78],\n\t[4, 49, 31],\n\t[2, 32, 14, 4, 33, 15],\n\t[4, 39, 13, 1, 40, 14],\n\t\n\t// 8\n\t[2, 121, 97],\n\t[2, 60, 38, 2, 61, 39],\n\t[4, 40, 18, 2, 41, 19],\n\t[4, 40, 14, 2, 41, 15],\n\t\n\t// 9\n\t[2, 146, 116],\n\t[3, 58, 36, 2, 59, 37],\n\t[4, 36, 16, 4, 37, 17],\n\t[4, 36, 12, 4, 37, 13],\n\t\n\t// 10\t\t\n\t[2, 86, 68, 2, 87, 69],\n\t[4, 69, 43, 1, 70, 44],\n\t[6, 43, 19, 2, 44, 20],\n\t[6, 43, 15, 2, 44, 16],\n\n\t// 11\n\t[4, 101, 81],\n\t[1, 80, 50, 4, 81, 51],\n\t[4, 50, 22, 4, 51, 23],\n\t[3, 36, 12, 8, 37, 13],\n\n\t// 12\n\t[2, 116, 92, 2, 117, 93],\n\t[6, 58, 36, 2, 59, 37],\n\t[4, 46, 20, 6, 47, 21],\n\t[7, 42, 14, 4, 43, 15],\n\n\t// 13\n\t[4, 133, 107],\n\t[8, 59, 37, 1, 60, 38],\n\t[8, 44, 20, 4, 45, 21],\n\t[12, 33, 11, 4, 34, 12],\n\n\t// 14\n\t[3, 145, 115, 1, 146, 116],\n\t[4, 64, 40, 5, 65, 41],\n\t[11, 36, 16, 5, 37, 17],\n\t[11, 36, 12, 5, 37, 13],\n\n\t// 15\n\t[5, 109, 87, 1, 110, 88],\n\t[5, 65, 41, 5, 66, 42],\n\t[5, 54, 24, 7, 55, 25],\n\t[11, 36, 12],\n\n\t// 16\n\t[5, 122, 98, 1, 123, 99],\n\t[7, 73, 45, 3, 74, 46],\n\t[15, 43, 19, 2, 44, 20],\n\t[3, 45, 15, 13, 46, 16],\n\n\t// 17\n\t[1, 135, 107, 5, 136, 108],\n\t[10, 74, 46, 1, 75, 47],\n\t[1, 50, 22, 15, 51, 23],\n\t[2, 42, 14, 17, 43, 15],\n\n\t// 18\n\t[5, 150, 120, 1, 151, 121],\n\t[9, 69, 43, 4, 70, 44],\n\t[17, 50, 22, 1, 51, 23],\n\t[2, 42, 14, 19, 43, 15],\n\n\t// 19\n\t[3, 141, 113, 4, 142, 114],\n\t[3, 70, 44, 11, 71, 45],\n\t[17, 47, 21, 4, 48, 22],\n\t[9, 39, 13, 16, 40, 14],\n\n\t// 20\n\t[3, 135, 107, 5, 136, 108],\n\t[3, 67, 41, 13, 68, 42],\n\t[15, 54, 24, 5, 55, 25],\n\t[15, 43, 15, 10, 44, 16],\n\n\t// 21\n\t[4, 144, 116, 4, 145, 117],\n\t[17, 68, 42],\n\t[17, 50, 22, 6, 51, 23],\n\t[19, 46, 16, 6, 47, 17],\n\n\t// 22\n\t[2, 139, 111, 7, 140, 112],\n\t[17, 74, 46],\n\t[7, 54, 24, 16, 55, 25],\n\t[34, 37, 13],\n\n\t// 23\n\t[4, 151, 121, 5, 152, 122],\n\t[4, 75, 47, 14, 76, 48],\n\t[11, 54, 24, 14, 55, 25],\n\t[16, 45, 15, 14, 46, 16],\n\n\t// 24\n\t[6, 147, 117, 4, 148, 118],\n\t[6, 73, 45, 14, 74, 46],\n\t[11, 54, 24, 16, 55, 25],\n\t[30, 46, 16, 2, 47, 17],\n\n\t// 25\n\t[8, 132, 106, 4, 133, 107],\n\t[8, 75, 47, 13, 76, 48],\n\t[7, 54, 24, 22, 55, 25],\n\t[22, 45, 15, 13, 46, 16],\n\n\t// 26\n\t[10, 142, 114, 2, 143, 115],\n\t[19, 74, 46, 4, 75, 47],\n\t[28, 50, 22, 6, 51, 23],\n\t[33, 46, 16, 4, 47, 17],\n\n\t// 27\n\t[8, 152, 122, 4, 153, 123],\n\t[22, 73, 45, 3, 74, 46],\n\t[8, 53, 23, 26, 54, 24],\n\t[12, 45, 15, 28, 46, 16],\n\n\t// 28\n\t[3, 147, 117, 10, 148, 118],\n\t[3, 73, 45, 23, 74, 46],\n\t[4, 54, 24, 31, 55, 25],\n\t[11, 45, 15, 31, 46, 16],\n\n\t// 29\n\t[7, 146, 116, 7, 147, 117],\n\t[21, 73, 45, 7, 74, 46],\n\t[1, 53, 23, 37, 54, 24],\n\t[19, 45, 15, 26, 46, 16],\n\n\t// 30\n\t[5, 145, 115, 10, 146, 116],\n\t[19, 75, 47, 10, 76, 48],\n\t[15, 54, 24, 25, 55, 25],\n\t[23, 45, 15, 25, 46, 16],\n\n\t// 31\n\t[13, 145, 115, 3, 146, 116],\n\t[2, 74, 46, 29, 75, 47],\n\t[42, 54, 24, 1, 55, 25],\n\t[23, 45, 15, 28, 46, 16],\n\n\t// 32\n\t[17, 145, 115],\n\t[10, 74, 46, 23, 75, 47],\n\t[10, 54, 24, 35, 55, 25],\n\t[19, 45, 15, 35, 46, 16],\n\n\t// 33\n\t[17, 145, 115, 1, 146, 116],\n\t[14, 74, 46, 21, 75, 47],\n\t[29, 54, 24, 19, 55, 25],\n\t[11, 45, 15, 46, 46, 16],\n\n\t// 34\n\t[13, 145, 115, 6, 146, 116],\n\t[14, 74, 46, 23, 75, 47],\n\t[44, 54, 24, 7, 55, 25],\n\t[59, 46, 16, 1, 47, 17],\n\n\t// 35\n\t[12, 151, 121, 7, 152, 122],\n\t[12, 75, 47, 26, 76, 48],\n\t[39, 54, 24, 14, 55, 25],\n\t[22, 45, 15, 41, 46, 16],\n\n\t// 36\n\t[6, 151, 121, 14, 152, 122],\n\t[6, 75, 47, 34, 76, 48],\n\t[46, 54, 24, 10, 55, 25],\n\t[2, 45, 15, 64, 46, 16],\n\n\t// 37\n\t[17, 152, 122, 4, 153, 123],\n\t[29, 74, 46, 14, 75, 47],\n\t[49, 54, 24, 10, 55, 25],\n\t[24, 45, 15, 46, 46, 16],\n\n\t// 38\n\t[4, 152, 122, 18, 153, 123],\n\t[13, 74, 46, 32, 75, 47],\n\t[48, 54, 24, 14, 55, 25],\n\t[42, 45, 15, 32, 46, 16],\n\n\t// 39\n\t[20, 147, 117, 4, 148, 118],\n\t[40, 75, 47, 7, 76, 48],\n\t[43, 54, 24, 22, 55, 25],\n\t[10, 45, 15, 67, 46, 16],\n\n\t// 40\n\t[19, 148, 118, 6, 149, 119],\n\t[18, 75, 47, 31, 76, 48],\n\t[34, 54, 24, 34, 55, 25],\n\t[20, 45, 15, 61, 46, 16]\n];\n\nQRRSBlock.getRSBlocks = function(typeNumber, errorCorrectLevel) {\n\t\n\tvar rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel);\n\t\n\tif (rsBlock === undefined) {\n\t\tthrow new Error(\"bad rs block @ typeNumber:\" + typeNumber + \"/errorCorrectLevel:\" + errorCorrectLevel);\n\t}\n\n\tvar length = rsBlock.length / 3;\n\t\n\tvar list = [];\n\t\n\tfor (var i = 0; i < length; i++) {\n\n\t\tvar count = rsBlock[i * 3 + 0];\n\t\tvar totalCount = rsBlock[i * 3 + 1];\n\t\tvar dataCount = rsBlock[i * 3 + 2];\n\n\t\tfor (var j = 0; j < count; j++) {\n\t\t\tlist.push(new QRRSBlock(totalCount, dataCount) );\t\n\t\t}\n\t}\n\t\n\treturn list;\n};\n\nQRRSBlock.getRsBlockTable = function(typeNumber, errorCorrectLevel) {\n\n\tswitch(errorCorrectLevel) {\n\tcase QRErrorCorrectLevel.L :\n\t\treturn QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];\n\tcase QRErrorCorrectLevel.M :\n\t\treturn QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];\n\tcase QRErrorCorrectLevel.Q :\n\t\treturn QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];\n\tcase QRErrorCorrectLevel.H :\n\t\treturn QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];\n\tdefault :\n\t\treturn undefined;\n\t}\n};\n\nmodule.exports = QRRSBlock;\n","function QRBitBuffer() {\n\tthis.buffer = [];\n\tthis.length = 0;\n}\n\nQRBitBuffer.prototype = {\n\n\tget : function(index) {\n\t\tvar bufIndex = Math.floor(index / 8);\n\t\treturn ( (this.buffer[bufIndex] >>> (7 - index % 8) ) & 1) == 1;\n\t},\n\t\n\tput : function(num, length) {\n\t\tfor (var i = 0; i < length; i++) {\n\t\t\tthis.putBit( ( (num >>> (length - i - 1) ) & 1) == 1);\n\t\t}\n\t},\n\t\n\tgetLengthInBits : function() {\n\t\treturn this.length;\n\t},\n\t\n\tputBit : function(bit) {\n\t\n\t\tvar bufIndex = Math.floor(this.length / 8);\n\t\tif (this.buffer.length <= bufIndex) {\n\t\t\tthis.buffer.push(0);\n\t\t}\n\t\n\t\tif (bit) {\n\t\t\tthis.buffer[bufIndex] |= (0x80 >>> (this.length % 8) );\n\t\t}\n\t\n\t\tthis.length++;\n\t}\n};\n\nmodule.exports = QRBitBuffer;\n","//---------------------------------------------------------------------\n// QRCode for JavaScript\n//\n// Copyright (c) 2009 Kazuhiko Arase\n//\n// URL: http://www.d-project.com/\n//\n// Licensed under the MIT license:\n// http://www.opensource.org/licenses/mit-license.php\n//\n// The word \"QR Code\" is registered trademark of \n// DENSO WAVE INCORPORATED\n// http://www.denso-wave.com/qrcode/faqpatent-e.html\n//\n//---------------------------------------------------------------------\n// Modified to work in node for this project (and some refactoring)\n//---------------------------------------------------------------------\n\nvar QR8bitByte = require('./QR8bitByte');\nvar QRUtil = require('./QRUtil');\nvar QRPolynomial = require('./QRPolynomial');\nvar QRRSBlock = require('./QRRSBlock');\nvar QRBitBuffer = require('./QRBitBuffer');\n\nfunction QRCode(typeNumber, errorCorrectLevel) {\n\tthis.typeNumber = typeNumber;\n\tthis.errorCorrectLevel = errorCorrectLevel;\n\tthis.modules = null;\n\tthis.moduleCount = 0;\n\tthis.dataCache = null;\n\tthis.dataList = [];\n}\n\nQRCode.prototype = {\n\t\n\taddData : function(data) {\n\t\tvar newData = new QR8bitByte(data);\n\t\tthis.dataList.push(newData);\n\t\tthis.dataCache = null;\n\t},\n\t\n\tisDark : function(row, col) {\n\t\tif (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) {\n\t\t\tthrow new Error(row + \",\" + col);\n\t\t}\n\t\treturn this.modules[row][col];\n\t},\n\n\tgetModuleCount : function() {\n\t\treturn this.moduleCount;\n\t},\n\t\n\tmake : function() {\n\t\t// Calculate automatically typeNumber if provided is < 1\n\t\tif (this.typeNumber < 1 ){\n\t\t\tvar typeNumber = 1;\n\t\t\tfor (typeNumber = 1; typeNumber < 40; typeNumber++) {\n\t\t\t\tvar rsBlocks = QRRSBlock.getRSBlocks(typeNumber, this.errorCorrectLevel);\n\n\t\t\t\tvar buffer = new QRBitBuffer();\n\t\t\t\tvar totalDataCount = 0;\n\t\t\t\tfor (var i = 0; i < rsBlocks.length; i++) {\n\t\t\t\t\ttotalDataCount += rsBlocks[i].dataCount;\n\t\t\t\t}\n\n\t\t\t\tfor (var x = 0; x < this.dataList.length; x++) {\n\t\t\t\t\tvar data = this.dataList[x];\n\t\t\t\t\tbuffer.put(data.mode, 4);\n\t\t\t\t\tbuffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber) );\n\t\t\t\t\tdata.write(buffer);\n\t\t\t\t}\n\t\t\t\tif (buffer.getLengthInBits() <= totalDataCount * 8)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tthis.typeNumber = typeNumber;\n\t\t}\n\t\tthis.makeImpl(false, this.getBestMaskPattern() );\n\t},\n\t\n\tmakeImpl : function(test, maskPattern) {\n\t\t\n\t\tthis.moduleCount = this.typeNumber * 4 + 17;\n\t\tthis.modules = new Array(this.moduleCount);\n\t\t\n\t\tfor (var row = 0; row < this.moduleCount; row++) {\n\t\t\t\n\t\t\tthis.modules[row] = new Array(this.moduleCount);\n\t\t\t\n\t\t\tfor (var col = 0; col < this.moduleCount; col++) {\n\t\t\t\tthis.modules[row][col] = null;//(col + row) % 3;\n\t\t\t}\n\t\t}\n\t\n\t\tthis.setupPositionProbePattern(0, 0);\n\t\tthis.setupPositionProbePattern(this.moduleCount - 7, 0);\n\t\tthis.setupPositionProbePattern(0, this.moduleCount - 7);\n\t\tthis.setupPositionAdjustPattern();\n\t\tthis.setupTimingPattern();\n\t\tthis.setupTypeInfo(test, maskPattern);\n\t\t\n\t\tif (this.typeNumber >= 7) {\n\t\t\tthis.setupTypeNumber(test);\n\t\t}\n\t\n\t\tif (this.dataCache === null) {\n\t\t\tthis.dataCache = QRCode.createData(this.typeNumber, this.errorCorrectLevel, this.dataList);\n\t\t}\n\t\n\t\tthis.mapData(this.dataCache, maskPattern);\n\t},\n\n\tsetupPositionProbePattern : function(row, col) {\n\t\t\n\t\tfor (var r = -1; r <= 7; r++) {\n\t\t\t\n\t\t\tif (row + r <= -1 || this.moduleCount <= row + r) continue;\n\t\t\t\n\t\t\tfor (var c = -1; c <= 7; c++) {\n\t\t\t\t\n\t\t\t\tif (col + c <= -1 || this.moduleCount <= col + c) continue;\n\t\t\t\t\n\t\t\t\tif ( (0 <= r && r <= 6 && (c === 0 || c === 6) ) || \n (0 <= c && c <= 6 && (r === 0 || r === 6) ) || \n (2 <= r && r <= 4 && 2 <= c && c <= 4) ) {\n\t\t\t\t\tthis.modules[row + r][col + c] = true;\n\t\t\t\t} else {\n\t\t\t\t\tthis.modules[row + r][col + c] = false;\n\t\t\t\t}\n\t\t\t}\t\t\n\t\t}\t\t\n\t},\n\t\n\tgetBestMaskPattern : function() {\n\t\n\t\tvar minLostPoint = 0;\n\t\tvar pattern = 0;\n\t\n\t\tfor (var i = 0; i < 8; i++) {\n\t\t\t\n\t\t\tthis.makeImpl(true, i);\n\t\n\t\t\tvar lostPoint = QRUtil.getLostPoint(this);\n\t\n\t\t\tif (i === 0 || minLostPoint > lostPoint) {\n\t\t\t\tminLostPoint = lostPoint;\n\t\t\t\tpattern = i;\n\t\t\t}\n\t\t}\n\t\n\t\treturn pattern;\n\t},\n\t\n\tcreateMovieClip : function(target_mc, instance_name, depth) {\n\t\n\t\tvar qr_mc = target_mc.createEmptyMovieClip(instance_name, depth);\n\t\tvar cs = 1;\n\t\n\t\tthis.make();\n\n\t\tfor (var row = 0; row < this.modules.length; row++) {\n\t\t\t\n\t\t\tvar y = row * cs;\n\t\t\t\n\t\t\tfor (var col = 0; col < this.modules[row].length; col++) {\n\t\n\t\t\t\tvar x = col * cs;\n\t\t\t\tvar dark = this.modules[row][col];\n\t\t\t\n\t\t\t\tif (dark) {\n\t\t\t\t\tqr_mc.beginFill(0, 100);\n\t\t\t\t\tqr_mc.moveTo(x, y);\n\t\t\t\t\tqr_mc.lineTo(x + cs, y);\n\t\t\t\t\tqr_mc.lineTo(x + cs, y + cs);\n\t\t\t\t\tqr_mc.lineTo(x, y + cs);\n\t\t\t\t\tqr_mc.endFill();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn qr_mc;\n\t},\n\n\tsetupTimingPattern : function() {\n\t\t\n\t\tfor (var r = 8; r < this.moduleCount - 8; r++) {\n\t\t\tif (this.modules[r][6] !== null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.modules[r][6] = (r % 2 === 0);\n\t\t}\n\t\n\t\tfor (var c = 8; c < this.moduleCount - 8; c++) {\n\t\t\tif (this.modules[6][c] !== null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.modules[6][c] = (c % 2 === 0);\n\t\t}\n\t},\n\t\n\tsetupPositionAdjustPattern : function() {\n\t\n\t\tvar pos = QRUtil.getPatternPosition(this.typeNumber);\n\t\t\n\t\tfor (var i = 0; i < pos.length; i++) {\n\t\t\n\t\t\tfor (var j = 0; j < pos.length; j++) {\n\t\t\t\n\t\t\t\tvar row = pos[i];\n\t\t\t\tvar col = pos[j];\n\t\t\t\t\n\t\t\t\tif (this.modules[row][col] !== null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tfor (var r = -2; r <= 2; r++) {\n\t\t\t\t\n\t\t\t\t\tfor (var c = -2; c <= 2; c++) {\n\t\t\t\t\t\n\t\t\t\t\t\tif (Math.abs(r) === 2 || \n Math.abs(c) === 2 ||\n (r === 0 && c === 0) ) {\n\t\t\t\t\t\t\tthis.modules[row + r][col + c] = true;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.modules[row + r][col + c] = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\t\n\tsetupTypeNumber : function(test) {\n\t\n\t\tvar bits = QRUtil.getBCHTypeNumber(this.typeNumber);\n var mod;\n\t\n\t\tfor (var i = 0; i < 18; i++) {\n\t\t\tmod = (!test && ( (bits >> i) & 1) === 1);\n\t\t\tthis.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod;\n\t\t}\n\t\n\t\tfor (var x = 0; x < 18; x++) {\n\t\t\tmod = (!test && ( (bits >> x) & 1) === 1);\n\t\t\tthis.modules[x % 3 + this.moduleCount - 8 - 3][Math.floor(x / 3)] = mod;\n\t\t}\n\t},\n\t\n\tsetupTypeInfo : function(test, maskPattern) {\n\t\n\t\tvar data = (this.errorCorrectLevel << 3) | maskPattern;\n\t\tvar bits = QRUtil.getBCHTypeInfo(data);\n var mod;\n\t\n\t\t// vertical\t\t\n\t\tfor (var v = 0; v < 15; v++) {\n\t\n\t\t\tmod = (!test && ( (bits >> v) & 1) === 1);\n\t\n\t\t\tif (v < 6) {\n\t\t\t\tthis.modules[v][8] = mod;\n\t\t\t} else if (v < 8) {\n\t\t\t\tthis.modules[v + 1][8] = mod;\n\t\t\t} else {\n\t\t\t\tthis.modules[this.moduleCount - 15 + v][8] = mod;\n\t\t\t}\n\t\t}\n\t\n\t\t// horizontal\n\t\tfor (var h = 0; h < 15; h++) {\n\t\n\t\t\tmod = (!test && ( (bits >> h) & 1) === 1);\n\t\t\t\n\t\t\tif (h < 8) {\n\t\t\t\tthis.modules[8][this.moduleCount - h - 1] = mod;\n\t\t\t} else if (h < 9) {\n\t\t\t\tthis.modules[8][15 - h - 1 + 1] = mod;\n\t\t\t} else {\n\t\t\t\tthis.modules[8][15 - h - 1] = mod;\n\t\t\t}\n\t\t}\n\t\n\t\t// fixed module\n\t\tthis.modules[this.moduleCount - 8][8] = (!test);\n\t\n\t},\n\t\n\tmapData : function(data, maskPattern) {\n\t\t\n\t\tvar inc = -1;\n\t\tvar row = this.moduleCount - 1;\n\t\tvar bitIndex = 7;\n\t\tvar byteIndex = 0;\n\t\t\n\t\tfor (var col = this.moduleCount - 1; col > 0; col -= 2) {\n\t\n\t\t\tif (col === 6) col--;\n\t\n\t\t\twhile (true) {\n\t\n\t\t\t\tfor (var c = 0; c < 2; c++) {\n\t\t\t\t\t\n\t\t\t\t\tif (this.modules[row][col - c] === null) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tvar dark = false;\n\t\n\t\t\t\t\t\tif (byteIndex < data.length) {\n\t\t\t\t\t\t\tdark = ( ( (data[byteIndex] >>> bitIndex) & 1) === 1);\n\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\tvar mask = QRUtil.getMask(maskPattern, row, col - c);\n\t\n\t\t\t\t\t\tif (mask) {\n\t\t\t\t\t\t\tdark = !dark;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tthis.modules[row][col - c] = dark;\n\t\t\t\t\t\tbitIndex--;\n\t\n\t\t\t\t\t\tif (bitIndex === -1) {\n\t\t\t\t\t\t\tbyteIndex++;\n\t\t\t\t\t\t\tbitIndex = 7;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\t\t\t\t\n\t\t\t\trow += inc;\n\t\n\t\t\t\tif (row < 0 || this.moduleCount <= row) {\n\t\t\t\t\trow -= inc;\n\t\t\t\t\tinc = -inc;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t}\n\n};\n\nQRCode.PAD0 = 0xEC;\nQRCode.PAD1 = 0x11;\n\nQRCode.createData = function(typeNumber, errorCorrectLevel, dataList) {\n\t\n\tvar rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel);\n\t\n\tvar buffer = new QRBitBuffer();\n\t\n\tfor (var i = 0; i < dataList.length; i++) {\n\t\tvar data = dataList[i];\n\t\tbuffer.put(data.mode, 4);\n\t\tbuffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber) );\n\t\tdata.write(buffer);\n\t}\n\n\t// calc num max data.\n\tvar totalDataCount = 0;\n\tfor (var x = 0; x < rsBlocks.length; x++) {\n\t\ttotalDataCount += rsBlocks[x].dataCount;\n\t}\n\n\tif (buffer.getLengthInBits() > totalDataCount * 8) {\n\t\tthrow new Error(\"code length overflow. (\" + \n buffer.getLengthInBits() + \n \">\" + \n totalDataCount * 8 + \n \")\");\n\t}\n\n\t// end code\n\tif (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {\n\t\tbuffer.put(0, 4);\n\t}\n\n\t// padding\n\twhile (buffer.getLengthInBits() % 8 !== 0) {\n\t\tbuffer.putBit(false);\n\t}\n\n\t// padding\n\twhile (true) {\n\t\t\n\t\tif (buffer.getLengthInBits() >= totalDataCount * 8) {\n\t\t\tbreak;\n\t\t}\n\t\tbuffer.put(QRCode.PAD0, 8);\n\t\t\n\t\tif (buffer.getLengthInBits() >= totalDataCount * 8) {\n\t\t\tbreak;\n\t\t}\n\t\tbuffer.put(QRCode.PAD1, 8);\n\t}\n\n\treturn QRCode.createBytes(buffer, rsBlocks);\n};\n\nQRCode.createBytes = function(buffer, rsBlocks) {\n\n\tvar offset = 0;\n\t\n\tvar maxDcCount = 0;\n\tvar maxEcCount = 0;\n\t\n\tvar dcdata = new Array(rsBlocks.length);\n\tvar ecdata = new Array(rsBlocks.length);\n\t\n\tfor (var r = 0; r < rsBlocks.length; r++) {\n\n\t\tvar dcCount = rsBlocks[r].dataCount;\n\t\tvar ecCount = rsBlocks[r].totalCount - dcCount;\n\n\t\tmaxDcCount = Math.max(maxDcCount, dcCount);\n\t\tmaxEcCount = Math.max(maxEcCount, ecCount);\n\t\t\n\t\tdcdata[r] = new Array(dcCount);\n\t\t\n\t\tfor (var i = 0; i < dcdata[r].length; i++) {\n\t\t\tdcdata[r][i] = 0xff & buffer.buffer[i + offset];\n\t\t}\n\t\toffset += dcCount;\n\t\t\n\t\tvar rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);\n\t\tvar rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1);\n\n\t\tvar modPoly = rawPoly.mod(rsPoly);\n\t\tecdata[r] = new Array(rsPoly.getLength() - 1);\n\t\tfor (var x = 0; x < ecdata[r].length; x++) {\n var modIndex = x + modPoly.getLength() - ecdata[r].length;\n\t\t\tecdata[r][x] = (modIndex >= 0)? modPoly.get(modIndex) : 0;\n\t\t}\n\n\t}\n\t\n\tvar totalCodeCount = 0;\n\tfor (var y = 0; y < rsBlocks.length; y++) {\n\t\ttotalCodeCount += rsBlocks[y].totalCount;\n\t}\n\n\tvar data = new Array(totalCodeCount);\n\tvar index = 0;\n\n\tfor (var z = 0; z < maxDcCount; z++) {\n\t\tfor (var s = 0; s < rsBlocks.length; s++) {\n\t\t\tif (z < dcdata[s].length) {\n\t\t\t\tdata[index++] = dcdata[s][z];\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (var xx = 0; xx < maxEcCount; xx++) {\n\t\tfor (var t = 0; t < rsBlocks.length; t++) {\n\t\t\tif (xx < ecdata[t].length) {\n\t\t\t\tdata[index++] = ecdata[t][xx];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn data;\n\n};\n\nmodule.exports = QRCode;\n","var QRCode = require('./../vendor/QRCode'),\n QRErrorCorrectLevel = require('./../vendor/QRCode/QRErrorCorrectLevel'),\n black = \"\\x1b[40m \\x1b[0m\",\n white = \"\\x1b[47m \\x1b[0m\",\n toCell = function (isBlack) {\n return isBlack ? black : white;\n },\n repeat = function (color) {\n return {\n times: function (count) {\n return new Array(count).join(color);\n }\n };\n },\n fill = function(length, value) {\n var arr = new Array(length);\n for (var i = 0; i < length; i++) {\n arr[i] = value;\n }\n return arr;\n };\n\nmodule.exports = {\n\n error: QRErrorCorrectLevel.L,\n\n generate: function (input, opts, cb) {\n if (typeof opts === 'function') {\n cb = opts;\n opts = {};\n }\n\n var qrcode = new QRCode(-1, this.error);\n qrcode.addData(input);\n qrcode.make();\n\n var output = '';\n if (opts && opts.small) {\n var BLACK = true, WHITE = false;\n var moduleCount = qrcode.getModuleCount();\n var moduleData = qrcode.modules.slice();\n\n var oddRow = moduleCount % 2 === 1;\n if (oddRow) {\n moduleData.push(fill(moduleCount, WHITE));\n }\n\n var platte= {\n WHITE_ALL: '\\u2588',\n WHITE_BLACK: '\\u2580',\n BLACK_WHITE: '\\u2584',\n BLACK_ALL: ' ',\n };\n\n var borderTop = repeat(platte.BLACK_WHITE).times(moduleCount + 3);\n var borderBottom = repeat(platte.WHITE_BLACK).times(moduleCount + 3);\n output += borderTop + '\\n';\n\n for (var row = 0; row < moduleCount; row += 2) {\n output += platte.WHITE_ALL;\n\n for (var col = 0; col < moduleCount; col++) {\n if (moduleData[row][col] === WHITE && moduleData[row + 1][col] === WHITE) {\n output += platte.WHITE_ALL;\n } else if (moduleData[row][col] === WHITE && moduleData[row + 1][col] === BLACK) {\n output += platte.WHITE_BLACK;\n } else if (moduleData[row][col] === BLACK && moduleData[row + 1][col] === WHITE) {\n output += platte.BLACK_WHITE;\n } else {\n output += platte.BLACK_ALL;\n }\n }\n\n output += platte.WHITE_ALL + '\\n';\n }\n\n if (!oddRow) {\n output += borderBottom;\n }\n } else {\n var border = repeat(white).times(qrcode.getModuleCount() + 3);\n\n output += border + '\\n';\n qrcode.modules.forEach(function (row) {\n output += white;\n output += row.map(toCell).join(''); \n output += white + '\\n';\n });\n output += border;\n }\n\n if (cb) cb(output);\n else console.log(output);\n },\n\n setErrorLevel: function (error) {\n this.error = QRErrorCorrectLevel[error] || this.error;\n }\n\n};\n","import m from\"node:crypto\";import _ from\"node:https\";const E={production:\"q.qq.com\",test:\"test.q.qq.com\"};function d(t=\"production\"){return E[t]}function l(){return m.randomBytes(32).toString(\"base64\")}var u;(function(t){t[t.NONE=0]=\"NONE\",t[t.PENDING=1]=\"PENDING\",t[t.COMPLETED=2]=\"COMPLETED\",t[t.EXPIRED=3]=\"EXPIRED\"})(u||(u={}));function b(t,r){const a=Buffer.from(r,\"base64\"),n=Buffer.from(t,\"base64\"),e=n.subarray(0,12),c=n.subarray(n.length-16),s=n.subarray(12,n.length-16),o=m.createDecipheriv(\"aes-256-gcm\",a,e);return o.setAuthTag(c),Buffer.concat([o.update(s),o.final()]).toString(\"utf8\")}function h(t,r,a){return new Promise((n,e)=>{const c=JSON.stringify(r),s=new URL(t),o=_.request({hostname:s.hostname,path:s.pathname+s.search,method:\"POST\",timeout:a,headers:{\"Content-Type\":\"application/json\",Accept:\"application/json\",\"Content-Length\":Buffer.byteLength(c)}},i=>{if(i.statusCode!==200){i.resume(),e(new Error(`HTTP ${i.statusCode} from ${t}`));return}let f=\"\";i.on(\"data\",p=>{f+=p}),i.on(\"end\",()=>{try{n(JSON.parse(f))}catch(p){e(p)}})});o.on(\"error\",e),o.on(\"timeout\",()=>{o.destroy(),e(new Error(`timeout fetching ${t}`))}),o.end(c)})}async function y(t=\"production\",r=1e4){const a=`https://${d(t)}/lite/create_bind_task`,n=l(),e=await h(a,{key:n},r);if(e.retcode!==0)throw new Error(e.msg??\"create_bind_task failed\");if(!e.data?.task_id)throw new Error(\"create_bind_task: missing task_id\");return{taskId:e.data.task_id,key:n}}async function g(t,r=\"production\",a=1e4){const n=`https://${d(r)}/lite/poll_bind_result`,e=await h(n,{task_id:t},a);if(e.retcode!==0)throw new Error(e.msg??\"poll_bind_result failed\");return{status:e.data?.status??u.NONE,botAppId:String(e.data?.bot_appid??\"\"),botEncryptSecret:e.data?.bot_encrypt_secret??\"\",userOpenid:e.data?.user_openid||void 0}}function w(t,r=\"\"){return`https://${d(\"production\")}/qqbot/openclaw/connect.html?task_id=${encodeURIComponent(t)}&source=${encodeURIComponent(r)}&_wv=2`}export{u as BindStatus,w as buildConnectUrl,y as createBindTask,b as decryptSecret,l as generateBindKey,d as getQQBotHost,g as pollBindResult};\n","import E from\"qrcode-terminal\";import{BindStatus as c,buildConnectUrl as w,createBindTask as b,decryptSecret as f,pollBindResult as A}from\"./qqbot-session.js\";const p=2e3;function F(r){return new Promise(o=>{E.generate(r,{small:!0},e=>{o(e)})})}function d(r,o){return new Promise((e,t)=>{if(o?.aborted){t(new DOMException(\"Aborted\",\"AbortError\"));return}const n=setTimeout(e,r);o?.addEventListener(\"abort\",()=>{clearTimeout(n),t(new DOMException(\"Aborted\",\"AbortError\"))},{once:!0})})}async function C(r,o,e){for(;!e?.aborted;){let t;try{t=await A(r)}catch{await d(p,e);continue}if(t.status===c.COMPLETED){const n=f(t.botEncryptSecret,o);return{outcome:\"scanned\",appId:t.botAppId,appSecret:n,userOpenid:t.userOpenid}}if(t.status===c.EXPIRED)return{outcome:\"expired\"};await d(p,e)}throw new DOMException(\"Aborted\",\"AbortError\")}function l(r,o){const e=new AbortController,t=o?.signal?AbortSignal.any([e.signal,o.signal]):e.signal;return(async()=>{const n=o?.displayQrCodeToConsole??!0;for(;;){if(t.aborted)throw new DOMException(\"Aborted\",\"AbortError\");let a;try{a=await b()}catch(u){throw new Error(`\\u83B7\\u53D6\\u7ED1\\u5B9A\\u4EFB\\u52A1\\u5931\\u8D25: ${u instanceof Error?u.message:String(u)}`,{cause:u})}const i=w(a.taskId,o?.source);if(n){const u=await F(i);console.log(u),console.log(`\\u8BF7\\u4F7F\\u7528\\u624B\\u673A QQ \\u626B\\u63CF\\u4E0A\\u65B9\\u4E8C\\u7EF4\\u7801\\uFF0C\\u5B8C\\u6210\\u673A\\u5668\\u4EBA\\u7ED1\\u5B9A\\u3002\n`)}r.onQrDisplayed?.(i);const s=await C(a.taskId,a.key,t);if(s.outcome===\"scanned\"){r.onSuccess([{appId:s.appId,appSecret:s.appSecret,userOpenid:s.userOpenid}]);return}r.onQrExpired?.(),n&&console.log(`\\u4E8C\\u7EF4\\u7801\\u5DF2\\u8FC7\\u671F\\uFF0C\\u6B63\\u5728\\u5237\\u65B0\\u2026\n`)}})().catch(n=>{if(n instanceof DOMException&&n.name===\"AbortError\"){r.onFailure(new Error(\"\\u5DF2\\u53D6\\u6D88\"));return}r.onFailure(n instanceof Error?n:new Error(String(n)))}),()=>e.abort()}function m(r){return new Promise((o,e)=>{l({onSuccess:o,onFailure:e},{...r,displayQrCodeToConsole:!0})})}export{m as qrConnect,l as startQrConnect};\n","import{startQrConnect as t,qrConnect as n}from\"./qr-connect.js\";export{n as qrConnect,t as startQrConnect};\n","/**\n * 账户键解析 — setup / login 共用\n *\n * 统一确定扫码/绑定凭据应写入哪个账户键。\n */\n\nimport type { OpenClawConfig } from 'openclaw/plugin-sdk';\nimport { listQQBotAccountIds, resolveQQBotAccount } from '../config.js';\n\n/**\n * 解析凭据写入的目标账户键。\n *\n * @param cfg 当前配置\n * @param appId 扫码或输入的 AppID\n * @param resolvedId 用户显式指定的账户名(如 --account / setup wizard),可选\n * @returns 账户键\n *\n * 优先级:\n * 1. resolvedId 指定 → 直接使用\n * 2. 已有同 appId 的账户 → 刷新凭据(复用该账户键)\n * 3. 零账户 → 'default'(首次配置)\n * 4. 已有其他账户 → appId(新增独立账户)\n */\nexport function resolveAccountKey(\n cfg: OpenClawConfig,\n appId: string,\n resolvedId?: string | null,\n): string {\n if (resolvedId) return resolvedId;\n\n // 同 appId → 刷新\n for (const id of listQQBotAccountIds(cfg)) {\n if (resolveQQBotAccount(cfg, id).appId === appId) return id;\n }\n\n // 零账户 → default\n if (listQQBotAccountIds(cfg).length === 0) return 'default';\n\n // 新增账户\n return appId;\n}\n","/**\n * openclaw setup 引导 — QQ Bot 扫码/手动绑定\n */\nimport type { OpenClawConfig } from 'openclaw/plugin-sdk';\nimport { DEFAULT_ACCOUNT_ID, formatDocsLink } from '../adapter/setup.js';\nimport { qrConnect } from '@tencent-connect/qqbot-connector';\nimport { applyQQBotAccountConfig, resolveQQBotAccount } from '../config.js';\nimport { resolveAccountKey } from './account-key.js';\n\ntype Prompter = {\n select: (opts: { message: string; options: Array<{ value: string; label: string; hint?: string }> }) => Promise<string>;\n text: (opts: { message: string; validate?: (v: string) => string | undefined }) => Promise<string>;\n note: (msg: string, title?: string) => Promise<void>;\n};\n\ntype Runtime = {\n log: (msg: string) => void;\n error: (msg: string) => void;\n};\n\nfunction isConfigured(cfg: OpenClawConfig, accountId: string): boolean {\n const account = resolveQQBotAccount(cfg, accountId);\n return Boolean(account.appId && account.clientSecret);\n}\n\nasync function linkViaQrCode(cfg: OpenClawConfig, _accountId: string, prompter: Prompter, rt: Runtime): Promise<OpenClawConfig> {\n try {\n const accounts: Array<{ appId: string; appSecret: string; userOpenid?: string }> = await qrConnect({ source: 'openclaw' });\n\n if (accounts.length === 0) {\n await prompter.note('未获取到任何 QQ Bot 账号信息。', 'QQ Bot');\n return cfg;\n }\n\n let next = cfg;\n for (const { appId, appSecret, userOpenid } of accounts) {\n // 同 appId 刷新;零账户 default;已有账户新增 appId\n const key = resolveAccountKey(cfg, appId);\n next = applyQQBotAccountConfig(next, key, { appId, clientSecret: appSecret });\n next = applyAccountDefaults(next, key, userOpenid);\n rt.log(`绑定成功!账户: ${key} (AppID: ${appId})`);\n }\n\n return next;\n } catch (err) {\n rt.error(`QQ Bot 绑定失败: ${String(err)}`);\n await prompter.note(`绑定失败,您可以稍后手动配置。\\n文档: ${formatDocsLink('/channels/qqbot', 'qqbot')}`, 'QQ Bot');\n return cfg;\n }\n}\n\nasync function linkViaManual(cfg: OpenClawConfig, _accountId: string, prompter: Prompter): Promise<OpenClawConfig> {\n const appIdInput = await prompter.text({ message: '请输入 QQ Bot AppID', validate: (v) => v.trim() ? undefined : 'AppID 不能为空' });\n const secret = await prompter.text({ message: '请输入 QQ Bot AppSecret', validate: (v) => v.trim() ? undefined : 'AppSecret 不能为空' });\n const appId = appIdInput.trim();\n const key = resolveAccountKey(cfg, appId);\n let next = applyQQBotAccountConfig(cfg, key, { appId, clientSecret: secret.trim() });\n next = applyAccountDefaults(next, key);\n await prompter.note('✔ QQ Bot 配置完成!', 'QQ Bot');\n return next;\n}\n\nexport async function finalizeQQBotSetup(params: {\n cfg: OpenClawConfig;\n accountId: string;\n prompter: Prompter;\n runtime: Runtime;\n}): Promise<{ cfg: OpenClawConfig }> {\n const accountId = params.accountId.trim() || DEFAULT_ACCOUNT_ID;\n const configured = isConfigured(params.cfg, accountId);\n\n const mode = await params.prompter.select({\n message: configured ? 'QQ 已绑定,选择操作' : '选择 QQ 绑定方式',\n options: [\n { value: 'qr', label: '扫码绑定(推荐)', hint: '使用 QQ 扫描二维码自动完成绑定' },\n { value: 'manual', label: '手动输入 QQ Bot AppID 和 AppSecret', hint: '需到 QQ 开放平台 q.qq.com 查看' },\n { value: 'skip', label: configured ? '保持当前配置' : '稍后配置' },\n ],\n });\n\n let next = params.cfg;\n if (mode === 'qr') {\n next = await linkViaQrCode(next, accountId, params.prompter, params.runtime);\n } else if (mode === 'manual') {\n next = await linkViaManual(next, accountId, params.prompter);\n } else if (!configured) {\n await params.prompter.note('您可以稍后运行以下命令重新配置:\\n openclaw channels add', 'QQ Bot');\n }\n\n return { cfg: next };\n}\n\nexport function applyAccountDefaults(cfg: OpenClawConfig, accountId: string, userOpenid?: string): OpenClawConfig {\n const next = { ...cfg, channels: { ...cfg.channels } };\n const qqbot = { ...(next.channels?.qqbot as Record<string, unknown> ?? {}) } as Record<string, unknown>;\n\n const defaults: Record<string, unknown> = { streaming: { mode: 'partial' }, dmPolicy: 'allowlist', mediaMaxMb: 200 };\n if (userOpenid) defaults.allowFrom = [userOpenid];\n\n if (accountId === DEFAULT_ACCOUNT_ID) {\n Object.assign(qqbot, defaults);\n } else {\n const accounts = { ...(qqbot.accounts as Record<string, unknown> ?? {}) };\n accounts[accountId] = { ...(accounts[accountId] as Record<string, unknown> ?? {}), ...defaults };\n qqbot.accounts = accounts;\n }\n\n next.channels = { ...next.channels, qqbot };\n return next;\n}\n","/**\n * @tencent-connect/openclaw-qqbot\n *\n * 独立版 QQ Bot 通道插件 — 基于 @tencent-connect/qqbot-nodejs SDK 重构。\n *\n * 直接依赖:\n * - openclaw/plugin-sdk:OpenClaw 插件框架\n * - @tencent-connect/qqbot-nodejs:QQ 开放平台 Node.js SDK\n */\nimport type { OpenClawPluginApi } from 'openclaw/plugin-sdk';\nimport { emptyPluginConfigSchema } from 'openclaw/plugin-sdk';\n\nimport { qqbotPlugin } from './src/channel.js';\nimport { setQQBotRuntime } from './src/runtime.js';\nimport { registerPlatformTool } from './src/tools/platform.js';\nimport { registerRemindTool } from './src/tools/remind.js';\nimport { verifyRuntimeContract } from './src/adapter/contract.js';\n\nlet registered = false;\n\nconst plugin = {\n id: 'openclaw-qqbot',\n name: 'QQ Bot',\n description: 'QQ Bot channel plugin',\n configSchema: emptyPluginConfigSchema(),\n register(api: OpenClawPluginApi) {\n setQQBotRuntime(api.runtime);\n\n if (!registered) {\n registered = true;\n const contract = verifyRuntimeContract(api.runtime);\n if (!contract.ok) {\n throw new Error(\n `openclaw-qqbot incompatible with openclaw ${contract.version}: ` +\n `missing required APIs: ${contract.missing.join(', ')}. ` +\n `Please upgrade openclaw or downgrade the plugin.`,\n );\n }\n }\n\n api.registerChannel({ plugin: qqbotPlugin as any });\n registerPlatformTool(api);\n registerRemindTool(api);\n },\n};\n\nexport default plugin;\n\n// ── Public API exports ──\n\nexport { qqbotPlugin } from './src/channel.js';\nexport { setQQBotRuntime, getQQBotRuntime } from './src/runtime.js';\nexport { getBotForAccount, tryGetBotForAccount, buildUserAgent } from './src/bot-instance.js';\nexport { qqbotOnboardingAdapter } from './src/features/onboarding.js';\nexport { QQBotGateway } from './src/gateway/index.js';\nexport { sendText, sendMedia } from './src/outbound/outbound-service.js';\nexport { parseTarget } from './src/outbound/target.js';\nexport { dispatchToOpenClaw } from './src/dispatch/index.js';\nexport {\n PersistedRefIndexStore,\n getPersistedRefIndexStore,\n flushAllRefIndexStores,\n} from './src/features/ref-index-store.js';\nexport {\n StreamingController,\n shouldUseStreaming,\n} from './src/outbound/streaming-controller.js';\nexport * from './src/types.js';\nexport * from './src/config.js';\n","/**\n * QQ Bot ChannelPlugin 定义\n *\n * 薄壳编排层 — 实现 OpenClaw ChannelPlugin 接口,\n * 将各子模块(gateway/outbound/config/features)连接为完整通道插件。\n */\nimport {\n type ChannelPlugin,\n type OpenClawConfig,\n applyAccountNameToChannelSection,\n deleteAccountFromConfigSection,\n setAccountEnabledInConfigSection,\n} from 'openclaw/plugin-sdk/core';\n\nimport type { ResolvedQQBotAccount } from './types.js';\nimport {\n DEFAULT_ACCOUNT_ID,\n listQQBotAccountIds,\n resolveQQBotAccount,\n applyQQBotAccountConfig,\n resolveDefaultQQBotAccountId,\n resolveRequireMention,\n resolveToolPolicy,\n resolveGroupConfig,\n} from './config.js';\nimport { getQQBotRuntime, tryGetQQBotRuntime } from './runtime.js';\nimport { getAdapters } from './adapter/resolve.js';\nimport { sendText, getGateway } from './outbound/outbound-service.js';\nimport { sendMedia } from './outbound/media-send.js';\nimport type { PluginLogger } from './utils/plugin-logger.js';\nimport { qqbotSetupWizard } from './setup/surface.js';\nimport { qqbotLogin, startQrLogin, waitQrLogin } from './setup/login.js';\nimport { normalizeTarget, isQQBotTarget } from './outbound/target.js';\nimport { sanitizeQQBotText } from './outbound/sanitize.js';\nimport { startAccountWithCredentialRecovery, logoutAndClearCredentials, stopAccountGracefully } from './gateway/lifecycle.js';\nimport { loadCredentialBackup } from './features/credential-backup.js';\nimport { isApprovalPayload, approvalStubs } from './features/approval-utils.js';\nimport { qqbotOnboardingAdapter } from './features/onboarding.js';\nimport { stripMentionText } from './utils/mention.js';\n\n/** QQ Bot 单条消息文本长度上限 */\nexport const TEXT_CHUNK_LIMIT = 5000;\n\n// ── GFM 表格检测 ──\n\n/** GFM 表格数据行: | col1 | col2 | */\nconst GFM_TABLE_DATA_RE = /^\\|.+\\|.*\\|/;\n/** GFM 表格分隔行: |---|:---:|---| (1 个或多于 1 个破折号,支持对齐冒号) */\nconst GFM_TABLE_SEP_RE = /^\\|[\\s:-]+\\|/;\n\n/**\n * 判断一行是否为 GFM 表格行(数据行或分隔行)。\n * 保障 table-aware chunker 不会在表格内部切分。\n */\nfunction isGfmTableLine(line: string): boolean {\n return GFM_TABLE_DATA_RE.test(line) || GFM_TABLE_SEP_RE.test(line);\n}\n\nexport const qqbotPlugin: ChannelPlugin<ResolvedQQBotAccount> = {\n id: 'qqbot',\n meta: {\n id: 'qqbot',\n label: 'QQ Bot',\n selectionLabel: 'QQ Bot',\n docsPath: '/docs/channels/qqbot',\n blurb: 'Connect to QQ via official QQ Bot API',\n order: 50,\n },\n capabilities: {\n chatTypes: ['direct', 'group'],\n media: true,\n reactions: false,\n threads: false,\n blockStreaming: false,\n },\n gatewayMethods: ['web.login.start', 'web.login.wait'],\n reload: { configPrefixes: ['channels.qqbot'] },\n\n // ── 群消息策略 ──\n groups: {\n resolveRequireMention: ({ cfg, accountId, groupId }) => {\n if (!groupId) return undefined;\n return resolveRequireMention(cfg, groupId, accountId ?? undefined);\n },\n resolveToolPolicy: ({ cfg, accountId, groupId }) => {\n if (!groupId) return undefined;\n const policy = resolveToolPolicy(cfg, groupId, accountId ?? undefined);\n if (policy === 'full') return undefined;\n if (policy === 'none') return { allow: [], deny: ['*'] };\n return { allow: [] };\n },\n resolveGroupIntroHint: ({ cfg, accountId, groupId }) => {\n if (!groupId) return undefined;\n const groupCfg = resolveGroupConfig(cfg, groupId, accountId ?? undefined);\n return groupCfg.name ? `当前群: ${groupCfg.name}` : undefined;\n },\n },\n\n // ── @mention 检测与清理 ──\n mentions: {\n stripMentions: ({ text, ctx }) => {\n const mentions = (ctx as any)?.mentions;\n return stripMentionText(text, mentions);\n },\n },\n\n // @ts-ignore onboarding 兼容\n onboarding: qqbotOnboardingAdapter,\n\n // ── 配置管理 ──\n config: {\n listAccountIds: (cfg) => listQQBotAccountIds(cfg),\n resolveAccount: (cfg, accountId) => resolveQQBotAccount(cfg, accountId),\n defaultAccountId: (cfg) => resolveDefaultQQBotAccountId(cfg),\n setAccountEnabled: ({ cfg, accountId, enabled }) =>\n setAccountEnabledInConfigSection({ cfg, sectionKey: 'qqbot', accountId, enabled, allowTopLevel: true }),\n deleteAccount: ({ cfg, accountId }) =>\n deleteAccountFromConfigSection({\n cfg, sectionKey: 'qqbot', accountId,\n clearBaseFields: ['appId', 'clientSecret', 'clientSecretFile', 'name'],\n }),\n isConfigured: (account) => {\n if (account?.appId && account?.clientSecret) return true;\n return loadCredentialBackup(account?.accountId) !== null;\n },\n describeAccount: (account) => ({\n accountId: account?.accountId ?? DEFAULT_ACCOUNT_ID,\n name: account?.name,\n enabled: account?.enabled ?? false,\n configured: Boolean(account?.appId && account?.clientSecret),\n tokenSource: account?.secretSource,\n }),\n resolveAllowFrom: ({ cfg, accountId }: { cfg: OpenClawConfig; accountId?: string | null }) => {\n const account = resolveQQBotAccount(cfg, accountId ?? undefined);\n return (account.config?.allowFrom ?? []).map((e: string | number) => String(e)) as (string | number)[];\n },\n formatAllowFrom: ({ allowFrom }: { allowFrom: (string | number)[] }) =>\n allowFrom\n .map((e: string | number) => String(e).trim())\n .filter(Boolean)\n .map((e: string) => e.replace(/^qqbot:/i, '').toUpperCase()),\n },\n\n // ── Setup ──\n setup: {\n resolveAccountId: ({ accountId }) => accountId?.trim().toLowerCase() || DEFAULT_ACCOUNT_ID,\n applyAccountName: ({ cfg, accountId, name }) =>\n applyAccountNameToChannelSection({ cfg, channelKey: 'qqbot', accountId, name }),\n validateInput: ({ input }) => {\n if (!input.token && !input.tokenFile && !input.useEnv) {\n return 'QQBot requires --token (format: appId:clientSecret) or --use-env';\n }\n return null;\n },\n applyAccountConfig: ({ cfg, accountId, input }) => {\n let appId = '';\n let clientSecret = '';\n if (input.token) {\n const parts = input.token.split(':');\n if (parts.length === 2) { appId = parts[0]; clientSecret = parts[1]; }\n }\n return applyQQBotAccountConfig(cfg, accountId, {\n appId, clientSecret,\n clientSecretFile: input.tokenFile,\n name: input.name,\n }) as OpenClawConfig;\n },\n },\n\n setupWizard: qqbotSetupWizard,\n\n // ── Messaging ──\n messaging: {\n normalizeTarget,\n targetResolver: {\n looksLikeId: isQQBotTarget,\n hint: 'QQ Bot 目标格式: qqbot:c2c:openid (私聊) 或 qqbot:group:groupid (群聊)',\n },\n },\n\n // ── 出站 ──\n outbound: {\n deliveryMode: 'direct' as const,\n sanitizeText: ({ text }: { text: string; payload: any }) => sanitizeQQBotText(text),\n chunker: (text, limit) => {\n const adapters = getAdapters(getQQBotRuntime());\n if (adapters.chunkMarkdownText) return adapters.chunkMarkdownText(text, limit);\n // fallback(低版本降级): 按换行边界切分,保留 Markdown 表格完整性\n const lines = text.split('\\n');\n const chunks: string[] = [];\n let current = '';\n let tableBuffer: string[] = [];\n\n const flushTable = () => {\n if (tableBuffer.length === 0) return;\n const tableBlock = tableBuffer.join('\\n');\n const candidate = current ? `${current}\\n${tableBlock}` : tableBlock;\n if (candidate.length > limit && current) {\n chunks.push(current);\n current = tableBlock;\n } else {\n current = candidate;\n }\n tableBuffer = [];\n };\n\n for (const line of lines) {\n if (isGfmTableLine(line)) {\n tableBuffer.push(line);\n continue;\n }\n\n // 遇到非表格行,先刷新缓冲的表格\n flushTable();\n\n const candidate = current ? `${current}\\n${line}` : line;\n if (candidate.length > limit && current) {\n chunks.push(current);\n current = line;\n } else {\n current = candidate;\n }\n }\n // 处理末尾的表格缓冲\n flushTable();\n if (current) chunks.push(current);\n return chunks.length > 0 ? chunks : [text];\n },\n chunkerMode: 'markdown',\n textChunkLimit: TEXT_CHUNK_LIMIT,\n shouldSuppressLocalPayloadPrompt: ({ payload }: any) => isApprovalPayload(payload),\n sendText: async ({ to, text, accountId, replyToId, cfg }) => {\n const account = resolveQQBotAccount(cfg, accountId ?? undefined);\n const outLog = createOutLog(account.accountId);\n outLog.debug(`sendText to=${to} len=${text.length} replyTo=${replyToId ?? '-'}`);\n const result = await sendText({ to, text, accountId, replyToId, account });\n if (result.error) throw new Error(result.error);\n return { channel: 'qqbot' as const, messageId: result.messageId ?? '' };\n },\n sendMedia: async ({ to, text, mediaUrl, accountId, replyToId, cfg }) => {\n const resolvedAccountId = accountId ?? resolveDefaultQQBotAccountId(cfg);\n const outLog = createOutLog(resolvedAccountId);\n outLog.debug(`sendMedia to=${to} url=${mediaUrl?.slice(0, 80)} len=${text?.length ?? 0} replyTo=${replyToId ?? '-'}`);\n const result = await sendMedia({\n to,\n source: mediaUrl ?? '',\n text,\n replyToId,\n accountId: resolvedAccountId,\n log: outLog,\n agentId: resolveMCPAgentId(to, resolvedAccountId, cfg, outLog),\n });\n if (result.error) {\n outLog.error(`sendMedia failed: ${result.error}`);\n throw new Error(result.error);\n }\n return { channel: 'qqbot' as const, messageId: result.messageId ?? '' };\n },\n },\n\n // ── 网关 ──\n gateway: {\n startAccount: (ctx) => startAccountWithCredentialRecovery(ctx),\n stopAccount: async (ctx: { accountId: string; log?: any }) => {\n await stopAccountGracefully({\n accountId: ctx.accountId,\n log: ctx.log,\n });\n },\n logoutAccount: (params) => logoutAndClearCredentials(params),\n loginWithQrStart: async ({ accountId }: { accountId?: string }) => startQrLogin(accountId),\n loginWithQrWait: async ({ accountId }: { accountId?: string }) => {\n const result = await waitQrLogin(accountId);\n return { connected: result.connected, message: result.message };\n },\n },\n\n // ── 登录认证 ──\n auth: {\n login: qqbotLogin as any,\n // 审批权限(从 approvalStubs 迁移)\n authorizeActorAction: () => ({ authorized: true } as const),\n getActionAvailabilityState: () => ({ kind: 'enabled' as const } as const),\n },\n\n // ── 状态 ──\n status: {\n defaultRuntime: {\n accountId: DEFAULT_ACCOUNT_ID,\n running: false,\n connected: false,\n lastConnectedAt: null,\n lastError: null,\n lastInboundAt: null,\n lastOutboundAt: null,\n },\n buildChannelSummary: ({ snapshot }) => ({\n configured: snapshot.configured ?? false,\n tokenSource: snapshot.tokenSource ?? 'none',\n running: snapshot.running ?? false,\n connected: snapshot.connected ?? false,\n lastConnectedAt: snapshot.lastConnectedAt ?? null,\n lastError: snapshot.lastError ?? null,\n }),\n buildAccountSnapshot: ({ account, runtime }) => ({\n accountId: account?.accountId ?? DEFAULT_ACCOUNT_ID,\n name: account?.name,\n enabled: account?.enabled ?? false,\n configured: Boolean(account?.appId && account?.clientSecret),\n tokenSource: account?.secretSource,\n running: Boolean(runtime?.running ?? false),\n connected: Boolean(runtime?.connected ?? false),\n lastConnectedAt: runtime?.lastConnectedAt ?? null,\n lastError: runtime?.lastError ?? null,\n lastInboundAt: runtime?.lastInboundAt ?? null,\n lastOutboundAt: runtime?.lastOutboundAt ?? null,\n }),\n },\n\n // ── 审批(stub — 实际由 features/approval-handler 处理)──\n ...approvalStubs,\n};\n\nfunction resolveMCPAgentId(to: string, accountId: string, cfg: unknown, log?: PluginLogger): string | undefined {\n try {\n const parts = to.split(':');\n const scope = parts[1];\n const peerId = parts[2];\n if (!scope || !peerId) return undefined;\n const rt = tryGetQQBotRuntime();\n if (!rt) return undefined;\n const route = getAdapters(rt).resolveAgentRoute?.({\n cfg, channel: 'qqbot', accountId,\n peer: { kind: scope === 'group' ? 'group' : 'direct', id: peerId },\n });\n log?.debug(`resolveMCPAgentId to=${to} => agentId=${route?.agentId ?? 'none'}`);\n return route?.agentId;\n } catch { return undefined; }\n}\n\nfunction createOutLog(accountId: string): PluginLogger {\n const gwLog = getGateway(accountId)?.log;\n return gwLog?.child('outbound') ?? ({} as PluginLogger);\n}\n\n// Re-export for backward compatibility\nexport { stripMentionText } from './utils/mention.js';\nexport { detectWasMentioned } from './utils/mention.js';\n","/**\n * Bot 实例访问器\n *\n * 提供统一的方式获取当前活跃的 QQBot SDK 实例。\n * 替代原 api.ts 的所有功能 — 消费者通过 getBotForAccount() 获取 bot,\n * 然后使用 bot.api / bot.send() 等 SDK 原生能力。\n */\nimport os from 'node:os';\nimport type { QQBot } from '@tencent-connect/qqbot-nodejs';\nimport { getGateway } from './outbound/outbound-service.js';\nimport { getPackageVersion } from './utils/pkg-version.js';\n\n\nconst PLUGIN_VERSION = getPackageVersion();\nlet _openclawVersion = 'unknown';\n\nexport function setOpenClawVersion(version: string): void {\n if (version) _openclawVersion = version;\n}\n\nexport function getOpenClawVersion(): string {\n return _openclawVersion;\n}\n\nexport function buildUserAgent(suffix?: string): string {\n const base = `QQBotPlugin/${PLUGIN_VERSION} (Node/${process.versions.node}; ${os.platform()}; OpenClaw/${_openclawVersion})`;\n return suffix ? `${base} ${suffix}` : base;\n}\n\n// ── Bot 实例获取 ──\n\n/**\n * 获取指定账户的 QQBot SDK 实例。\n *\n * @throws 如果该账户的 gateway 尚未启动\n *\n * @example\n * ```ts\n * const bot = getBotForAccount(accountId);\n * await bot.send({ target, content: 'hello' });\n * const guilds = await bot.api.get('/users/@me/guilds');\n * const token = await bot.api.getToken();\n * ```\n */\nexport function getBotForAccount(accountId: string): QQBot {\n const gw = getGateway(accountId);\n if (!gw) {\n throw new Error(`[qqbot] Bot \"${accountId}\" not running — gateway not started`);\n }\n return gw.bot;\n}\n\n/**\n * 尝试获取指定账户的 QQBot SDK 实例(不抛异常)。\n * 返回 null 表示 gateway 尚未启动。\n */\nexport function tryGetBotForAccount(accountId: string): QQBot | null {\n const gw = getGateway(accountId);\n return gw?.bot ?? null;\n}\n","/**\n * 出站消息服务\n *\n * 负责将 AI 回复通过 QQBotGateway 发送到 QQ。\n * 超时保护由 QQBotGateway 内部统一处理,本层做 target 解析 + 被动回复限额管控。\n */\nimport * as path from 'node:path';\nimport { MediaFileType } from '@tencent-connect/qqbot-nodejs';\nimport type { QQBotGateway } from '../gateway/index.js';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport { parseTarget } from './target.js';\nimport { ReplyLimiter } from './reply-limiter.js';\n\n// ── Gateway + Limiter 注册表(生命周期由 channel.ts 管理)──\n\nconst gateways = new Map<string, QQBotGateway>();\nconst limiters = new Map<string, ReplyLimiter>();\n\nfunction getLimiter(accountId: string): ReplyLimiter {\n let l = limiters.get(accountId);\n if (!l) {\n l = new ReplyLimiter();\n limiters.set(accountId, l);\n }\n return l;\n}\n\n/**\n * 解析 replyToId:超出被动回复限额时自动降级为主动消息(不传 msgId)。\n * @returns 实际使用的 msgId(超限时为 undefined)\n */\nfunction resolveMsgId(replyToId: string | undefined, accountId: string): string | undefined {\n if (!replyToId) return undefined;\n const limiter = getLimiter(accountId);\n const result = limiter.checkLimit(replyToId);\n if (!result.allowed) return undefined;\n limiter.record(replyToId);\n return replyToId;\n}\n\nexport function registerGateway(accountId: string, gw: QQBotGateway): void {\n gateways.set(accountId, gw);\n}\n\nexport function unregisterGateway(accountId: string): void {\n gateways.delete(accountId);\n limiters.delete(accountId);\n}\n\nexport function getGateway(accountId: string): QQBotGateway | undefined {\n return gateways.get(accountId);\n}\n\n// ── 媒体类型映射 ──\n\nexport type MediaKind = 'image' | 'voice' | 'video' | 'file';\n\nconst MEDIA_KIND_TO_FILE_TYPE: Record<MediaKind, MediaFileType> = {\n image: MediaFileType.IMAGE,\n voice: MediaFileType.VOICE,\n video: MediaFileType.VIDEO,\n file: MediaFileType.FILE,\n};\n\nexport interface SendResult {\n messageId?: string;\n error?: string;\n errorCode?: string;\n qqBizCode?: number;\n}\n\n// ── 公开 API(channel.ts / deliver-pipeline.ts 调用)──\n\nexport async function sendText(params: {\n to: string;\n text: string;\n accountId?: string;\n replyToId?: string;\n account: ResolvedQQBotAccount;\n}): Promise<SendResult> {\n const accountId = params.account.accountId;\n const gw = gateways.get(accountId);\n if (!gw) return { error: `Bot \"${accountId}\" not running` };\n try {\n const target = parseTarget(params.to);\n const msgId = resolveMsgId(params.replyToId, accountId);\n const result = await gw.sendText(target, params.text, { msgId });\n return { messageId: result.id };\n } catch (err: unknown) { return formatError(err); }\n}\n\nexport async function sendMedia(params: {\n to: string;\n text?: string;\n mediaUrl: string;\n mediaKind?: MediaKind;\n accountId?: string;\n replyToId?: string;\n account: ResolvedQQBotAccount;\n}): Promise<SendResult> {\n const accountId = params.account.accountId;\n const gw = gateways.get(accountId);\n if (!gw) return { error: `Bot \"${accountId}\" not running` };\n try {\n const target = parseTarget(params.to);\n const kind = params.mediaKind ?? 'image';\n const msgId = resolveMsgId(params.replyToId, accountId);\n if (kind === 'voice') {\n const source = resolveVoiceSource(params.mediaUrl);\n const result = await gw.sendVoice(target, source, { text: params.text, msgId });\n return { messageId: result.id };\n }\n if (kind === 'video') {\n const result = await gw.sendVideo(target, params.mediaUrl, { text: params.text, msgId });\n return { messageId: result.id };\n }\n if (kind === 'file') {\n const result = await gw.sendFile(target, params.mediaUrl, { text: params.text, msgId });\n return { messageId: result.id };\n }\n const fileType = MEDIA_KIND_TO_FILE_TYPE[kind];\n const result = await gw.sendMedia(target, params.mediaUrl, { text: params.text, msgId, fileType });\n return { messageId: result.id };\n } catch (err: unknown) { return formatError(err); }\n}\n\nexport async function sendVoice(params: {\n to: string;\n source: { url?: string; base64?: string };\n accountId?: string;\n replyToId?: string;\n account: ResolvedQQBotAccount;\n}): Promise<SendResult> {\n const accountId = params.account.accountId;\n const gw = gateways.get(accountId);\n if (!gw) return { error: `Bot \"${accountId}\" not running` };\n try {\n const target = parseTarget(params.to);\n const msgId = resolveMsgId(params.replyToId, accountId);\n const result = await gw.sendVoice(target, params.source, { msgId });\n return { messageId: result.id };\n } catch (err: unknown) { return formatError(err); }\n}\n\nexport async function sendVideo(params: {\n to: string;\n videoUrl: string;\n accountId?: string;\n replyToId?: string;\n account: ResolvedQQBotAccount;\n}): Promise<SendResult> {\n const accountId = params.account.accountId;\n const gw = gateways.get(accountId);\n if (!gw) return { error: `Bot \"${accountId}\" not running` };\n try {\n const target = parseTarget(params.to);\n const msgId = resolveMsgId(params.replyToId, accountId);\n const result = await gw.sendVideo(target, params.videoUrl, { msgId });\n return { messageId: result.id };\n } catch (err: unknown) { return formatError(err); }\n}\n\n// ── OutboundService(deliver-pipeline 专用)──\n\nexport class OutboundService {\n constructor(private readonly gw: QQBotGateway, private readonly accountId: string) {}\n\n async sendText(to: string, text: string, msgId?: string): Promise<SendResult> {\n try {\n const target = parseTarget(to);\n const resolvedMsgId = resolveMsgId(msgId, this.accountId);\n const result = await this.gw.sendText(target, text, { msgId: resolvedMsgId });\n return { messageId: result.id };\n } catch (err: unknown) { return formatError(err); }\n }\n\n async sendMedia(to: string, source: string, opts?: { text?: string; msgId?: string; mediaKind?: MediaKind }): Promise<SendResult> {\n try {\n const target = parseTarget(to);\n const kind = opts?.mediaKind ?? 'image';\n const resolvedMsgId = resolveMsgId(opts?.msgId, this.accountId);\n if (kind === 'voice') {\n const voiceSource = resolveVoiceSource(source);\n const result = await this.gw.sendVoice(target, voiceSource, { text: opts?.text, msgId: resolvedMsgId });\n return { messageId: result.id };\n }\n if (kind === 'video') {\n const result = await this.gw.sendVideo(target, source, { text: opts?.text, msgId: resolvedMsgId });\n return { messageId: result.id };\n }\n if (kind === 'file') {\n const result = await this.gw.sendFile(target, source, { text: opts?.text, msgId: resolvedMsgId, fileName: path.basename(source) });\n return { messageId: result.id };\n }\n const fileType = MEDIA_KIND_TO_FILE_TYPE[kind];\n const result = await this.gw.sendMedia(target, source, { text: opts?.text, msgId: resolvedMsgId, fileType });\n return { messageId: result.id };\n } catch (err: unknown) { return formatError(err); }\n }\n}\n\n// ── 辅助 ──\n\nfunction resolveVoiceSource(source: string): { url?: string; base64?: string; localPath?: string } {\n if (source.startsWith('http://') || source.startsWith('https://')) return { url: source };\n if (source.startsWith('/') || source.startsWith('./') || source.startsWith('../')) return { localPath: source };\n if (source.startsWith('data:')) {\n const i = source.indexOf(',');\n return { base64: i > 0 ? source.slice(i + 1) : source };\n }\n return { base64: source };\n}\n\nfunction formatError(err: unknown): SendResult {\n if (err instanceof Error) {\n const result: SendResult = { error: err.message };\n if ('code' in err) result.errorCode = String((err as any).code);\n if ('qqBizCode' in err) result.qqBizCode = (err as any).qqBizCode;\n return result;\n }\n return { error: String(err) };\n}\n","/**\n * High-level facade for the QQ Open Platform Node.js SDK.\n *\n * 这是大部分用户的主要入口。它把协议层(HTTP / WebSocket / 媒体上传 / 流式\n * 消息)组合成一个清晰的「机器人客户端」对象,类似 `@line/bot-sdk` 或\n * `@larksuiteoapi/node-sdk` 在其他平台上的角色。\n *\n * Example:\n * ```ts\n * import { QQBot } from \"@tencent-connect/qqbot-nodejs\";\n *\n * const bot = new QQBot({\n * appId: process.env.QQBOT_APP_ID!,\n * appSecret: process.env.QQBOT_APP_SECRET!,\n * });\n *\n * bot.on(\"message\", async (ctx, msg) => {\n * await bot.sendText(msg.replyTarget, `Echo: ${msg.content}`);\n * });\n *\n * await bot.start();\n * ```\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport {\n createMiddlewareContext,\n runMiddlewareChain,\n type Middleware,\n type MiddlewareContext,\n} from \"./middleware/types.js\";\nimport { ApiClient } from \"./protocol/api/api-client.js\";\nimport { ChunkedMediaApi } from \"./protocol/api/media-chunked.js\";\nimport { MediaApi } from \"./protocol/api/media.js\";\nimport { MessageApi } from \"./protocol/api/messages.js\";\nimport { TokenManager } from \"./protocol/api/token.js\";\nimport { GatewayConnection, type SessionPersistencePort } from \"./protocol/gateway/gateway-connection.js\";\nimport type { InboundMessage } from \"./protocol/gateway/event-dispatcher.js\";\nimport { WebhookTransport } from \"./protocol/transport/webhook.js\";\nimport type { EventTransport, WebhookServerAdapter } from \"./protocol/transport/types.js\";\nimport {\n MediaFileType,\n type ChatScope,\n type Credentials,\n type GatewayAccount,\n type InlineKeyboard,\n type InteractionEvent,\n type Logger,\n type MessageResponse,\n type UploadMediaResponse,\n} from \"./protocol/types.js\";\nimport { LARGE_FILE_THRESHOLD, sanitizeFileName } from \"./protocol/utils/file-utils.js\";\nimport { UploadCache } from \"./protocol/utils/upload-cache.js\";\nimport { StreamSession, type StreamSessionOptions } from \"./streaming.js\";\n\n// ============ Public types ============\n\n/** QQ Open Platform message type codes (msg_type). */\nexport const MsgType = {\n /** Plain text. */\n TEXT: 0,\n /** Markdown. */\n MARKDOWN: 2,\n /** Ark template message. */\n ARK: 3,\n /** Embed message. */\n EMBED: 4,\n /** Rich media (image/video/voice/file). */\n MEDIA: 7,\n} as const;\nexport type MsgType = (typeof MsgType)[keyof typeof MsgType];\n\n/** Universal message send options — supports all QQ platform message types. */\nexport interface SendMessageOptions {\n /** Target peer. When `msgId` is present → reply; otherwise → proactive. */\n target: ReplyTarget;\n /** Message type (auto-detected if omitted). */\n msgType?: MsgType;\n /** Plain text content. */\n content?: string;\n /** Markdown payload (msg_type=2). */\n markdown?: { content: string; custom_template_id?: string; params?: Array<{ key: string; values: string[] }> };\n /** Ark template payload (msg_type=3). */\n ark?: { template_id: number; kv: Array<{ key: string; value?: string; obj?: unknown[] }> };\n /** Embed payload (msg_type=4). */\n embed?: Record<string, unknown>;\n /** Rich media reference (msg_type=7, from uploadMedia response). */\n media?: { file_info: string };\n /** Inline keyboard (buttons). */\n keyboard?: InlineKeyboard;\n /** Message reference (quote/reply). */\n messageReference?: { message_id: string };\n /** Pass-through extra fields (for new platform features not yet typed). */\n extra?: Record<string, unknown>;\n}\n\n/** API Gateway interface — call any QQ Open Platform REST API. */\nexport interface ApiGateway {\n /**\n * Authenticated GET request.\n * @param query — values are auto-stringified (numbers accepted).\n */\n get<T = unknown>(path: string, query?: Record<string, string | number | boolean>): Promise<T>;\n /** Authenticated POST request. */\n post<T = unknown>(path: string, body?: unknown): Promise<T>;\n /** Authenticated PUT request. */\n put<T = unknown>(path: string, body?: unknown): Promise<T>;\n /** Authenticated PATCH request. */\n patch<T = unknown>(path: string, body?: unknown): Promise<T>;\n /** Authenticated DELETE request. */\n delete<T = unknown>(path: string): Promise<T>;\n /** Get the current valid access token (for advanced/custom usage). */\n getToken(): Promise<string>;\n}\n\n/** Resolved peer for outbound messages. */\nexport interface ReplyTarget {\n /** \"c2c\" for private chat, \"group\" for group chat. */\n scope: ChatScope;\n /** User openid (c2c) or group openid (group). */\n targetId: string;\n /** Inbound message id — used to associate the reply with the trigger. */\n msgId?: string;\n}\n\nexport interface QQBotOptions {\n /** QQ Open Platform AppID. */\n appId: string;\n /** QQ Open Platform AppSecret. */\n appSecret: string;\n /** Stable account identifier used for logging / session persistence. */\n accountId?: string;\n /** Whether the bot has markdown permission. Defaults to false. */\n markdownSupport?: boolean;\n /** Logger. Defaults to a no-op logger. */\n logger?: Logger;\n /** User-Agent header. Defaults to `qqbot-nodejs/<version>`. */\n userAgent?: string;\n /** Override the QQ Open Platform API base URL (mostly for tests). */\n baseUrl?: string;\n /** Override the token endpoint base URL. Defaults to `https://bots.qq.com`. */\n tokenBaseUrl?: string;\n /** Optional session persistence (e.g. file-backed). */\n sessionPersistence?: SessionPersistencePort;\n /** Custom intent mask. Defaults to FULL_INTENTS (group + c2c + interaction). */\n intents?: number;\n /** Override the upload cache. Defaults to a private in-memory cache. */\n uploadCache?: UploadCache;\n\n /**\n * Event transport mode.\n * - `\"websocket\"` (default): long-lived WS connection with heartbeat/RESUME.\n * - `\"webhook\"`: QQ POSTs events to your HTTPS endpoint.\n * - Custom `EventTransport` instance for advanced use.\n */\n transport?: \"websocket\" | \"webhook\" | EventTransport;\n\n /**\n * Token prefetch strategy at startup.\n * - `\"sync\"` (default): Await token fetch before accepting traffic.\n * Provides fail-fast semantics — credential errors surface immediately.\n * - `\"async\"`: Start accepting traffic immediately; token is fetched in\n * the background. Faster startup but first requests may fail if token\n * is not yet available.\n */\n tokenPrefetch?: \"sync\" | \"async\";\n\n /** Webhook-specific options (only used when `transport: \"webhook\"`). */\n webhook?: {\n /** Port to listen on. Defaults to 8080. */\n port?: number;\n /** Path to listen on. Defaults to \"/\". */\n path?: string;\n /** Bring your own HTTP server adapter (Express/Fastify/Koa). */\n server?: WebhookServerAdapter;\n };\n}\n\n/** Lightweight context for interaction events (pre-reserved for future middleware). */\nexport interface InteractionContext {\n /** The QQBot instance. */\n readonly bot: QQBot;\n /** The interaction event. */\n readonly event: InteractionEvent;\n /** Per-request state slot (for future middleware to share data). */\n state: Record<string, unknown>;\n /** Timestamp when the SDK received this event. */\n readonly receivedAt: number;\n}\n\n/** Lightweight context for raw gateway events (pre-reserved for future middleware). */\nexport interface RawEventContext {\n /** The QQBot instance. */\n readonly bot: QQBot;\n /** Gateway event type string (e.g. \"GUILD_CREATE\", \"FRIEND_ADD\"). */\n readonly eventType: string;\n /** Raw event data from the platform. */\n readonly data: unknown;\n /** Per-request state slot (for future middleware to share data). */\n state: Record<string, unknown>;\n /** Timestamp when the SDK received this event. */\n readonly receivedAt: number;\n}\n\n/** Built-in event names. */\nexport type QQBotEventMap = {\n ready: (data: unknown) => void;\n resumed: (data: unknown) => void;\n error: (err: Error) => void;\n message: (ctx: MiddlewareContext, msg: QQBotInboundMessage) => void | Promise<void>;\n interaction: (ctx: InteractionContext, event: InteractionEvent) => void | Promise<void>;\n /**\n * Raw gateway event pass-through for any event not handled by SDK internals.\n *\n * Covers all platform events (guild/group/friend lifecycle, reactions, etc.)\n * without SDK needing explicit support. New platform events are immediately\n * available here with zero SDK changes.\n *\n * @example\n * ```ts\n * bot.on('rawEvent', (ctx) => {\n * if (ctx.eventType === 'FRIEND_ADD') {\n * const e = ctx.data as { openid: string; timestamp: string };\n * console.log('New friend:', e.openid);\n * }\n * });\n * ```\n */\n rawEvent: (ctx: RawEventContext) => void | Promise<void>;\n};\n\n/**\n * Augmented inbound message with a ready-to-use `replyTarget`.\n */\nexport interface QQBotInboundMessage extends InboundMessage {\n /** The peer this message originated from — pass to `bot.sendText`/`bot.sendFile`. */\n replyTarget: ReplyTarget;\n}\n\n// ============ QQBot class ============\n\nconst noopLogger: Logger = {\n info: () => {},\n error: () => {},\n warn: () => {},\n debug: () => {},\n};\n\n/**\n * High-level QQ Open Platform client.\n *\n * Owns an isolated stack of low-level primitives (token manager, HTTP\n * client, message API, media APIs, gateway connection) per bot instance,\n * so multiple bots can run concurrently without sharing global state.\n */\nexport class QQBot {\n // Public protocol primitives — exposed for advanced users.\n readonly tokenManager: TokenManager;\n readonly apiClient: ApiClient;\n readonly messageApi: MessageApi;\n readonly mediaApi: MediaApi;\n readonly chunkedMediaApi: ChunkedMediaApi;\n\n private readonly opts: QQBotOptions;\n private readonly logger: Logger;\n private readonly creds: Credentials;\n private readonly account: GatewayAccount;\n private readonly userAgent: string;\n private readonly uploadCache: UploadCache;\n private readonly middlewares: Middleware[] = [];\n private readonly handlers: { [K in keyof QQBotEventMap]: Set<QQBotEventMap[K]> } = {\n ready: new Set(),\n resumed: new Set(),\n error: new Set(),\n message: new Set(),\n interaction: new Set(),\n rawEvent: new Set(),\n };\n\n private gateway: GatewayConnection | null = null;\n private abortController: AbortController | null = null;\n private _apiGateway: ApiGateway | null = null;\n\n constructor(options: QQBotOptions) {\n if (!options.appId) {\n throw new Error(\"QQBot: appId is required\");\n }\n if (!options.appSecret) {\n throw new Error(\"QQBot: appSecret is required\");\n }\n this.opts = options;\n this.logger = options.logger ?? noopLogger;\n this.userAgent = options.userAgent ?? `qqbot-nodejs/0.1.0 (Node/${process.versions.node})`;\n this.creds = {\n appId: options.appId,\n clientSecret: options.appSecret,\n };\n this.account = {\n accountId: options.accountId ?? options.appId,\n appId: options.appId,\n clientSecret: options.appSecret,\n markdownSupport: options.markdownSupport === true,\n };\n\n this.uploadCache = options.uploadCache ?? new UploadCache({ logger: this.logger });\n\n this.apiClient = new ApiClient({\n logger: this.logger,\n userAgent: this.userAgent,\n baseUrl: options.baseUrl,\n });\n this.tokenManager = new TokenManager({\n logger: this.logger,\n userAgent: this.userAgent,\n baseUrl: options.tokenBaseUrl,\n });\n this.messageApi = new MessageApi(this.apiClient, this.tokenManager, {\n markdownSupport: options.markdownSupport === true,\n logger: this.logger,\n });\n\n const cacheAdapter = {\n computeHash: (data: string) => this.uploadCache.computeHash(data),\n get: (hash: string, scope: string, targetId: string, fileType: number) =>\n this.uploadCache.get(hash, scope as ChatScope, targetId, fileType),\n set: (\n hash: string,\n scope: string,\n targetId: string,\n fileType: number,\n fileInfo: string,\n fileUuid: string,\n ttl: number,\n ) =>\n this.uploadCache.set(\n hash,\n scope as ChatScope,\n targetId,\n fileType,\n fileInfo,\n fileUuid,\n ttl,\n ),\n };\n\n this.mediaApi = new MediaApi(this.apiClient, this.tokenManager, {\n logger: this.logger,\n uploadCache: cacheAdapter,\n sanitizeFileName,\n });\n this.chunkedMediaApi = new ChunkedMediaApi(this.apiClient, this.tokenManager, {\n logger: this.logger,\n uploadCache: cacheAdapter,\n sanitizeFileName,\n });\n }\n\n // ============ Public getters ============\n\n /** The QQ Open Platform AppID this bot is bound to. */\n get appId(): string {\n return this.creds.appId;\n }\n\n /** The stable account id (defaults to appId). */\n get accountId(): string {\n return this.account.accountId;\n }\n\n // ============ Event listeners ============\n\n on<K extends keyof QQBotEventMap>(event: K, handler: QQBotEventMap[K]): this {\n this.handlers[event].add(handler);\n return this;\n }\n\n off<K extends keyof QQBotEventMap>(event: K, handler: QQBotEventMap[K]): this {\n this.handlers[event].delete(handler);\n return this;\n }\n\n // ============ Middleware ============\n\n /**\n * Register an inbound middleware. Middlewares run in registration order\n * before the `message` event listeners; calling `ctx.stop()` (or simply\n * not calling `next()`) short-circuits the chain — including the final\n * `message` listener.\n *\n * @example\n * ```ts\n * import { accessPolicy, mentionGate } from \"@tencent-connect/qqbot-nodejs\";\n * bot.use(accessPolicy({ group: { mode: \"allowlist\", allow: [...] } }));\n * bot.use(mentionGate());\n * bot.on(\"message\", async (ctx, msg) => { ... });\n * ```\n */\n use(...middleware: Middleware[]): this {\n for (const mw of middleware) {\n if (typeof mw !== \"function\") {\n throw new Error(\"QQBot.use: middleware must be a function\");\n }\n this.middlewares.push(mw);\n }\n return this;\n }\n\n /** Read the registered middleware chain (for diagnostics). */\n getMiddlewares(): readonly Middleware[] {\n return this.middlewares;\n }\n\n private async emit<K extends keyof QQBotEventMap>(\n event: K,\n ...args: Parameters<QQBotEventMap[K]>\n ): Promise<void> {\n for (const handler of this.handlers[event]) {\n try {\n await Promise.resolve((handler as (...a: unknown[]) => unknown)(...args));\n } catch (err) {\n this.logger.error?.(\n `[qqbot] handler for \"${String(event)}\" threw: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n // ============ Lifecycle ============\n\n /**\n * Start receiving events from QQ Open Platform.\n *\n * - **WebSocket mode** (default): connects to the WS gateway with heartbeat/RESUME.\n * - **Webhook mode**: starts an HTTP server to receive POST callbacks.\n *\n * Resolves when {@link stop} or the abort signal terminates the connection.\n */\n async start(externalSignal?: AbortSignal): Promise<void> {\n if (this.gateway) {\n throw new Error(\"QQBot: already started\");\n }\n this.abortController = new AbortController();\n if (externalSignal) {\n if (externalSignal.aborted) {\n this.abortController.abort();\n } else {\n externalSignal.addEventListener(\"abort\", () => this.abortController?.abort(), {\n once: true,\n });\n }\n }\n\n const transportMode = this.opts.transport ?? \"websocket\";\n\n if (transportMode === \"webhook\") {\n await this.startWebhook();\n } else if (transportMode === \"websocket\") {\n await this.startWebSocket();\n } else {\n // Custom EventTransport instance\n const custom = transportMode as EventTransport;\n await custom.start();\n }\n\n // Cleanup\n this.tokenManager.stopBackgroundRefresh(this.creds.appId);\n this.gateway = null;\n this.abortController = null;\n }\n\n /** Stop the transport and background refreshers. */\n stop(): void {\n this.abortController?.abort();\n this.tokenManager.stopBackgroundRefresh(this.creds.appId);\n this.gateway = null;\n this.abortController = null;\n }\n\n // ============ Token initialization ============\n\n /**\n * Initialize token based on the configured prefetch strategy.\n *\n * - `\"sync\"` (default): awaits the first token fetch, providing fail-fast\n * semantics so credential errors surface at startup.\n * - `\"async\"`: fires the token fetch in the background and starts the\n * background refresher immediately — trades fail-fast for faster startup.\n */\n private async initToken(): Promise<void> {\n const mode = this.opts.tokenPrefetch ?? \"sync\";\n\n if (mode === \"sync\") {\n await this.tokenManager.getAccessToken(this.creds.appId, this.creds.clientSecret);\n } else {\n // Fire-and-forget: fetch token in background, log errors but don't block startup.\n this.tokenManager.getAccessToken(this.creds.appId, this.creds.clientSecret).catch((err) => {\n this.logger.error?.(`[qqbot] async token prefetch failed: ${err}`);\n void this.emit(\"error\", err instanceof Error ? err : new Error(String(err)));\n });\n }\n\n this.tokenManager.startBackgroundRefresh(this.creds.appId, this.creds.clientSecret);\n }\n\n // ============ Transport: WebSocket ============\n\n private async startWebSocket(): Promise<void> {\n await this.initToken();\n\n this.gateway = new GatewayConnection({\n account: this.account,\n abortSignal: this.abortController!.signal,\n log: this.logger,\n userAgent: this.userAgent,\n intents: this.opts.intents,\n session: this.opts.sessionPersistence,\n getAccessToken: () =>\n this.tokenManager.getAccessToken(this.creds.appId, this.creds.clientSecret),\n clearTokenCache: () => this.tokenManager.clearCache(this.creds.appId),\n getGatewayUrl: () => this.messageApi.getGatewayUrl(this.creds),\n onReady: (data) => {\n this.logger.info?.(`[qqbot] gateway READY`);\n void this.emit(\"ready\", data);\n },\n onResumed: (data) => {\n this.logger.info?.(`[qqbot] gateway RESUMED`);\n void this.emit(\"resumed\", data);\n },\n onError: (err) => {\n void this.emit(\"error\", err);\n },\n onMessage: (raw) => this.handleInboundMessage(raw),\n onInteraction: (event) => {\n const ctx: InteractionContext = { bot: this, event, state: {}, receivedAt: Date.now() };\n void this.emit(\"interaction\", ctx, event);\n },\n onRawEvent: (type, data) => {\n const ctx: RawEventContext = { bot: this, eventType: type, data, state: {}, receivedAt: Date.now() };\n void this.emit(\"rawEvent\", ctx);\n },\n });\n\n await this.gateway.start();\n }\n\n // ============ Transport: Webhook ============\n\n private async startWebhook(): Promise<void> {\n await this.initToken();\n\n const webhook = new WebhookTransport(\n {\n appId: this.creds.appId,\n appSecret: this.creds.clientSecret,\n port: this.opts.webhook?.port,\n path: this.opts.webhook?.path,\n server: this.opts.webhook?.server,\n accountId: this.account.accountId,\n log: this.logger,\n abortSignal: this.abortController!.signal,\n },\n {\n onReady: (data) => {\n this.logger.info?.(`[qqbot] webhook READY`);\n void this.emit(\"ready\", data);\n },\n onResumed: (data) => {\n void this.emit(\"resumed\", data);\n },\n onError: (err) => {\n void this.emit(\"error\", err);\n },\n onMessage: (raw) => this.handleInboundMessage(raw),\n onInteraction: (event) => {\n const ctx: InteractionContext = { bot: this, event, state: {}, receivedAt: Date.now() };\n void this.emit(\"interaction\", ctx, event);\n },\n },\n );\n\n await webhook.start();\n }\n\n // ============ Shared inbound message handler ============\n\n private async handleInboundMessage(raw: InboundMessage): Promise<void> {\n const replyTarget = this.deriveReplyTarget(raw);\n if (!replyTarget) {\n this.logger.debug?.(`[qqbot] inbound message has no reply target — skipping`);\n return;\n }\n const augmented: QQBotInboundMessage = { ...raw, replyTarget };\n\n const ctx = createMiddlewareContext({\n bot: this,\n message: augmented,\n log: this.logger,\n });\n\n // Koa-style: compose middleware chain with emit as the final downstream.\n const downstream: Middleware = async () => {\n await this.emit(\"message\", ctx, ctx.message);\n };\n const chain: Middleware[] = [...this.middlewares, downstream];\n\n try {\n await runMiddlewareChain(chain, ctx);\n } catch (err) {\n this.logger.error?.(\n `[qqbot] middleware chain threw: ${err instanceof Error ? err.message : String(err)}`,\n );\n void this.emit(\"error\", err instanceof Error ? err : new Error(String(err)));\n }\n }\n\n // ============ Message sending ============\n\n /**\n * Universal message send — supports all QQ Open Platform message types.\n *\n * This is the most flexible sending method. It accepts the full parameter\n * set of POST `/v2/users/{openid}/messages` or `/v2/groups/{group_openid}/messages`.\n * Use it when the convenience helpers (sendText, sendMarkdown, etc.) don't\n * cover your use case.\n *\n * `msg_type` is auto-detected if not specified:\n * - markdown field present → 2 (Markdown)\n * - ark field present → 3 (Ark)\n * - embed field present → 4 (Embed)\n * - media field present → 7 (Rich media)\n * - otherwise → 0 (Text)\n *\n * @example Send a keyboard message\n * ```ts\n * await bot.send({\n * target: msg.replyTarget,\n * msgType: MsgType.MARKDOWN,\n * markdown: { content: '# Hello' },\n * keyboard: { content: { rows: [...] } },\n * });\n * ```\n *\n * @example Send a proactive message (no msgId)\n * ```ts\n * await bot.send({\n * target: { scope: 'c2c', targetId: openid },\n * content: 'Hello from bot!',\n * });\n * ```\n */\n async send(opts: SendMessageOptions): Promise<MessageResponse> {\n const body: Record<string, unknown> = {};\n if (opts.target.msgId) body.msg_id = opts.target.msgId;\n if (opts.msgType !== undefined) body.msg_type = opts.msgType;\n if (opts.content !== undefined) body.content = opts.content;\n if (opts.markdown) body.markdown = opts.markdown;\n if (opts.ark) body.ark = opts.ark;\n if (opts.embed) body.embed = opts.embed;\n if (opts.media) body.media = opts.media;\n if (opts.keyboard) body.keyboard = opts.keyboard;\n if (opts.messageReference) body.message_reference = opts.messageReference;\n if (opts.extra) Object.assign(body, opts.extra);\n return this.messageApi.sendRaw(opts.target.scope, opts.target.targetId, this.creds, body);\n }\n\n /**\n * Send a text message to a C2C user or group (smart mode).\n *\n * **Difference from `send()`**:\n * - `sendText` auto-selects msg_type based on `markdownSupport` config\n * (markdown bots automatically send as msg_type=2).\n * - `send()` is explicit mode — you control msg_type directly.\n *\n * When `target.msgId` is present the message is treated as a reply\n * (tied to the inbound message lifecycle); otherwise it is treated as\n * a proactive push.\n */\n async sendText(target: ReplyTarget, content: string): Promise<MessageResponse> {\n if (target.msgId) {\n return this.messageApi.sendMessage(target.scope, target.targetId, content, this.creds, {\n msgId: target.msgId,\n });\n }\n return this.messageApi.sendProactiveMessage(\n target.scope,\n target.targetId,\n content,\n this.creds,\n );\n }\n\n /** Send a text message with an inline keyboard. */\n async sendTextWithKeyboard(\n target: ReplyTarget,\n content: string,\n inlineKeyboard: InlineKeyboard,\n ): Promise<MessageResponse> {\n return this.messageApi.sendMessage(target.scope, target.targetId, content, this.creds, {\n msgId: target.msgId,\n inlineKeyboard,\n });\n }\n\n /**\n * Send a Markdown message (msg_type=2).\n *\n * @example\n * ```ts\n * await bot.sendMarkdown(msg.replyTarget, '# Hello **world**');\n * await bot.sendMarkdown(msg.replyTarget, '# Click below', {\n * keyboard: { content: { rows: [...] } },\n * });\n * ```\n */\n async sendMarkdown(\n target: ReplyTarget,\n content: string,\n opts?: { keyboard?: InlineKeyboard },\n ): Promise<MessageResponse> {\n return this.send({\n target,\n msgType: MsgType.MARKDOWN,\n markdown: { content },\n keyboard: opts?.keyboard,\n });\n }\n\n /**\n * Recall (delete) a previously sent message.\n *\n * @example\n * ```ts\n * const sent = await bot.sendText(target, 'oops');\n * await bot.recallMessage(target, sent.id);\n * ```\n */\n async recallMessage(target: ReplyTarget, messageId: string): Promise<void> {\n return this.messageApi.recallMessage(target.scope, target.targetId, messageId, this.creds);\n }\n\n /**\n * Send a wakeup/recall message (C2C only, 30-day window).\n *\n * After a user initiates a conversation, the bot can send periodic\n * recall messages within 30 days using `is_wakeup: true`.\n * Platform enforces frequency limits.\n *\n * @example\n * ```ts\n * await bot.sendWakeup({ scope: 'c2c', targetId: openid }, '你有新消息!');\n * ```\n */\n async sendWakeup(target: ReplyTarget, content: string): Promise<MessageResponse> {\n if (target.scope !== \"c2c\") {\n throw new Error(\"sendWakeup is only supported for C2C targets\");\n }\n return this.send({ target, content, extra: { is_wakeup: true } });\n }\n\n /**\n * Send a message to a guild text channel.\n *\n * @example\n * ```ts\n * await bot.sendChannelMessage(channelId, '频道消息', { msgId });\n * ```\n */\n async sendChannelMessage(\n channelId: string,\n content: string,\n opts?: { msgId?: string; keyboard?: InlineKeyboard; messageReference?: string },\n ): Promise<MessageResponse> {\n const body: Record<string, unknown> = { content };\n if (opts?.msgId) body.msg_id = opts.msgId;\n if (opts?.keyboard) body.keyboard = opts.keyboard;\n if (opts?.messageReference) body.message_reference = { message_id: opts.messageReference };\n return this.messageApi.sendChannelMessageRaw(channelId, this.creds, body);\n }\n\n /**\n * Send a direct message (DM) in a guild.\n *\n * @example\n * ```ts\n * await bot.sendDmMessage(guildId, '私信内容', { msgId });\n * ```\n */\n async sendDmMessage(\n guildId: string,\n content: string,\n opts?: { msgId?: string },\n ): Promise<MessageResponse> {\n const body: Record<string, unknown> = { content };\n if (opts?.msgId) body.msg_id = opts.msgId;\n return this.messageApi.sendDmMessageRaw(guildId, this.creds, body);\n }\n\n /** Send a typing indicator (C2C only). */\n async sendTyping(target: ReplyTarget, durationSec = 30): Promise<{ refIdx?: string }> {\n if (target.scope !== \"c2c\") {\n throw new Error(\"sendTyping is only supported for C2C targets\");\n }\n return this.messageApi.sendInputNotify({\n openid: target.targetId,\n creds: this.creds,\n msgId: target.msgId,\n inputSecond: durationSec,\n });\n }\n\n /** Acknowledge an INTERACTION_CREATE event. */\n async acknowledgeInteraction(\n interactionId: string,\n code = 0,\n data?: Record<string, unknown>,\n ): Promise<void> {\n return this.messageApi.acknowledgeInteraction(interactionId, this.creds, code, data);\n }\n\n // ============ API Gateway ============\n\n /**\n * Open Platform API Gateway — call any QQ Open Platform REST API with\n * automatic token injection and refresh.\n *\n * This is the \"escape hatch\" for any API not wrapped by a dedicated method.\n * All requests are authenticated, rate-limit aware, and return structured errors.\n *\n * @example List guilds\n * ```ts\n * const guilds = await bot.api.get('/users/@me/guilds');\n * ```\n *\n * @example Create an announcement\n * ```ts\n * await bot.api.post(`/guilds/${guildId}/announces`, {\n * message_id: msgId, channel_id: channelId,\n * });\n * ```\n *\n * @example Get a raw access token\n * ```ts\n * const token = await bot.api.getToken();\n * ```\n */\n get api(): ApiGateway {\n if (this._apiGateway) return this._apiGateway;\n this._apiGateway = {\n get: <T = unknown>(path: string, query?: Record<string, string | number | boolean>) =>\n this.apiRequest<T>(\"GET\", path, undefined, query),\n post: <T = unknown>(path: string, body?: unknown) =>\n this.apiRequest<T>(\"POST\", path, body),\n put: <T = unknown>(path: string, body?: unknown) =>\n this.apiRequest<T>(\"PUT\", path, body),\n patch: <T = unknown>(path: string, body?: unknown) =>\n this.apiRequest<T>(\"PATCH\", path, body),\n delete: <T = unknown>(path: string) =>\n this.apiRequest<T>(\"DELETE\", path),\n getToken: () =>\n this.tokenManager.getAccessToken(this.creds.appId, this.creds.clientSecret),\n };\n return this._apiGateway;\n }\n\n private async apiRequest<T>(\n method: string,\n path: string,\n body?: unknown,\n query?: Record<string, string | number | boolean>,\n ): Promise<T> {\n const token = await this.tokenManager.getAccessToken(\n this.creds.appId,\n this.creds.clientSecret,\n );\n let fullPath = path;\n if (query && Object.keys(query).length > 0) {\n const params = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined && v !== null) params.set(k, String(v));\n }\n fullPath = `${path}?${params.toString()}`;\n }\n return this.apiClient.request<T>(token, method, fullPath, body ?? undefined);\n }\n\n // ============ Streaming ============\n\n /**\n * Open a C2C stream session for incremental output.\n *\n * @returns A {@link StreamSession} — call `update(fullText)` repeatedly,\n * then `complete()` when finished.\n */\n openStream(opts: {\n target: ReplyTarget;\n eventId?: string;\n throttleMs?: number;\n }): StreamSession {\n if (opts.target.scope !== \"c2c\") {\n throw new Error(\"Streaming is only supported for C2C targets\");\n }\n if (!opts.target.msgId) {\n throw new Error(\"Streaming requires target.msgId from the inbound message\");\n }\n const sessionOptions: StreamSessionOptions = {\n openid: opts.target.targetId,\n msgId: opts.target.msgId,\n creds: this.creds,\n eventId: opts.eventId,\n throttleMs: opts.throttleMs,\n logger: this.logger,\n };\n return new StreamSession(this.messageApi, sessionOptions);\n }\n\n // ============ Media ============\n\n /**\n * Upload media (image / voice / video / file) to a target. Automatically\n * dispatches to the chunked-upload path when the source exceeds\n * {@link LARGE_FILE_THRESHOLD} bytes.\n */\n async uploadMedia(opts: {\n target: ReplyTarget;\n fileType: MediaFileType;\n /** Mutually exclusive: pass exactly one source. */\n url?: string;\n fileData?: string;\n buffer?: Buffer;\n localPath?: string;\n fileName?: string;\n /** Whether to let the server send the message immediately. */\n srvSendMsg?: boolean;\n onProgress?: (uploaded: number, total: number) => void;\n }): Promise<UploadMediaResponse> {\n const sources = [opts.url, opts.fileData, opts.buffer, opts.localPath].filter(\n (v) => v !== undefined,\n );\n if (sources.length === 0) {\n throw new Error(\"uploadMedia: one of url/fileData/buffer/localPath is required\");\n }\n if (sources.length > 1) {\n throw new Error(\"uploadMedia: provide exactly one source\");\n }\n\n const size = await this.computeSourceSize(opts);\n const useChunked = size !== null && size >= LARGE_FILE_THRESHOLD;\n\n // Auto-fill fileName from localPath or URL when not explicitly provided\n const fileName = opts.fileName\n ?? (opts.localPath ? path.basename(opts.localPath) : undefined)\n ?? (opts.url ? decodeURIComponent(path.basename(new URL(opts.url).pathname)) || undefined : undefined);\n\n if (useChunked && (opts.localPath || opts.buffer)) {\n const source = opts.localPath\n ? { kind: \"localPath\" as const, path: opts.localPath, size: size! }\n : {\n kind: \"buffer\" as const,\n buffer: opts.buffer!,\n fileName,\n };\n return this.chunkedMediaApi.uploadChunked({\n scope: opts.target.scope,\n targetId: opts.target.targetId,\n fileType: opts.fileType,\n source,\n creds: this.creds,\n fileName,\n onProgress: opts.onProgress\n ? (p) => opts.onProgress!(p.uploadedBytes, p.totalBytes)\n : undefined,\n });\n }\n\n return this.mediaApi.uploadMedia(\n opts.target.scope,\n opts.target.targetId,\n opts.fileType,\n this.creds,\n {\n url: opts.url,\n fileData: opts.fileData,\n buffer: opts.buffer,\n localPath: opts.localPath,\n srvSendMsg: opts.srvSendMsg,\n fileName,\n },\n );\n }\n\n /**\n * Upload + send a media message to a C2C user or group.\n */\n async sendMedia(opts: {\n target: ReplyTarget;\n fileType: MediaFileType;\n url?: string;\n fileData?: string;\n buffer?: Buffer;\n localPath?: string;\n fileName?: string;\n /** Optional caption (sent as the `content` field). */\n content?: string;\n onProgress?: (uploaded: number, total: number) => void;\n }): Promise<{ upload: UploadMediaResponse; message: MessageResponse | undefined }> {\n const upload = await this.uploadMedia({ ...opts, srvSendMsg: false });\n const message = await this.mediaApi.sendMediaMessage(\n opts.target.scope,\n opts.target.targetId,\n upload.file_info,\n this.creds,\n {\n msgId: opts.target.msgId,\n content: opts.content,\n },\n );\n return { upload, message };\n }\n\n /** Convenience: upload + send an image. */\n async sendImage(\n target: ReplyTarget,\n source: { url?: string; buffer?: Buffer; localPath?: string },\n opts?: { content?: string; onProgress?: (uploaded: number, total: number) => void },\n ): Promise<{ upload: UploadMediaResponse; message: MessageResponse | undefined }> {\n return this.sendMedia({\n target,\n fileType: MediaFileType.IMAGE,\n ...source,\n content: opts?.content,\n onProgress: opts?.onProgress,\n });\n }\n\n /** Convenience: upload + send a video. */\n async sendVideo(\n target: ReplyTarget,\n source: { url?: string; buffer?: Buffer; localPath?: string },\n opts?: { content?: string; onProgress?: (uploaded: number, total: number) => void },\n ): Promise<{ upload: UploadMediaResponse; message: MessageResponse | undefined }> {\n return this.sendMedia({\n target,\n fileType: MediaFileType.VIDEO,\n ...source,\n content: opts?.content,\n onProgress: opts?.onProgress,\n });\n }\n\n /** Convenience: upload + send a voice message. */\n async sendVoice(\n target: ReplyTarget,\n source: { url?: string; buffer?: Buffer; localPath?: string },\n opts?: { onProgress?: (uploaded: number, total: number) => void },\n ): Promise<{ upload: UploadMediaResponse; message: MessageResponse | undefined }> {\n return this.sendMedia({\n target,\n fileType: MediaFileType.VOICE,\n ...source,\n onProgress: opts?.onProgress,\n });\n }\n\n /** Convenience: upload + send a generic file (for users with file-message permission). */\n async sendFile(\n target: ReplyTarget,\n source: { url?: string; buffer?: Buffer; localPath?: string },\n opts?: {\n fileName?: string;\n content?: string;\n onProgress?: (uploaded: number, total: number) => void;\n },\n ): Promise<{ upload: UploadMediaResponse; message: MessageResponse | undefined }> {\n return this.sendMedia({\n target,\n fileType: MediaFileType.FILE,\n ...source,\n fileName: opts?.fileName,\n content: opts?.content,\n onProgress: opts?.onProgress,\n });\n }\n\n // ============ Internal ============\n\n private deriveReplyTarget(raw: InboundMessage): ReplyTarget | null {\n if (raw.kind === \"c2c\") {\n return { scope: \"c2c\", targetId: raw.senderId, msgId: raw.messageId };\n }\n if (raw.kind === \"group\" && raw.groupOpenid) {\n return { scope: \"group\", targetId: raw.groupOpenid, msgId: raw.messageId };\n }\n // Guild / DM 暂时没有 channel-level 的 c2c/group 等价。SDK 选择忽略\n // 这两类消息(业务侧仍然可以通过 raw 字段自己处理)。\n return null;\n }\n\n private async computeSourceSize(opts: {\n url?: string;\n fileData?: string;\n buffer?: Buffer;\n localPath?: string;\n }): Promise<number | null> {\n if (opts.buffer) {\n return opts.buffer.length;\n }\n if (opts.localPath) {\n try {\n const stat = await fs.promises.stat(opts.localPath);\n return stat.size;\n } catch {\n return null;\n }\n }\n if (opts.fileData) {\n // base64 length → byte length estimate.\n return Math.floor((opts.fileData.length * 3) / 4);\n }\n // url upload — server-side size, can't know up-front.\n return null;\n }\n}\n","/**\n * Middleware chain — pluggable inbound message processing pipeline.\n *\n * Inspired by Koa / Express middleware: each middleware receives a context\n * object and a `next()` function, and may run arbitrary logic before / after\n * downstream middleware. Calling `ctx.stop()` (or simply not calling `next()`)\n * short-circuits the chain.\n *\n * The SDK ships several **built-in middleware** in `./middleware/*` that cover\n * common needs (access policy, mention gating, command dispatch). Users can\n * compose them, replace them, or add their own.\n *\n * @example\n * ```ts\n * import { QQBot, accessPolicy, mentionGate } from \"@tencent-connect/qqbot-nodejs\";\n *\n * const bot = new QQBot({ appId, appSecret });\n *\n * bot.use(accessPolicy({ allowFrom: [\"*\"] }));\n * bot.use(mentionGate({ requireMentionInGroup: true }));\n * bot.use(async (ctx, next) => {\n * // Custom logic before\n * await next();\n * // Custom logic after\n * });\n *\n * bot.on(\"message\", async (msg) => {\n * await bot.sendText(msg.replyTarget, `Echo: ${msg.content}`);\n * });\n * ```\n */\n\nimport type { QQBot, QQBotInboundMessage, ReplyTarget } from \"../QQBot.js\";\nimport type { Logger } from \"../protocol/types.js\";\n\n// ============ Types ============\n\n/**\n * Free-form per-request state slot, augmentable via TS module augmentation.\n * Built-in middleware fills well-known keys (see `middleware/index.ts`).\n */\nexport interface MiddlewareState {\n [key: string]: unknown;\n\n /**\n * Dynamic policy injection — set by an upstream \"policy injector\" middleware.\n *\n * Built-in middlewares read well-known keys from here as a fallback:\n *\n * **Group policy** (`state.policy.group`):\n * - `requireMention` — `mentionGate` fallback\n * - `ignoreOtherMentions` — `mentionGate` fallback\n * - `historyLimit` — `historyBuffer` fallback\n *\n * **Session scope** (`state.policy.scope`): `\"c2c\" | \"group\" | \"dm\" | \"channel\"`\n *\n * **Extensibility** — users can add any keys for custom middlewares:\n * ```\n * ctx.state.policy.customToolPolicy = \"full\";\n * ```\n */\n policy?: {\n group?: {\n requireMention?: boolean;\n ignoreOtherMentions?: boolean;\n historyLimit?: number;\n [key: string]: unknown;\n };\n scope?: \"c2c\" | \"group\" | \"dm\" | \"channel\";\n [key: string]: unknown;\n };\n}\n\n/**\n * Context passed to each middleware.\n *\n * Mutating `ctx.message` or `ctx.replyTarget` propagates to downstream\n * middleware and to the final `bot.on(\"message\")` listener.\n */\nexport interface MiddlewareContext {\n /** The QQBot instance — middlewares can call `bot.sendText` etc. */\n readonly bot: QQBot;\n /** The inbound message being processed. Mutable — middlewares can rewrite. */\n message: QQBotInboundMessage;\n /** The derived reply target. Same object reference as `message.replyTarget`. */\n readonly replyTarget: ReplyTarget;\n /**\n * Free-form per-request state slot. Use this to share data between\n * middlewares (e.g. a parsed command struct, an authentication result).\n *\n * Built-in middleware writes well-known keys here:\n * - `command`: parsed slash-command (set by `slashCommand` middleware)\n * - `mention`: mention-gate decision (set by `mentionGate` middleware)\n * - `history`: buffered history entries (set by `historyBuffer` middleware)\n *\n * Users may also add arbitrary keys.\n */\n state: MiddlewareState;\n /** Logger. */\n readonly log: Logger;\n /**\n * Stop the chain immediately. Equivalent to not calling `next()` and\n * preventing any downstream middleware (or the `message` event listener)\n * from running.\n */\n stop(reason?: string): void;\n /** Whether `stop()` has been called (or some upstream short-circuited). */\n readonly stopped: boolean;\n /** Reason passed to `stop()`, if any. */\n readonly stopReason: string | undefined;\n /**\n * AbortSignal for this request. Aborted when `ctx.abort()` is called.\n *\n * Use this to cancel in-flight async work (e.g. streaming, LLM calls)\n * when a middleware (like `concurrencyGuard`) decides to interrupt\n * the current processing.\n *\n * @example\n * ```ts\n * bot.on(\"message\", async (ctx, msg) => {\n * for await (const chunk of llmStream) {\n * if (ctx.signal.aborted) break;\n * await stream.update(chunk);\n * }\n * });\n * ```\n */\n readonly signal: AbortSignal;\n /**\n * Abort the current request processing. Triggers `signal` and calls\n * `stop()`. Use from middleware to interrupt an in-flight handler.\n */\n abort(reason?: string): void;\n /** Whether `abort()` has been called. */\n readonly aborted: boolean;\n /**\n * Timestamp (`Date.now()`) when the SDK created this context.\n * Use to measure end-to-end latency from event arrival to handler completion.\n */\n readonly receivedAt: number;\n}\n\n/** Middleware function — `next` advances the chain. */\nexport type Middleware = (\n ctx: MiddlewareContext,\n next: () => Promise<void>,\n) => void | Promise<void>;\n\n// ============ Policy Resolution Helper ============\n\n/**\n * Resolve an option with the standard 3-tier priority chain:\n *\n * 1. `explicit` — the value passed directly to the middleware Options\n * 2. `ctx.state.policy.<path>` — injected by an upstream policy injector\n * 3. `defaultValue` — hardcoded fallback\n *\n * `path` uses dot notation to navigate nested policy keys, e.g.\n * `\"group.requireMention\"`, `\"group.historyLimit\"`, `\"myFeature.threshold\"`.\n *\n * @example\n * ```ts\n * const limit = resolvePolicy(ctx, \"group.historyLimit\", options.limit, 10);\n * const reqMention = resolvePolicy(ctx, \"group.requireMention\", options.requireMentionInGroup, true);\n * ```\n */\nexport function resolvePolicy<T>(\n ctx: MiddlewareContext,\n path: string,\n explicit: T | undefined,\n defaultValue: T,\n): T {\n if (explicit !== undefined && explicit !== null) {\n return explicit;\n }\n const keys = path.split(\".\");\n let value: unknown = ctx.state.policy;\n for (const key of keys) {\n if (value === null || value === undefined) break;\n value = (value as Record<string, unknown>)[key];\n }\n if (value !== undefined && value !== null) {\n return value as T;\n }\n return defaultValue;\n}\n\n// ============ Implementation ============\n\n/**\n * Run a middleware chain.\n *\n * Returns `true` if the chain ran to completion (no `stop()`); `false` if\n * any middleware short-circuited.\n *\n * Errors thrown by middleware propagate up — the caller decides whether to\n * surface them via `bot.emit(\"error\")` or swallow them.\n */\nexport async function runMiddlewareChain(\n middlewares: Middleware[],\n ctx: MiddlewareContext,\n): Promise<boolean> {\n let index = -1;\n\n const dispatch = async (i: number): Promise<void> => {\n if (i <= index) {\n throw new Error(\"next() called multiple times\");\n }\n index = i;\n if (ctx.stopped) {\n return;\n }\n if (i >= middlewares.length) {\n return;\n }\n const fn = middlewares[i];\n if (!fn) {\n return;\n }\n await fn(ctx, () => dispatch(i + 1));\n };\n\n await dispatch(0);\n return !ctx.stopped;\n}\n\n/**\n * Build a fresh middleware context for one inbound message.\n *\n * Internal helper — used by {@link QQBot} when dispatching inbound events.\n */\nexport function createMiddlewareContext(params: {\n bot: QQBot;\n message: QQBotInboundMessage;\n log: Logger;\n}): MiddlewareContext {\n const receivedAt = Date.now();\n let stopped = false;\n let stopReason: string | undefined;\n const ac = new AbortController();\n\n const ctx: MiddlewareContext = {\n bot: params.bot,\n message: params.message,\n replyTarget: params.message.replyTarget,\n state: {},\n log: params.log,\n stop(reason?: string) {\n stopped = true;\n stopReason = reason;\n },\n get stopped() {\n return stopped;\n },\n get stopReason() {\n return stopReason;\n },\n get signal() {\n return ac.signal;\n },\n abort(reason?: string) {\n ac.abort(reason);\n stopped = true;\n stopReason = reason ?? \"aborted\";\n },\n get aborted() {\n return ac.signal.aborted;\n },\n receivedAt,\n };\n\n return ctx;\n}\n","/**\n * Protocol-level public types for the QQ Open Platform.\n *\n * 这一层只关心与 QQ 官方 API 协议相关的数据结构(HTTP 请求/响应、\n * WebSocket Gateway 事件、消息体、媒体类型等),不包含任何上层框架\n * 业务概念(路由、ACP、access policy 等)。\n *\n * 上层应用通过 `QQBot` facade 与这些类型交互;高级用户也可以从\n * `@tencent-connect/qqbot-nodejs/protocol` 直接消费这些低层类型。\n */\n\n// ============ Structured API Error ============\n\n/**\n * QQ 开放平台 HTTP 调用失败时抛出的结构化错误。\n *\n * 携带 HTTP 状态码、API 路径以及业务错误码,供上层重试/降级判断使用。\n */\nexport class ApiError extends Error {\n override readonly name = \"ApiError\";\n\n constructor(\n message: string,\n /** HTTP status code returned by the QQ Open Platform. */\n public readonly httpStatus: number,\n /** API path that produced the error (e.g. `/v2/users/{id}/messages`). */\n public readonly path: string,\n /** Business error code from the response body (`code` or `err_code`). */\n public readonly bizCode?: number,\n /** Original error message from the response body. */\n public readonly bizMessage?: string,\n ) {\n super(message);\n }\n}\n\n// ============ Logger ============\n\n/**\n * Logger interface used across all protocol modules.\n *\n * `info` and `error` are required; `warn` and `debug` are optional.\n */\nexport interface Logger {\n info: (msg: string, meta?: Record<string, unknown>) => void;\n error: (msg: string, meta?: Record<string, unknown>) => void;\n warn?: (msg: string, meta?: Record<string, unknown>) => void;\n debug?: (msg: string, meta?: Record<string, unknown>) => void;\n}\n\n/** @deprecated Use {@link Logger}. */\nexport type EngineLogger = Logger;\n\n// ============ Chat Scope ============\n\n/** Chat scope used to unify C2C/Group path construction. */\nexport type ChatScope = \"c2c\" | \"group\";\n\n// ============ Credentials ============\n\n/** Credentials needed to authenticate API requests. */\nexport interface Credentials {\n appId: string;\n clientSecret: string;\n}\n\n// ============ Message Response ============\n\n/** Standard message send response from the QQ Open Platform. */\nexport interface MessageResponse {\n id: string;\n timestamp: number | string;\n /** Reference index for future quoting. */\n ext_info?: {\n ref_idx?: string;\n };\n}\n\n// ============ Media Types ============\n\n/** QQ Open Platform media file type codes. */\nexport enum MediaFileType {\n IMAGE = 1,\n VIDEO = 2,\n VOICE = 3,\n FILE = 4,\n}\n\n/** Media upload response from the QQ Open Platform. */\nexport interface UploadMediaResponse {\n file_uuid: string;\n file_info: string;\n ttl: number;\n id?: string;\n}\n\n/** Structured metadata recorded for outbound messages. */\nexport interface OutboundMeta {\n text?: string;\n mediaType?: \"image\" | \"voice\" | \"video\" | \"file\";\n mediaUrl?: string;\n mediaLocalPath?: string;\n ttsText?: string;\n}\n\n// ============ API Client Config ============\n\n/** Configuration for the core HTTP client. */\nexport interface ApiClientConfig {\n baseUrl?: string;\n defaultTimeoutMs?: number;\n fileUploadTimeoutMs?: number;\n logger?: Logger;\n userAgent?: string | (() => string);\n}\n\n// ============ Chunked Upload Types ============\n\nexport interface UploadPart {\n index: number;\n presigned_url: string;\n}\n\nexport interface UploadPrepareResponse {\n upload_id: string;\n block_size: number;\n parts: UploadPart[];\n concurrency?: number;\n retry_timeout?: number;\n}\n\nexport interface UploadPrepareHashes {\n md5: string;\n sha1: string;\n md5_10m: string;\n}\n\n// ============ Stream Message Types ============\n\nexport const StreamInputMode = {\n REPLACE: \"replace\",\n} as const;\nexport type StreamInputMode = (typeof StreamInputMode)[keyof typeof StreamInputMode];\n\nexport const StreamInputState = {\n GENERATING: 1,\n DONE: 10,\n} as const;\nexport type StreamInputState = (typeof StreamInputState)[keyof typeof StreamInputState];\n\nexport const StreamContentType = {\n MARKDOWN: \"markdown\",\n} as const;\nexport type StreamContentType = (typeof StreamContentType)[keyof typeof StreamContentType];\n\nexport interface StreamMessageRequest {\n input_mode: StreamInputMode;\n input_state: StreamInputState;\n content_type: StreamContentType;\n content_raw: string;\n event_id: string;\n msg_id: string;\n stream_msg_id?: string;\n msg_seq: number;\n index: number;\n}\n\n// ============ Inline Keyboard ============\n\nexport interface KeyboardButton {\n id: string;\n render_data: {\n label: string;\n visited_label: string;\n style: number;\n };\n action: {\n type: number;\n permission: { type: number };\n data: string;\n click_limit?: number;\n };\n group_id?: string;\n}\n\nexport interface InlineKeyboard {\n content: {\n rows: Array<{ buttons: KeyboardButton[] }>;\n };\n}\n\n// ============ Interaction Event ============\n\nexport interface InteractionEvent {\n id: string;\n type: number;\n scene?: string;\n chat_type?: number;\n timestamp?: string;\n guild_id?: string;\n channel_id?: string;\n user_openid?: string;\n group_openid?: string;\n group_member_openid?: string;\n version: number;\n data: {\n type: number;\n resolved: {\n button_data?: string;\n button_id?: string;\n user_id?: string;\n feature_id?: string;\n message_id?: string;\n };\n };\n}\n\n// ============ Gateway WebSocket Types ============\n\nexport interface WSPayload {\n op: number;\n d: unknown;\n s?: number;\n t?: string;\n}\n\nexport interface RawMessageAttachment {\n content_type: string;\n url: string;\n filename?: string;\n voice_wav_url?: string;\n asr_refer_text?: string;\n}\n\nexport interface RawMsgElement {\n msg_idx?: string;\n content?: string;\n attachments?: Array<\n RawMessageAttachment & {\n height?: number;\n width?: number;\n size?: number;\n }\n >;\n}\n\nexport interface C2CMessageEvent {\n id: string;\n content: string;\n timestamp: string;\n author: { user_openid: string };\n attachments?: RawMessageAttachment[];\n message_scene?: { ext?: string[] };\n message_type?: number;\n msg_elements?: RawMsgElement[];\n}\n\nexport interface GuildMessageEvent {\n id: string;\n content: string;\n timestamp: string;\n author: { id: string; username?: string };\n channel_id: string;\n guild_id: string;\n attachments?: RawMessageAttachment[];\n message_scene?: { ext?: string[] };\n}\n\nexport interface GroupMessageEvent {\n id: string;\n content: string;\n timestamp: string;\n author: {\n member_openid: string;\n username?: string;\n bot?: boolean;\n };\n group_openid: string;\n attachments?: RawMessageAttachment[];\n mentions?: Array<{\n scope?: \"all\" | \"single\";\n id?: string;\n user_openid?: string;\n member_openid?: string;\n nickname?: string;\n username?: string;\n bot?: boolean;\n is_you?: boolean;\n }>;\n message_scene?: { source?: string; ext?: string[] };\n message_type?: number;\n msg_elements?: RawMsgElement[];\n}\n\n// ============ Account ============\n\n/** Lightweight account record consumed by GatewayConnection. */\nexport interface GatewayAccount {\n /** Stable account identifier (used for logging / session persistence). */\n accountId: string;\n appId: string;\n clientSecret: string;\n /** Whether this bot has markdown permission. */\n markdownSupport?: boolean;\n}\n","/**\n * General formatting utilities. Pure helpers with zero external dependencies.\n */\n\n/** Format any error object into a readable string, traversing the `.cause` chain. */\nexport function formatErrorMessage(err: unknown): string {\n if (err instanceof Error) {\n let formatted = err.message || err.name || \"Error\";\n let cause: unknown = err.cause;\n const seen = new Set<unknown>([err]);\n while (cause && !seen.has(cause)) {\n seen.add(cause);\n if (cause instanceof Error) {\n if (cause.message) {\n formatted += ` | ${cause.message}`;\n }\n cause = cause.cause;\n } else if (typeof cause === \"string\") {\n formatted += ` | ${cause}`;\n break;\n } else {\n break;\n }\n }\n return formatted;\n }\n if (typeof err === \"string\") {\n return err;\n }\n if (\n err === null ||\n err === undefined ||\n typeof err === \"number\" ||\n typeof err === \"boolean\" ||\n typeof err === \"bigint\"\n ) {\n return String(err);\n }\n try {\n return JSON.stringify(err);\n } catch {\n return Object.prototype.toString.call(err);\n }\n}\n\n/** Format a millisecond duration into a human-readable string (e.g. \"5m 30s\"). */\nexport function formatDuration(durationMs: number): string {\n const seconds = Math.round(durationMs / 1000);\n if (seconds < 60) {\n return `${seconds}s`;\n }\n const minutes = Math.floor(seconds / 60);\n const remainSeconds = seconds % 60;\n return remainSeconds > 0 ? `${minutes}m ${remainSeconds}s` : `${minutes}m`;\n}\n\n/** Format a byte count into a human-readable string (e.g. \"1.23 MB\"). */\nexport function formatFileSize(bytes: number): string {\n if (bytes < 1024) {\n return `${bytes} B`;\n }\n if (bytes < 1024 * 1024) {\n return `${(bytes / 1024).toFixed(2)} KB`;\n }\n if (bytes < 1024 * 1024 * 1024) {\n return `${(bytes / 1024 / 1024).toFixed(2)} MB`;\n }\n return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`;\n}\n","/**\n * Core HTTP client for the QQ Open Platform REST API.\n *\n * - `ApiClient` is an instance — config (baseUrl, timeout, logger, UA)\n * is injected via the constructor, eliminating module-level globals.\n * - Throws structured `ApiError` with httpStatus, bizCode, and path fields.\n * - Detects HTML error pages from CDN/gateway and returns user-friendly messages.\n */\n\nimport { ApiError, type ApiClientConfig, type Logger } from \"../types.js\";\nimport { formatErrorMessage } from \"../utils/format.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.sgroup.qq.com\";\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst FILE_UPLOAD_TIMEOUT_MS = 120_000;\n\nexport interface RequestOptions {\n timeoutMs?: number;\n redactBodyKeys?: string[];\n uploadRequest?: boolean;\n}\n\n/**\n * Stateful HTTP client for the QQ Open Platform.\n */\nexport class ApiClient {\n private readonly baseUrl: string;\n private readonly defaultTimeoutMs: number;\n private readonly fileUploadTimeoutMs: number;\n private readonly logger?: Logger;\n private readonly resolveUserAgent: () => string;\n\n constructor(config: ApiClientConfig = {}) {\n this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n this.defaultTimeoutMs = config.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.fileUploadTimeoutMs = config.fileUploadTimeoutMs ?? FILE_UPLOAD_TIMEOUT_MS;\n this.logger = config.logger;\n const ua = config.userAgent ?? \"qqbot-nodejs/unknown\";\n this.resolveUserAgent = typeof ua === \"function\" ? ua : () => ua;\n }\n\n async request<T = unknown>(\n accessToken: string,\n method: string,\n path: string,\n body?: unknown,\n options?: RequestOptions,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n\n const headers: Record<string, string> = {\n Authorization: `QQBot ${accessToken}`,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": this.resolveUserAgent(),\n };\n\n const isFileUpload =\n options?.uploadRequest === true ||\n path.includes(\"/files\") ||\n path.includes(\"/upload_prepare\") ||\n path.includes(\"/upload_part_finish\");\n const timeout =\n options?.timeoutMs ?? (isFileUpload ? this.fileUploadTimeoutMs : this.defaultTimeoutMs);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n const fetchInit: RequestInit = { method, headers, signal: controller.signal };\n if (body) {\n fetchInit.body = JSON.stringify(body);\n }\n\n this.logger?.debug?.(`[qqbot:api] >>> ${method} ${url} (timeout: ${timeout}ms)`);\n if (body && this.logger?.debug) {\n const logBody = { ...(body as Record<string, unknown>) };\n for (const key of options?.redactBodyKeys ?? [\"file_data\"]) {\n if (typeof logBody[key] === \"string\") {\n logBody[key] = `<redacted ${logBody[key].length} chars>`;\n }\n }\n this.logger.debug(`[qqbot:api] >>> Body: ${JSON.stringify(logBody)}`);\n }\n\n let res: Response;\n try {\n res = await fetch(url, fetchInit);\n } catch (err) {\n clearTimeout(timeoutId);\n if (err instanceof Error && err.name === \"AbortError\") {\n this.logger?.error?.(`[qqbot:api] <<< Timeout after ${timeout}ms`);\n throw new ApiError(`Request timeout [${path}]: exceeded ${timeout}ms`, 0, path);\n }\n this.logger?.error?.(`[qqbot:api] <<< Network error: ${formatErrorMessage(err)}`);\n throw new ApiError(`Network error [${path}]: ${formatErrorMessage(err)}`, 0, path);\n } finally {\n clearTimeout(timeoutId);\n }\n\n const traceId = res.headers.get(\"x-tps-trace-id\") ?? \"\";\n this.logger?.info?.(\n `[qqbot:api] <<< Status: ${res.status} ${res.statusText}${traceId ? ` | TraceId: ${traceId}` : \"\"}`,\n );\n\n let rawBody: string;\n try {\n rawBody = await res.text();\n } catch (err) {\n throw new ApiError(\n `Failed to read response [${path}]: ${formatErrorMessage(err)}`,\n res.status,\n path,\n );\n }\n this.logger?.debug?.(`[qqbot:api] <<< Body: ${rawBody}`);\n\n const contentType = res.headers.get(\"content-type\") ?? \"\";\n const isHtmlResponse = contentType.includes(\"text/html\") || rawBody.trimStart().startsWith(\"<\");\n\n if (!res.ok) {\n if (isHtmlResponse) {\n const statusHint =\n res.status === 502 || res.status === 503 || res.status === 504\n ? \"调用发生异常,请稍候重试\"\n : res.status === 429\n ? \"请求过于频繁,已被限流\"\n : `开放平台返回 HTTP ${res.status}`;\n throw new ApiError(`${statusHint}(${path}),请稍后重试`, res.status, path);\n }\n\n try {\n const error = JSON.parse(rawBody) as {\n message?: string;\n code?: number;\n err_code?: number;\n };\n const bizCode = error.code ?? error.err_code;\n throw new ApiError(\n `API Error [${path}]: ${error.message ?? rawBody}`,\n res.status,\n path,\n bizCode,\n error.message,\n );\n } catch (parseErr) {\n if (parseErr instanceof ApiError) {\n throw parseErr;\n }\n throw new ApiError(\n `API Error [${path}] HTTP ${res.status}: ${rawBody.slice(0, 200)}`,\n res.status,\n path,\n );\n }\n }\n\n if (isHtmlResponse) {\n throw new ApiError(\n `QQ 服务端返回了非 JSON 响应(${path}),可能是临时故障,请稍后重试`,\n res.status,\n path,\n );\n }\n\n try {\n return JSON.parse(rawBody) as T;\n } catch {\n throw new ApiError(`开放平台响应格式异常(${path}),请稍后重试`, res.status, path);\n }\n }\n}\n","/**\n * Chunked media upload for the QQ Open Platform (large files > 5MB).\n *\n * Flow:\n * 1. `upload_prepare` — submit file metadata + (md5/sha1/md5_10m) hashes,\n * receive `{ upload_id, block_size, parts[], concurrency? }`.\n * 2. For every part:\n * a. PUT bytes to the pre-signed COS URL.\n * b. POST `upload_part_finish` with persistent retry.\n * 3. POST `complete_upload` — returns `{ file_uuid, file_info, ttl }`.\n */\n\nimport * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as https from \"node:https\";\nimport {\n ApiError,\n MediaFileType,\n type ChatScope,\n type Credentials,\n type Logger,\n type UploadMediaResponse,\n type UploadPart,\n type UploadPrepareHashes,\n type UploadPrepareResponse,\n} from \"../types.js\";\nimport { formatFileSize } from \"../utils/format.js\";\nimport type { ApiClient } from \"./api-client.js\";\nimport type { SanitizeFileNameFn, UploadCacheAdapter } from \"./media.js\";\nimport {\n buildPartFinishPersistentPolicy,\n COMPLETE_UPLOAD_RETRY_POLICY,\n PART_FINISH_RETRY_POLICY,\n UPLOAD_PREPARE_FALLBACK_CODE,\n withRetry,\n} from \"./retry.js\";\nimport { uploadCompletePath, uploadPartFinishPath, uploadPreparePath } from \"./routes.js\";\nimport type { TokenManager } from \"./token.js\";\n\n/**\n * Raised when `upload_prepare` returns {@link UPLOAD_PREPARE_FALLBACK_CODE}.\n */\nexport class UploadDailyLimitExceededError extends Error {\n override readonly name = \"UploadDailyLimitExceededError\";\n\n constructor(\n public readonly filePath: string,\n public readonly fileSize: number,\n originalMessage: string,\n ) {\n super(originalMessage);\n }\n}\n\nexport interface ChunkedUploadProgress {\n completedParts: number;\n totalParts: number;\n uploadedBytes: number;\n totalBytes: number;\n}\n\n/** Source of bytes for a chunked upload. */\nexport type ChunkedMediaSource =\n | { kind: \"localPath\"; path: string; size: number }\n | { kind: \"buffer\"; buffer: Buffer; fileName?: string };\n\nexport interface UploadChunkedOptions {\n scope: ChatScope;\n targetId: string;\n fileType: MediaFileType;\n source: ChunkedMediaSource;\n creds: Credentials;\n fileName?: string;\n onProgress?: (progress: ChunkedUploadProgress) => void;\n logPrefix?: string;\n}\n\nexport interface ChunkedMediaApiConfig {\n logger?: Logger;\n uploadCache?: UploadCacheAdapter;\n sanitizeFileName?: SanitizeFileNameFn;\n}\n\nconst DEFAULT_CONCURRENT_PARTS = 1;\nconst MAX_CONCURRENT_PARTS = 10;\nconst MAX_PART_FINISH_RETRY_TIMEOUT_MS = 10 * 60 * 1000;\nconst PART_UPLOAD_TIMEOUT_MS = 300_000;\nconst MD5_10M_SIZE = 10_002_432;\n\nexport class ChunkedMediaApi {\n private readonly client: ApiClient;\n private readonly tokenManager: TokenManager;\n private readonly logger?: Logger;\n private readonly cache?: UploadCacheAdapter;\n private readonly sanitize: SanitizeFileNameFn;\n\n constructor(client: ApiClient, tokenManager: TokenManager, config: ChunkedMediaApiConfig = {}) {\n this.client = client;\n this.tokenManager = tokenManager;\n this.logger = config.logger;\n this.cache = config.uploadCache;\n this.sanitize = config.sanitizeFileName ?? ((n) => n);\n }\n\n async uploadChunked(opts: UploadChunkedOptions): Promise<UploadMediaResponse> {\n const prefix = opts.logPrefix ?? \"[qqbot:chunked-upload]\";\n const input = resolveSource(opts.source, opts.fileName);\n\n const displayName = input.fileName;\n const fileSize = input.size;\n const pathLabel = input.kind === \"localPath\" ? input.path : \"<buffer>\";\n\n this.logger?.info?.(\n `${prefix} Start: file=${displayName} size=${formatFileSize(fileSize)} type=${opts.fileType}`,\n );\n\n const hashes = await computeHashes(input);\n this.logger?.debug?.(\n `${prefix} hashes: md5=${hashes.md5} sha1=${hashes.sha1} md5_10m=${hashes.md5_10m}`,\n );\n\n if (this.cache) {\n const cached = this.cache.get(hashes.md5, opts.scope, opts.targetId, opts.fileType);\n if (cached) {\n this.logger?.info?.(\n `${prefix} cache HIT (md5=${hashes.md5.slice(0, 8)}) — skipping chunked upload`,\n );\n return { file_uuid: \"\", file_info: cached, ttl: 0 };\n }\n }\n\n const fileNameForPrepare =\n opts.fileType === MediaFileType.FILE ? this.sanitize(displayName) : displayName;\n const prepareResp = await this.callUploadPrepare(\n opts,\n fileNameForPrepare,\n fileSize,\n hashes,\n pathLabel,\n );\n\n const { upload_id, parts } = prepareResp;\n const block_size = prepareResp.block_size;\n const maxConcurrent = Math.min(\n prepareResp.concurrency ? prepareResp.concurrency : DEFAULT_CONCURRENT_PARTS,\n MAX_CONCURRENT_PARTS,\n );\n const retryTimeoutMs = prepareResp.retry_timeout\n ? Math.min(prepareResp.retry_timeout * 1000, MAX_PART_FINISH_RETRY_TIMEOUT_MS)\n : undefined;\n\n this.logger?.info?.(\n `${prefix} prepared: upload_id=${upload_id} block=${formatFileSize(block_size)} parts=${parts.length} concurrency=${maxConcurrent}`,\n );\n\n let completedParts = 0;\n let uploadedBytes = 0;\n\n const uploadPart = async (part: UploadPart): Promise<void> => {\n const partIndex = part.index;\n const offset = (partIndex - 1) * block_size;\n const length = Math.min(block_size, fileSize - offset);\n\n const partBuffer = await readPart(input, offset, length);\n const md5Hex = crypto.createHash(\"md5\").update(partBuffer).digest(\"hex\");\n\n this.logger?.debug?.(\n `${prefix} part ${partIndex}/${parts.length}: ${formatFileSize(length)} offset=${offset} md5=${md5Hex}`,\n );\n\n await putToPresignedUrl(\n part.presigned_url,\n partBuffer,\n partIndex,\n parts.length,\n this.logger,\n prefix,\n );\n\n await this.callUploadPartFinish(opts, upload_id, partIndex, length, md5Hex, retryTimeoutMs);\n\n completedParts++;\n uploadedBytes += length;\n this.logger?.info?.(\n `${prefix} part ${partIndex}/${parts.length} done (${completedParts}/${parts.length})`,\n );\n\n opts.onProgress?.({\n completedParts,\n totalParts: parts.length,\n uploadedBytes,\n totalBytes: fileSize,\n });\n };\n\n await runWithConcurrency(\n parts.map((part) => () => uploadPart(part)),\n maxConcurrent,\n );\n\n this.logger?.info?.(`${prefix} all parts uploaded, completing...`);\n\n const result = await this.callCompleteUpload(opts, upload_id);\n this.logger?.info?.(`${prefix} completed: file_uuid=${result.file_uuid} ttl=${result.ttl}s`);\n\n if (this.cache && result.file_info && result.ttl > 0) {\n this.cache.set(\n hashes.md5,\n opts.scope,\n opts.targetId,\n opts.fileType,\n result.file_info,\n result.file_uuid,\n result.ttl,\n );\n }\n\n return result;\n }\n\n private async callUploadPrepare(\n opts: UploadChunkedOptions,\n fileName: string,\n fileSize: number,\n hashes: UploadPrepareHashes,\n pathLabel: string,\n ): Promise<UploadPrepareResponse> {\n const token = await this.tokenManager.getAccessToken(opts.creds.appId, opts.creds.clientSecret);\n const path = uploadPreparePath(opts.scope, opts.targetId);\n try {\n return await this.client.request<UploadPrepareResponse>(\n token,\n \"POST\",\n path,\n {\n file_type: opts.fileType,\n file_name: fileName,\n file_size: fileSize,\n md5: hashes.md5,\n sha1: hashes.sha1,\n md5_10m: hashes.md5_10m,\n },\n { uploadRequest: true },\n );\n } catch (err) {\n if (err instanceof ApiError && err.bizCode === UPLOAD_PREPARE_FALLBACK_CODE) {\n throw new UploadDailyLimitExceededError(pathLabel, fileSize, err.message);\n }\n throw err;\n }\n }\n\n private async callUploadPartFinish(\n opts: UploadChunkedOptions,\n uploadId: string,\n partIndex: number,\n blockSize: number,\n md5: string,\n retryTimeoutMs?: number,\n ): Promise<void> {\n const persistentPolicy = buildPartFinishPersistentPolicy(retryTimeoutMs);\n const path = uploadPartFinishPath(opts.scope, opts.targetId);\n await withRetry(\n async () => {\n const token = await this.tokenManager.getAccessToken(\n opts.creds.appId,\n opts.creds.clientSecret,\n );\n return this.client.request(\n token,\n \"POST\",\n path,\n {\n upload_id: uploadId,\n part_index: partIndex,\n block_size: blockSize,\n md5,\n },\n { uploadRequest: true },\n );\n },\n PART_FINISH_RETRY_POLICY,\n persistentPolicy,\n this.logger,\n );\n }\n\n private async callCompleteUpload(\n opts: UploadChunkedOptions,\n uploadId: string,\n ): Promise<UploadMediaResponse> {\n const path = uploadCompletePath(opts.scope, opts.targetId);\n return withRetry(\n async () => {\n const token = await this.tokenManager.getAccessToken(\n opts.creds.appId,\n opts.creds.clientSecret,\n );\n return this.client.request<UploadMediaResponse>(\n token,\n \"POST\",\n path,\n { upload_id: uploadId },\n { uploadRequest: true },\n );\n },\n COMPLETE_UPLOAD_RETRY_POLICY,\n undefined,\n this.logger,\n );\n }\n}\n\n// ============ Source resolution ============\n\ntype ChunkedInput =\n | { kind: \"localPath\"; path: string; size: number; fileName: string }\n | { kind: \"buffer\"; buffer: Buffer; size: number; fileName: string };\n\nfunction resolveSource(source: ChunkedMediaSource, fileNameOverride?: string): ChunkedInput {\n if (source.kind === \"localPath\") {\n const inferredName = source.path.split(/[/\\\\]/).pop() || \"file\";\n return {\n kind: \"localPath\",\n path: source.path,\n size: source.size,\n fileName: fileNameOverride ?? inferredName,\n };\n }\n return {\n kind: \"buffer\",\n buffer: source.buffer,\n size: source.buffer.length,\n fileName: fileNameOverride ?? source.fileName ?? \"file\",\n };\n}\n\nasync function readPart(input: ChunkedInput, offset: number, length: number): Promise<Buffer> {\n if (input.kind === \"buffer\") {\n return input.buffer.subarray(offset, offset + length);\n }\n const handle = await fs.promises.open(input.path, \"r\");\n try {\n const buf = Buffer.alloc(length);\n const { bytesRead } = await handle.read(buf, 0, length, offset);\n return bytesRead < length ? buf.subarray(0, bytesRead) : buf;\n } finally {\n await handle.close();\n }\n}\n\nasync function computeHashes(input: ChunkedInput): Promise<UploadPrepareHashes> {\n if (input.kind === \"buffer\") {\n const md5 = crypto.createHash(\"md5\").update(input.buffer).digest(\"hex\");\n const sha1 = crypto.createHash(\"sha1\").update(input.buffer).digest(\"hex\");\n const md5_10m =\n input.size > MD5_10M_SIZE\n ? crypto.createHash(\"md5\").update(input.buffer.subarray(0, MD5_10M_SIZE)).digest(\"hex\")\n : md5;\n return { md5, sha1, md5_10m };\n }\n\n return new Promise((resolve, reject) => {\n const md5 = crypto.createHash(\"md5\");\n const sha1 = crypto.createHash(\"sha1\");\n const md5_10m = crypto.createHash(\"md5\");\n let consumed = 0;\n const needsMd5_10m = input.size > MD5_10M_SIZE;\n\n const stream = fs.createReadStream(input.path);\n stream.on(\"data\", (chunk: Buffer | string) => {\n const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n md5.update(buf);\n sha1.update(buf);\n if (needsMd5_10m) {\n const remaining = MD5_10M_SIZE - consumed;\n if (remaining > 0) {\n md5_10m.update(remaining >= buf.length ? buf : buf.subarray(0, remaining));\n }\n }\n consumed += buf.length;\n });\n stream.on(\"end\", () => {\n const md5Hex = md5.digest(\"hex\");\n const sha1Hex = sha1.digest(\"hex\");\n resolve({\n md5: md5Hex,\n sha1: sha1Hex,\n md5_10m: needsMd5_10m ? md5_10m.digest(\"hex\") : md5Hex,\n });\n });\n stream.on(\"error\", reject);\n });\n}\n\n// ============ COS PUT ============\n\nconst PART_UPLOAD_MAX_RETRIES = 2;\n\n/**\n * PUT data to COS via raw https.request, bypassing fetch/undici.\n * Used because Node.js fetch can fail unpredictably in long-running processes.\n */\nfunction putToCOS(\n presignedUrl: string,\n data: Buffer,\n signal: AbortSignal,\n): Promise<{ status: number; etag: string; requestId: string }> {\n return new Promise((resolve, reject) => {\n const parsed = new URL(presignedUrl);\n const req = https.request(\n parsed,\n {\n method: \"PUT\",\n headers: { \"Content-Length\": String(data.length) },\n signal,\n },\n (res) => {\n const chunks: Buffer[] = [];\n res.on(\"data\", (c: Buffer) => chunks.push(c));\n res.on(\"end\", () => {\n const etag = (res.headers.etag ?? \"\").replace(/\"/g, \"\");\n const requestId = res.headers[\"x-cos-request-id\"]?.toString() ?? \"-\";\n if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {\n resolve({ status: res.statusCode, etag, requestId });\n } else {\n reject(\n new Error(\n `COS PUT failed: ${res.statusCode} ${res.statusMessage ?? \"\"} - ${\n Buffer.concat(chunks).toString().slice(0, 120)\n }`,\n ),\n );\n }\n });\n res.on(\"error\", reject);\n },\n );\n req.on(\"error\", (err) => {\n // Suppress DOMException typings from AbortSignal error events\n reject(err as unknown as Error);\n });\n req.end(data);\n });\n}\n\nasync function putToPresignedUrl(\n presignedUrl: string,\n data: Buffer,\n partIndex: number,\n totalParts: number,\n logger: Logger | undefined,\n prefix: string,\n): Promise<void> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= PART_UPLOAD_MAX_RETRIES; attempt++) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), PART_UPLOAD_TIMEOUT_MS);\n\n try {\n const startTime = Date.now();\n const { etag, requestId } = await putToCOS(\n presignedUrl,\n data,\n controller.signal,\n );\n const elapsed = Date.now() - startTime;\n\n logger?.debug?.(\n `${prefix} PUT part ${partIndex}/${totalParts} OK (${elapsed}ms ETag=${etag} requestId=${requestId})`,\n );\n return;\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n const code = (err as NodeJS.ErrnoException).code ?? \"none\";\n const causeMsg = (() => {\n const c = err instanceof Error ? err.cause : undefined;\n return c instanceof Error ? c.message : \"none\";\n })();\n if (lastError.name === \"AbortError\") {\n lastError = new Error(\n `Part ${partIndex}/${totalParts} upload timeout after ${PART_UPLOAD_TIMEOUT_MS}ms`,\n );\n }\n if (attempt < PART_UPLOAD_MAX_RETRIES) {\n const delay = 1000 * 2 ** attempt;\n (logger?.warn ?? logger?.error)?.(\n `${prefix} PUT part ${partIndex}/${totalParts} attempt ${attempt + 1} failed (${lastError.message.slice(0, 120)} code=${code} cause=${causeMsg}), retrying in ${delay}ms`,\n );\n await sleep(delay);\n } else {\n (logger?.error)?.(\n `${prefix} PUT part ${partIndex}/${totalParts} all retries exhausted (code=${code} cause=${causeMsg})`,\n );\n }\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n throw lastError ?? new Error(`Part ${partIndex}/${totalParts} upload failed`);\n}\n\nasync function runWithConcurrency(\n tasks: Array<() => Promise<void>>,\n maxConcurrent: number,\n): Promise<void> {\n for (let i = 0; i < tasks.length; i += maxConcurrent) {\n const batch = tasks.slice(i, i + maxConcurrent);\n await Promise.all(batch.map((task) => task()));\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * Generic retry engine for QQ Bot API requests.\n */\n\nimport type { Logger } from \"../types.js\";\nimport { formatErrorMessage } from \"../utils/format.js\";\n\nexport interface RetryPolicy {\n maxRetries: number;\n baseDelayMs: number;\n backoff: \"exponential\" | \"fixed\";\n shouldRetry?: (error: Error, attempt: number) => boolean;\n}\n\nexport interface PersistentRetryPolicy {\n timeoutMs: number;\n intervalMs: number;\n shouldPersistRetry: (error: Error) => boolean;\n}\n\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n policy: RetryPolicy,\n persistentPolicy?: PersistentRetryPolicy,\n logger?: Logger,\n): Promise<T> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= policy.maxRetries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(formatErrorMessage(err));\n\n if (persistentPolicy?.shouldPersistRetry(lastError)) {\n (logger?.warn ?? logger?.error)?.(\n `[qqbot:retry] Hit persistent-retry trigger, entering persistent loop (timeout=${persistentPolicy.timeoutMs / 1000}s)`,\n );\n return await persistentRetryLoop(fn, persistentPolicy, logger);\n }\n\n if (policy.shouldRetry?.(lastError, attempt) === false) {\n throw lastError;\n }\n\n if (attempt < policy.maxRetries) {\n const delay =\n policy.backoff === \"exponential\" ? policy.baseDelayMs * 2 ** attempt : policy.baseDelayMs;\n\n logger?.debug?.(\n `[qqbot:retry] Attempt ${attempt + 1} failed, retrying in ${delay}ms: ${lastError.message.slice(0, 100)}`,\n );\n await sleep(delay);\n }\n }\n }\n\n throw lastError!;\n}\n\nasync function persistentRetryLoop<T>(\n fn: () => Promise<T>,\n policy: PersistentRetryPolicy,\n logger?: Logger,\n): Promise<T> {\n const deadline = Date.now() + policy.timeoutMs;\n let attempt = 0;\n let lastError: Error | null = null;\n\n while (Date.now() < deadline) {\n try {\n const result = await fn();\n logger?.debug?.(`[qqbot:retry] Persistent retry succeeded after ${attempt} retries`);\n return result;\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(formatErrorMessage(err));\n\n if (!policy.shouldPersistRetry(lastError)) {\n logger?.error?.(`[qqbot:retry] Persistent retry: error is no longer retryable, aborting`);\n throw lastError;\n }\n\n attempt++;\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n break;\n }\n\n const actualDelay = Math.min(policy.intervalMs, remaining);\n (logger?.warn ?? logger?.error)?.(\n `[qqbot:retry] Persistent retry #${attempt}: retrying in ${actualDelay}ms (remaining=${Math.round(remaining / 1000)}s)`,\n );\n await sleep(actualDelay);\n }\n }\n\n logger?.error?.(\n `[qqbot:retry] Persistent retry timed out after ${policy.timeoutMs / 1000}s (${attempt} attempts)`,\n );\n throw lastError ?? new Error(`Persistent retry timed out (${policy.timeoutMs / 1000}s)`);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// ============ Pre-built Retry Policies ============\n\nexport const UPLOAD_RETRY_POLICY: RetryPolicy = {\n maxRetries: 2,\n baseDelayMs: 1000,\n backoff: \"exponential\",\n shouldRetry: (error) => {\n const msg = error.message;\n return !(\n msg.includes(\"400\") ||\n msg.includes(\"401\") ||\n msg.includes(\"Invalid\") ||\n msg.includes(\"timeout\") ||\n msg.includes(\"Timeout\")\n );\n },\n};\n\nexport const COMPLETE_UPLOAD_RETRY_POLICY: RetryPolicy = {\n maxRetries: 2,\n baseDelayMs: 2000,\n backoff: \"exponential\",\n};\n\nexport const PART_FINISH_RETRY_POLICY: RetryPolicy = {\n maxRetries: 2,\n baseDelayMs: 1000,\n backoff: \"exponential\",\n};\n\nexport function buildPartFinishPersistentPolicy(\n retryTimeoutMs?: number,\n retryableCodes: Set<number> = PART_FINISH_RETRYABLE_CODES,\n): PersistentRetryPolicy {\n return {\n timeoutMs: retryTimeoutMs ?? 2 * 60 * 1000,\n intervalMs: 1000,\n shouldPersistRetry: (error) => {\n if (retryableCodes.size === 0) {\n return false;\n }\n if (\"bizCode\" in error && typeof (error as { bizCode?: number }).bizCode === \"number\") {\n return retryableCodes.has((error as { bizCode: number }).bizCode);\n }\n return false;\n },\n };\n}\n\nexport const PART_FINISH_RETRYABLE_CODES: Set<number> = new Set([40093001]);\nexport const UPLOAD_PREPARE_FALLBACK_CODE = 40093002;\n","/**\n * Centralized API route templates for the QQ Open Platform.\n *\n * Eliminates C2C/Group path duplication by parameterizing on `ChatScope`.\n */\n\nimport type { ChatScope } from \"../types.js\";\n\nexport function messagePath(scope: ChatScope, targetId: string): string {\n return scope === \"c2c\" ? `/v2/users/${targetId}/messages` : `/v2/groups/${targetId}/messages`;\n}\n\nexport function channelMessagePath(channelId: string): string {\n return `/channels/${channelId}/messages`;\n}\n\nexport function dmMessagePath(guildId: string): string {\n return `/dms/${guildId}/messages`;\n}\n\nexport function mediaUploadPath(scope: ChatScope, targetId: string): string {\n return scope === \"c2c\" ? `/v2/users/${targetId}/files` : `/v2/groups/${targetId}/files`;\n}\n\nexport function uploadPreparePath(scope: ChatScope, targetId: string): string {\n return scope === \"c2c\"\n ? `/v2/users/${targetId}/upload_prepare`\n : `/v2/groups/${targetId}/upload_prepare`;\n}\n\nexport function uploadPartFinishPath(scope: ChatScope, targetId: string): string {\n return scope === \"c2c\"\n ? `/v2/users/${targetId}/upload_part_finish`\n : `/v2/groups/${targetId}/upload_part_finish`;\n}\n\nexport function uploadCompletePath(scope: ChatScope, targetId: string): string {\n return mediaUploadPath(scope, targetId);\n}\n\nexport function streamMessagePath(openid: string): string {\n return `/v2/users/${openid}/stream_messages`;\n}\n\nexport function gatewayPath(): string {\n return \"/gateway\";\n}\n\nexport function interactionPath(interactionId: string): string {\n return `/interactions/${interactionId}`;\n}\n\n/**\n * Generate a message sequence number in the 0..65535 range.\n *\n * Used by both `messages.ts` and `media.ts` to avoid duplicate definitions.\n */\nexport function getNextMsgSeq(_msgId: string): number {\n const timePart = Date.now() % 100_000_000;\n const random = Math.floor(Math.random() * 65536);\n return (timePart ^ random) % 65536;\n}\n","/**\n * Small-file media upload API for the QQ Open Platform.\n */\n\nimport * as fs from \"node:fs\";\nimport {\n MediaFileType,\n type ChatScope,\n type Credentials,\n type Logger,\n type MessageResponse,\n type UploadMediaResponse,\n} from \"../types.js\";\nimport { ApiClient } from \"./api-client.js\";\nimport { UPLOAD_RETRY_POLICY, withRetry } from \"./retry.js\";\nimport { getNextMsgSeq, mediaUploadPath, messagePath } from \"./routes.js\";\nimport { TokenManager } from \"./token.js\";\nimport { MAX_UPLOAD_SIZE } from \"../utils/file-utils.js\";\nimport { formatFileSize } from \"../utils/format.js\";\n\n/** base64 编码后的大小上限检查(4/3 × 原始大小 + padding,取 1.4 倍系数) */\nconst MAX_BASE64_CHECK_SIZE = Math.ceil(MAX_UPLOAD_SIZE * 1.4);\n\nfunction formatUploadSize(): string {\n return formatFileSize(MAX_UPLOAD_SIZE);\n}\n\n/** Upload cache adapter. The caller provides the implementation. */\nexport interface UploadCacheAdapter {\n computeHash: (data: string) => string;\n get: (hash: string, scope: string, targetId: string, fileType: number) => string | null;\n set: (\n hash: string,\n scope: string,\n targetId: string,\n fileType: number,\n fileInfo: string,\n fileUuid: string,\n ttl: number,\n ) => void;\n}\n\nexport type SanitizeFileNameFn = (name: string) => string;\n\nexport interface MediaApiConfig {\n logger?: Logger;\n uploadCache?: UploadCacheAdapter;\n sanitizeFileName?: SanitizeFileNameFn;\n}\n\n/** Small-file media upload module. */\nexport class MediaApi {\n private readonly client: ApiClient;\n private readonly tokenManager: TokenManager;\n private readonly logger?: Logger;\n private readonly cache?: UploadCacheAdapter;\n private readonly sanitize: SanitizeFileNameFn;\n\n constructor(client: ApiClient, tokenManager: TokenManager, config: MediaApiConfig = {}) {\n this.client = client;\n this.tokenManager = tokenManager;\n this.logger = config.logger;\n this.cache = config.uploadCache;\n this.sanitize = config.sanitizeFileName ?? ((n) => n);\n }\n\n /**\n * Upload media via base64, URL, buffer, or local file path to a C2C or Group target.\n */\n async uploadMedia(\n scope: ChatScope,\n targetId: string,\n fileType: MediaFileType,\n creds: Credentials,\n opts: {\n url?: string;\n fileData?: string;\n buffer?: Buffer;\n localPath?: string;\n srvSendMsg?: boolean;\n fileName?: string;\n },\n ): Promise<UploadMediaResponse> {\n const sources = [opts.url, opts.fileData, opts.buffer, opts.localPath].filter(\n (v) => v !== undefined,\n );\n if (sources.length === 0) {\n throw new Error(`uploadMedia: one of url/fileData/buffer/localPath is required`);\n }\n if (sources.length > 1) {\n throw new Error(\n `uploadMedia: url/fileData/buffer/localPath are mutually exclusive (got ${sources.length})`,\n );\n }\n\n let fileData = opts.fileData;\n if (opts.buffer) {\n fileData = opts.buffer.toString(\"base64\");\n } else if (opts.localPath) {\n const buf = await fs.promises.readFile(opts.localPath);\n fileData = buf.toString(\"base64\");\n }\n\n // 前置检查:base64 数据不能超过 QQ 单次上传上限,避免走到 API 才报错\n if (fileData && fileData.length > MAX_BASE64_CHECK_SIZE) {\n const sizeMB = (fileData.length / (1024 * 1024)).toFixed(1);\n throw new Error(\n `fileData too large (${sizeMB}MB decoded); QQ Bot single upload limit is ${formatUploadSize()}`,\n );\n }\n\n if (fileData && this.cache) {\n const hash = this.cache.computeHash(fileData);\n const cached = this.cache.get(hash, scope, targetId, fileType);\n if (cached) {\n return { file_uuid: \"\", file_info: cached, ttl: 0 };\n }\n }\n\n const body: Record<string, unknown> = {\n file_type: fileType,\n srv_send_msg: opts.srvSendMsg ?? false,\n };\n if (opts.url) {\n body.url = opts.url;\n } else if (fileData) {\n body.file_data = fileData;\n }\n if (fileType === MediaFileType.FILE && opts.fileName) {\n body.file_name = this.sanitize(opts.fileName);\n }\n\n const token = await this.tokenManager.getAccessToken(creds.appId, creds.clientSecret);\n const path = mediaUploadPath(scope, targetId);\n\n const result = await withRetry(\n () =>\n this.client.request<UploadMediaResponse>(token, \"POST\", path, body, {\n redactBodyKeys: [\"file_data\"],\n uploadRequest: true,\n }),\n UPLOAD_RETRY_POLICY,\n undefined,\n this.logger,\n );\n\n if (fileData && result.file_info && result.ttl > 0 && this.cache) {\n const hash = this.cache.computeHash(fileData);\n this.cache.set(\n hash,\n scope,\n targetId,\n fileType,\n result.file_info,\n result.file_uuid,\n result.ttl,\n );\n }\n\n return result;\n }\n\n /**\n * Send a media message (post upload) to a C2C or Group target.\n */\n async sendMediaMessage(\n scope: ChatScope,\n targetId: string,\n fileInfo: string,\n creds: Credentials,\n opts?: {\n msgId?: string;\n content?: string;\n },\n ): Promise<MessageResponse> {\n const token = await this.tokenManager.getAccessToken(creds.appId, creds.clientSecret);\n const msgSeq = opts?.msgId ? getNextMsgSeq(opts.msgId) : 1;\n const path = messagePath(scope, targetId);\n\n return this.client.request<MessageResponse>(token, \"POST\", path, {\n msg_type: 7,\n media: { file_info: fileInfo },\n msg_seq: msgSeq,\n ...(opts?.content ? { content: opts.content } : {}),\n ...(opts?.msgId ? { msg_id: opts.msgId } : {}),\n });\n }\n}\n","/**\n * File-related helpers for the QQ Open Platform SDK.\n *\n * Pure helpers: filename sanitization, size constants, file size formatting.\n * The SDK does NOT bundle SSRF / remote-fetch logic — those are concerns\n * of the host application (e.g. an OpenClaw plugin) and should be\n * implemented at the integration layer.\n */\n\nimport { MediaFileType } from \"../types.js\";\n\n/** Maximum file size accepted by the QQ Bot one-shot upload API (base64 direct). */\nexport const MAX_UPLOAD_SIZE = 20 * 1024 * 1024;\n\n/** Absolute upper bound enforced on the chunked upload path. */\nexport const CHUNKED_UPLOAD_MAX_SIZE = 100 * 1024 * 1024;\n\n/** Threshold above which uploads are dispatched to the chunked path. */\nexport const LARGE_FILE_THRESHOLD = 5 * 1024 * 1024;\n\n/** Per-{@link MediaFileType} upload metadata. */\nexport const MEDIA_FILE_TYPE_INFO: Record<MediaFileType, { maxSize: number; name: string }> = {\n [MediaFileType.IMAGE]: { maxSize: 30 * 1024 * 1024, name: \"image\" },\n [MediaFileType.VIDEO]: { maxSize: 100 * 1024 * 1024, name: \"video\" },\n [MediaFileType.VOICE]: { maxSize: 20 * 1024 * 1024, name: \"voice\" },\n [MediaFileType.FILE]: { maxSize: 100 * 1024 * 1024, name: \"file\" },\n};\n\nexport function getFileTypeName(fileType: number): string {\n return MEDIA_FILE_TYPE_INFO[fileType as MediaFileType]?.name ?? \"file\";\n}\n\nexport function getMaxUploadSize(fileType: number): number {\n return MEDIA_FILE_TYPE_INFO[fileType as MediaFileType]?.maxSize ?? CHUNKED_UPLOAD_MAX_SIZE;\n}\n\n/** Canonical ext → MIME table. Single source of truth. */\nconst MIME_TYPES: Record<string, string> = {\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".png\": \"image/png\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n \".bmp\": \"image/bmp\",\n \".mp4\": \"video/mp4\",\n \".mov\": \"video/quicktime\",\n \".avi\": \"video/x-msvideo\",\n \".mkv\": \"video/x-matroska\",\n \".webm\": \"video/webm\",\n \".pdf\": \"application/pdf\",\n \".doc\": \"application/msword\",\n \".docx\": \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \".xls\": \"application/vnd.ms-excel\",\n \".xlsx\": \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n \".zip\": \"application/zip\",\n \".tar\": \"application/x-tar\",\n \".gz\": \"application/gzip\",\n \".txt\": \"text/plain\",\n};\n\n/** Extensions accepted as image uploads by the QQ Bot media pipeline. */\nconst IMAGE_EXTENSIONS = new Set([\".jpg\", \".jpeg\", \".png\", \".gif\", \".webp\", \".bmp\"]);\n\n/**\n * Extract the lowercase extension (`\".png\"`) from a path-like string.\n *\n * Inlined from the lookup helpers so this module stays free of `node:path`\n * imports — works the same for `/abs/foo.PNG`, `relative/foo.PNG`,\n * `bare.PNG`, and accepts both forward and backward slashes.\n */\nfunction lowercaseExt(filePath: string): string {\n const sepIdx = Math.max(filePath.lastIndexOf(\"/\"), filePath.lastIndexOf(\"\\\\\"));\n const base = sepIdx === -1 ? filePath : filePath.slice(sepIdx + 1);\n const dotIdx = base.lastIndexOf(\".\");\n if (dotIdx <= 0) {\n return \"\";\n }\n return base.slice(dotIdx).toLowerCase();\n}\n\n/** Infer a MIME type from a file path's extension. */\nexport function getMimeType(filePath: string): string {\n return MIME_TYPES[lowercaseExt(filePath)] ?? \"application/octet-stream\";\n}\n\n/**\n * Return the image MIME type for a local file path, or `null` if the\n * extension is not in the supported image whitelist.\n *\n * Use this instead of {@link getMimeType} when the caller must enforce\n * \"image formats only\" as a business rule (e.g. constructing a\n * `data:image/...;base64,` URL).\n */\nexport function getImageMimeType(filePath: string): string | null {\n const ext = lowercaseExt(filePath);\n if (!IMAGE_EXTENSIONS.has(ext)) {\n return null;\n }\n return MIME_TYPES[ext] ?? null;\n}\n\n/**\n * Sanitize a filename for safe transmission to the QQ Open Platform.\n *\n * - Strips path separators / control characters.\n * - Collapses repeated whitespace.\n * - Falls back to `\"file\"` when the result is empty.\n */\nexport function sanitizeFileName(name: string): string {\n if (!name) {\n return \"file\";\n }\n \n const cleaned = name\n .replace(/[\\\\/:*?\"<>|]/g, \"_\")\n // eslint-disable-next-line no-control-regex\n .replace(/[\\u0000-\\u001f\\u007f]/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n return cleaned || \"file\";\n}\n","/**\n * Message-sending API for the QQ Open Platform.\n */\n\nimport type {\n ChatScope,\n Credentials,\n InlineKeyboard,\n Logger,\n MessageResponse,\n OutboundMeta,\n StreamMessageRequest,\n} from \"../types.js\";\nimport { formatErrorMessage } from \"../utils/format.js\";\nimport { ApiClient } from \"./api-client.js\";\nimport {\n channelMessagePath,\n dmMessagePath,\n gatewayPath,\n getNextMsgSeq,\n interactionPath,\n messagePath,\n streamMessagePath,\n} from \"./routes.js\";\nimport { TokenManager } from \"./token.js\";\n\nexport interface MessageApiConfig {\n /** Whether the QQ Bot has markdown permission. */\n markdownSupport: boolean;\n logger?: Logger;\n}\n\ntype OnMessageSentCallback = (refIdx: string, meta: OutboundMeta) => void;\n\nexport class MessageApi {\n private readonly client: ApiClient;\n private readonly tokenManager: TokenManager;\n private readonly markdownSupport: boolean;\n private readonly logger?: Logger;\n private messageSentHook: OnMessageSentCallback | null = null;\n\n constructor(client: ApiClient, tokenManager: TokenManager, config: MessageApiConfig) {\n this.client = client;\n this.tokenManager = tokenManager;\n this.markdownSupport = config.markdownSupport;\n this.logger = config.logger;\n }\n\n onMessageSent(callback: OnMessageSentCallback): void {\n this.messageSentHook = callback;\n }\n\n notifyMessageSent(refIdx: string, meta: OutboundMeta): void {\n if (this.messageSentHook) {\n try {\n this.messageSentHook(refIdx, meta);\n } catch (err) {\n this.logger?.error?.(\n `[qqbot:messages] onMessageSent hook error: ${formatErrorMessage(err)}`,\n );\n }\n }\n }\n\n async sendMessage(\n scope: ChatScope,\n targetId: string,\n content: string,\n creds: Credentials,\n opts?: {\n msgId?: string;\n messageReference?: string;\n inlineKeyboard?: InlineKeyboard;\n },\n ): Promise<MessageResponse> {\n const token = await this.tokenManager.getAccessToken(creds.appId, creds.clientSecret);\n const msgSeq = opts?.msgId ? getNextMsgSeq(opts.msgId) : 1;\n const body = this.buildMessageBody(\n content,\n opts?.msgId,\n msgSeq,\n opts?.messageReference,\n opts?.inlineKeyboard,\n );\n const path = messagePath(scope, targetId);\n return this.sendAndNotify(creds.appId, token, \"POST\", path, body, { text: content });\n }\n\n async sendProactiveMessage(\n scope: ChatScope,\n targetId: string,\n content: string,\n creds: Credentials,\n ): Promise<MessageResponse> {\n if (!content?.trim()) {\n throw new Error(\"Proactive message content must not be empty\");\n }\n const token = await this.tokenManager.getAccessToken(creds.appId, creds.clientSecret);\n const body = this.buildProactiveBody(content);\n const path = messagePath(scope, targetId);\n return this.sendAndNotify(creds.appId, token, \"POST\", path, body, { text: content });\n }\n\n async sendChannelMessage(opts: {\n channelId: string;\n content: string;\n creds: Credentials;\n msgId?: string;\n }): Promise<MessageResponse> {\n const token = await this.tokenManager.getAccessToken(opts.creds.appId, opts.creds.clientSecret);\n return this.client.request<MessageResponse>(token, \"POST\", channelMessagePath(opts.channelId), {\n content: opts.content,\n ...(opts.msgId ? { msg_id: opts.msgId } : {}),\n });\n }\n\n async sendDmMessage(opts: {\n guildId: string;\n content: string;\n creds: Credentials;\n msgId?: string;\n }): Promise<MessageResponse> {\n const token = await this.tokenManager.getAccessToken(opts.creds.appId, opts.creds.clientSecret);\n return this.client.request<MessageResponse>(token, \"POST\", dmMessagePath(opts.guildId), {\n content: opts.content,\n ...(opts.msgId ? { msg_id: opts.msgId } : {}),\n });\n }\n\n /** Send a typing indicator to a C2C user. */\n async sendInputNotify(opts: {\n openid: string;\n creds: Credentials;\n msgId?: string;\n inputSecond?: number;\n }): Promise<{ refIdx?: string }> {\n const inputSecond = opts.inputSecond ?? 60;\n const token = await this.tokenManager.getAccessToken(opts.creds.appId, opts.creds.clientSecret);\n const msgSeq = opts.msgId ? getNextMsgSeq(opts.msgId) : 1;\n const response = await this.client.request<{ ext_info?: { ref_idx?: string } }>(\n token,\n \"POST\",\n messagePath(\"c2c\", opts.openid),\n {\n msg_type: 6,\n input_notify: { input_type: 1, input_second: inputSecond },\n msg_seq: msgSeq,\n ...(opts.msgId ? { msg_id: opts.msgId } : {}),\n },\n );\n return { refIdx: response.ext_info?.ref_idx };\n }\n\n async acknowledgeInteraction(\n interactionId: string,\n creds: Credentials,\n code = 0,\n data?: Record<string, unknown>,\n ): Promise<void> {\n const token = await this.tokenManager.getAccessToken(creds.appId, creds.clientSecret);\n const body: Record<string, unknown> = { code };\n if (data) body.data = data;\n await this.client.request(token, \"PUT\", interactionPath(interactionId), body);\n }\n\n /** Get the WebSocket gateway URL for the bot. */\n async getGatewayUrl(creds: Credentials): Promise<string> {\n const token = await this.tokenManager.getAccessToken(creds.appId, creds.clientSecret);\n const data = await this.client.request<{ url: string }>(token, \"GET\", gatewayPath());\n return data.url;\n }\n\n /**\n * Send a C2C stream message chunk (`/v2/users/{openid}/stream_messages`).\n * Only supported for one-to-one chats.\n */\n async sendC2CStreamMessage(\n creds: Credentials,\n openid: string,\n req: StreamMessageRequest,\n ): Promise<MessageResponse> {\n const token = await this.tokenManager.getAccessToken(creds.appId, creds.clientSecret);\n const path = streamMessagePath(openid);\n const body: Record<string, unknown> = {\n input_mode: req.input_mode,\n input_state: req.input_state,\n content_type: req.content_type,\n content_raw: req.content_raw,\n event_id: req.event_id,\n msg_id: req.msg_id,\n msg_seq: req.msg_seq,\n index: req.index,\n };\n if (req.stream_msg_id) {\n body.stream_msg_id = req.stream_msg_id;\n }\n return this.client.request<MessageResponse>(token, \"POST\", path, body);\n }\n\n /**\n * Raw message send — transparently forwards all fields to the QQ Open Platform API.\n *\n * This is the \"escape hatch\" for any message type not covered by the\n * higher-level helpers. Fields like `msg_type`, `markdown`, `ark`, `embed`,\n * `keyboard`, `media`, `message_reference`, `is_wakeup` etc. are passed through\n * as-is to `/v2/users/{openid}/messages` or `/v2/groups/{group_openid}/messages`.\n *\n * Auto-injects `msg_seq` if not provided.\n */\n async sendRaw(\n scope: ChatScope,\n targetId: string,\n creds: Credentials,\n body: Record<string, unknown>,\n ): Promise<MessageResponse> {\n const token = await this.tokenManager.getAccessToken(creds.appId, creds.clientSecret);\n const path = messagePath(scope, targetId);\n\n // Auto-inject msg_seq if missing\n if (body.msg_seq === undefined) {\n body.msg_seq = body.msg_id ? getNextMsgSeq(body.msg_id as string) : 1;\n }\n // Auto-detect msg_type if missing\n if (body.msg_type === undefined) {\n if (body.markdown) body.msg_type = 2;\n else if (body.ark) body.msg_type = 3;\n else if (body.embed) body.msg_type = 4;\n else if (body.media) body.msg_type = 7;\n else body.msg_type = 0;\n }\n\n // Remove undefined values\n const cleaned = Object.fromEntries(\n Object.entries(body).filter(([, v]) => v !== undefined),\n );\n\n return this.sendAndNotify(\n creds.appId,\n token,\n \"POST\",\n path,\n cleaned,\n { text: (cleaned.content as string) ?? (cleaned.markdown as { content?: string } | undefined)?.content },\n );\n }\n\n /**\n * Send a message to a guild text channel.\n * Supports content, keyboard, message_reference, and arbitrary extra fields.\n */\n async sendChannelMessageRaw(\n channelId: string,\n creds: Credentials,\n body: Record<string, unknown>,\n ): Promise<MessageResponse> {\n const token = await this.tokenManager.getAccessToken(creds.appId, creds.clientSecret);\n const cleaned = Object.fromEntries(\n Object.entries(body).filter(([, v]) => v !== undefined),\n );\n return this.client.request<MessageResponse>(\n token, \"POST\", channelMessagePath(channelId), cleaned,\n );\n }\n\n /**\n * Send a DM (direct message) in a guild.\n */\n async sendDmMessageRaw(\n guildId: string,\n creds: Credentials,\n body: Record<string, unknown>,\n ): Promise<MessageResponse> {\n const token = await this.tokenManager.getAccessToken(creds.appId, creds.clientSecret);\n const cleaned = Object.fromEntries(\n Object.entries(body).filter(([, v]) => v !== undefined),\n );\n return this.client.request<MessageResponse>(\n token, \"POST\", dmMessagePath(guildId), cleaned,\n );\n }\n\n /**\n * Recall (delete) a message.\n */\n async recallMessage(\n scope: ChatScope,\n targetId: string,\n messageId: string,\n creds: Credentials,\n ): Promise<void> {\n const token = await this.tokenManager.getAccessToken(creds.appId, creds.clientSecret);\n const path = `${messagePath(scope, targetId)}/${messageId}`;\n await this.client.request(token, \"DELETE\", path);\n }\n\n private async sendAndNotify(\n _appId: string,\n accessToken: string,\n method: string,\n path: string,\n body: unknown,\n meta: OutboundMeta,\n ): Promise<MessageResponse> {\n const result = await this.client.request<MessageResponse>(accessToken, method, path, body);\n if (result.ext_info?.ref_idx && this.messageSentHook) {\n try {\n this.messageSentHook(result.ext_info.ref_idx, meta);\n } catch (err) {\n this.logger?.error?.(\n `[qqbot:messages] onMessageSent hook error: ${formatErrorMessage(err)}`,\n );\n }\n }\n return result;\n }\n\n private buildMessageBody(\n content: string,\n msgId: string | undefined,\n msgSeq: number,\n messageReference?: string,\n inlineKeyboard?: InlineKeyboard,\n ): Record<string, unknown> {\n const body: Record<string, unknown> = this.markdownSupport\n ? { markdown: { content }, msg_type: 2, msg_seq: msgSeq }\n : { content, msg_type: 0, msg_seq: msgSeq };\n\n if (msgId) {\n body.msg_id = msgId;\n }\n if (messageReference && !this.markdownSupport) {\n body.message_reference = { message_id: messageReference };\n }\n if (inlineKeyboard) {\n body.keyboard = inlineKeyboard;\n }\n return body;\n }\n\n private buildProactiveBody(content: string): Record<string, unknown> {\n return this.markdownSupport ? { markdown: { content }, msg_type: 2 } : { content, msg_type: 0 };\n }\n}\n\nexport { getNextMsgSeq } from \"./routes.js\";\n","/**\n * Token management for the QQ Open Platform.\n *\n * Per-appId token manager with caching, singleflight, and background refresh.\n * All state encapsulated in the class — no module-level globals.\n */\n\nimport type { Logger } from \"../types.js\";\nimport { formatErrorMessage } from \"../utils/format.js\";\n\nconst DEFAULT_TOKEN_BASE_URL = \"https://bots.qq.com\";\nconst TOKEN_PATH = \"/app/getAppAccessToken\";\nconst DEFAULT_TOKEN_TIMEOUT_MS = 10_000;\nconst FIVE_MINUTES_MS = 5 * 60 * 1000;\n\ninterface CachedToken {\n token: string;\n expiresAt: number;\n appId: string;\n}\n\nexport interface BackgroundRefreshOptions {\n refreshAheadMs?: number;\n randomOffsetMs?: number;\n minRefreshIntervalMs?: number;\n retryDelayMs?: number;\n}\n\nexport class TokenManager {\n private readonly cache = new Map<string, CachedToken>();\n private readonly fetchPromises = new Map<string, Promise<string>>();\n private readonly refreshControllers = new Map<string, AbortController>();\n private readonly logger?: Logger;\n private readonly resolveUserAgent: () => string;\n private readonly baseUrl: string;\n\n constructor(config?: { logger?: Logger; userAgent?: string | (() => string); baseUrl?: string }) {\n this.logger = config?.logger;\n const ua = config?.userAgent ?? \"qqbot-nodejs/unknown\";\n this.resolveUserAgent = typeof ua === \"function\" ? ua : () => ua;\n this.baseUrl = config?.baseUrl ?? DEFAULT_TOKEN_BASE_URL;\n }\n\n async getAccessToken(appId: string, clientSecret: string): Promise<string> {\n const normalizedId = appId.trim();\n const cached = this.cache.get(normalizedId);\n\n const refreshAheadMs = cached\n ? Math.min(FIVE_MINUTES_MS, (cached.expiresAt - Date.now()) / 3)\n : 0;\n\n if (cached && Date.now() < cached.expiresAt - refreshAheadMs) {\n return cached.token;\n }\n\n let pending = this.fetchPromises.get(normalizedId);\n if (pending) {\n this.logger?.debug?.(`[qqbot:token:${normalizedId}] Fetch in progress, reusing promise`);\n return pending;\n }\n\n pending = (async () => {\n try {\n return await this.doFetchToken(normalizedId, clientSecret);\n } finally {\n this.fetchPromises.delete(normalizedId);\n }\n })();\n\n this.fetchPromises.set(normalizedId, pending);\n return pending;\n }\n\n clearCache(appId?: string): void {\n if (appId) {\n this.cache.delete(appId.trim());\n this.logger?.debug?.(`[qqbot:token:${appId}] Cache cleared`);\n } else {\n this.cache.clear();\n this.logger?.debug?.(`[token] All caches cleared`);\n }\n }\n\n getStatus(appId: string): {\n status: \"valid\" | \"expired\" | \"refreshing\" | \"none\";\n expiresAt: number | null;\n } {\n if (this.fetchPromises.has(appId)) {\n return { status: \"refreshing\", expiresAt: this.cache.get(appId)?.expiresAt ?? null };\n }\n const cached = this.cache.get(appId);\n if (!cached) {\n return { status: \"none\", expiresAt: null };\n }\n const remaining = cached.expiresAt - Date.now();\n const isValid = remaining > Math.min(FIVE_MINUTES_MS, remaining / 3);\n return { status: isValid ? \"valid\" : \"expired\", expiresAt: cached.expiresAt };\n }\n\n startBackgroundRefresh(\n appId: string,\n clientSecret: string,\n options?: BackgroundRefreshOptions,\n ): void {\n if (this.refreshControllers.has(appId)) {\n this.logger?.info?.(`[qqbot:token:${appId}] Background refresh already running`);\n return;\n }\n\n const {\n refreshAheadMs = 5 * 60 * 1000,\n randomOffsetMs = 30 * 1000,\n minRefreshIntervalMs = 60 * 1000,\n retryDelayMs = 5 * 1000,\n } = options ?? {};\n\n const controller = new AbortController();\n this.refreshControllers.set(appId, controller);\n const { signal } = controller;\n\n const loop = async () => {\n this.logger?.info?.(`[qqbot:token:${appId}] Background refresh started`);\n\n while (!signal.aborted) {\n try {\n await this.getAccessToken(appId, clientSecret);\n const cached = this.cache.get(appId);\n\n if (cached) {\n const expiresIn = cached.expiresAt - Date.now();\n const randomOffset = Math.random() * randomOffsetMs;\n const refreshIn = Math.max(\n expiresIn - refreshAheadMs - randomOffset,\n minRefreshIntervalMs,\n );\n this.logger?.debug?.(\n `[qqbot:token:${appId}] Next refresh in ${Math.round(refreshIn / 1000)}s`,\n );\n await this.abortableSleep(refreshIn, signal);\n } else {\n await this.abortableSleep(minRefreshIntervalMs, signal);\n }\n } catch (err) {\n if (signal.aborted) {\n break;\n }\n this.logger?.error?.(\n `[qqbot:token:${appId}] Background refresh failed: ${formatErrorMessage(err)}`,\n );\n await this.abortableSleep(retryDelayMs, signal);\n }\n }\n\n this.refreshControllers.delete(appId);\n this.logger?.info?.(`[qqbot:token:${appId}] Background refresh stopped`);\n };\n\n loop().catch((err) => {\n if (this.refreshControllers.has(appId)) {\n this.refreshControllers.delete(appId);\n this.logger?.error?.(\n `[qqbot:token:${appId}] Background refresh crashed: ${formatErrorMessage(err)}`,\n );\n }\n });\n }\n\n stopBackgroundRefresh(appId?: string): void {\n if (appId) {\n const ctrl = this.refreshControllers.get(appId);\n if (ctrl) {\n ctrl.abort();\n this.refreshControllers.delete(appId);\n }\n } else {\n for (const ctrl of this.refreshControllers.values()) {\n ctrl.abort();\n }\n this.refreshControllers.clear();\n }\n }\n\n isBackgroundRefreshRunning(appId?: string): boolean {\n if (appId) {\n return this.refreshControllers.has(appId);\n }\n return this.refreshControllers.size > 0;\n }\n\n private async doFetchToken(appId: string, clientSecret: string): Promise<string> {\n const url = `${this.baseUrl}${TOKEN_PATH}`;\n this.logger?.debug?.(`[qqbot:token:${appId}] >>> POST ${url}`);\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), DEFAULT_TOKEN_TIMEOUT_MS);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"User-Agent\": this.resolveUserAgent(),\n },\n body: JSON.stringify({ appId, clientSecret }),\n signal: controller.signal,\n });\n } catch (err) {\n this.logger?.error?.(`[qqbot:token:${appId}] Network error: ${formatErrorMessage(err)}`);\n throw new Error(`Network error getting access_token: ${formatErrorMessage(err)}`, {\n cause: err,\n });\n } finally {\n clearTimeout(timeout);\n }\n\n const traceId = response.headers.get(\"x-tps-trace-id\") ?? \"\";\n this.logger?.debug?.(\n `[qqbot:token:${appId}] <<< ${response.status}${traceId ? ` | TraceId: ${traceId}` : \"\"}`,\n );\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => \"\");\n throw new Error(\n `Token fetch failed: HTTP ${response.status}${errorBody ? ` — ${errorBody.slice(0, 200)}` : \"\"}`,\n );\n }\n\n let data: { access_token?: string; expires_in?: number };\n try {\n const rawBody = await response.text();\n const logBody = rawBody.replace(/\"access_token\"\\s*:\\s*\"[^\"]+\"/g, '\"access_token\": \"***\"');\n this.logger?.debug?.(`[qqbot:token:${appId}] <<< Body: ${logBody}`);\n data = JSON.parse(rawBody);\n } catch (err) {\n throw new Error(`Failed to parse access_token response: ${formatErrorMessage(err)}`, {\n cause: err,\n });\n }\n\n if (!data.access_token) {\n throw new Error(`Failed to get access_token: ${JSON.stringify(data)}`);\n }\n\n const expiresAt = Date.now() + (data.expires_in ?? 7200) * 1000;\n this.cache.set(appId, { token: data.access_token, expiresAt, appId });\n this.logger?.debug?.(\n `[qqbot:token:${appId}] Cached, expires at: ${new Date(expiresAt).toISOString()}`,\n );\n\n return data.access_token;\n }\n\n private abortableSleep(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal.aborted) {\n reject(signal.reason ?? new DOMException(\"The operation was aborted\", \"AbortError\"));\n return;\n }\n const timer = setTimeout(() => {\n signal.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal.reason ?? new DOMException(\"The operation was aborted\", \"AbortError\"));\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n }\n}\n","import createWebSocketStream from './lib/stream.js';\nimport extension from './lib/extension.js';\nimport PerMessageDeflate from './lib/permessage-deflate.js';\nimport Receiver from './lib/receiver.js';\nimport Sender from './lib/sender.js';\nimport subprotocol from './lib/subprotocol.js';\nimport WebSocket from './lib/websocket.js';\nimport WebSocketServer from './lib/websocket-server.js';\n\nexport {\n createWebSocketStream,\n extension,\n PerMessageDeflate,\n Receiver,\n Sender,\n subprotocol,\n WebSocket,\n WebSocketServer\n};\n\nexport default WebSocket;\n","/**\n * Gateway message decoding utilities.\n *\n * Handles the various data formats that the QQ Bot WebSocket can deliver\n * (string, Buffer, Buffer[], ArrayBuffer).\n */\n\nexport function decodeGatewayMessageData(data: unknown): string {\n if (typeof data === \"string\") {\n return data;\n }\n if (Buffer.isBuffer(data)) {\n return data.toString(\"utf8\");\n }\n if (Array.isArray(data) && data.every((chunk) => Buffer.isBuffer(chunk))) {\n return Buffer.concat(data).toString(\"utf8\");\n }\n if (data instanceof ArrayBuffer) {\n return Buffer.from(data).toString(\"utf8\");\n }\n if (ArrayBuffer.isView(data)) {\n return Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString(\"utf8\");\n }\n return \"\";\n}\n\nexport function readOptionalMessageSceneExt(event: Record<string, unknown>): string[] | undefined {\n if (!(\"message_scene\" in event)) {\n return undefined;\n }\n const scene = event.message_scene as { ext?: string[] } | undefined;\n return scene?.ext;\n}\n","/**\n * QQ Bot WebSocket Gateway protocol constants.\n *\n * Pure protocol-layer constants. Zero external dependencies.\n */\n\n/** QQ Bot WebSocket intents grouped by permission level. */\nconst INTENTS = {\n GUILDS: 1 << 0,\n GUILD_MEMBERS: 1 << 1,\n PUBLIC_GUILD_MESSAGES: 1 << 30,\n DIRECT_MESSAGE: 1 << 12,\n GROUP_AND_C2C: 1 << 25,\n /** Button interaction callbacks (INTERACTION_CREATE). */\n INTERACTION: 1 << 26,\n} as const;\n\nexport const FULL_INTENTS =\n INTENTS.GUILDS |\n INTENTS.GUILD_MEMBERS |\n INTENTS.PUBLIC_GUILD_MESSAGES |\n INTENTS.DIRECT_MESSAGE |\n INTENTS.GROUP_AND_C2C |\n INTENTS.INTERACTION;\n\nexport const RECONNECT_DELAYS = [1000, 2000, 5000, 10000, 30000, 60000] as const;\nexport const RATE_LIMIT_DELAY = 60000;\nexport const MAX_RECONNECT_ATTEMPTS = 100;\nexport const MAX_QUICK_DISCONNECT_COUNT = 3;\nexport const QUICK_DISCONNECT_THRESHOLD = 5000;\n\n/** Gateway opcodes used by the QQ Bot WebSocket protocol. */\nexport const GatewayOp = {\n DISPATCH: 0,\n HEARTBEAT: 1,\n IDENTIFY: 2,\n RESUME: 6,\n RECONNECT: 7,\n INVALID_SESSION: 9,\n HELLO: 10,\n HEARTBEAT_ACK: 11,\n} as const;\n\n/** WebSocket close codes used by the QQ Gateway. */\nexport const GatewayCloseCode = {\n NORMAL: 1000,\n AUTH_FAILED: 4004,\n INVALID_SESSION: 4006,\n SEQ_OUT_OF_RANGE: 4007,\n RATE_LIMITED: 4008,\n SESSION_TIMEOUT: 4009,\n SERVER_ERROR_START: 4900,\n SERVER_ERROR_END: 4913,\n INSUFFICIENT_INTENTS: 4914,\n DISALLOWED_INTENTS: 4915,\n} as const;\n\n/** Event type strings dispatched under opcode 0 (DISPATCH). */\nexport const GatewayEvent = {\n READY: \"READY\",\n RESUMED: \"RESUMED\",\n // ── Message events ──\n C2C_MESSAGE_CREATE: \"C2C_MESSAGE_CREATE\",\n AT_MESSAGE_CREATE: \"AT_MESSAGE_CREATE\",\n DIRECT_MESSAGE_CREATE: \"DIRECT_MESSAGE_CREATE\",\n GROUP_AT_MESSAGE_CREATE: \"GROUP_AT_MESSAGE_CREATE\",\n GROUP_MESSAGE_CREATE: \"GROUP_MESSAGE_CREATE\",\n // ── Interaction ──\n INTERACTION_CREATE: \"INTERACTION_CREATE\",\n // ── Guild events (P1) ──\n GUILD_CREATE: \"GUILD_CREATE\",\n GUILD_UPDATE: \"GUILD_UPDATE\",\n GUILD_DELETE: \"GUILD_DELETE\",\n GUILD_MEMBER_ADD: \"GUILD_MEMBER_ADD\",\n GUILD_MEMBER_UPDATE: \"GUILD_MEMBER_UPDATE\",\n GUILD_MEMBER_REMOVE: \"GUILD_MEMBER_REMOVE\",\n CHANNEL_CREATE: \"CHANNEL_CREATE\",\n CHANNEL_UPDATE: \"CHANNEL_UPDATE\",\n CHANNEL_DELETE: \"CHANNEL_DELETE\",\n // ── Group/C2C lifecycle events (P1) ──\n GROUP_ADD_ROBOT: \"GROUP_ADD_ROBOT\",\n GROUP_DEL_ROBOT: \"GROUP_DEL_ROBOT\",\n GROUP_MSG_REJECT: \"GROUP_MSG_REJECT\",\n GROUP_MSG_RECEIVE: \"GROUP_MSG_RECEIVE\",\n FRIEND_ADD: \"FRIEND_ADD\",\n FRIEND_DEL: \"FRIEND_DEL\",\n C2C_MSG_REJECT: \"C2C_MSG_REJECT\",\n C2C_MSG_RECEIVE: \"C2C_MSG_RECEIVE\",\n // ── Reaction events ──\n MESSAGE_REACTION_ADD: \"MESSAGE_REACTION_ADD\",\n MESSAGE_REACTION_REMOVE: \"MESSAGE_REACTION_REMOVE\",\n} as const;\n\n/** Interaction sub-types carried in `InteractionEvent.data.type`. */\nexport const InteractionType = {\n CONFIG_QUERY: 2001,\n CONFIG_UPDATE: 2002,\n} as const;\n","/**\n * Event dispatcher — convert raw WebSocket op=0 events into typed events.\n *\n * Pure mapping logic with zero side effects.\n */\n\nimport type {\n C2CMessageEvent,\n GroupMessageEvent,\n GuildMessageEvent,\n InteractionEvent,\n Logger,\n} from \"../types.js\";\nimport { readOptionalMessageSceneExt } from \"./codec.js\";\nimport { GatewayEvent } from \"./constants.js\";\n\n// ============ Inbound sub-structures ============\n\n/** A media attachment on an inbound message. */\nexport interface InboundAttachment {\n content_type: string;\n url: string;\n filename?: string;\n height?: number;\n width?: number;\n size?: number;\n /** WAV URL for voice messages (QQ server-side conversion). */\n voice_wav_url?: string;\n /** ASR transcription text for voice messages (QQ built-in speech recognition). */\n asr_refer_text?: string;\n}\n\n/** One element from the `msg_elements` array pushed by QQ. */\nexport interface InboundMsgElement {\n /** Sequential message index (used for quote resolution). */\n msg_idx?: string;\n /** Text content of this element. */\n content?: string;\n /** Attachments carried by this element. */\n attachments?: InboundAttachment[];\n}\n\n// ============ Inbound message ============\n\n/** Inbound message envelope normalised across C2C / Group / Guild scopes. */\nexport interface InboundMessage {\n /** Raw event type from the gateway. */\n rawEventType: string;\n /** Conversation kind. */\n kind: \"c2c\" | \"group\" | \"guild\" | \"dm\";\n senderId: string;\n senderName?: string;\n senderIsBot?: boolean;\n content: string;\n messageId: string;\n timestamp: string;\n channelId?: string;\n guildId?: string;\n groupOpenid?: string;\n attachments?: InboundAttachment[];\n refMsgIdx?: string;\n msgIdx?: string;\n msgType?: number;\n /** @mentions list (group events only). */\n mentions?: GroupMessageEvent[\"mentions\"];\n /** Raw message scene info. */\n messageScene?: { source?: string; ext?: string[] };\n /**\n * Raw msg_elements from the QQ push event.\n *\n * When a user quotes/replies to a message, `msg_elements[0]` contains the\n * quoted message's content and attachments. This is the **only** source of\n * truth when the quoted message is not in our RefIndexStore (cache miss).\n */\n msgElements?: InboundMsgElement[];\n /**\n * The original platform event object, transparently forwarded.\n *\n * Use this when you need fields not yet mapped to the normalized interface.\n * The shape depends on `kind`:\n * - `\"c2c\"` → `C2CMessageEvent`\n * - `\"group\"` → `GroupMessageEvent`\n * - `\"guild\"` / `\"dm\"` → `GuildMessageEvent`\n *\n * New platform fields are immediately accessible here without SDK updates.\n */\n raw: C2CMessageEvent | GroupMessageEvent | GuildMessageEvent;\n}\n\nexport type DispatchResult =\n | { action: \"ready\"; data: unknown; sessionId: string }\n | { action: \"resumed\"; data: unknown }\n | { action: \"message\"; msg: InboundMessage }\n | { action: \"interaction\"; event: InteractionEvent }\n | { action: \"raw\"; type: string; data: unknown }\n | { action: \"ignore\" };\n\ninterface Refs {\n refMsgIdx?: string;\n msgIdx?: string;\n}\n\nconst REF_INDEX_KEY = \"msg_idx\";\n\n/**\n * Parse `ref_idx` and `msg_idx` from message_scene.ext + msg_elements.\n */\nfunction parseRefIndices(\n ext: string[] | undefined,\n msgType?: number,\n msgElements?: Array<{ msg_idx?: string }>,\n): Refs {\n let refMsgIdx: string | undefined;\n let msgIdx: string | undefined;\n\n if (Array.isArray(ext)) {\n for (const entry of ext) {\n if (typeof entry !== \"string\") {\n continue;\n }\n const eq = entry.indexOf(\"=\");\n if (eq < 0) {\n continue;\n }\n const key = entry.slice(0, eq).trim();\n const val = entry.slice(eq + 1).trim();\n if (!val) {\n continue;\n }\n if (key === REF_INDEX_KEY) {\n msgIdx = val;\n } else if (key === \"ref_msg_idx\") {\n refMsgIdx = val;\n }\n }\n }\n\n if (msgType === 103 && Array.isArray(msgElements)) {\n for (const el of msgElements) {\n if (el?.msg_idx) {\n refMsgIdx = el.msg_idx;\n break;\n }\n }\n }\n\n return { refMsgIdx, msgIdx };\n}\n\nexport function dispatchEvent(\n eventType: string,\n data: unknown,\n _accountId: string,\n _log?: Logger,\n): DispatchResult {\n if (eventType === GatewayEvent.READY) {\n const d = data as { session_id: string };\n return { action: \"ready\", data, sessionId: d.session_id };\n }\n\n if (eventType === GatewayEvent.RESUMED) {\n return { action: \"resumed\", data };\n }\n\n if (eventType === GatewayEvent.C2C_MESSAGE_CREATE) {\n const ev = data as C2CMessageEvent;\n const refs = parseRefIndices(ev.message_scene?.ext, ev.message_type, ev.msg_elements);\n return {\n action: \"message\",\n msg: {\n rawEventType: eventType,\n kind: \"c2c\",\n senderId: ev.author.user_openid,\n content: ev.content,\n messageId: ev.id,\n timestamp: ev.timestamp,\n attachments: ev.attachments,\n refMsgIdx: refs.refMsgIdx,\n msgIdx: refs.msgIdx,\n msgType: ev.message_type,\n messageScene: ev.message_scene,\n msgElements: ev.msg_elements,\n raw: ev,\n },\n };\n }\n\n if (eventType === GatewayEvent.AT_MESSAGE_CREATE) {\n const ev = data as GuildMessageEvent;\n const refs = parseRefIndices(\n readOptionalMessageSceneExt(ev as unknown as Record<string, unknown>),\n );\n return {\n action: \"message\",\n msg: {\n rawEventType: eventType,\n kind: \"guild\",\n senderId: ev.author.id,\n senderName: ev.author.username,\n content: ev.content,\n messageId: ev.id,\n timestamp: ev.timestamp,\n channelId: ev.channel_id,\n guildId: ev.guild_id,\n attachments: ev.attachments,\n refMsgIdx: refs.refMsgIdx,\n msgIdx: refs.msgIdx,\n raw: ev,\n },\n };\n }\n\n if (eventType === GatewayEvent.DIRECT_MESSAGE_CREATE) {\n const ev = data as GuildMessageEvent;\n const refs = parseRefIndices(\n readOptionalMessageSceneExt(ev as unknown as Record<string, unknown>),\n );\n return {\n action: \"message\",\n msg: {\n rawEventType: eventType,\n kind: \"dm\",\n senderId: ev.author.id,\n senderName: ev.author.username,\n content: ev.content,\n messageId: ev.id,\n timestamp: ev.timestamp,\n guildId: ev.guild_id,\n attachments: ev.attachments,\n refMsgIdx: refs.refMsgIdx,\n msgIdx: refs.msgIdx,\n raw: ev,\n },\n };\n }\n\n if (\n eventType === GatewayEvent.GROUP_AT_MESSAGE_CREATE ||\n eventType === GatewayEvent.GROUP_MESSAGE_CREATE\n ) {\n const ev = data as GroupMessageEvent;\n const refs = parseRefIndices(ev.message_scene?.ext, ev.message_type, ev.msg_elements);\n return {\n action: \"message\",\n msg: {\n rawEventType: eventType,\n kind: \"group\",\n senderId: ev.author.member_openid,\n senderName: ev.author.username,\n senderIsBot: ev.author.bot,\n content: ev.content,\n messageId: ev.id,\n timestamp: ev.timestamp,\n groupOpenid: ev.group_openid,\n attachments: ev.attachments,\n refMsgIdx: refs.refMsgIdx,\n msgIdx: refs.msgIdx,\n msgType: ev.message_type,\n mentions: ev.mentions,\n messageScene: ev.message_scene,\n msgElements: ev.msg_elements,\n raw: ev,\n },\n };\n }\n\n if (eventType === GatewayEvent.INTERACTION_CREATE) {\n return { action: \"interaction\", event: data as InteractionEvent };\n }\n\n // All other gateway events are forwarded as raw events.\n // This covers guild/group/friend lifecycle, reactions, and any future\n // platform events — zero SDK changes required for new event types.\n return { action: \"raw\", type: eventType, data };\n}\n","/**\n * WebSocket reconnection state machine and close-code handler.\n */\n\nimport type { Logger } from \"../types.js\";\nimport {\n GatewayCloseCode,\n MAX_QUICK_DISCONNECT_COUNT,\n MAX_RECONNECT_ATTEMPTS,\n QUICK_DISCONNECT_THRESHOLD,\n RATE_LIMIT_DELAY,\n RECONNECT_DELAYS,\n} from \"./constants.js\";\n\nexport interface CloseAction {\n shouldReconnect: boolean;\n reconnectDelay?: number;\n clearSession: boolean;\n refreshToken: boolean;\n fatal: boolean;\n reason: string;\n}\n\nexport class ReconnectState {\n private attempts = 0;\n private lastConnectTime = 0;\n private quickDisconnectCount = 0;\n\n constructor(\n private readonly accountId: string,\n private readonly log?: Logger,\n ) {}\n\n onConnected(): void {\n this.attempts = 0;\n this.lastConnectTime = Date.now();\n }\n\n isExhausted(): boolean {\n return this.attempts >= MAX_RECONNECT_ATTEMPTS;\n }\n\n getNextDelay(customDelay?: number): number {\n const delay =\n customDelay ?? RECONNECT_DELAYS[Math.min(this.attempts, RECONNECT_DELAYS.length - 1)];\n this.attempts++;\n this.log?.debug?.(`[${this.accountId}] Reconnecting in ${delay}ms (attempt ${this.attempts})`);\n return delay;\n }\n\n handleClose(code: number, isAborted: boolean): CloseAction {\n if (\n code === GatewayCloseCode.INSUFFICIENT_INTENTS ||\n code === GatewayCloseCode.DISALLOWED_INTENTS\n ) {\n const reason =\n code === GatewayCloseCode.INSUFFICIENT_INTENTS ? \"offline/sandbox-only\" : \"banned\";\n this.log?.error(`[${this.accountId}] Bot is ${reason}. Please contact QQ platform.`);\n return {\n shouldReconnect: false,\n clearSession: false,\n refreshToken: false,\n fatal: true,\n reason,\n };\n }\n\n if (code === GatewayCloseCode.AUTH_FAILED) {\n this.log?.info(`[${this.accountId}] Invalid token (4004), will refresh token and reconnect`);\n return {\n shouldReconnect: !isAborted,\n clearSession: false,\n refreshToken: true,\n fatal: false,\n reason: \"invalid token (4004)\",\n };\n }\n\n if (code === GatewayCloseCode.RATE_LIMITED) {\n this.log?.info(`[${this.accountId}] Rate limited (4008), waiting ${RATE_LIMIT_DELAY}ms`);\n return {\n shouldReconnect: !isAborted,\n reconnectDelay: RATE_LIMIT_DELAY,\n clearSession: false,\n refreshToken: false,\n fatal: false,\n reason: \"rate limited (4008)\",\n };\n }\n\n if (\n code === GatewayCloseCode.INVALID_SESSION ||\n code === GatewayCloseCode.SEQ_OUT_OF_RANGE ||\n code === GatewayCloseCode.SESSION_TIMEOUT\n ) {\n const codeDesc: Record<number, string> = {\n [GatewayCloseCode.INVALID_SESSION]: \"session no longer valid\",\n [GatewayCloseCode.SEQ_OUT_OF_RANGE]: \"invalid seq on resume\",\n [GatewayCloseCode.SESSION_TIMEOUT]: \"session timed out\",\n };\n this.log?.info(`[${this.accountId}] Error ${code} (${codeDesc[code]}), will re-identify`);\n return {\n shouldReconnect: !isAborted,\n clearSession: true,\n refreshToken: true,\n fatal: false,\n reason: codeDesc[code],\n };\n }\n\n if (code >= GatewayCloseCode.SERVER_ERROR_START && code <= GatewayCloseCode.SERVER_ERROR_END) {\n this.log?.info(`[${this.accountId}] Internal error (${code}), will re-identify`);\n return {\n shouldReconnect: !isAborted && code !== GatewayCloseCode.NORMAL,\n clearSession: true,\n refreshToken: true,\n fatal: false,\n reason: `internal error (${code})`,\n };\n }\n\n const connectionDuration = Date.now() - this.lastConnectTime;\n if (connectionDuration < QUICK_DISCONNECT_THRESHOLD && this.lastConnectTime > 0) {\n this.quickDisconnectCount++;\n this.log?.debug?.(\n `[${this.accountId}] Quick disconnect detected (${connectionDuration}ms), count: ${this.quickDisconnectCount}`,\n );\n\n if (this.quickDisconnectCount >= MAX_QUICK_DISCONNECT_COUNT) {\n this.log?.error(\n `[${this.accountId}] Too many quick disconnects. This may indicate a permission issue.`,\n );\n this.quickDisconnectCount = 0;\n return {\n shouldReconnect: !isAborted && code !== 1000,\n reconnectDelay: RATE_LIMIT_DELAY,\n clearSession: false,\n refreshToken: false,\n fatal: false,\n reason: \"too many quick disconnects\",\n };\n }\n } else {\n this.quickDisconnectCount = 0;\n }\n\n return {\n shouldReconnect: !isAborted && code !== GatewayCloseCode.NORMAL,\n clearSession: false,\n refreshToken: false,\n fatal: false,\n reason: `close code ${code}`,\n };\n }\n}\n","/**\n * GatewayConnection — pure protocol-layer WebSocket lifecycle.\n *\n * Owns: WebSocket connection, heartbeat, reconnect, IDENTIFY/RESUME, event\n * dispatch.\n *\n * Does NOT own: token caching (delegated via callbacks), session persistence\n * (delegated via callbacks), business policies, message queueing.\n *\n * All state injected through the constructor — no module-level globals.\n */\n\nimport WebSocket from \"ws\";\nimport type { GatewayAccount, InteractionEvent, Logger, WSPayload } from \"../types.js\";\nimport { decodeGatewayMessageData } from \"./codec.js\";\nimport { FULL_INTENTS, GatewayOp, RATE_LIMIT_DELAY } from \"./constants.js\";\nimport { dispatchEvent, type InboundMessage } from \"./event-dispatcher.js\";\nimport { ReconnectState } from \"./reconnect.js\";\n\n/** Persisted session info for RESUME after reconnection. */\nexport interface PersistedSession {\n sessionId: string;\n lastSeq: number | null;\n}\n\n/** Hooks for session persistence (optional). */\nexport interface SessionPersistencePort {\n load: () => PersistedSession | null;\n save: (session: PersistedSession) => void;\n clear: () => void;\n}\n\nexport interface GatewayConnectionOptions {\n account: GatewayAccount;\n abortSignal: AbortSignal;\n log?: Logger;\n\n /** User-Agent header sent on the WebSocket upgrade request. */\n userAgent?: string | (() => string);\n\n /** Resolve a fresh access_token for IDENTIFY / RESUME. */\n getAccessToken: () => Promise<string>;\n /** Force-clear any cached token (called when 4004 invalid token). */\n clearTokenCache?: () => void;\n /** Request the WebSocket gateway URL from the QQ Open Platform. */\n getGatewayUrl: (accessToken: string) => Promise<string>;\n\n /** Optional: persist session id + last seq across process restarts. */\n session?: SessionPersistencePort;\n\n /** Custom intent mask. Defaults to FULL_INTENTS. */\n intents?: number;\n\n // ---- Event handlers ----\n onReady?: (data: unknown) => void;\n onResumed?: (data: unknown) => void;\n onError?: (error: Error) => void;\n onMessage: (msg: InboundMessage) => void | Promise<void>;\n onInteraction?: (event: InteractionEvent) => void | Promise<void>;\n onRawEvent?: (type: string, data: unknown) => void | Promise<void>;\n}\n\n/** Pure-protocol gateway connection. */\nexport class GatewayConnection {\n private isAborted = false;\n private currentWs: WebSocket | null = null;\n private heartbeatInterval: ReturnType<typeof setInterval> | null = null;\n private sessionId: string | null = null;\n private lastSeq: number | null = null;\n private isConnecting = false;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private shouldRefreshToken = false;\n\n private readonly reconnect: ReconnectState;\n private readonly opts: GatewayConnectionOptions;\n private readonly resolveUserAgent: () => string;\n\n constructor(opts: GatewayConnectionOptions) {\n this.opts = opts;\n this.reconnect = new ReconnectState(opts.account.accountId, opts.log);\n const ua = opts.userAgent ?? \"qqbot-nodejs/unknown\";\n this.resolveUserAgent = typeof ua === \"function\" ? ua : () => ua;\n }\n\n /** Start the connection loop. Resolves when abortSignal fires. */\n async start(): Promise<void> {\n this.restoreSession();\n this.registerAbortHandler();\n await this.connect();\n return new Promise<void>((resolve) => {\n this.opts.abortSignal.addEventListener(\"abort\", () => resolve());\n });\n }\n\n // ============ Session persistence ============\n\n private restoreSession(): void {\n const saved = this.opts.session?.load();\n if (saved) {\n this.sessionId = saved.sessionId;\n this.lastSeq = saved.lastSeq;\n this.opts.log?.info?.(\n `[${this.opts.account.accountId}] Restored session: sessionId=${saved.sessionId}, lastSeq=${saved.lastSeq}`,\n );\n }\n }\n\n private saveCurrentSession(): void {\n if (!this.sessionId || !this.opts.session) {\n return;\n }\n this.opts.session.save({\n sessionId: this.sessionId,\n lastSeq: this.lastSeq,\n });\n }\n\n // ============ Abort + cleanup ============\n\n private registerAbortHandler(): void {\n this.opts.abortSignal.addEventListener(\"abort\", () => {\n this.isAborted = true;\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n this.cleanup();\n });\n }\n\n private cleanup(): void {\n if (this.heartbeatInterval) {\n clearInterval(this.heartbeatInterval);\n this.heartbeatInterval = null;\n }\n if (\n this.currentWs &&\n (this.currentWs.readyState === WebSocket.OPEN ||\n this.currentWs.readyState === WebSocket.CONNECTING)\n ) {\n this.currentWs.close();\n }\n this.currentWs = null;\n }\n\n // ============ Reconnect ============\n\n private scheduleReconnect(customDelay?: number): void {\n if (this.isAborted || this.reconnect.isExhausted()) {\n this.opts.log?.error(\n `[${this.opts.account.accountId}] Max reconnect attempts reached or aborted`,\n );\n return;\n }\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n const delay = this.reconnect.getNextDelay(customDelay);\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n if (!this.isAborted) {\n void this.connect();\n }\n }, delay);\n }\n\n // ============ Connect ============\n\n private async connect(): Promise<void> {\n const { log, account } = this.opts;\n\n if (this.isConnecting) {\n log?.debug?.(`[${account.accountId}] Already connecting, skip`);\n return;\n }\n this.isConnecting = true;\n\n try {\n this.cleanup();\n if (this.shouldRefreshToken) {\n log?.debug?.(`[${account.accountId}] Refreshing token...`);\n this.opts.clearTokenCache?.();\n this.shouldRefreshToken = false;\n }\n\n const accessToken = await this.opts.getAccessToken();\n log?.info(`[${account.accountId}] ✅ Access token obtained`);\n const gatewayUrl = await this.opts.getGatewayUrl(accessToken);\n log?.info(`[${account.accountId}] Connecting to ${gatewayUrl}`);\n\n const ws = new WebSocket(gatewayUrl, {\n headers: { \"User-Agent\": this.resolveUserAgent() },\n });\n this.currentWs = ws;\n\n ws.on(\"open\", () => {\n log?.info(`[${account.accountId}] WebSocket connected`);\n this.isConnecting = false;\n this.reconnect.onConnected();\n });\n\n ws.on(\"message\", async (data) => {\n try {\n const rawData = decodeGatewayMessageData(data);\n const payload = JSON.parse(rawData) as WSPayload;\n const { op, d, s, t } = payload;\n\n if (s) {\n this.lastSeq = s;\n this.saveCurrentSession();\n }\n\n switch (op) {\n case GatewayOp.HELLO:\n this.handleHello(ws, d, accessToken);\n break;\n\n case GatewayOp.DISPATCH: {\n log?.debug?.(\n `[${account.accountId}] Dispatch event: t=${t} payload=${previewPayload(d)}`,\n );\n const result = dispatchEvent(t ?? \"\", d, account.accountId, log);\n if (result.action === \"ready\") {\n this.sessionId = result.sessionId;\n this.saveCurrentSession();\n this.opts.onReady?.(result.data);\n } else if (result.action === \"resumed\") {\n (this.opts.onResumed ?? this.opts.onReady)?.(result.data);\n this.saveCurrentSession();\n } else if (result.action === \"interaction\") {\n // Interaction 有 handler → 调用;否则 fallback 到 rawEvent\n if (this.opts.onInteraction) {\n void Promise.resolve(this.opts.onInteraction(result.event));\n } else if (this.opts.onRawEvent) {\n void Promise.resolve(this.opts.onRawEvent(payload.t!, payload.d));\n }\n } else if (result.action === \"message\") {\n void Promise.resolve(this.opts.onMessage(result.msg));\n } else if (result.action === \"raw\") {\n if (this.opts.onRawEvent) {\n void Promise.resolve(this.opts.onRawEvent(result.type, result.data));\n }\n }\n break;\n }\n\n case GatewayOp.HEARTBEAT_ACK:\n break;\n\n case GatewayOp.RECONNECT:\n this.cleanup();\n this.scheduleReconnect();\n break;\n\n case GatewayOp.INVALID_SESSION: {\n const canResume = d as boolean;\n if (!canResume) {\n this.sessionId = null;\n this.lastSeq = null;\n this.opts.session?.clear();\n this.shouldRefreshToken = true;\n }\n this.cleanup();\n this.scheduleReconnect(3000);\n break;\n }\n }\n } catch (err) {\n log?.error(\n `[${account.accountId}] Message parse error: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n });\n\n ws.on(\"close\", (code, reason) => {\n log?.info(`[${account.accountId}] WebSocket closed: ${code} ${reason.toString()}`);\n this.isConnecting = false;\n this.handleClose(code);\n });\n\n ws.on(\"error\", (err) => {\n log?.error(`[${account.accountId}] WebSocket error: ${err.message}`);\n this.opts.onError?.(err);\n });\n } catch (err) {\n this.isConnecting = false;\n const errMsg = err instanceof Error ? err.message : String(err);\n log?.error(`[${account.accountId}] Connection failed: ${errMsg}`);\n if (errMsg.includes(\"Too many requests\") || errMsg.includes(\"100001\")) {\n this.scheduleReconnect(RATE_LIMIT_DELAY);\n } else {\n this.scheduleReconnect();\n }\n }\n }\n\n // ============ Protocol handlers ============\n\n private handleHello(ws: WebSocket, d: unknown, accessToken: string): void {\n const intents = this.opts.intents ?? FULL_INTENTS;\n if (this.sessionId && this.lastSeq !== null) {\n ws.send(\n JSON.stringify({\n op: GatewayOp.RESUME,\n d: {\n token: `QQBot ${accessToken}`,\n session_id: this.sessionId,\n seq: this.lastSeq,\n },\n }),\n );\n } else {\n ws.send(\n JSON.stringify({\n op: GatewayOp.IDENTIFY,\n d: {\n token: `QQBot ${accessToken}`,\n intents,\n shard: [0, 1],\n },\n }),\n );\n }\n\n const interval = (d as { heartbeat_interval: number }).heartbeat_interval;\n if (this.heartbeatInterval) {\n clearInterval(this.heartbeatInterval);\n }\n this.heartbeatInterval = setInterval(() => {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify({ op: GatewayOp.HEARTBEAT, d: this.lastSeq }));\n }\n }, interval);\n }\n\n private handleClose(code: number): void {\n const action = this.reconnect.handleClose(code, this.isAborted);\n\n if (action.clearSession) {\n this.sessionId = null;\n this.lastSeq = null;\n this.opts.session?.clear();\n }\n if (action.refreshToken) {\n this.shouldRefreshToken = true;\n }\n\n this.cleanup();\n\n if (action.fatal) {\n return;\n }\n if (action.shouldReconnect) {\n this.scheduleReconnect(action.reconnectDelay);\n }\n }\n}\n\n/**\n * Serialize a gateway event payload for debug logging.\n *\n * JSON-stringifies the payload; returns `\"(non-serializable)\"` for cyclic\n * or otherwise unserializable values so logging never throws.\n */\nfunction previewPayload(data: unknown): string {\n if (data === undefined) return \"undefined\";\n if (data === null) return \"null\";\n try {\n const s = JSON.stringify(data);\n return s === undefined ? \"(non-serializable)\" : s;\n } catch {\n return \"(non-serializable)\";\n }\n}\n","/**\n * Webhook signature verification — Ed25519.\n *\n * QQ Open Platform uses Ed25519 for webhook callback verification:\n * 1. Bot secret is padded/truncated to 32 bytes as the Ed25519 seed.\n * 2. The private key signs `timestamp + body` for request verification.\n * 3. For callback URL validation (op:13), signs `event_ts + plain_token`.\n *\n * Reference: https://bot.q.qq.com/wiki/develop/api-v2/dev-prepare/interface-framework/event-emit.html\n */\n\nimport * as crypto from \"node:crypto\";\n\n// ============ Seed derivation ============\n\n/**\n * Derive an Ed25519 seed (32 bytes) from the bot secret.\n * QQ's spec: repeat the secret until >= 32 chars, then truncate to 32.\n */\nfunction deriveSeed(botSecret: string): Buffer {\n let seed = botSecret;\n while (seed.length < 32) {\n seed = seed + seed;\n }\n return Buffer.from(seed.slice(0, 32), \"utf-8\");\n}\n\n/**\n * Generate Ed25519 key pair from bot secret.\n */\nfunction getKeyPair(botSecret: string): { privateKey: crypto.KeyObject; publicKey: crypto.KeyObject } {\n const seed = deriveSeed(botSecret);\n // Node.js Ed25519: create private key from raw 32-byte seed\n const privateKey = crypto.createPrivateKey({\n key: Buffer.concat([\n // Ed25519 PKCS8 DER prefix for 32-byte seed\n Buffer.from(\"302e020100300506032b657004220420\", \"hex\"),\n seed,\n ]),\n format: \"der\",\n type: \"pkcs8\",\n });\n const publicKey = crypto.createPublicKey(privateKey);\n return { privateKey, publicKey };\n}\n\n// ============ Signature generation ============\n\n/**\n * Sign a message using the bot's Ed25519 private key.\n * Returns hex-encoded signature.\n */\nexport function ed25519Sign(botSecret: string, message: Buffer): string {\n const { privateKey } = getKeyPair(botSecret);\n const signature = crypto.sign(null, message, privateKey);\n return signature.toString(\"hex\");\n}\n\n// ============ Signature verification ============\n\n/**\n * Verify an Ed25519 signature from a QQ webhook callback request.\n *\n * @param params.body - Raw request body (Buffer)\n * @param params.timestamp - Value of `X-Signature-Timestamp` header\n * @param params.signature - Value of `X-Signature-Ed25519` header (hex string)\n * @param params.botSecret - The bot's AppSecret\n * @returns `true` if signature is valid\n */\nexport function verifyWebhookSignature(params: {\n body: Buffer;\n timestamp: string;\n signature: string;\n botSecret: string;\n}): boolean {\n const { body, timestamp, signature, botSecret } = params;\n\n try {\n const { publicKey } = getKeyPair(botSecret);\n const message = Buffer.concat([\n Buffer.from(timestamp, \"utf-8\"),\n body,\n ]);\n const sigBuffer = Buffer.from(signature, \"hex\");\n return crypto.verify(null, message, publicKey, sigBuffer);\n } catch {\n return false;\n }\n}\n\n// ============ Callback URL validation (op:13) ============\n\n/**\n * Generate the response for callback URL validation (op:13).\n *\n * QQ sends `{ op: 13, d: { plain_token, event_ts } }` to verify\n * the callback URL. We must return `{ plain_token, signature }`.\n *\n * @param params.plainToken - The `plain_token` from the validation request\n * @param params.eventTs - The `event_ts` from the validation request\n * @param params.botSecret - The bot's AppSecret\n */\nexport function signValidationResponse(params: {\n plainToken: string;\n eventTs: string;\n botSecret: string;\n}): { plain_token: string; signature: string } {\n const { plainToken, eventTs, botSecret } = params;\n\n // Sign: event_ts + plain_token\n const message = Buffer.from(eventTs + plainToken, \"utf-8\");\n const signature = ed25519Sign(botSecret, message);\n\n return {\n plain_token: plainToken,\n signature,\n };\n}\n","/**\n * Built-in node:http webhook server adapter.\n *\n * A minimal HTTP server that receives QQ webhook callbacks. If you prefer\n * Express/Fastify/Koa, implement `WebhookServerAdapter` and pass it as\n * `webhook.server` in QQBotOptions.\n */\n\nimport * as http from \"node:http\";\nimport type { WebhookRequestHandler, WebhookServerAdapter } from \"./types.js\";\n\nexport class NodeHttpWebhookServer implements WebhookServerAdapter {\n private server: http.Server | null = null;\n\n async listen(port: number, path: string, handler: WebhookRequestHandler): Promise<void> {\n return new Promise((resolve, reject) => {\n const server = http.createServer(async (req, res) => {\n // Only accept POST to the configured path\n if (req.method !== \"POST\" || req.url !== path) {\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(\"Not Found\");\n return;\n }\n\n // Collect body\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", async () => {\n try {\n const body = Buffer.concat(chunks);\n const headers: Record<string, string | string[] | undefined> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n headers[key.toLowerCase()] = value;\n }\n\n const response = await handler({ body, headers });\n\n res.writeHead(response.status, {\n \"Content-Type\": \"application/json\",\n ...(response.headers ?? {}),\n });\n res.end(response.body);\n } catch (_err) {\n res.writeHead(500, { \"Content-Type\": \"text/plain\" });\n res.end(\"Internal Server Error\");\n }\n });\n });\n\n server.on(\"error\", reject);\n server.listen(port, () => {\n this.server = server;\n resolve();\n });\n });\n }\n\n close(): void {\n if (this.server) {\n this.server.close();\n this.server = null;\n }\n }\n}\n","/**\n * WebhookTransport — receive QQ Bot events via HTTP POST callbacks.\n *\n * This transport implements the QQ Open Platform Webhook mode:\n * 1. Starts an HTTP server (built-in or user-provided adapter)\n * 2. Verifies Ed25519 signatures on every request\n * 3. Handles callback URL validation (op:13)\n * 4. Dispatches op:0 events through the same `dispatchEvent` as WebSocket\n * 5. Returns op:12 ACK to QQ platform\n *\n * Usage:\n * ```ts\n * const bot = new QQBot({\n * appId, appSecret,\n * transport: \"webhook\",\n * webhook: { port: 8443, path: \"/callback\" },\n * });\n * await bot.start();\n * ```\n */\n\nimport { dispatchEvent } from \"../gateway/event-dispatcher.js\";\nimport type { Logger } from \"../types.js\";\nimport type {\n EventTransport,\n EventTransportCallbacks,\n WebhookRequest,\n WebhookResponse,\n WebhookServerAdapter,\n WebhookTransportOptions,\n} from \"./types.js\";\nimport { signValidationResponse, verifyWebhookSignature } from \"./webhook-verify.js\";\nimport { NodeHttpWebhookServer } from \"./webhook-server-node.js\";\n\n// OpCodes relevant to webhook\nconst OP_DISPATCH = 0;\nconst OP_HTTP_CALLBACK_ACK = 12;\nconst OP_VALIDATION = 13;\n\ninterface WebhookPayload {\n id?: string;\n op: number;\n d?: unknown;\n s?: number;\n t?: string;\n}\n\nexport class WebhookTransport implements EventTransport {\n private readonly opts: WebhookTransportOptions;\n private readonly callbacks: EventTransportCallbacks;\n private readonly log: Logger | undefined;\n private readonly server: WebhookServerAdapter;\n private readonly accountId: string;\n private stopped = false;\n private stopResolve: (() => void) | null = null;\n\n constructor(opts: WebhookTransportOptions, callbacks: EventTransportCallbacks) {\n this.opts = opts;\n this.callbacks = callbacks;\n this.log = opts.log;\n this.server = opts.server ?? new NodeHttpWebhookServer();\n this.accountId = opts.accountId ?? opts.appId;\n }\n\n async start(): Promise<void> {\n const port = this.opts.port ?? 8080;\n const path = this.opts.path ?? \"/\";\n\n this.log?.info?.(`[webhook] starting on port ${port}, path ${path}`);\n\n await this.server.listen(port, path, (req) => this.handleRequest(req));\n\n this.log?.info?.(`[webhook] listening on :${port}${path}`);\n\n // Notify ready\n this.callbacks.onReady?.({ transport: \"webhook\", port, path });\n\n // Wait until stopped\n if (this.opts.abortSignal) {\n await new Promise<void>((resolve) => {\n if (this.opts.abortSignal!.aborted) {\n resolve();\n return;\n }\n this.stopResolve = resolve;\n this.opts.abortSignal!.addEventListener(\"abort\", () => this.stop(), { once: true });\n });\n } else {\n // Wait until stop() is called externally\n await new Promise<void>((resolve) => {\n this.stopResolve = resolve;\n });\n }\n }\n\n stop(): void {\n if (this.stopped) return;\n this.stopped = true;\n this.server.close();\n this.log?.info?.(`[webhook] stopped`);\n this.stopResolve?.();\n }\n\n // ============ Request handler ============\n\n private async handleRequest(req: WebhookRequest): Promise<WebhookResponse> {\n // Parse body\n let payload: WebhookPayload;\n try {\n payload = JSON.parse(req.body.toString(\"utf-8\")) as WebhookPayload;\n } catch {\n this.log?.warn?.(`[webhook] invalid JSON body`);\n return { status: 400, body: JSON.stringify({ error: \"invalid json\" }) };\n }\n\n // ── op:13 — Callback URL validation (no signature check needed) ──\n if (payload.op === OP_VALIDATION) {\n return this.handleValidation(payload);\n }\n\n // ── Signature verification ──\n const timestamp = getHeader(req.headers, \"x-signature-timestamp\") ?? \"\";\n const signature = getHeader(req.headers, \"x-signature-ed25519\") ?? \"\";\n\n if (!timestamp || !signature) {\n this.log?.warn?.(`[webhook] missing signature headers`);\n return { status: 401, body: JSON.stringify({ error: \"missing signature\" }) };\n }\n\n const valid = verifyWebhookSignature({\n body: req.body,\n timestamp,\n signature,\n botSecret: this.opts.appSecret,\n });\n\n if (!valid) {\n this.log?.warn?.(`[webhook] signature verification failed`);\n return { status: 401, body: JSON.stringify({ error: \"invalid signature\" }) };\n }\n\n // ── op:0 — Dispatch event (async, fire-and-forget) ──\n // Return ACK immediately, process message in background.\n // Otherwise long-running handlers (AI inference) block the HTTP response,\n // causing the QQ platform to timeout and re-deliver the event.\n if (payload.op === OP_DISPATCH) {\n this.handleDispatch(payload).catch((err) => {\n this.log?.error?.(\n `[webhook] dispatch error: ${err instanceof Error ? err.message : String(err)}`,\n );\n });\n }\n\n // Return ACK (op:12) for all valid requests\n return {\n status: 200,\n body: JSON.stringify({ op: OP_HTTP_CALLBACK_ACK, d: 0 }),\n };\n }\n\n // ============ Validation handler (op:13) ============\n\n private handleValidation(payload: WebhookPayload): WebhookResponse {\n const d = payload.d as { plain_token?: string; event_ts?: string } | undefined;\n\n if (!d?.plain_token || !d?.event_ts) {\n this.log?.warn?.(`[webhook] validation missing plain_token or event_ts`);\n return { status: 400, body: JSON.stringify({ error: \"invalid validation\" }) };\n }\n\n this.log?.info?.(`[webhook] handling callback URL validation`);\n\n const response = signValidationResponse({\n plainToken: d.plain_token,\n eventTs: d.event_ts,\n botSecret: this.opts.appSecret,\n });\n\n return {\n status: 200,\n body: JSON.stringify(response),\n };\n }\n\n // ============ Dispatch handler (op:0) ============\n\n private async handleDispatch(payload: WebhookPayload): Promise<void> {\n const eventType = payload.t ?? \"\";\n const data = payload.d;\n\n this.log?.debug?.(\n `[webhook] dispatch event: t=${eventType} payload=${JSON.stringify(data)}`,\n );\n\n const result = dispatchEvent(eventType, data, this.accountId, this.log);\n\n switch (result.action) {\n case \"ready\":\n this.callbacks.onReady?.(result.data);\n break;\n case \"resumed\":\n this.callbacks.onResumed?.(result.data);\n break;\n case \"message\":\n try {\n await this.callbacks.onMessage(result.msg);\n } catch (err) {\n this.callbacks.onError?.(\n err instanceof Error ? err : new Error(String(err)),\n );\n }\n break;\n case \"interaction\":\n try {\n await this.callbacks.onInteraction?.(result.event);\n } catch (err) {\n this.callbacks.onError?.(\n err instanceof Error ? err : new Error(String(err)),\n );\n }\n break;\n case \"ignore\":\n break;\n }\n }\n}\n\n// ============ Helpers ============\n\nfunction getHeader(\n headers: Record<string, string | string[] | undefined>,\n key: string,\n): string | undefined {\n const val = headers[key];\n if (Array.isArray(val)) return val[0];\n return val;\n}\n","/**\n * Cache `file_info` values returned by the QQ Bot API so identical uploads\n * can be reused before the server-side TTL expires.\n */\n\nimport * as crypto from \"node:crypto\";\nimport type { ChatScope, Logger } from \"../types.js\";\n\ninterface CacheEntry {\n fileInfo: string;\n fileUuid: string;\n expiresAt: number;\n}\n\nconst MAX_CACHE_SIZE = 500;\n\n/** Compute an MD5 hash used as part of the cache key. */\nexport function computeFileHash(data: string | Buffer): string {\n return crypto.createHash(\"md5\").update(data).digest(\"hex\");\n}\n\nfunction buildCacheKey(\n contentHash: string,\n scope: string,\n targetId: string,\n fileType: number,\n): string {\n return `${contentHash}:${scope}:${targetId}:${fileType}`;\n}\n\n/**\n * In-memory upload cache. Each {@link QQBot} instance owns its own\n * cache so multiple concurrent bots do not compete for the same map.\n */\nexport class UploadCache {\n private readonly cache = new Map<string, CacheEntry>();\n private readonly logger?: Logger;\n\n constructor(options?: { logger?: Logger }) {\n this.logger = options?.logger;\n }\n\n computeHash(data: string | Buffer): string {\n return computeFileHash(data);\n }\n\n get(contentHash: string, scope: ChatScope, targetId: string, fileType: number): string | null {\n const key = buildCacheKey(contentHash, scope, targetId, fileType);\n const entry = this.cache.get(key);\n if (!entry) {\n return null;\n }\n if (Date.now() >= entry.expiresAt) {\n this.cache.delete(key);\n return null;\n }\n this.logger?.debug?.(\n `[upload-cache] HIT key=${key.slice(0, 40)}... uuid=${entry.fileUuid}`,\n );\n return entry.fileInfo;\n }\n\n set(\n contentHash: string,\n scope: ChatScope,\n targetId: string,\n fileType: number,\n fileInfo: string,\n fileUuid: string,\n ttl: number,\n ): void {\n if (this.cache.size >= MAX_CACHE_SIZE) {\n const now = Date.now();\n for (const [k, v] of this.cache) {\n if (now >= v.expiresAt) {\n this.cache.delete(k);\n }\n }\n if (this.cache.size >= MAX_CACHE_SIZE) {\n const keys = Array.from(this.cache.keys());\n for (let i = 0; i < keys.length / 2; i++) {\n this.cache.delete(keys[i]);\n }\n }\n }\n const key = buildCacheKey(contentHash, scope, targetId, fileType);\n const safetyMargin = 60;\n const effectiveTtl = Math.max(ttl - safetyMargin, 10);\n this.cache.set(key, {\n fileInfo,\n fileUuid,\n expiresAt: Date.now() + effectiveTtl * 1000,\n });\n this.logger?.debug?.(\n `[upload-cache] SET key=${key.slice(0, 40)}... ttl=${effectiveTtl}s uuid=${fileUuid}`,\n );\n }\n\n stats(): { size: number; maxSize: number } {\n return { size: this.cache.size, maxSize: MAX_CACHE_SIZE };\n }\n\n clear(): void {\n this.cache.clear();\n this.logger?.debug?.(`[upload-cache] cleared`);\n }\n}\n","/**\n * High-level helpers for QQ Bot C2C stream messages.\n *\n * QQ 开放平台只对 C2C(私聊)开放 stream_messages API。本模块提供一个\n * 简单的 sequencer,把上层应用的「逐字/逐 token 输出」适配为 QQ 协议\n * 要求的 `index + msg_seq` 序列。\n */\n\nimport { MessageApi } from \"./protocol/api/messages.js\";\nimport { getNextMsgSeq } from \"./protocol/api/routes.js\";\nimport {\n StreamContentType,\n StreamInputMode,\n StreamInputState,\n type Credentials,\n type Logger,\n type MessageResponse,\n type StreamMessageRequest,\n} from \"./protocol/types.js\";\nimport { formatErrorMessage } from \"./protocol/utils/format.js\";\n\n/** Throttle constants — match QQ open platform best practice. */\nconst DEFAULT_THROTTLE_MS = 500;\nconst MIN_THROTTLE_MS = 300;\n\nexport interface StreamSessionOptions {\n /** Target user openid. */\n openid: string;\n /** Inbound message id used to anchor the reply. */\n msgId: string;\n /** Credentials. */\n creds: Credentials;\n /**\n * Optional event id passed to QQ open platform `stream_messages`. Defaults\n * to the inbound `msgId` — matches QQ's expectation that this field carries\n * the originating message id, not a freshly-generated UUID.\n */\n eventId?: string;\n /** Throttle interval between flushes (ms). Default 500ms, min 300ms. */\n throttleMs?: number;\n /** Logger. */\n logger?: Logger;\n}\n\n/**\n * Lightweight controller for QQ C2C stream messages.\n *\n * Usage:\n * ```ts\n * const stream = bot.openStream({ openid, msgId });\n * for (const chunk of chunks) {\n * await stream.update(currentText); // currentText is the full text so far\n * }\n * await stream.complete(); // marks input_state = DONE\n * ```\n *\n * Important: `update()` expects the *full* current message text, not the\n * delta. The QQ API uses `input_mode=replace` semantics — each frame\n * replaces the previous one in place.\n */\n/** Maximum number of retries on rate-limit (429 / 50002). */\nconst MAX_FLUSH_RETRIES = 3;\n/** Base delay for exponential backoff on rate-limit (ms). */\nconst RATE_LIMIT_BASE_DELAY_MS = 1000;\n\nexport class StreamSession {\n private readonly api: MessageApi;\n private readonly opts: StreamSessionOptions;\n private readonly throttleMs: number;\n private readonly eventId: string;\n\n private streamMsgId: string | undefined;\n private index = 0;\n /**\n * `msg_seq` for the current stream session. QQ open platform expects all\n * frames in one stream to share the same `msg_seq` (only `index` advances).\n */\n private msgSeq: number | null = null;\n private lastFlushAt = 0;\n private lastSentText = \"\";\n private pendingText = \"\";\n private pendingTimer: ReturnType<typeof setTimeout> | null = null;\n private flushInProgress = false;\n private flushPromise: Promise<MessageResponse | undefined> | null = null;\n private isCompleted = false;\n\n constructor(api: MessageApi, opts: StreamSessionOptions) {\n this.api = api;\n this.opts = opts;\n this.throttleMs = Math.max(opts.throttleMs ?? DEFAULT_THROTTLE_MS, MIN_THROTTLE_MS);\n this.eventId = opts.eventId ?? opts.msgId;\n }\n\n /**\n * Update the current full message text. Will be sent at most once per\n * throttle window.\n */\n async update(fullText: string): Promise<void> {\n if (this.isCompleted) {\n return;\n }\n this.pendingText = fullText;\n const now = Date.now();\n const elapsed = now - this.lastFlushAt;\n\n if (this.flushInProgress) {\n // A flush is in flight — leave pendingText for the trailing flush.\n return;\n }\n\n if (elapsed >= this.throttleMs) {\n await this.flush(StreamInputState.GENERATING);\n return;\n }\n\n if (!this.pendingTimer) {\n const wait = this.throttleMs - elapsed;\n this.pendingTimer = setTimeout(() => {\n this.pendingTimer = null;\n if (!this.isCompleted) {\n this.flush(StreamInputState.GENERATING).catch((err) => {\n this.opts.logger?.error?.(\n `[qqbot:stream] throttle flush error: ${formatErrorMessage(err)}`,\n );\n });\n }\n }, wait);\n }\n }\n\n /** Mark the stream as DONE. Sends a final frame with the latest text. */\n async complete(): Promise<MessageResponse | undefined> {\n if (this.isCompleted) {\n return undefined;\n }\n this.isCompleted = true;\n if (this.pendingTimer) {\n clearTimeout(this.pendingTimer);\n this.pendingTimer = null;\n }\n // 等待当前正在进行的 flush 完成,避免 DONE 帧被跳过\n if (this.flushPromise) {\n await this.flushPromise.catch(() => {});\n }\n return this.flush(StreamInputState.DONE);\n }\n\n /** Force-cancel without sending a DONE frame (caller must clean up). */\n cancel(): void {\n this.isCompleted = true;\n if (this.pendingTimer) {\n clearTimeout(this.pendingTimer);\n this.pendingTimer = null;\n }\n }\n\n // ============ Internal ============\n\n private async flush(state: StreamInputState): Promise<MessageResponse | undefined> {\n if (this.flushInProgress) {\n return undefined;\n }\n if (this.pendingText === this.lastSentText && state !== StreamInputState.DONE) {\n return undefined;\n }\n this.flushInProgress = true;\n const promise = this.doFlush(state);\n this.flushPromise = promise;\n return promise;\n }\n\n private async doFlush(state: StreamInputState): Promise<MessageResponse | undefined> {\n let flushFailed = false;\n try {\n const text = this.pendingText;\n if (this.msgSeq === null) {\n this.msgSeq = getNextMsgSeq(this.opts.msgId);\n }\n const currentIndex = this.index++;\n const req: StreamMessageRequest = {\n input_mode: StreamInputMode.REPLACE,\n input_state: state,\n content_type: StreamContentType.MARKDOWN,\n content_raw: text,\n event_id: this.eventId,\n msg_id: this.opts.msgId,\n msg_seq: this.msgSeq,\n index: currentIndex,\n };\n if (this.streamMsgId) {\n req.stream_msg_id = this.streamMsgId;\n }\n\n const resp = await this.sendWithRetry(req);\n if (resp?.id && !this.streamMsgId) {\n this.streamMsgId = resp.id;\n }\n this.lastSentText = text;\n this.lastFlushAt = Date.now();\n return resp;\n } catch (err) {\n flushFailed = true;\n this.opts.logger?.error?.(\n `[qqbot:stream] flush failed (state=${state}): ${formatErrorMessage(err)}`,\n );\n throw err;\n } finally {\n this.flushInProgress = false;\n // Trailing flush if more text arrived during the in-flight call.\n // Skip trailing flush on failure to avoid retry storm.\n if (\n !flushFailed &&\n !this.isCompleted &&\n this.pendingText !== this.lastSentText &&\n !this.pendingTimer &&\n state !== StreamInputState.DONE\n ) {\n await this.flush(StreamInputState.GENERATING);\n }\n }\n }\n\n /**\n * Send a stream message with exponential backoff on rate-limit errors.\n * QQ returns err_code 50002 or HTTP 429 when rate-limited.\n */\n private async sendWithRetry(req: StreamMessageRequest): Promise<MessageResponse | undefined> {\n for (let attempt = 0; attempt <= MAX_FLUSH_RETRIES; attempt++) {\n try {\n return await this.api.sendC2CStreamMessage(this.opts.creds, this.opts.openid, req);\n } catch (err: unknown) {\n if (!this.isRateLimitError(err) || attempt >= MAX_FLUSH_RETRIES) {\n throw err;\n }\n const delay = RATE_LIMIT_BASE_DELAY_MS * Math.pow(2, attempt);\n this.opts.logger?.debug?.(\n `[qqbot:stream] rate limited, retry ${attempt + 1}/${MAX_FLUSH_RETRIES} after ${delay}ms`,\n );\n await new Promise((r) => setTimeout(r, delay));\n // Advance index for the retry to avoid stale index conflict\n req.index = this.index++;\n }\n }\n return undefined;\n }\n\n /** Check if an error is a rate-limit error (QQ err_code 50002 or HTTP 429). */\n private isRateLimitError(err: unknown): boolean {\n const msg = err instanceof Error ? err.message : String(err);\n if (msg.includes(\"rate limit\")) return true;\n const code = (err as Record<string, unknown>)?.code ?? (err as Record<string, unknown>)?.err_code;\n if (code === 50002 || code === 429) return true;\n return false;\n }\n}\n","/**\n * Message filter middleware — drop invalid/duplicate/self-echo messages.\n *\n * Combines two common filtering needs into a single middleware:\n * 1. **Self-echo**: QQ may deliver the bot's own outbound messages back\n * as inbound events (especially in group chats).\n * 2. **Deduplication**: QQ occasionally pushes the same event more than\n * once (network retries, reconnect overlap, etc.).\n *\n * Both are enabled by default. Disable either via options.\n *\n * @example\n * ```ts\n * bot.use(messageFilter()); // both enabled\n * bot.use(messageFilter({ skipSelfEcho: false })); // only dedup\n * bot.use(messageFilter({ dedup: { windowMs: 10_000 }})); // custom window\n * ```\n */\n\nimport type { Middleware } from \"./types.js\";\n\nexport interface MessageFilterOptions {\n /**\n * Drop messages where `senderIsBot === true`.\n * Defaults to `true`.\n */\n skipSelfEcho?: boolean;\n /**\n * Deduplication settings. Set to `false` to disable entirely.\n * Defaults to `{ windowMs: 5000, maxSize: 1000 }`.\n */\n dedup?: false | {\n /** Time window (ms) to keep messageIds. Defaults to `5000`. */\n windowMs?: number;\n /** Max tracked ids. Defaults to `1000`. */\n maxSize?: number;\n };\n}\n\nexport function messageFilter(options: MessageFilterOptions = {}): Middleware {\n const skipSelfEcho = options.skipSelfEcho ?? true;\n const dedupOpts = options.dedup !== false\n ? { windowMs: 5000, maxSize: 1000, ...(options.dedup ?? {}) }\n : null;\n\n const seen = dedupOpts ? new Map<string, number>() : null;\n\n function evict(now: number): void {\n if (!seen || !dedupOpts) return;\n if (seen.size <= dedupOpts.maxSize) return;\n for (const [key, ts] of seen) {\n if (now - ts > dedupOpts.windowMs || seen.size > dedupOpts.maxSize) {\n seen.delete(key);\n } else {\n break;\n }\n }\n }\n\n return async (ctx, next) => {\n // 1. Self-echo check\n if (skipSelfEcho && ctx.message.senderIsBot) {\n ctx.stop(\"self-echo\");\n return;\n }\n\n // 2. Deduplication check\n if (seen && dedupOpts) {\n const id = ctx.message.messageId;\n const now = Date.now();\n if (seen.has(id)) {\n ctx.log.debug?.(`[message-filter] dropping duplicate messageId=${id}`);\n ctx.stop(\"deduplication\");\n return;\n }\n seen.set(id, now);\n evict(now);\n }\n\n await next();\n };\n}\n","/**\n * Content-sanitizer middleware — clean up raw message text.\n *\n * QQ delivers messages with `<@!{appId}>` mention markers, extra whitespace,\n * and sometimes face-tag markup. This middleware strips those artifacts so\n * downstream middleware (and the user's `onMessage` handler) receives clean\n * human-readable text.\n *\n * Operates on `ctx.message.content` in-place — downstream sees the\n * sanitized version.\n */\n\nimport type { Middleware, MiddlewareContext } from \"./types.js\";\n\nexport interface ContentSanitizerOptions {\n /**\n * Whether to strip `<@!{appId}>` / `<@{appId}>` mention markers.\n * Defaults to `true`.\n */\n stripBotMention?: boolean;\n /**\n * Whether to strip all `<@!{id}>` markers (not just the bot's).\n * Defaults to `false`.\n */\n stripAllMentions?: boolean;\n /**\n * Whether to collapse multiple consecutive whitespace / newlines into\n * a single space. Defaults to `false`.\n */\n collapseWhitespace?: boolean;\n /**\n * Whether to parse QQ face tags `[<face,id=xx/>]` into emoji text.\n * Defaults to `false` (just strip them).\n */\n parseFaceTags?: boolean;\n /**\n * Custom transform applied after all built-in sanitization.\n */\n transform?: (content: string, ctx: MiddlewareContext) => string;\n}\n\n/**\n * @example\n * ```ts\n * bot.use(contentSanitizer()); // Default: strip bot mention + trim\n * bot.use(contentSanitizer({ stripAllMentions: true, collapseWhitespace: true }));\n * ```\n */\nexport function contentSanitizer(options: ContentSanitizerOptions = {}): Middleware {\n const {\n stripBotMention = true,\n stripAllMentions = false,\n collapseWhitespace = false,\n parseFaceTags = false,\n transform,\n } = options;\n\n return async (ctx, next) => {\n let content = ctx.message.content ?? \"\";\n\n // Strip mention markers\n if (stripAllMentions) {\n content = content.replace(/<@!?\\d+>\\s*/g, \"\");\n } else if (stripBotMention) {\n const appId = ctx.bot.appId;\n if (appId) {\n const re = new RegExp(`<@!?${appId.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}>\\\\s*`, \"g\");\n content = re[Symbol.replace](content, \"\");\n }\n }\n\n // Face tags: old format [<face,id=xx/>] + new format <faceType=xx,faceId=\"xx\",ext=\"base64\">\n if (parseFaceTags) {\n // Old format: map face ids to emoji\n content = content.replace(/\\[<face,id=(\\d+)\\/?>]/g, (_m, id) => faceToEmoji(id) ?? \"\");\n // New format: decode base64 ext to get face name\n content = content.replace(/<faceType=\\d+,faceId=\"[^\"]*\",ext=\"([^\"]*)\">/g, (_m, ext) => {\n try {\n const decoded = Buffer.from(ext, 'base64').toString('utf-8');\n const parsed = JSON.parse(decoded);\n return `【表情: ${parsed.text || '未知表情'}】`;\n } catch {\n return _m;\n }\n });\n } else {\n // Strip both formats\n content = content.replace(/\\[<face,id=\\d+\\/?>]/g, \"\");\n content = content.replace(/<faceType=\\d+,faceId=\"[^\"]*\",ext=\"[^\"]*\">/g, \"\");\n }\n\n // Collapse whitespace\n if (collapseWhitespace) {\n content = content.replace(/\\s+/g, \" \");\n }\n\n // Trim\n content = content.trim();\n\n // Custom transform\n if (transform) {\n content = transform(content, ctx);\n }\n\n ctx.message.content = content;\n await next();\n };\n}\n\n/** Simplified face-id → emoji mapping (QQ common faces). */\nfunction faceToEmoji(id: string): string | undefined {\n const map: Record<string, string> = {\n \"0\": \"😊\", \"1\": \"😣\", \"2\": \"😍\", \"4\": \"😎\", \"5\": \"😭\",\n \"6\": \"😳\", \"7\": \"🤐\", \"8\": \"😴\", \"9\": \"😢\", \"10\": \"😰\",\n \"11\": \"😡\", \"12\": \"🤗\", \"13\": \"⭐\", \"14\": \"🌟\", \"15\": \"🌙\",\n \"16\": \"👍\", \"18\": \"✊\", \"21\": \"😊\", \"23\": \"😢\", \"25\": \"🤔\",\n \"26\": \"😱\", \"27\": \"😅\", \"28\": \"😁\", \"29\": \"🤮\", \"30\": \"💪\",\n \"32\": \"🎉\", \"33\": \"😤\", \"34\": \"😏\", \"35\": \"🥺\", \"49\": \"🐷\",\n \"53\": \"🎂\", \"60\": \"☕\", \"63\": \"🌹\", \"66\": \"❤️\", \"74\": \"🌞\",\n \"75\": \"🌛\", \"76\": \"👏\", \"78\": \"🤝\", \"79\": \"✌️\", \"85\": \"🎅\",\n \"89\": \"🍉\", \"96\": \"😓\", \"97\": \"😲\", \"100\": \"😂\", \"101\": \"😊\",\n \"104\": \"😭\", \"106\": \"😱\", \"109\": \"😘\", \"111\": \"🥰\",\n };\n return map[id];\n}\n","/**\n * Rate-limiter middleware — configurable multi-tier rate limiting.\n *\n * Supports three tiers: per-sender, per-group, and global. Uses a\n * sliding-window counter algorithm. When a message exceeds the limit,\n * the chain is short-circuited; an optional `onLimit` hook lets users\n * send a friendly rejection or log the event.\n */\n\nimport type { Middleware, MiddlewareContext } from \"./types.js\";\n\nexport interface RateLimitTier {\n /** Max requests within the window. */\n max: number;\n /** Window duration in milliseconds. */\n windowMs: number;\n}\n\nexport interface RateLimiterOptions {\n /** Per-sender limit. Keyed by `senderId`. */\n perSender?: RateLimitTier;\n /** Per-group limit. Keyed by `groupOpenid` (group only; C2C uses senderId). */\n perGroup?: RateLimitTier;\n /** Global limit across all messages. */\n global?: RateLimitTier;\n /**\n * Hook invoked when a message is rate-limited. Use to send a reply or\n * log. Return value is ignored.\n */\n onLimit?: (ctx: MiddlewareContext, tier: \"perSender\" | \"perGroup\" | \"global\") => void | Promise<void>;\n}\n\n// ============ Sliding window counter ============\n\nclass SlidingWindow {\n private readonly buckets = new Map<string, number[]>();\n private readonly max: number;\n private readonly windowMs: number;\n\n constructor(tier: RateLimitTier) {\n this.max = tier.max;\n this.windowMs = tier.windowMs;\n }\n\n /** Returns `true` if allowed, `false` if rate-limited. */\n check(key: string): boolean {\n const now = Date.now();\n let arr = this.buckets.get(key);\n if (!arr) {\n arr = [];\n this.buckets.set(key, arr);\n }\n\n // Evict expired entries\n while (arr.length > 0 && now - arr[0]! > this.windowMs) {\n arr.shift();\n }\n\n if (arr.length >= this.max) {\n return false;\n }\n\n arr.push(now);\n return true;\n }\n}\n\n/**\n * @example\n * ```ts\n * bot.use(rateLimiter({\n * perSender: { max: 5, windowMs: 60_000 }, // 5 msg/min per user\n * perGroup: { max: 30, windowMs: 60_000 }, // 30 msg/min per group\n * global: { max: 100, windowMs: 60_000 }, // 100 msg/min total\n * onLimit: async (ctx, tier) => {\n * await ctx.bot.sendText(ctx.replyTarget, `请稍后再试(${tier}限流)`);\n * },\n * }));\n * ```\n */\nexport function rateLimiter(options: RateLimiterOptions = {}): Middleware {\n const { onLimit } = options;\n const perSender = options.perSender ? new SlidingWindow(options.perSender) : undefined;\n const perGroup = options.perGroup ? new SlidingWindow(options.perGroup) : undefined;\n const global = options.global ? new SlidingWindow(options.global) : undefined;\n\n return async (ctx, next) => {\n const senderId = ctx.message.senderId;\n const groupKey = ctx.message.groupOpenid ?? senderId;\n\n // Check global first (cheapest to short-circuit)\n if (global && !global.check(\"__global__\")) {\n await onLimit?.(ctx, \"global\");\n ctx.stop(\"rate-limit:global\");\n return;\n }\n\n if (perGroup && !perGroup.check(groupKey)) {\n await onLimit?.(ctx, \"perGroup\");\n ctx.stop(\"rate-limit:perGroup\");\n return;\n }\n\n if (perSender && !perSender.check(senderId)) {\n await onLimit?.(ctx, \"perSender\");\n ctx.stop(\"rate-limit:perSender\");\n return;\n }\n\n await next();\n };\n}\n","/**\n * Concurrency-guard middleware — per-target serial message processing.\n *\n * Ensures only one message is being processed at a time for each\n * target (user or group). Prevents concurrent stream_messages calls\n * to the same QQ user/group which causes platform 500 errors.\n *\n * Strategy:\n * - \"queue\": New messages wait until the current one finishes.\n * - \"drop\": New messages are silently dropped while busy.\n * - \"abort\": Cancel the current processing, start the new one.\n * - \"merge\": Buffer new messages while the active one is being\n * processed. When the active message finishes, all\n * buffered messages are merged via `onMerge` into one\n * survivor ctx. The survivor proceeds through the rest\n * of the chain via its own `next()`. Non-survivors\n * return silently (chain stops).\n *\n * The concurrency key is `${scope}:${targetId}` (e.g. \"c2c:OPENID\"\n * or \"group:GROUPID\").\n *\n * Implementation note on \"merge\" — the \"queue + batch\" model:\n *\n * The active message (A) runs through the full chain normally via\n * `next()`. Incoming messages (B, C, D) pause on a promise without\n * calling `ctx.stop()` — they do not advance the chain. When A\n * finishes, all buffered ctxs are merged. One survivor is chosen;\n * its waiter is marked and proceeds to call `next()`. All other\n * waiters return silently.\n *\n * This guarantees:\n * 1. A runs immediately with zero delay and its content stays\n * untouched.\n * 2. Buffered messages are merged and processed exactly once per\n * batch via the survivor's own `next()`.\n * 3. Non-survivors' chains stop naturally (no upstream `next()`\n * call), no `ctx.stop()` needed.\n * 4. No suspended-promise complexity, no framework coupling.\n *\n * @example\n * ```ts\n * bot.use(concurrencyGuard({ strategy: \"queue\", maxQueue: 3 }));\n * bot.use(concurrencyGuard({\n * strategy: \"merge\",\n * maxQueue: 50,\n * onMerge: (buffered) => mergeContexts(buffered),\n * onDispatch: (merged) => bot.sendText(...), // legacy\n * }));\n * ```\n */\n\nimport type { Middleware, MiddlewareContext } from \"./types.js\";\n\nexport type ConcurrencyStrategy = \"queue\" | \"drop\" | \"abort\" | \"merge\";\n\nexport interface ConcurrencyGuardOptions {\n /**\n * How to handle a new message when the target is busy.\n * - \"queue\": Wait for the current message to finish (default).\n * - \"drop\": Silently skip the new message.\n * - \"abort\": Abort current processing via AbortController, then process new.\n * - \"merge\": Buffer new messages while busy; when the active message\n * finishes, merge & dispatch the batch.\n */\n strategy?: ConcurrencyStrategy;\n /**\n * Maximum processing time (ms) for a single active message. When the\n * active chain exceeds this duration, the guard **aborts** the chain\n * via `ctx.abort()`, releases the lock, and drains any buffered\n * messages.\n *\n * Aborting propagates the `AbortSignal` through the remaining middleware\n * chain and downstream agent pipeline, canceling in-flight LLM calls,\n * tool executions, and streaming.\n *\n * Set to `0` to disable the timeout (no limit). Default: `0` (disabled).\n */\n maxProcessingMs?: number;\n /** Max queued messages per target (for \"queue\") or max buffered messages (for \"merge\"). Default: 3. */\n maxQueue?: number;\n /** Called when a message is dropped (strategy=drop or queue/buffer overflow). */\n onDrop?: (ctx: MiddlewareContext) => void | Promise<void>;\n /**\n * Merge function for \"merge\" strategy. Receives the array of buffered\n * contexts in arrival order and must return one of them (the \"survivor\"),\n * mutated in place to represent the merged result.\n *\n * If not provided, {@link defaultMerge} is used: the first context is\n * kept and all message contents are concatenated with newline separators.\n */\n onMerge?: (buffered: MiddlewareContext[]) => MiddlewareContext;\n /**\n * @deprecated Legacy merge-dispatch callback. When set, merged batches\n * are dispatched via `onDispatch(mergedCtx)` instead of flowing through\n * the survivor's own `next()`. Prefer the default chain-based mechanism.\n */\n onDispatch?: (ctx: MiddlewareContext) => void | Promise<void>;\n /**\n * Predicate signaling an urgent message that should skip the queue.\n * When true (e.g. `/stop`), all buffered/waiting messages for the\n * same target are silently dropped and the ctx proceeds through the\n * remaining middleware chain immediately (parallel to active owner).\n * Only meaningful with \"merge\" strategy.\n */\n urgentPredicate?: (ctx: MiddlewareContext) => boolean;\n}\n\ninterface QueueEntry {\n run: () => void;\n ctx?: MiddlewareContext;\n}\n\n/** A buffered ctx waiting for the current batch to finish. */\ninterface MergeWaiter {\n ctx: MiddlewareContext;\n resolve: () => void;\n /** Set when this waiter's ctx is chosen as the survivor. */\n markSurvivor: () => void;\n}\n\ninterface TargetState {\n busy: boolean;\n queue: QueueEntry[];\n activeCtx?: MiddlewareContext;\n mergeBuffer?: MiddlewareContext[];\n mergeWaiters?: MergeWaiter[];\n}\n\nexport function concurrencyGuard(options: ConcurrencyGuardOptions = {}): Middleware {\n const strategy = options.strategy ?? \"queue\";\n const maxQueue = options.maxQueue ?? 3;\n const { onDrop, onMerge, onDispatch, urgentPredicate, maxProcessingMs } = options;\n\n const locks = new Map<string, TargetState>();\n\n function getState(key: string): TargetState {\n let s = locks.get(key);\n if (!s) {\n s = { busy: false, queue: [] };\n locks.set(key, s);\n }\n return s;\n }\n\n function cleanupState(key: string): void {\n const s = locks.get(key);\n if (\n s &&\n !s.busy &&\n s.queue.length === 0 &&\n (!s.mergeBuffer || s.mergeBuffer.length === 0)\n ) {\n locks.delete(key);\n }\n }\n\n function targetKey(ctx: MiddlewareContext): string {\n const t = ctx.message.replyTarget;\n return `${t.scope}:${t.targetId}`;\n }\n\n const guard: Middleware = async (ctx, next) => {\n const key = targetKey(ctx);\n const state = getState(key);\n\n if (!state.busy) {\n // No active processing — proceed immediately.\n state.busy = true;\n state.activeCtx = ctx;\n\n if (strategy === \"merge\") {\n state.mergeBuffer = [];\n state.mergeWaiters = [];\n }\n\n // Timeout watchdog: abort the active chain after maxProcessingMs.\n // ctx.abort() propagates AbortSignal through the remaining middleware\n // chain and downstream agent pipeline, canceling in-flight LLM calls,\n // tool executions, and streaming.\n let timedOut = false;\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n if (maxProcessingMs && maxProcessingMs > 0) {\n timeoutId = setTimeout(() => {\n timedOut = true;\n ctx.log.warn?.(\n `[concurrency] aborting active chain ${key} after ${maxProcessingMs}ms`,\n );\n ctx.abort('concurrency:processing-timeout');\n // Release lock BEFORE draining — the next message in queue will\n // set busy=true, which would be overwritten if we did it after.\n state.busy = false;\n state.activeCtx = undefined;\n if (strategy === 'merge') {\n drainMergeBuffer(key, state, ctx).catch(() => {});\n } else {\n drainQueue(key);\n }\n cleanupState(key);\n }, maxProcessingMs);\n }\n\n try {\n await next();\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n\n if (!timedOut) {\n // timeout not triggered, release lock normally\n state.activeCtx = undefined;\n\n if (strategy === \"merge\") {\n await drainMergeBuffer(key, state, ctx);\n state.busy = false;\n } else {\n state.busy = false;\n if (strategy === \"queue\" || strategy === \"abort\") {\n drainQueue(key);\n }\n }\n\n cleanupState(key);\n }\n }\n return;\n }\n\n // Target is busy — apply strategy\n switch (strategy) {\n case \"merge\": {\n if (!state.mergeBuffer || !state.mergeWaiters) {\n state.mergeBuffer = [];\n state.mergeWaiters = [];\n }\n\n // Urgent message: flush buffered waiters, continue remaining chain\n // immediately via next() in parallel to active owner.\n if (urgentPredicate?.(ctx)) {\n ctx.log.debug?.(`[concurrency:merge] urgent for ${key}`);\n for (const w of state.mergeWaiters) w.resolve();\n state.mergeBuffer.length = 0;\n state.mergeWaiters.length = 0;\n await next();\n return;\n }\n\n if (state.mergeBuffer.length >= maxQueue) {\n ctx.log.debug?.(`[concurrency:merge] buffer full (${maxQueue}), drop for ${key}`);\n await onDrop?.(ctx);\n ctx.stop(\"concurrency:merge-full\");\n return;\n }\n\n ctx.log.debug?.(\n `[concurrency:merge] buffered: ${key} (msgId=${ctx.message.messageId} ` +\n `pos=${state.mergeBuffer.length + 1})`,\n );\n\n // Buffer the ctx and pause — do NOT stop the chain.\n state.mergeBuffer.push(ctx);\n\n // Wait until the batch is processed. If we are the survivor,\n // proceed to next(); otherwise return silently.\n let isSurvivor = false;\n await new Promise<void>((resolve) => {\n state.mergeWaiters!.push({\n ctx,\n resolve,\n markSurvivor: () => {\n isSurvivor = true;\n },\n });\n });\n\n if (isSurvivor) {\n await next();\n }\n return;\n }\n\n case \"drop\": {\n ctx.log.debug?.(`[concurrency] drop message for busy target ${key}`);\n await onDrop?.(ctx);\n ctx.stop(\"concurrency:drop\");\n return;\n }\n\n case \"abort\": {\n ctx.log.debug?.(`[concurrency] abort previous for ${key}`);\n\n state.activeCtx?.abort(\"concurrency:abort\");\n\n for (const entry of state.queue) {\n entry.ctx?.abort(\"concurrency:superseded\");\n entry.run();\n }\n state.queue.length = 0;\n\n await waitForRelease(state, ctx);\n\n if (ctx.signal.aborted) {\n ctx.log.debug?.(`[concurrency] superseded while waiting for ${key}`);\n return;\n }\n\n state.busy = true;\n state.activeCtx = ctx;\n try {\n await next();\n } finally {\n state.busy = false;\n state.activeCtx = undefined;\n drainQueue(key);\n }\n return;\n }\n\n case \"queue\":\n default: {\n if (state.queue.length >= maxQueue) {\n ctx.log.debug?.(`[concurrency] queue full (${maxQueue}), drop for ${key}`);\n await onDrop?.(ctx);\n ctx.stop(\"concurrency:queue-full\");\n return;\n }\n\n ctx.log.debug?.(`[concurrency] queued message for ${key} (pos=${state.queue.length + 1})`);\n await new Promise<void>((resolve) => {\n state.queue.push({ run: resolve });\n });\n\n state.busy = true;\n state.activeCtx = ctx;\n try {\n await next();\n } finally {\n state.busy = false;\n state.activeCtx = undefined;\n drainQueue(key);\n }\n return;\n }\n }\n };\n\n /**\n * Drain the merge buffer: merge buffered ctxs into one survivor,\n * mark it, and resolve all waiters. The survivor then proceeds\n * through its own `next()`. If `onDispatch` is set (legacy),\n * dispatch via callback instead.\n */\n async function drainMergeBuffer(\n key: string,\n state: TargetState,\n ownerCtx: MiddlewareContext,\n ): Promise<void> {\n while (state.mergeBuffer && state.mergeBuffer.length > 0) {\n const buffered = state.mergeBuffer.splice(0);\n const waiters = state.mergeWaiters?.splice(0) ?? [];\n\n // Merge the buffered ctxs.\n const survivor = onMerge ? onMerge(buffered) : defaultMerge(buffered);\n const validSurvivor = buffered.includes(survivor) ? survivor : buffered[0]!;\n\n ownerCtx.log.debug?.(\n `[concurrency:merge] flushing batch: ${key} (count=${buffered.length})`,\n );\n\n // Dispatch: onDispatch (legacy) or survivor self-proceeds via next().\n if (onDispatch) {\n state.activeCtx = validSurvivor;\n try {\n await onDispatch(validSurvivor);\n } catch (err) {\n validSurvivor.log.error?.(\n `[concurrency:merge] onDispatch error: ${err instanceof Error ? err.message : String(err)}`,\n );\n } finally {\n state.activeCtx = undefined;\n }\n // All waiters resolve — none are survivors.\n for (const w of waiters) w.resolve();\n } else {\n // Mark the survivor's waiter; resolve all.\n for (const w of waiters) {\n if (w.ctx === validSurvivor) w.markSurvivor();\n w.resolve();\n }\n }\n }\n\n state.mergeBuffer = undefined;\n state.mergeWaiters = undefined;\n }\n\n function drainQueue(key: string) {\n const state = locks.get(key);\n if (!state) return;\n const entry = state.queue.shift();\n if (entry) {\n entry.run();\n } else if (!state.busy) {\n locks.delete(key);\n }\n }\n\n function waitForRelease(\n state: { busy: boolean; queue: QueueEntry[] },\n ctx?: MiddlewareContext,\n ): Promise<void> {\n if (!state.busy) return Promise.resolve();\n return new Promise<void>((resolve) => {\n state.queue.unshift({ run: resolve, ctx });\n });\n }\n\n /**\n * Default merge: concatenate contents with newline, keep first ctx as survivor.\n */\n function defaultMerge(buffered: MiddlewareContext[]): MiddlewareContext {\n const first = buffered[0]!;\n if (buffered.length === 1) return first;\n\n const contentBearingCtxs = buffered.filter(\n (ctx) => (ctx.message.content ?? \"\") !== \"\",\n );\n\n const contents = contentBearingCtxs.map((ctx) => ctx.message.content as string);\n if (contents.length > 0) {\n first.message.content = contents.join(\"\\n\");\n }\n\n const envelopes = contentBearingCtxs\n .map((ctx) => ctx.state.envelope as string | undefined)\n .filter(Boolean);\n if (envelopes.length > 0) {\n first.state.envelope = envelopes.join(\"\\n\\n\");\n }\n\n const allAttachments = contentBearingCtxs.flatMap(\n (ctx) => ctx.message.attachments ?? [],\n );\n if (allAttachments.length > 0) {\n first.message.attachments = allAttachments;\n }\n\n return first;\n }\n\n return guard;\n}\n","/**\n * Mention-gate middleware — decide whether a group message should be answered.\n *\n * QQ Bot group messages may or may not contain an `@bot` mention. Many\n * production bots want to **only respond to mentions** in group chats while\n * still listening to all C2C messages. This middleware encapsulates that\n * common decision.\n *\n * The decision is exposed in `ctx.state.mention` for downstream middleware\n * to inspect (e.g. a history-buffer middleware may still record non-mention\n * messages without triggering a reply).\n */\n\nimport type { Middleware, MiddlewareContext } from \"./types.js\";\nimport { resolvePolicy } from \"./types.js\";\n\n// ============ Types ============\n\nexport interface MentionDecision {\n /** True if the bot was explicitly @-mentioned. */\n wasMentioned: boolean;\n /**\n * True if the message satisfies an *implicit* mention rule (e.g. it is a\n * reply to a previous bot message).\n */\n implicit: boolean;\n /** Effective answer: whether the bot should respond. */\n shouldAnswer: boolean;\n /** When `shouldAnswer === false`, the gate's reason for skipping. */\n reason: \"no_mention\" | \"other_mention\" | \"passthrough\";\n}\n\nexport interface MentionGateOptions {\n /**\n * Require an `@bot` mention in group messages. Defaults to `true`.\n * When `false`, every group message is treated as addressed to the bot.\n */\n requireMentionInGroup?: boolean;\n\n /**\n * Treat C2C messages as always addressed to the bot. Defaults to `true`.\n */\n alwaysAnswerC2C?: boolean;\n\n /**\n * Optional implicit-mention probe — return true to bypass the\n * `requireMentionInGroup` check (e.g. when the message is a reply to a\n * prior bot message). This is a per-message signal, orthogonal to\n * `resolveConfig` which is a per-group policy.\n */\n isImplicitMention?: (ctx: MiddlewareContext) => boolean;\n\n /**\n * Hook invoked when the gate skips a message. Use for telemetry or to\n * record the message into a history buffer despite skipping.\n */\n onSkip?: (ctx: MiddlewareContext, decision: MentionDecision) => void;\n\n /**\n * If `true`, skip group messages that mention other users/bots but do\n * **not** mention this bot. This prevents the bot from responding to\n * conversations directed at other participants.\n *\n * Defaults to `false`.\n */\n ignoreOtherMentions?: boolean;\n\n /**\n * If `true`, do **not** stop the middleware chain when the message should\n * not be answered — only annotate `ctx.state.mention`. Useful when other\n * middleware (e.g. history buffering) still needs to run.\n *\n * Defaults to `false` (stop the chain on skip).\n */\n passthrough?: boolean;\n\n /**\n * Dynamic config resolver — called for each message before the gate logic\n * runs. The returned values override the static defaults\n * (`requireMentionInGroup`, `ignoreOtherMentions`).\n *\n * Use this when different groups / contexts need different mention policies\n * without registering separate `mentionGate` instances.\n */\n resolveConfig?: (ctx: MiddlewareContext) => {\n requireMentionInGroup?: boolean;\n ignoreOtherMentions?: boolean;\n };\n}\n\n// ============ Helpers ============\n\n/** Heuristic: detect `<@!{appId}>` or `<@{appId}>` mention markers in content. */\nfunction detectMentionInContent(content: string, appId: string): boolean {\n if (!content || !appId) return false;\n const re = new RegExp(`<@!?${appId.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}>`);\n return re.test(content);\n}\n\n// ============ Middleware ============\n\n/**\n * Build a mention-gate middleware.\n *\n * @example\n * ```ts\n * // Static config\n * bot.use(mentionGate({ requireMentionInGroup: true }));\n *\n * // Dynamic per-group config\n * bot.use(mentionGate({\n * resolveConfig: (ctx) => {\n * const cfg = resolveGroupConfig(ctx.message.groupId);\n * return {\n * requireMentionInGroup: cfg.requireMention,\n * ignoreOtherMentions: cfg.ignoreOtherMentions,\n * };\n * },\n * }));\n * ```\n */\nexport function mentionGate(options: MentionGateOptions = {}): Middleware {\n const {\n requireMentionInGroup = true,\n alwaysAnswerC2C = true,\n isImplicitMention,\n onSkip,\n ignoreOtherMentions = false,\n passthrough = false,\n resolveConfig,\n } = options;\n\n return async (ctx, next) => {\n const msg = ctx.message;\n const appId = ctx.bot.appId;\n\n // Only group messages need gating\n if (msg.kind !== \"group\") {\n const decision: MentionDecision = {\n wasMentioned: true,\n implicit: false,\n shouldAnswer: alwaysAnswerC2C || (msg.kind !== \"c2c\" && msg.kind !== \"dm\"),\n reason: \"passthrough\",\n };\n ctx.state.mention = decision;\n await next();\n return;\n }\n\n // Dynamic config resolution — priority:\n // 1. resolveConfig callback (explicit per-middleware override)\n // 2. ctx.state.policy (injected by upstream policy injector)\n // 3. static option defaults\n const dynamic = resolveConfig?.(ctx);\n const effectiveRequireMention = resolvePolicy(\n ctx, \"group.requireMention\",\n dynamic?.requireMentionInGroup,\n requireMentionInGroup,\n );\n const effectiveIgnoreOther = resolvePolicy(\n ctx, \"group.ignoreOtherMentions\",\n dynamic?.ignoreOtherMentions,\n ignoreOtherMentions,\n );\n\n // ---- Group: decide ----\n //\n // Priority order for \"was mentioned\":\n // 1. rawEventType === GROUP_AT_MESSAGE_CREATE\n // The event itself is QQ's authoritative signal — the Open Platform\n // only delivers this event when the user actually @-ed the bot.\n // 2. mentions[].is_you === true\n // Server-marked structured mention list (present on some payloads).\n // 3. Content contains `<@!{appId}>` or `<@{appId}>`\n // Fallback scan for self-hosted / private-domain bots that also\n // receive GROUP_MESSAGE_CREATE (every group message, not just @).\n const wasMentioned =\n msg.rawEventType === \"GROUP_AT_MESSAGE_CREATE\" ||\n (Array.isArray(msg.mentions) && msg.mentions.some((m) => m?.is_you === true)) ||\n detectMentionInContent(msg.content, appId);\n\n const implicit = isImplicitMention?.(ctx) ?? false;\n\n // ---- Determine shouldAnswer & reason ----\n let shouldAnswer = !effectiveRequireMention || wasMentioned || implicit;\n let reason: MentionDecision[\"reason\"] = shouldAnswer ? \"passthrough\" : \"no_mention\";\n\n // ignoreOtherMentions: when requireMention is true and the message\n // @-ed someone else but NOT this bot, override to skip.\n // When requireMention is false the user explicitly wants all group\n // messages answered, so ignoreOtherMentions does not apply.\n if (\n effectiveIgnoreOther &&\n effectiveRequireMention &&\n !wasMentioned &&\n !implicit\n ) {\n const mentions = msg.mentions as Array<{ is_you?: boolean; bot?: boolean }> | undefined;\n if (Array.isArray(mentions) && mentions.length > 0) {\n shouldAnswer = false;\n reason = \"other_mention\";\n }\n }\n\n const decision: MentionDecision = {\n wasMentioned,\n implicit,\n shouldAnswer,\n reason,\n };\n\n ctx.state.mention = decision;\n\n if (!shouldAnswer) {\n onSkip?.(ctx, decision);\n ctx.log.debug?.(\n `[mention-gate] skip group message (${reason}): wasMentioned=${wasMentioned}, implicit=${implicit}`,\n );\n if (!passthrough) {\n ctx.stop(`mention-gate:${reason}`);\n return;\n }\n }\n\n await next();\n };\n}\n","/**\n * Quote & Ref-Index middleware — record messages + resolve quoted references.\n *\n * A single middleware that does two things in one pass:\n * 1. **Record** — captures a compact summary of the current message into\n * a pluggable store (default: in-memory LRU), keyed by `msgIdx` or `messageId`.\n * 2. **Resolve** — if the current message quotes/replies to a previous one\n * (`refMsgIdx` set), looks it up from the store or falls back to QQ's\n * `msg_elements[0]` push data.\n *\n * The resolved quote is attached to `ctx.state.quote`.\n *\n * @example\n * ```ts\n * import { quoteRef } from \"@tencent-connect/qqbot-nodejs\";\n *\n * bot.use(quoteRef()); // zero-config\n * bot.use(quoteRef({ maxSize: 1000 })); // custom LRU size\n * bot.use(quoteRef({ store: myRedisStore })); // custom backend\n * ```\n */\n\nimport type { Middleware, MiddlewareContext } from \"./types.js\";\n\n// ============ Types ============\n\n/** Compact summary stored per message. */\nexport interface RefEntry {\n messageId: string;\n senderId: string;\n senderName?: string;\n content: string;\n timestamp: string;\n /** Extensible: stores may accept extra keys for domain-specific metadata. */\n [key: string]: unknown;\n}\n\n/** Pluggable store interface for ref-index. */\nexport interface RefIndexStore {\n get(key: string): RefEntry | undefined | Promise<RefEntry | undefined>;\n set(key: string, entry: RefEntry): void | Promise<void>;\n}\n\n/** Attachment summary from the quoted message (from msg_elements). */\nexport interface QuotedAttachment {\n contentType: string;\n url: string;\n filename?: string;\n /** ASR text for voice messages (QQ built-in speech recognition). */\n asrText?: string;\n}\n\n/** Resolved quote attached to `ctx.state.quote`. */\nexport interface ResolvedQuote {\n /** The ref key that was looked up. */\n refKey: string;\n /** Source of the resolution. */\n source: \"store\" | \"msg_elements\" | \"none\";\n /** The resolved entry from store (cache hit). */\n entry?: RefEntry;\n /** Raw content from msg_elements[0] (cache miss fallback). */\n rawContent?: string;\n /** Attachments from the quoted message (from msg_elements[0]). */\n attachments?: QuotedAttachment[];\n /**\n * Combined text representation of the quoted message.\n * Prefers store entry content; falls back to msg_elements content.\n */\n text: string;\n}\n\nexport interface QuoteRefOptions {\n /** Backing store. Defaults to an in-memory LRU. */\n store?: RefIndexStore;\n /** Max entries in the default in-memory LRU. Defaults to 500. */\n maxSize?: number;\n /** Max content chars stored per entry. Defaults to 200. */\n contentLimit?: number;\n /** Enrich the stored entry with extra fields before writing. */\n enrichEntry?: (entry: RefEntry, ctx: MiddlewareContext) => RefEntry;\n /** When true, prefer msg_elements data over stored entry for richer content. */\n preferMsgElements?: boolean;\n}\n\n// ============ Default in-memory LRU ============\n\n/**\n * Default in-memory ref-index store (LRU eviction).\n *\n * Suitable for single-process bots. For multi-instance deployments,\n * implement `RefIndexStore` with Redis / SQL / etc.\n */\nexport class MemoryRefIndexStore implements RefIndexStore {\n private readonly map = new Map<string, RefEntry>();\n private readonly maxSize: number;\n\n constructor(maxSize = 500) {\n this.maxSize = maxSize;\n }\n\n get(key: string): RefEntry | undefined {\n return this.map.get(key);\n }\n\n set(key: string, entry: RefEntry): void {\n if (this.map.size >= this.maxSize) {\n const first = this.map.keys().next().value;\n if (first !== undefined) this.map.delete(first);\n }\n this.map.set(key, entry);\n }\n}\n\n// ============ Middleware ============\n\n/**\n * Combined quote & ref-index middleware.\n *\n * One middleware does both: records the current message into the store\n * **and** resolves any quoted message reference.\n *\n * @example\n * ```ts\n * bot.use(quoteRef()); // that's it — zero config\n *\n * bot.on(\"message\", (msg) => {\n * // Access resolved quote via middleware state\n * // (ctx.state.quote is typed as ResolvedQuote)\n * });\n * ```\n */\nexport function quoteRef(options: QuoteRefOptions = {}): Middleware {\n const contentLimit = options.contentLimit ?? 200;\n const store = options.store ?? new MemoryRefIndexStore(options.maxSize ?? 500);\n\n return async (ctx, next) => {\n const msg = ctx.message;\n\n // ── 1. Record current message ──\n const key = msg.msgIdx ?? msg.messageId;\n if (key) {\n let entry: RefEntry = {\n messageId: msg.messageId,\n senderId: msg.senderId,\n senderName: msg.senderName,\n content: (msg.content ?? \"\").slice(0, contentLimit),\n timestamp: msg.timestamp,\n isBot: msg.senderIsBot,\n scope: msg.kind,\n };\n if (options.enrichEntry) {\n entry = options.enrichEntry(entry, ctx);\n }\n await store.set(key, entry);\n }\n\n // ── 2. Resolve quoted message (if any) ──\n const refKey = msg.refMsgIdx;\n if (refKey) {\n const entry = await store.get(refKey);\n\n // 尝试从 msg_elements 解析引用内容(优先于 store entry 时使用)\n const resolveFromElements = () => {\n const el = msg.msgElements?.[0];\n if (!el || (!el.content && !el.attachments?.length)) return null;\n const rawContent = el.content ?? \"\";\n const attachments = parseAttachments(el.attachments);\n return { rawContent, attachments, text: buildText(rawContent, attachments) };\n };\n\n if (entry) {\n const elementsQuote = options.preferMsgElements !== false ? resolveFromElements() : null;\n if (elementsQuote) {\n ctx.state.quote = {\n refKey, source: \"msg_elements\",\n rawContent: elementsQuote.rawContent,\n attachments: elementsQuote.attachments,\n text: elementsQuote.text,\n };\n } else {\n ctx.state.quote = {\n refKey, source: \"store\", entry,\n text: entry.content || \"[empty message]\",\n };\n }\n ctx.log.debug?.(`[quote-ref] hit refKey=${refKey} sender=${entry.senderId}`);\n } else {\n const elementsQuote = resolveFromElements();\n if (elementsQuote) {\n ctx.state.quote = {\n refKey, source: \"msg_elements\",\n rawContent: elementsQuote.rawContent,\n attachments: elementsQuote.attachments,\n text: elementsQuote.text,\n };\n } else {\n ctx.state.quote = { refKey, source: \"none\", text: \"\" };\n }\n ctx.log.debug?.(`[quote-ref] ${elementsQuote ? 'fallback' : 'miss'} refKey=${refKey}`);\n }\n }\n\n await next();\n };\n}\n\n// ============ Helpers ============\n\nfunction parseAttachments(\n raw: Array<{ content_type: string; url: string; filename?: string; asr_refer_text?: string }> | undefined,\n): QuotedAttachment[] {\n if (!raw || raw.length === 0) return [];\n return raw.map((a) => ({\n contentType: a.content_type,\n url: a.url,\n filename: a.filename,\n asrText: a.asr_refer_text,\n }));\n}\n\nfunction buildText(content: string, attachments: QuotedAttachment[]): string {\n const parts: string[] = [];\n if (content.trim()) parts.push(content.trim());\n for (const att of attachments) {\n const t = att.contentType.toLowerCase();\n if (t.startsWith(\"audio/\")) {\n parts.push(att.asrText ? `[voice: ${att.asrText}]` : \"[voice]\");\n } else if (t.startsWith(\"image/\")) {\n parts.push(att.filename ? `[image: ${att.filename}]` : \"[image]\");\n } else if (t.startsWith(\"video/\")) {\n parts.push(att.filename ? `[video: ${att.filename}]` : \"[video]\");\n } else {\n parts.push(`[file: ${att.filename ?? \"untitled\"}]`);\n }\n }\n return parts.join(\"\\n\") || \"[empty message]\";\n}\n","/**\n * Envelope-formatter middleware — assemble LLM prompt context.\n *\n * Gathers information from upstream middleware (history, quote, mention\n * state, sender info) and formats it into a structured context string\n * attached to `ctx.state.envelope`. This is the \"last mile\" before the\n * user's LLM call — providing a ready-to-use prompt prefix.\n *\n * This middleware is **optional** — users who prefer to build their own\n * prompt can skip it and read `ctx.state.history` / `ctx.state.quote`\n * directly.\n */\n\nimport type { Middleware, MiddlewareContext } from \"./types.js\";\nimport type { HistoryEntry } from \"./history-buffer.js\";\nimport type { ResolvedQuote } from \"./quote-ref.js\";\n\nexport interface EnvelopeFormatterOptions {\n /**\n * Maximum number of history entries to include.\n * Defaults to `5`.\n */\n historyLimit?: number;\n /**\n * Whether to include the resolved quote in the envelope.\n * Defaults to `true`.\n */\n includeQuote?: boolean;\n /**\n * Whether to include sender name / id as context.\n * Defaults to `true`.\n */\n includeSender?: boolean;\n /**\n * Custom formatter override. If provided, replaces the default logic\n * entirely. Receives the same ctx and should return the formatted string.\n */\n format?: (ctx: MiddlewareContext) => string;\n}\n\n/**\n * @example\n * ```ts\n * bot.use(historyBuffer({ limit: 10 }));\n * bot.use(quoteResolver({ store: refStore }));\n * bot.use(envelopeFormatter({ historyLimit: 5 }));\n *\n * bot.on(\"message\", async (msg) => {\n * const envelope = msg._ctx?.state.envelope as string;\n * const response = await llm.chat(envelope + \"\\n\" + msg.content);\n * await bot.sendText(msg.replyTarget, response);\n * });\n * ```\n */\nexport function envelopeFormatter(options: EnvelopeFormatterOptions = {}): Middleware {\n const {\n historyLimit = 5,\n includeQuote = true,\n includeSender = true,\n format,\n } = options;\n\n return async (ctx, next) => {\n if (format) {\n ctx.state.envelope = format(ctx);\n } else {\n ctx.state.envelope = buildEnvelope(ctx, { historyLimit, includeQuote, includeSender });\n }\n\n ctx.log.debug?.(\n `[envelope] built ${(ctx.state.envelope as string).length} chars` +\n ` (sender=${includeSender}, quote=${includeQuote}, history=${(ctx.state.history as unknown[] | undefined)?.length ?? 0}/${historyLimit})`,\n );\n\n await next();\n };\n}\n\nfunction buildEnvelope(\n ctx: MiddlewareContext,\n opts: { historyLimit: number; includeQuote: boolean; includeSender: boolean },\n): string {\n const sections: string[] = [];\n\n // ── Context meta ──\n if (opts.includeSender) {\n const name = ctx.message.senderName || ctx.message.senderId;\n const scope = ctx.message.kind === \"group\"\n ? `group(${ctx.message.groupOpenid ?? \"unknown\"})`\n : ctx.message.kind;\n sections.push(`<from>\\nuser: ${name}\\nscope: ${scope}\\n</from>`);\n }\n\n // ── Quoted message (reply-to) ──\n if (opts.includeQuote) {\n const quote = ctx.state.quote as ResolvedQuote | undefined;\n if (quote && quote.text) {\n const sender = quote.entry?.senderName || quote.entry?.senderId;\n const line = sender ? `${sender}: ${quote.text}` : quote.text;\n sections.push(`<reply_to>\\n${line}\\n</reply_to>`);\n }\n }\n\n // ── Conversation history ──\n const history = ctx.state.history as HistoryEntry[] | undefined;\n if (history && history.length > 0) {\n const recent = history.slice(-opts.historyLimit);\n const lines = recent.map((h) => {\n const name = h.senderName || h.senderId;\n return `${name}: ${h.content.slice(0, 200)}`;\n });\n sections.push(`<history>\\n${lines.join(\"\\n\")}\\n</history>`);\n }\n\n // ── Current message ──\n const content = (ctx.message.content ?? \"\").trim();\n const attachments = ctx.message.attachments;\n const hasAttachments = attachments && attachments.length > 0;\n\n if (content || hasAttachments) {\n const parts: string[] = [];\n if (content) {\n parts.push(content);\n }\n if (hasAttachments) {\n for (const att of attachments) {\n const t = att.content_type.toLowerCase();\n if (t.startsWith(\"image/\")) parts.push(`[image: ${att.filename ?? \"image\"}]`);\n else if (t.startsWith(\"audio/\") || t === \"voice\") {\n parts.push(att.asr_refer_text ? `[voice: ${att.asr_refer_text}]` : \"[voice]\");\n }\n else if (t.startsWith(\"video/\")) parts.push(\"[video]\");\n else parts.push(`[file: ${att.filename ?? \"file\"}]`);\n }\n }\n sections.push(`<message>\\n${parts.join(\"\\n\")}\\n</message>`);\n }\n\n return sections.join(\"\\n\\n\");\n}\n","/**\n * Slash-command middleware — register `/cmd` style commands.\n *\n * Pattern: any message whose content starts with a configured prefix\n * (default `/`) is intercepted; the matched command's `handler` runs, and\n * the chain is short-circuited so the `bot.on(\"message\")` listener does\n * not fire.\n *\n * Commands are **explicitly registered** on the middleware instance so the\n * SDK does not enforce any specific business commands — users define what\n * makes sense for their bot.\n */\n\nimport type { ReplyTarget } from \"../QQBot.js\";\nimport type { Middleware, MiddlewareContext } from \"./types.js\";\n\n// ============ Types ============\n\n/** Parsed command struct exposed via `ctx.state.command`. */\nexport interface ParsedCommand {\n /** The command name (without leading prefix). */\n name: string;\n /** Whitespace-separated arguments after the command name. */\n args: string[];\n /** Raw arguments string (everything after the command name). */\n raw: string;\n}\n\n/** Result returned by a slash-command handler. */\nexport type SlashCommandResult =\n | string // Text reply\n | { kind: \"text\"; content: string } // Explicit text\n | { kind: \"noop\" } // Silently consume\n | void; // = noop\n\nexport interface SlashCommandHandlerContext extends MiddlewareContext {\n /** Parsed command struct. */\n command: ParsedCommand;\n}\n\n/** Spec for one registered command. */\nexport interface SlashCommand {\n /** Command name(s). The first is canonical; the rest are aliases. */\n name: string | string[];\n /** Brief description (used by `/help`). */\n description?: string;\n /** Optional usage hint (used by `/help`). */\n usage?: string;\n /** Whether this command is hidden from `/help`. Defaults to false. */\n hidden?: boolean;\n /** Scope restriction: \"c2c\" = only private chat, \"group\" = only group, \"all\" = both (default). */\n scope?: \"c2c\" | \"group\" | \"all\";\n /**\n * Handler. Return a string to send a text reply, or `{ kind: \"noop\" }`\n * to silently consume.\n */\n handler: (ctx: SlashCommandHandlerContext) => Promise<SlashCommandResult> | SlashCommandResult;\n /**\n * Optional authorization check executed before the handler.\n * - Return `true` to allow execution.\n * - Return `false` or a string to block; the string is sent as an error\n * reply (e.g. `\"⚠️ 无权限\"`). Default blocked message: `\"⚠️ 无权限执行此命令\"`.\n */\n authorized?: (ctx: SlashCommandHandlerContext) => boolean | string;\n}\n\nexport interface SlashCommandOptions {\n /** Command prefix(es). Defaults to `[\"/\"]`. */\n prefixes?: string[];\n /** Pre-registered commands. */\n commands?: SlashCommand[];\n /**\n * If `true`, when a command throws, the error is caught and\n * `{ kind: \"text\", content: errorMessage }` is sent as a reply.\n * Defaults to `true`.\n */\n catchErrors?: boolean;\n /**\n * Auto-register a `/help` command listing all visible commands.\n * Defaults to `true`.\n */\n autoHelp?: boolean;\n /**\n * Global allowlist of user OpenIDs permitted to use slash commands.\n * If empty or not set, all users can use commands.\n * Non-allowlisted users' command-like messages pass through to `next()`\n * as regular messages.\n */\n allowFrom?: string[];\n}\n\n// ============ Implementation ============\n\n/**\n * Slash-command middleware factory.\n *\n * Returns a middleware **and** a registry — the registry lets you keep\n * adding commands after the middleware is registered with the bot.\n *\n * @example\n * ```ts\n * const slash = slashCommand({ prefixes: [\"/\", \"!\"] });\n * slash.register({\n * name: \"ping\",\n * description: \"Reply with pong\",\n * handler: () => \"pong\",\n * });\n *\n * slash.register({\n * name: [\"whoami\", \"me\"],\n * handler: (ctx) => `You are ${ctx.message.senderId}`,\n * });\n *\n * bot.use(slash.middleware);\n * ```\n */\nexport function slashCommand(options: SlashCommandOptions = {}): {\n middleware: Middleware;\n register: (cmd: SlashCommand) => void;\n unregister: (name: string) => void;\n list: () => SlashCommand[];\n} {\n const prefixes = options.prefixes ?? [\"/\"];\n const catchErrors = options.catchErrors ?? true;\n const autoHelp = options.autoHelp ?? true;\n const allowFrom = options.allowFrom ?? [];\n\n const registry = new Map<string, SlashCommand>();\n\n const register = (cmd: SlashCommand): void => {\n const names = Array.isArray(cmd.name) ? cmd.name : [cmd.name];\n if (names.length === 0) {\n throw new Error(\"slash-command: name must not be empty\");\n }\n for (const n of names) {\n const key = n.toLowerCase();\n if (registry.has(key)) {\n throw new Error(`slash-command: duplicate name \"${n}\"`);\n }\n registry.set(key, cmd);\n }\n };\n\n const unregister = (name: string): void => {\n registry.delete(name.toLowerCase());\n };\n\n const list = (): SlashCommand[] => {\n const seen = new Set<SlashCommand>();\n const out: SlashCommand[] = [];\n for (const cmd of registry.values()) {\n if (!seen.has(cmd)) {\n seen.add(cmd);\n out.push(cmd);\n }\n }\n return out;\n };\n\n // Pre-register commands\n for (const cmd of options.commands ?? []) {\n register(cmd);\n }\n\n // Auto /help\n if (autoHelp && !registry.has(\"help\")) {\n register({\n name: \"help\",\n description: \"List all available commands\",\n handler: () => {\n const lines: string[] = [\"Available commands:\"];\n for (const cmd of list()) {\n if (cmd.hidden) continue;\n const names = Array.isArray(cmd.name) ? cmd.name.join(\", \") : cmd.name;\n const desc = cmd.description ?? \"\";\n const usage = cmd.usage ? ` — ${cmd.usage}` : \"\";\n lines.push(`/${names}${usage}${desc ? ` — ${desc}` : \"\"}`);\n }\n return lines.join(\"\\n\");\n },\n });\n }\n\n const middleware: Middleware = async (ctx, next) => {\n const content = (ctx.message.content ?? \"\").trim();\n if (!content) {\n await next();\n return;\n }\n\n // Strip server-side `<@bot>` mention markers from content for matching.\n const cleaned = content.replace(/<@!?[^>]+>\\s*/g, \"\").trim();\n\n // 群聊场景:必须 @bot 才触发命令\n if (ctx.message.kind === 'group') {\n const msg = ctx.message as { mentions?: Array<{ is_you?: boolean }>; rawEventType?: string };\n const wasMentioned =\n msg.rawEventType === 'GROUP_AT_MESSAGE_CREATE' ||\n msg.mentions?.some((m) => m.is_you);\n if (!wasMentioned) {\n await next();\n return;\n }\n }\n\n // Find matching prefix\n const prefix = prefixes.find((p) => cleaned.startsWith(p));\n if (!prefix) {\n await next();\n return;\n }\n\n const body = cleaned.slice(prefix.length);\n const match = /^(\\S+)(?:\\s+(.*))?$/.exec(body);\n if (!match) {\n await next();\n return;\n }\n\n const [, name, rest = \"\"] = match;\n const cmd = registry.get(name!.toLowerCase());\n if (!cmd) {\n await next();\n return;\n }\n\n // AllowFrom check: non-allowlisted users bypass commands as regular messages\n const isAllowed = allowFrom.length === 0\n || allowFrom.includes('*')\n || allowFrom.includes(ctx.message.senderId);\n if (!isAllowed) {\n await next();\n return;\n }\n\n const parsed: ParsedCommand = {\n name: name!.toLowerCase(),\n args: rest ? rest.split(/\\s+/) : [],\n raw: rest,\n };\n ctx.state.command = parsed;\n\n // Scope check: reject if command is restricted to a different scope\n const cmdScope = cmd.scope ?? \"all\";\n if (cmdScope !== \"all\") {\n const msgKind = ctx.message.kind;\n const isC2C = msgKind === \"c2c\" || msgKind === \"dm\";\n const isGroup = msgKind === \"group\";\n if ((cmdScope === \"c2c\" && !isC2C) || (cmdScope === \"group\" && !isGroup)) {\n const hint = cmdScope === \"c2c\" ? \"该指令仅限私聊使用\" : \"该指令仅限群聊使用\";\n await sendCommandResult(ctx, hint);\n ctx.stop(`command:scope-denied:${parsed.name}`);\n return;\n }\n }\n\n const handlerCtx = ctx as SlashCommandHandlerContext;\n handlerCtx.command = parsed;\n\n // Per-command authorization check\n if (cmd.authorized) {\n const auth = cmd.authorized(handlerCtx);\n if (auth !== true) {\n const authMsg = typeof auth === 'string' ? auth : '⚠️ 无权限执行此命令';\n await sendCommandResult(ctx, { kind: 'text', content: authMsg });\n ctx.stop(`command:unauthorized:${parsed.name}`);\n return;\n }\n }\n\n try {\n const result = await cmd.handler(handlerCtx);\n await sendCommandResult(ctx, result);\n } catch (err) {\n if (catchErrors) {\n const msg = err instanceof Error ? err.message : String(err);\n ctx.log.error?.(`[slash-command] handler \"${parsed.name}\" threw: ${msg}`);\n await sendCommandResult(ctx, { kind: \"text\", content: `Error: ${msg}` });\n } else {\n ctx.stop(`command:error:${parsed.name}`);\n throw err;\n }\n }\n\n // Commands always short-circuit the chain.\n ctx.stop(`command:matched:${parsed.name}`);\n };\n\n return { middleware, register, unregister, list };\n}\n\nasync function sendCommandResult(\n ctx: MiddlewareContext,\n result: SlashCommandResult,\n): Promise<void> {\n if (result === undefined || result === null) return;\n if (typeof result === \"string\") {\n if (result) await ctx.bot.sendText(ctx.replyTarget as ReplyTarget, result);\n return;\n }\n if (typeof result === \"object\") {\n if (result.kind === \"text\" && result.content) {\n await ctx.bot.sendText(ctx.replyTarget as ReplyTarget, result.content);\n }\n // kind === \"noop\" → no-op\n }\n}\n","/**\n * History-buffer middleware — keep recent group messages around.\n *\n * Many bots want to feed the LLM a few rounds of recent context, even for\n * messages that did not directly @-mention the bot. This middleware\n * silently records every group message into a per-group ring buffer, then\n * exposes the buffered list via `ctx.state.history`.\n *\n * **Pluggable storage** — the actual store is whatever object the user\n * passes in. The SDK ships an in-memory implementation; users can drop in\n * Redis / SQL / file-backed stores via the `HistoryStore` interface.\n */\n\nimport type { Middleware, MiddlewareContext } from \"./types.js\";\nimport { resolvePolicy } from \"./types.js\";\n\n// ============ Types ============\n\n/** One buffered message. */\nexport interface HistoryEntry {\n senderId: string;\n senderName?: string;\n content: string;\n timestamp: number;\n /** Original message id (for de-duplication). */\n messageId: string;\n}\n\n/** Pluggable storage backend. */\nexport interface HistoryStore {\n /** Append one entry into a group's buffer. Caller-provided ordering. */\n append(groupKey: string, entry: HistoryEntry, limit: number): void | Promise<void>;\n /** Return the most-recent entries for a group (oldest → newest). */\n list(groupKey: string, limit: number): HistoryEntry[] | Promise<HistoryEntry[]>;\n /** Optional: clear a group's buffer. */\n clear?(groupKey: string): void | Promise<void>;\n}\n\nexport interface HistoryBufferOptions {\n /** Max entries kept per group. Defaults to 50. */\n limit?: number;\n /** Storage backend. Defaults to {@link MemoryHistoryStore}. */\n store?: HistoryStore;\n /**\n * Whether to record the message even when an upstream middleware called\n * `ctx.stop()` (e.g. mention-gate skipped a no-mention group message).\n * Defaults to `true` — history exists precisely to capture the chatter\n * we did not respond to.\n */\n recordOnSkip?: boolean;\n /** Group-key derivation. Defaults to `groupOpenid`. */\n groupKey?: (ctx: MiddlewareContext) => string | undefined;\n}\n\n// ============ In-memory store ============\n\n/**\n * Default in-memory history store. Keeps a ring buffer per group key.\n *\n * Suitable for single-process bots; for multi-instance deployments use\n * a Redis / SQL backed implementation.\n */\nexport class MemoryHistoryStore implements HistoryStore {\n private readonly buffers = new Map<string, HistoryEntry[]>();\n\n append(groupKey: string, entry: HistoryEntry, limit: number): void {\n let buf = this.buffers.get(groupKey);\n if (!buf) {\n buf = [];\n this.buffers.set(groupKey, buf);\n }\n // De-dup by messageId.\n if (buf.some((e) => e.messageId === entry.messageId)) {\n return;\n }\n buf.push(entry);\n if (buf.length > limit) {\n buf.splice(0, buf.length - limit);\n }\n }\n\n list(groupKey: string, limit: number): HistoryEntry[] {\n const buf = this.buffers.get(groupKey);\n if (!buf) return [];\n return buf.slice(-limit);\n }\n\n clear(groupKey: string): void {\n this.buffers.delete(groupKey);\n }\n\n /** Diagnostic: number of groups with buffered history. */\n size(): number {\n return this.buffers.size;\n }\n}\n\n// ============ Middleware ============\n\n/**\n * Build a history-buffer middleware.\n *\n * The middleware:\n * 1. Records the current group message into the store (before downstream).\n * 2. Exposes `ctx.state.history: HistoryEntry[]` to downstream middleware\n * and to the final `bot.on(\"message\")` listener (oldest → newest,\n * excluding the current message).\n *\n * @example\n * ```ts\n * const history = historyBuffer({ limit: 5 });\n * bot.use(mentionGate({ passthrough: true })); // record even on no-mention\n * bot.use(history);\n *\n * bot.on(\"message\", async (msg) => {\n * const buf = msg.history ?? [];\n * const prompt = buf.map(h => `${h.senderName}: ${h.content}`).join(\"\\n\");\n * // ...\n * });\n * ```\n */\nexport function historyBuffer(options: HistoryBufferOptions = {}): Middleware {\n const limit = options.limit ?? 50;\n const store = options.store ?? new MemoryHistoryStore();\n const recordOnSkip = options.recordOnSkip ?? true;\n const getKey =\n options.groupKey ??\n ((ctx) => (ctx.message.kind === \"group\" ? ctx.message.groupOpenid : undefined));\n\n const m: Middleware = async (ctx, next) => {\n const key = getKey(ctx);\n if (!key) {\n await next();\n return;\n }\n\n // Effective limit — priority: static option > ctx.state.policy > default 50\n const effectiveLimit = resolvePolicy(ctx, \"group.historyLimit\", limit, 50);\n\n // 1. List buffered (excluding current message)\n const buffered = await store.list(key, effectiveLimit);\n ctx.state.history = buffered;\n\n // 2. Record the current message (after exposing previous buffer)\n const entry: HistoryEntry = {\n senderId: ctx.message.senderId,\n senderName: ctx.message.senderName,\n content: ctx.message.content,\n timestamp: Date.parse(ctx.message.timestamp) || Date.now(),\n messageId: ctx.message.messageId,\n };\n\n try {\n await store.append(key, entry, effectiveLimit);\n } catch (err) {\n ctx.log.error?.(\n `[history-buffer] append failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n if (ctx.stopped && !recordOnSkip) {\n return;\n }\n\n await next();\n };\n\n return m;\n}\n","/**\n * Typing-indicator middleware — auto-send a `typing` notification for C2C\n * messages so the user sees a visual hint while the bot is processing.\n *\n * Supports keepalive mode: periodically re-send typing every `intervalSec`\n * to cover long-running AI inference (QQ platform typing window is ~60s).\n *\n * QQ Open Platform restriction: `sendInputNotify` only works for C2C\n * (private chat) targets. Group / Guild messages are silently skipped.\n */\n\nimport type { Middleware } from \"./types.js\";\n\n// ============ Options ============\n\nexport interface TypingIndicatorOptions {\n /** Indicator duration per send, in seconds. Defaults to 60. */\n durationSec?: number;\n /**\n * Only send typing for messages that pass `predicate(ctx)`. Defaults to\n * always (returns true).\n */\n predicate?: (ctx: import(\"./types.js\").MiddlewareContext) => boolean;\n /**\n * If `true`, the typing call is awaited before continuing the chain.\n * Defaults to `false` (fire-and-forget).\n */\n awaitTyping?: boolean;\n /**\n * Enable keepalive: re-send typing every `keepAliveIntervalMs` until\n * the downstream middleware chain completes. Defaults to `true`.\n */\n keepAlive?: boolean;\n /**\n * Keepalive interval in milliseconds. Defaults to 50_000 (50s).\n * Should be less than the platform's typing window (~60s).\n */\n keepAliveIntervalMs?: number;\n}\n\n// ============ Constants ============\n\nconst DEFAULT_DURATION_SEC = 60;\nconst DEFAULT_KEEPALIVE_INTERVAL_MS = 50_000;\n\n// ============ Middleware ============\n\n/**\n * Auto-send `typing` notification for inbound C2C messages.\n *\n * With keepalive enabled (default), typing is refreshed periodically\n * throughout the entire middleware chain + message handler execution,\n * covering long-running AI inference that may take 30s+.\n *\n * @example\n * ```ts\n * bot.use(typingIndicator()); // keepalive enabled by default\n * bot.on(\"message\", async (msg) => {\n * // User sees \"typing…\" continuously while the LLM is generating\n * const reply = await llm.chat(msg.content);\n * await bot.sendText(msg.replyTarget, reply);\n * });\n * ```\n */\nexport function typingIndicator(options: TypingIndicatorOptions = {}): Middleware {\n const durationSec = options.durationSec ?? DEFAULT_DURATION_SEC;\n const predicate = options.predicate ?? (() => true);\n const awaitTyping = options.awaitTyping ?? false;\n const keepAlive = options.keepAlive ?? true;\n const keepAliveIntervalMs = options.keepAliveIntervalMs ?? DEFAULT_KEEPALIVE_INTERVAL_MS;\n\n return async (ctx, next) => {\n if (ctx.message.kind !== \"c2c\" || !predicate(ctx)) {\n await next();\n return;\n }\n\n const sendTyping = () =>\n ctx.bot.sendTyping(ctx.replyTarget, durationSec).catch((err) => {\n ctx.log.debug?.(\n `[typing] failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n });\n\n // Send initial typing\n const promise = sendTyping();\n if (awaitTyping) {\n await promise;\n }\n\n // Setup keepalive timer\n let timer: ReturnType<typeof setInterval> | null = null;\n if (keepAlive) {\n timer = setInterval(() => {\n sendTyping();\n }, keepAliveIntervalMs);\n }\n\n try {\n await next();\n } finally {\n if (timer) {\n clearInterval(timer);\n }\n }\n };\n}\n","/**\n * Error-handler middleware — catch downstream errors and send a friendly reply.\n *\n * Add this **first** in the chain (before any other middleware) so it sees\n * errors from all downstream stages.\n *\n * @example\n * ```ts\n * bot.use(errorHandler({\n * format: (err) => `抱歉,处理消息时出错:${err.message}`,\n * }));\n * bot.use(slashCommand(...));\n * ```\n */\n\nimport { ApiError } from \"../protocol/types.js\";\nimport type { Middleware, MiddlewareContext } from \"./types.js\";\n\nexport interface ErrorHandlerOptions {\n /** Build a user-facing reply from the caught error. */\n format?: (err: Error, ctx: MiddlewareContext) => string;\n /**\n * If true, also re-throw the error so `bot.on(\"error\")` listeners fire.\n * Defaults to `false` (consume the error after replying).\n */\n rethrow?: boolean;\n /**\n * Filter — only handle errors that match. Defaults to all `Error`\n * instances. Useful to skip e.g. ApiError 4xx (let the user see them).\n */\n filter?: (err: Error) => boolean;\n}\n\nconst DEFAULT_FORMAT = (err: Error): string => {\n if (err instanceof ApiError) {\n if (err.bizMessage) return `[QQ ${err.bizCode ?? err.httpStatus}] ${err.bizMessage}`;\n return `[QQ ${err.httpStatus}] ${err.message}`;\n }\n return err.message || \"Unknown error\";\n};\n\n/**\n * Build an error-handler middleware. The middleware **wraps** the rest of\n * the chain — if any downstream middleware (or the `message` listener)\n * throws, it catches the error, sends a reply, and optionally re-emits.\n */\nexport function errorHandler(options: ErrorHandlerOptions = {}): Middleware {\n const format = options.format ?? DEFAULT_FORMAT;\n const rethrow = options.rethrow ?? false;\n const filter = options.filter ?? (() => true);\n\n return async (ctx, next) => {\n try {\n await next();\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n if (!filter(e)) {\n if (rethrow) throw e;\n return;\n }\n ctx.log.error?.(`[error-handler] caught: ${e.message}`);\n\n try {\n const reply = format(e, ctx);\n if (reply) {\n await ctx.bot.sendText(ctx.replyTarget, reply);\n }\n } catch (sendErr) {\n ctx.log.error?.(\n `[error-handler] failed to send error reply: ${sendErr instanceof Error ? sendErr.message : String(sendErr)}`,\n );\n }\n\n if (rethrow) throw e;\n }\n };\n}\n","/**\n * Generic key-value store interface — used by built-in pluggable backends.\n *\n * The SDK ships:\n * - {@link MemoryKVStore}: in-memory (default for tests / simple deployments)\n * - {@link FileKVStore}: JSON-file backed (single-process, simple deployments)\n *\n * Users can plug in Redis / SQL / cloud KV by implementing this same\n * interface. See `examples/storage-redis/` for a Redis adapter sketch.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\n// ============ Interface ============\n\n/**\n * Generic async KV store with optional TTL support.\n *\n * All methods may be sync or async; callers always `await` them.\n */\nexport interface KVStore {\n /** Get a value. Returns `undefined` if missing or expired. */\n get<T = unknown>(key: string): Promise<T | undefined> | T | undefined;\n /** Set a value with optional TTL (milliseconds). */\n set<T = unknown>(key: string, value: T, ttlMs?: number): Promise<void> | void;\n /** Delete a value. Returns true if it existed. */\n delete(key: string): Promise<boolean> | boolean;\n /** Check key existence (cheaper than `get` on some backends). */\n has?(key: string): Promise<boolean> | boolean;\n /** Iterate keys matching a prefix. Optional. */\n keys?(prefix?: string): Promise<string[]> | string[];\n /** Clear all keys (or those matching a prefix). Optional. */\n clear?(prefix?: string): Promise<void> | void;\n}\n\n// ============ Memory ============\n\ninterface MemoryEntry {\n value: unknown;\n expireAt?: number;\n}\n\n/**\n * In-memory KV store. Suitable for single-process deployments and tests.\n */\nexport class MemoryKVStore implements KVStore {\n private readonly map = new Map<string, MemoryEntry>();\n\n get<T = unknown>(key: string): T | undefined {\n const entry = this.map.get(key);\n if (!entry) return undefined;\n if (entry.expireAt && entry.expireAt <= Date.now()) {\n this.map.delete(key);\n return undefined;\n }\n return entry.value as T;\n }\n\n set<T = unknown>(key: string, value: T, ttlMs?: number): void {\n this.map.set(key, {\n value,\n expireAt: ttlMs && ttlMs > 0 ? Date.now() + ttlMs : undefined,\n });\n }\n\n delete(key: string): boolean {\n return this.map.delete(key);\n }\n\n has(key: string): boolean {\n const entry = this.map.get(key);\n if (!entry) return false;\n if (entry.expireAt && entry.expireAt <= Date.now()) {\n this.map.delete(key);\n return false;\n }\n return true;\n }\n\n keys(prefix?: string): string[] {\n const all = [...this.map.keys()];\n return prefix ? all.filter((k) => k.startsWith(prefix)) : all;\n }\n\n clear(prefix?: string): void {\n if (!prefix) {\n this.map.clear();\n return;\n }\n for (const k of this.map.keys()) {\n if (k.startsWith(prefix)) this.map.delete(k);\n }\n }\n\n /** Diagnostic: number of keys (incl. expired-but-not-yet-cleaned). */\n size(): number {\n return this.map.size;\n }\n}\n\n// ============ File-backed ============\n\nexport interface FileKVStoreOptions {\n /** Directory holding the JSON file. */\n dir: string;\n /** File name. Defaults to `kv-store.json`. */\n fileName?: string;\n /** Save throttle window, ms. Defaults to 1000. */\n saveThrottleMs?: number;\n /** Optional logger. */\n logger?: { error?: (msg: string) => void };\n}\n\ninterface FileEntry {\n value: unknown;\n expireAt?: number;\n}\n\n/**\n * JSON-file backed KV store.\n *\n * Loads on construct, throttles writes. Suitable for single-process bots\n * with light persistence needs (e.g. session id, refIdx).\n *\n * Not safe for multi-process / multi-instance use — use a real KV\n * (Redis, SQLite, ...) for that.\n */\nexport class FileKVStore implements KVStore {\n private readonly map = new Map<string, FileEntry>();\n private readonly filePath: string;\n private readonly saveThrottleMs: number;\n private saveTimer: ReturnType<typeof setTimeout> | null = null;\n private dirty = false;\n private readonly logger: NonNullable<FileKVStoreOptions[\"logger\"]>;\n\n constructor(opts: FileKVStoreOptions) {\n this.filePath = path.join(opts.dir, opts.fileName ?? \"kv-store.json\");\n this.saveThrottleMs = opts.saveThrottleMs ?? 1000;\n this.logger = opts.logger ?? {};\n this.load();\n }\n\n get<T = unknown>(key: string): T | undefined {\n const entry = this.map.get(key);\n if (!entry) return undefined;\n if (entry.expireAt && entry.expireAt <= Date.now()) {\n this.map.delete(key);\n this.scheduleSave();\n return undefined;\n }\n return entry.value as T;\n }\n\n set<T = unknown>(key: string, value: T, ttlMs?: number): void {\n this.map.set(key, {\n value,\n expireAt: ttlMs && ttlMs > 0 ? Date.now() + ttlMs : undefined,\n });\n this.scheduleSave();\n }\n\n delete(key: string): boolean {\n const removed = this.map.delete(key);\n if (removed) this.scheduleSave();\n return removed;\n }\n\n has(key: string): boolean {\n return this.get(key) !== undefined;\n }\n\n keys(prefix?: string): string[] {\n const all = [...this.map.keys()];\n return prefix ? all.filter((k) => k.startsWith(prefix)) : all;\n }\n\n clear(prefix?: string): void {\n if (!prefix) {\n this.map.clear();\n } else {\n for (const k of this.map.keys()) {\n if (k.startsWith(prefix)) this.map.delete(k);\n }\n }\n this.scheduleSave();\n }\n\n /** Force flush pending writes. Call before process exit. */\n flush(): void {\n if (this.saveTimer) {\n clearTimeout(this.saveTimer);\n this.saveTimer = null;\n }\n if (this.dirty) {\n this.doSave();\n }\n }\n\n // ============ Internal ============\n\n private load(): void {\n try {\n if (!fs.existsSync(this.filePath)) return;\n const raw = fs.readFileSync(this.filePath, \"utf-8\");\n const data = JSON.parse(raw) as Record<string, FileEntry>;\n const now = Date.now();\n for (const [k, e] of Object.entries(data)) {\n if (e.expireAt && e.expireAt <= now) continue;\n this.map.set(k, e);\n }\n } catch (err) {\n this.logger.error?.(\n `[file-kv-store] load failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n private scheduleSave(): void {\n this.dirty = true;\n if (this.saveTimer) return;\n this.saveTimer = setTimeout(() => {\n this.saveTimer = null;\n this.doSave();\n }, this.saveThrottleMs);\n }\n\n private doSave(): void {\n try {\n fs.mkdirSync(path.dirname(this.filePath), { recursive: true });\n const obj: Record<string, FileEntry> = {};\n for (const [k, e] of this.map) obj[k] = e;\n const tmp = this.filePath + \".tmp\";\n fs.writeFileSync(tmp, JSON.stringify(obj, null, 2), \"utf-8\");\n fs.renameSync(tmp, this.filePath);\n this.dirty = false;\n } catch (err) {\n this.logger.error?.(\n `[file-kv-store] save failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n}\n","/**\n * Bridge `KVStore` → `SessionPersistencePort`.\n *\n * Lets users plug any KV backend (Memory / File / Redis / ...) into the\n * gateway session persistence slot via a single line:\n *\n * ```ts\n * import { QQBot, kvSessionPersistence, FileKVStore } from \"@tencent-connect/qqbot-nodejs\";\n *\n * const bot = new QQBot({\n * appId, appSecret,\n * sessionPersistence: kvSessionPersistence({\n * store: new FileKVStore({ dir: \"./data\" }),\n * accountId: \"my-bot\",\n * }),\n * });\n * ```\n */\n\nimport type {\n PersistedSession,\n SessionPersistencePort,\n} from \"../protocol/gateway/gateway-connection.js\";\nimport type { KVStore } from \"./kv-store.js\";\n\nexport interface KVSessionPersistenceOptions {\n /** Underlying KV store. */\n store: KVStore;\n /** Stable account id used as the storage key. */\n accountId: string;\n /** Optional key prefix. Defaults to `qqbot:session:`. */\n prefix?: string;\n /** Optional TTL for the entry, ms. Defaults to 5 min (matches QQ session expiry). */\n ttlMs?: number;\n}\n\n/**\n * Build a {@link SessionPersistencePort} backed by any {@link KVStore}.\n */\nexport function kvSessionPersistence(opts: KVSessionPersistenceOptions): SessionPersistencePort {\n const prefix = opts.prefix ?? \"qqbot:session:\";\n const key = `${prefix}${opts.accountId}`;\n const ttlMs = opts.ttlMs ?? 5 * 60 * 1000;\n\n return {\n load() {\n const v = opts.store.get<PersistedSession>(key);\n // Note: KVStore.get may return Promise<>. The gateway port expects\n // a sync result, so callers using async stores should await\n // separately during startup (or use `awaitable` adapter, see TODO).\n if (v && typeof (v as Promise<PersistedSession>).then === \"function\") {\n // Async store — we can't synchronously resolve here; warn and\n // return null. Users should provide a sync store or warm-up\n // the session via `await kvWarmup(...)` before `bot.start()`.\n return null;\n }\n return (v as PersistedSession) ?? null;\n },\n save(session) {\n void opts.store.set(key, session, ttlMs);\n },\n clear() {\n void opts.store.delete(key);\n },\n };\n}\n","/**\n * 出站目标地址解析\n *\n * 将 OpenClaw 规范的目标地址字符串(如 qqbot:c2c:xxx / qqbot:group:xxx)\n * 转换为 SDK 的 ReplyTarget 结构。\n *\n * 也导出共享的正则常量供 channel.ts messaging 段复用。\n */\nimport type { ReplyTarget } from '@tencent-connect/qqbot-nodejs';\n\n// ── 共享正则常量 ──\n\n/** 32 位十六进制 OpenID(不带连字符) */\nexport const OPENID_HEX_RE = /^[0-9a-fA-F]{32}$/;\n\n/** UUID 格式的 OpenID(带连字符) */\nexport const OPENID_UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;\n\n/** 带 qqbot: 前缀的目标格式 */\nexport const QQBOT_PREFIX_RE = /^qqbot:(c2c|group|channel):/i;\n\n/** 不带前缀但有 scope 标识 */\nexport const SCOPE_PREFIX_RE = /^(c2c|group|channel):/i;\n\n/**\n * 判断 ID 是否看起来像 QQ Bot 目标格式\n */\nexport function isQQBotTarget(id: string): boolean {\n if (QQBOT_PREFIX_RE.test(id)) return true;\n if (SCOPE_PREFIX_RE.test(id)) return true;\n if (OPENID_HEX_RE.test(id)) return true;\n return OPENID_UUID_RE.test(id);\n}\n\n/**\n * 规范化目标地址字符串\n */\nexport function normalizeTarget(target: string): string | undefined {\n const id = target.replace(/^qqbot:/i, '');\n if (id.startsWith('c2c:') || id.startsWith('group:') || id.startsWith('channel:')) {\n return `qqbot:${id}`;\n }\n if (OPENID_HEX_RE.test(id)) return `qqbot:c2c:${id}`;\n if (OPENID_UUID_RE.test(id)) return `qqbot:c2c:${id}`;\n return undefined;\n}\n\n/**\n * 解析目标地址字符串为 SDK ReplyTarget\n */\nexport function parseTarget(to: string): ReplyTarget {\n const id = to.replace(/^qqbot:/i, '');\n\n if (id.startsWith('c2c:')) {\n return { scope: 'c2c', targetId: id.slice(4) };\n }\n if (id.startsWith('group:')) {\n return { scope: 'group', targetId: id.slice(6) };\n }\n\n // 默认当作 c2c(32 位十六进制 / UUID 格式的 openid)\n return { scope: 'c2c', targetId: id };\n}\n","/**\n * 被动回复限额管理\n *\n * QQ Bot 被动回复有限额(同一条消息最多回复 N 次,超时后不能再被动回复)。\n * 当限额耗尽或消息过期时,标记应降级为主动消息(proactive)。\n */\n\nexport interface ReplyLimiterConfig {\n /** 每条消息最大被动回复次数(默认 4) */\n limit?: number;\n /** 消息 ID 过期时间 ms(默认 1 小时) */\n ttlMs?: number;\n /** 最大跟踪消息数(LRU 驱逐,默认 10000) */\n maxTrackedMessages?: number;\n}\n\nexport interface ReplyLimitResult {\n allowed: boolean;\n remaining: number;\n shouldFallbackToProactive: boolean;\n fallbackReason?: 'expired' | 'limit_exceeded';\n}\n\ninterface TrackedMessage {\n count: number;\n firstSeenAt: number;\n}\n\n/**\n * 被动回复限额管理器\n *\n * @example\n * ```ts\n * const limiter = new ReplyLimiter({ limit: 4, ttlMs: 3600_000 });\n * const result = limiter.checkLimit(messageId);\n * if (!result.allowed) {\n * // 降级为主动消息\n * } else {\n * limiter.record(messageId);\n * // 正常被动回复\n * }\n * ```\n */\nexport class ReplyLimiter {\n private readonly limit: number;\n private readonly ttlMs: number;\n private readonly maxTracked: number;\n private readonly messages = new Map<string, TrackedMessage>();\n\n constructor(config?: ReplyLimiterConfig) {\n this.limit = config?.limit ?? 4;\n this.ttlMs = config?.ttlMs ?? 3600_000; // 1 小时\n this.maxTracked = config?.maxTrackedMessages ?? 10_000;\n }\n\n /**\n * 检查是否允许对指定消息继续被动回复\n */\n checkLimit(messageId: string): ReplyLimitResult {\n const now = Date.now();\n const tracked = this.messages.get(messageId);\n\n if (!tracked) {\n return { allowed: true, remaining: this.limit, shouldFallbackToProactive: false };\n }\n\n // TTL 过期\n if (now - tracked.firstSeenAt > this.ttlMs) {\n this.messages.delete(messageId);\n return {\n allowed: false,\n remaining: 0,\n shouldFallbackToProactive: true,\n fallbackReason: 'expired',\n };\n }\n\n // 限额检查\n const remaining = Math.max(0, this.limit - tracked.count);\n if (remaining <= 0) {\n return {\n allowed: false,\n remaining: 0,\n shouldFallbackToProactive: true,\n fallbackReason: 'limit_exceeded',\n };\n }\n\n return { allowed: true, remaining, shouldFallbackToProactive: false };\n }\n\n /**\n * 记录一次被动回复\n */\n record(messageId: string): void {\n const now = Date.now();\n const tracked = this.messages.get(messageId);\n\n if (tracked) {\n tracked.count++;\n } else {\n // LRU 驱逐\n if (this.messages.size >= this.maxTracked) {\n const firstKey = this.messages.keys().next().value;\n if (firstKey) this.messages.delete(firstKey);\n }\n this.messages.set(messageId, { count: 1, firstSeenAt: now });\n }\n }\n\n /**\n * 获取统计信息\n */\n getStats(): { trackedMessages: number; totalReplies: number } {\n let totalReplies = 0;\n for (const [, v] of this.messages) {\n totalReplies += v.count;\n }\n return { trackedMessages: this.messages.size, totalReplies };\n }\n\n /**\n * 清除所有跟踪数据\n */\n clear(): void {\n this.messages.clear();\n }\n}\n","/**\n * 版本号获取工具。\n *\n * - getPackageVersion(): 插件自身版本(@tencent-connect/openclaw-qqbot)\n * - getOpenclawVersion(): OpenClaw 框架版本降级链\n *\n * 兼容 CJS(tsup bundle)环境,不依赖 require.resolve。\n */\n\nimport path from \"node:path\";\nimport fs from \"node:fs\";\n\n/** OpenClaw 框架版本缓存(一次查完不再重复 io) */\nlet _cachedOpenclawVersion: string | undefined;\n\ndeclare const __PLUGIN_VERSION__: string;\n\n/**\n * 获取插件自身版本号。\n * 编译时由 tsup define 注入,零运行时 IO。\n */\nexport function getPackageVersion(): string {\n return typeof __PLUGIN_VERSION__ !== 'undefined' ? __PLUGIN_VERSION__ : 'unknown';\n}\n\n// ── OpenClaw 框架版本 ──\n\n/**\n * OpenClaw 框架版本降级链(优先后)。\n * 1. PluginRuntime.version(3.31+,非 \"unknown\")\n * 2. OPENCLAW_VERSION / OPENCLAW_SERVICE_VERSION 环境变量\n * 3. 文件系统搜索 openclaw/package.json\n * 4. 兜底 \"unknown\"\n */\nexport function getOpenclawVersion(runtimeVersion?: string): string {\n if (_cachedOpenclawVersion) return _cachedOpenclawVersion;\n\n // 1. runtime.version(排除兜底值)\n if (runtimeVersion && runtimeVersion !== \"unknown\") {\n return _cachedOpenclawVersion = runtimeVersion;\n }\n // 2. 环境变量\n if (process.env.OPENCLAW_VERSION) {\n return _cachedOpenclawVersion = process.env.OPENCLAW_VERSION;\n }\n if (process.env.OPENCLAW_SERVICE_VERSION) {\n return _cachedOpenclawVersion = process.env.OPENCLAW_SERVICE_VERSION;\n }\n // 3. 文件系统\n const pkgVer = readOpenclawPackageVersion();\n if (pkgVer) return _cachedOpenclawVersion = pkgVer;\n\n return \"unknown\";\n}\n\nfunction readOpenclawPackageVersion(): string | undefined {\n try {\n const dirs = searchRoots();\n for (const dir of dirs) {\n const pkgPath = path.join(dir, \"package.json\");\n try {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf8\")) as { name?: string; version?: string };\n if (pkg.name === \"openclaw\" && pkg.version) return pkg.version;\n } catch { /* next */ }\n }\n } catch { /* fallthrough */ }\n return undefined;\n}\n\nfunction searchRoots(): string[] {\n const roots: string[] = [];\n\n // 从插件自身路径向上查找\n if (typeof __filename === \"string\") {\n let dir = path.dirname(__filename);\n for (let i = 0; i < 10; i++) {\n roots.push(dir);\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n }\n\n // STATE_DIR 指向的 openclaw 安装目录附近\n for (const key of [\"OPENCLAW_STATE_DIR\", \"CLAWDBOT_STATE_DIR\", \"MOLTBOT_STATE_DIR\"]) {\n const v = process.env[key];\n if (v) {\n roots.push(path.dirname(v));\n roots.push(path.resolve(v, \"..\"));\n }\n }\n\n // 常见安装路径\n const home = process.env.HOME || process.env.USERPROFILE || \"/tmp\";\n for (const name of [\"openclaw\", \"clawdbot\", \"moltbot\"]) {\n roots.push(path.join(home, `.${name}`));\n }\n roots.push(process.cwd());\n\n return [...new Set(roots.filter((r) => typeof r === \"string\" && r.length > 0))];\n}\n","/**\n * 持久化 RefIndex 存储\n *\n * SDK 提供的 MemoryRefIndexStore 在进程重启后即丢失,\n * 而 QQ 引用消息(REFIDX_xxx)入站事件只携带 key,必须本地缓存才能回查。\n *\n * 设计:\n * - 内存 LRU(与 SDK 同款)保证 O(1) 读取\n * - JSONL 追加写持久化,进程重启时按时间顺序回放重建 LRU\n * - 写入触发 compact 阈值时重写文件(去重 + 截断到 maxEntries)\n * - 文件路径: ~/.openclaw/qqbot/data/ref-index.jsonl\n *\n * 实现 SDK 的 RefIndexStore 接口,可直接通过 `quoteRef({ store })` 注入。\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { RefEntry, RefIndexStore } from '@tencent-connect/qqbot-nodejs';\nimport { getQQBotDataDir } from '../utils/platform.js';\nimport { createPluginLogger } from '../utils/plugin-logger.js';\nconst log = createPluginLogger({ prefix: '[ref-index]' });\n\n\n// ── 常量 ──\n\nconst DEFAULT_MAX_ENTRIES = 50000;\nconst DEFAULT_FILENAME = 'ref-index.jsonl';\n/** 当磁盘 line 数超过 maxEntries * COMPACT_RATIO 时触发 compact */\nconst COMPACT_RATIO = 2;\n/** 内存 LRU 的最小容量保护值 */\nconst MIN_MAX_ENTRIES = 100;\n\n// ── 磁盘行结构 ──\n\ninterface DiskLine {\n /** RefIndex 键,例如 REFIDX_xxx 或 messageId */\n k: string;\n /** RefEntry 内容 */\n v: RefEntry;\n /** 写入时间戳(毫秒) */\n t: number;\n}\n\n// ── 持久化 Store ──\n\nexport interface PersistedRefIndexStoreOptions {\n /** 内存与磁盘的最大条目数。默认 2000。 */\n maxEntries?: number;\n /** 自定义存储文件路径。默认 ~/.openclaw/qqbot/data/ref-index.jsonl */\n filePath?: string;\n}\n\n/**\n * 持久化版本的 RefIndexStore\n *\n * - get:仅查内存(启动时从磁盘回放重建)\n * - set:内存 + JSONL 追加写;磁盘行数过多时触发 compact\n */\nexport class PersistedRefIndexStore implements RefIndexStore {\n private readonly memory = new Map<string, RefEntry>();\n private readonly maxEntries: number;\n private readonly filePath: string;\n /** 当前磁盘累计写入的行数(用于 compact 阈值判断) */\n private diskLineCount = 0;\n /** 是否已成功初始化(磁盘回放完成) */\n private initialized = false;\n /** 串行化写入,防止并发 append 撕裂行 */\n private writeChain: Promise<void> = Promise.resolve();\n\n constructor(options: PersistedRefIndexStoreOptions = {}) {\n this.maxEntries = Math.max(options.maxEntries ?? DEFAULT_MAX_ENTRIES, MIN_MAX_ENTRIES);\n this.filePath = options.filePath ?? path.join(getQQBotDataDir('data'), DEFAULT_FILENAME);\n this.init();\n }\n\n /**\n * 初始化:按时间顺序回放 JSONL 重建内存 LRU\n */\n private init(): void {\n try {\n if (!fs.existsSync(this.filePath)) {\n this.initialized = true;\n return;\n }\n const raw = fs.readFileSync(this.filePath, 'utf8');\n const lines = raw.split('\\n').filter((l) => l.length > 0);\n this.diskLineCount = lines.length;\n\n // 按时间排序回放(防止文件被外部修改后乱序)\n const parsed: DiskLine[] = [];\n for (const line of lines) {\n try {\n const obj = JSON.parse(line) as DiskLine;\n if (obj?.k && obj?.v) {\n parsed.push(obj);\n }\n } catch {\n // 跳过损坏行\n }\n }\n parsed.sort((a, b) => a.t - b.t);\n\n for (const { k, v } of parsed) {\n this.touchMemory(k, v);\n }\n\n // 启动时若磁盘行数已超阈值,立刻 compact 一次\n if (this.diskLineCount > this.maxEntries * COMPACT_RATIO) {\n this.compactSync();\n }\n } catch (err) {\n log.error(\n `init failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n } finally {\n this.initialized = true;\n }\n }\n\n // ── RefIndexStore 接口 ──\n\n get(key: string): RefEntry | undefined {\n return this.memory.get(key);\n }\n\n set(key: string, entry: RefEntry): void {\n this.touchMemory(key, entry);\n // 追加写持久化(串行化)\n this.writeChain = this.writeChain.then(() => this.appendToDisk(key, entry));\n }\n\n // ── 内部:内存 LRU 维护 ──\n\n private touchMemory(key: string, entry: RefEntry): void {\n // 已存在则先删除再插入,保证插入顺序 = LRU 顺序\n if (this.memory.has(key)) {\n this.memory.delete(key);\n } else if (this.memory.size >= this.maxEntries) {\n // 容量已满 → 淘汰最旧的(Map 的迭代顺序即插入顺序)\n const oldest = this.memory.keys().next().value;\n if (oldest !== undefined) {\n this.memory.delete(oldest);\n }\n }\n this.memory.set(key, entry);\n }\n\n // ── 内部:磁盘追加 + compact ──\n\n private async appendToDisk(key: string, entry: RefEntry): Promise<void> {\n const line: DiskLine = { k: key, v: entry, t: Date.now() };\n const text = JSON.stringify(line) + '\\n';\n try {\n await fs.promises.appendFile(this.filePath, text, 'utf8');\n this.diskLineCount += 1;\n if (this.diskLineCount > this.maxEntries * COMPACT_RATIO) {\n await this.compact();\n }\n } catch (err) {\n log.error(\n `append failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n /**\n * 异步 compact:将内存 LRU 状态完整重写到磁盘,丢弃历史冗余。\n */\n private async compact(): Promise<void> {\n const tmpPath = `${this.filePath}.tmp`;\n try {\n const now = Date.now();\n // Map 迭代顺序 = 插入顺序(旧 → 新),保持 LRU 顺序\n const lines: string[] = [];\n for (const [k, v] of this.memory.entries()) {\n lines.push(JSON.stringify({ k, v, t: now } satisfies DiskLine));\n }\n const content = lines.length > 0 ? lines.join('\\n') + '\\n' : '';\n await fs.promises.writeFile(tmpPath, content, 'utf8');\n await fs.promises.rename(tmpPath, this.filePath);\n this.diskLineCount = lines.length;\n log.info(\n `compacted to ${lines.length} entries`,\n );\n } catch (err) {\n log.error(\n `compact failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n // 清理可能残留的 tmp 文件\n try {\n await fs.promises.unlink(tmpPath);\n } catch {\n // ignore\n }\n }\n }\n\n /**\n * 同步 compact(仅 init 阶段使用,避免回放后立刻保留巨大磁盘文件)\n */\n private compactSync(): void {\n const tmpPath = `${this.filePath}.tmp`;\n try {\n const now = Date.now();\n const lines: string[] = [];\n for (const [k, v] of this.memory.entries()) {\n lines.push(JSON.stringify({ k, v, t: now } satisfies DiskLine));\n }\n const content = lines.length > 0 ? lines.join('\\n') + '\\n' : '';\n fs.writeFileSync(tmpPath, content, 'utf8');\n fs.renameSync(tmpPath, this.filePath);\n this.diskLineCount = lines.length;\n } catch (err) {\n log.error(\n `compactSync failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n // ── 诊断 ──\n\n /** 当前内存中的条目数 */\n get size(): number {\n return this.memory.size;\n }\n\n /** 是否已完成初始化(磁盘回放) */\n get isInitialized(): boolean {\n return this.initialized;\n }\n\n /** 诊断快照 */\n stats(): {\n memoryEntries: number;\n diskLines: number;\n maxEntries: number;\n filePath: string;\n } {\n return {\n memoryEntries: this.memory.size,\n diskLines: this.diskLineCount,\n maxEntries: this.maxEntries,\n filePath: this.filePath,\n };\n }\n\n /**\n * 强制将当前内存状态持久化到磁盘(进程退出前调用)\n */\n flush(): void {\n this.compactSync();\n }\n}\n\n// ── 默认单例(按 accountId 隔离) ──\n\nconst stores = new Map<string, PersistedRefIndexStore>();\n\n/**\n * 获取按 accountId 隔离的持久化 RefIndexStore 单例。\n *\n * 每个账户独立存储文件,避免多账户混用同一个 refIdx 命名空间。\n */\n\nexport function getPersistedRefIndexStore(accountId: string): PersistedRefIndexStore {\n let store = stores.get(accountId);\n if (!store) {\n const filePath = path.join(getQQBotDataDir('data', accountId), DEFAULT_FILENAME);\n store = new PersistedRefIndexStore({ filePath });\n stores.set(accountId, store);\n }\n return store;\n}\n\n/**\n * 进程退出前 flush 所有 store\n */\nexport function flushAllRefIndexStores(): void {\n for (const store of stores.values()) {\n store.flush();\n }\n}\n","/**\n * 跨平台兼容工具\n *\n * 统一 Mac / Linux / Windows 三大系统的:\n * - 用户主目录获取\n * - 临时目录获取\n * - 本地路径判断\n * - ffmpeg / ffprobe 可执行文件路径\n * - silk-wasm 原生模块兼容性检测\n * - 启动诊断报告\n */\n\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport * as fs from \"node:fs\";\n\n// ============ 基础平台信息 ============\n\nexport type PlatformType = \"darwin\" | \"linux\" | \"win32\" | \"other\";\n\nexport function getPlatform(): PlatformType {\n const p = process.platform;\n if (p === \"darwin\" || p === \"linux\" || p === \"win32\") return p;\n return \"other\";\n}\n\nexport function isWindows(): boolean {\n return process.platform === \"win32\";\n}\n\n// ============ 用户主目录 ============\n\n/**\n * 安全获取用户主目录\n *\n * 优先级:\n * 1. os.homedir()(Node 原生,所有平台)\n * 2. $HOME(Mac/Linux)或 %USERPROFILE%(Windows)\n * 3. 降级到 /tmp(Linux/Mac)或 os.tmpdir()(Windows)\n *\n * 与之前 `process.env.HOME || \"/home/ubuntu\"` 的硬编码相比,\n * 现在能正确处理 Windows 和非 ubuntu 用户。\n */\nexport function getHomeDir(): string {\n try {\n const home = os.homedir();\n if (home && fs.existsSync(home)) return home;\n } catch {}\n\n // fallback 环境变量\n const envHome = process.env.HOME || process.env.USERPROFILE;\n if (envHome && fs.existsSync(envHome)) return envHome;\n\n // 最后降级\n return os.tmpdir();\n}\n\n/**\n * 获取 .openclaw/qqbot 下的子目录路径,并自动创建\n * 替代各文件中分散的 path.join(HOME, \".openclaw\", \"qqbot\", ...)\n */\nexport function getQQBotDataDir(...subPaths: string[]): string {\n const dir = path.join(getHomeDir(), \".openclaw\", \"qqbot\", ...subPaths);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n return dir;\n}\n\n/**\n * 获取 .openclaw/media/qqbot 下的子目录路径,并自动创建\n *\n * 与 getQQBotDataDir 不同,此目录位于 OpenClaw 核心的媒体安全白名单\n * (~/.openclaw/media) 之下,下载到这里的文件可以被框架的 image/media\n * 工具直接访问,不会触发 \"Local media path is not under an allowed directory\" 错误。\n *\n * 用于存放从 QQ 下载的图片、语音等需要被框架处理的媒体文件。\n */\nexport function getQQBotMediaDir(...subPaths: string[]): string {\n const dir = path.join(getHomeDir(), \".openclaw\", \"media\", \"qqbot\", ...subPaths);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n return dir;\n}\n\n// ============ 临时目录 ============\n\n/**\n * 获取系统临时目录(跨平台安全)\n * Mac: /var/folders/... 或 /tmp\n * Linux: /tmp\n * Windows: %TEMP% 或 C:\\Users\\xxx\\AppData\\Local\\Temp\n */\nexport function getTempDir(): string {\n return os.tmpdir();\n}\n\n// ============ 波浪线路径展开 ============\n\n/**\n * 展开路径中的波浪线(~)为用户主目录\n *\n * Mac/Linux 用户经常使用 `~/Desktop/file.png` 这样的路径,\n * 但 Node.js 的 fs 模块不会像 shell 一样自动展开 `~`。\n *\n * 支持:\n * - `~/xxx` → `/Users/you/xxx`(Mac)或 `/home/you/xxx`(Linux)\n * - `~` → `/Users/you`\n * - 非 `~` 开头的路径原样返回\n *\n * 注意: 不支持 `~otheruser/xxx` 语法(极少使用,且需要系统调用获取其他用户信息)\n */\nexport function expandTilde(p: string): string {\n if (!p) return p;\n if (p === \"~\") return getHomeDir();\n if (p.startsWith(\"~/\") || p.startsWith(\"~\\\\\")) {\n return path.join(getHomeDir(), p.slice(2));\n }\n return p;\n}\n\n/**\n * 对路径进行完整的规范化处理:剥离 file:// 前缀 + 展开波浪线 + 去除首尾空白\n * 所有文件操作前应通过此函数处理用户输入的路径\n */\nexport function normalizePath(p: string): string {\n let result = p.trim();\n // 剥离 file:// 协议前缀: file:///Users/... → /Users/...\n if (result.startsWith(\"file://\")) {\n result = result.slice(\"file://\".length);\n // 处理 URL 编码(file:// 路径中空格等字符可能被编码)\n try {\n result = decodeURIComponent(result);\n } catch {\n // decodeURIComponent 失败时保留原样\n }\n }\n return expandTilde(result);\n}\n\n// ============ 文件名 UTF-8 规范化 ============\n\n/**\n * 规范化文件名为 QQ Bot API 要求的 UTF-8 编码格式\n *\n * 问题场景:\n * - macOS HFS+/APFS 文件系统使用 NFD(Unicode 分解形式)存储文件名,\n * 例如「中文.txt」被分解为多个码点,QQ Bot API 可能拒绝\n * - 文件名可能包含 API 不接受的特殊控制字符\n * - URL 路径中可能包含 percent-encoded 的文件名需要解码\n *\n * 处理:\n * 1. Unicode NFC 规范化(将 NFD 分解形式合并为 NFC 组合形式)\n * 2. 去除 ASCII 控制字符(0x00-0x1F, 0x7F)\n * 3. 去除首尾空白\n * 4. 对 percent-encoded 的文件名尝试 URI 解码\n */\nexport function sanitizeFileName(name: string): string {\n if (!name) return name;\n\n let result = name.trim();\n\n // 尝试 URI 解码(处理 URL 中 percent-encoded 的中文文件名)\n // 例如 %E4%B8%AD%E6%96%87.txt → 中文.txt\n if (result.includes(\"%\")) {\n try {\n result = decodeURIComponent(result);\n } catch {\n // 解码失败(非合法 percent-encoding),保留原始值\n }\n }\n\n // Unicode NFC 规范化:将 macOS NFD 分解形式合并为标准 NFC 组合形式\n result = result.normalize(\"NFC\");\n\n // 去除 ASCII 控制字符(保留所有可打印字符和非 ASCII Unicode 字符)\n result = result.replace(/[\\x00-\\x1F\\x7F]/g, \"\");\n\n return result;\n}\n\n// ============ 本地路径判断 ============\n\n/**\n * 判断字符串是否为本地文件路径(非 URL)\n *\n * 覆盖:\n * - Unix 绝对路径: /Users/..., /home/..., /tmp/...\n * - Windows 绝对路径: C:\\..., D:/..., \\\\server\\share\n * - 相对路径: ./file, ../file\n * - 波浪线路径: ~/Desktop/file.png\n * - file:// 协议: file:///Users/..., file:///home/...\n *\n * 不匹配:\n * - http:// / https:// URL\n * - data: URL\n */\nexport function isLocalPath(p: string): boolean {\n if (!p) return false;\n // file:// 协议(本地文件 URI)\n if (p.startsWith(\"file://\")) return true;\n // 波浪线路径(Mac/Linux 用户常用)\n if (p === \"~\" || p.startsWith(\"~/\") || p.startsWith(\"~\\\\\")) return true;\n // Unix 绝对路径\n if (p.startsWith(\"/\")) return true;\n // Windows 盘符: C:\\ 或 C:/\n if (/^[a-zA-Z]:[\\\\/]/.test(p)) return true;\n // Windows UNC: \\\\server\\share\n if (p.startsWith(\"\\\\\\\\\")) return true;\n // 相对路径\n if (p.startsWith(\"./\") || p.startsWith(\"../\")) return true;\n // Windows 相对路径\n if (p.startsWith(\".\\\\\") || p.startsWith(\"..\\\\\")) return true;\n return false;\n}\n\n/**\n * 判断 markdown 中提取的路径是否像本地路径\n * 比 isLocalPath 更宽松,用于从 markdown ![](path) 中检测误用\n */\nexport function looksLikeLocalPath(p: string): boolean {\n if (isLocalPath(p)) return true;\n // 常见系统目录前缀(不以 / 开头时也匹配)\n return /^(?:Users|home|tmp|var|private|[A-Z]:)/i.test(p);\n}\n\n// ============ ffmpeg 跨平台检测 ============\n\nlet _ffmpegPath: string | null | undefined; // undefined = 未检测, null = 不可用\nlet _ffmpegCheckPromise: Promise<string | null> | null = null;\n\n/**\n * 检测 ffmpeg 是否可用,返回可执行路径\n *\n * Windows 上检测 ffmpeg.exe,Mac/Linux 检测 ffmpeg\n * 支持通过环境变量 FFMPEG_PATH 指定自定义路径\n *\n * @returns ffmpeg 可执行文件路径,不可用返回 null\n */\nexport function detectFfmpeg(): Promise<string | null> {\n if (_ffmpegPath !== undefined) return Promise.resolve(_ffmpegPath);\n if (_ffmpegCheckPromise) return _ffmpegCheckPromise;\n\n _ffmpegCheckPromise = (async () => {\n // 1. 环境变量自定义路径\n const envPath = process.env.FFMPEG_PATH;\n if (envPath) {\n const ok = await testExecutable(envPath, [\"-version\"]);\n if (ok) {\n _ffmpegPath = envPath;\n console.log(`[platform] ffmpeg found via FFMPEG_PATH: ${envPath}`);\n return _ffmpegPath;\n }\n console.warn(`[platform] FFMPEG_PATH set but not working: ${envPath}`);\n }\n\n // 2. 系统 PATH 中检测\n const cmd = isWindows() ? \"ffmpeg.exe\" : \"ffmpeg\";\n const ok = await testExecutable(cmd, [\"-version\"]);\n if (ok) {\n _ffmpegPath = cmd;\n console.log(`[platform] ffmpeg detected in PATH`);\n return _ffmpegPath;\n }\n\n // 3. 常见安装位置(Mac brew、Windows choco/scoop)\n const commonPaths = isWindows()\n ? [\n \"C:\\\\ffmpeg\\\\bin\\\\ffmpeg.exe\",\n path.join(process.env.LOCALAPPDATA || \"\", \"Programs\", \"ffmpeg\", \"bin\", \"ffmpeg.exe\"),\n path.join(process.env.ProgramFiles || \"\", \"ffmpeg\", \"bin\", \"ffmpeg.exe\"),\n ]\n : [\n \"/usr/local/bin/ffmpeg\", // Mac brew\n \"/opt/homebrew/bin/ffmpeg\", // Mac ARM brew\n \"/usr/bin/ffmpeg\", // Linux apt\n \"/snap/bin/ffmpeg\", // Linux snap\n ];\n\n for (const p of commonPaths) {\n if (p && fs.existsSync(p)) {\n const works = await testExecutable(p, [\"-version\"]);\n if (works) {\n _ffmpegPath = p;\n console.log(`[platform] ffmpeg found at: ${p}`);\n return _ffmpegPath;\n }\n }\n }\n\n _ffmpegPath = null;\n return null;\n })().finally(() => {\n _ffmpegCheckPromise = null;\n });\n\n return _ffmpegCheckPromise;\n}\n\n/** 测试可执行文件是否在系统 PATH 中可访问 */\nfunction testExecutable(cmd: string, _args: string[]): Promise<boolean> {\n return new Promise((resolve) => {\n // 直接路径检查\n if (path.isAbsolute(cmd) || cmd.includes(\"/\") || cmd.includes(\"\\\\\")) {\n try {\n fs.accessSync(cmd, fs.constants.X_OK);\n resolve(true);\n return;\n } catch {\n resolve(false);\n return;\n }\n }\n\n // PATH 查找\n const pathEnv = process.env.PATH || \"\";\n const paths = pathEnv.split(path.delimiter);\n for (const p of paths) {\n const cmdPath = path.join(p, cmd);\n try {\n fs.accessSync(cmdPath, fs.constants.X_OK);\n resolve(true);\n return;\n } catch {\n // 继续搜索\n }\n }\n resolve(false);\n });\n}\n\n/** 重置 ffmpeg 缓存(用于测试) */\nexport function resetFfmpegCache(): void {\n _ffmpegPath = undefined;\n _ffmpegCheckPromise = null;\n}\n\n// ============ silk-wasm 兼容性 ============\n\nlet _silkWasmAvailable: boolean | null = null;\n\n/**\n * 检测 silk-wasm 是否可用\n *\n * silk-wasm 依赖 WASM 运行时,在某些环境(如老版本 Node、某些容器)可能不可用。\n * 提前检测避免运行时崩溃。\n */\nexport async function checkSilkWasmAvailable(): Promise<boolean> {\n if (_silkWasmAvailable !== null) return _silkWasmAvailable;\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const silkWasm = await import(\"silk-wasm\" as string) as any;\n const isSilk = silkWasm.isSilk;\n // 用一个空 buffer 快速测试 WASM 是否能加载\n isSilk(new Uint8Array(0));\n _silkWasmAvailable = true;\n console.log(\"[platform] silk-wasm: available\");\n } catch (err) {\n _silkWasmAvailable = false;\n console.warn(`[platform] silk-wasm: NOT available (${err instanceof Error ? err.message : String(err)})`);\n }\n return _silkWasmAvailable;\n}\n\n// ============ 启动环境诊断 ============\n\nexport interface DiagnosticReport {\n platform: string;\n arch: string;\n nodeVersion: string;\n homeDir: string;\n tempDir: string;\n dataDir: string;\n ffmpeg: string | null;\n silkWasm: boolean;\n warnings: string[];\n}\n\n/**\n * 运行启动诊断,返回环境报告\n * 在 gateway 启动时调用,打印环境信息并给出警告\n */\nexport async function runDiagnostics(): Promise<DiagnosticReport> {\n const warnings: string[] = [];\n\n const platform = `${process.platform} (${os.release()})`;\n const arch = process.arch;\n const nodeVersion = process.version;\n const homeDir = getHomeDir();\n const tempDir = getTempDir();\n const dataDir = getQQBotDataDir();\n\n // 检测 ffmpeg\n const ffmpegPath = await detectFfmpeg();\n if (!ffmpegPath) {\n warnings.push(\n isWindows()\n ? \"⚠️ ffmpeg 未安装。语音/视频格式转换将受限。安装方式: choco install ffmpeg 或 scoop install ffmpeg 或从 https://ffmpeg.org 下载\"\n : getPlatform() === \"darwin\"\n ? \"⚠️ ffmpeg 未安装。语音/视频格式转换将受限。安装方式: brew install ffmpeg\"\n : \"⚠️ ffmpeg 未安装。语音/视频格式转换将受限。安装方式: sudo apt install ffmpeg 或 sudo yum install ffmpeg\"\n );\n }\n\n // 检测 silk-wasm\n const silkWasm = await checkSilkWasmAvailable();\n if (!silkWasm) {\n warnings.push(\"⚠️ silk-wasm 不可用。QQ 语音消息的收发将无法工作。请确认 Node.js 版本 >= 16 且 WASM 支持正常\");\n }\n\n // 检查数据目录可写性\n try {\n const testFile = path.join(dataDir, \".write-test\");\n fs.writeFileSync(testFile, \"test\");\n fs.unlinkSync(testFile);\n } catch {\n warnings.push(`⚠️ 数据目录不可写: ${dataDir}。请检查权限`);\n }\n\n // Windows 特殊提醒\n if (isWindows()) {\n // 检查路径中是否有中文或空格(可能导致某些工具异常)\n if (/[\\u4e00-\\u9fa5]/.test(homeDir) || homeDir.includes(\" \")) {\n warnings.push(`⚠️ 用户目录包含中文或空格: ${homeDir}。某些工具可能无法正常工作,建议设置 QQBOT_DATA_DIR 环境变量指定纯英文路径`);\n }\n }\n\n const report: DiagnosticReport = {\n platform,\n arch,\n nodeVersion,\n homeDir,\n tempDir,\n dataDir,\n ffmpeg: ffmpegPath,\n silkWasm,\n warnings,\n };\n\n // 打印诊断报告\n console.log(\"=== QQBot 环境诊断 ===\");\n console.log(` 平台: ${platform} (${arch})`);\n console.log(` Node: ${nodeVersion}`);\n console.log(` 主目录: ${homeDir}`);\n console.log(` 数据目录: ${dataDir}`);\n console.log(` ffmpeg: ${ffmpegPath ?? \"未安装\"}`);\n console.log(` silk-wasm: ${silkWasm ? \"可用\" : \"不可用\"}`);\n if (warnings.length > 0) {\n console.log(\" --- 警告 ---\");\n for (const w of warnings) {\n console.log(` ${w}`);\n }\n }\n console.log(\"======================\");\n\n return report;\n}\n","/**\n * 请求级上下文(基于 AsyncLocalStorage)\n *\n * 解决并发消息下工具获取当前会话信息的竞态问题。\n * gateway 在处理每条入站消息时通过 runWithRequestContext() 建立作用域,\n * 作用域内的所有异步代码(包括 AI agent 调用、tool execute)\n * 都能通过 getRequestContext() 安全地拿到当前请求的上下文。\n */\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\nexport interface RequestContext {\n /** 投递目标地址,如 qqbot:c2c:xxx 或 qqbot:group:xxx */\n target: string;\n /** 当前请求的 QQBot 账户 ID(多账户场景) */\n accountId?: string;\n /** 当前消息 ID */\n messageId?: string;\n /** 发送者 open_id */\n openId?: string;\n}\n\nconst asyncLocalStorage = new AsyncLocalStorage<RequestContext>();\n\n/**\n * 在请求级作用域中执行回调\n */\nexport function runWithRequestContext<T>(ctx: RequestContext, fn: () => T): T {\n return asyncLocalStorage.run(ctx, fn);\n}\n\n/**\n * 获取当前请求的上下文\n */\nexport function getRequestContext(): RequestContext | undefined {\n return asyncLocalStorage.getStore();\n}\n\n/**\n * 获取当前请求的投递目标地址\n */\nexport function getRequestTarget(): string | undefined {\n return asyncLocalStorage.getStore()?.target;\n}\n\n/**\n * 获取当前请求的账户 ID\n */\nexport function getRequestAccountId(): string | undefined {\n return asyncLocalStorage.getStore()?.accountId;\n}\n","/**\n * PluginLogger — 统一日志接口\n *\n * 默认后端为 OpenClaw 框架 logger(`runtime.logging.getChildLogger`),\n * 运行时不可用时临时降级 console,就绪后自动切换缓存。\n */\n\nimport { getRequestContext } from \"../request-context.js\";\nimport { tryGetQQBotRuntime } from \"../runtime.js\";\n\nexport interface PluginLogger {\n info(msg: string, meta?: Record<string, unknown>): void;\n warn(msg: string, meta?: Record<string, unknown>): void;\n error(msg: string, meta?: Record<string, unknown>): void;\n debug(msg: string, meta?: Record<string, unknown>): void;\n child(tag: string): PluginLogger;\n}\n\nexport interface PluginLoggerOpts {\n prefix?: string;\n output?: Pick<PluginLogger, 'info' | 'warn' | 'error' | 'debug'>;\n /** 强制 console 输出,忽略 runtime.logging(register 阶段使用) */\n forceConsole?: boolean;\n}\n\n// ─── Console fallback ───────────────────────────────────────────────────────\n\nfunction consoleSink(): Pick<PluginLogger, 'info' | 'warn' | 'error' | 'debug'> {\n const C = '\\x1b[36m', Y = '\\x1b[33m', R = '\\x1b[31m', G = '\\x1b[90m', X = '\\x1b[0m';\n return {\n debug: (m) => console.debug(`${G}[qqbot]${X}`, m),\n info: (m) => console.log(`${C}[qqbot]${X}`, m),\n warn: (m) => console.warn(`${Y}[qqbot]${X}`, m),\n error: (m) => console.error(`${R}[qqbot]${X}`, m),\n };\n}\n\n// ─── Trace metadata ─────────────────────────────────────────────────────────\n\nfunction enrichMeta(meta?: Record<string, unknown>): Record<string, unknown> | undefined {\n const ctx = getRequestContext();\n if (!ctx) return meta;\n const trace: Record<string, unknown> = {};\n if (ctx.accountId) trace.accountId = ctx.accountId;\n if (ctx.messageId) trace.messageId = ctx.messageId;\n if (ctx.openId) trace.openId = ctx.openId;\n if (Object.keys(trace).length === 0) return meta;\n return meta ? { ...trace, ...meta } : trace;\n}\n\n// ─── Framework bridge ───────────────────────────────────────────────────────\n\ntype Sink = Pick<PluginLogger, 'info' | 'warn' | 'error' | 'debug'>;\n\nfunction frameworkSink(): Sink {\n // 不缓存——register() 阶段 getChildLogger 可行但输出未接好。\n // 每次使用时重新 resolve,gateway 启动后自然拿到正确 logger。\n const resolve = (): Sink => {\n try {\n const r = tryGetQQBotRuntime();\n if (r?.logging) {\n const child = r.logging.getChildLogger({ subsystem: 'qqbot/core' }) as any;\n return {\n debug: child.debug?.bind(child),\n info: child.info.bind(child),\n warn: child.warn.bind(child),\n error: child.error.bind(child),\n };\n }\n } catch {}\n return consoleSink();\n };\n\n return {\n debug: (msg, meta) => resolve().debug?.(msg, meta),\n info: (msg, meta) => resolve().info(msg, meta),\n warn: (msg, meta) => resolve().warn(msg, meta),\n error: (msg, meta) => resolve().error(msg, meta),\n };\n}\n\n// ─── Factory ────────────────────────────────────────────────────────────────\n\nexport function createPluginLogger(opts: PluginLoggerOpts = {}): PluginLogger {\n const output = opts.output ?? (opts.forceConsole ? consoleSink() : frameworkSink());\n const prefix = opts.prefix ?? '';\n\n const fmt = (msg: string): string =>\n prefix ? `${prefix} ${msg}` : msg;\n\n const buildChild = (parentPrefix: string, tag: string): PluginLogger =>\n createPluginLogger({\n output,\n prefix: parentPrefix ? `${parentPrefix}[${tag}]` : `[${tag}]`,\n });\n\n return {\n info: (msg, meta) => output.info(fmt(msg), enrichMeta(meta)),\n warn: (msg, meta) => output.warn(fmt(msg), enrichMeta(meta)),\n error: (msg, meta) => output.error(fmt(msg), enrichMeta(meta)),\n debug: (msg, meta) => output.debug(fmt(msg), enrichMeta(meta)),\n child: (tag: string) => buildChild(prefix, tag),\n };\n}\n","/**\n * QQBot 插件运行时管理。\n *\n * 日志统一由 `utils/plugin-logger.ts` 提供,此处只管理 runtime 实例。\n */\n\nimport type { PluginRuntime } from \"openclaw/plugin-sdk\";\nimport { setOpenClawVersion } from \"./bot-instance.js\";\nimport { flushAllRefIndexStores } from \"./features/ref-index-store.js\";\nimport { getOpenclawVersion } from \"./utils/pkg-version.js\";\n\nlet runtime: PluginRuntime | null = null;\nlet exitHooksInstalled = false;\n\nexport function setQQBotRuntime(next: PluginRuntime) {\n runtime = next;\n const version = getOpenclawVersion(next.version);\n setOpenClawVersion(version);\n installExitHooksOnce();\n}\n\nfunction installExitHooksOnce(): void {\n if (exitHooksInstalled) return;\n exitHooksInstalled = true;\n\n const flush = () => {\n try { flushAllRefIndexStores(); } catch {}\n };\n\n process.on('beforeExit', flush);\n process.on('SIGINT', () => { flush(); process.exit(0); });\n process.on('SIGTERM', () => { flush(); process.exit(0); });\n}\n\nexport function getQQBotRuntime(): PluginRuntime {\n if (!runtime) throw new Error(\"QQBot runtime not initialized\");\n return runtime;\n}\n\nexport function tryGetQQBotRuntime(): PluginRuntime | null {\n return runtime;\n}\n","/**\n * 统一富媒体发送入口\n *\n * 职责:\n * 1. 路径安全校验(只允许白名单目录下的本地文件)\n * 2. 类型推断(扩展名 / MIME / 显式指定)\n * 3. 路由分发(image / voice / video / file)\n * 4. 语音发送失败 fallback 到文件\n *\n * 所有出站媒体发送(channel.outbound.sendMedia / deliver pipeline / Message 工具)\n * 统一经过此入口。\n */\nimport * as path from 'node:path';\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport { resolveAgentWorkspace } from '../adapter/workspace.js';\nimport type { ReplyTarget } from '@tencent-connect/qqbot-nodejs';\nimport type { QQBotGateway } from '../gateway/index.js';\nimport { tryGetQQBotRuntime } from '../runtime.js';\nimport { getAdapters } from '../adapter/resolve.js';\nimport type { PluginLogger } from '../utils/plugin-logger.js';\nimport { validateRemoteUrl } from '../utils/ssrf-guard.js';\nimport { getGateway } from './outbound-service.js';\nimport { parseTarget } from './target.js';\nimport {\n isLocalFilePath,\n isDataUrl,\n normalizePath,\n inferMediaKind,\n inferMediaKindFromMime,\n isPathInAllowedRoots,\n} from './local-file-router.js';\nimport type { MediaKind } from './outbound-service.js';\n\n// ── 类型 ──\n\nexport interface SendMediaParams {\n /** 目标 (qqbot:c2c:xxx / qqbot:group:xxx) */\n to: string;\n /** 媒体源(URL / 本地路径 / data URL) */\n source: string;\n /** 附带文本 */\n text?: string;\n /** 显式指定类型(优先级最高) */\n mediaKind?: MediaKind;\n /** MIME type 提示(优先级次于 mediaKind) */\n mimeType?: string;\n /** 被动回复 ID */\n replyToId?: string;\n /** 账户 ID */\n accountId: string;\n /** 日志 */\n log?: PluginLogger;\n /** Agent ID(用于解析相对路径的工作区) */\n agentId?: string;\n}\n\nexport interface SendMediaResult {\n messageId?: string;\n error?: string;\n /** 是否走了 fallback 路径 */\n fallback?: boolean;\n}\n\n// ── 安全常量 ──\n\n/** 收集临时目录根路径:os.tmpdir + Unix /tmp(处理 macOS /tmp→/private/tmp 符号链接) */\nfunction resolveTempRoots(): string[] {\n const roots = new Set<string>();\n try {\n const tmp = os.tmpdir();\n roots.add(fs.existsSync(tmp) ? fs.realpathSync(tmp) : tmp);\n } catch { /* skip */ }\n // Unix: 解析 /tmp 真实路径,覆盖 macOS 符号链接场景\n if (process.platform !== 'win32') {\n try { roots.add(fs.realpathSync('/tmp')); } catch { /* skip */ }\n }\n return [...roots];\n}\n\n/**\n * 构建动态白名单目录列表。\n *\n * 从 openclaw 核心已解析的 workspaceDir 反向推导配置目录,\n * 同时保留已知配置目录名以向后兼容。\n */\nfunction buildDynamicAllowedRoots(workspaceDir?: string): string[] {\n const home = os.homedir();\n const derivedBase = workspaceDir ? path.dirname(workspaceDir) : path.join(home, '.openclaw');\n const roots: string[] = [];\n const added = new Set<string>();\n\n const addRoot = (p: string) => {\n try {\n const real = fs.existsSync(p) ? fs.realpathSync(p) : p;\n if (!added.has(real)) {\n added.add(real);\n roots.push(real);\n }\n } catch { /* skip */ }\n };\n\n // 已知配置目录名(包括默认和开发模式)\n const knownBases = [path.join(home, '.openclaw'), path.join(home, '.openclaw-dev')];\n\n // 当前配置目录优先(从 workspaceDir 反推)\n if (workspaceDir) {\n addRoot(path.join(derivedBase, 'media'));\n addRoot(path.join(derivedBase, 'workspace'));\n addRoot(path.join(derivedBase, 'outbound'));\n addRoot(workspaceDir);\n }\n\n // 所有已知配置目录(去重,确保向后兼容)\n for (const base of knownBases) {\n addRoot(path.join(base, 'media'));\n addRoot(path.join(base, 'workspace'));\n addRoot(path.join(base, 'outbound'));\n }\n\n // 临时目录\n for (const t of resolveTempRoots()) addRoot(t);\n\n return roots;\n}\n\n/** 入站 Base64 / Data URL 最大字节数(10MB) */\nconst MAX_DATA_URL_BYTES = 10 * 1024 * 1024;\n\n// ── 统一入口 ──\n\n/**\n * 统一富媒体发送入口\n */\nexport async function sendMedia(params: SendMediaParams): Promise<SendMediaResult> {\n const { source, accountId, log } = params;\n const mlog = log?.child('media');\n\n if (!source) {\n mlog?.error('source is empty');\n return { error: 'sendMedia: source is required' };\n }\n\n // 1. 安全校验 + 路径规范化\n const wsDir = resolveWorkspaceFromAgent(params.agentId);\n mlog?.debug(`resolveMediaPath source=${source} agentId=${params.agentId ?? 'none'} workspaceDir=${wsDir ?? 'none'}`);\n const resolved = await resolveMediaPath(source, mlog, wsDir);\n if (!resolved.ok) {\n mlog?.error(`resolveMediaPath failed: ${resolved.error}`);\n return { error: resolved.error };\n }\n\n // 2. 推断类型\n const kind = params.mediaKind\n ?? (params.mimeType ? inferMediaKindFromMime(params.mimeType) : undefined)\n ?? inferMediaKind(resolved.path);\n\n // 3. 获取 gateway\n const gw = getGateway(accountId);\n if (!gw) {\n return { error: `Bot \"${accountId}\" not running` };\n }\n\n const target = parseTarget(params.to);\n\n // 4. 路由分发\n switch (kind) {\n case 'voice':\n return sendVoiceMedia(gw, target, resolved.path, params);\n case 'video':\n return sendVideoMedia(gw, target, resolved.path, params);\n case 'file':\n return sendFileMedia(gw, target, resolved.path, params);\n case 'image':\n default:\n return sendImageMedia(gw, target, resolved.path, params);\n }\n}\n\n// ── 路径安全校验 ──\n\ninterface ResolveResult {\n ok: true;\n path: string;\n isLocal: boolean;\n}\n\ninterface ResolveError {\n ok: false;\n error: string;\n}\n\nasync function resolveMediaPath(source: string, log?: SendMediaParams['log'], workspaceDir?: string): Promise<ResolveResult | ResolveError> {\n const normalized = normalizePath(source);\n\n // Data URL → 大小限制检查\n if (isDataUrl(normalized)) {\n if (normalized.length > MAX_DATA_URL_BYTES) {\n const sizeMB = (normalized.length / (1024 * 1024)).toFixed(1);\n return { ok: false, error: `Data URL 过大(${sizeMB}MB,最大 10MB)` };\n }\n return { ok: true, path: normalized, isLocal: false };\n }\n\n // 远程 URL → SSRF 安全检查\n if (normalized.startsWith('http://') || normalized.startsWith('https://')) {\n try {\n await validateRemoteUrl(normalized);\n } catch (err) {\n log?.warn(`SSRF blocked for media URL: ${normalized}`);\n return { ok: false, error: `媒体 URL 被 SSRF 防护拦截: ${err instanceof Error ? err.message : String(err)}` };\n }\n return { ok: true, path: normalized, isLocal: false };\n }\n\n // 纯文件名→工作区兜底查找\n if (!isLocalFilePath(normalized)) {\n const resolved = resolveWorkingFile(normalized, workspaceDir);\n if (resolved) {\n return { ok: true, path: resolved, isLocal: true };\n }\n return { ok: true, path: normalized, isLocal: false };\n }\n\n // 本地路径 → 安全校验\n const resolved = path.resolve(normalized);\n if (!fs.existsSync(resolved)) {\n return { ok: false, error: `File not found: ${resolved}` };\n }\n\n let real: string;\n try {\n real = fs.realpathSync(resolved);\n } catch {\n return { ok: false, error: `Cannot resolve path: ${resolved}` };\n }\n\n // 动态白名单:静态根目录 + 当前 agent 工作区\n const dynamicRoots = buildDynamicAllowedRoots(workspaceDir);\n const allowed = isPathInAllowedRoots(real, dynamicRoots);\n\n if (!allowed) {\n log?.warn(`path blocked — not in allowed directory: ${real}`);\n return { ok: false, error: `文件路径不在允许的目录中` };\n }\n\n return { ok: true, path: real, isLocal: true };\n}\n\n/** Agent ID → workspaceDir(动态加载 plugin-sdk/health) */\nfunction resolveWorkspaceFromAgent(agentId?: string): string | undefined {\n const cfg = resolveConfigViaAdapter();\n if (!cfg) return undefined;\n return resolveAgentWorkspace(cfg, agentId);\n}\n\n/** 通过 adapter 获取配置 */\nfunction resolveConfigViaAdapter(): Record<string, unknown> | undefined {\n try {\n const rt = tryGetQQBotRuntime();\n if (!rt) return undefined;\n return getAdapters(rt).getConfig?.();\n } catch { return undefined; }\n}\n\n/** 纯文件名在 cwd + 工作区兜底查找 */\nfunction resolveWorkingFile(name: string, workspaceDir?: string): string | null {\n for (const p of [path.resolve(name), workspaceDir ? path.join(workspaceDir, name) : null]) {\n if (p && fs.existsSync(p)) return p;\n }\n return null;\n}\n\n// ── 各类型 sender ──\n\nasync function sendImageMedia(\n gw: QQBotGateway,\n target: ReplyTarget,\n source: string,\n params: SendMediaParams,\n): Promise<SendMediaResult> {\n try {\n const result = await gw.sendMedia(target, source, {\n text: params.text,\n msgId: params.replyToId,\n });\n return { messageId: result.id };\n } catch (err) {\n return { error: formatErr(err) };\n }\n}\n\nasync function sendVoiceMedia(\n gw: QQBotGateway,\n target: ReplyTarget,\n source: string,\n params: SendMediaParams,\n): Promise<SendMediaResult> {\n // 语音源路由:本地 → { localPath },URL → { url },其他 → { base64 }\n const voiceSource = resolveVoiceSource(source);\n\n try {\n const result = await gw.sendVoice(target, voiceSource, {\n msgId: params.replyToId,\n });\n return { messageId: result.id };\n } catch (err) {\n // 语音失败 → fallback 到文件发送\n params.log?.child('media')?.warn(`sendVoice failed (${formatErr(err)}), falling back to sendFile`);\n try {\n const fileName = path.basename(source);\n const fallback = await gw.sendFile(target, source, {\n text: params.text,\n msgId: params.replyToId,\n fileName,\n });\n return { messageId: fallback.id, fallback: true };\n } catch (fallbackErr) {\n return { error: `voice: ${formatErr(err)} | fallback file: ${formatErr(fallbackErr)}` };\n }\n }\n}\n\nasync function sendVideoMedia(\n gw: QQBotGateway,\n target: ReplyTarget,\n source: string,\n params: SendMediaParams,\n): Promise<SendMediaResult> {\n try {\n const result = await gw.sendVideo(target, source, {\n text: params.text,\n msgId: params.replyToId,\n });\n return { messageId: result.id };\n } catch (err) {\n return { error: formatErr(err) };\n }\n}\n\nasync function sendFileMedia(\n gw: QQBotGateway,\n target: ReplyTarget,\n source: string,\n params: SendMediaParams,\n): Promise<SendMediaResult> {\n try {\n const fileName = path.basename(source);\n const result = await gw.sendFile(target, source, {\n text: params.text,\n msgId: params.replyToId,\n fileName,\n });\n return { messageId: result.id };\n } catch (err) {\n return { error: formatErr(err) };\n }\n}\n\n// ── 辅助 ──\n\nfunction resolveVoiceSource(source: string): { url?: string; base64?: string; localPath?: string } {\n if (source.startsWith('http://') || source.startsWith('https://')) {\n return { url: source };\n }\n if (source.startsWith('data:') || (!source.startsWith('/') && !source.startsWith('./') && !source.startsWith('../') && !source.startsWith('~'))) {\n // data URL 或纯 base64 字符串\n const commaIdx = source.indexOf(',');\n return { base64: commaIdx > 0 ? source.slice(commaIdx + 1) : source };\n }\n return { localPath: source };\n}\n\nfunction formatErr(err: unknown): string {\n if (err instanceof Error) return err.message;\n return String(err);\n}\n","/**\n * Agent workspace 目录解析(动态加载 plugin-sdk/health)\n *\n * 新版本 openclaw 有 openclaw/plugin-sdk/health 导出,\n * 旧版本不可用时 fallback 到 QQ Bot media 目录。\n */\nimport { createRequire } from 'node:module';\nimport { getQQBotMediaDir } from '../utils/platform.js';\n\nconst req = createRequire(__filename);\n\nlet health: {\n resolveAgentWorkspaceDir: (cfg: any, agentId: string) => string;\n resolveDefaultAgentId: (cfg: any) => string;\n} | null = null;\n\nexport function resolveAgentWorkspace(cfg: any, agentId?: string): string {\n if (!health) {\n try {\n health = req('openclaw/plugin-sdk/health') as typeof health;\n } catch {\n health = null;\n }\n }\n if (health) {\n return health.resolveAgentWorkspaceDir(cfg, agentId ?? health.resolveDefaultAgentId(cfg));\n }\n return getQQBotMediaDir();\n}\n","/**\r\n * 远程 URL 安全校验\r\n *\r\n * 下载外部资源前,确保目标地址不会命中内部网络或云元数据端点,\r\n * 避免模型输出的恶意链接触达内网服务。\r\n */\r\n\r\nimport net from \"node:net\";\r\nimport dns from \"node:dns/promises\";\r\n\r\n/* ---------- 内网 / 保留地址判定 ---------- */\r\n\r\n/** IPv4 保留网段前缀(覆盖 RFC 1918、链路本地、回环等) */\r\nconst RESERVED_V4_PREFIXES = [\r\n \"127.\", // loopback\r\n \"10.\", // class-A private\r\n \"192.168.\", // class-C private\r\n \"169.254.\", // link-local / cloud metadata\r\n] as const;\r\n\r\n/** 172.16.0.0 – 172.31.255.255 需要单独用正则匹配 */\r\nconst PRIVATE_172_RE = /^172\\.(1[6-9]|2\\d|3[01])\\./;\r\n\r\n/**\r\n * 检查给定 IP 是否落在不可路由 / 私有网段内。\r\n *\r\n * 覆盖:\r\n * - IPv4: 127/8, 10/8, 172.16/12, 192.168/16, 169.254/16, 0.0.0.0\r\n * - IPv6: ::1, ::, fe80 (link-local), fc/fd (ULA)\r\n */\r\nexport function isReservedAddr(ip: string): boolean {\r\n // --- IPv4 ---\r\n if (ip === \"0.0.0.0\") return true;\r\n for (const pfx of RESERVED_V4_PREFIXES) {\r\n if (ip.startsWith(pfx)) return true;\r\n }\r\n if (PRIVATE_172_RE.test(ip)) return true;\r\n\r\n // --- IPv6 ---\r\n const lower = ip.toLowerCase();\r\n if (lower === \"::1\" || lower === \"::\") return true;\r\n if (lower.startsWith(\"fe80:\")) return true; // link-local\r\n if (lower.startsWith(\"fc\") || lower.startsWith(\"fd\")) return true; // unique local\r\n return false;\r\n}\r\n\r\n/* ---------- URL 合法性校验 ---------- */\r\n\r\nconst ALLOWED_SCHEMES = new Set([\"http:\", \"https:\"]);\r\n\r\n/** QQ 官方媒体/API 域名白名单,匹配时跳过 DNS 解析检查 */\r\nconst QQ_TRUSTED_DOMAINS = new Set([\r\n // QQ Bot API\r\n \"api.sgroup.qq.com\",\r\n \"sandbox.api.sgroup.qq.com\",\r\n // QQ Bot Token\r\n \"bots.qq.com\",\r\n // QQ 多媒体上传/下载\r\n \"multimedia.nt.qq.com.cn\",\r\n \"multimedia.nt.qq.com\",\r\n // QQ 群文件\r\n \"grouppro.grouppro.qq.com\",\r\n]);\r\n\r\n/** 检查 hostname 是否匹配 QQ 白名单(支持子域名通配 *.example.com) */\r\nfunction isQQTrustedDomain(hostname: string): boolean {\r\n if (QQ_TRUSTED_DOMAINS.has(hostname)) return true;\r\n // 检查一级子域名:*.multimedia.nt.qq.com.cn\r\n const dot = hostname.indexOf('.');\r\n if (dot > 0) {\r\n const parent = hostname.slice(dot + 1);\r\n return QQ_TRUSTED_DOMAINS.has(parent);\r\n }\r\n return false;\r\n}\r\n\r\n/**\r\n * 校验远程 URL 是否可安全请求。\r\n *\r\n * 规则:\r\n * 1. 仅放行 http / https 协议\r\n * 2. QQ 白名单域名直接放行(免 DNS 解析)\r\n * 3. 若 URL 直接携带 IP 则即时判定\r\n * 4. 若为域名则先做 DNS 解析,逐条检查解析结果\r\n *\r\n * @throws {Error} 当 URL 指向受限地址时\r\n */\r\nexport async function validateRemoteUrl(raw: string): Promise<void> {\r\n const url = new URL(raw);\r\n\r\n if (!ALLOWED_SCHEMES.has(url.protocol)) {\r\n throw new Error(\r\n `不支持的协议 \"${url.protocol}\",仅允许 http/https(URL: ${raw})`,\r\n );\r\n }\r\n\r\n // 去掉 IPv6 方括号\r\n const host = url.hostname.replace(/^\\[|\\]$/g, \"\");\r\n\r\n // QQ 官方域名 → 免 DNS 检查直接放行\r\n if (isQQTrustedDomain(host)) return;\r\n\r\n if (net.isIP(host)) {\r\n assertPublicAddr(host, raw);\r\n return;\r\n }\r\n\r\n // 域名 → 解析后逐条检查\r\n try {\r\n const ips = await dns.resolve(host);\r\n for (const ip of ips) {\r\n assertPublicAddr(ip, raw, host);\r\n }\r\n } catch (err) {\r\n // 已经是我们自己抛的安全错误,继续向上传播\r\n if (err instanceof Error && err.message.includes(\"内网\")) throw err;\r\n // DNS 查询失败不阻塞,后续 fetch 会产生网络错误\r\n console.warn(`[url-check] DNS 解析 \"${host}\" 失败: ${err}`);\r\n }\r\n}\r\n\r\n/* ---------- 内部辅助 ---------- */\r\n\r\n/** 断言 IP 为公网地址,否则抛出错误 */\r\nfunction assertPublicAddr(ip: string, originalUrl: string, domain?: string): void {\r\n if (!isReservedAddr(ip)) return;\r\n\r\n const target = domain ? `域名 \"${domain}\" 解析到内网地址 \"${ip}\"` : `内网地址 \"${ip}\"`;\r\n throw new Error(\r\n `禁止访问${target},已拦截潜在的 SSRF 请求(URL: ${originalUrl})`,\r\n );\r\n}\r\n","/**\n * 本地文件路由 — 检测路径并按类型自动选择发送方式\n *\n * 本地文件路径检测 + 文件类型推断。\n */\nimport * as path from 'node:path';\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport type { MediaKind } from './outbound-service.js';\n\n/**\n * 规范化本地路径\n * - 剥离 file:// 前缀\n * - 展开 ~ 为 home 目录\n * - URL decode\n */\nexport function normalizePath(p: string): string {\n let result = p;\n if (result.startsWith('file://')) {\n result = result.slice('file://'.length);\n // Windows: file:///C:/path → C:/path\n if (/^\\/[a-zA-Z]:[\\\\/]/.test(result)) {\n result = result.slice(1);\n }\n }\n // URL decode\n try {\n result = decodeURIComponent(result);\n } catch {\n // keep raw if decode fails\n }\n // ~ 展开\n if (result === '~' || result.startsWith('~/') || result.startsWith('~\\\\')) {\n result = result.replace(/^~/, os.homedir());\n }\n return result;\n}\n\n/**\n * 判断一个 source 是否为本地文件路径\n */\nexport function isLocalFilePath(source: string): boolean {\n if (!source) return false;\n if (source.startsWith('http://') || source.startsWith('https://')) return false;\n if (source.startsWith('data:')) return false;\n if (source.startsWith('file://')) return true;\n if (source === '~' || source.startsWith('~/') || source.startsWith('~\\\\')) return true;\n if (source.startsWith('/')) return true;\n if (/^[a-zA-Z]:[\\\\/]/.test(source)) return true;\n if (source.startsWith('\\\\\\\\')) return true;\n if (source.startsWith('./') || source.startsWith('../')) return true;\n if (source.startsWith('.\\\\') || source.startsWith('..\\\\')) return true;\n return false;\n}\n\n/**\n * 判断一个 source 是否为 Base64 data URL\n */\nexport function isDataUrl(source: string): boolean {\n return source.startsWith('data:');\n}\n\n/**\n * 验证本地文件存在且可读\n */\nexport function validateLocalFile(filePath: string): boolean {\n try {\n fs.accessSync(filePath, fs.constants.R_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * 获取本地文件大小(字节),不存在返回 -1\n */\nexport function getFileSize(filePath: string): number {\n try {\n return fs.statSync(filePath).size;\n } catch {\n return -1;\n }\n}\n\n// ── 扩展名 → MediaKind 映射 ──\n\nconst IMAGE_EXTS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg', '.ico']);\nconst VOICE_EXTS = new Set(['.wav', '.mp3', '.silk', '.amr', '.ogg', '.flac', '.aac', '.m4a']);\nconst VIDEO_EXTS = new Set(['.mp4', '.avi', '.mov', '.mkv', '.webm', '.flv', '.wmv']);\n\n/**\n * 根据文件扩展名推断 MediaKind\n */\nexport function inferMediaKind(filePath: string): MediaKind {\n const ext = path.extname(filePath).toLowerCase();\n if (IMAGE_EXTS.has(ext)) return 'image';\n if (VOICE_EXTS.has(ext)) return 'voice';\n if (VIDEO_EXTS.has(ext)) return 'video';\n return 'file';\n}\n\n/**\n * 根据 MIME type 推断 MediaKind\n */\nexport function inferMediaKindFromMime(mime: string): MediaKind {\n const lower = mime.toLowerCase();\n if (lower.startsWith('image/')) return 'image';\n if (lower.startsWith('audio/') || lower === 'voice') return 'voice';\n if (lower.startsWith('video/')) return 'video';\n return 'file';\n}\n\n/**\n * 从 data URL 提取 MIME type\n */\nexport function extractMimeFromDataUrl(dataUrl: string): string | undefined {\n const match = dataUrl.match(/^data:([^;,]+)/);\n return match?.[1];\n}\n\n// ── 路径安全校验 ──\n\n/**\n * 校验本地文件真实路径是否在允许的目录列表中。\n * 同时解析符号链接和规范化路径,防止路径穿越。\n *\n * @returns true 表示路径在允许的目录内\n */\nexport function isPathInAllowedRoots(absPath: string, allowedRoots: string[]): boolean {\n if (!allowedRoots.length) return false;\n try {\n const real = fs.realpathSync(absPath);\n return allowedRoots.some((root) => {\n try {\n const rootReal = fs.existsSync(root) ? fs.realpathSync(root) : root;\n return real.startsWith(rootReal + path.sep) || real === rootReal;\n } catch {\n return false;\n }\n });\n } catch {\n return false;\n }\n}\n","/**\n * OpenClaw setup 向导 — QQ Bot 配置界面\n */\nimport type { ChannelSetupWizard } from '../adapter/setup.js';\nimport { createStandardChannelSetupStatus, setSetupChannelEnabled } from '../adapter/setup.js';\nimport { listQQBotAccountIds, resolveQQBotAccount, resolveDefaultQQBotAccountId } from '../config.js';\nimport { finalizeQQBotSetup } from './finalize.js';\n\nconst CHANNEL = 'qqbot' as const;\n\nexport const qqbotSetupWizard: ChannelSetupWizard = {\n channel: CHANNEL,\n status: createStandardChannelSetupStatus({\n channelLabel: 'QQ Bot',\n configuredLabel: 'configured',\n unconfiguredLabel: 'needs AppID + AppSecret',\n configuredHint: 'configured',\n unconfiguredHint: 'needs AppID + AppSecret',\n configuredScore: 1,\n unconfiguredScore: 6,\n resolveConfigured: ({ cfg, accountId }) =>\n (accountId ? [accountId] : listQQBotAccountIds(cfg as any)).some((id) => {\n const account = resolveQQBotAccount(cfg as any, id);\n return Boolean(account.appId && account.clientSecret);\n }),\n }),\n // 未配置时默认使用 default 账号,有账户时框架会提示选择\n resolveAccountIdForConfigure: async ({ cfg, shouldPromptAccountIds, accountOverride, defaultAccountId }) => {\n if (accountOverride) return accountOverride;\n const ids = listQQBotAccountIds(cfg as any);\n if (ids.length === 0) return 'default';\n if (!shouldPromptAccountIds) return ids[0];\n return defaultAccountId || resolveDefaultQQBotAccountId(cfg as any);\n },\n credentials: [],\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n finalize: (async ({ cfg, accountId, prompter, runtime }: any) =>\n finalizeQQBotSetup({ cfg, accountId, prompter: prompter as any, runtime: runtime as any })) as any,\n disable: (cfg) => { setSetupChannelEnabled(cfg, CHANNEL, false); return cfg; },\n};\n","/**\n * QQ Bot QR 登录 — 两阶段接口适配\n *\n * 使用 @tencent-connect/qqbot-connector 的 startQrConnect 回调 API,\n * 将扫码绑定流程拆分为 start / wait 两个阶段,对接 ChannelPlugin 的\n * gateway.loginWithQrStart / loginWithQrWait。\n */\nimport { startQrConnect, type QrConnectCredentials } from '@tencent-connect/qqbot-connector';\n\n// ── 类型 ──\n\nexport interface QrLoginStartResult {\n qrDataUrl?: string;\n message: string;\n}\n\nexport interface QrLoginWaitResult {\n connected: boolean;\n message: string;\n credentials?: QrConnectCredentials[];\n}\n\n// ── 挂起的登录会话 ──\n\ninterface PendingSession {\n /** 等待扫码结果的 Promise(onSuccess resolve, onFailure reject) */\n credentialsPromise: Promise<QrConnectCredentials[]>;\n /** 取消当前流程 */\n dispose: () => void;\n}\n\nconst pendingSessions = new Map<string, PendingSession>();\n\n// ── 启动 QR 登录 ──\n\nexport function startQrLogin(accountId?: string, source = 'openclaw'): Promise<QrLoginStartResult> {\n const key = accountId ?? 'default';\n\n // 清理旧会话\n pendingSessions.get(key)?.dispose();\n pendingSessions.delete(key);\n\n return new Promise((resolve) => {\n let credentialsResolve: (creds: QrConnectCredentials[]) => void;\n let credentialsReject: (err: Error) => void;\n\n // 凭据 Promise — 在 onSuccess/onFailure 中 settle\n const credentialsPromise = new Promise<QrConnectCredentials[]>((res, rej) => {\n credentialsResolve = res;\n credentialsReject = rej;\n });\n\n const dispose = startQrConnect(\n {\n onQrDisplayed(url) {\n resolve({\n qrDataUrl: url,\n message: '请使用手机 QQ 扫描二维码完成绑定',\n });\n },\n onSuccess: (creds) => credentialsResolve!(creds),\n onFailure: (err) => credentialsReject!(err),\n },\n { displayQrCodeToConsole: true, source },\n );\n\n pendingSessions.set(key, { dispose, credentialsPromise });\n });\n}\n\n// ── 等待扫码结果 ──\n\nexport async function waitQrLogin(accountId?: string): Promise<QrLoginWaitResult> {\n const key = accountId ?? 'default';\n const session = pendingSessions.get(key);\n\n if (!session) {\n return { connected: false, message: '没有正在进行的登录会话,请先运行 login 命令。' };\n }\n\n try {\n const credentials = await session.credentialsPromise;\n pendingSessions.delete(key);\n\n if (credentials.length === 0) {\n return { connected: false, message: '未获取到 QQ Bot 凭据。' };\n }\n\n return {\n connected: true,\n message: `绑定成功!AppID: ${credentials.map((c) => c.appId).join(', ')}`,\n credentials,\n };\n } catch (err) {\n pendingSessions.delete(key);\n return {\n connected: false,\n message: `绑定失败: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n}\n\n// ── 主动取消 ──\n\nexport function cancelQrLogin(accountId?: string): void {\n const key = accountId ?? 'default';\n pendingSessions.get(key)?.dispose();\n pendingSessions.delete(key);\n}\n\n// ── 解析 channelInput (appId:clientSecret) ──\n\nexport function parseChannelInput(channelInput?: string | null): { appId: string; clientSecret: string } | null {\n if (!channelInput) return null;\n const parts = channelInput.trim().split(':');\n if (parts.length === 2 && parts[0] && parts[1]) {\n return { appId: parts[0], clientSecret: parts[1] };\n }\n return null;\n}\n\n// ── auth.login 入口 ──\n\nexport interface QqbotLoginParams {\n cfg: Record<string, unknown>;\n accountId?: string | null;\n channelInput?: string | null;\n verbose?: boolean;\n /** 框架注入的 runtime(含 config 持久化 API) */\n [key: string]: unknown;\n}\n\n/**\n * auth.login 完整实现。\n *\n * 账号策略:\n * --account <id> → 写入指定账户\n * channelInput → 用 appId 作为账户键\n * QR 扫码 → 用扫码返回的 appId 作为新账户键\n */\nexport async function qqbotLogin({\n cfg,\n accountId,\n channelInput,\n verbose,\n ...rest\n}: QqbotLoginParams): Promise<void> {\n // 延迟导入,避免循环依赖\n const { applyQQBotAccountConfig } = await import('../config.js');\n const { persistAuthConfig } = await import('../adapter/resolve.js');\n const { applyAccountDefaults } = await import('./finalize.js');\n const { resolveAccountKey } = await import('./account-key.js');\n\n const runtime: Record<string, unknown> = rest as any;\n const resolvedId = accountId ? accountId.trim().toLowerCase() : null;\n\n // 如果传了 appId:clientSecret,直接写入配置\n const parsed = parseChannelInput(channelInput);\n if (parsed) {\n const key = resolveAccountKey(cfg as any, parsed.appId, resolvedId);\n const result = applyQQBotAccountConfig(cfg as any, key, {\n appId: parsed.appId,\n clientSecret: parsed.clientSecret,\n });\n Object.assign(cfg, result);\n // 对齐 setup:写入默认 streaming/dmPolicy\n const withDefaults = applyAccountDefaults(cfg as any, key);\n Object.assign(cfg, withDefaults);\n await persistAuthConfig(runtime, cfg, 'restart');\n if (verbose) console.log(`QQ Bot 已配置 (${key}) AppID: ${parsed.appId}`);\n return;\n }\n\n // QR 扫码登录\n const { qrDataUrl, message } = await startQrLogin(resolvedId || 'pending');\n console.log(`\\n${message}`);\n if (qrDataUrl) console.log(`QR 链接: ${qrDataUrl}`);\n\n const result = await waitQrLogin(resolvedId || 'pending');\n if (!result.connected || !result.credentials) {\n throw new Error(result.message);\n }\n\n for (const cred of result.credentials) {\n const key = resolveAccountKey(cfg as any, cred.appId, resolvedId);\n const next = applyQQBotAccountConfig(cfg as any, key, {\n appId: cred.appId,\n clientSecret: cred.appSecret,\n });\n Object.assign(cfg, next);\n // 对齐 setup:写入默认 streaming/dmPolicy,扫码用户加入白名单\n const withDefaults = applyAccountDefaults(cfg as any, key, cred.userOpenid);\n Object.assign(cfg, withDefaults);\n }\n await persistAuthConfig(runtime, cfg, 'restart');\n console.log(`QQ Bot 登录成功!`);\n}\n","/**\n * QQBot 文本消毒\n *\n * 剥离框架内部脚手架标签(system-reminder、thinking、reasoning 等),\n * 保留 Markdown/HTML 用于 QQ Bot Markdown 渲染。\n */\n\nconst INTERNAL_TAGS = [\n // 框架脚手架标签\n /<system-reminder\\b[^>]*>[\\s\\S]*?<\\/system-reminder>/gi,\n /<previous_response\\b[^>]*>[\\s\\S]*?<\\/previous_response>/gi,\n /<\\s*\\/?\\s*(?:system-reminder|previous_response)\\b[^>]*\\/?\\s*>/gi,\n // 模型推理/思考内容\n // deepseek: `think`...`/think` — 匹配完整标签块,标签名 think 必须完整\n // 格式说明:deepseek 用 `` ` `` (反引号) 替代 XML 的 `<` `>` 作为标签定界符\n // `think` ≡ <think> 开标签\n // `/think` ≡ </think> 闭标签\n /`think`[\\s\\S]*?`\\/think`/gi,\n /<\\s*\\/?\\s*think\\b[^>]*\\/?\\s*>/gi,\n // claude: <thinking>...</thinking>\n /<thinking\\b[^>]*>[\\s\\S]*?<\\/thinking>/gi,\n /<\\s*\\/?\\s*thinking\\b[^>]*\\/?\\s*>/gi,\n];\n\n/** 剥离内部运行时脚手架块 + 模型推理内容 */\nexport function sanitizeQQBotText(text: string): string {\n let result = text;\n for (const re of INTERNAL_TAGS) {\n result = result.replace(re, '');\n }\n return result.trim();\n}\n","/**\n * Gateway 生命周期管理\n *\n * 封装 startAccount / logoutAccount 的业务逻辑:\n * - 凭证恢复\n * - QQBotGateway 实例创建与注册\n * - Features 初始化(update-checker、approval-handler)\n * - 登出时凭证清除\n */\nimport type { OpenClawConfig } from 'openclaw/plugin-sdk/core';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport { DEFAULT_ACCOUNT_ID, resolveQQBotAccount, applyQQBotAccountConfig } from '../config.js';\nimport { getQQBotRuntime } from '../runtime.js';\nimport { getAdapters } from '../adapter/resolve.js';\nimport { QQBotGateway } from './qqbot-gateway.js';\nimport { createPluginLogger } from '../utils/plugin-logger.js';\nimport type { PluginLogger } from '../utils/plugin-logger.js';\nimport { registerGateway, unregisterGateway, getGateway } from '../outbound/outbound-service.js';\nimport { saveCredentialBackup, loadCredentialBackup } from '../features/credential-backup.js';\nimport { triggerUpdateCheck } from '../features/update-checker.js';\nimport { QQBotApprovalHandler, registerApprovalHandler, unregisterApprovalHandler, getApprovalHandler } from '../features/approval-handler.js';\n\nexport interface StartAccountContext {\n account: ResolvedQQBotAccount;\n abortSignal?: AbortSignal;\n cfg: any;\n /**\n * 框架传入的基础 logger(无 child 方法)。内部会自动包装为 PluginLogger。\n * 写日志的优先级:info > warn > error > debug,方法均为必选。\n */\n log?: { info: (msg: string) => void; warn: (msg: string) => void; error: (msg: string) => void; debug: (msg: string) => void };\n getStatus: () => Record<string, unknown>;\n setStatus: (s: Record<string, unknown>) => void;\n [key: string]: unknown;\n}\n\n/**\n * 启动账户(含凭证恢复 + features 初始化)\n */\nexport async function startAccountWithCredentialRecovery(ctx: StartAccountContext): Promise<void> {\n let { account } = ctx;\n const { abortSignal, cfg } = ctx;\n const log: PluginLogger = createPluginLogger({\n prefix: `[${account.accountId}]`,\n ...(ctx.log?.info ? { output: ctx.log as PluginLogger } : {}),\n });\n const runtime = getQQBotRuntime();\n\n // 凭证恢复:配置中 appId/secret 为空时尝试从暂存文件恢复\n if (!account.appId || !account.clientSecret) {\n const backup = loadCredentialBackup(account.accountId);\n if (backup) {\n log?.info(`[qqbot:${account.accountId}] 从暂存文件恢复凭证 (appId=${backup.appId})`);\n try {\n const restoredCfg = applyQQBotAccountConfig(cfg, account.accountId, {\n appId: backup.appId,\n clientSecret: backup.clientSecret,\n });\n const adapters = getAdapters(runtime);\n if (adapters.persistConfig) {\n await adapters.persistConfig(() => restoredCfg);\n }\n account = resolveQQBotAccount(restoredCfg, account.accountId);\n } catch (e) {\n log?.error(`[qqbot:${account.accountId}] 凭证恢复失败: ${e}`);\n }\n }\n }\n\n // 创建 gateway 实例并注册\n const gw = new QQBotGateway(account, runtime, log);\n registerGateway(account.accountId, gw);\n\n await gw.start(\n {\n onReady: () => {\n saveCredentialBackup(account.accountId, account.appId, account.clientSecret);\n ctx.setStatus({\n ...ctx.getStatus(),\n running: true,\n connected: true,\n lastConnectedAt: Date.now(),\n });\n\n // ── Features 初始化(gateway ready 后触发)──\n initFeatures(account, cfg, log).catch((e) => {\n log?.error(`[qqbot:${account.accountId}] initFeatures error: ${e}`);\n });\n },\n onError: (error) => {\n log?.error(`[qqbot:${account.accountId}] Gateway error: ${error.message}`);\n ctx.setStatus({ ...ctx.getStatus(), lastError: error.message });\n },\n },\n abortSignal,\n );\n}\n\n/**\n * Gateway ready 后初始化各 feature 模块\n */\nasync function initFeatures(account: ResolvedQQBotAccount, cfg: any, log: PluginLogger): Promise<void> {\n // 1. 版本更新检测(后台预热,fire-and-forget)\n triggerUpdateCheck(log);\n\n const existing = getApprovalHandler(account.accountId);\n if (existing) {\n await existing.stop();\n unregisterApprovalHandler(account.accountId);\n }\n const approvalLog = log.child('approval');\n try {\n const handler = new QQBotApprovalHandler({\n accountId: account.accountId,\n appId: account.appId,\n clientSecret: account.clientSecret,\n cfg,\n log: approvalLog,\n });\n registerApprovalHandler(account.accountId, handler);\n await handler.start();\n approvalLog.info('registered');\n } catch (e) {\n approvalLog.debug(`not available: ${e}`);\n }\n}\n\n/**\n * 停止账户 — 主动调用,与 abort 信号双保险。\n *\n * 框架先 abort.abort() 让 startAccount promise 自然 resolve,\n * 再调用 stopAccount 给插件机会做主动清理(关 WebSocket、注销处理器、刷新持久化)。\n *\n * 实现策略:\n * 1. 主动调用 bot.stop() — 比 abort 信号更立刻、不依赖事件循环时机\n * 2. 注销 approval handler / gateway\n * 3. 立即返回;剩余资源(typing keepalive 定时器等)由 abort 信号触发收尾\n */\nexport async function stopAccountGracefully(params: {\n accountId: string;\n log?: PluginLogger;\n}): Promise<void> {\n const { accountId, log } = params;\n const gw = getGateway(accountId);\n\n // 1. 主动停止 bot\n if (gw) {\n try {\n await gw.stop();\n log?.info(`[qqbot:${accountId}] gateway stopped`);\n } catch (err) {\n log?.error(`[qqbot:${accountId}] gateway stop error: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n unregisterGateway(accountId);\n try {\n const h = getApprovalHandler(accountId);\n if (h) await h.stop();\n } catch {\n }\n unregisterApprovalHandler(accountId);\n}\n\n/**\n * 登出账户(清除凭证)\n */\nexport async function logoutAndClearCredentials(params: {\n accountId: string;\n cfg: any;\n}): Promise<{ ok: boolean; cleared: boolean; envToken: boolean; loggedOut: boolean }> {\n const { accountId, cfg } = params;\n unregisterGateway(accountId);\n try {\n const h = getApprovalHandler(accountId);\n if (h) await h.stop();\n } catch {\n }\n unregisterApprovalHandler(accountId);\n\n const nextCfg = { ...cfg } as OpenClawConfig;\n const nextQQBot = cfg.channels?.qqbot ? { ...cfg.channels.qqbot } : undefined;\n let cleared = false;\n let changed = false;\n\n if (nextQQBot) {\n const qqbot = nextQQBot as Record<string, unknown>;\n if (accountId === DEFAULT_ACCOUNT_ID && qqbot.clientSecret) {\n delete qqbot.clientSecret;\n cleared = true;\n changed = true;\n }\n const accounts = qqbot.accounts as Record<string, Record<string, unknown>> | undefined;\n if (accounts && accountId in accounts) {\n const entry = accounts[accountId];\n if (entry && 'clientSecret' in entry) {\n delete entry.clientSecret;\n cleared = true;\n changed = true;\n }\n if (entry && Object.keys(entry).length === 0) {\n delete accounts[accountId];\n changed = true;\n }\n }\n }\n\n if (changed && nextQQBot) {\n nextCfg.channels = { ...nextCfg.channels, qqbot: nextQQBot };\n const runtime = getQQBotRuntime();\n const adapters = getAdapters(runtime);\n if (adapters.persistConfig) {\n await adapters.persistConfig(() => nextCfg);\n }\n }\n\n const resolved = resolveQQBotAccount(changed ? nextCfg : cfg, accountId);\n const loggedOut = resolved.secretSource === 'none';\n const envToken = Boolean(process.env.QQBOT_CLIENT_SECRET);\n return { ok: true, cleared, envToken, loggedOut };\n}\n","/**\n * QQBotGateway — 封装单个 Bot 实例的完整生命周期\n */\nimport {\n QQBot,\n FileKVStore,\n kvSessionPersistence,\n MediaFileType,\n type ReplyTarget,\n type QQBotInboundMessage,\n type MiddlewareContext,\n type InteractionEvent,\n type MessageResponse,\n type StreamSession,\n} from '@tencent-connect/qqbot-nodejs';\nimport os from 'node:os';\nimport type { PluginRuntime } from 'openclaw/plugin-sdk';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport type { PluginLogger } from '../utils/plugin-logger.js';\nimport { createPluginLogger } from '../utils/plugin-logger.js';\nimport { setupMiddlewares } from './middleware-setup.js';\nimport { handleMessage, handleInteraction } from './event-handlers.js';\nimport { getQQBotDataDir } from '../utils/platform.js';\nimport { buildUserAgent } from '../bot-instance.js';\nimport { createPluginWebhookAdapter } from '../adapter/webhook.js';\nimport { getPersistedRefIndexStore } from '../features/ref-index-store.js';\nimport { getCachedMsgId } from '../features/msgid-cache.js';\n\nexport interface GatewayCallbacks {\n onReady?: () => void;\n onError?: (error: Error) => void;\n}\n\nexport interface SendOptions {\n msgId?: string;\n text?: string;\n}\n\n// ── 超时常量 ──\n\nconst TEXT_TIMEOUT_MS = 30_000;\nconst MEDIA_TIMEOUT_MS = 300_000;\n\nfunction resolveMs(envKey: string, defaultMs: number): number {\n const env = process.env[envKey];\n if (env) {\n const v = Number(env);\n if (!Number.isNaN(v) && v > 0) return v;\n }\n return defaultMs;\n}\n\nfunction withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise<T> {\n if (ms <= 0) return promise;\n return Promise.race([\n promise,\n new Promise<T>((_, reject) =>\n setTimeout(() => reject(new Error(`出站超时: ${label} (${ms}ms)`)), ms),\n ),\n ]);\n}\n\nexport class QQBotGateway {\n readonly bot: QQBot;\n private readonly account: ResolvedQQBotAccount;\n private readonly runtime: PluginRuntime;\n readonly log: PluginLogger;\n private readonly textTimeout: number;\n private readonly mediaTimeout: number;\n\n constructor(account: ResolvedQQBotAccount, runtime: PluginRuntime, log?: PluginLogger) {\n this.textTimeout = resolveMs('OPENCLAW_OUTBOUND_TIMEOUT_MS', TEXT_TIMEOUT_MS);\n this.mediaTimeout = resolveMs('OPENCLAW_OUTBOUND_MEDIA_TIMEOUT_MS', MEDIA_TIMEOUT_MS);\n this.account = account;\n this.runtime = runtime;\n this.log = log ?? createPluginLogger({ prefix: `[qqbot:${account.accountId}]` });\n\n const dataDir = getQQBotDataDir(account.accountId);\n\n const isWebhook = account.config.transport === 'webhook';\n\n this.bot = new QQBot({\n appId: account.appId,\n appSecret: account.clientSecret,\n accountId: account.accountId,\n markdownSupport: account.markdownSupport,\n userAgent: buildUserAgent(account.userAgentSuffix),\n baseUrl: process.env.QQBOT_BASE_URL?.replace(/\\/+$/, '') || 'https://api.sgroup.qq.com',\n tokenBaseUrl: process.env.QQBOT_TOKEN_BASE_URL?.replace(/\\/+$/, '') || 'https://bots.qq.com',\n transport: account.config.transport,\n webhook: isWebhook ? { path: account.config.webhook?.path, server: createPluginWebhookAdapter({ account, log: this.log }) } : undefined,\n sessionPersistence: kvSessionPersistence({\n store: new FileKVStore({ dir: dataDir, fileName: 'session.json' }),\n accountId: account.accountId,\n }),\n tokenPrefetch: 'sync',\n logger: this.log,\n });\n\n // 包装 sendText/sendMedia,回复后自动写入 ref-index store\n this.wrapBotSendForRefIndex();\n\n // concurrencyGuard 的 merge 策略合并后会继续走完剩余中间件链,\n // 最终与单条消息一样统一由下方 bot.on('message') 处理转发,\n // 因此这里不再需要单独的 onMergeDispatch 回调。\n setupMiddlewares(this.bot, account, {\n getRuntime: () => runtime,\n });\n }\n\n async start(callbacks?: GatewayCallbacks, signal?: AbortSignal): Promise<void> {\n const handleReady = () => {\n this.log.info(`Gateway ready`);\n callbacks?.onReady?.();\n };\n this.bot.on(`ready`, handleReady);\n this.bot.on(`resumed`, handleReady);\n\n\n this.bot.on('error', (err: Error) => {\n this.log.error(`Gateway error: ${err.message}`);\n callbacks?.onError?.(err);\n });\n\n const gatewayLog = this.log.child('gateway');\n\n this.bot.on('message', async (ctx: MiddlewareContext, msg: QQBotInboundMessage) => {\n gatewayLog.debug(`message msgId=${msg.messageId}`);\n try {\n await handleMessage(ctx, msg, this.account, this.runtime, this.log);\n } catch (err) {\n gatewayLog.error(`Dispatch error: ${err instanceof Error ? err.message : String(err)}`);\n }\n });\n\n this.bot.on('interaction', (_ctx, event: InteractionEvent) => {\n handleInteraction(event, this.account, this.runtime, this.log, (id, code, data) =>\n this.bot.acknowledgeInteraction(id, code, data),\n ).catch((err) => {\n this.log.error(`Interaction error: ${err}`);\n });\n });\n\n await this.bot.start(signal);\n }\n\n async stop(): Promise<void> {\n await this.bot.stop();\n }\n\n async sendText(target: ReplyTarget, text: string, opts?: SendOptions): Promise<MessageResponse> {\n return withTimeout(\n this.bot.sendText(attachMsgId(target, opts), text),\n this.textTimeout, 'sendText',\n );\n }\n\n async sendMedia(\n target: ReplyTarget,\n source: string,\n opts?: SendOptions & { fileType?: MediaFileType },\n ): Promise<MessageResponse> {\n const resolvedTarget = attachMsgId(target, opts);\n const fileType = opts?.fileType ?? MediaFileType.IMAGE;\n const sourceOpts = resolveMediaSource(source);\n const result = await withTimeout(\n this.bot.sendMedia({ target: resolvedTarget, fileType, ...sourceOpts, content: opts?.text }),\n this.mediaTimeout, 'sendMedia',\n );\n return result.message ?? { id: '', timestamp: Date.now() };\n }\n\n async sendVoice(\n target: ReplyTarget,\n source: { url?: string; base64?: string; localPath?: string },\n opts?: SendOptions,\n ): Promise<MessageResponse> {\n const resolvedTarget = attachMsgId(target, opts);\n\n if (source.base64) {\n const result = await withTimeout(\n this.bot.sendMedia({ target: resolvedTarget, fileType: MediaFileType.VOICE, fileData: source.base64, content: opts?.text }),\n this.mediaTimeout, 'sendVoice(base64)',\n );\n return result.message ?? { id: '', timestamp: Date.now() };\n }\n if (source.localPath) {\n const result = await withTimeout(\n this.bot.sendMedia({ target: resolvedTarget, fileType: MediaFileType.VOICE, localPath: source.localPath, content: opts?.text }),\n this.mediaTimeout, 'sendVoice(path)',\n );\n return result.message ?? { id: '', timestamp: Date.now() };\n }\n const result = await withTimeout(\n this.bot.sendMedia({ target: resolvedTarget, fileType: MediaFileType.VOICE, url: source.url!, content: opts?.text }),\n this.mediaTimeout, 'sendVoice(url)',\n );\n return result.message ?? { id: '', timestamp: Date.now() };\n }\n\n async sendVideo(\n target: ReplyTarget,\n source: string,\n opts?: SendOptions,\n ): Promise<MessageResponse> {\n const resolvedTarget = attachMsgId(target, opts);\n const sourceOpts = resolveMediaSource(source);\n const result = await withTimeout(\n this.bot.sendMedia({ target: resolvedTarget, fileType: MediaFileType.VIDEO, ...sourceOpts, content: opts?.text }),\n this.mediaTimeout, 'sendVideo',\n );\n return result.message ?? { id: '', timestamp: Date.now() };\n }\n\n async sendFile(\n target: ReplyTarget,\n source: string,\n opts?: SendOptions & { fileName?: string },\n ): Promise<MessageResponse> {\n const resolvedTarget = attachMsgId(target, opts);\n const sourceOpts = resolveMediaSource(source);\n const result = await withTimeout(\n this.bot.sendMedia({ target: resolvedTarget, fileType: MediaFileType.FILE, ...sourceOpts, fileName: opts?.fileName, content: opts?.text }),\n this.mediaTimeout, 'sendFile',\n );\n return result.message ?? { id: '', timestamp: Date.now() };\n }\n\n openStream(target: ReplyTarget, msgId: string): StreamSession {\n return this.bot.openStream({\n target: { ...target, msgId },\n });\n }\n\n async sendTyping(target: ReplyTarget): Promise<void> {\n await this.bot.sendTyping(target);\n }\n\n private wrapBotSendForRefIndex(): void {\n const { accountId, appId } = this.account;\n const senderName = this.account.config.name ?? appId;\n\n const storeEntry = (msg: MessageResponse, content: string, scope: string, mediaKind?: string): void => {\n const refIdx = msg.ext_info?.ref_idx;\n if (!refIdx) return;\n // 空内容降级为媒体类型标签\n const finalContent = content || mediaKind\n ? mediaKind === 'voice' ? '[语音]'\n : mediaKind === 'image' ? '[图片]'\n : mediaKind ? `[${mediaKind}]`\n : content\n : '';\n const entry = {\n messageId: msg.id, content: finalContent, senderId: appId, senderName,\n timestamp: typeof msg.timestamp === 'number' ? new Date(msg.timestamp).toISOString() : msg.timestamp,\n isBot: true, scope,\n };\n getPersistedRefIndexStore(accountId).set(refIdx, entry as any);\n };\n\n // sendText → 直接捕获 text content\n const origSendText = this.bot.sendText.bind(this.bot);\n this.bot.sendText = async (target, text, ...rest) => {\n const result = await origSendText(target, text, ...rest);\n storeEntry(result, text, target.scope);\n return result;\n };\n\n // sendMedia → ext_info 在 result.message 里\n const origSendMedia = this.bot.sendMedia.bind(this.bot);\n this.bot.sendMedia = async (params: any) => {\n const result = await origSendMedia(params);\n const msg = (result as any).message as MessageResponse | undefined;\n if (msg) storeEntry(msg, '', params.target?.scope ?? '', params.mediaKind);\n return result;\n };\n\n // openStream → 流式消息 complete() 时才返回 ext_info.ref_idx\n const origOpenStream = this.bot.openStream.bind(this.bot);\n this.bot.openStream = (opts) => {\n const session = origOpenStream(opts);\n let lastContent = '';\n const origUpdate = session.update.bind(session);\n session.update = async (content: string) => {\n lastContent = content;\n return origUpdate(content);\n };\n const origComplete = session.complete.bind(session);\n session.complete = async (): Promise<any> => {\n const result = await origComplete();\n if (result?.ext_info?.ref_idx) {\n getPersistedRefIndexStore(accountId).set(result.ext_info.ref_idx, {\n messageId: result.id, content: lastContent, senderId: appId, senderName,\n timestamp: typeof result.timestamp === 'number' ? new Date(result.timestamp).toISOString() : result.timestamp,\n isBot: true, scope: opts.target?.scope ?? '',\n } as any);\n }\n return result;\n };\n return session;\n };\n }\n}\n\nfunction attachMsgId(target: ReplyTarget, opts?: SendOptions): ReplyTarget {\n if (opts?.msgId) return { ...target, msgId: opts.msgId };\n // 无显式 msgId 时尝试从缓存获取\n const cached = getCachedMsgId(target.scope, target.targetId);\n return cached ? { ...target, msgId: cached } : target;\n}\n\nfunction resolveMediaSource(source: string): { url?: string; localPath?: string; fileData?: string } {\n if (source.startsWith('data:')) {\n const commaIdx = source.indexOf(',');\n if (commaIdx > 0) {\n return { fileData: source.slice(commaIdx + 1) };\n }\n return { fileData: source };\n }\n if (source.startsWith('http://') || source.startsWith('https://')) {\n return { url: source };\n }\n if (source.startsWith('file://')) {\n let p = source.slice('file://'.length);\n if (/^\\/[a-zA-Z]:[\\\\/]/.test(p)) p = p.slice(1);\n try { p = decodeURIComponent(p); } catch {}\n return { localPath: p };\n }\n if (source === '~' || source.startsWith('~/') || source.startsWith('~\\\\')) {\n return { localPath: source.replace(/^~/, os.homedir()) };\n }\n if (\n source.startsWith('/') ||\n source.startsWith('./') || source.startsWith('../') ||\n source.startsWith('.\\\\') || source.startsWith('..\\\\') ||\n /^[a-zA-Z]:[\\\\/]/.test(source) ||\n source.startsWith('\\\\\\\\')\n ) {\n return { localPath: source };\n }\n return { url: source };\n}\n","import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport { getPackageVersion } from '../utils/pkg-version.js';\n\nconst PLUGIN_VERSION = getPackageVersion();\n\n/** 群聊时隐藏的仅限私聊指令 */\nconst GROUP_EXCLUDED = new Set([\n 'bot-upgrade', 'bot-clear-storage', 'bot-logs',\n 'bot-approve', 'bot-group-always', 'bot-group-allways', 'bot-streaming', 'bot-me',\n]);\n\n/** /bot-help — 查看所有指令以及用途 */\nexport function botHelp(_account: ResolvedQQBotAccount, allCommands: () => SlashCommand[]): SlashCommand {\n return {\n name: 'bot-help',\n description: '查看所有指令以及用途',\n usage: [\n '/bot-help',\n '',\n '列出所有可用的 QQBot 插件内置指令及其简要说明。',\n '使用 /指令名 ? 可查看某条指令的详细用法。',\n ].join('\\n'),\n handler: (ctx) => {\n const isGroup = ctx.message.kind === 'group';\n const lines = ['### QQBot插件内置调试指令', ''];\n\n for (const cmd of allCommands()) {\n const name = Array.isArray(cmd.name) ? cmd.name[0] : cmd.name;\n if (cmd.hidden) continue;\n if (isGroup && GROUP_EXCLUDED.has(name)) continue;\n if (cmd.authorized && cmd.authorized(ctx as any) !== true) continue;\n lines.push(`<qqbot-cmd-input text=\"/${name}\" show=\"/${name}\"/> ${cmd.description ?? ''}`);\n }\n\n lines.push('', `> 插件版本 v${PLUGIN_VERSION}`);\n return lines.join('\\n');\n },\n };\n}\n","import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';\n\n/** /bot-ping — 测试当前 openclaw 与 QQ 连接的网络延迟 */\nexport function botPing(): SlashCommand {\n return {\n name: 'bot-ping',\n description: '测试当前 openclaw 与 QQ 连接的网络延迟',\n usage: [\n '/bot-ping',\n '',\n '测试 OpenClaw 主机与 QQ 服务器之间的网络延迟。',\n '返回网络传输耗时和插件处理耗时。',\n ].join('\\n'),\n handler: (ctx) => {\n const now = Date.now();\n const ts = (ctx.message as any).timestamp;\n const eventTime = ts ? new Date(ts).getTime() : NaN;\n if (isNaN(eventTime)) {\n return '✅ pong!';\n }\n const totalMs = now - eventTime;\n const qqToPlugin = ctx.receivedAt - eventTime;\n const pluginProcess = now - ctx.receivedAt;\n return [\n '✅ pong!',\n '',\n `⏱ 延迟: ${totalMs}ms`,\n ` ├ 网络传输: ${qqToPlugin}ms`,\n ` └ 插件处理: ${pluginProcess}ms`,\n ].join('\\n');\n },\n };\n}\n","/**\n * 版本检查器\n *\n * - triggerUpdateCheck(): gateway 启动时调用,后台预热缓存\n * - getUpdateInfo(): 每次实时查询 npm registry,返回最新结果\n *\n * 使用 HTTPS 直接请求 npm registry API(不依赖 npm CLI),\n * 支持多 registry fallback:npmjs.org → npmmirror.com,解决国内网络问题。\n */\n\nimport https from \"node:https\";\nimport { getPackageVersion } from \"../utils/pkg-version.js\";\nimport type { PluginLogger } from '../utils/plugin-logger.js';\n\nconst PKG_NAME = \"@tencent-connect/openclaw-qqbot\";\nconst ENCODED_PKG = encodeURIComponent(PKG_NAME);\n\nconst REGISTRIES = [\n `https://registry.npmjs.org/${ENCODED_PKG}`,\n `https://registry.npmmirror.com/${ENCODED_PKG}`,\n];\n\nlet CURRENT_VERSION = getPackageVersion();\n\nexport interface UpdateInfo {\n current: string;\n latest: string | null;\n stable: string | null;\n alpha: string | null;\n hasUpdate: boolean;\n checkedAt: number;\n error?: string;\n}\n\nfunction fetchJson(url: string, timeoutMs: number): Promise<any> {\n return new Promise((resolve, reject) => {\n const req = https.get(url, { timeout: timeoutMs, headers: { Accept: \"application/json\" } }, (res) => {\n if (res.statusCode !== 200) {\n res.resume();\n reject(new Error(`HTTP ${res.statusCode} from ${url}`));\n return;\n }\n let data = \"\";\n res.on(\"data\", (chunk: string) => { data += chunk; });\n res.on(\"end\", () => {\n try { resolve(JSON.parse(data)); } catch (e) { reject(e); }\n });\n });\n req.on(\"error\", reject);\n req.on(\"timeout\", () => { req.destroy(); reject(new Error(`timeout fetching ${url}`)); });\n });\n}\n\nasync function fetchDistTags(log?: PluginLogger): Promise<Record<string, string>> {\n for (const url of REGISTRIES) {\n try {\n const json = await fetchJson(url, 10_000);\n const tags = json[\"dist-tags\"];\n if (tags && typeof tags === \"object\") return tags;\n } catch (e: any) {\n log?.debug(`[update-checker] ${url} failed: ${e.message}`);\n }\n }\n throw new Error(\"all registries failed\");\n}\n\nfunction buildUpdateInfo(tags: Record<string, string>): UpdateInfo {\n const currentIsPrerelease = CURRENT_VERSION.includes(\"-\");\n const stableTag = tags.latest || null;\n const alphaTag = tags.alpha || null;\n\n const compareTarget = currentIsPrerelease ? alphaTag : stableTag;\n\n const hasUpdate = typeof compareTarget === \"string\"\n && compareTarget !== CURRENT_VERSION\n && compareVersions(compareTarget, CURRENT_VERSION) > 0;\n\n return {\n current: CURRENT_VERSION,\n latest: compareTarget,\n stable: stableTag,\n alpha: alphaTag,\n hasUpdate,\n checkedAt: Date.now(),\n };\n}\n\n/**\n * Gateway 启动时调用,后台预热版本缓存。\n * log 通过闭包传递,避免模块级可变状态。\n */\nexport function triggerUpdateCheck(log: PluginLogger): void {\n getUpdateInfo(log).then((info) => {\n if (info.hasUpdate) {\n log.info(`[update-checker] new version available: ${info.latest} (current: ${CURRENT_VERSION})`);\n }\n }).catch(() => {});\n}\n\n/** 每次实时查询 npm registry */\nexport async function getUpdateInfo(log?: PluginLogger): Promise<UpdateInfo> {\n try {\n const tags = await fetchDistTags(log);\n return buildUpdateInfo(tags);\n } catch (err: any) {\n log?.debug(`[update-checker] check failed: ${err.message}`);\n return { current: CURRENT_VERSION, latest: null, stable: null, alpha: null, hasUpdate: false, checkedAt: Date.now(), error: err.message };\n }\n}\n\n/**\n * 检查指定版本是否存在于 npm registry\n */\nexport async function checkVersionExists(version: string, pkgName?: string): Promise<boolean> {\n const registries = pkgName ? buildRegistries(pkgName) : REGISTRIES;\n for (const baseUrl of registries) {\n try {\n const url = `${baseUrl}/${version}`;\n const json = await fetchJson(url, 10_000);\n if (json && json.version === version) return true;\n } catch {\n // try next registry\n }\n }\n return false;\n}\n\nfunction buildRegistries(pkgName: string): string[] {\n const encoded = encodeURIComponent(pkgName);\n return [\n `https://registry.npmjs.org/${encoded}`,\n `https://registry.npmmirror.com/${encoded}`,\n ];\n}\n\nfunction compareVersions(a: string, b: string): number {\n const parse = (v: string) => {\n const clean = v.replace(/^v/, \"\");\n const [main, pre] = clean.split(\"-\", 2);\n return { parts: main.split(\".\").map(Number), pre: pre || null };\n };\n const pa = parse(a);\n const pb = parse(b);\n for (let i = 0; i < 3; i++) {\n const diff = (pa.parts[i] || 0) - (pb.parts[i] || 0);\n if (diff !== 0) return diff;\n }\n if (!pa.pre && pb.pre) return 1;\n if (pa.pre && !pb.pre) return -1;\n if (!pa.pre && !pb.pre) return 0;\n const aParts = pa.pre!.split(\".\");\n const bParts = pb.pre!.split(\".\");\n for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {\n const aP = aParts[i] ?? \"\";\n const bP = bParts[i] ?? \"\";\n const aNum = Number(aP);\n const bNum = Number(bP);\n if (!isNaN(aNum) && !isNaN(bNum)) {\n if (aNum !== bNum) return aNum - bNum;\n } else {\n if (aP < bP) return -1;\n if (aP > bP) return 1;\n }\n }\n return 0;\n}\n","import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport { getPackageVersion } from '../utils/pkg-version.js';\nimport { getUpdateInfo } from '../features/update-checker.js';\nimport { getOpenClawVersion } from '../bot-instance.js';\n\nconst PLUGIN_VERSION = getPackageVersion();\nconst GITHUB_URL = 'https://github.com/nicepkg/openclaw';\n\n/** /bot-version — 查看插件版本号 */\nexport function botVersion(_account: ResolvedQQBotAccount): SlashCommand {\n return {\n name: 'bot-version',\n description: '查看插件版本号',\n usage: [\n '/bot-version',\n '',\n '查看当前 QQBot 插件版本和 OpenClaw 框架版本。',\n '同时检查是否有新版本可用。',\n ].join('\\n'),\n handler: async () => {\n const frameworkVersion = getOpenClawVersion();\n const lines = [\n `🦞框架版本:${frameworkVersion}`,\n `🤖QQBot 插件版本:v${PLUGIN_VERSION}`,\n ];\n\n const info = await getUpdateInfo();\n if (info.checkedAt === 0) {\n lines.push('⏳ 版本检查中...');\n } else if (info.error) {\n lines.push('⚠️ 版本检查失败');\n } else if (info.hasUpdate && info.latest) {\n lines.push(`🆕最新可用版本:v${info.latest},点击 <qqbot-cmd-input text=\"/bot-upgrade\" show=\"/bot-upgrade\"/> 查看升级指引`);\n }\n\n lines.push(`🌟官方 GitHub 仓库:[点击前往](${GITHUB_URL})`);\n return lines.join('\\n');\n },\n };\n}\n","import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';\n\n/** /bot-me — 查看发送者 OpenID(仅私聊) */\nexport function botMe(): SlashCommand {\n return {\n name: 'bot-me',\n description: '查看你的 OpenID(仅私聊)',\n usage: `/bot-me\n\n查看你在当前 QQBot 应用下的唯一 OpenID。\n此 ID 用于管理员识别、访问控制等场景。`,\n scope: 'c2c',\n handler: (ctx) => {\n const senderId = ctx.message.senderId ?? 'unknown';\n return `🆔 你的 OpenID: \\`${senderId}\\``;\n },\n };\n}\n","import type { ResolvedQQBotAccount } from '../types.js';\nimport type { SlashCommandHandlerContext } from '@tencent-connect/qqbot-nodejs';\nimport type { PluginRuntime } from 'openclaw/plugin-sdk';\nimport { getAdapters } from '../adapter/resolve.js';\n\n/**\n * 命令授权检查(用于 SlashCommand.authorized 回调)。\n *\n * 规则:\n * - mode=open → 允许\n * - allowFrom 为空或含 \"*\" → 允许\n * - senderId 在 allowFrom 中 → 允许\n * - 否则 → 返回错误消息\n */\nexport function checkCommandAuth(ctx: SlashCommandHandlerContext): boolean | string {\n const p = (ctx.state as any).policy;\n const mode = p?.c2cMode ?? 'allowlist';\n const allowFrom: string[] = p?.allowFrom ?? [];\n if (mode === 'open' || !allowFrom.length || allowFrom.includes('*')) return true;\n return allowFrom.includes(ctx.message.senderId) || '⚠️ 无权限执行此命令';\n}\n\ninterface PersistResult {\n persist: (updater: (cfg: any) => void) => Promise<void>;\n}\n\n/**\n * 底层:获取 persistConfig,统一处理 runtime/adapters 判空。\n * @returns [error, result] — error 非空时不可用;result.persist 直接调 persistConfig。\n */\nasync function resolvePersistFn(getRuntime: () => PluginRuntime): Promise<[string | null, PersistResult | null]> {\n const runtime = getRuntime();\n if (!runtime) return ['⚠️ runtime 不可用,无法修改配置。', null];\n\n const adapters = getAdapters(runtime);\n if (!adapters.persistConfig) {\n return ['⚠️ 当前框架版本不支持在线修改配置,请手动编辑配置文件。', null];\n }\n\n return [\n null,\n { persist: (updater: (cfg: any) => void) => adapters.persistConfig!(updater) },\n ];\n}\n\n/**\n * 持久化更新**账户**配置。自动处理命名账户 / 默认账户路径差异。\n */\nexport async function updateAccountConfig(\n account: ResolvedQQBotAccount,\n getRuntime: () => PluginRuntime,\n updater: (accountConfig: Record<string, unknown>) => void,\n): Promise<string | null> {\n const [err, result] = await resolvePersistFn(getRuntime);\n if (err || !result) return err;\n\n try {\n await result.persist((cfg: any) => {\n cfg.channels ??= {};\n cfg.channels.qqbot ??= {};\n\n const qqbot = cfg.channels.qqbot;\n const accountId = account.accountId;\n const isNamedAccount = accountId !== 'default' && qqbot.accounts?.[accountId];\n\n if (isNamedAccount) {\n qqbot.accounts[accountId] ??= {};\n updater(qqbot.accounts[accountId]);\n } else {\n updater(qqbot);\n }\n });\n return null;\n } catch (e) {\n return `⚠️ 配置修改失败:${e instanceof Error ? e.message : String(e)}`;\n }\n}\n\n/**\n * 持久化更新**框架通用**配置(不从 channels.qqbot.accounts 走)。\n * 适用于 tools.exec 等非账户级字段。\n */\nexport async function updateGlobalConfig(\n getRuntime: () => PluginRuntime,\n updater: (cfg: any) => void,\n): Promise<string | null> {\n const [err, result] = await resolvePersistFn(getRuntime);\n if (err || !result) return err;\n\n try {\n await result.persist(updater);\n return null;\n } catch (e) {\n return `⚠️ 配置修改失败:${e instanceof Error ? e.message : String(e)}`;\n }\n}\n","import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport { getPackageVersion } from '../utils/pkg-version.js';\nimport { getUpdateInfo } from '../features/update-checker.js';\nimport { checkCommandAuth } from './config-util.js';\n\nconst PLUGIN_VERSION = getPackageVersion();\nconst DEFAULT_UPGRADE_URL = 'https://docs.qq.com/doc/DSGxOZk1oVnVKVkpq';\nconst GITHUB_URL = 'https://github.com/nicepkg/openclaw';\n\n/** /bot-upgrade — 检查更新并查看升级指引 */\nexport function botUpgrade(account: ResolvedQQBotAccount): SlashCommand {\n return {\n name: 'bot-upgrade',\n description: '检查更新并查看升级指引',\n scope: 'c2c',\n authorized: checkCommandAuth,\n usage: [\n '/bot-upgrade 检查是否有新版本',\n ].join('\\n'),\n handler: async () => {\n const url = account.config.upgradeUrl ?? DEFAULT_UPGRADE_URL;\n const info = await getUpdateInfo();\n\n if (info.checkedAt === 0) {\n return '⏳ 版本检查中,请稍后再试';\n }\n\n if (info.error) {\n return [\n '❌ 主机网络访问异常,无法检查更新',\n '',\n `查看升级指引:[点击查看](${url})`,\n ].join('\\n');\n }\n\n if (!info.hasUpdate) {\n return [\n `✅ 当前已是最新版本 v${PLUGIN_VERSION}`,\n '',\n `项目地址:[GitHub](${GITHUB_URL})`,\n ].join('\\n');\n }\n\n return [\n '🆕 发现新版本',\n '',\n `当前版本:**v${PLUGIN_VERSION}**`,\n `最新版本:**v${info.latest}**`,\n '',\n `📖 升级指引:[点击查看](${url})`,\n `🌟 官方 GitHub 仓库:[点击前往](${GITHUB_URL})`,\n ].join('\\n');\n },\n };\n}\n","import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';\nimport type { PluginRuntime } from 'openclaw/plugin-sdk';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport { updateAccountConfig, checkCommandAuth } from './config-util.js';\n\n/** /bot-streaming — 一键开关流式消息 */\nexport function botStreaming(account: ResolvedQQBotAccount, getRuntime: () => PluginRuntime): SlashCommand {\n return {\n name: 'bot-streaming',\n description: '一键开关流式消息',\n scope: 'c2c',\n authorized: checkCommandAuth,\n usage: `/bot-streaming\n\n查看当前流式消息状态,或切换开/关。\n流式消息仅支持 C2C(私聊)场景。`,\n handler: async (ctx) => {\n const args = (Array.isArray(ctx.command.args) ? ctx.command.args.join(' ') : String(ctx.command.args ?? '')).trim().toLowerCase();\n const streaming = account.config?.streaming;\n const currentEnabled = typeof streaming === 'boolean'\n ? streaming\n : (streaming as any)?.mode !== 'off';\n\n // 无参数 → 显示状态\n if (!args) {\n const status = currentEnabled ? '✅ 已启用' : '❌ 未启用';\n const toggleHint = currentEnabled\n ? '<qqbot-cmd-input text=\"/bot-streaming off\" show=\"关闭流式\"/>'\n : '<qqbot-cmd-input text=\"/bot-streaming on\" show=\"开启流式\"/>';\n return [\n `🌊 流式消息状态: ${status}`,\n '',\n '流式消息仅支持 C2C(私聊)场景。',\n `点击 ${toggleHint} 切换。`,\n ].join('\\n');\n }\n\n // on / off → 切换\n const targetEnabled = args === 'on' || args === '1' || args === 'true';\n if (targetEnabled === currentEnabled) {\n return `ℹ️ 流式消息已经是${currentEnabled ? '开启' : '关闭'}状态,无需切换。`;\n }\n\n const error = await updateAccountConfig(account, getRuntime, (acfg) => {\n (acfg as any).streaming = { mode: targetEnabled ? 'partial' : 'off' };\n });\n if (error) return error;\n\n account.config.streaming = { mode: targetEnabled ? 'partial' : 'off' };\n return targetEnabled\n ? '✅ 流式消息已开启,私聊消息将以流式方式发送。'\n : '✅ 流式消息已关闭,私聊消息将以静态方式发送。';\n },\n };\n}\n","import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport { checkCommandAuth } from './config-util.js';\nimport { getQQBotMediaDir } from '../utils/platform.js';\n\nconst MAX_DISPLAY = 10;\n\ninterface FileEntry {\n filePath: string;\n size: number;\n}\n\n/** 递归扫描目录下所有文件,按大小降序 */\nfunction scanFiles(dirPath: string): FileEntry[] {\n const files: FileEntry[] = [];\n if (!fs.existsSync(dirPath)) return files;\n\n const walk = (dir: string) => {\n let entries: fs.Dirent[];\n try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) { walk(full); }\n else if (entry.isFile()) {\n try { files.push({ filePath: full, size: fs.statSync(full).size }); } catch { /* skip */ }\n }\n }\n };\n walk(dirPath);\n return files.sort((a, b) => b.size - a.size);\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;\n}\n\n/** /bot-clear-storage — 清理下载文件(两步:扫描 → --force 删除) */\nexport function botClearStorage(_account: ResolvedQQBotAccount): SlashCommand {\n const targetDir = getQQBotMediaDir('downloads');\n const displayDir = targetDir.replace(os.homedir(), '~');\n\n return {\n name: 'bot-clear-storage',\n description: '清理通过QQBot对话产生的文件以及下载的资源',\n scope: 'c2c',\n authorized: checkCommandAuth,\n usage: [\n '/bot-clear-storage',\n '',\n '扫描当前机器人产生的下载文件并列出明细。',\n '确认后执行删除,释放主机磁盘空间。',\n '',\n '/bot-clear-storage --force 确认执行清理',\n '',\n '⚠️ 仅在私聊中可用。',\n ].join('\\n'),\n handler: (ctx) => {\n const isForce = ctx.command.raw?.trim() === '--force';\n\n if (!isForce) {\n // ── 第一步:扫描并展示 ──\n const files = scanFiles(targetDir);\n if (files.length === 0) {\n return `✅ 当前没有需要清理的文件\\n\\n目录 \\`${displayDir}\\` 为空或不存在。`;\n }\n\n const totalSize = files.reduce((s, f) => s + f.size, 0);\n const lines = [\n `即将清理 \\`${displayDir}\\` 下所有文件,共 ${files.length} 个,占用 ${formatSize(totalSize)}。`,\n '',\n `文件概况:`,\n ];\n\n for (const f of files.slice(0, MAX_DISPLAY)) {\n const rel = path.relative(targetDir, f.filePath).replace(/\\\\/g, '/');\n lines.push(` ${rel} (${formatSize(f.size)})`);\n }\n if (files.length > MAX_DISPLAY) {\n lines.push(` ...共 ${files.length} 个文件 (${formatSize(totalSize)})`);\n }\n\n lines.push('', '---', '', '确认清理后文件将永久删除,后续 AI 无法找回。', '‼️ <qqbot-cmd-enter text=\"/bot-clear-storage --force\" />');\n return lines.join('\\n');\n }\n\n // ── 第二步:--force 执行删除 ──\n const files = scanFiles(targetDir);\n if (files.length === 0) {\n return '✅ 没有需要清理的文件。';\n }\n\n let deleted = 0;\n let failed = 0;\n for (const f of files) {\n try { fs.unlinkSync(f.filePath); deleted++; } catch { failed++; }\n }\n\n const parts = [`🗑️ 已删除 ${deleted} 个文件`];\n if (failed > 0) parts.push(`,${failed} 个失败`);\n parts.push(`\\n📁 \\`${displayDir}\\``);\n\n // 清理空目录\n try { fs.rmdirSync(targetDir); } catch { /* not empty */ }\n\n return parts.join('');\n },\n };\n}\n","import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport crypto from 'node:crypto';\nimport { getQQBotMediaDir } from '../utils/platform.js';\nimport { checkCommandAuth } from './config-util.js';\nimport type { PluginRuntime } from 'openclaw/plugin-sdk';\nimport { getAdapters } from '../adapter/resolve.js';\n\n\nconst MAX_LINES_PER_FILE = 1000;\nconst MAX_FILES = 4;\nconst LOG_KEYWORDS = ['gateway', 'openclaw', 'clawdbot', 'moltbot'];\nconst LOG_PATTERN = new RegExp(LOG_KEYWORDS.join('|'), 'i');\n\ninterface LogFileEntry {\n filePath: string;\n sourceDir: string;\n mtime: number;\n}\n\n// ── 配置日志路径 ──\n\n/** 从 openclaw.json logging.file 提取配置的日志文件路径 */\nfunction getConfiguredLogFiles(runtime: PluginRuntime): string[] {\n const files: string[] = [];\n try {\n const cfg = getAdapters(runtime).getConfig?.() ?? {};\n const logFile = (cfg as any)?.logging?.file;\n if (typeof logFile === 'string') {\n files.push(path.resolve(logFile));\n }\n } catch { /* config unavailable */ }\n return files;\n}\n\n// ── 候选目录收集 ──\n\nfunction collectCandidateLogDirs(runtime: PluginRuntime): string[] {\n const homeDir = os.homedir();\n const dirs = new Set<string>();\n const pushDir = (p: string | undefined | null) => {\n if (!p) return;\n try { dirs.add(path.resolve(p)); } catch { /* skip */ }\n };\n const pushStateDir = (stateDir: string) => {\n if (!stateDir) return;\n pushDir(stateDir);\n pushDir(path.join(stateDir, 'logs'));\n };\n\n // 0. 从配置 logging.file 提取目录(最精确)\n for (const logFile of getConfiguredLogFiles(runtime)) {\n pushDir(path.dirname(logFile));\n }\n\n // 1. *_STATE_DIR 环境变量\n for (const [key, value] of Object.entries(process.env)) {\n if (!value) continue;\n if (/STATE_DIR$/i.test(key) && /(OPENCLAW|CLAWDBOT|MOLTBOT)/i.test(key)) {\n pushStateDir(value);\n }\n }\n\n // 2. 常见状态目录\n for (const name of LOG_KEYWORDS) {\n pushDir(path.join(homeDir, `.${name}`));\n pushDir(path.join(homeDir, `.${name}`, 'logs'));\n pushDir(path.join(homeDir, name));\n pushDir(path.join(homeDir, name, 'logs'));\n }\n\n // 3. home/cwd/AppData 下包含产品名的子目录\n const searchRoots = new Set([homeDir, process.cwd(), path.dirname(process.cwd())]);\n if (process.env.APPDATA) searchRoots.add(process.env.APPDATA);\n if (process.env.LOCALAPPDATA) searchRoots.add(process.env.LOCALAPPDATA);\n for (const root of searchRoots) {\n try {\n const entries = fs.readdirSync(root, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const re = LOG_PATTERN;\n if (!re.test(entry.name)) continue;\n const base = path.join(root, entry.name);\n pushDir(base);\n pushDir(path.join(base, 'logs'));\n }\n } catch { /* skip */ }\n }\n\n // 4. /var/log (Linux)\n if (process.platform !== 'win32') {\n for (const name of LOG_KEYWORDS) {\n pushDir(path.join('/var/log', name));\n }\n }\n\n // 5. /tmp\n if (process.platform === 'win32') {\n pushDir('C:\\\\tmp');\n if (process.env.TEMP) pushDir(process.env.TEMP);\n if (process.env.TMP) pushDir(process.env.TMP);\n if (process.env.LOCALAPPDATA) pushDir(path.join(process.env.LOCALAPPDATA, 'Temp'));\n } else {\n pushDir('/tmp');\n }\n for (const name of LOG_KEYWORDS) {\n pushDir(path.join('/tmp', name));\n if (process.platform === 'win32' && process.env.TEMP) {\n pushDir(path.join(process.env.TEMP, name));\n }\n }\n\n // PM2\n const pm2Home = process.env.PM2_HOME ?? path.join(homeDir, '.pm2');\n pushDir(path.join(pm2Home, 'logs'));\n\n return Array.from(dirs);\n}\n\n// ── 日志文件收集 ──\n\nfunction collectRecentLogFiles(logDirs: string[], runtime: PluginRuntime): LogFileEntry[] {\n const candidates: LogFileEntry[] = [];\n const dedupe = new Set<string>();\n\n const pushFile = (filePath: string, sourceDir: string) => {\n const normalized = path.resolve(filePath);\n if (dedupe.has(normalized)) return;\n try {\n const stat = fs.statSync(normalized);\n if (!stat.isFile() || stat.size === 0) return;\n dedupe.add(normalized);\n candidates.push({ filePath: normalized, sourceDir, mtime: stat.mtimeMs });\n } catch { /* skip */ }\n };\n\n // 配置指定的日志文件(最高优先级)\n for (const logFile of getConfiguredLogFiles(runtime)) {\n pushFile(logFile, path.dirname(logFile));\n }\n\n for (const dir of logDirs) {\n // 知名文件名\n for (const name of ['gateway.log', 'gateway.err.log', 'openclaw.log', 'clawdbot.log', 'moltbot.log']) {\n pushFile(path.join(dir, name), dir);\n }\n // 扫描所有 .log/.txt,按关键词过滤\n try {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isFile()) continue;\n if (!/\\.(log|txt)$/i.test(entry.name)) continue;\n const re = LOG_PATTERN;\n if (!re.test(entry.name)) continue;\n pushFile(path.join(dir, entry.name), dir);\n }\n } catch { /* skip */ }\n }\n\n return candidates.sort((a, b) => b.mtime - a.mtime);\n}\n\n// ── 命令 ──\n\n/** /bot-logs — 导出本地日志文件 */\nexport function botLogs(runtime: PluginRuntime): SlashCommand {\n return {\n name: 'bot-logs',\n description: '导出本地日志文件',\n scope: 'c2c',\n authorized: checkCommandAuth,\n usage: [\n '/bot-logs',\n '',\n `导出最近的 OpenClaw 日志文件(最多 ${MAX_FILES} 个)。`,\n `每个文件最多保留最后 ${MAX_LINES_PER_FILE} 行,以文件形式返回。`,\n ].join('\\n'),\n handler: async (ctx) => {\n const logDirs = collectCandidateLogDirs(runtime);\n const recentFiles = collectRecentLogFiles(logDirs, runtime).slice(0, MAX_FILES);\n\n if (recentFiles.length === 0) {\n const existingDirs = logDirs.filter((d) => { try { return fs.existsSync(d); } catch { return false; } });\n const searched = existingDirs.length > 0\n ? existingDirs.map((d) => ` • ${d}`).join('\\n')\n : logDirs.map((d) => ` • ${d}`).join('\\n');\n return [\n '⚠️ 未找到日志文件',\n '',\n '已搜索以下路径:',\n searched,\n ].join('\\n');\n }\n\n const lines: string[] = [];\n let totalIncluded = 0;\n let totalOriginal = 0;\n let truncatedCount = 0;\n\n for (const logFile of recentFiles) {\n try {\n const content = fs.readFileSync(logFile.filePath, 'utf8');\n const allLines = content.split('\\n');\n const tail = allLines.slice(-MAX_LINES_PER_FILE);\n if (tail.length > 0) {\n const fileName = path.basename(logFile.filePath);\n lines.push(`\\n== ${fileName} (last ${tail.length}/${allLines.length}) ==`);\n lines.push(...tail);\n totalIncluded += tail.length;\n totalOriginal += allLines.length;\n if (allLines.length > MAX_LINES_PER_FILE) truncatedCount++;\n }\n } catch { /* skip */ }\n }\n\n if (lines.length === 0) {\n return '⚠️ 找到日志文件但读取失败';\n }\n\n const tmpDir = getQQBotMediaDir('exports');\n if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir, { recursive: true });\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);\n const suffix = crypto.randomBytes(4).toString('hex');\n const tmpFile = path.join(tmpDir, `bot-logs-${timestamp}-${suffix}.txt`);\n fs.writeFileSync(tmpFile, lines.join('\\n'), 'utf8');\n\n let summary = `${recentFiles.length} 个日志文件,共 ${totalIncluded} 行`;\n if (truncatedCount > 0) summary += `(${truncatedCount} 个截断,原始 ${totalOriginal} 行)`;\n\n try {\n const senderId = ctx.message.senderId;\n if (senderId) {\n await ctx.bot.sendFile(\n { scope: 'c2c', targetId: senderId, msgId: ctx.message.messageId },\n { localPath: tmpFile },\n { fileName: `bot-logs-${timestamp}-${suffix}.txt` },\n );\n }\n } catch (err) {\n return `📋 ${summary}\\n⚠️ 文件发送失败:${err instanceof Error ? err.message : err}\\n📎 ${tmpFile}`;\n }\n\n return `📋 ${summary}`;\n },\n };\n}\n","import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';\nimport type { PluginRuntime } from 'openclaw/plugin-sdk';\nimport { getAdapters } from '../adapter/resolve.js';\nimport { updateGlobalConfig, checkCommandAuth } from './config-util.js';\n\n/** 审批预设配置 */\nconst PRESETS: Record<string, { security: string; ask: string; desc: string }> = {\n on: { security: 'allowlist', ask: 'on-miss', desc: '开启审批(白名单模式)' },\n off: { security: 'full', ask: 'off', desc: '关闭审批' },\n always: { security: 'allowlist', ask: 'always', desc: '严格模式(每次都审批)' },\n};\n\n/** 格式化当前审批状态 */\nfunction formatStatus(security: string, ask: string): string {\n const secIcon = security === 'full' ? '🟢' : security === 'allowlist' ? '🟡' : '🔴';\n const askIcon = ask === 'off' ? '🟢' : ask === 'always' ? '🔴' : '🟡';\n const desc =\n security === 'deny' ? '⚠️ 当前为 deny 模式,所有命令执行被拒绝' :\n security === 'full' && ask === 'off' ? '✅ 所有命令无需审批直接执行' :\n security === 'allowlist' && ask === 'on-miss' ? '🛡️ 白名单命令直接执行,其余需审批' :\n ask === 'always' ? '🔒 每次命令执行都需要人工审批' :\n `ℹ️ security=${security}, ask=${ask}`;\n\n return [\n '🔐 当前审批配置',\n '',\n `${secIcon} 安全模式 (security): **${security}**`,\n `${askIcon} 审批模式 (ask): **${ask}**`,\n '',\n desc,\n ].join('\\n');\n}\n\n/** 操作指引菜单 */\nfunction menuText(): string {\n return [\n '🔐 命令执行审批配置',\n '',\n '<qqbot-cmd-input text=\"/bot-approve on\" show=\"/bot-approve on\"/> 开启审批(白名单模式)',\n '<qqbot-cmd-input text=\"/bot-approve off\" show=\"/bot-approve off\"/> 关闭审批',\n '<qqbot-cmd-input text=\"/bot-approve always\" show=\"/bot-approve always\"/> 严格模式',\n '<qqbot-cmd-input text=\"/bot-approve reset\" show=\"/bot-approve reset\"/> 恢复默认',\n '<qqbot-cmd-input text=\"/bot-approve status\" show=\"/bot-approve status\"/> 查看当前配置',\n ].join('\\n');\n}\n\n/** /bot-approve — 管理命令执行审批配置 */\nexport function botApprove(getRuntime: () => PluginRuntime): SlashCommand {\n return {\n name: 'bot-approve',\n description: '管理命令执行审批配置',\n scope: 'c2c',\n authorized: checkCommandAuth,\n usage: [\n '/bot-approve 查看操作指引',\n '/bot-approve on 开启审批(白名单模式,推荐)',\n '/bot-approve off 关闭审批,命令直接执行',\n '/bot-approve always 始终审批,每次执行都需审批',\n '/bot-approve reset 恢复框架默认值',\n '/bot-approve status 查看当前审批配置',\n ].join('\\n'),\n handler: async (ctx) => {\n const arg = (Array.isArray(ctx.command.args) ? ctx.command.args.join(' ') : String(ctx.command.args ?? '')).trim().toLowerCase();\n const runtime = getRuntime();\n\n if (!runtime) {\n return '⚠️ runtime 不可用,无法管理审批配置。';\n }\n\n const adapters = getAdapters(runtime);\n\n const loadExecConfig = () => {\n const cfg = adapters.getConfig?.() ?? {};\n const tools = (cfg as any).tools ?? {};\n const exec = tools.exec ?? {};\n return {\n security: String(exec.security ?? 'deny'),\n ask: String(exec.ask ?? 'on-miss'),\n };\n };\n\n // 无参数 → 操作指引\n if (!arg) {\n return menuText();\n }\n\n // status → 查看当前配置\n if (arg === 'status') {\n const { security, ask } = loadExecConfig();\n return [\n formatStatus(security, ask),\n '',\n '<qqbot-cmd-input text=\"/bot-approve on\" show=\"/bot-approve on\"/> 开启审批',\n '<qqbot-cmd-input text=\"/bot-approve off\" show=\"/bot-approve off\"/> 关闭审批',\n '<qqbot-cmd-input text=\"/bot-approve always\" show=\"/bot-approve always\"/> 严格模式',\n '<qqbot-cmd-input text=\"/bot-approve reset\" show=\"/bot-approve reset\"/> 恢复默认',\n ].join('\\n');\n }\n\n // on / off / always → 写入预设\n const preset = PRESETS[arg];\n if (preset) {\n const error = await updateGlobalConfig(getRuntime, (cfg: any) => {\n cfg.tools ??= {};\n cfg.tools.exec ??= {};\n cfg.tools.exec.security = preset.security;\n cfg.tools.exec.ask = preset.ask;\n });\n if (error) return error;\n\n if (arg === 'on') {\n return ['✅ 审批已开启', '', '• security = allowlist', '• ask = on-miss', '', '已批准的命令自动加入白名单,下次直接执行。'].join('\\n');\n }\n if (arg === 'off') {\n return ['✅ 审批已关闭', '', '• security = full', '• ask = off', '', '⚠️ 所有命令将直接执行,不会弹出审批确认。'].join('\\n');\n }\n return ['✅ 已切换为严格审批模式', '', '• security = allowlist', '• ask = always', '', '每个命令都会弹出审批按钮,需手动确认。'].join('\\n');\n }\n\n // reset → 删除 tools.exec.security 和 tools.exec.ask\n if (arg === 'reset') {\n const error = await updateGlobalConfig(getRuntime, (cfg: any) => {\n const exec = cfg.tools?.exec;\n if (exec) {\n delete exec.security;\n delete exec.ask;\n if (Object.keys(exec).length === 0) delete cfg.tools.exec;\n if (cfg.tools && Object.keys(cfg.tools).length === 0) delete cfg.tools;\n }\n });\n if (error) return error;\n\n return ['✅ 审批配置已重置', '', '已移除 tools.exec.security 和 tools.exec.ask', '框架将使用默认值(security=deny, ask=on-miss)', '', '如需开启命令执行,请使用 /bot-approve on'].join('\\n');\n }\n\n return [\n `❌ 未知参数: ${arg}`,\n '',\n '可用选项: on | off | always | reset | status',\n ].join('\\n');\n },\n };\n}\n","import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';\nimport type { PluginRuntime } from 'openclaw/plugin-sdk';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport { updateAccountConfig, checkCommandAuth } from './config-util.js';\n\n/** /bot-group-always — 修改群消息默认响应模式 */\nexport function botGroupAlways(account: ResolvedQQBotAccount, getRuntime: () => PluginRuntime): SlashCommand {\n return {\n name: ['bot-group-always', 'bot-group-allways'],\n description: '修改群消息默认响应模式',\n scope: 'c2c',\n authorized: checkCommandAuth,\n usage: [\n '/bot-group-always on AI 自主判断何时发言(无需 @)',\n '/bot-group-always off 仅在被 @ 时回复',\n '/bot-group-always 查看当前设置',\n '',\n '设为 on 后,AI 会自主判断每条消息是否需要回复(无需 @)。',\n '仍可通过 groups.{groupId}.requireMention 对单个群覆盖。',\n ].join('\\n'),\n handler: async (ctx) => {\n const arg = (Array.isArray(ctx.command.args) ? ctx.command.args.join(' ') : String(ctx.command.args ?? '')).trim().toLowerCase();\n const currentRequireMention = account.config.defaultRequireMention ?? true;\n\n // 无参数 → 查看状态\n if (!arg) {\n return [\n `🤖 群自主发言状态:${currentRequireMention ? '❌ 仅被 @ 时回复' : '✅ 自主判断何时发言'}`,\n `使用 <qqbot-cmd-input text=\"/bot-group-always on\" show=\"/bot-group-always on\"/> 设为自主发言`,\n `使用 <qqbot-cmd-input text=\"/bot-group-always off\" show=\"/bot-group-always off\"/> 设为仅被 @ 时回复`,\n ].join('\\n');\n }\n\n if (arg !== 'on' && arg !== 'off') {\n return '❌ 参数错误,请使用 on 或 off\\n\\n示例:/bot-group-always on';\n }\n\n // on = 自主发言 (requireMention=false), off = 仅被 @ (requireMention=true)\n const newRequireMention = arg === 'off';\n\n if (newRequireMention === currentRequireMention) {\n return `🤖 群自主发言已经是\"${arg}\"状态,无需操作`;\n }\n\n const error = await updateAccountConfig(account, getRuntime, (acfg) => {\n (acfg as any).defaultRequireMention = newRequireMention;\n });\n if (error) return error;\n\n // 更新内存中的配置\n account.config.defaultRequireMention = newRequireMention;\n\n return [\n `✅ 群自主发言已设置为 ${newRequireMention ? '**off**(仅被 @ 时回复)' : '**on**(AI 自主判断何时发言)'}`,\n '',\n newRequireMention\n ? '仅在被 @ 机器人才会回复。'\n : 'AI 将自主判断群消息是否需要回复,无需被 @ 即可发言。',\n ].join('\\n');\n },\n };\n}\n","/**\n * Pairing Runtime 动态加载(兼容旧版框架)\n *\n * 从 OpenClaw 安装目录动态加载 conversation-runtime 模块中的配对函数。\n * 不支持时返回 null,配对功能降级为不可用。\n */\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\n\nexport interface PairingApi {\n readAllowFromStore: (params: { channel: string; accountId: string }) => Promise<string[]>;\n issueChallenge: (params: { channel: string; id: string; accountId: string }) => Promise<{ code: string }>;\n buildReply: (params: { code: string; channel: string }) => string;\n approveCode: (params: { channel: string; code: string; accountId?: string }) => Promise<{ id: string } | null>;\n}\n\nlet _api: PairingApi | null | undefined;\n\n/** 获取 Pairing API,首次调用触发加载并缓存 */\nexport function getPairingApi(): PairingApi | null {\n if (_api !== undefined) return _api;\n _api = loadPairingApi();\n return _api;\n}\n\nfunction loadPairingApi(): PairingApi | null {\n const currentFile = __filename;\n const req = createRequire(currentFile);\n const pluginRoot = path.resolve(path.dirname(currentFile), '..', '..');\n const fs = req('node:fs') as typeof import('node:fs');\n\n const tryLoad = (root: string) => {\n for (const rel of ['dist/plugin-sdk/conversation-runtime.js', 'plugin-sdk/conversation-runtime.js']) {\n const p = path.join(root, rel);\n try {\n if (fs.existsSync(p)) return req(p);\n } catch { /* try next */ }\n }\n return null;\n };\n\n let mod: any = null;\n try {\n const { findOpenclawRoot } = req(path.join(pluginRoot, 'scripts', 'link-sdk-core.cjs')) as {\n findOpenclawRoot: (root: string) => string | null;\n };\n const root = findOpenclawRoot(pluginRoot);\n if (root) mod = tryLoad(root);\n } catch { /* fallback */ }\n\n if (!mod) {\n try {\n const entry = process.argv[1];\n if (entry) {\n const realEntry = fs.realpathSync(entry);\n let dir = path.dirname(realEntry);\n for (let i = 0; i < 6; i++) {\n mod = tryLoad(dir);\n if (mod) break;\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n }\n } catch { /* fallback */ }\n }\n\n if (!mod?.readChannelAllowFromStore) return null;\n\n return {\n // readChannelAllowFromStore(channel, env?, accountId?) — 位置参数\n readAllowFromStore: (params) =>\n mod.readChannelAllowFromStore(params.channel, undefined, params.accountId),\n\n // upsertChannelPairingRequest({ channel, id, accountId }) — 对象参数\n issueChallenge: (params) =>\n mod.upsertChannelPairingRequest({\n channel: params.channel,\n id: params.id,\n accountId: params.accountId,\n }).then((r: any) => ({ code: r.code as string })),\n\n // buildPairingReply({ channel, idLine, code }) — 对象参数\n buildReply: (params) =>\n mod.buildPairingReply({\n channel: params.channel,\n idLine: '', // qqbot 无额外 ID 信息,留空即可\n code: params.code,\n }),\n\n // approveChannelPairingCode({ channel, code, accountId? }) — 对象参数\n approveCode: (params) =>\n mod.approveChannelPairingCode({\n channel: params.channel,\n code: params.code,\n accountId: params.accountId,\n }),\n };\n}\n","import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';\nimport type { PluginRuntime } from 'openclaw/plugin-sdk';\nimport { getPairingApi } from '../adapter/pairing.js';\nimport { checkCommandAuth } from './config-util.js';\n\n/** /bot-pairing — DM 配对审批管理 */\nexport function botPairing(_getRuntime: () => PluginRuntime): SlashCommand {\n return {\n name: 'bot-pairing',\n description: '管理 DM 配对审批',\n scope: 'c2c',\n hidden: true,\n usage: `/bot-pairing approve <code>\n\n批准指定配对码,允许对应用户私聊机器人。\n配对码由用户首次私聊时自动生成。`,\n authorized: checkCommandAuth,\n handler: async (ctx) => {\n const args = (Array.isArray(ctx.command.args) ? ctx.command.args.join(' ') : String(ctx.command.args ?? '')).trim();\n const parts = args.split(/\\s+/);\n const subCmd = parts[0]?.toLowerCase();\n const code = parts[1]?.trim();\n\n if (subCmd !== 'approve' || !code) {\n return '⚠️ 用法: /bot-pairing approve <配对码>';\n }\n\n const api = getPairingApi();\n if (!api) {\n return '⚠️ 当前 OpenClaw 版本不支持配对审批功能。';\n }\n\n try {\n const result = await api.approveCode({\n channel: 'qqbot',\n code,\n });\n\n if (!result?.id) {\n return [\n `⚠️ 配对码 \\`${code}\\` 无效或已过期。`,\n '',\n '每个配对码有效期为 1 小时。',\n ].join('\\n');\n }\n\n return [\n '✅ 已批准用户访问。',\n '',\n `用户 ID: \\`${result.id}\\``,\n ].join('\\n');\n } catch (err) {\n return `❌ 审批失败: ${(err as Error).message}`;\n }\n },\n };\n}\n","/**\n * 斜杠命令注册表\n *\n * 通过 SDK 的 slashCommand 中间件统一注册所有内置命令。\n * 每个命令拆分为独立文件,此处仅编排。\n */\nimport type { SlashCommand } from '@tencent-connect/qqbot-nodejs';\nimport type { PluginRuntime } from 'openclaw/plugin-sdk';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport { botHelp } from './bot-help.js';\nimport { botPing } from './bot-ping.js';\nimport { botVersion } from './bot-version.js';\nimport { botMe } from './bot-me.js';\nimport { botUpgrade } from './bot-upgrade.js';\nimport { botStreaming } from './bot-streaming.js';\nimport { botClearStorage } from './bot-clear-storage.js';\nimport { botLogs } from './bot-logs.js';\nimport { botApprove } from './bot-approve.js';\nimport { botGroupAlways } from './bot-group-always.js';\nimport { botPairing } from './bot-pairing.js';\n\nexport interface CommandBuildOptions {\n getRuntime: () => PluginRuntime;\n}\n\n/**\n * 构建标准命令列表(匹配后直接回复,不进入 AI)\n */\nexport function buildCommandList(account: ResolvedQQBotAccount, opts: CommandBuildOptions): SlashCommand[] {\n const commands: SlashCommand[] = [];\n\n // help 需要访问完整命令列表,延迟绑定\n const help = botHelp(account, () => commands);\n commands.push(\n help,\n botPing(),\n botVersion(account),\n botMe(),\n botUpgrade(account),\n botLogs(opts.getRuntime()),\n botStreaming(account, opts.getRuntime),\n botClearStorage(account),\n botApprove(opts.getRuntime),\n botGroupAlways(account, opts.getRuntime),\n botPairing(opts.getRuntime),\n );\n\n return commands;\n}\n","/**\n * 入站附件处理中间件\n *\n * 处理入站消息中的语音/图片/视频附件:\n * - 语音:下载 → SILK转WAV → STT转文字 → 写入 ctx.state.processedAttachments\n * - 图片:提取 URL 列表\n * - 其他:标记为附件描述\n *\n * 插入位置:envelopeFormatter 之前\n */\nimport * as path from 'node:path';\nimport type { MiddlewareContext } from '@tencent-connect/qqbot-nodejs';\nimport {\n convertSilkToWav,\n isVoiceAttachment,\n} from '@tencent-connect/qqbot-nodejs/protocol';\nimport type { MessageAttachment } from '../types.js';\nimport { transcribeAudio, resolveSTTConfig } from '../utils/stt.js';\nimport { formatVoiceText, formatDuration, type VoiceTranscript, type TranscriptSource } from '../utils/voice-text.js';\nimport { downloadRemoteMedia } from '../adapter/media.js';\nimport { getAdapters } from '../adapter/resolve.js';\n\nexport { formatVoiceText, formatDuration };\nexport type { VoiceTranscript, TranscriptSource };\n\n/** 处理后的附件结果(写入 ctx.state.processedAttachments) */\nexport interface ProcessedAttachments {\n voiceText: string;\n imageUrls: string[];\n otherInfo: string;\n transcripts: VoiceTranscript[];\n /** 下载到本地的媒体路径(图片 + 语音,供 AI 引用) */\n localMediaPaths: string[];\n /** 对应 localMediaPaths 的 MIME type */\n localMediaTypes: string[];\n /** 远端 URL 列表(下载失败时的回退) */\n remoteMediaUrls: string[];\n}\n\ninterface AttachmentMiddlewareOptions {\n /** 获取 runtime */\n getRuntime: () => any;\n}\n\n/**\n * 附件处理中间件\n *\n * SDK ctx 已提供 log 和 accountId,只需传入配置获取函数。\n */\nexport function attachmentProcessor(opts: AttachmentMiddlewareOptions) {\n return async (ctx: MiddlewareContext, next: () => Promise<void>) => {\n const msg = ctx.message;\n const attachments = msg.attachments as MessageAttachment[] | undefined;\n\n if (attachments?.length) {\n const runtime = opts.getRuntime();\n const adapters = getAdapters(runtime);\n const cfg = (adapters.getConfig?.() ?? {}) as Record<string, unknown>;\n const log = ctx.log;\n const result = await processAttachments(attachments, cfg, log);\n\n if (result.voiceText || result.imageUrls.length > 0 || result.otherInfo || result.localMediaPaths.length > 0) {\n ctx.state.processedAttachments = result;\n }\n }\n\n await next();\n };\n}\n\n// ── 核心处理逻辑 ──\n\ntype Log = { info: (m: string) => void; error: (m: string) => void; debug?: (m: string) => void };\n\nasync function processAttachments(\n attachments: MessageAttachment[],\n cfg: Record<string, unknown>,\n log?: Log,\n): Promise<ProcessedAttachments> {\n const sttCfg = resolveSTTConfig(cfg);\n const audioPolicy = resolveAudioPolicy(cfg);\n\n const imageUrls: string[] = [];\n const otherParts: string[] = [];\n const transcripts: VoiceTranscript[] = [];\n const localMediaPaths: string[] = [];\n const localMediaTypes: string[] = [];\n const remoteMediaUrls: string[] = [];\n\n // 并行下载所有附件\n const tasks = attachments.map(async (att) => {\n const isVoice = isVoiceAttachment(att);\n const isImage = att.content_type?.startsWith('image/');\n const url = normalizeUrl(att.url);\n\n if (isImage && url) {\n const localPath = await downloadMediaFile(url, att.filename, log);\n return { type: 'image' as const, localPath, url, contentType: att.content_type ?? 'image/png' };\n }\n\n if (isVoice) {\n const transcript = await processVoiceAttachment(att, sttCfg, audioPolicy, log);\n return { type: 'voice' as const, transcript };\n }\n\n // other 类型也尝试下载\n if (url) {\n const localPath = await downloadMediaFile(url, att.filename, log);\n return { type: 'other' as const, localPath, url, filename: att.filename ?? att.content_type };\n }\n return { type: 'other' as const, localPath: null, url: '', filename: att.filename ?? att.content_type };\n });\n\n const results = await Promise.all(tasks);\n\n // 按原始顺序收集结果\n for (const result of results) {\n if (result.type === 'image') {\n if (result.localPath) {\n imageUrls.push(result.localPath);\n localMediaPaths.push(result.localPath);\n localMediaTypes.push(result.contentType);\n } else {\n imageUrls.push(result.url);\n remoteMediaUrls.push(result.url);\n }\n } else if (result.type === 'voice') {\n transcripts.push(result.transcript);\n if (result.transcript.localPath) {\n localMediaPaths.push(result.transcript.localPath);\n localMediaTypes.push('audio/wav');\n } else if (result.transcript.remoteUrl) {\n remoteMediaUrls.push(result.transcript.remoteUrl);\n }\n } else if (result.type === 'other') {\n if (result.localPath) {\n otherParts.push(`[Attachment: ${result.localPath}]`);\n localMediaPaths.push(result.localPath);\n localMediaTypes.push('application/octet-stream');\n } else {\n otherParts.push(`[Attachment: ${result.filename}]`);\n }\n }\n }\n\n return {\n voiceText: formatVoiceText(transcripts),\n imageUrls,\n otherInfo: otherParts.join('\\n'),\n transcripts,\n localMediaPaths,\n localMediaTypes,\n remoteMediaUrls,\n };\n}\n\n// ── 语音处理 ──\n\nasync function processVoiceAttachment(\n att: MessageAttachment,\n sttCfg: ReturnType<typeof resolveSTTConfig>,\n audioPolicy: AudioPolicyResolved,\n log?: Log,\n): Promise<VoiceTranscript> {\n const asrReferText = att.asr_refer_text?.trim() || undefined;\n // 远端 URL 兜底:优先 wav_url,其次原始 url\n const remoteUrl = normalizeUrl(att.voice_wav_url) || normalizeUrl(att.url) || undefined;\n\n // STT 未配置:直接走 ASR / fallback\n if (!sttCfg) {\n if (asrReferText) {\n log?.debug?.(`Voice: using asr_refer_text (STT not configured)`);\n return { text: asrReferText, source: 'asr', asrReferText, remoteUrl };\n }\n return {\n text: '[Voice message - transcription unavailable]',\n source: 'fallback',\n asrReferText,\n remoteUrl,\n };\n }\n\n let localPath: string | undefined;\n let duration: number | undefined;\n\n try {\n const wavUrl = normalizeUrl(att.voice_wav_url);\n if (wavUrl) {\n const downloaded = await downloadMediaFile(wavUrl, undefined, log);\n if (downloaded) {\n localPath = downloaded;\n log?.debug?.(`Voice: downloaded WAV from voice_wav_url`);\n }\n }\n\n if (!localPath) {\n const silkUrl = normalizeUrl(att.url);\n if (silkUrl) {\n const silkPath = await downloadMediaFile(silkUrl, att.filename, log);\n if (silkPath) {\n const ext = path.extname(silkPath).toLowerCase();\n if (audioPolicy.sttDirectFormats.includes(ext)) {\n localPath = silkPath;\n } else {\n const wavResult = await convertSilkToWav(silkPath);\n if (wavResult) {\n localPath = wavResult.wavPath;\n duration = wavResult.duration / 1000;\n log?.debug?.(`Voice: SILK→WAV (${formatDuration(duration)})`);\n } else {\n localPath = silkPath;\n }\n }\n }\n }\n }\n } catch (err) {\n log?.error(`Voice download/convert failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (localPath) {\n try {\n const transcript = await transcribeAudio(localPath, cfg2stt(sttCfg));\n if (transcript) {\n log?.debug?.(`Voice STT: ${transcript.slice(0, 80)}...`);\n return { text: transcript, source: 'stt', duration, localPath, remoteUrl, asrReferText };\n }\n } catch (err) {\n log?.error(`Voice STT failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n if (asrReferText) {\n return { text: asrReferText, source: 'asr', duration, localPath, remoteUrl, asrReferText };\n }\n\n return {\n text: '[Voice message - transcription failed]',\n source: 'fallback',\n duration,\n localPath,\n remoteUrl,\n asrReferText,\n };\n}\n\n// ── 配置 ──\n\ninterface AudioPolicyResolved {\n sttDirectFormats: string[];\n uploadDirectFormats: string[];\n transcodeEnabled: boolean;\n}\n\nfunction resolveAudioPolicy(cfg: Record<string, unknown>): AudioPolicyResolved {\n const channels = cfg.channels as Record<string, unknown> | undefined;\n const qqbot = channels?.qqbot as Record<string, unknown> | undefined;\n const policy = qqbot?.audioFormatPolicy as Record<string, unknown> | undefined;\n\n return {\n sttDirectFormats: normalizeFormats((policy?.sttDirectFormats as string[]) ?? []),\n uploadDirectFormats: normalizeFormats(\n (policy?.uploadDirectFormats as string[]) ??\n (qqbot?.voiceDirectUploadFormats as string[]) ??\n ['.wav', '.mp3', '.silk'],\n ),\n transcodeEnabled: (policy?.transcodeEnabled as boolean) !== false,\n };\n}\n\nfunction normalizeFormats(formats: string[]): string[] {\n return formats.map((f) => {\n const lower = f.toLowerCase().trim();\n return lower.startsWith('.') ? lower : `.${lower}`;\n });\n}\n\nfunction cfg2stt(sttCfg: NonNullable<ReturnType<typeof resolveSTTConfig>>): Record<string, unknown> {\n return { channels: { qqbot: { stt: sttCfg } } };\n}\n\n// ── 文件工具 ──\n\nfunction normalizeUrl(url: string | undefined): string {\n if (!url) return '';\n return url.startsWith('//') ? `https:${url}` : url;\n}\n\nasync function downloadMediaFile(\n url: string,\n filename?: string,\n log?: Log,\n): Promise<string | null> {\n // 仅允许 HTTPS(安全策略)\n if (!url.startsWith('https://')) {\n log?.debug?.(`Skipping non-HTTPS URL: ${url.slice(0, 80)}`);\n return null;\n }\n\n try {\n const result = await downloadRemoteMedia({\n url,\n subdir: 'qqbot/downloads',\n originalFilename: filename,\n maxBytes: 500 * 1024 * 1024,\n timeoutMs: 120_000,\n });\n log?.debug?.(`Downloaded: ${result.path}`);\n return result.path;\n } catch (err) {\n log?.error(`Download failed: ${url.slice(0, 80)} — ${err instanceof Error ? err.message : String(err)}`);\n return null;\n }\n}\n","/**\n * Passive reply limiter — enforce per-message reply count and TTL limits.\n *\n * QQ Bot restricts how many passive replies can be sent in response to a\n * single inbound message (4 per hour by default). This module tracks reply\n * counts and determines whether the next reply should be passive or\n * fall back to proactive mode.\n *\n * The module is a **class** with zero I/O dependencies, fully supporting\n * multi-account concurrent operation via separate instances.\n */\n\n/** Configuration for the reply limiter. */\nexport interface ReplyLimiterConfig {\n /** Maximum passive replies per message. Defaults to 4. */\n limit?: number;\n /** TTL in milliseconds for the passive reply window. Defaults to 1 hour. */\n ttlMs?: number;\n /** Maximum number of tracked messages before eviction. Defaults to 10000. */\n maxTrackedMessages?: number;\n}\n\n/** Result of a passive-reply limit check. */\nexport interface ReplyLimitResult {\n /** Whether a passive reply is still allowed. */\n allowed: boolean;\n /** Number of remaining passive replies. */\n remaining: number;\n /** Whether the caller should fall back to proactive mode. */\n shouldFallbackToProactive: boolean;\n /** Reason for the fallback. */\n fallbackReason?: \"expired\" | \"limit_exceeded\";\n /** Human-readable diagnostic message. */\n message?: string;\n}\n\ninterface ReplyRecord {\n count: number;\n firstReplyAt: number;\n}\n\nconst DEFAULT_LIMIT = 4;\nconst DEFAULT_TTL_MS = 60 * 60 * 1000;\nconst DEFAULT_MAX_TRACKED = 10_000;\n\n/**\n * Per-account reply limiter with automatic eviction.\n *\n * Usage:\n * ```ts\n * const limiter = new ReplyLimiter({ limit: 4, ttlMs: 3600000 });\n * const check = limiter.checkLimit(messageId);\n * if (check.allowed) {\n * await sendPassiveReply(...);\n * limiter.record(messageId);\n * } else if (check.shouldFallbackToProactive) {\n * await sendProactiveMessage(...);\n * }\n * ```\n */\nexport class ReplyLimiter {\n private readonly limit: number;\n private readonly ttlMs: number;\n private readonly maxTracked: number;\n private readonly tracker = new Map<string, ReplyRecord>();\n\n constructor(config?: ReplyLimiterConfig) {\n this.limit = config?.limit ?? DEFAULT_LIMIT;\n this.ttlMs = config?.ttlMs ?? DEFAULT_TTL_MS;\n this.maxTracked = config?.maxTrackedMessages ?? DEFAULT_MAX_TRACKED;\n }\n\n /** Check whether a passive reply is allowed for the given message. */\n checkLimit(messageId: string): ReplyLimitResult {\n const now = Date.now();\n this.evictIfNeeded(now);\n\n const record = this.tracker.get(messageId);\n\n if (!record) {\n return {\n allowed: true,\n remaining: this.limit,\n shouldFallbackToProactive: false,\n };\n }\n\n if (now - record.firstReplyAt > this.ttlMs) {\n return {\n allowed: false,\n remaining: 0,\n shouldFallbackToProactive: true,\n fallbackReason: \"expired\",\n message: `Message is older than ${this.ttlMs / (60 * 60 * 1000)}h; sending as a proactive message instead`,\n };\n }\n\n const remaining = this.limit - record.count;\n if (remaining <= 0) {\n return {\n allowed: false,\n remaining: 0,\n shouldFallbackToProactive: true,\n fallbackReason: \"limit_exceeded\",\n message: `Passive reply limit reached (${this.limit} per hour); sending proactively instead`,\n };\n }\n\n return {\n allowed: true,\n remaining,\n shouldFallbackToProactive: false,\n };\n }\n\n /** Record one passive reply against a message. */\n record(messageId: string): void {\n const now = Date.now();\n const existing = this.tracker.get(messageId);\n\n if (!existing) {\n this.tracker.set(messageId, { count: 1, firstReplyAt: now });\n } else if (now - existing.firstReplyAt > this.ttlMs) {\n this.tracker.set(messageId, { count: 1, firstReplyAt: now });\n } else {\n existing.count++;\n }\n }\n\n /** Return diagnostic stats. */\n getStats(): { trackedMessages: number; totalReplies: number } {\n let totalReplies = 0;\n for (const record of this.tracker.values()) {\n totalReplies += record.count;\n }\n return { trackedMessages: this.tracker.size, totalReplies };\n }\n\n /** Return limiter configuration. */\n getConfig(): { limit: number; ttlMs: number; ttlHours: number } {\n return {\n limit: this.limit,\n ttlMs: this.ttlMs,\n ttlHours: this.ttlMs / (60 * 60 * 1000),\n };\n }\n\n /** Clear all tracked records. */\n clear(): void {\n this.tracker.clear();\n }\n\n /** Opportunistically evict expired records to keep the tracker bounded. */\n private evictIfNeeded(now: number): void {\n if (this.tracker.size <= this.maxTracked) {\n return;\n }\n for (const [id, rec] of this.tracker) {\n if (now - rec.firstReplyAt > this.ttlMs) {\n this.tracker.delete(id);\n }\n }\n }\n}\n","/**\n * Media tag normalization for QQ Bot messages.\n *\n * Normalizes malformed `<qqimg>`, `<qqvoice>`, etc. tags emitted by\n * smaller models into canonical wrapped-tag format.\n *\n * Zero external dependencies.\n */\n\n/** Lowercase and trim a string, returning empty string for falsy input. */\nfunction lc(s: string): string {\n return (s ?? \"\").toLowerCase().trim();\n}\n\n/** Expand `~` prefix to the process home directory. */\nfunction expandTilde(p: string): string {\n if (!p) {\n return p;\n }\n const home =\n typeof process !== \"undefined\" ? (process.env.HOME ?? process.env.USERPROFILE) : undefined;\n if (!home) {\n return p;\n }\n if (p === \"~\") {\n return home;\n }\n if (p.startsWith(\"~/\") || p.startsWith(\"~\\\\\")) {\n return `${home}/${p.slice(2)}`;\n }\n return p;\n}\n\n// Canonical media tags. `qqmedia` is the generic auto-routing tag.\nconst VALID_TAGS = [\"qqimg\", \"qqvoice\", \"qqvideo\", \"qqfile\", \"qqmedia\"] as const;\n\n// Lowercased aliases that should normalize to the canonical tag set.\nconst TAG_ALIASES: Record<string, (typeof VALID_TAGS)[number]> = {\n qq_img: \"qqimg\",\n qqimage: \"qqimg\",\n qq_image: \"qqimg\",\n qqpic: \"qqimg\",\n qq_pic: \"qqimg\",\n qqpicture: \"qqimg\",\n qq_picture: \"qqimg\",\n qqphoto: \"qqimg\",\n qq_photo: \"qqimg\",\n img: \"qqimg\",\n image: \"qqimg\",\n pic: \"qqimg\",\n picture: \"qqimg\",\n photo: \"qqimg\",\n qq_voice: \"qqvoice\",\n qqaudio: \"qqvoice\",\n qq_audio: \"qqvoice\",\n voice: \"qqvoice\",\n audio: \"qqvoice\",\n qq_video: \"qqvideo\",\n video: \"qqvideo\",\n qq_file: \"qqfile\",\n qqdoc: \"qqfile\",\n qq_doc: \"qqfile\",\n file: \"qqfile\",\n doc: \"qqfile\",\n document: \"qqfile\",\n qq_media: \"qqmedia\",\n media: \"qqmedia\",\n attachment: \"qqmedia\",\n attach: \"qqmedia\",\n qqattachment: \"qqmedia\",\n qq_attachment: \"qqmedia\",\n qqsend: \"qqmedia\",\n qq_send: \"qqmedia\",\n send: \"qqmedia\",\n};\n\nconst ALL_TAG_NAMES = [...VALID_TAGS, ...Object.keys(TAG_ALIASES)];\nALL_TAG_NAMES.sort((a, b) => b.length - a.length);\n\nconst TAG_NAME_PATTERN = ALL_TAG_NAMES.join(\"|\");\n\nconst LEFT_BRACKET = \"(?:[<\\uff1c\\u003c]|&lt;)\";\nconst RIGHT_BRACKET = \"(?:[>\\uff1e\\u003e]|&gt;)\";\n\n/** Match self-closing media-tag syntax with file/src/path/url attributes. */\nexport const SELF_CLOSING_TAG_REGEX = new RegExp(\n \"`?\" +\n LEFT_BRACKET +\n \"\\\\s*(\" +\n TAG_NAME_PATTERN +\n \")\" +\n \"(?:\\\\s+(?!file|src|path|url)[a-z_-]+\\\\s*=\\\\s*[\\\"']?[^\\\"'\\\\s\\uff1c<>\\uff1e>]*?[\\\"']?)*\" +\n \"\\\\s+(?:file|src|path|url)\\\\s*=\\\\s*\" +\n \"[\\\"']?\" +\n \"([^\\\"'\\\\s>\\uff1e]+?)\" +\n \"[\\\"']?\" +\n \"(?:\\\\s+[a-z_-]+\\\\s*=\\\\s*[\\\"']?[^\\\"'\\\\s\\uff1c<>\\uff1e>]*?[\\\"']?)*\" +\n \"\\\\s*/?\" +\n \"\\\\s*\" +\n RIGHT_BRACKET +\n \"`?\",\n \"gi\",\n);\n\n/** Match malformed wrapped media tags that should be normalized. */\nexport const FUZZY_MEDIA_TAG_REGEX = new RegExp(\n \"`?\" +\n LEFT_BRACKET +\n \"\\\\s*(\" +\n TAG_NAME_PATTERN +\n \")\\\\s*\" +\n RIGHT_BRACKET +\n \"[\\\"']?\\\\s*\" +\n \"([^<\\uff1c<\\uff1e>\\\"'`]+?)\" +\n \"\\\\s*[\\\"']?\" +\n LEFT_BRACKET +\n \"\\\\s*/?\\\\s*(?:\" +\n TAG_NAME_PATTERN +\n \")\\\\s*\" +\n RIGHT_BRACKET +\n \"`?\",\n \"gi\",\n);\n\n/** Normalize a raw tag name into the canonical tag set. */\nfunction resolveTagName(raw: string): (typeof VALID_TAGS)[number] {\n const lower = lc(raw);\n if ((VALID_TAGS as readonly string[]).includes(lower)) {\n return lower as (typeof VALID_TAGS)[number];\n }\n return TAG_ALIASES[lower] ?? \"qqimg\";\n}\n\n/** Match wrapped tags whose bodies need newline and tab cleanup. */\nconst MULTILINE_TAG_CLEANUP = new RegExp(\n \"(\" +\n LEFT_BRACKET +\n \"\\\\s*(?:\" +\n TAG_NAME_PATTERN +\n \")\\\\s*\" +\n RIGHT_BRACKET +\n \")\" +\n \"([\\\\s\\\\S]*?)\" +\n \"(\" +\n LEFT_BRACKET +\n \"\\\\s*/?\\\\s*(?:\" +\n TAG_NAME_PATTERN +\n \")\\\\s*\" +\n RIGHT_BRACKET +\n \")\",\n \"gi\",\n);\n\n/** Normalize malformed media-tag output into canonical wrapped tags. */\nexport function normalizeMediaTags(text: string): string {\n const normalizeWrappedTag = (_match: string, rawTag: string, content: string): string => {\n const tag = resolveTagName(rawTag);\n const trimmed = content.trim();\n if (!trimmed) {\n return _match;\n }\n const expanded = expandTilde(trimmed);\n return `<${tag}>${expanded}</${tag}>`;\n };\n\n let cleaned = text.replace(SELF_CLOSING_TAG_REGEX, normalizeWrappedTag);\n\n cleaned = cleaned.replace(\n MULTILINE_TAG_CLEANUP,\n (_m, open: string, body: string, close: string) => {\n const flat = body.replace(/[\\r\\n\\t]+/g, \" \").replace(/ {2,}/g, \" \");\n return open + flat + close;\n },\n );\n\n return cleaned.replace(FUZZY_MEDIA_TAG_REGEX, normalizeWrappedTag);\n}\n","/**\n * Generic JSONL-backed ref-index store.\n *\n * QQ Bot quote / reply messages identify the quoted message via a\n * `refMsgIdx` string. To render the quoted-message preview the bot needs\n * to remember the original sender + content + attachments associated\n * with each `refMsgIdx`. This class implements the storage layer of that\n * cache:\n *\n * - Append-only JSONL on disk (cheap writes, replay on startup).\n * - Bounded in-memory map with TTL eviction.\n * - Compaction when on-disk lines >> in-memory entries.\n *\n * Path resolution is **delegated to the embedder** via the constructor:\n * the SDK itself does not know where the host application wants its data\n * (`~/.openclaw/qqbot/data/ref-index.jsonl`, `/var/lib/qqbot/...`, etc).\n */\n\nimport fs from \"node:fs\";\nimport { formatErrorMessage } from \"./format.js\";\nimport type { RefIndexEntry } from \"./ref-attachments.js\";\n\n/** Optional logger contract used for diagnostic messages. */\nexport interface RefIndexStoreLogger {\n log?: (msg: string) => void;\n error?: (msg: string) => void;\n}\n\n/** Configuration for {@link JsonlRefIndexStore}. */\nexport interface JsonlRefIndexStoreOptions {\n /** Resolve the on-disk JSONL file path. Called lazily on first I/O. */\n filePath: () => string;\n /** Ensure the parent directory exists. Called before each append. */\n ensureDir: () => void;\n /** Maximum number of in-memory entries before LRU-by-create-time eviction. */\n maxEntries?: number;\n /** Time-to-live for each entry, in milliseconds. */\n ttlMs?: number;\n /** Disk lines / cache entries ratio that triggers compaction. */\n compactThresholdRatio?: number;\n /** Optional logger for diagnostics. */\n logger?: RefIndexStoreLogger;\n}\n\ninterface RefIndexLine {\n k: string;\n v: RefIndexEntry;\n t: number;\n}\n\nconst NOOP_LOGGER: Required<RefIndexStoreLogger> = {\n log: () => {},\n error: () => {},\n};\n\nconst DEFAULT_MAX_ENTRIES = 50_000;\nconst DEFAULT_TTL_MS = 7 * 24 * 60 * 60 * 1000;\nconst DEFAULT_COMPACT_RATIO = 2;\n\n/**\n * Append-only JSONL store for QQ Bot ref-index entries.\n *\n * Each instance owns one file path and one in-memory map. Multi-account\n * deployments should construct one store per logical scope.\n */\nexport class JsonlRefIndexStore {\n private readonly options: Required<Omit<JsonlRefIndexStoreOptions, \"logger\">> & {\n logger: Required<RefIndexStoreLogger>;\n };\n\n private cache: Map<string, RefIndexEntry & { _createdAt: number }> | null = null;\n private totalLinesOnDisk = 0;\n\n constructor(options: JsonlRefIndexStoreOptions) {\n this.options = {\n filePath: options.filePath,\n ensureDir: options.ensureDir,\n maxEntries: options.maxEntries ?? DEFAULT_MAX_ENTRIES,\n ttlMs: options.ttlMs ?? DEFAULT_TTL_MS,\n compactThresholdRatio: options.compactThresholdRatio ?? DEFAULT_COMPACT_RATIO,\n logger: {\n log: options.logger?.log ?? NOOP_LOGGER.log,\n error: options.logger?.error ?? NOOP_LOGGER.error,\n },\n };\n }\n\n /** Persist a refIdx mapping for one message. */\n set(refIdx: string, entry: RefIndexEntry): void {\n const store = this.loadFromFile();\n this.evictIfNeeded();\n const now = Date.now();\n store.set(refIdx, { ...entry, _createdAt: now });\n this.appendLine({\n k: refIdx,\n v: {\n content: entry.content,\n senderId: entry.senderId,\n senderName: entry.senderName,\n timestamp: entry.timestamp,\n isBot: entry.isBot,\n attachments: entry.attachments,\n },\n t: now,\n });\n if (this.shouldCompact()) {\n this.compactFile();\n }\n }\n\n /** Look up one quoted message by refIdx. */\n get(refIdx: string): RefIndexEntry | null {\n const store = this.loadFromFile();\n const entry = store.get(refIdx);\n if (!entry) {\n return null;\n }\n if (Date.now() - entry._createdAt > this.options.ttlMs) {\n store.delete(refIdx);\n return null;\n }\n return {\n content: entry.content,\n senderId: entry.senderId,\n senderName: entry.senderName,\n timestamp: entry.timestamp,\n isBot: entry.isBot,\n attachments: entry.attachments,\n };\n }\n\n /** Compact the store before process exit when needed. */\n flush(): void {\n if (this.cache && this.shouldCompact()) {\n this.compactFile();\n }\n }\n\n /** Return diagnostic stats. */\n getStats(): { size: number; maxEntries: number; totalLinesOnDisk: number; filePath: string } {\n const store = this.loadFromFile();\n return {\n size: store.size,\n maxEntries: this.options.maxEntries,\n totalLinesOnDisk: this.totalLinesOnDisk,\n filePath: this.options.filePath(),\n };\n }\n\n // ============ Internal ============\n\n private loadFromFile(): Map<string, RefIndexEntry & { _createdAt: number }> {\n if (this.cache !== null) {\n return this.cache;\n }\n this.cache = new Map();\n this.totalLinesOnDisk = 0;\n\n try {\n const file = this.options.filePath();\n if (!fs.existsSync(file)) {\n return this.cache;\n }\n const raw = fs.readFileSync(file, \"utf-8\");\n const lines = raw.split(\"\\n\");\n const now = Date.now();\n let expired = 0;\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) {\n continue;\n }\n this.totalLinesOnDisk++;\n try {\n const entry = JSON.parse(trimmed) as RefIndexLine;\n if (!entry.k || !entry.v || !entry.t) {\n continue;\n }\n if (now - entry.t > this.options.ttlMs) {\n expired++;\n continue;\n }\n this.cache.set(entry.k, { ...entry.v, _createdAt: entry.t });\n } catch {\n // Skip malformed lines; they will be dropped by the next compaction.\n }\n }\n this.options.logger.log(\n `[ref-index-store] Loaded ${this.cache.size} entries from ${this.totalLinesOnDisk} lines (${expired} expired)`,\n );\n if (this.shouldCompact()) {\n this.compactFile();\n }\n } catch (err) {\n this.options.logger.error(`[ref-index-store] Failed to load: ${formatErrorMessage(err)}`);\n this.cache = new Map();\n }\n return this.cache;\n }\n\n private appendLine(line: RefIndexLine): void {\n try {\n this.options.ensureDir();\n fs.appendFileSync(this.options.filePath(), JSON.stringify(line) + \"\\n\", \"utf-8\");\n this.totalLinesOnDisk++;\n } catch (err) {\n this.options.logger.error(`[ref-index-store] Failed to append: ${formatErrorMessage(err)}`);\n }\n }\n\n private shouldCompact(): boolean {\n if (!this.cache) {\n return false;\n }\n return (\n this.totalLinesOnDisk > this.cache.size * this.options.compactThresholdRatio &&\n this.totalLinesOnDisk > 1000\n );\n }\n\n private compactFile(): void {\n if (!this.cache) {\n return;\n }\n const before = this.totalLinesOnDisk;\n try {\n this.options.ensureDir();\n const file = this.options.filePath();\n const tmpPath = file + \".tmp\";\n const lines: string[] = [];\n for (const [key, entry] of this.cache) {\n lines.push(\n JSON.stringify({\n k: key,\n v: {\n content: entry.content,\n senderId: entry.senderId,\n senderName: entry.senderName,\n timestamp: entry.timestamp,\n isBot: entry.isBot,\n attachments: entry.attachments,\n },\n t: entry._createdAt,\n }),\n );\n }\n fs.writeFileSync(tmpPath, lines.join(\"\\n\") + \"\\n\", \"utf-8\");\n fs.renameSync(tmpPath, file);\n this.totalLinesOnDisk = this.cache.size;\n this.options.logger.log(\n `[ref-index-store] Compacted: ${before} lines → ${this.totalLinesOnDisk} lines`,\n );\n } catch (err) {\n this.options.logger.error(`[ref-index-store] Compact failed: ${formatErrorMessage(err)}`);\n }\n }\n\n private evictIfNeeded(): void {\n if (!this.cache || this.cache.size < this.options.maxEntries) {\n return;\n }\n const now = Date.now();\n for (const [key, entry] of this.cache) {\n if (now - entry._createdAt > this.options.ttlMs) {\n this.cache.delete(key);\n }\n }\n if (this.cache.size >= this.options.maxEntries) {\n const entries = [...this.cache.entries()];\n entries.sort((a, b) => a[1]._createdAt - b[1]._createdAt);\n const toRemove = entries.slice(0, this.cache.size - this.options.maxEntries + 1000);\n for (const [key] of toRemove) {\n this.cache.delete(key);\n }\n this.options.logger.log(`[ref-index-store] Evicted ${toRemove.length} oldest entries`);\n }\n }\n}\n","/**\n * Multi-account JSON-file session persistence for the WebSocket gateway.\n *\n * QQ Open Platform allows a bot to RESUME after a brief disconnect by\n * replaying the last `(session_id, lastSeq)` it observed. This module\n * persists that pair plus `intentLevelIndex` (so the auto-fallback logic\n * can resume at the right intent level on restart) into per-account JSON\n * files, with throttled writes and automatic expiry of stale sessions.\n *\n * Path resolution is delegated to the embedder via the constructor — the\n * SDK does not assume any particular directory layout.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { formatErrorMessage } from \"./format.js\";\n\n/** Persisted gateway session state. */\nexport interface SessionState {\n sessionId: string | null;\n lastSeq: number | null;\n lastConnectedAt: number;\n intentLevelIndex: number;\n accountId: string;\n savedAt: number;\n appId?: string;\n}\n\n/** Optional logger contract used for diagnostic messages. */\nexport interface SessionStoreLogger {\n log?: (msg: string) => void;\n error?: (msg: string) => void;\n}\n\n/** Configuration for {@link FileSessionStore}. */\nexport interface FileSessionStoreOptions {\n /** Resolve the directory that holds session JSON files. */\n getDir: () => string;\n /** Ensure the directory exists. Called before writing. */\n ensureDir: () => void;\n /** Session expiry, in milliseconds. Defaults to 5 minutes. */\n expireMs?: number;\n /** Save throttle window, in milliseconds. Defaults to 1 second. */\n saveThrottleMs?: number;\n /** Optional logger. */\n logger?: SessionStoreLogger;\n}\n\nconst NOOP_LOGGER: Required<SessionStoreLogger> = {\n log: () => {},\n error: () => {},\n};\n\nconst DEFAULT_EXPIRE_MS = 5 * 60 * 1000;\nconst DEFAULT_SAVE_THROTTLE_MS = 1000;\n\n/**\n * Multi-account file-backed gateway session store.\n *\n * One instance handles all accounts owned by a single QQ Bot deployment —\n * the per-account file path is derived deterministically from\n * `accountId`. The on-disk filename uses base64url(accountId) so any\n * Unicode/special-character account id is safe.\n */\nexport class FileSessionStore {\n private readonly options: Required<Omit<FileSessionStoreOptions, \"logger\">> & {\n logger: Required<SessionStoreLogger>;\n };\n\n private readonly throttleState = new Map<\n string,\n {\n pendingState: SessionState | null;\n lastSaveTime: number;\n throttleTimer: ReturnType<typeof setTimeout> | null;\n }\n >();\n\n constructor(options: FileSessionStoreOptions) {\n this.options = {\n getDir: options.getDir,\n ensureDir: options.ensureDir,\n expireMs: options.expireMs ?? DEFAULT_EXPIRE_MS,\n saveThrottleMs: options.saveThrottleMs ?? DEFAULT_SAVE_THROTTLE_MS,\n logger: {\n log: options.logger?.log ?? NOOP_LOGGER.log,\n error: options.logger?.error ?? NOOP_LOGGER.error,\n },\n };\n }\n\n /** Load a saved session, rejecting expired or mismatched-appId entries. */\n load(accountId: string, expectedAppId?: string): SessionState | null {\n try {\n let filePath: string | null = null;\n for (const candidatePath of this.getCandidateSessionPaths(accountId)) {\n if (fs.existsSync(candidatePath)) {\n filePath = candidatePath;\n break;\n }\n }\n if (!filePath) {\n return null;\n }\n\n const data = fs.readFileSync(filePath, \"utf-8\");\n const state = JSON.parse(data) as SessionState;\n const now = Date.now();\n\n if (now - state.savedAt > this.options.expireMs) {\n this.options.logger.log(\n `[session-store] Session expired for ${accountId}, age: ${Math.round((now - state.savedAt) / 1000)}s`,\n );\n try {\n fs.unlinkSync(filePath);\n } catch {\n // Best-effort cleanup; ignore errors.\n }\n return null;\n }\n\n if (expectedAppId && state.appId && state.appId !== expectedAppId) {\n this.options.logger.log(\n `[session-store] appId mismatch for ${accountId}: saved=${state.appId}, current=${expectedAppId}. Discarding stale session.`,\n );\n try {\n fs.unlinkSync(filePath);\n } catch {\n // Best-effort cleanup; ignore errors.\n }\n return null;\n }\n\n if (!state.sessionId || state.lastSeq === null || state.lastSeq === undefined) {\n this.options.logger.log(`[session-store] Invalid session data for ${accountId}`);\n return null;\n }\n\n this.options.logger.log(\n `[session-store] Loaded session for ${accountId}: sessionId=${state.sessionId}, lastSeq=${state.lastSeq}, appId=${state.appId ?? \"unknown\"}, age=${Math.round((now - state.savedAt) / 1000)}s`,\n );\n return state;\n } catch (err) {\n this.options.logger.error(\n `[session-store] Failed to load session for ${accountId}: ${formatErrorMessage(err)}`,\n );\n return null;\n }\n }\n\n /** Save session state with throttling. */\n save(state: SessionState): void {\n const { accountId } = state;\n let throttle = this.throttleState.get(accountId);\n if (!throttle) {\n throttle = { pendingState: null, lastSaveTime: 0, throttleTimer: null };\n this.throttleState.set(accountId, throttle);\n }\n\n const now = Date.now();\n const timeSinceLastSave = now - throttle.lastSaveTime;\n\n if (timeSinceLastSave >= this.options.saveThrottleMs) {\n this.doSaveSession(state);\n throttle.lastSaveTime = now;\n throttle.pendingState = null;\n if (throttle.throttleTimer) {\n clearTimeout(throttle.throttleTimer);\n throttle.throttleTimer = null;\n }\n } else {\n throttle.pendingState = state;\n if (!throttle.throttleTimer) {\n const delay = this.options.saveThrottleMs - timeSinceLastSave;\n throttle.throttleTimer = setTimeout(() => {\n const t = this.throttleState.get(accountId);\n if (t?.pendingState) {\n this.doSaveSession(t.pendingState);\n t.lastSaveTime = Date.now();\n t.pendingState = null;\n }\n if (t) {\n t.throttleTimer = null;\n }\n }, delay);\n }\n }\n }\n\n /** Clear a saved session and any pending throttle state. */\n clear(accountId: string): void {\n const throttle = this.throttleState.get(accountId);\n if (throttle) {\n if (throttle.throttleTimer) {\n clearTimeout(throttle.throttleTimer);\n }\n this.throttleState.delete(accountId);\n }\n try {\n let cleared = false;\n for (const filePath of this.getCandidateSessionPaths(accountId)) {\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath);\n cleared = true;\n }\n }\n if (cleared) {\n this.options.logger.log(`[session-store] Cleared session for ${accountId}`);\n }\n } catch (err) {\n this.options.logger.error(\n `[session-store] Failed to clear session for ${accountId}: ${formatErrorMessage(err)}`,\n );\n }\n }\n\n /** Update only lastSeq on the persisted session. */\n updateLastSeq(accountId: string, lastSeq: number): void {\n const existing = this.load(accountId);\n if (existing?.sessionId) {\n this.save({ ...existing, lastSeq });\n }\n }\n\n /** Load all saved sessions from disk. */\n getAllSessions(): SessionState[] {\n const sessions = new Map<string, SessionState>();\n try {\n const sessionDir = this.options.getDir();\n if (!fs.existsSync(sessionDir)) {\n return [];\n }\n const files = fs.readdirSync(sessionDir);\n\n for (const file of files) {\n if (this.isSessionFileName(file)) {\n try {\n const { state } = this.readSessionStateFile(file);\n if (typeof state.accountId !== \"string\" || !state.accountId) {\n continue;\n }\n const existing = sessions.get(state.accountId);\n if (!existing || (state.savedAt ?? 0) >= (existing.savedAt ?? 0)) {\n sessions.set(state.accountId, state);\n }\n } catch {\n // Skip malformed files; cleanup is opportunistic.\n }\n }\n }\n } catch {\n // Directory may not exist yet; return empty.\n }\n return [...sessions.values()];\n }\n\n /** Remove expired session files from disk. Returns number removed. */\n cleanupExpiredSessions(): number {\n let cleaned = 0;\n try {\n const sessionDir = this.options.getDir();\n if (!fs.existsSync(sessionDir)) {\n return 0;\n }\n const now = Date.now();\n const files = fs.readdirSync(sessionDir);\n\n for (const file of files) {\n if (this.isSessionFileName(file)) {\n const filePath = path.join(sessionDir, file);\n try {\n const { state } = this.readSessionStateFile(file);\n\n if (now - state.savedAt > this.options.expireMs) {\n fs.unlinkSync(filePath);\n cleaned++;\n this.options.logger.log(`[session-store] Cleaned expired session: ${file}`);\n }\n } catch {\n try {\n fs.unlinkSync(filePath);\n cleaned++;\n } catch {\n // Best-effort cleanup; ignore errors.\n }\n }\n }\n }\n } catch {\n // Directory may not exist yet; nothing to do.\n }\n return cleaned;\n }\n\n // ============ Internal ============\n\n private encodeAccountIdForFileName(accountId: string): string {\n return Buffer.from(accountId, \"utf8\").toString(\"base64url\");\n }\n\n private getLegacySessionPath(accountId: string): string {\n const safeId = accountId.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n return path.join(this.options.getDir(), `session-${safeId}.json`);\n }\n\n private getSessionPath(accountId: string): string {\n const encodedId = this.encodeAccountIdForFileName(accountId);\n return path.join(this.options.getDir(), `session-${encodedId}.json`);\n }\n\n private getCandidateSessionPaths(accountId: string): string[] {\n const primaryPath = this.getSessionPath(accountId);\n const legacyPath = this.getLegacySessionPath(accountId);\n return primaryPath === legacyPath ? [primaryPath] : [primaryPath, legacyPath];\n }\n\n private isSessionFileName(file: string): boolean {\n return file.startsWith(\"session-\") && file.endsWith(\".json\");\n }\n\n private readSessionStateFile(file: string): { filePath: string; state: SessionState } {\n const filePath = path.join(this.options.getDir(), file);\n const data = fs.readFileSync(filePath, \"utf-8\");\n return { filePath, state: JSON.parse(data) as SessionState };\n }\n\n private doSaveSession(state: SessionState): void {\n const filePath = this.getSessionPath(state.accountId);\n const legacyPath = this.getLegacySessionPath(state.accountId);\n try {\n this.options.ensureDir();\n const stateToSave: SessionState = { ...state, savedAt: Date.now() };\n fs.writeFileSync(filePath, JSON.stringify(stateToSave, null, 2), \"utf-8\");\n if (legacyPath !== filePath && fs.existsSync(legacyPath)) {\n fs.unlinkSync(legacyPath);\n }\n this.options.logger.log(\n `[session-store] Saved session for ${state.accountId}: sessionId=${state.sessionId}, lastSeq=${state.lastSeq}`,\n );\n } catch (err) {\n this.options.logger.error(\n `[session-store] Failed to save session for ${state.accountId}: ${formatErrorMessage(err)}`,\n );\n }\n }\n}\n","/**\n * Text parsing utilities — zero external dependency.\n *\n * Contains pure functions for QQ Bot inbound message text processing:\n * internal-marker stripping, ref-index extraction, face-tag rendering,\n * and attachment-summary normalization.\n */\n\nimport type { RefAttachmentSummary } from \"./ref-attachments.js\";\n\n// ============ Internal markers ============\n\nconst INTERNAL_MARKER_RE = /\\[internal:?\\s*[^\\]]*\\]|\\[debug:?\\s*[^\\]]*\\]|\\[system:?\\s*[^\\]]*\\]/gi;\n\n/** Remove internal markers like `[internal:...]`, `[debug:...]`, `[system:...]`. */\nexport function filterInternalMarkers(text: string | undefined | null): string {\n if (!text) {\n return \"\";\n }\n return text.replace(INTERNAL_MARKER_RE, \"\").trim();\n}\n\n// ============ Ref indices ============\n\n/** QQ message type for quote-reply messages. */\nexport const MSG_TYPE_QUOTE = 103;\n\n/**\n * Parse message_scene.ext to extract refMsgIdx and msgIdx.\n *\n * Supports both ext prefix formats:\n * - `ref_msg_idx=` / `msg_idx=` (platform native format)\n * - `refMsgIdx:` / `msgIdx:` (legacy internal format)\n *\n * When `messageType` equals `MSG_TYPE_QUOTE` (103) and `msgElements` is\n * provided, `msgElements[0].msg_idx` takes precedence over the ext-parsed\n * `refMsgIdx` value — the element-level index is more authoritative for\n * quote messages.\n */\nexport function parseRefIndices(\n ext?: string[],\n messageType?: number,\n msgElements?: Array<{ msg_idx?: string }>,\n): { refMsgIdx?: string; msgIdx?: string } {\n let refMsgIdx: string | undefined;\n let msgIdx: string | undefined;\n\n if (ext && ext.length > 0) {\n for (const item of ext) {\n if (typeof item !== \"string\") {\n continue;\n }\n if (item.startsWith(\"ref_msg_idx=\")) {\n refMsgIdx = item.slice(\"ref_msg_idx=\".length).trim();\n } else if (item.startsWith(\"msg_idx=\")) {\n msgIdx = item.slice(\"msg_idx=\".length).trim();\n } else if (item.startsWith(\"refMsgIdx:\")) {\n refMsgIdx = item.slice(\"refMsgIdx:\".length).trim();\n } else if (item.startsWith(\"msgIdx:\")) {\n msgIdx = item.slice(\"msgIdx:\".length).trim();\n }\n }\n }\n\n if (messageType === MSG_TYPE_QUOTE) {\n const refElement = msgElements?.[0];\n if (refElement?.msg_idx) {\n refMsgIdx = refElement.msg_idx;\n }\n }\n\n return { refMsgIdx, msgIdx };\n}\n\n// ============ Face tags ============\n\nconst MAX_FACE_EXT_BYTES = 64 * 1024;\n\n/** Estimate Base64 decoded byte size. */\nfunction estimateBase64Size(base64: string): number {\n const len = base64.length;\n const padding = base64.endsWith(\"==\") ? 2 : base64.endsWith(\"=\") ? 1 : 0;\n return Math.ceil((len * 3) / 4) - padding;\n}\n\n/** Replace QQ face tags with readable text labels. */\nexport function parseFaceTags(text: string | undefined | null): string {\n if (!text) {\n return \"\";\n }\n\n return text.replace(/<faceType=\\d+,faceId=\"[^\"]*\",ext=\"([^\"]*)\">/g, (_match, ext: string) => {\n try {\n if (estimateBase64Size(ext) > MAX_FACE_EXT_BYTES) {\n return \"[Emoji: unknown emoji]\";\n }\n const decoded = Buffer.from(ext, \"base64\").toString(\"utf-8\");\n const parsed = JSON.parse(decoded);\n const faceName = parsed.text || \"unknown emoji\";\n return `[Emoji: ${faceName}]`;\n } catch {\n return _match;\n }\n });\n}\n\n// ============ Attachment summaries ============\n\n/** Lowercase a string safely. */\nfunction lc(s: string | undefined | null): string {\n return (s ?? \"\").toLowerCase();\n}\n\n/** Build attachment summaries for ref-index caching. */\nexport function buildAttachmentSummaries(\n attachments?: Array<{\n content_type: string;\n url: string;\n filename?: string;\n voice_wav_url?: string;\n }>,\n localPaths?: Array<string | null>,\n): RefAttachmentSummary[] | undefined {\n if (!attachments || attachments.length === 0) {\n return undefined;\n }\n\n return attachments.map((att, idx) => {\n const ct = lc(att.content_type);\n let type: RefAttachmentSummary[\"type\"] = \"unknown\";\n if (ct.startsWith(\"image/\")) {\n type = \"image\";\n } else if (\n ct === \"voice\" ||\n ct.startsWith(\"audio/\") ||\n ct.includes(\"silk\") ||\n ct.includes(\"amr\")\n ) {\n type = \"voice\";\n } else if (ct.startsWith(\"video/\")) {\n type = \"video\";\n } else if (ct.startsWith(\"application/\") || ct.startsWith(\"text/\")) {\n type = \"file\";\n }\n\n return {\n type,\n filename: att.filename,\n contentType: att.content_type,\n localPath: localPaths?.[idx] ?? undefined,\n };\n });\n}\n","/**\n * Unified media-source abstraction for the QQ Bot upload pipeline.\n *\n * All rich-media entry points funnel through {@link normalizeSource}\n * before reaching {@link MediaApi}.\n *\n * ## Why four branches?\n *\n * - `url` — remote http(s) URL that the QQ server can fetch directly.\n * - `base64` — in-memory base64 string (typically from a `data:` URL).\n * - `localPath` — on-disk file; kept as a path so a future chunked-upload\n * implementation can stream it via `fs.createReadStream` without the 4/3×\n * base64 memory overhead.\n * - `buffer` — in-memory raw bytes (e.g. TTS output, downloaded url-fallback).\n *\n * ## Security baseline (localPath branch)\n *\n * `openLocalFile` is the canonical implementation of \"safely open a\n * local file for upload\":\n *\n * 1. `O_NOFOLLOW` refuses to traverse symlinks (prevents post-whitelist\n * symlink swaps / TOCTOU attacks).\n * 2. `fstat` on the opened descriptor — NOT `fs.stat` on the path —\n * so the size check applies to the exact byte stream we will read.\n * 3. Rejects non-regular files (sockets / devices / directories).\n * 4. Enforces a caller-specified `maxSize` ceiling at open time so\n * oversized files fail fast without allocating a full buffer.\n *\n * Callers are still responsible for *root-whitelist* validation (i.e.\n * \"this path must live under the allowed media directory\") before\n * passing the path in; this function enforces *file-level* safety only.\n */\n\nimport * as fs from \"node:fs\";\nimport { MAX_UPLOAD_SIZE, getMimeType } from \"./file-utils.js\";\nimport { formatFileSize } from \"./format.js\";\n\n// ============ Types ============\n\n/**\n * Fully normalized media source. Downstream uploaders switch on `kind`.\n *\n * - `url`: remote URL — upload via `file_data=null; url=...`.\n * - `base64`: already-encoded base64 — upload via `file_data=...`.\n * - `localPath`: on-disk file — one-shot path reads it into a buffer;\n * chunked path streams it via `fs.createReadStream`.\n * - `buffer`: raw bytes in memory — same as above minus disk I/O.\n */\nexport type MediaSource =\n | { kind: \"url\"; url: string }\n | { kind: \"base64\"; data: string; mime?: string }\n | { kind: \"localPath\"; path: string; size: number; mime?: string }\n | { kind: \"buffer\"; buffer: Buffer; fileName?: string; mime?: string };\n\n/**\n * Untyped media source accepted from callers.\n *\n * `url` may be either a remote `http(s)://...` URL or a `data:<mime>;base64,...`\n * data URL — {@link normalizeSource} transparently resolves the latter to a\n * `base64` branch.\n */\nexport type RawMediaSource =\n | { url: string }\n | { base64: string; mime?: string }\n | { localPath: string }\n | { buffer: Buffer; fileName?: string; mime?: string };\n\n// ============ data: URL ============\n\nconst DATA_URL_RE = /^data:([^;,]+);base64,(.+)$/i;\n\n/**\n * Parse a `data:<mime>;base64,<payload>` URL.\n *\n * Returns `null` when the string is not a data URL or does not declare\n * base64 encoding. Non-base64 data URLs are intentionally rejected\n * because the QQ upload API ingests raw base64, not arbitrary\n * URL-encoded payloads.\n */\nexport function tryParseDataUrl(value: string): { mime: string; data: string } | null {\n if (!value.startsWith(\"data:\")) {\n return null;\n }\n const m = value.match(DATA_URL_RE);\n if (!m) {\n return null;\n }\n return { mime: m[1], data: m[2] };\n}\n\n// ============ Local file safe open ============\n\n/**\n * Opened handle to a local file, with metadata already validated against\n * QQ upload limits.\n *\n * Callers MUST call {@link OpenedLocalFile.close} (typically in a `finally`).\n */\nexport interface OpenedLocalFile {\n handle: fs.promises.FileHandle;\n size: number;\n close(): Promise<void>;\n}\n\n/**\n * Open a local file for upload with defense-in-depth.\n *\n * See module documentation for the security guarantees enforced here.\n */\nexport async function openLocalFile(\n filePath: string,\n opts: { maxSize?: number } = {},\n): Promise<OpenedLocalFile> {\n const maxSize = opts.maxSize ?? MAX_UPLOAD_SIZE;\n const openFlags =\n fs.constants.O_RDONLY | (\"O_NOFOLLOW\" in fs.constants ? fs.constants.O_NOFOLLOW : 0);\n const handle = await fs.promises.open(filePath, openFlags);\n try {\n const stat = await handle.stat();\n if (!stat.isFile()) {\n throw new Error(\"Path is not a regular file\");\n }\n if (stat.size > maxSize) {\n throw new Error(\n `File is too large (${formatFileSize(stat.size)}); QQ Bot API limit is ${formatFileSize(maxSize)}`,\n );\n }\n return {\n handle,\n size: stat.size,\n close: () => handle.close(),\n };\n } catch (err) {\n await handle.close().catch(() => undefined);\n throw err;\n }\n}\n\n// ============ Normalization ============\n\n/**\n * Normalize a {@link RawMediaSource} into a {@link MediaSource}.\n *\n * - Strings passed via `{ url }` that start with `data:` are auto-resolved\n * to a `base64` branch.\n * - `localPath` branches open the file with {@link openLocalFile} solely to\n * validate size / regular-file / O_NOFOLLOW invariants. The handle is\n * closed immediately — actual reading is deferred to the uploader so\n * the chunked path can stream without double-reading.\n * - `buffer` branches enforce the same ceiling inline.\n *\n * `maxSize` defaults to {@link MAX_UPLOAD_SIZE} (20MB, one-shot upload limit).\n * Callers that dispatch to the chunked uploader should pass a larger ceiling\n * (e.g. `CHUNKED_UPLOAD_MAX_SIZE`, or a value derived from\n * `getMaxUploadSize(fileType)`).\n *\n * NOTE: Root-whitelist validation is a caller concern. This function\n * assumes the path has already passed such checks.\n */\nexport async function normalizeSource(\n raw: RawMediaSource,\n opts: { maxSize?: number } = {},\n): Promise<MediaSource> {\n const maxSize = opts.maxSize ?? MAX_UPLOAD_SIZE;\n\n if (\"url\" in raw) {\n const parsed = tryParseDataUrl(raw.url);\n if (parsed) {\n return { kind: \"base64\", data: parsed.data, mime: parsed.mime };\n }\n return { kind: \"url\", url: raw.url };\n }\n\n if (\"base64\" in raw) {\n return { kind: \"base64\", data: raw.base64, mime: raw.mime };\n }\n\n if (\"localPath\" in raw) {\n const opened = await openLocalFile(raw.localPath, { maxSize });\n try {\n return {\n kind: \"localPath\",\n path: raw.localPath,\n size: opened.size,\n mime: getMimeType(raw.localPath),\n };\n } finally {\n await opened.close();\n }\n }\n\n if (raw.buffer.length > maxSize) {\n throw new Error(\n `Buffer is too large (${formatFileSize(raw.buffer.length)}); QQ Bot API limit is ${formatFileSize(maxSize)}`,\n );\n }\n return {\n kind: \"buffer\",\n buffer: raw.buffer,\n fileName: raw.fileName,\n mime: raw.mime,\n };\n}\n\n// ============ Materialization helpers ============\n\n/**\n * Read a {@link MediaSource} into the `{ url?, fileData?, fileName? }` shape\n * expected by `MediaApi.uploadMedia` (one-shot upload path).\n *\n * Chunked upload should bypass this helper and feed the uploader directly\n * from the `localPath` / `buffer` branch.\n */\nexport async function materializeForOneShotUpload(\n source: MediaSource,\n): Promise<{ url?: string; fileData?: string; fileName?: string }> {\n switch (source.kind) {\n case \"url\":\n return { url: source.url };\n case \"base64\":\n return { fileData: source.data };\n case \"localPath\": {\n const opened = await openLocalFile(source.path);\n try {\n const buf = await opened.handle.readFile();\n return { fileData: buf.toString(\"base64\") };\n } finally {\n await opened.close();\n }\n }\n case \"buffer\":\n return {\n fileData: source.buffer.toString(\"base64\"),\n fileName: source.fileName,\n };\n default: {\n const _exhaustive: never = source;\n throw new Error(\n `materializeForOneShotUpload: unsupported MediaSource kind: ${JSON.stringify(_exhaustive)}`,\n );\n }\n }\n}\n","/**\n * Image dimension helpers for QQ Bot markdown image syntax.\n *\n * QQ Bot markdown images use `![#widthpx #heightpx](url)`.\n *\n * The header parsers are all pure functions. URL probing requires a\n * caller-provided {@link ImageBytesFetcher} so this module stays free of\n * networking / SSRF concerns — the OpenClaw plugin (or any embedder) is\n * responsible for enforcing public-network-only / hostname-allowlist\n * policies before any byte hits this code.\n */\n\nimport { Buffer } from \"node:buffer\";\nimport { formatErrorMessage } from \"./format.js\";\n\nexport interface ImageSize {\n width: number;\n height: number;\n}\n\n/** Default dimensions used when probing fails. */\nexport const DEFAULT_IMAGE_SIZE: ImageSize = { width: 512, height: 512 };\n\n// ============ Header parsers ============\n\n/** Parse image dimensions from the PNG header. */\nfunction parsePngSize(buffer: Buffer): ImageSize | null {\n // PNG signature: 89 50 4E 47 0D 0A 1A 0A\n if (buffer.length < 24) {\n return null;\n }\n if (buffer[0] !== 0x89 || buffer[1] !== 0x50 || buffer[2] !== 0x4e || buffer[3] !== 0x47) {\n return null;\n }\n // The IHDR chunk begins at byte 8, with width/height at 16..23.\n const width = buffer.readUInt32BE(16);\n const height = buffer.readUInt32BE(20);\n return { width, height };\n}\n\n/** Parse image dimensions from JPEG SOF0/SOF2 markers. */\nfunction parseJpegSize(buffer: Buffer): ImageSize | null {\n // JPEG signature: FF D8 FF\n if (buffer.length < 4) {\n return null;\n }\n if (buffer[0] !== 0xff || buffer[1] !== 0xd8) {\n return null;\n }\n\n let offset = 2;\n while (offset < buffer.length - 9) {\n if (buffer[offset] !== 0xff) {\n offset++;\n continue;\n }\n\n const marker = buffer[offset + 1];\n // SOF0 (0xC0) and SOF2 (0xC2) contain dimensions.\n if (marker === 0xc0 || marker === 0xc2) {\n // Layout: FF C0 length(2) precision(1) height(2) width(2)\n if (offset + 9 <= buffer.length) {\n const height = buffer.readUInt16BE(offset + 5);\n const width = buffer.readUInt16BE(offset + 7);\n return { width, height };\n }\n }\n\n if (offset + 3 < buffer.length) {\n const blockLength = buffer.readUInt16BE(offset + 2);\n offset += 2 + blockLength;\n } else {\n break;\n }\n }\n\n return null;\n}\n\n/** Parse image dimensions from the GIF header. */\nfunction parseGifSize(buffer: Buffer): ImageSize | null {\n if (buffer.length < 10) {\n return null;\n }\n const signature = buffer.toString(\"ascii\", 0, 6);\n if (signature !== \"GIF87a\" && signature !== \"GIF89a\") {\n return null;\n }\n const width = buffer.readUInt16LE(6);\n const height = buffer.readUInt16LE(8);\n return { width, height };\n}\n\n/** Parse image dimensions from WebP headers. */\nfunction parseWebpSize(buffer: Buffer): ImageSize | null {\n if (buffer.length < 30) {\n return null;\n }\n\n const riff = buffer.toString(\"ascii\", 0, 4);\n const webp = buffer.toString(\"ascii\", 8, 12);\n if (riff !== \"RIFF\" || webp !== \"WEBP\") {\n return null;\n }\n\n const chunkType = buffer.toString(\"ascii\", 12, 16);\n\n // VP8 (lossy)\n if (chunkType === \"VP8 \") {\n if (buffer.length >= 30 && buffer[23] === 0x9d && buffer[24] === 0x01 && buffer[25] === 0x2a) {\n const width = buffer.readUInt16LE(26) & 0x3fff;\n const height = buffer.readUInt16LE(28) & 0x3fff;\n return { width, height };\n }\n }\n\n // VP8L (lossless)\n if (chunkType === \"VP8L\") {\n if (buffer.length >= 25 && buffer[20] === 0x2f) {\n const bits = buffer.readUInt32LE(21);\n const width = (bits & 0x3fff) + 1;\n const height = ((bits >> 14) & 0x3fff) + 1;\n return { width, height };\n }\n }\n\n // VP8X (extended format)\n if (chunkType === \"VP8X\") {\n if (buffer.length >= 30) {\n const width = (buffer[24] | (buffer[25] << 8) | (buffer[26] << 16)) + 1;\n const height = (buffer[27] | (buffer[28] << 8) | (buffer[29] << 16)) + 1;\n return { width, height };\n }\n }\n\n return null;\n}\n\n/** Parse image dimensions from raw image bytes (PNG / JPEG / GIF / WebP). */\nexport function parseImageSize(buffer: Buffer): ImageSize | null {\n return (\n parsePngSize(buffer) ?? parseJpegSize(buffer) ?? parseGifSize(buffer) ?? parseWebpSize(buffer)\n );\n}\n\n// ============ URL / data-URL probing ============\n\n/**\n * Bytes fetcher contract used by {@link getImageSizeFromUrl}.\n *\n * Embedders inject their own implementation so SSRF policy, hostname\n * allowlists, and other transport concerns stay outside the SDK.\n */\nexport type ImageBytesFetcher = (url: string, opts: { maxBytes: number }) => Promise<Buffer>;\n\n/**\n * Fetch image dimensions from a URL using only the first 64 KB of bytes.\n *\n * The caller is responsible for SSRF safety: the {@link ImageBytesFetcher}\n * implementation should reject private/reserved/loopback/link-local/metadata\n * destinations before any byte is read.\n *\n * Returns `null` when the fetch fails or the bytes don't match any\n * supported image header.\n */\nexport async function getImageSizeFromUrl(\n url: string,\n fetchBytes: ImageBytesFetcher,\n opts: { maxBytes?: number; logger?: { debug?: (msg: string) => void } } = {},\n): Promise<ImageSize | null> {\n const maxBytes = opts.maxBytes ?? 65_536;\n try {\n const buffer = await fetchBytes(url, { maxBytes });\n const size = parseImageSize(buffer);\n if (size) {\n opts.logger?.debug?.(\n `[image-size] Got size from URL: ${size.width}x${size.height} - ${url.slice(0, 60)}...`,\n );\n }\n return size;\n } catch (err) {\n opts.logger?.debug?.(\n `[image-size] Error fetching ${url.slice(0, 60)}...: ${formatErrorMessage(err)}`,\n );\n return null;\n }\n}\n\n/** Parse image dimensions from a Base64 data URL. */\nexport function getImageSizeFromDataUrl(\n dataUrl: string,\n logger?: { debug?: (msg: string) => void },\n): ImageSize | null {\n try {\n // Format: data:image/png;base64,xxxxx\n const matches = dataUrl.match(/^data:image\\/[^;]+;base64,(.+)$/);\n if (!matches) {\n return null;\n }\n\n const base64Data = matches[1];\n const buffer = Buffer.from(base64Data, \"base64\");\n\n const size = parseImageSize(buffer);\n if (size) {\n logger?.debug?.(`[image-size] Got size from Base64: ${size.width}x${size.height}`);\n }\n\n return size;\n } catch (err) {\n logger?.debug?.(`[image-size] Error parsing Base64: ${formatErrorMessage(err)}`);\n return null;\n }\n}\n\n// ============ QQ Bot markdown image syntax ============\n\n/** Format a markdown image with QQ Bot width/height annotations. */\nexport function formatQQBotMarkdownImage(url: string, size: ImageSize | null): string {\n const { width, height } = size ?? DEFAULT_IMAGE_SIZE;\n return `![#${width}px #${height}px](${url})`;\n}\n\n/** Return true when markdown already contains QQ Bot size annotations. */\nexport function hasQQBotImageSize(markdownImage: string): boolean {\n return /!\\[#\\d+px\\s+#\\d+px\\]/.test(markdownImage);\n}\n\n/** Extract width and height from QQBot markdown image syntax: `![#Wpx #Hpx](url)`. */\nexport function extractQQBotImageSize(markdownImage: string): ImageSize | null {\n const match = markdownImage.match(/!\\[#(\\d+)px\\s+#(\\d+)px\\]/);\n if (match) {\n return { width: Number.parseInt(match[1], 10), height: Number.parseInt(match[2], 10) };\n }\n return null;\n}\n","/**\n * ffmpeg / silk-wasm runtime detection helpers.\n *\n * QQ voice messaging requires SILK-encoded audio. Two transcoding paths\n * are supported:\n * - **ffmpeg** (preferred): native decoder for any input format.\n * - **silk-wasm + mpg123-decoder** (fallback): WASM decoders for SILK\n * and MP3 respectively, used when ffmpeg is not installed on the host.\n *\n * Both paths are loaded **lazily** so SDK consumers that never send voice\n * messages don't pay the detection cost.\n */\n\nimport { execFile } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\n/** Return true when running on Windows. */\nexport function isWindows(): boolean {\n return process.platform === \"win32\";\n}\n\nlet _ffmpegPath: string | null | undefined;\nlet _ffmpegCheckPromise: Promise<string | null> | null = null;\n\n/**\n * Detect ffmpeg and return an executable path when available.\n *\n * The result is cached for the lifetime of the process; call\n * {@link resetFfmpegCache} between tests to force re-detection.\n *\n * Detection order:\n * 1. `FFMPEG_PATH` environment variable\n * 2. Plain `ffmpeg` (or `ffmpeg.exe`) on PATH\n * 3. Common install locations (Homebrew, apt, snap, choco, scoop)\n */\nexport function detectFfmpeg(): Promise<string | null> {\n if (_ffmpegPath !== undefined) {\n return Promise.resolve(_ffmpegPath);\n }\n if (_ffmpegCheckPromise) {\n return _ffmpegCheckPromise;\n }\n\n _ffmpegCheckPromise = (async () => {\n const envPath = process.env.FFMPEG_PATH;\n if (envPath) {\n const ok = await testExecutable(envPath, [\"-version\"]);\n if (ok) {\n _ffmpegPath = envPath;\n return _ffmpegPath;\n }\n }\n\n const cmd = isWindows() ? \"ffmpeg.exe\" : \"ffmpeg\";\n const ok = await testExecutable(cmd, [\"-version\"]);\n if (ok) {\n _ffmpegPath = cmd;\n return _ffmpegPath;\n }\n\n const commonPaths = isWindows()\n ? [\n \"C:\\\\ffmpeg\\\\bin\\\\ffmpeg.exe\",\n path.join(process.env.LOCALAPPDATA || \"\", \"Programs\", \"ffmpeg\", \"bin\", \"ffmpeg.exe\"),\n path.join(process.env.ProgramFiles || \"\", \"ffmpeg\", \"bin\", \"ffmpeg.exe\"),\n ]\n : [\n \"/usr/local/bin/ffmpeg\",\n \"/opt/homebrew/bin/ffmpeg\",\n \"/usr/bin/ffmpeg\",\n \"/snap/bin/ffmpeg\",\n ];\n\n for (const p of commonPaths) {\n if (p && fs.existsSync(p)) {\n const works = await testExecutable(p, [\"-version\"]);\n if (works) {\n _ffmpegPath = p;\n return _ffmpegPath;\n }\n }\n }\n\n _ffmpegPath = null;\n return null;\n })().finally(() => {\n _ffmpegCheckPromise = null;\n });\n\n return _ffmpegCheckPromise;\n}\n\n/** Return true when the executable responds successfully to the given args. */\nfunction testExecutable(cmd: string, args: string[]): Promise<boolean> {\n return new Promise((resolve) => {\n execFile(cmd, args, { timeout: 5000 }, (err) => {\n resolve(!err);\n });\n });\n}\n\n/** Reset ffmpeg detection state, mainly for tests. */\nexport function resetFfmpegCache(): void {\n _ffmpegPath = undefined;\n _ffmpegCheckPromise = null;\n}\n\nlet _silkWasmAvailable: boolean | null = null;\n\n/**\n * Check whether silk-wasm can run in the current environment.\n *\n * The SDK does NOT bundle `silk-wasm` as a hard dependency; this probe\n * dynamically imports it and falls back gracefully when the package is\n * not installed. Add `silk-wasm` to your dependencies (or peer\n * dependencies) when you need voice-message support.\n */\nexport async function checkSilkWasmAvailable(): Promise<boolean> {\n if (_silkWasmAvailable !== null) {\n return _silkWasmAvailable;\n }\n try {\n const { isSilk } = (await import(\"silk-wasm\")) as { isSilk: (data: Uint8Array) => boolean };\n isSilk(new Uint8Array(0));\n _silkWasmAvailable = true;\n } catch {\n _silkWasmAvailable = false;\n }\n return _silkWasmAvailable;\n}\n","/**\n * Audio format conversion utilities.\n *\n * Handles SILK ↔ PCM ↔ WAV ↔ MP3 conversions for QQ Bot voice messaging.\n * Prefers ffmpeg when available; falls back to WASM decoders (`silk-wasm`,\n * `mpg123-decoder`) for environments without native tooling.\n *\n * The SDK does NOT bundle `silk-wasm` / `mpg123-decoder` as hard\n * dependencies — both are loaded via `import()` and degrade to `null`\n * when missing. Add them to your application's dependencies when you\n * need voice-message support.\n */\n\nimport { execFile } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { detectFfmpeg, isWindows } from \"./ffmpeg.js\";\nimport { formatErrorMessage } from \"./format.js\";\n\n// ============ silk-wasm shim ============\n\n/**\n * Minimal typed surface of `silk-wasm` we depend on. Defined locally so the\n * SDK builds cleanly without having `silk-wasm` installed.\n */\ninterface SilkWasmModule {\n isSilk(data: Uint8Array): boolean;\n encode(\n data: Uint8Array,\n sampleRate: number,\n ): Promise<{ data: Uint8Array; duration: number }>;\n decode(\n data: Uint8Array,\n sampleRate: number,\n ): Promise<{ data: Uint8Array; duration: number }>;\n}\n\nlet _silkWasmPromise: Promise<SilkWasmModule | null> | null = null;\n\n/** Lazy-load the silk-wasm module (singleton cache; returns null on failure). */\nexport function loadSilkWasm(): Promise<SilkWasmModule | null> {\n if (_silkWasmPromise) {\n return _silkWasmPromise;\n }\n _silkWasmPromise = (async () => {\n try {\n const mod = (await import(\"silk-wasm\")) as unknown as SilkWasmModule;\n return mod;\n } catch {\n return null;\n }\n })();\n return _silkWasmPromise;\n}\n\n// ============ Logger ============\n\ninterface AudioLogger {\n log?: (msg: string) => void;\n warn?: (msg: string) => void;\n error?: (msg: string) => void;\n}\n\nconst NOOP_LOGGER: Required<AudioLogger> = {\n log: () => {},\n warn: () => {},\n error: () => {},\n};\n\nfunction resolveLogger(logger?: AudioLogger): Required<AudioLogger> {\n return {\n log: logger?.log ?? NOOP_LOGGER.log,\n warn: logger?.warn ?? NOOP_LOGGER.warn,\n error: logger?.error ?? NOOP_LOGGER.error,\n };\n}\n\n// ============ Utilities ============\n\nfunction lowerExt(p: string): string {\n return path.extname(p).toLowerCase();\n}\n\n/** Wrap raw PCM s16le data into a standard WAV file. */\nexport function pcmToWav(\n pcmData: Uint8Array,\n sampleRate: number,\n channels: number = 1,\n bitsPerSample: number = 16,\n): Buffer {\n const byteRate = sampleRate * channels * (bitsPerSample / 8);\n const blockAlign = channels * (bitsPerSample / 8);\n const dataSize = pcmData.length;\n const headerSize = 44;\n const fileSize = headerSize + dataSize;\n\n const buffer = Buffer.alloc(fileSize);\n\n buffer.write(\"RIFF\", 0);\n buffer.writeUInt32LE(fileSize - 8, 4);\n buffer.write(\"WAVE\", 8);\n\n buffer.write(\"fmt \", 12);\n buffer.writeUInt32LE(16, 16);\n buffer.writeUInt16LE(1, 20);\n buffer.writeUInt16LE(channels, 22);\n buffer.writeUInt32LE(sampleRate, 24);\n buffer.writeUInt32LE(byteRate, 28);\n buffer.writeUInt16LE(blockAlign, 32);\n buffer.writeUInt16LE(bitsPerSample, 34);\n\n buffer.write(\"data\", 36);\n buffer.writeUInt32LE(dataSize, 40);\n Buffer.from(pcmData.buffer, pcmData.byteOffset, pcmData.byteLength).copy(buffer, headerSize);\n\n return buffer;\n}\n\n/** Strip the AMR header that may be present in QQ voice payloads. */\nexport function stripAmrHeader(buf: Buffer): Buffer {\n const AMR_HEADER = Buffer.from(\"#!AMR\\n\");\n if (buf.length > 6 && buf.subarray(0, 6).equals(AMR_HEADER)) {\n return buf.subarray(6);\n }\n return buf;\n}\n\n/** Convert a SILK or AMR voice file to WAV format. */\nexport async function convertSilkToWav(\n inputPath: string,\n outputDir?: string,\n): Promise<{ wavPath: string; duration: number } | null> {\n if (!fs.existsSync(inputPath)) {\n return null;\n }\n\n const fileBuf = fs.readFileSync(inputPath);\n const strippedBuf = stripAmrHeader(fileBuf);\n const rawData = new Uint8Array(\n strippedBuf.buffer,\n strippedBuf.byteOffset,\n strippedBuf.byteLength,\n );\n\n const silk = await loadSilkWasm();\n if (!silk || !silk.isSilk(rawData)) {\n return null;\n }\n\n const sampleRate = 24000;\n const result = await silk.decode(rawData, sampleRate);\n const wavBuffer = pcmToWav(result.data, sampleRate);\n\n const dir = outputDir || path.dirname(inputPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n const baseName = path.basename(inputPath, path.extname(inputPath));\n const wavPath = path.join(dir, `${baseName}.wav`);\n fs.writeFileSync(wavPath, wavBuffer);\n\n return { wavPath, duration: result.duration };\n}\n\n/** Check whether an attachment is a voice file (by MIME type or extension). */\nexport function isVoiceAttachment(att: { content_type?: string; filename?: string }): boolean {\n if (att.content_type === \"voice\" || att.content_type?.startsWith(\"audio/\")) {\n return true;\n }\n const ext = att.filename ? lowerExt(att.filename) : \"\";\n return [\".amr\", \".silk\", \".slk\", \".slac\"].includes(ext);\n}\n\n/** Check whether a file path is a known audio format. */\nexport function isAudioFile(filePath: string, mimeType?: string): boolean {\n if (mimeType) {\n if (mimeType === \"voice\" || mimeType.startsWith(\"audio/\")) {\n return true;\n }\n }\n const ext = lowerExt(filePath);\n return [\n \".silk\",\n \".slk\",\n \".amr\",\n \".wav\",\n \".mp3\",\n \".ogg\",\n \".opus\",\n \".aac\",\n \".flac\",\n \".m4a\",\n \".wma\",\n \".pcm\",\n ].includes(ext);\n}\n\nconst QQ_NATIVE_VOICE_MIMES = new Set([\n \"audio/silk\",\n \"audio/amr\",\n \"audio/wav\",\n \"audio/wave\",\n \"audio/x-wav\",\n \"audio/mpeg\",\n \"audio/mp3\",\n]);\n\nconst QQ_NATIVE_VOICE_EXTS = new Set([\".silk\", \".slk\", \".amr\", \".wav\", \".mp3\"]);\n\n/** Check whether a voice file needs transcoding for upload (QQ-native formats skip it). */\nexport function shouldTranscodeVoice(filePath: string, mimeType?: string): boolean {\n if (mimeType && QQ_NATIVE_VOICE_MIMES.has(mimeType.toLowerCase())) {\n return false;\n }\n const ext = lowerExt(filePath);\n if (QQ_NATIVE_VOICE_EXTS.has(ext)) {\n return false;\n }\n return isAudioFile(filePath, mimeType);\n}\n\nconst QQ_NATIVE_UPLOAD_FORMATS = [\".wav\", \".mp3\", \".silk\"];\n\nfunction normalizeFormats(formats: string[]): string[] {\n return formats.map((f) => {\n const lower = f.toLowerCase();\n return lower.startsWith(\".\") ? lower : `.${lower}`;\n });\n}\n\n/**\n * Convert a local audio file to Base64-encoded SILK for QQ API upload.\n *\n * Attempts conversion via ffmpeg → WASM decoders → null fallback chain.\n * Direct-upload formats listed in `directUploadFormats` (defaults to\n * `.wav` / `.mp3` / `.silk`) skip transcoding entirely.\n */\nexport async function audioFileToSilkBase64(\n filePath: string,\n directUploadFormats?: string[],\n logger?: AudioLogger,\n): Promise<string | null> {\n const log = resolveLogger(logger);\n\n if (!fs.existsSync(filePath)) {\n return null;\n }\n\n const buf = fs.readFileSync(filePath);\n if (buf.length === 0) {\n log.error(`[audio-convert] file is empty: ${filePath}`);\n return null;\n }\n\n const ext = lowerExt(filePath);\n const uploadFormats = directUploadFormats\n ? normalizeFormats(directUploadFormats)\n : QQ_NATIVE_UPLOAD_FORMATS;\n if (uploadFormats.includes(ext)) {\n log.log(`[audio-convert] direct upload (QQ native format): ${ext} (${buf.length} bytes)`);\n return buf.toString(\"base64\");\n }\n\n if ([\".slk\", \".slac\"].includes(ext)) {\n const stripped = stripAmrHeader(buf);\n const raw = new Uint8Array(stripped.buffer, stripped.byteOffset, stripped.byteLength);\n const silk = await loadSilkWasm();\n if (silk?.isSilk(raw)) {\n log.log(`[audio-convert] SILK file, direct use: ${filePath} (${buf.length} bytes)`);\n return buf.toString(\"base64\");\n }\n }\n\n const rawCheck = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);\n const strippedCheck = stripAmrHeader(buf);\n const strippedRaw = new Uint8Array(\n strippedCheck.buffer,\n strippedCheck.byteOffset,\n strippedCheck.byteLength,\n );\n const silkForCheck = await loadSilkWasm();\n if (silkForCheck?.isSilk(rawCheck) || silkForCheck?.isSilk(strippedRaw)) {\n log.log(`[audio-convert] SILK detected by header: ${filePath} (${buf.length} bytes)`);\n return buf.toString(\"base64\");\n }\n\n const targetRate = 24000;\n\n const ffmpegCmd = await detectFfmpeg();\n if (ffmpegCmd) {\n try {\n log.log(\n `[audio-convert] ffmpeg (${ffmpegCmd}): converting ${ext} (${buf.length} bytes) → PCM s16le ${targetRate}Hz`,\n );\n const pcmBuf = await ffmpegToPCM(ffmpegCmd, filePath, targetRate);\n if (pcmBuf.length === 0) {\n log.error(`[audio-convert] ffmpeg produced empty PCM output`);\n return null;\n }\n const { silkBuffer } = await pcmToSilk(pcmBuf, targetRate);\n log.log(`[audio-convert] ffmpeg: ${ext} → SILK done (${silkBuffer.length} bytes)`);\n return silkBuffer.toString(\"base64\");\n } catch (err) {\n log.error(`[audio-convert] ffmpeg conversion failed: ${formatErrorMessage(err)}`);\n }\n }\n\n log.log(`[audio-convert] fallback: trying WASM decoders for ${ext}`);\n\n if (ext === \".pcm\") {\n const pcmBuf = Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength);\n const { silkBuffer } = await pcmToSilk(pcmBuf, targetRate);\n return silkBuffer.toString(\"base64\");\n }\n\n if (ext === \".wav\" || (buf.length >= 4 && buf.toString(\"ascii\", 0, 4) === \"RIFF\")) {\n const wavInfo = parseWavFallback(buf);\n if (wavInfo) {\n const { silkBuffer } = await pcmToSilk(wavInfo, targetRate);\n return silkBuffer.toString(\"base64\");\n }\n }\n\n if (ext === \".mp3\" || ext === \".mpeg\") {\n const pcmBuf = await wasmDecodeMp3ToPCM(buf, targetRate, logger);\n if (pcmBuf) {\n const { silkBuffer } = await pcmToSilk(pcmBuf, targetRate);\n log.log(`[audio-convert] WASM: MP3 → SILK done (${silkBuffer.length} bytes)`);\n return silkBuffer.toString(\"base64\");\n }\n }\n\n const installHint = isWindows()\n ? \"Install ffmpeg with choco install ffmpeg, scoop install ffmpeg, or from https://ffmpeg.org\"\n : process.platform === \"darwin\"\n ? \"Install ffmpeg with brew install ffmpeg\"\n : \"Install ffmpeg with sudo apt install ffmpeg or sudo yum install ffmpeg\";\n log.error(`[audio-convert] unsupported format: ${ext} (no ffmpeg available). ${installHint}`);\n return null;\n}\n\n/**\n * Wait for a file to appear and stabilize, then return its final size.\n *\n * Polls at `pollMs` intervals; returns 0 on timeout or persistent empty file.\n */\nexport async function waitForFile(\n filePath: string,\n timeoutMs: number = 30000,\n pollMs: number = 500,\n logger?: AudioLogger,\n): Promise<number> {\n const log = resolveLogger(logger);\n const start = Date.now();\n let lastSize = -1;\n let stableCount = 0;\n let fileExists = false;\n let fileAppearedAt = 0;\n let pollCount = 0;\n\n const emptyGiveUpMs = 10000;\n const noFileGiveUpMs = 15000;\n\n while (Date.now() - start < timeoutMs) {\n pollCount++;\n try {\n const stat = fs.statSync(filePath);\n if (!fileExists) {\n fileExists = true;\n fileAppearedAt = Date.now();\n log.log(\n `[audio-convert] waitForFile: file appeared (${stat.size} bytes, after ${Date.now() - start}ms): ${path.basename(filePath)}`,\n );\n }\n if (stat.size > 0) {\n if (stat.size === lastSize) {\n stableCount++;\n if (stableCount >= 2) {\n log.log(\n `[audio-convert] waitForFile: ready (${stat.size} bytes, waited ${Date.now() - start}ms, polls=${pollCount})`,\n );\n return stat.size;\n }\n } else {\n stableCount = 0;\n }\n lastSize = stat.size;\n } else {\n if (Date.now() - fileAppearedAt > emptyGiveUpMs) {\n log.error(\n `[audio-convert] waitForFile: file still empty after ${emptyGiveUpMs}ms, giving up: ${path.basename(filePath)}`,\n );\n return 0;\n }\n }\n } catch {\n if (!fileExists && Date.now() - start > noFileGiveUpMs) {\n log.error(\n `[audio-convert] waitForFile: file never appeared after ${noFileGiveUpMs}ms, giving up: ${path.basename(filePath)}`,\n );\n return 0;\n }\n }\n await new Promise((r) => setTimeout(r, pollMs));\n }\n\n try {\n const finalStat = fs.statSync(filePath);\n if (finalStat.size > 0) {\n log.warn(\n `[audio-convert] waitForFile: timeout but file has data (${finalStat.size} bytes), using it`,\n );\n return finalStat.size;\n }\n log.error(\n `[audio-convert] waitForFile: timeout after ${timeoutMs}ms, file exists but empty (0 bytes): ${path.basename(filePath)}`,\n );\n } catch {\n log.error(\n `[audio-convert] waitForFile: timeout after ${timeoutMs}ms, file never appeared: ${path.basename(filePath)}`,\n );\n }\n return 0;\n}\n\n/** Encode PCM s16le data into SILK format. */\nexport async function pcmToSilk(\n pcmBuffer: Buffer,\n sampleRate: number,\n): Promise<{ silkBuffer: Buffer; duration: number }> {\n const silk = await loadSilkWasm();\n if (!silk) {\n throw new Error(\"silk-wasm is not available; cannot encode PCM to SILK\");\n }\n const pcmData = new Uint8Array(pcmBuffer.buffer, pcmBuffer.byteOffset, pcmBuffer.byteLength);\n const result = await silk.encode(pcmData, sampleRate);\n return {\n silkBuffer: Buffer.from(result.data.buffer, result.data.byteOffset, result.data.byteLength),\n duration: result.duration,\n };\n}\n\n/** Use ffmpeg to convert any audio to mono 24 kHz PCM s16le. */\nexport function ffmpegToPCM(\n ffmpegCmd: string,\n inputPath: string,\n sampleRate: number = 24000,\n): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const args = [\n \"-i\",\n inputPath,\n \"-f\",\n \"s16le\",\n \"-ar\",\n String(sampleRate),\n \"-ac\",\n \"1\",\n \"-acodec\",\n \"pcm_s16le\",\n \"-v\",\n \"error\",\n \"pipe:1\",\n ];\n execFile(\n ffmpegCmd,\n args,\n {\n maxBuffer: 50 * 1024 * 1024,\n encoding: \"buffer\",\n ...(isWindows() ? { windowsHide: true } : {}),\n },\n (err, stdout) => {\n if (err) {\n reject(new Error(`ffmpeg failed: ${err.message}`));\n return;\n }\n resolve(stdout as unknown as Buffer);\n },\n );\n });\n}\n\n/**\n * Minimal typed surface of `mpg123-decoder` we depend on. Defined locally\n * so the SDK builds cleanly without having `mpg123-decoder` installed.\n */\ninterface Mpg123DecoderModule {\n MPEGDecoder: new () => {\n ready: Promise<unknown>;\n decode(data: Uint8Array): {\n samplesDecoded: number;\n sampleRate: number;\n channelData: Float32Array[];\n };\n free(): void;\n };\n}\n\n/** Decode MP3 to PCM via mpg123-decoder WASM (fallback when ffmpeg is unavailable). */\nexport async function wasmDecodeMp3ToPCM(\n buf: Buffer,\n targetRate: number,\n logger?: AudioLogger,\n): Promise<Buffer | null> {\n const log = resolveLogger(logger);\n try {\n const mod = (await import(\"mpg123-decoder\")) as unknown as Mpg123DecoderModule;\n const { MPEGDecoder } = mod;\n log.log(`[audio-convert] WASM MP3 decode: size=${buf.length} bytes`);\n const decoder = new MPEGDecoder();\n await decoder.ready;\n\n const decoded = decoder.decode(new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength));\n decoder.free();\n\n if (decoded.samplesDecoded === 0 || decoded.channelData.length === 0) {\n log.error(\n `[audio-convert] WASM MP3 decode: no samples (samplesDecoded=${decoded.samplesDecoded})`,\n );\n return null;\n }\n\n log.log(\n `[audio-convert] WASM MP3 decode: samples=${decoded.samplesDecoded}, sampleRate=${decoded.sampleRate}, channels=${decoded.channelData.length}`,\n );\n\n let floatMono: Float32Array;\n if (decoded.channelData.length === 1) {\n floatMono = decoded.channelData[0];\n } else {\n floatMono = new Float32Array(decoded.samplesDecoded);\n const channels = decoded.channelData.length;\n for (let i = 0; i < decoded.samplesDecoded; i++) {\n let sum = 0;\n for (let ch = 0; ch < channels; ch++) {\n sum += decoded.channelData[ch][i];\n }\n floatMono[i] = sum / channels;\n }\n }\n\n const s16 = new Uint8Array(floatMono.length * 2);\n const view = new DataView(s16.buffer);\n for (let i = 0; i < floatMono.length; i++) {\n const clamped = Math.max(-1, Math.min(1, floatMono[i]));\n const val = clamped < 0 ? clamped * 32768 : clamped * 32767;\n view.setInt16(i * 2, Math.round(val), true);\n }\n\n let pcm: Uint8Array = s16;\n if (decoded.sampleRate !== targetRate) {\n const inputSamples = s16.length / 2;\n const outputSamples = Math.round((inputSamples * targetRate) / decoded.sampleRate);\n const output = new Uint8Array(outputSamples * 2);\n const inView = new DataView(s16.buffer, s16.byteOffset, s16.byteLength);\n const outView = new DataView(output.buffer, output.byteOffset, output.byteLength);\n for (let i = 0; i < outputSamples; i++) {\n const srcIdx = (i * decoded.sampleRate) / targetRate;\n const idx0 = Math.floor(srcIdx);\n const idx1 = Math.min(idx0 + 1, inputSamples - 1);\n const frac = srcIdx - idx0;\n const s0 = inView.getInt16(idx0 * 2, true);\n const s1 = inView.getInt16(idx1 * 2, true);\n const sample = Math.round(s0 + (s1 - s0) * frac);\n outView.setInt16(i * 2, Math.max(-32768, Math.min(32767, sample)), true);\n }\n pcm = output;\n }\n\n return Buffer.from(pcm.buffer, pcm.byteOffset, pcm.byteLength);\n } catch (err) {\n log.error(`[audio-convert] WASM MP3 decode failed: ${formatErrorMessage(err)}`);\n if (err instanceof Error && err.stack) {\n log.error(`[audio-convert] stack: ${err.stack}`);\n }\n return null;\n }\n}\n\n/** Parse a standard PCM WAV and extract mono 24 kHz PCM data (fallback without ffmpeg). */\nexport function parseWavFallback(buf: Buffer): Buffer | null {\n if (buf.length < 44) {\n return null;\n }\n if (buf.toString(\"ascii\", 0, 4) !== \"RIFF\") {\n return null;\n }\n if (buf.toString(\"ascii\", 8, 12) !== \"WAVE\") {\n return null;\n }\n if (buf.toString(\"ascii\", 12, 16) !== \"fmt \") {\n return null;\n }\n\n const audioFormat = buf.readUInt16LE(20);\n if (audioFormat !== 1) {\n return null;\n }\n\n const channels = buf.readUInt16LE(22);\n const sampleRate = buf.readUInt32LE(24);\n const bitsPerSample = buf.readUInt16LE(34);\n if (bitsPerSample !== 16) {\n return null;\n }\n\n let offset = 36;\n while (offset < buf.length - 8) {\n const chunkId = buf.toString(\"ascii\", offset, offset + 4);\n const chunkSize = buf.readUInt32LE(offset + 4);\n if (chunkId === \"data\") {\n const dataStart = offset + 8;\n const dataEnd = Math.min(dataStart + chunkSize, buf.length);\n let pcm = new Uint8Array(buf.buffer, buf.byteOffset + dataStart, dataEnd - dataStart);\n\n if (channels > 1) {\n const samplesPerCh = pcm.length / (2 * channels);\n const mono = new Uint8Array(samplesPerCh * 2);\n const inV = new DataView(pcm.buffer, pcm.byteOffset, pcm.byteLength);\n const outV = new DataView(mono.buffer, mono.byteOffset, mono.byteLength);\n for (let i = 0; i < samplesPerCh; i++) {\n let sum = 0;\n for (let ch = 0; ch < channels; ch++) {\n sum += inV.getInt16((i * channels + ch) * 2, true);\n }\n outV.setInt16(i * 2, Math.max(-32768, Math.min(32767, Math.round(sum / channels))), true);\n }\n pcm = mono;\n }\n\n const targetRate = 24000;\n if (sampleRate !== targetRate) {\n const inSamples = pcm.length / 2;\n const outSamples = Math.round((inSamples * targetRate) / sampleRate);\n const out = new Uint8Array(outSamples * 2);\n const inV = new DataView(pcm.buffer, pcm.byteOffset, pcm.byteLength);\n const outV = new DataView(out.buffer, out.byteOffset, out.byteLength);\n for (let i = 0; i < outSamples; i++) {\n const src = (i * sampleRate) / targetRate;\n const i0 = Math.floor(src);\n const i1 = Math.min(i0 + 1, inSamples - 1);\n const f = src - i0;\n const s0 = inV.getInt16(i0 * 2, true);\n const s1 = inV.getInt16(i1 * 2, true);\n outV.setInt16(\n i * 2,\n Math.max(-32768, Math.min(32767, Math.round(s0 + (s1 - s0) * f))),\n true,\n );\n }\n pcm = out;\n }\n\n return Buffer.from(pcm.buffer, pcm.byteOffset, pcm.byteLength);\n }\n offset += 8 + chunkSize;\n }\n\n return null;\n}\n\nexport type { AudioLogger };\n","/**\n * STT (Speech-to-Text) 语音转文字服务\n *\n * 支持 OpenAI 兼容的 /audio/transcriptions 接口。\n * 配置优先级:\n * 1. channels.qqbot.stt(插件级)\n * 2. 框架级 audio model 配置\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport interface STTConfig {\n enabled: boolean;\n baseUrl: string;\n apiKey: string;\n model: string;\n}\n\n/**\n * 从 OpenClaw 配置中解析 STT 设置\n */\nexport function resolveSTTConfig(cfg: Record<string, unknown>): STTConfig | null {\n const channels = asRecord(cfg.channels);\n const qqbot = asRecord(channels?.qqbot);\n const sttCfg = asRecord(qqbot?.stt);\n\n // 显式禁用\n if (sttCfg?.enabled === false) {\n return null;\n }\n\n const models = asRecord(cfg.models);\n const providers = asRecord(models?.providers);\n\n // 1. 插件级 STT 配置\n if (sttCfg) {\n const providerId = readString(sttCfg, 'provider') ?? 'openai';\n const providerCfg = asRecord(providers?.[providerId]);\n const baseUrl = readString(sttCfg, 'baseUrl') ?? readString(providerCfg, 'baseUrl');\n const apiKey = readString(sttCfg, 'apiKey') ?? readString(providerCfg, 'apiKey');\n const model = readString(sttCfg, 'model') ?? 'whisper-1';\n if (baseUrl && apiKey) {\n return { enabled: true, baseUrl: baseUrl.replace(/\\/+$/, ''), apiKey, model };\n }\n }\n\n // 2. 框架级 audio model fallback\n const tools = asRecord(cfg.tools);\n const media = asRecord(tools?.media);\n const audio = asRecord(media?.audio);\n const audioModels = audio?.models;\n const audioModelEntry = Array.isArray(audioModels) ? asRecord(audioModels[0]) : undefined;\n if (audioModelEntry) {\n const providerId = readString(audioModelEntry, 'provider') ?? 'openai';\n const providerCfg = asRecord(providers?.[providerId]);\n const baseUrl = readString(audioModelEntry, 'baseUrl') ?? readString(providerCfg, 'baseUrl');\n const apiKey = readString(audioModelEntry, 'apiKey') ?? readString(providerCfg, 'apiKey');\n const model = readString(audioModelEntry, 'model') ?? 'whisper-1';\n if (baseUrl && apiKey) {\n return { enabled: true, baseUrl: baseUrl.replace(/\\/+$/, ''), apiKey, model };\n }\n }\n\n return null;\n}\n\n/**\n * 调用 STT 服务转录音频文件\n */\nexport async function transcribeAudio(\n audioPath: string,\n cfg: Record<string, unknown>,\n): Promise<string | null> {\n const sttCfg = resolveSTTConfig(cfg);\n if (!sttCfg) {\n return null;\n }\n\n const fileBuffer = fs.readFileSync(audioPath);\n const fileName = sanitizeFileName(path.basename(audioPath));\n const mime = guessMimeType(fileName);\n\n const form = new FormData();\n form.append('file', new Blob([fileBuffer], { type: mime }), fileName);\n form.append('model', sttCfg.model);\n\n const resp = await fetch(`${sttCfg.baseUrl}/audio/transcriptions`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${sttCfg.apiKey}` },\n body: form,\n });\n\n if (!resp.ok) {\n const detail = await resp.text().catch(() => '');\n throw new Error(`STT failed (HTTP ${resp.status}): ${detail.slice(0, 300)}`);\n }\n\n const result = (await resp.json()) as { text?: string };\n return result.text?.trim() || null;\n}\n\n// ── 内部工具函数 ──\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return undefined;\n}\n\nfunction readString(obj: Record<string, unknown> | undefined, key: string): string | undefined {\n const val = obj?.[key];\n if (typeof val === 'string' && val.trim()) {\n return val.trim();\n }\n return undefined;\n}\n\nfunction sanitizeFileName(name: string): string {\n return name.replace(/[^a-zA-Z0-9._-]/g, '_');\n}\n\nfunction guessMimeType(fileName: string): string {\n const ext = path.extname(fileName).toLowerCase();\n const mimeMap: Record<string, string> = {\n '.wav': 'audio/wav',\n '.mp3': 'audio/mpeg',\n '.ogg': 'audio/ogg',\n '.flac': 'audio/flac',\n '.m4a': 'audio/mp4',\n '.aac': 'audio/aac',\n '.silk': 'audio/silk',\n '.amr': 'audio/amr',\n '.pcm': 'audio/pcm',\n };\n return mimeMap[ext] ?? 'application/octet-stream';\n}\n","/**\n * 语音转录文本格式化工具\n *\n * 将 STT / ASR 转录结果格式化为用户可读文本,注入到 AI 的入站消息中。\n */\n\n/** 转录来源 */\nexport type TranscriptSource = 'stt' | 'asr' | 'fallback';\n\n/**\n * 单条语音转录结果\n *\n * 设计原则:行存自洽 — 单条语音的转录文本、来源、媒体引用全部聚合在\n * 一个对象中,避免与 `ProcessedAttachments` 上的多个平行数组按下标对齐。\n * 渲染层(如 `buildDynamicCtx`)通过 `transcripts.map(...)` 投影出\n * `paths` / `urls` / `asrTexts` 列表。\n */\nexport interface VoiceTranscript {\n /** 转录文本(STT/ASR/fallback 三选一的最终结果) */\n text: string;\n /** 转录来源 */\n source: TranscriptSource;\n /** 音频时长(秒) */\n duration?: number;\n /** 本地音频路径(下载/转码后的 wav 等) */\n localPath?: string;\n /** 远端音频 URL(未本地化时的兜底引用) */\n remoteUrl?: string;\n /** 平台原始 ASR 文本(不一定等于 `text`,例如 STT 成功时 text=STT 结果) */\n asrReferText?: string;\n}\n\n/**\n * 格式化单条或多条语音转录为文本\n */\nexport function formatVoiceText(transcripts: VoiceTranscript[]): string {\n if (transcripts.length === 0) {\n return '';\n }\n if (transcripts.length === 1) {\n const t = transcripts[0];\n const durationStr = t.duration ? ` (${formatDuration(t.duration)})` : '';\n return `[Voice message${durationStr}] ${t.text}`;\n }\n return transcripts.map((t, i) => {\n const durationStr = t.duration ? ` (${formatDuration(t.duration)})` : '';\n return `[Voice ${i + 1}${durationStr}] ${t.text}`;\n }).join('\\n');\n}\n\n/**\n * 格式化时长(秒 → \"Xs\" 或 \"M:SS\")\n */\nexport function formatDuration(seconds: number): string {\n if (seconds < 60) {\n return `${Math.round(seconds)}s`;\n }\n const mins = Math.floor(seconds / 60);\n const secs = Math.round(seconds % 60);\n return `${mins}:${secs.toString().padStart(2, '0')}`;\n}\n","/**\n * 远程媒体下载(动态加载 plugin-sdk/media-runtime,降级到 fetch)\n *\n * 新版本 openclaw 有 openclaw/plugin-sdk/media-runtime(含 SSRF 防护/重试/大小限制),\n * 旧版本不可用时降级到原生 fetch 直连。\n */\nimport { createRequire } from 'node:module';\nimport * as dns from 'node:dns';\nimport * as path from 'node:path';\nimport * as fs from 'node:fs';\nimport * as crypto from 'node:crypto';\nimport { getQQBotMediaDir } from '../utils/platform.js';\n\nconst req = createRequire(__filename);\n\ntype SaveRemoteMedia = (opts: {\n url: string;\n subdir?: string;\n originalFilename?: string;\n maxBytes?: number;\n timeoutMs?: number;\n}) => Promise<{ path: string }>;\n\nlet _save: SaveRemoteMedia | null | undefined;\n\nexport function downloadRemoteMedia(opts: {\n url: string;\n subdir?: string;\n originalFilename?: string;\n maxBytes?: number;\n timeoutMs?: number;\n}): Promise<{ path: string }> {\n if (_save === undefined) {\n try {\n const mod = req('openclaw/plugin-sdk/media-runtime') as { saveRemoteMedia: SaveRemoteMedia };\n _save = mod.saveRemoteMedia;\n } catch {\n _save = null;\n }\n }\n return _save ? _save(opts) : downloadViaFetch(opts);\n}\n\n// ── SSRF 防护 ──\n\nconst PRIVATE_RANGES: Array<[netmask: bigint, prefix: number]> = [\n [0x0A000000n, 8], // 10.0.0.0/8\n [0xAC100000n, 12], // 172.16.0.0/12\n [0xC0A80000n, 16], // 192.168.0.0/16\n [0x7F000000n, 8], // 127.0.0.0/8\n [0xA9FE0000n, 16], // 169.254.0.0/16\n [0xE0000000n, 4], // 224.0.0.0/4 (multicast)\n];\n\nfunction ipToBigInt(ip: string): bigint {\n return ip.split('.').reduce((acc, octet) => (acc << 8n) | BigInt(Number(octet)), 0n);\n}\n\nfunction isPrivateIP(ip: string): boolean {\n const val = ipToBigInt(ip);\n return PRIVATE_RANGES.some(([mask, prefix]) => (val >> (32n - BigInt(prefix))) === (mask >> (32n - BigInt(prefix))));\n}\n\nasync function assertSafeHostname(hostname: string): Promise<void> {\n const addresses = await dns.promises.resolve4(hostname).catch(() => []);\n if (addresses.length === 0) throw new Error(`DNS resolution failed: ${hostname}`);\n for (const addr of addresses) {\n if (isPrivateIP(addr)) {\n throw new Error(`SSRF blocked: ${hostname} resolves to private IP ${addr}`);\n }\n }\n}\n\n// ── 降级 fetch ──\n\n/** 降级:原生 fetch 直连(含 SSRF 防护、大小限制) */\nasync function downloadViaFetch(opts: {\n url: string;\n subdir?: string;\n originalFilename?: string;\n maxBytes?: number;\n timeoutMs?: number;\n}): Promise<{ path: string }> {\n const parsed = new URL(opts.url);\n if (parsed.protocol !== 'https:') {\n throw new Error(`Only HTTPS allowed: ${parsed.protocol}`);\n }\n await assertSafeHostname(parsed.hostname);\n\n const dir = getQQBotMediaDir(opts.subdir ?? 'downloads');\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n\n const resp = await fetch(opts.url, {\n signal: AbortSignal.timeout(opts.timeoutMs ?? 120_000),\n });\n if (!resp.ok) throw new Error(`Download HTTP ${resp.status}`);\n\n const maxBytes = opts.maxBytes ?? 500 * 1024 * 1024;\n const buf = Buffer.from(await resp.arrayBuffer());\n if (buf.length > maxBytes) throw new Error(`Download exceeds ${(maxBytes / 1024 / 1024).toFixed(0)}MB`);\n\n const ext = opts.originalFilename\n ? path.extname(opts.originalFilename) || '.bin'\n : '.bin';\n const name = opts.originalFilename\n ? path.basename(opts.originalFilename, path.extname(opts.originalFilename))\n : 'download';\n const rand = crypto.randomBytes(4).toString('hex');\n const filePath = path.join(dir, `${name}_${Date.now()}_${rand}${ext}`);\n fs.writeFileSync(filePath, buf);\n return { path: filePath };\n}\n","/**\n * Body 组装器(消息入站 body 组装)\n *\n * SDK 中间件链已经完成了所有预处理:\n * - ctx.message.content ← contentSanitizer(face 解析 + mention 清洗)\n * - ctx.state.quote ← quoteRef\n * - ctx.state.history ← historyBuffer\n * - ctx.state.mention ← mentionGate\n * - ctx.state.processedAttachments ← attachmentProcessor(语音 STT、图片 URL)\n *\n * 本模块仅负责\"按框架协议把上述上下文拼成最终字符串\",是纯函数。\n *\n * 输出字段语义(与框架 buildCtxPayload 语义一致):\n * - webBody → ctxPayload.Body (Web UI 展示)\n * - agentBody → ctxPayload.BodyForAgent (AI 看到的)\n * - rawBody → ctxPayload.RawBody / CommandBody(命令解析、审计)\n * - systemPrompt → ctxPayload.GroupSystemPrompt\n */\nimport type {\n MiddlewareContext,\n QQBotInboundMessage,\n ResolvedQuote,\n HistoryEntry,\n} from '@tencent-connect/qqbot-nodejs';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport type { ProcessedAttachments } from '../middleware/attachment.js';\nimport { getAdapters } from '../adapter/resolve.js';\n\n// ── 协议常量 ─────────────────────────────\nconst QUOTE_BEGIN = '[Quoted message begins]';\nconst QUOTE_END = '[Quoted message ends]';\nconst REF_BEGIN = '[Reference message begins]';\nconst REF_END = '[Reference message ends]';\nconst HISTORY_BEGIN = '[Chat history begins]';\nconst HISTORY_END = '[Chat history ends]';\nconst MERGE_CTX_BEGIN = '[Merged messages begins]';\nconst MERGE_CTX_END = '[Merged messages ends]';\nconst CURRENT_MSG = '[Current message]';\n\nexport interface AssembledBody {\n /** Web UI 展示用 body */\n webBody: string;\n /** AI 实际接收的 body(dynamicCtx + userMessage [+ history 前缀]) */\n agentBody: string;\n /** 原始 content(命令解析 / 审计用) */\n rawBody: string;\n /** 群系统提示(account.systemPrompt + 群级提示拼接) */\n systemPrompt?: string;\n}\n\n/**\n * 把 SDK 中间件链产出的所有上下文组装为框架规约的 body。\n */\nexport function assembleBody(\n ctx: MiddlewareContext,\n msg: QQBotInboundMessage,\n account: ResolvedQQBotAccount,\n getRuntime?: () => any,\n): AssembledBody {\n const rawBody = msg.content ?? '';\n const isGroup = msg.kind === 'group';\n\n const mentionState = ctx.state.mention as { wasMentioned?: boolean } | undefined;\n const wasMentioned = mentionState?.wasMentioned ?? false;\n const processed = ctx.state.processedAttachments as ProcessedAttachments | undefined;\n const quote = ctx.state.quote as ResolvedQuote | undefined;\n const history = ctx.state.history as HistoryEntry[] | undefined;\n const mergedMessages = ctx.state.mergedMessages as MiddlewareContext[] | undefined;\n\n // ── Layer 1: userContent(清洗后的文本 + 语音转录 + 附件描述) ──\n const userContent = buildUserContent(ctx.message.content ?? '', processed);\n\n // ── Layer 2: quotePart ──\n const quotePart = buildQuotePart(quote);\n\n // ── Layer 3: userMessage(群带 [sender] 前缀 + (@you),合并消息特殊处理) ──\n const userMessage = mergedMessages && mergedMessages.length > 0\n ? buildMergedUserMessage({ messages: mergedMessages, quotePart, isGroup, wasMentioned, getRuntime })\n : buildUserMessage({ msg, userContent, quotePart, isGroup, wasMentioned });\n\n // ── Layer 4: dynamicCtx(媒体元数据块 + msg_elements 上下文) ──\n const dynamicCtx = buildDynamicCtx(processed, msg, quote);\n\n // ── Layer 5: agentBody(命令直通 / 群被@时前置历史) ──\n const agentBody = buildAgentBody({\n userContent,\n base: dynamicCtx + userMessage,\n isGroup,\n wasMentioned,\n history,\n });\n\n // ── webBody(合并消息包含全部前置 + 最后一条 + quotePart,对齐 agentBody) ──\n const bodyContent = mergedMessages && mergedMessages.length > 0\n ? userMessage\n : `${quotePart}${userContent}`;\n\n // ── webBody 外层 envelope 渲染(用 formatInboundEnvelope 包装) ──\n const webBody = getRuntime\n ? renderWebBody(getRuntime, bodyContent, msg, isGroup)\n : bodyContent;\n\n const systemPrompt = account.systemPrompt?.trim() || undefined;\n\n return { webBody, agentBody, rawBody, systemPrompt };\n}\n\n// ── 局部组装函数 ─────────────────────────────────────────────\n\n/** Layer 1:sanitized + 语音转录 + 附件描述 */\nfunction buildUserContent(sanitizedRaw: string, processed: ProcessedAttachments | undefined): string {\n const sanitized = sanitizedRaw.trim();\n const voiceText = processed?.voiceText ?? '';\n const attachmentInfo = processed?.otherInfo ? `\\n${processed.otherInfo}` : '';\n\n if (voiceText) {\n return (sanitized ? `${sanitized}\\n${voiceText}` : voiceText) + attachmentInfo;\n }\n return sanitized + attachmentInfo;\n}\n\n/** Layer 2:[Quoted message begins]…[Quoted message ends] */\nfunction buildQuotePart(quote: ResolvedQuote | undefined): string {\n if (!quote) return '';\n const text = quote.text || 'Original content unavailable';\n return `${QUOTE_BEGIN}\\n${text}\\n${QUOTE_END}\\n${CURRENT_MSG}\\n`;\n}\n\n/** Layer 3:quote + [Sender] {content}{(@you)?} */\nfunction buildUserMessage(input: {\n msg: QQBotInboundMessage;\n userContent: string;\n quotePart: string;\n isGroup: boolean;\n wasMentioned: boolean;\n}): string {\n const { msg, userContent, quotePart, isGroup, wasMentioned } = input;\n const atYouTag = isGroup && wasMentioned ? ' (@you)' : '';\n\n if (isGroup) {\n const senderLabel = formatSenderLabel(msg.senderName, msg.senderId);\n return `${quotePart}[${senderLabel}] ${userContent}${atYouTag}`;\n }\n return `${quotePart}${userContent}`;\n}\n\n/** Layer 3(合并版):concurrencyGuard merge 透传的多条消息 */\nfunction buildMergedUserMessage(input: {\n messages: MiddlewareContext[];\n quotePart: string;\n isGroup: boolean;\n wasMentioned: boolean;\n getRuntime?: () => any;\n}): string {\n const { messages, quotePart, isGroup, wasMentioned, getRuntime } = input;\n if (messages.length <= 1) {\n const single = messages[0]!;\n return buildUserMessage({\n msg: single.message,\n userContent: single.message.content ?? '',\n quotePart,\n isGroup,\n wasMentioned,\n });\n }\n\n const formatEnvelope = getRuntime\n ? getAdapters(getRuntime()).formatEnvelope\n : null;\n\n const lines = messages.map((ctx, i) => {\n const isLast = i === messages.length - 1;\n const line = formatMergedLine(ctx, { isGroup, isLast, wasMentioned, formatEnvelope });\n return line && isLast ? `${quotePart}${line}` : line;\n }).filter(Boolean);\n\n if (!isGroup || allSameSender(messages)) {\n return lines.join('\\n');\n }\n\n const last = lines.pop()!;\n return [MERGE_CTX_BEGIN, ...lines, MERGE_CTX_END, CURRENT_MSG, last].join('\\n');\n}\n\nfunction formatMergedLine(\n ctx: MiddlewareContext,\n opts: { isGroup: boolean; isLast: boolean; wasMentioned: boolean; formatEnvelope: ((p: Record<string, unknown>) => string) | null },\n): string {\n const m = ctx.message;\n const content = (m.content ?? '').trim();\n\n if (opts.formatEnvelope && opts.isGroup) {\n const atYouTag = opts.isLast && opts.wasMentioned ? ' (@you)' : '';\n return opts.formatEnvelope({\n channel: 'qqbot',\n from: formatSenderLabel(m.senderName, m.senderId),\n timestamp: normalizeTimestamp(m.timestamp),\n body: content + atYouTag,\n chatType: 'group',\n });\n }\n return opts.isGroup\n ? `${formatSenderLabel(m.senderName, m.senderId)}: ${content}`\n : content;\n}\n\nfunction allSameSender(messages: MiddlewareContext[]): boolean {\n const first = messages[0]?.message.senderId;\n return messages.every((c) => c.message.senderId === first);\n}\n\n/** Layer 4:- Images / - Voice / - ASR 元数据块 + msg_elements 引用上下文 */\nfunction buildDynamicCtx(\n processed: ProcessedAttachments | undefined,\n msg: QQBotInboundMessage,\n quote: ResolvedQuote | undefined,\n): string {\n const lines: string[] = [];\n\n // Images\n if (processed?.imageUrls.length) {\n lines.push(`- Images: ${processed.imageUrls.join(', ')}`);\n }\n\n // Voice:从 transcripts 行存投影出 paths + urls,去重后拼接\n const transcripts = processed?.transcripts ?? [];\n const voiceRefs = unique([\n ...transcripts.map((t) => t.localPath).filter(isNonEmpty),\n ...transcripts.map((t) => t.remoteUrl).filter(isNonEmpty),\n ]);\n if (voiceRefs.length > 0) {\n lines.push(`- Voice: ${voiceRefs.join(', ')}`);\n }\n\n // ASR:source==='asr' 的 text,或任意 transcript 上的 asrReferText\n const asrTexts = unique(\n transcripts\n .map((t) => (t.source === 'asr' ? t.text : t.asrReferText))\n .filter(isNonEmpty),\n );\n if (asrTexts.length > 0) {\n lines.push(`- ASR: ${asrTexts.join(' | ')}`);\n }\n\n // msg_elements 上下文(仅当非引用消息时解析,避免与 quotePart 重复)\n if (!quote) {\n const elementsCtx = buildMsgElementsContext(msg);\n if (elementsCtx.length > 0) {\n lines.push(REF_BEGIN, ...elementsCtx, REF_END);\n }\n }\n\n return lines.length > 0 ? `${lines.join('\\n')}\\n\\n` : '';\n}\n\n/** 从 msg_elements 提取上下文(非引用消息也解析,如被回复的 bot 消息等) */\nfunction buildMsgElementsContext(msg: QQBotInboundMessage): string[] {\n const elements = msg.msgElements;\n if (!elements || elements.length === 0) return [];\n\n const lines: string[] = [];\n let index = 0;\n for (const el of elements) {\n const content = el.content?.trim();\n if (!content) continue;\n index += 1;\n\n const author = (el as Record<string, unknown>).author as\n | { username?: string }\n | undefined;\n const sender = author?.username ?? '未知';\n\n lines.push(\n `=== 消息 ${index} ===`,\n `[消息内容] ${content}`,\n `[发送者] ${sender}`,\n );\n }\n\n return lines;\n}\n\nfunction unique<T>(arr: T[]): T[] {\n return [...new Set(arr)];\n}\n\nfunction isNonEmpty(x: string | undefined): x is string {\n return typeof x === 'string' && x.length > 0;\n}\n\n/** Layer 5:base = dynamicCtx+userMessage;命令直通;群被@叠历史前缀 */\nfunction buildAgentBody(input: {\n userContent: string;\n base: string;\n isGroup: boolean;\n wasMentioned: boolean;\n history: HistoryEntry[] | undefined;\n}): string {\n const { userContent, base, isGroup, wasMentioned, history } = input;\n\n // 斜杠命令直通:去除一切装饰及首尾空白\n if (userContent.trim().startsWith('/')) {\n return userContent;\n }\n\n // 群被@ + 有历史 → 叠历史前缀\n if (isGroup && wasMentioned && history && history.length > 0) {\n const historyText = history\n .map((h) => {\n const label = formatSenderLabel(h.senderName, h.senderId);\n return `[${label}] ${h.content}`;\n })\n .join('\\n');\n return [HISTORY_BEGIN, historyText, '', HISTORY_END, CURRENT_MSG, base].join('\\n');\n }\n\n return base;\n}\n\n/** \"Nick (openid)\" 标签;当 name 已含 id 时避免双重包裹 */\nfunction formatSenderLabel(name: string | undefined, id: string): string {\n if (!name) return id;\n return name.includes(id) ? name : `${name} (${id})`;\n}\n\n/** webBody 外层 envelope 渲染 */\nfunction renderWebBody(\n getRuntime: () => any,\n bodyContent: string,\n msg: QQBotInboundMessage,\n isGroup: boolean,\n): string {\n try {\n const { formatEnvelope } = getAdapters(getRuntime());\n if (!formatEnvelope) return bodyContent;\n return formatEnvelope({\n channel: 'qqbot',\n from: msg.senderName ?? msg.senderId,\n timestamp: normalizeTimestamp(msg.timestamp),\n body: bodyContent,\n chatType: isGroup ? 'group' : 'direct',\n });\n } catch {\n return bodyContent;\n }\n}\n\n/** timestamp 归一化到 epoch ms */\nfunction normalizeTimestamp(ts: string | number | undefined): number {\n if (typeof ts === 'number') return ts;\n if (typeof ts === 'string') {\n const d = new Date(ts).getTime();\n if (!Number.isNaN(d)) return d;\n }\n return Date.now();\n}\n","/**\n * 动态策略注入中间件\n *\n * 每条消息到达时,按 groupId 解析群配置并注入 `ctx.state.policy`。\n * SDK 内置中间件(mentionGate、historyBuffer 等)自动从 `ctx.state.policy`\n * 读取动态策略作为 fallback,无需各自注册 `resolveConfig`。\n *\n * 优先级链(与旧版一致):\n * 具体群配置 > 通配符 \"*\" > defaultRequireMention > 硬编码默认值\n */\nimport type { Middleware } from '@tencent-connect/qqbot-nodejs';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport { resolveGroupConfigFromAccount } from '../config.js';\n\n/**\n * 创建 policy injector 中间件。\n *\n * 注入的 `ctx.state.policy` 结构:\n * ```\n * {\n * scope: \"c2c\" | \"group\",\n * group: {\n * requireMention: boolean,\n * ignoreOtherMentions: boolean,\n * historyLimit: number,\n * // ... 可扩展任意字段\n * }\n * }\n * ```\n *\n * 使用者可在 `policy.group` 上添加自定义字段,如:\n * ```\n * ctx.state.policy.toolPolicy = \"full\";\n * ```\n * 自定义中间件通过 `ctx.state.policy` 读取即可。\n */\nexport function createPolicyInjector(account: ResolvedQQBotAccount): Middleware {\n return async (ctx, next) => {\n const msg = ctx.message as any;\n const scope = msg.kind as 'c2c' | 'group' | 'dm' | 'channel';\n\n const policy: Record<string, unknown> = {\n scope,\n accountId: account.accountId,\n // 访问控制:dmPolicy(c2c) / groupPolicy(group),默认 allowlist\n c2cMode: account.config?.dmPolicy ?? 'allowlist',\n groupMode: account.config?.groupPolicy ?? 'allowlist',\n allowFrom: account.config?.allowFrom ?? [],\n groupAllowFrom: account.config?.groupAllowFrom ?? [],\n };\n\n if (scope === 'group') {\n const groupOpenid = msg.groupOpenid ?? '';\n const groupCfg = resolveGroupConfigFromAccount(account, groupOpenid);\n policy.group = {\n requireMention: groupCfg.requireMention,\n ignoreOtherMentions: groupCfg.ignoreOtherMentions,\n historyLimit: groupCfg.historyLimit,\n prompt: groupCfg.prompt,\n };\n }\n\n ctx.state.policy = policy;\n await next();\n };\n}\n","import { MemoryHistoryStore } from '@tencent-connect/qqbot-nodejs';\nimport type { HistoryStore } from '@tencent-connect/qqbot-nodejs';\n\nlet _store: HistoryStore | null = null;\n\nexport function getHistoryStore(): HistoryStore {\n if (!_store) _store = new MemoryHistoryStore();\n return _store;\n}\n\n/** 用 accountId 前缀隔离多账号,避免同群历史串用 */\nexport function historyGroupKey(accountId: string, groupId: string): string {\n return `${accountId}:${groupId}`;\n}\n\n/** 清空群历史(dispatch 完成后调用) */\nexport function clearGroupHistory(accountId: string, groupId: string): void {\n _store?.clear?.(historyGroupKey(accountId, groupId));\n}\n","/**\n * 动态访问控制中间件。\n *\n * 从 ctx.state.policy(由 policy-injector 注入)动态读取策略,支持配置热更新。\n * 模式:disabled | open | allowlist | pairing(仅 c2c)\n */\nimport type { Middleware } from '@tencent-connect/qqbot-nodejs';\nimport { getPairingApi } from '../adapter/pairing.js';\n\n\n/**\n * 创建动态访问控制中间件。\n *\n * 决策优先级:\n * 1. disabled → 拒绝\n * 2. open → 放行\n * 3. allowlist → allowFrom 匹配(默认模式)\n * 4. pairing → allowFrom + pairing store,未配对发起挑战\n */\nexport function dynamicAccessControl(params: {\n accountId: string;\n getRuntime: () => any;\n}): Middleware {\n const { accountId, getRuntime } = params;\n\n return async (ctx, next) => {\n const p = ctx.state.policy as Record<string, unknown> | undefined;\n const isGroup = ctx.message.kind === 'group';\n const mode: string = isGroup\n ? (p?.groupMode as string) ?? 'open'\n : (p?.c2cMode as string) ?? 'allowlist';\n\n if (mode === 'disabled') {\n ctx.log?.info?.(`[access] blocked ${isGroup ? 'group' : 'c2c'} from ${ctx.message.senderId}: policy disabled`);\n ctx.stop('access:policy_disabled');\n return;\n }\n if (mode === 'open') {\n await next();\n return;\n }\n\n // allowlist / pairing:c2c 用 allowFrom,group 用 groupAllowFrom\n const allowList = isGroup\n ? ((p?.groupAllowFrom as string[]) ?? [])\n : ((p?.allowFrom as string[]) ?? []);\n if (!allowList.length || allowList.includes('*')) {\n await next();\n return;\n }\n\n const id = isGroup\n ? (ctx.message.groupOpenid ?? '')\n : (ctx.message.senderId as string);\n if (allowList.includes(id)) {\n await next();\n return;\n }\n\n // allowlist 未匹配 → pairing 模式尝试 pairing store(仅 c2c)\n if (mode === 'pairing' && !isGroup) {\n await checkPairingMode(ctx, next, {\n accountId,\n getRuntime,\n senderId: ctx.message.senderId as string,\n });\n return;\n }\n\n // 未匹配 → 拒绝\n const listLabel = isGroup ? 'groupAllowFrom' : 'allowFrom';\n ctx.log?.info?.(\n `[access] blocked ${isGroup ? 'group' : 'c2c'} from ${id}: not in ${listLabel}`,\n );\n ctx.stop('access:not_allowlisted');\n };\n}\n\n/**\n * pairing 模式:检查 pairing store + 发起配对挑战。\n */\nasync function checkPairingMode(\n ctx: any,\n next: () => Promise<void>,\n opts: { accountId: string; getRuntime: () => any; senderId: string },\n): Promise<void> {\n const api = getPairingApi();\n if (!api) {\n ctx.log?.info?.(`[access] pairing unavailable for ${opts.senderId}`);\n ctx.stop('access:pairing_unavailable');\n return;\n }\n\n try {\n const storeIds = await api.readAllowFromStore({\n channel: 'qqbot',\n accountId: opts.accountId,\n });\n if (storeIds.includes(opts.senderId) || storeIds.includes('*')) {\n await next();\n return;\n }\n\n const challenge = await api.issueChallenge({\n channel: 'qqbot',\n id: opts.senderId,\n accountId: opts.accountId,\n });\n const frameworkReply = api.buildReply({\n code: challenge.code,\n channel: 'qqbot',\n });\n const reply = [\n frameworkReply,\n '',\n 'QQ 管理员可直接执行:',\n '',\n '```',\n `/bot-pairing approve ${challenge.code}`,\n '```',\n ].join('\\n');\n\n ctx.log?.info?.(`[access] pairing required for ${opts.senderId}`);\n await ctx.bot.sendText(ctx.replyTarget, reply).catch(() => {/* ignore */});\n ctx.stop('access:pairing_required');\n } catch (err) {\n ctx.log?.error?.(`[access] pairing error: ${(err as Error).message}`);\n ctx.stop(`access:pairing_error: ${(err as Error).message}`);\n }\n}\n","/**\n * Mention 工具函数\n *\n * @mention 检测与清理,供 channel.ts 和 gateway 层使用。\n */\n\nexport interface MentionEntry {\n member_openid?: string;\n id?: string;\n user_openid?: string;\n is_you?: boolean;\n nickname?: string;\n username?: string;\n}\n\n/** 清理 @mention:替换 <@openid> 为 @用户名,去除 @机器人自身 */\nexport function stripMentionText(text: string, mentions?: MentionEntry[]): string {\n if (!text || !mentions?.length) return text;\n let cleaned = text;\n for (const m of mentions) {\n const openid = m.member_openid ?? m.id ?? m.user_openid;\n if (!openid) continue;\n if (m.is_you) {\n cleaned = cleaned.replace(new RegExp(`<@!?${openid}>`, 'g'), '').trim();\n } else {\n const displayName = m.nickname ?? m.username;\n if (displayName) {\n cleaned = cleaned.replace(new RegExp(`<@!?${openid}>`, 'g'), `@${displayName}`);\n }\n }\n }\n return cleaned;\n}\n\n/** 检测消息是否 @了机器人 */\nexport function detectWasMentioned({ eventType, mentions, content, mentionPatterns }: {\n eventType?: string;\n mentions?: Array<{ is_you?: boolean }>;\n content?: string;\n mentionPatterns?: string[];\n}): boolean {\n if (mentions?.some((m) => m.is_you)) return true;\n if (eventType === 'GROUP_AT_MESSAGE_CREATE') return true;\n if (mentionPatterns?.length && content) {\n for (const pattern of mentionPatterns) {\n try {\n if (new RegExp(pattern, 'i').test(content)) return true;\n } catch { /* 无效正则,跳过 */ }\n }\n }\n return false;\n}\n","/**\n * SDK 中间件编排\n *\n * 根据账户配置组装 SDK 内置中间件链。\n * 中间件负责过滤和上下文富化;concurrencyGuard 负责串行+合并,\n * 合并后的消息继续走完剩余中间件链,最终统一由 bot.on(\"message\") 处理转发。\n */\nimport type { QQBot, MiddlewareContext } from '@tencent-connect/qqbot-nodejs';\nimport {\n messageFilter,\n contentSanitizer,\n rateLimiter,\n concurrencyGuard,\n mentionGate,\n quoteRef,\n historyBuffer,\n envelopeFormatter,\n typingIndicator,\n slashCommand,\n errorHandler,\n} from '@tencent-connect/qqbot-nodejs';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport { buildCommandList } from '../commands/index.js';\nimport { attachmentProcessor } from '../middleware/attachment.js';\nimport { assembleBody } from '../dispatch/body-assembler.js';\nimport { getPersistedRefIndexStore } from '../features/ref-index-store.js';\nimport { createPolicyInjector } from '../middleware/policy-injector.js';\nimport { getHistoryStore, historyGroupKey } from '../features/history-store.js';\nimport { dynamicAccessControl } from '../middleware/access-control.js';\nimport { stripMentionText } from '../utils/mention.js';\n\nexport interface MiddlewareSetupOptions {\n /** 获取 runtime */\n getRuntime: () => any;\n}\n\n/**\n * 为 QQBot 实例编排完整的中间件链\n */\nexport function setupMiddlewares(bot: QQBot, account: ResolvedQQBotAccount, opts: MiddlewareSetupOptions): void {\n // 1. 错误兜底(最外层洋葱皮)\n bot.use(errorHandler());\n\n // 2. 消息过滤:bot 回声 + 消息去重\n bot.use(messageFilter({ skipSelfEcho: false }));\n\n // 3. 动态策略注入 — 每条消息注入 ctx.state.policy\n // 后续 dynamicAccessControl / mentionGate / historyBuffer 自动读取\n bot.use(createPolicyInjector(account));\n\n // 4. 群历史缓冲 — 放在门控之前,确保所有消息(含未 @bot)都计入上下文\n // limit 从 ctx.state.policy.group.historyLimit 读取,key 带 accountId 前缀隔离多账号\n bot.use(historyBuffer({\n store: getHistoryStore(),\n groupKey: (ctx) => {\n const gid = ctx.message.groupOpenid;\n if (ctx.message.kind !== 'group' || !gid) return undefined;\n return historyGroupKey(account.accountId, gid);\n },\n }));\n\n // 5. 动态访问控制 — 从 ctx.state.policy 动态读取,支持 pairing\n bot.use(dynamicAccessControl({\n accountId: account.accountId,\n getRuntime: opts.getRuntime,\n }));\n\n // 6. 群聊 @bot 门控(从 ctx.state.policy.group 读取动态配置)\n bot.use(mentionGate());\n // 7. 内容清洗(去 @marker、表情标签、多余空白)\n // SDK 用 appId 匹配 @标记,但 QQ openid 不等于 appId,追加 stripMentionText 正确剥离\n bot.use(contentSanitizer({\n parseFaceTags: true,\n transform: (content, ctx) => stripMentionText(content, (ctx.message as any).mentions),\n }));\n\n // 8. 三层限流(sender / group / global)\n bot.use(rateLimiter());\n\n // 9. 斜杠命令(在并发锁之前,命令匹配后直接 reply + stop,不排队)\n // 依赖:ctx.state.policy(policyInjector, #3)、ctx.message.*(原始消息)\n // 注意:/stop 是框架级命令,不在 qqbot 命令列表中,仍由 urgentPredicate 处理\n const slash = slashCommand({ commands: buildCommandList(account, { getRuntime: opts.getRuntime }) });\n bot.use(slash.middleware);\n\n // 10. 并发串行+合并(在副作用中间件之前)\n // - 同 peer 串行处理,避免平台 session conflict\n // - 处理中消息暂存 buffer;完成后合并为一条,继续走完剩余中间件链\n // (typingIndicator/quoteRef/attachmentProcessor/... 直到 bot.on(\"message\"))\n // - 合并时清除 assembledBody 让 dispatch.ts 用合并后 content 重建\n // - 超时后 abort 处理链(取消 LLM 调用),释放锁并排空缓冲\n bot.use(concurrencyGuard({\n strategy: 'merge',\n maxQueue: 50,\n maxProcessingMs: account.processingTimeoutMs,\n /** 紧急指令(/stop)跳过排队,立即处理 */\n urgentPredicate: (ctx: MiddlewareContext) => {\n return (ctx.message.content as string ?? '').trim() === '/stop';\n },\n onMerge: (buffered) => {\n const last = buffered[buffered.length - 1];\n if (buffered.length === 1) return last;\n\n // 透传原始消息列表,格式拼接下沉给下游 envelopeFormatter / assembleBody\n (last.state as Record<string, unknown>).mergedMessages = buffered;\n\n // 合并附件(所有 buffer 中的附件汇总到 survivor)\n const attachments = buffered.flatMap((c) => c.message.attachments ?? []);\n if (attachments.length > 0) {\n last.message.attachments = attachments;\n }\n\n // 清除 assembledBody,让 dispatch.ts 用合并后的 ctx 重新构建\n delete (last.state as Record<string, unknown>).assembledBody;\n\n return last;\n },\n }));\n\n // 11. C2C 输入状态指示器\n bot.use(typingIndicator());\n\n // 12. 引用消息解析(默认优先 msg_elements 获取文件名等丰富信息)\n bot.use(quoteRef({\n store: getPersistedRefIndexStore(account.accountId),\n }));\n\n // 13. 附件处理(语音 STT 转录 + 图片/文件下载)\n bot.use(attachmentProcessor({ getRuntime: opts.getRuntime }));\n\n // 14. 上下文组装(构建框架规约的 body)\n bot.use(envelopeFormatter({\n format: (ctx) => {\n const assembled = assembleBody(ctx, ctx.message as never, account, opts.getRuntime);\n (ctx.state as Record<string, unknown>).assembledBody = assembled;\n return assembled.agentBody;\n },\n }));\n}\n","/**\n * 信封构建器\n *\n * 将 SDK MiddlewareContext 中由各中间件填充的 state 数据\n * 转换为 OpenClaw 框架标准的 InboundMessage 结构。\n */\nimport type { MiddlewareContext, QQBotInboundMessage, HistoryEntry, ResolvedQuote } from '@tencent-connect/qqbot-nodejs';\nimport type { ResolvedQQBotAccount } from '../types.js';\n\n/**\n * OpenClaw 入站消息结构(传递给 runtime.channel.dispatch)\n */\nexport interface OpenClawInboundMessage {\n channelId: string;\n accountId: string;\n targetId: string;\n chatScope: 'direct' | 'group';\n senderId: string;\n senderName?: string;\n messageId: string;\n content: string;\n history?: Array<{ role: string; content: string; senderId?: string; senderName?: string }>;\n quote?: { content: string; senderId: string; attachments?: unknown[] };\n attachments?: unknown[];\n /** 入站图片 URL 列表(从附件中提取) */\n imageUrls?: string[];\n groupId?: string;\n systemPrompt?: string;\n}\n\n/**\n * 将 HistoryEntry 转换为 OpenClaw 历史格式\n */\nfunction mapHistory(\n entries: HistoryEntry[] | undefined,\n): OpenClawInboundMessage['history'] {\n if (!entries || entries.length === 0) return undefined;\n return entries.map((e) => ({\n role: 'user',\n content: e.content,\n senderId: e.senderId,\n senderName: e.senderName,\n }));\n}\n\n/**\n * 将 ResolvedQuote 转换为 OpenClaw quote 格式\n */\nfunction mapQuote(\n quote: ResolvedQuote | undefined,\n): OpenClawInboundMessage['quote'] {\n if (!quote) return undefined;\n return {\n content: quote.text,\n senderId: quote.entry?.senderId ?? '',\n attachments: quote.attachments as unknown[],\n };\n}\n\n/**\n * 从 SDK 中间件上下文构建 OpenClaw InboundMessage\n */\nexport function buildEnvelope(\n ctx: MiddlewareContext,\n msg: QQBotInboundMessage,\n account: ResolvedQQBotAccount,\n): OpenClawInboundMessage {\n const scope = msg.replyTarget.scope;\n const targetId = scope === 'group'\n ? `qqbot:group:${msg.replyTarget.targetId}`\n : `qqbot:c2c:${msg.replyTarget.targetId}`;\n\n // envelope 是 string 类型,直接作为 content 使用;回退到原始消息内容\n const envelope = ctx.state.envelope as string | undefined;\n let content = envelope ?? msg.content;\n\n // 将语音转录文本和附件信息注入到 content 中\n const processed = ctx.state.processedAttachments as\n | { voiceText?: string; imageUrls?: string[]; otherInfo?: string }\n | undefined;\n\n if (processed) {\n const parts: string[] = [];\n if (processed.voiceText) {\n parts.push(processed.voiceText);\n }\n if (content) {\n parts.push(content);\n }\n if (processed.otherInfo) {\n parts.push(processed.otherInfo);\n }\n content = parts.join('\\n');\n }\n\n return {\n channelId: 'qqbot',\n accountId: account.accountId,\n targetId,\n chatScope: scope === 'group' ? 'group' : 'direct',\n senderId: msg.senderId,\n senderName: msg.senderName,\n messageId: msg.messageId,\n content,\n history: mapHistory(ctx.state.history as HistoryEntry[] | undefined),\n quote: mapQuote(ctx.state.quote as ResolvedQuote | undefined),\n attachments: msg.attachments,\n imageUrls: processed?.imageUrls,\n groupId: scope === 'group' ? msg.replyTarget.targetId : undefined,\n systemPrompt: account.systemPrompt,\n };\n}\n","/**\n * 出站 Deliver Pipeline — 处理 AI 回复的富媒体分发\n *\n * 框架通过 ReplyPayload 结构化字段投递回复:\n * - text: 纯文本\n * - mediaUrl: 单个媒体 URL\n * - mediaUrls: 多个媒体 URL\n * - audioAsVoice: 语音气泡(TTS)\n *\n * Pipeline 按顺序处理:\n * 1. 语音意图 — audioAsVoice (TTS → SILK → 发送)\n * 2. 媒体发送 — mediaUrl / mediaUrls(图片/文件/视频)\n * 3. 纯文本回复 — 走 debounce 合并\n */\nimport { sendMedia } from './media-send.js';\nimport type { MediaKind, SendResult } from './outbound-service.js';\nimport type { DeliverDebouncer } from './debounce.js';\nimport type { PluginLogger } from '../utils/plugin-logger.js';\nimport { isPathInAllowedRoots } from './local-file-router.js';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\n\n// ── 类型 ──\n\nexport interface DeliverPayload {\n text?: string;\n mediaUrl?: string;\n mediaUrls?: string[];\n audioAsVoice?: boolean;\n}\n\nexport interface DeliverInfo {\n kind: string;\n}\n\nexport interface DeliverContext {\n qualifiedTarget: string;\n accountId: string;\n replyToId: string;\n /** 会话类型(用于判断语音是否可用) */\n chatScope?: 'direct' | 'group';\n /** 发送文本 */\n sendText: (to: string, text: string) => Promise<SendResult>;\n /** 发送媒体 */\n sendMedia: (to: string, source: string, opts?: { text?: string; mediaKind?: MediaKind }) => Promise<SendResult>;\n /** TTS 文字转语音(返回本地音频路径,失败返回 null) */\n textToSpeech?: (params: { text: string; cfg?: unknown; channel?: string; accountId?: string }) => Promise<{ audioPath: string | null; error?: string }>;\n /** 音频文件转 SILK Base64(QQ 语音格式) */\n audioFileToSilkBase64?: (audioPath: string) => Promise<string | null>;\n /** 出站文本合并防抖(可选,启用时纯文本走 debounce) */\n debouncer?: DeliverDebouncer;\n /** 运行时配置 */\n cfg?: unknown;\n /** 日志 */\n log?: PluginLogger;\n /** Agent ID(用于解析相对文件路径的工作区) */\n agentId?: string;\n}\n\n/**\n * 解析 ReplyPayload 中的媒体 URL 列表(优先 mediaUrls,fallback 单个 mediaUrl)\n */\nfunction resolveMediaUrls(payload: DeliverPayload): string[] {\n if (payload.mediaUrls?.length) {\n return payload.mediaUrls.filter(Boolean);\n }\n if (payload.mediaUrl) {\n return [payload.mediaUrl];\n }\n // attachments[].path → 本地文件路径(findLocalPath() 展开可能相对路径)\n return resolveAttachmentPaths(payload);\n}\n\nfunction resolveAttachmentPaths(payload: DeliverPayload): string[] {\n const attachments = (payload as any).attachments as Array<{ path?: string; url?: string }> | undefined;\n if (!attachments?.length) return [];\n return attachments\n .map((a) => a.url ?? a.path)\n .filter((p): p is string => typeof p === 'string' && p.length > 0);\n}\n\n/**\n * 出站 Deliver Pipeline\n *\n * 按顺序处理:\n * 1. 语音意图 → TTS\n * 2. 媒体发送 → mediaUrl / mediaUrls\n * 3. 纯文本回复 → debounce 合并\n */\nexport async function deliverReply(\n payload: DeliverPayload,\n _info: DeliverInfo | undefined,\n ctx: DeliverContext,\n): Promise<void> {\n const text = payload.text?.trim() ?? '';\n const mediaUrls = resolveMediaUrls(payload);\n const hasMedia = mediaUrls.length > 0;\n\n ctx.log?.debug(`[text] textLen=${text.length} mediaCount=${mediaUrls.length}`);\n\n // ── Layer 1: 语音意图 ──\n if (payload.audioAsVoice) {\n if (ctx.textToSpeech && text) {\n const handled = await handleVoiceIntent(text, ctx);\n if (handled) {\n return; // 语音已发送,不再重复发送媒体或文本\n }\n }\n // TTS 不可用或合成失败 → 降级为纯文本 + 媒体\n }\n\n // ── Layer 2: 媒体发送 ──\n if (hasMedia) {\n await sendMediaUrls(ctx, mediaUrls);\n // 附带文本一并发送\n if (text) {\n const result = await ctx.sendText(ctx.qualifiedTarget, text);\n if (result.error) {\n ctx.log?.error(`[media] sendText failed: ${result.error}`);\n }\n }\n return;\n }\n\n // ── Layer 3: 纯文本回复 ──\n if (!text) return;\n\n if (ctx.debouncer) {\n await ctx.debouncer.enqueue(ctx.qualifiedTarget, text);\n } else {\n const result = await ctx.sendText(ctx.qualifiedTarget, text);\n if (result.error) {\n ctx.log?.error(`[text] sendText failed: ${result.error}`);\n }\n }\n}\n\n// ── 媒体批量发送 ──\n\nasync function sendMediaUrls(ctx: DeliverContext, urls: string[]): Promise<void> {\n const failedUrls: string[] = [];\n for (const url of urls) {\n const result = await sendMedia({\n to: ctx.qualifiedTarget,\n source: url,\n replyToId: ctx.replyToId,\n accountId: ctx.accountId,\n log: ctx.log,\n agentId: ctx.agentId,\n });\n if (result.error) {\n ctx.log?.error(`[media] ${result.error}`);\n failedUrls.push(url);\n }\n }\n\n // 媒体发送失败时通知用户\n if (failedUrls.length > 0) {\n const count = failedUrls.length;\n const total = urls.length;\n const failMsg = count === total\n ? `⚠️ 媒体发送失败(${count} 个),请重试`\n : `⚠️ ${count}/${total} 个媒体发送失败`;\n const textResult = await ctx.sendText(ctx.qualifiedTarget, failMsg);\n if (textResult.error) {\n ctx.log?.error(`[media] failed to send failure notification: ${textResult.error}`);\n }\n }\n}\n\n// ── 语音意图处理 ──\n\nasync function handleVoiceIntent(text: string, ctx: DeliverContext): Promise<boolean> {\n if (!text || !ctx.textToSpeech) return false;\n\n // 仅 c2c/group 支持语音消息;其他场景 fallback 发文本\n if (ctx.chatScope && ctx.chatScope !== 'direct' && ctx.chatScope !== 'group') {\n return false;\n }\n\n try {\n const ttsResult = await ctx.textToSpeech({\n text,\n cfg: ctx.cfg,\n channel: 'qqbot',\n accountId: ctx.accountId,\n });\n\n if (!ttsResult.audioPath) {\n ctx.log?.error(`[tts] TTS failed: ${ttsResult.error ?? 'no audio path returned'}`);\n return false;\n }\n\n // TTS 路径安全校验:确保音频文件在允许的临时目录内\n if (!isTtsPathSafe(ttsResult.audioPath)) {\n ctx.log?.error(`[tts] TTS audio path blocked: ${ttsResult.audioPath}`);\n return false;\n }\n\n // 优先走 SILK base64\n if (ctx.audioFileToSilkBase64) {\n const silkBase64 = await ctx.audioFileToSilkBase64(ttsResult.audioPath);\n if (silkBase64) {\n const result = await ctx.sendMedia(ctx.qualifiedTarget, silkBase64, { mediaKind: 'voice' });\n if (result.error) ctx.log?.error(`[tts] sendVoice(base64) failed: ${result.error}`);\n return !result.error;\n }\n ctx.log?.error(`[tts] SILK conversion failed, trying localPath fallback`);\n }\n\n // Fallback: 直接发本地路径(SDK 内部处理格式转换)\n const result = await ctx.sendMedia(ctx.qualifiedTarget, ttsResult.audioPath, { mediaKind: 'voice' });\n if (result.error) {\n ctx.log?.error(`[tts] sendVoice(localPath) failed: ${result.error}`);\n return false;\n }\n return true;\n } catch (err) {\n ctx.log?.error(`[tts] TTS/voice send failed: ${err instanceof Error ? err.message : String(err)}`);\n return false;\n }\n}\n\n// ── TTS 路径安全 ──\n\nconst TTS_ALLOWED_ROOTS = [\n path.join(os.homedir(), '.openclaw'),\n os.tmpdir(),\n '/tmp',\n];\n\nfunction isTtsPathSafe(audioPath: string): boolean {\n if (!path.isAbsolute(audioPath)) return false;\n return isPathInAllowedRoots(audioPath, TTS_ALLOWED_ROOTS);\n}\n","/**\n * ctxPayload 构建器\n *\n * 与内置版 outbound-dispatch.ts:buildCtxPayload 对齐,\n * 将 SDK 消息数据转换为 OpenClaw 框架标准的入站上下文。\n */\nimport type { MiddlewareContext, QQBotInboundMessage } from '@tencent-connect/qqbot-nodejs';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport type { AssembledBody } from './body-assembler.js';\nimport type { OpenClawInboundMessage } from './envelope-builder.js';\nimport type { RuntimeAdapters } from '../adapter/resolve.js';\n\nexport interface CtxPayloadParams {\n assembled: AssembledBody;\n envelope: OpenClawInboundMessage;\n route: { sessionKey: string; accountId: string; agentId?: string };\n msg: QQBotInboundMessage;\n ctx: MiddlewareContext;\n adapters: RuntimeAdapters;\n}\n\nexport function buildCtxPayload(params: CtxPayloadParams): any {\n const { assembled, envelope, route, msg, ctx, adapters } = params;\n\n const isSlashCommand = /^\\//.test(assembled.rawBody ?? '');\n const convKind = envelope.chatScope === 'group' ? 'group' : 'direct';\n const peerId = convKind === 'group'\n ? (envelope.groupId ?? envelope.senderId)\n : envelope.senderId;\n const groupId = convKind === 'group' ? envelope.groupId : undefined;\n\n const processed = ctx.state.processedAttachments as any;\n const voicePaths = processed?.localMediaPaths?.filter((_: string, i: number) =>\n processed.localMediaTypes?.[i]?.startsWith('audio/')) ?? [];\n const voiceUrls = processed?.remoteMediaUrls?.filter((_: string, i: number) =>\n processed.remoteMediaUrls?.[i]?.startsWith?.('audio/')) ?? [];\n\n const msgTimestamp = (msg as any).timestamp ?? (msg as any).Timestamp;\n\n return adapters.buildInboundContext?.({\n channel: 'qqbot',\n accountId: route.accountId,\n provider: 'qqbot',\n surface: 'qqbot',\n messageId: envelope.messageId,\n timestamp: msgTimestamp ? new Date(msgTimestamp).getTime() : Date.now(),\n from: envelope.targetId,\n sender: { id: envelope.senderId, name: envelope.senderName },\n conversation: {\n kind: convKind,\n id: peerId,\n label: assembled.systemPrompt,\n },\n message: {\n body: assembled.webBody,\n bodyForAgent: assembled.agentBody,\n rawBody: assembled.rawBody,\n commandBody: assembled.rawBody,\n },\n route: {\n agentId: route.agentId ?? 'default',\n routeSessionKey: route.sessionKey,\n accountId: route.accountId,\n },\n reply: {\n to: envelope.targetId,\n replyToId: envelope.messageId,\n originatingTo: envelope.targetId,\n },\n access: {\n commands: { authorized: isSlashCommand },\n },\n command: isSlashCommand\n ? { kind: 'text-slash' as const, body: assembled.rawBody!, authorized: true }\n : undefined,\n media: voicePaths.length > 0\n ? voicePaths.map((p: string, i: number) => ({\n contentType: processed?.localMediaTypes?.[i] ?? 'audio/silk',\n localPath: p,\n url: voiceUrls[i],\n }))\n : voiceUrls.length > 0\n ? voiceUrls.map((u: string) => ({ contentType: 'audio/wav', url: u }))\n : undefined,\n supplemental: {\n quote: envelope.quote\n ? { id: envelope.messageId, body: envelope.quote.content, sender: envelope.quote.senderId }\n : undefined,\n groupSystemPrompt: envelope.systemPrompt,\n },\n extra: {\n ...(isSlashCommand ? { CommandSource: 'text' } : {}),\n ...(groupId ? { QQGroupOpenid: groupId } : {}),\n ...(processed?.localMediaPaths?.length\n ? {\n MediaPaths: processed.localMediaPaths,\n MediaPath: processed.localMediaPaths[0],\n MediaTypes: processed.localMediaTypes,\n MediaType: processed.localMediaTypes?.[0],\n }\n : {}),\n ...(processed?.remoteMediaUrls?.length\n ? {\n MediaUrls: processed.remoteMediaUrls,\n MediaUrl: processed.remoteMediaUrls[0],\n }\n : {}),\n },\n });\n}\n","/**\n * 出站消息合并防抖\n *\n * 当短时间内收到多次 deliver 时,将文本合并为一条消息发送,避免消息轰炸。\n *\n * 注:框架 ReplyDispatcher 通过 sendChain 保证 deliver 严格串行——\n * enqueue 返回的 Promise 在文本实际发出前不会 resolve,因此上游\n * 不会并发调用 deliver,无需 flushBeforeMedia。\n */\nimport type { DeliverDebounceConfig } from '../types.js';\n\nconst DEFAULT_WINDOW_MS = 1500;\nconst DEFAULT_MAX_WAIT_MS = 8000;\nconst DEFAULT_SEPARATOR = '\\n\\n---\\n\\n';\n\ninterface PendingDeliver {\n texts: string[];\n firstAt: number;\n timer: ReturnType<typeof setTimeout> | null;\n resolve: () => void;\n}\n\nexport class DeliverDebouncer {\n private pending = new Map<string, PendingDeliver>();\n private readonly windowMs: number;\n private readonly maxWaitMs: number;\n private readonly separator: string;\n private readonly flush: (targetId: string, mergedText: string) => Promise<void>;\n\n constructor(\n config: DeliverDebounceConfig | undefined,\n flush: (targetId: string, mergedText: string) => Promise<void>,\n ) {\n this.windowMs = config?.windowMs ?? DEFAULT_WINDOW_MS;\n this.maxWaitMs = config?.maxWaitMs ?? DEFAULT_MAX_WAIT_MS;\n this.separator = config?.separator ?? DEFAULT_SEPARATOR;\n this.flush = flush;\n }\n\n get enabled(): boolean {\n return this.windowMs > 0;\n }\n\n /**\n * 入队一条纯文本待发送消息\n * @returns Promise,在文本实际发送时 resolve\n */\n async enqueue(targetId: string, text: string): Promise<void> {\n if (!this.enabled) {\n await this.flush(targetId, text);\n return;\n }\n\n const existing = this.pending.get(targetId);\n if (existing) {\n existing.texts.push(text);\n // 重置窗口定时器\n if (existing.timer) clearTimeout(existing.timer);\n // 检查是否超过最大等待\n if (Date.now() - existing.firstAt >= this.maxWaitMs) {\n await this.doFlush(targetId);\n } else {\n existing.timer = setTimeout(() => this.doFlush(targetId), this.windowMs);\n }\n return;\n }\n\n // 新建 pending\n return new Promise<void>((resolve) => {\n const pending: PendingDeliver = {\n texts: [text],\n firstAt: Date.now(),\n timer: setTimeout(() => this.doFlush(targetId), this.windowMs),\n resolve,\n };\n this.pending.set(targetId, pending);\n });\n }\n\n private async doFlush(targetId: string): Promise<void> {\n const pending = this.pending.get(targetId);\n if (!pending) return;\n this.pending.delete(targetId);\n\n if (pending.timer) clearTimeout(pending.timer);\n const merged = pending.texts.join(this.separator);\n\n try {\n await this.flush(targetId, merged);\n } finally {\n pending.resolve();\n }\n }\n\n /**\n * 强制刷新所有 pending\n */\n async flushAll(): Promise<void> {\n const keys = [...this.pending.keys()];\n await Promise.all(keys.map((k) => this.doFlush(k)));\n }\n}\n","/**\n * QQ Bot 流式消息控制器\n *\n * 核心约束:QQ 流式 API 替换模式下,已下发文本的**前缀不可变更**。\n *\n * 状态机:\n * IDLE → (first chunk) → STREAMING → (complete) → DONE\n * → (prefix changed) → DONE\n * → (new reply) → IDLE → …\n *\n * onPartialReply(text) 输入:模型全量文本,持续增长。\n * finalize() 标记:框架通知回复结束,用 lastAccepted 收尾。\n */\n\nimport type { ReplyTarget, StreamSession } from '@tencent-connect/qqbot-nodejs';\nimport type { PluginLogger } from '../utils/plugin-logger.js';\nimport type { QQBotGateway } from '../gateway/qqbot-gateway.js';\n\n// ── 类型 ──\n\nexport type StreamingPhase = 'idle' | 'streaming' | 'done' | 'failed';\n\nexport interface StreamingControllerDeps {\n gateway: QQBotGateway;\n target: ReplyTarget;\n accountId: string;\n replyToId: string;\n log?: PluginLogger;\n}\n\n// ── 控制器 ──\n\nexport class StreamingController {\n private phase: StreamingPhase = 'idle';\n private session: StreamSession | null = null;\n\n /** QQ 已接受的最新文本 — 单源真理 */\n private lastAcceptedFull = '';\n\n /** 已成功发送的分片数(降级:=0 则走静态消息兜底) */\n private sentChunkCount = 0;\n\n /** 同步标志:收到第一个 onPartialReply 即置 true(不等 async 完成) */\n private _hasStarted = false;\n\n /** 串行队列 */\n private chain: Promise<unknown> = Promise.resolve();\n\n constructor(private readonly deps: StreamingControllerDeps) {}\n\n // ── 公共访问器 ──\n\n get currentPhase(): StreamingPhase { return this.phase; }\n\n /** 是否已成功发送至少一个流式分片 */\n get hasSentChunks(): boolean { return this.sentChunkCount > 0; }\n\n /** 同步标志:流式已启动(不等异步完成),用于 final 去重 */\n get hasStarted(): boolean { return this._hasStarted; }\n\n get isTerminal(): boolean {\n return this.phase === 'done' || this.phase === 'failed';\n }\n\n get shouldFallbackToStatic(): boolean {\n return this.isTerminal && this.sentChunkCount === 0;\n }\n\n // ── 入口 ──\n\n onPartialReply(text: string): Promise<void> {\n this._hasStarted = true; // 同步置位:不等异步发送,final 就能感知流式已启动\n this.chain = this.chain.then(() => this.handleChunk(text)).catch((err) => {\n this.deps.log?.error(`onPartialReply error: ${err instanceof Error ? err.message : String(err)}`);\n this.transition('failed', 'chunk_error');\n });\n return this.chain as Promise<void>;\n }\n\n finalize(): Promise<void> {\n this.chain = this.chain.then(() => this.handleFinalize()).catch((err) => {\n this.deps.log?.error(`finalize error: ${err instanceof Error ? err.message : String(err)}`);\n this.transition('failed', 'finalize_error');\n });\n return this.chain as Promise<void>;\n }\n\n async abort(reason?: string): Promise<void> {\n if (this.isTerminal) return;\n this.deps.log?.warn(`aborting stream reason=${reason ?? 'manual'} sent=${this.sentChunkCount}`);\n if (this.session) {\n try { await this.session.complete(); } catch (e) {\n this.deps.log?.error(`abort complete failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n this.session = null;\n }\n this.transition('failed', `abort:${reason ?? 'manual'}`);\n }\n\n // ── 核心逻辑 ──\n\n private async handleChunk(text: string): Promise<void> {\n if (this.isTerminal || !text) return;\n\n // 正常续写:新文本前缀匹配(含空白归一化)\n if (prefixMatches(this.lastAcceptedFull, text)) {\n if (text.length !== this.lastAcceptedFull.length) {\n await this.sendUpdate(text);\n }\n return;\n }\n\n // 无已下发内容 → 首发\n if (!this.lastAcceptedFull) {\n await this.sendUpdate(text);\n return;\n }\n\n // 前缀不匹配 — 长度回退 → 新回复(工具调用后)\n if (text.length < this.lastAcceptedFull.length) {\n this.deps.log?.info(`new reply: lastAccepted=${this.lastAcceptedFull.length}→chunk=${text.length}`);\n await this.completeSession('new_reply');\n this.lastAcceptedFull = '';\n await this.sendUpdate(text);\n return;\n }\n\n // 前缀不匹配但长度增长 → 模型重写尾部,同一条流追加\n const commonLen = longestCommonPrefix(this.lastAcceptedFull, text);\n const extra = text.slice(Math.max(commonLen, 0));\n const merged = this.lastAcceptedFull + extra;\n this.deps.log?.warn(\n `prefix retry: lastAccepted=${this.lastAcceptedFull.length}→chunk=${text.length} common=${commonLen} extra=${extra.length}, appending`,\n );\n await this.sendUpdate(merged);\n }\n\n private async handleFinalize(): Promise<void> {\n if (this.isTerminal) return;\n\n // 用已下发文本收尾 — 不用框架 deliver 的文本(框架可能追加 ⚠️ 标记)\n if (this.session) {\n await this.completeSession();\n this.transition('done', 'finalize');\n this.deps.log?.info(`stream done chunks=${this.sentChunkCount} chars=${this.lastAcceptedFull.length}`);\n return;\n }\n\n // 无会话 — 视有无下发决定终态\n if (this.sentChunkCount > 0) {\n this.transition('done', 'finalize:no_session');\n } else {\n this.transition('failed', 'finalize:fallback');\n }\n }\n\n // ── QQ 交互 ──\n\n private async sendUpdate(text: string): Promise<void> {\n if (!this.session) {\n this.session = this.deps.gateway.openStream(this.deps.target, this.deps.replyToId);\n this.transition('streaming', 'first_chunk');\n this.deps.log?.info(`stream opened (firstChunk=${text.length})`);\n }\n try {\n await this.session.update(text);\n this.lastAcceptedFull = text;\n this.sentChunkCount++;\n } catch (err) {\n this.deps.log?.error(`update failed (len=${text.length}): ${err instanceof Error ? err.message : String(err)}`);\n this.session = null;\n this.transition('failed', 'update_error');\n }\n }\n\n private async completeSession(reason?: string): Promise<void> {\n if (!this.session) return;\n this.deps.log?.info(`completing stream (sent=${this.sentChunkCount} chars=${this.lastAcceptedFull.length} reason=${reason ?? 'done'})`);\n try {\n await this.session.complete();\n } catch (err) {\n this.deps.log?.error(`complete failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n this.session = null;\n }\n\n // ── 状态机 ──\n\n private transition(next: StreamingPhase, reason: string): void {\n if (this.phase === next) return;\n this.deps.log?.info(`phase: ${this.phase} → ${next} (${reason})`);\n this.phase = next;\n }\n}\n\n/** 归一化空白符:连续换行/空格合并为单个空格 */\nfunction normalizeWs(s: string): string {\n return s.replace(/\\s+/g, ' ');\n}\n\n/** 忽略空白前缀比较 */\nfunction prefixMatches(accepted: string, incoming: string): boolean {\n if (incoming.startsWith(accepted)) return true;\n return normalizeWs(incoming).startsWith(normalizeWs(accepted));\n}\n\nfunction longestCommonPrefix(a: string, b: string): number {\n let i = 0;\n while (i < a.length && i < b.length && a[i] === b[i]) i++;\n return i;\n}\n\n// ── 入口判断 ──\n\nimport type { ResolvedQQBotAccount } from '../types.js';\n\nexport function shouldUseStreaming(\n account: ResolvedQQBotAccount,\n targetScope: 'c2c' | 'group' | 'channel',\n): boolean {\n if (targetScope !== 'c2c') return false;\n const streaming = account.config?.streaming;\n if (typeof streaming === 'boolean') return streaming;\n if (streaming && typeof streaming === 'object') {\n return (streaming as any).mode !== 'off';\n }\n return false;\n}\n","/**\n * 消息转发 — 入站消息 → OpenClaw AI\n *\n * 核心职责:\n * 1. 从 SDK MiddlewareContext 构建 OpenClaw 标准信封\n * 2. 通过 runtime-adapter 将消息交给 AI 处理\n *\n * 架构说明:\n * - 所有 runtime.channel.* 访问均通过 runtime-adapter 隔离\n * - log: 前缀由 PluginLogger + 框架自动注入,消息体不重复 accountId\n */\nimport type { MiddlewareContext, QQBotInboundMessage } from '@tencent-connect/qqbot-nodejs';\nimport type { PluginRuntime } from 'openclaw/plugin-sdk';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport type { PluginLogger } from '../utils/plugin-logger.js';\nimport { buildEnvelope } from './envelope-builder.js';\nimport { assembleBody, type AssembledBody } from './body-assembler.js';\nimport { sendText, getGateway } from '../outbound/outbound-service.js';\nimport { sendMedia } from '../outbound/media-send.js';\nimport { deliverReply, type DeliverPayload, type DeliverInfo, type DeliverContext } from '../outbound/deliver-pipeline.js';\nimport { buildCtxPayload } from './ctx-builder.js';\n\nimport { DeliverDebouncer } from '../outbound/debounce.js';\nimport { StreamingController, shouldUseStreaming } from '../outbound/streaming-controller.js';\nimport { getAdapters } from '../adapter/resolve.js';\nimport { clearGroupHistory } from '../features/history-store.js';\n\n\n/**\n * 将经过中间件处理的入站消息转发给 OpenClaw AI\n */\nexport async function dispatchToOpenClaw(\n ctx: MiddlewareContext,\n msg: QQBotInboundMessage,\n account: ResolvedQQBotAccount,\n runtime: PluginRuntime,\n log?: PluginLogger,\n): Promise<void> {\n const dlog = log?.child('dispatch');\n const adapters = getAdapters(runtime, dlog);\n const envelope = buildEnvelope(ctx, msg, account);\n\n dlog?.debug(`received sender=${envelope.senderId} scope=${envelope.chatScope} msgId=${envelope.messageId}`);\n\n if (!adapters.dispatchReply) {\n dlog?.error(`runtime adapter dispatchReply not available (openclaw=${adapters.version})`);\n return;\n }\n\n const assembled: AssembledBody =\n ((ctx.state as Record<string, unknown>).assembledBody as AssembledBody | undefined) ??\n assembleBody(ctx, msg, account);\n\n const cfg = adapters.getConfig?.() ?? {};\n\n const route = adapters.resolveAgentRoute?.({\n cfg,\n channel: 'qqbot',\n accountId: account.accountId,\n peer: {\n kind: envelope.chatScope === 'group' ? 'group' : 'direct',\n id: envelope.chatScope === 'group' ? (envelope.groupId ?? envelope.senderId) : envelope.senderId,\n },\n }) ?? { sessionKey: `qqbot:${account.accountId}:${envelope.senderId}`, accountId: account.accountId };\n\n const qualifiedTarget = envelope.targetId;\n const agentId = route.agentId ?? 'default';\n const storePath = adapters.resolveStorePath?.((cfg as any)?.session?.store, { agentId }) ?? '';\n\n const ctxPayload = buildCtxPayload({ assembled, envelope, route, msg, ctx, adapters });\n\n const ttsRuntime = (runtime as any)?.tts ?? (runtime as any)?.channel?.runtimeContexts?.get?.('tts');\n\n const debounceConfig = account.config?.deliverDebounce;\n const debouncer = debounceConfig?.enabled !== false\n ? new DeliverDebouncer(debounceConfig, (targetId, mergedText) =>\n sendText({ to: targetId, text: mergedText, accountId: account.accountId, replyToId: envelope.messageId, account }).then(() => {}),\n )\n : undefined;\n\n const deliverCtx: DeliverContext = {\n qualifiedTarget,\n accountId: account.accountId,\n replyToId: envelope.messageId,\n chatScope: envelope.chatScope === 'group' ? 'group' : 'direct',\n cfg,\n debouncer: debouncer?.enabled ? debouncer : undefined,\n sendText: (to, text) => sendText({ to, text, accountId: account.accountId, replyToId: envelope.messageId, account }),\n sendMedia: (to, source, opts) => sendMedia({\n to,\n source,\n text: opts?.text ?? '',\n replyToId: envelope.messageId,\n accountId: account.accountId,\n agentId: route.agentId,\n log: deliverCtx.log,\n }),\n textToSpeech: ttsRuntime?.textToSpeech\n ? (params) => ttsRuntime.textToSpeech(params)\n : undefined,\n audioFileToSilkBase64: ttsRuntime?.audioFileToSilkBase64\n ? (audioPath: string) => ttsRuntime.audioFileToSilkBase64(audioPath)\n : undefined,\n log: log?.child('deliver'),\n agentId: route.agentId ?? 'default',\n };\n\n const streamingEnabled = shouldUseStreaming(\n account,\n envelope.chatScope === 'group' ? 'group' : 'c2c',\n );\n\n const streamingController = streamingEnabled\n ? createStreamingController(envelope, account, log?.child('streaming'))\n : null;\n\n if (streamingController) {\n dlog?.debug(`streaming enabled for ${envelope.senderId}`);\n }\n\n const deliveredMediaUrls = new Set<string>();\n const deliveredTexts = new Set<string>();\n\n if (!adapters.inboundRun) {\n // 低版本:手动 session + dispatchReply 直调\n if (adapters.recordInboundSession) {\n try {\n await adapters.recordInboundSession({\n storePath,\n sessionKey: route.sessionKey,\n ctx: ctxPayload,\n });\n } catch { /* best-effort */ }\n }\n await adapters.dispatchReply!({\n ctx: ctxPayload,\n cfg,\n dispatcherOptions: {\n deliver: async (payload: DeliverPayload, info?: DeliverInfo) => {\n const text = payload.text?.trim() ?? '';\n // 低版本无 block/final 协议,流式启动后 final 仍需跳过(降级除外)\n if (!payload.mediaUrl && !payload.mediaUrls?.length\n && text\n && (deliveredTexts.has(text)\n || (streamingController?.hasStarted && !streamingController?.shouldFallbackToStatic))\n ) {\n return;\n }\n const filteredPayload = deliveredMediaUrls.size > 0\n ? {\n ...payload,\n mediaUrl: payload.mediaUrl && !deliveredMediaUrls.has(payload.mediaUrl)\n ? payload.mediaUrl : undefined,\n mediaUrls: payload.mediaUrls?.filter((u) => !deliveredMediaUrls.has(u)),\n }\n : payload;\n await deliverReply(filteredPayload, info, deliverCtx);\n if (text) deliveredTexts.add(text);\n // 记录已投递媒体(流式路径可能已先发送)\n for (const u of filteredPayload.mediaUrls ?? []) deliveredMediaUrls.add(u);\n if (filteredPayload.mediaUrl) deliveredMediaUrls.add(filteredPayload.mediaUrl);\n },\n },\n replyOptions: {\n abortSignal: ctx.signal,\n runId: envelope.messageId,\n ...(streamingController\n ? {\n onPartialReply: async (p: { text?: string }) => {\n if (p.text) await streamingController.onPartialReply(p.text);\n },\n }\n : {}),\n },\n });\n if (streamingController && !streamingController.isTerminal) {\n await streamingController.finalize();\n }\n if (debouncer) await debouncer.flushAll();\n } else {\n await adapters.inboundRun!({\n channel: 'qqbot',\n accountId: route.accountId,\n raw: envelope,\n adapter: {\n ingest: (raw: any) => ({\n id: envelope.messageId,\n rawText: assembled.rawBody,\n textForAgent: assembled.agentBody,\n textForCommands: assembled.rawBody,\n raw,\n }),\n resolveTurn: (_input: unknown, _eventClass: unknown, _preflight: unknown) => ({\n channel: 'qqbot',\n accountId: route.accountId,\n routeSessionKey: route.sessionKey,\n storePath,\n ctxPayload,\n recordInboundSession: adapters.recordInboundSession,\n record: {\n onRecordError: (err: unknown) => {\n dlog?.error(`Session record error: ${err}`);\n },\n },\n runDispatch: () => {\n return adapters.dispatchReply!({\n ctx: ctxPayload,\n cfg,\n dispatcherOptions: {\n deliver: async (payload: DeliverPayload, info?: DeliverInfo) => {\n try {\n const kind = (info as any)?.kind as string | undefined;\n const text = payload.text?.trim() ?? '';\n const hasMedia = !!(payload.mediaUrl || payload.mediaUrls?.length);\n dlog?.debug(`deliver kind=${kind ?? 'none'} textLen=${text.length} voice=${!!payload.audioAsVoice} media=${hasMedia}`);\n\n // ── 1. block: 媒体/语音立即发送,文本留给流式 ──\n if (kind === 'block') {\n if (payload.audioAsVoice) {\n await deliverReply(payload, info, deliverCtx);\n } else {\n await forwardMediaUrls(payload, deliverCtx, deliveredMediaUrls, dlog);\n }\n }\n\n // ── 2. 流式路径:流式已启动且未降级 → 跳过静态发送 ──\n if (streamingController?.hasStarted && !streamingController.shouldFallbackToStatic) {\n if (kind !== 'block') await streamingController.finalize();\n if (!streamingController.shouldFallbackToStatic) return;\n dlog?.warn(`streaming fallback to static`);\n }\n\n // ── 3. 文本去重:同文本已发过 → 跳过 ──\n if (kind === 'final' && !hasMedia && text && deliveredTexts.has(text)) {\n return;\n }\n\n // ── 4. tool 媒体:立即转发 ──\n if (kind === 'tool') {\n await forwardMediaUrls(payload, deliverCtx, deliveredMediaUrls, dlog);\n return;\n }\n\n // ── 5. 默认路径:过滤已发媒体 + 发送 ──\n const filteredPayload = filterDeliveredMedia(payload, deliveredMediaUrls);\n await deliverReply(filteredPayload, info, deliverCtx);\n if (text) deliveredTexts.add(text);\n } catch (err) {\n dlog?.error(`deliver error: ${err instanceof Error ? err.message : String(err)}`);\n }\n },\n },\n replyOptions: {\n abortSignal: ctx.signal,\n runId: envelope.messageId,\n ...(streamingController\n ? {\n onPartialReply: async (p: { text?: string }) => {\n if (p.text) await streamingController.onPartialReply(p.text);\n },\n }\n : {}),\n },\n });\n },\n }),\n },\n });\n }\n \n dlog?.debug(`inboundRun completed sessionKey=${route.sessionKey}`);\n\n // 群消息回复后清空历史缓存(避免下次 @ 时重复组包)\n if (envelope.chatScope === 'group') {\n clearGroupHistory(account.accountId, envelope.groupId ?? envelope.senderId);\n }\n\n if (streamingController && !streamingController.isTerminal) {\n await streamingController.finalize();\n }\n\n if (debouncer) {\n await debouncer.flushAll();\n }\n}\n\nfunction createStreamingController(\n envelope: ReturnType<typeof buildEnvelope>,\n account: ResolvedQQBotAccount,\n log?: PluginLogger,\n): StreamingController | null {\n const gw = getGateway(account.accountId);\n if (!gw) {\n log?.error(`cannot enable streaming — gateway not running`);\n return null;\n }\n return new StreamingController({\n gateway: gw,\n target: {\n scope: 'c2c',\n targetId: envelope.senderId,\n msgId: envelope.messageId,\n },\n accountId: account.accountId,\n replyToId: envelope.messageId,\n log,\n });\n}\n\n// ── 辅助函数 ──\n\n/** 提取 payload 中的媒体 URL 并逐个发送(去重) */\nasync function forwardMediaUrls(\n payload: DeliverPayload,\n ctx: DeliverContext,\n delivered: Set<string>,\n log?: PluginLogger,\n): Promise<void> {\n const urls: string[] = [];\n if (payload.mediaUrls?.length) urls.push(...payload.mediaUrls);\n if (payload.mediaUrl && !urls.includes(payload.mediaUrl)) urls.push(payload.mediaUrl);\n const newUrls = urls.filter((u) => !delivered.has(u));\n for (const url of newUrls) {\n try {\n await sendMedia({\n to: ctx.qualifiedTarget,\n source: url,\n text: '',\n replyToId: ctx.replyToId,\n accountId: ctx.accountId,\n log: ctx.log,\n agentId: ctx.agentId,\n });\n delivered.add(url);\n } catch (err) {\n log?.error(`media forward failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n}\n\n/** 过滤已发送的媒体 URL */\nfunction filterDeliveredMedia(\n payload: DeliverPayload,\n delivered: Set<string>,\n): DeliverPayload {\n if (delivered.size === 0) return payload;\n return {\n ...payload,\n mediaUrl: payload.mediaUrl && !delivered.has(payload.mediaUrl) ? payload.mediaUrl : undefined,\n mediaUrls: payload.mediaUrls?.filter((u) => !delivered.has(u)),\n };\n}\n","/**\n * Gateway Runtime 动态加载(兼容旧版框架)\n *\n * openclaw < 3.22 上 gateway-runtime.js 不存在,此函数返回 null。\n * 审批功能降级为不可用,不影响消息收发等核心能力。\n */\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\n\nexport interface ApprovalGatewayClient {\n start: () => void | Promise<void>;\n stop: () => void | Promise<void>;\n request: (method: string, params: unknown) => Promise<unknown>;\n}\n\n/** 动态加载 gateway-runtime 模块,旧版框架不可用时返回 null */\nexport function loadApprovalGatewayRuntime(): {\n createOperatorApprovalsGatewayClient: (...args: any[]) => Promise<ApprovalGatewayClient>;\n} | null {\n const req = createRequire(__filename);\n const pluginRoot = path.resolve(path.dirname(__filename), '..', '..');\n const fs = req('node:fs') as typeof import('node:fs');\n\n const tryLoadFromRoot = (root: string) => {\n for (const rel of ['dist/plugin-sdk/gateway-runtime.js', 'plugin-sdk/gateway-runtime.js']) {\n const p = path.join(root, rel);\n try {\n if (fs.existsSync(p)) return req(p);\n } catch { /* try next */ }\n }\n return null;\n };\n\n try {\n const { findOpenclawRoot } = req(path.join(pluginRoot, 'scripts', 'link-sdk-core.cjs')) as {\n findOpenclawRoot: (root: string) => string | null;\n };\n const root = findOpenclawRoot(pluginRoot);\n if (root) {\n const mod = tryLoadFromRoot(root);\n if (mod) return mod;\n }\n } catch { /* fallback */ }\n\n try {\n const entry = process.argv[1];\n if (entry) {\n const realEntry = fs.realpathSync(entry);\n let dir = path.dirname(realEntry);\n for (let i = 0; i < 6; i++) {\n const mod = tryLoadFromRoot(dir);\n if (mod) return mod;\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n }\n } catch { /* fallback */ }\n\n return null;\n}\n","/**\n * QQBot Approval Handler\n *\n * 监听 Gateway 的 exec/plugin approval 事件,\n * 直接调用 QQ API 发送带 Inline Keyboard 的审批消息。\n * 参考 DiscordExecApprovalHandler 的实现模式。\n *\n * 兼容性:gateway-runtime / approval-runtime 模块在 openclaw < 3.22 上不存在,\n * 使用动态 import 避免插件整体加载失败,旧版框架上审批功能自动降级(不可用)。\n */\n\nimport type { OpenClawConfig } from \"openclaw/plugin-sdk\";\nimport { getBotForAccount } from \"../bot-instance.js\";\nimport type { PluginLogger } from '../utils/plugin-logger.js';\nimport type { InlineKeyboard, KeyboardButton } from \"../types.js\";\n\nimport { loadApprovalGatewayRuntime, type ApprovalGatewayClient } from '../adapter/gateway.js';\ntype EventFrame = {\n event: string;\n payload: unknown;\n};\n\n/** approval-runtime 中的审批请求/结果类型(内联定义,避免顶层 import) */\ninterface ExecApprovalRequest {\n id: string;\n expiresAtMs: number;\n request: {\n commandPreview?: string;\n command?: string;\n cwd?: string;\n agentId?: string;\n turnSourceAccountId?: string;\n sessionKey?: string;\n turnSourceTo?: string;\n [key: string]: unknown;\n };\n}\n\ninterface ExecApprovalResolved {\n id: string;\n decision: string;\n resolvedBy?: string;\n [key: string]: unknown;\n}\n\ninterface PluginApprovalRequest {\n id: string;\n request: {\n timeoutMs?: number;\n severity?: \"critical\" | \"info\" | string;\n title: string;\n description?: string;\n toolName?: string;\n pluginId?: string;\n agentId?: string;\n turnSourceAccountId?: string;\n sessionKey?: string;\n turnSourceTo?: string;\n [key: string]: unknown;\n };\n}\n\ninterface PluginApprovalResolved {\n id: string;\n decision: string;\n resolvedBy?: string;\n [key: string]: unknown;\n}\n\n// ─── 类型 ───────────────────────────────────────────────────\n\nexport interface QQBotApprovalHandlerOpts {\n accountId: string;\n appId: string;\n clientSecret: string;\n cfg: OpenClawConfig;\n gatewayUrl?: string;\n log?: PluginLogger;\n}\n\ntype ApprovalKind = \"exec\" | \"plugin\";\n\ntype CachedApprovalRequest =\n | { kind: \"exec\"; request: ExecApprovalRequest }\n | { kind: \"plugin\"; request: PluginApprovalRequest };\n\ntype PendingEntry = {\n targets: Array<{ type: \"c2c\" | \"group\"; id: string }>;\n timeoutId: ReturnType<typeof setTimeout>;\n};\n\n// ─── 辅助函数 ───────────────────────────────────────────────\n\nfunction toShortId(approvalId: string): string {\n return approvalId.replace(/^(exec|plugin):/, \"\").slice(0, 8);\n}\n\nfunction resolveApprovalKind(approvalId: string): ApprovalKind {\n return approvalId.startsWith(\"plugin:\") ? \"plugin\" : \"exec\";\n}\n\nfunction buildExecApprovalText(request: ExecApprovalRequest): string {\n const expiresIn = Math.max(\n 0,\n Math.round((request.expiresAtMs - Date.now()) / 1000)\n );\n const lines: string[] = [\"🔐 命令执行审批\", \"\"];\n const cmd = request.request.commandPreview ?? request.request.command ?? \"\";\n if (cmd) lines.push(`\\`\\`\\`\\n${cmd.slice(0, 300)}\\n\\`\\`\\``);\n if (request.request.cwd) lines.push(`📁 目录: ${request.request.cwd}`);\n if (request.request.agentId) lines.push(`🤖 Agent: ${request.request.agentId}`);\n lines.push(\"\", `⏱️ 超时: ${expiresIn} 秒`);\n return lines.join(\"\\n\");\n}\n\nfunction buildPluginApprovalText(request: PluginApprovalRequest): string {\n const timeoutSec = Math.round((request.request.timeoutMs ?? 120_000) / 1000);\n const severityIcon =\n request.request.severity === \"critical\" ? \"🔴\"\n : request.request.severity === \"info\" ? \"🔵\"\n : \"🟡\";\n\n const lines: string[] = [`${severityIcon} 审批请求`, \"\"];\n lines.push(`📋 ${request.request.title}`);\n if (request.request.description) lines.push(`📝 ${request.request.description}`);\n if (request.request.toolName) lines.push(`🔧 工具: ${request.request.toolName}`);\n if (request.request.pluginId) lines.push(`🔌 插件: ${request.request.pluginId}`);\n if (request.request.agentId) lines.push(`🤖 Agent: ${request.request.agentId}`);\n lines.push(\"\", `⏱️ 超时: ${timeoutSec} 秒`);\n return lines.join(\"\\n\");\n}\n\n/**\n * Inline Keyboard(内嵌回调型按钮)\n * type=1(Callback):点击触发 INTERACTION_CREATE,button_data = data 字段\n * group_id 相同 → 点一个后其余变灰(三选一语义)\n * click_limit=1 → 每人只能点一次\n * permission.type=2 → 所有人可操作\n */\nfunction buildApprovalKeyboard(approvalId: string): InlineKeyboard {\n const makeBtn = (\n id: string,\n label: string,\n visitedLabel: string,\n data: string,\n style: 0 | 1\n ): KeyboardButton => ({\n id,\n render_data: { label, visited_label: visitedLabel, style },\n action: {\n type: 1,\n data,\n permission: { type: 2 },\n click_limit: 1,\n },\n group_id: \"approval\",\n });\n return {\n content: {\n rows: [\n {\n buttons: [\n makeBtn(\"allow\", \"✅ 允许一次\", \"已允许\", `approve:${approvalId}:allow-once`, 1),\n makeBtn(\"always\", \"⭐ 始终允许\", \"已始终允许\", `approve:${approvalId}:allow-always`, 1),\n makeBtn(\"deny\", \"❌ 拒绝\", \"已拒绝\", `approve:${approvalId}:deny`, 0),\n ],\n },\n ],\n },\n };\n}\n\n/** 从 sessionKey 或 turnSourceTo 提取投递目标 */\nfunction resolveTarget(\n sessionKey: string | null | undefined,\n turnSourceTo: string | null | undefined\n): { type: \"c2c\" | \"group\"; id: string } | null {\n // 优先从 sessionKey 解析(如 agent:main:qqbot:direct:OPENID)\n const sk = sessionKey ?? turnSourceTo;\n if (!sk) return null;\n const m = sk.match(/qqbot:(c2c|direct|group):([A-F0-9]+)/i);\n if (!m) return null;\n const type = m[1]!.toLowerCase() === \"group\" ? \"group\" : \"c2c\";\n return { type, id: m[2]! };\n}\n\n// ─── Handler 类 ──────────────────────────────────────────────\n\nexport class QQBotApprovalHandler {\n private gatewayClient: ApprovalGatewayClient | null = null;\n private pending = new Map<string, PendingEntry>();\n private requestCache = new Map<string, CachedApprovalRequest>();\n private opts: QQBotApprovalHandlerOpts;\n private started = false;\n private connected = false;\n\n constructor(opts: QQBotApprovalHandlerOpts) {\n this.opts = opts;\n }\n\n async start(): Promise<void> {\n if (this.started) return;\n this.started = true;\n const { log } = this.opts;\n log?.debug?.(`[qqbot:${this.opts.accountId}] approval-handler: starting`);\n\n // 动态加载 gateway-runtime(兼容旧版框架 / pnpm 环境)\n const gatewayRuntime = loadApprovalGatewayRuntime();\n if (!gatewayRuntime) {\n log?.debug?.(`[qqbot:${this.opts.accountId}] approval-handler: gateway-runtime not available, approval disabled`);\n this.started = false;\n return;\n }\n\n try {\n this.gatewayClient = await gatewayRuntime.createOperatorApprovalsGatewayClient({\n config: this.opts.cfg,\n gatewayUrl: this.opts.gatewayUrl,\n clientDisplayName: \"QQBot Approval Handler\",\n onEvent: (evt: EventFrame) => this.handleGatewayEvent(evt),\n onHelloOk: () => {\n this.connected = true;\n log?.debug?.(`[qqbot:${this.opts.accountId}] approval-handler: connected to gateway`);\n },\n onConnectError: (err: { message: string }) => log?.error(`[qqbot:${this.opts.accountId}] approval-handler: connect error: ${err.message}`),\n onClose: (code: number, reason: string) => {\n this.connected = false;\n log?.debug?.(`[qqbot:${this.opts.accountId}] approval-handler: gateway closed: ${code} ${reason}`);\n },\n });\n this.gatewayClient.start();\n setApprovalFeatureAvailable(true);\n } catch (err) {\n log?.error(`[qqbot:${this.opts.accountId}] approval-handler: failed to create gateway client: ${err}`);\n this.started = false;\n }\n }\n\n async stop(): Promise<void> {\n if (!this.started) return;\n this.started = false;\n for (const entry of this.pending.values()) clearTimeout(entry.timeoutId);\n this.pending.clear();\n this.requestCache.clear();\n await this.gatewayClient?.stop();\n this.gatewayClient = null;\n this.opts.log?.debug(`[qqbot:${this.opts.accountId}] approval-handler: stopped`);\n }\n\n /** gateway 是否已建立连接 */\n get isConnected(): boolean { return this.connected; }\n\n /** 检查是否有指定 shortId 对应的 pending 审批 */\n hasShortId(shortId: string): boolean {\n for (const id of this.pending.keys()) {\n if (toShortId(id) === shortId) return true;\n }\n return false;\n }\n\n /** 解析审批请求(供 Interaction 回调或 /approve 命令调用) */\n async resolveApproval(\n approvalId: string,\n decision: \"allow-once\" | \"allow-always\" | \"deny\"\n ): Promise<boolean> {\n if (!this.gatewayClient) {\n this.opts.log?.warn(`[qqbot:${this.opts.accountId}] approval-handler: resolve ignored ${approvalId} → gatewayClient not ready`);\n return false;\n }\n\n // 查找完整 ID:支持完整 ID(exec:uuid / plugin:uuid)、纯 UUID、或 shortId(8位)\n let fullId = approvalId;\n if (this.pending.has(approvalId)) {\n fullId = approvalId;\n } else {\n // 尝试在 pending keys 中匹配:纯 UUID 可能对应 exec:uuid 或 plugin:uuid\n for (const id of this.pending.keys()) {\n if (id === approvalId) { fullId = id; break; }\n // 纯 UUID 匹配:pending key 的 uuid 部分等于传入值\n if (id.replace(/^(exec|plugin):/, \"\") === approvalId) { fullId = id; break; }\n // shortId 匹配\n if (toShortId(id) === approvalId) { fullId = id; break; }\n }\n // 也在 requestCache 中查找(handleResolved 可能已清除 pending)\n if (fullId === approvalId && !this.requestCache.has(approvalId)) {\n for (const id of this.requestCache.keys()) {\n if (id.replace(/^(exec|plugin):/, \"\") === approvalId) { fullId = id; break; }\n }\n }\n }\n\n const kind = resolveApprovalKind(fullId);\n const method = kind === \"plugin\" ? \"plugin.approval.resolve\" : \"exec.approval.resolve\";\n const isPending = this.pending.has(fullId);\n const isCached = this.requestCache.has(fullId);\n\n this.opts.log?.debug?.(`[qqbot:${this.opts.accountId}] approval-handler: resolving ${fullId} (input=${approvalId}) kind=${kind} → ${decision}, pending=${isPending}, cached=${isCached}`);\n\n try {\n await this.gatewayClient.request(method, { id: fullId, decision });\n this.opts.log?.debug(`[qqbot:${this.opts.accountId}] approval-handler: RPC success ${toShortId(fullId)} → ${decision} (method=${method})`);\n return true;\n } catch (err) {\n this.opts.log?.error(`[qqbot:${this.opts.accountId}] approval-handler: resolve failed: ${err}`);\n return false;\n }\n }\n\n private handleGatewayEvent(evt: EventFrame): void {\n if (evt.event === \"exec.approval.requested\") {\n void this.handleRequested(evt.payload as ExecApprovalRequest, \"exec\");\n } else if (evt.event === \"plugin.approval.requested\") {\n void this.handleRequested(evt.payload as PluginApprovalRequest, \"plugin\");\n } else if (evt.event === \"exec.approval.resolved\") {\n void this.handleResolved(evt.payload as ExecApprovalResolved);\n } else if (evt.event === \"plugin.approval.resolved\") {\n void this.handleResolved(evt.payload as PluginApprovalResolved);\n }\n }\n\n private async handleRequested(\n request: ExecApprovalRequest | PluginApprovalRequest,\n kind: ApprovalKind\n ): Promise<void> {\n const { log, appId, clientSecret, accountId } = this.opts;\n const shortId = toShortId(request.id);\n\n // 只处理本账号的请求\n const reqAccountId = (request.request as any).turnSourceAccountId?.trim();\n if (reqAccountId && reqAccountId !== accountId) {\n log?.debug?.(`[qqbot:${accountId}] approval-handler: ${kind} ${shortId} ignored → account mismatch (req=${reqAccountId})`);\n return;\n }\n\n // 解析投递目标\n const sessionKey = (request.request as any).sessionKey;\n const turnSourceTo = (request.request as any).turnSourceTo;\n const target = resolveTarget(sessionKey, turnSourceTo);\n if (!target) {\n log?.info(`[qqbot:${accountId}] approval-handler: no QQ target for ${shortId} (session=${sessionKey})`);\n return;\n }\n\n // 缓存请求\n this.requestCache.set(\n request.id,\n kind === \"plugin\"\n ? { kind: \"plugin\", request: request as PluginApprovalRequest }\n : { kind: \"exec\", request: request as ExecApprovalRequest }\n );\n\n log?.info(`[qqbot:${accountId}] approval-handler: sending ${kind} approval ${shortId} to ${target.type}:${target.id}`);\n\n const text = kind === \"plugin\"\n ? buildPluginApprovalText(request as PluginApprovalRequest)\n : buildExecApprovalText(request as ExecApprovalRequest);\n\n const keyboard = buildApprovalKeyboard(request.id);\n\n const timeoutMs = kind === \"plugin\"\n ? ((request as PluginApprovalRequest).request.timeoutMs ?? 120_000)\n : Math.max(0, (request as ExecApprovalRequest).expiresAtMs - Date.now());\n\n // 短暂延迟,确保框架侧 waitDecision 已就绪,避免时序竞争\n await new Promise((r) => setTimeout(r, 2000));\n\n try {\n const bot = getBotForAccount(accountId);\n const target_reply = {\n scope: target.type as \"c2c\" | \"group\",\n targetId: target.id,\n };\n await bot.sendTextWithKeyboard(target_reply, text, keyboard as any);\n log?.info(`[qqbot:${accountId}] approval-handler: sent ${kind} approval ${shortId}`);\n\n const timeoutId = setTimeout(() => {\n this.handleTimeout(request.id, target);\n }, timeoutMs + 2_000);\n\n this.pending.set(request.id, { targets: [target], timeoutId });\n } catch (err) {\n this.requestCache.delete(request.id);\n log?.error(`[qqbot:${accountId}] approval-handler: failed to send approval ${shortId}: ${err}`);\n }\n }\n\n private async handleResolved(\n resolved: ExecApprovalResolved | PluginApprovalResolved\n ): Promise<void> {\n const entry = this.pending.get(resolved.id);\n const resolvedBy = (resolved as any).resolvedBy ?? \"unknown\";\n const kind = resolveApprovalKind(resolved.id);\n\n this.opts.log?.info(\n `[qqbot:${this.opts.accountId}] approval-handler: gateway confirmed ${toShortId(resolved.id)} → ${resolved.decision} (kind=${kind}, resolvedBy=${resolvedBy}, wasPending=${!!entry})`\n );\n\n if (!entry) return;\n\n clearTimeout(entry.timeoutId);\n this.pending.delete(resolved.id);\n this.requestCache.delete(resolved.id);\n // 框架 Forwarder 负责发送 resolved 通知(已通过 buildResolvedPayload=null 抑制),此处不重复发送\n }\n\n private async handleTimeout(\n approvalId: string,\n target: { type: \"c2c\" | \"group\"; id: string }\n ): Promise<void> {\n const { log, accountId } = this.opts;\n if (!this.pending.has(approvalId)) return;\n this.pending.delete(approvalId);\n this.requestCache.delete(approvalId);\n log?.info(`[qqbot:${accountId}] approval-handler: timeout ${toShortId(approvalId)}`);\n // 超时由框架处理,此处仅清理状态,不重复发消息\n }\n}\n\n// ─── 模块级 handler 注册 ────────────────────────────────────\n\nconst _handlers = new Map<string, QQBotApprovalHandler>();\n\n/** 审批功能是否可用(gateway-runtime 模块加载成功则为 true) */\nlet _approvalFeatureAvailable = false;\n\nexport function isApprovalFeatureAvailable(): boolean {\n return _approvalFeatureAvailable;\n}\n\nexport function setApprovalFeatureAvailable(available: boolean): void {\n _approvalFeatureAvailable = available;\n}\n\nexport function registerApprovalHandler(accountId: string, handler: QQBotApprovalHandler): void {\n _handlers.set(accountId, handler);\n}\n\nexport function unregisterApprovalHandler(accountId: string): void {\n _handlers.delete(accountId);\n}\n\nexport function getApprovalHandler(accountId: string): QQBotApprovalHandler | undefined {\n return _handlers.get(accountId);\n}\n\nexport function findApprovalHandlerForShortId(shortId: string): QQBotApprovalHandler | undefined {\n for (const handler of _handlers.values()) {\n if (handler.hasShortId(shortId)) return handler;\n }\n return undefined;\n}\n","import { createPluginLogger } from \"../utils/plugin-logger.js\";\n\nconst log = createPluginLogger({ prefix: '[proactive]' });\n\n/**\n * QQ Bot 主动发送消息模块\n * \n * 该模块提供以下能力:\n * 1. 记录已知用户(曾与机器人交互过的用户)\n * 2. 主动发送消息给用户或群组\n * 3. 查询已知用户列表\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { ResolvedQQBotAccount } from \"../types.js\";\n\n// ============ 类型定义(本地) ============\n\n/**\n * 已知用户信息\n */\nexport interface KnownUser {\n type: \"c2c\" | \"group\" | \"channel\";\n openid: string;\n accountId: string;\n nickname?: string;\n firstInteractionAt: number;\n lastInteractionAt: number;\n}\n\n/**\n * 主动发送消息选项\n */\nexport interface ProactiveSendOptions {\n to: string;\n text: string;\n type?: \"c2c\" | \"group\" | \"channel\";\n imageUrl?: string;\n accountId?: string;\n}\n\n/**\n * 主动发送消息结果\n */\nexport interface ProactiveSendResult {\n success: boolean;\n messageId?: string;\n timestamp?: number | string;\n error?: string;\n}\n\n/**\n * 列出已知用户选项\n */\nexport interface ListKnownUsersOptions {\n type?: \"c2c\" | \"group\" | \"channel\";\n accountId?: string;\n sortByLastInteraction?: boolean;\n limit?: number;\n}\nimport {\n getBotForAccount,\n} from \"../bot-instance.js\";\nimport { resolveQQBotAccount } from \"../config.js\";\nimport type { OpenClawConfig } from \"openclaw/plugin-sdk\";\n\n// ============ 用户存储管理 ============\n\n/**\n * 已知用户存储\n * 使用简单的 JSON 文件存储,保存在 .openclaw/qqbot 目录下\n */\nimport { getQQBotDataDir } from \"../utils/platform.js\";\n\nconst STORAGE_DIR = getQQBotDataDir(\"data\");\nconst KNOWN_USERS_FILE = path.join(STORAGE_DIR, \"known-users.json\");\n\n// 内存缓存\nlet knownUsersCache: Map<string, KnownUser> | null = null;\nlet cacheLastModified = 0;\n\n/**\n * 确保存储目录存在\n */\nfunction ensureStorageDir(): void {\n if (!fs.existsSync(STORAGE_DIR)) {\n fs.mkdirSync(STORAGE_DIR, { recursive: true });\n }\n}\n\n/**\n * 生成用户唯一键\n */\nfunction getUserKey(type: string, openid: string, accountId: string): string {\n return `${accountId}:${type}:${openid}`;\n}\n\n/**\n * 从文件加载已知用户\n */\nfunction loadKnownUsers(): Map<string, KnownUser> {\n if (knownUsersCache !== null) {\n // 检查文件是否被修改\n try {\n const stat = fs.statSync(KNOWN_USERS_FILE);\n if (stat.mtimeMs <= cacheLastModified) {\n return knownUsersCache;\n }\n } catch {\n // 文件不存在,使用缓存\n return knownUsersCache;\n }\n }\n\n const users = new Map<string, KnownUser>();\n \n try {\n if (fs.existsSync(KNOWN_USERS_FILE)) {\n const data = fs.readFileSync(KNOWN_USERS_FILE, \"utf-8\");\n const parsed = JSON.parse(data) as KnownUser[];\n for (const user of parsed) {\n const key = getUserKey(user.type, user.openid, user.accountId);\n users.set(key, user);\n }\n cacheLastModified = fs.statSync(KNOWN_USERS_FILE).mtimeMs;\n }\n } catch (err) {\n log.error(`Failed to load known users: ${err}`);\n }\n\n knownUsersCache = users;\n return users;\n}\n\n/**\n * 保存已知用户到文件\n */\nfunction saveKnownUsers(users: Map<string, KnownUser>): void {\n try {\n ensureStorageDir();\n const data = Array.from(users.values());\n fs.writeFileSync(KNOWN_USERS_FILE, JSON.stringify(data, null, 2), \"utf-8\");\n cacheLastModified = Date.now();\n knownUsersCache = users;\n } catch (err) {\n log.error(`Failed to save known users: ${err}`);\n }\n}\n\n/**\n * 记录一个已知用户(当收到用户消息时调用)\n * \n * @param user - 用户信息\n */\nexport function recordKnownUser(user: Omit<KnownUser, \"firstInteractionAt\">): void {\n const users = loadKnownUsers();\n const key = getUserKey(user.type, user.openid, user.accountId);\n \n const existing = users.get(key);\n const now = user.lastInteractionAt || Date.now();\n \n users.set(key, {\n ...user,\n lastInteractionAt: now,\n firstInteractionAt: existing?.firstInteractionAt ?? now,\n // 更新昵称(如果有新的)\n nickname: user.nickname || existing?.nickname,\n });\n \n saveKnownUsers(users);\n log.info(`Recorded user: ${key}`);\n}\n\n/**\n * 获取一个已知用户\n * \n * @param type - 用户类型\n * @param openid - 用户 openid\n * @param accountId - 账户 ID\n */\nexport function getKnownUser(type: string, openid: string, accountId: string): KnownUser | undefined {\n const users = loadKnownUsers();\n const key = getUserKey(type, openid, accountId);\n return users.get(key);\n}\n\n/**\n * 列出已知用户\n * \n * @param options - 过滤选项\n */\nexport function listKnownUsers(options?: ListKnownUsersOptions): KnownUser[] {\n const users = loadKnownUsers();\n let result = Array.from(users.values());\n \n // 过滤类型\n if (options?.type) {\n result = result.filter(u => u.type === options.type);\n }\n \n // 过滤账户\n if (options?.accountId) {\n result = result.filter(u => u.accountId === options.accountId);\n }\n \n // 排序\n if (options?.sortByLastInteraction !== false) {\n result.sort((a, b) => b.lastInteractionAt - a.lastInteractionAt);\n }\n \n // 限制数量\n if (options?.limit && options.limit > 0) {\n result = result.slice(0, options.limit);\n }\n \n return result;\n}\n\n/**\n * 删除一个已知用户\n * \n * @param type - 用户类型\n * @param openid - 用户 openid\n * @param accountId - 账户 ID\n */\nexport function removeKnownUser(type: string, openid: string, accountId: string): boolean {\n const users = loadKnownUsers();\n const key = getUserKey(type, openid, accountId);\n const deleted = users.delete(key);\n if (deleted) {\n saveKnownUsers(users);\n }\n return deleted;\n}\n\n/**\n * 清除所有已知用户\n * \n * @param accountId - 可选,只清除指定账户的用户\n */\nexport function clearKnownUsers(accountId?: string): number {\n const users = loadKnownUsers();\n let count = 0;\n \n if (accountId) {\n for (const [key, user] of users) {\n if (user.accountId === accountId) {\n users.delete(key);\n count++;\n }\n }\n } else {\n count = users.size;\n users.clear();\n }\n \n if (count > 0) {\n saveKnownUsers(users);\n }\n return count;\n}\n\n// ============ 主动发送消息 ============\n\n/**\n * 主动发送消息(带配置解析)\n * 注意:与 outbound.ts 中的 sendProactiveMessage 不同,这个函数接受 OpenClawConfig 并自动解析账户\n * \n * @param options - 发送选项\n * @param cfg - OpenClaw 配置\n * @returns 发送结果\n * \n * @example\n * ```typescript\n * // 发送私聊消息\n * const result = await sendProactive({\n * to: \"E7A8F3B2C1D4E5F6A7B8C9D0E1F2A3B4\", // 用户 openid\n * text: \"你好!这是一条主动消息\",\n * type: \"c2c\",\n * }, cfg);\n * \n * // 发送群聊消息\n * const result = await sendProactive({\n * to: \"A1B2C3D4E5F6A7B8\", // 群组 openid\n * text: \"群公告:今天有活动\",\n * type: \"group\",\n * }, cfg);\n * \n * // 发送带图片的消息\n * const result = await sendProactive({\n * to: \"E7A8F3B2C1D4E5F6A7B8C9D0E1F2A3B4\",\n * text: \"看看这张图片\",\n * imageUrl: \"https://example.com/image.png\",\n * type: \"c2c\",\n * }, cfg);\n * ```\n */\nexport async function sendProactive(\n options: ProactiveSendOptions,\n cfg: OpenClawConfig\n): Promise<ProactiveSendResult> {\n const { to, text, type = \"c2c\", imageUrl, accountId = \"default\" } = options;\n \n // 解析账户配置\n const account = resolveQQBotAccount(cfg, accountId);\n \n if (!account.appId || !account.clientSecret) {\n return {\n success: false,\n error: \"QQBot not configured (missing appId or clientSecret)\",\n };\n }\n \n try {\n const bot = getBotForAccount(account.accountId);\n const target = { scope: type as \"c2c\" | \"group\", targetId: to };\n\n // 如果有图片,先发送图片\n if (imageUrl) {\n try {\n await bot.sendImage(target, { url: imageUrl });\n log.info(`Sent image to ${type}:${to}`);\n } catch (err) {\n log.error(`Failed to send image: ${err}`);\n }\n }\n \n // 发送文本消息(主动消息:无 msgId)\n if (type === \"channel\") {\n return {\n success: false,\n error: \"Channel proactive messages are not supported. Please use group or c2c.\",\n };\n }\n\n const result = await bot.sendText(target, text);\n log.info(`Sent message to ${type}:${to}, id: ${result.id}`);\n \n return {\n success: true,\n messageId: result.id,\n timestamp: result.timestamp,\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n log.error(`Failed to send message: ${message}`);\n \n return {\n success: false,\n error: message,\n };\n }\n}\n\n/**\n * 批量发送主动消息\n * \n * @param recipients - 接收者列表(openid 数组)\n * @param text - 消息内容\n * @param type - 消息类型\n * @param cfg - OpenClaw 配置\n * @param accountId - 账户 ID\n * @returns 发送结果列表\n */\nexport async function sendBulkProactiveMessage(\n recipients: string[],\n text: string,\n type: \"c2c\" | \"group\",\n cfg: OpenClawConfig,\n accountId = \"default\"\n): Promise<Array<{ to: string; result: ProactiveSendResult }>> {\n const results: Array<{ to: string; result: ProactiveSendResult }> = [];\n \n for (const to of recipients) {\n const result = await sendProactive({ to, text, type, accountId }, cfg);\n results.push({ to, result });\n \n // 添加延迟,避免频率限制\n await new Promise(resolve => setTimeout(resolve, 500));\n }\n \n return results;\n}\n\n/**\n * 发送消息给所有已知用户\n * \n * @param text - 消息内容\n * @param cfg - OpenClaw 配置\n * @param options - 过滤选项\n * @returns 发送结果统计\n */\nexport async function broadcastMessage(\n text: string,\n cfg: OpenClawConfig,\n options?: {\n type?: \"c2c\" | \"group\";\n accountId?: string;\n limit?: number;\n }\n): Promise<{\n total: number;\n success: number;\n failed: number;\n results: Array<{ to: string; result: ProactiveSendResult }>;\n}> {\n const users = listKnownUsers({\n type: options?.type,\n accountId: options?.accountId,\n limit: options?.limit,\n sortByLastInteraction: true,\n });\n \n // 过滤掉频道用户(不支持主动发送)\n const validUsers = users.filter(u => u.type === \"c2c\" || u.type === \"group\");\n \n const results: Array<{ to: string; result: ProactiveSendResult }> = [];\n let success = 0;\n let failed = 0;\n \n for (const user of validUsers) {\n const result = await sendProactive({\n to: user.openid,\n text,\n type: user.type as \"c2c\" | \"group\",\n accountId: user.accountId,\n }, cfg);\n \n results.push({ to: user.openid, result });\n \n if (result.success) {\n success++;\n } else {\n failed++;\n }\n \n // 添加延迟,避免频率限制\n await new Promise(resolve => setTimeout(resolve, 500));\n }\n \n return {\n total: validUsers.length,\n success,\n failed,\n results,\n };\n}\n\n// ============ 辅助函数 ============\n\n/**\n * 根据账户配置直接发送主动消息(不需要 cfg)\n * \n * @param account - 已解析的账户配置\n * @param to - 目标 openid\n * @param text - 消息内容\n * @param type - 消息类型\n */\nexport async function sendProactiveMessageDirect(\n account: ResolvedQQBotAccount,\n to: string,\n text: string,\n type: \"c2c\" | \"group\" = \"c2c\"\n): Promise<ProactiveSendResult> {\n if (!account.appId || !account.clientSecret) {\n return {\n success: false,\n error: \"QQBot not configured (missing appId or clientSecret)\",\n };\n }\n \n try {\n const bot = getBotForAccount(account.accountId);\n const target = { scope: type, targetId: to };\n const result = await bot.sendText(target, text);\n \n return {\n success: true,\n messageId: result.id,\n timestamp: result.timestamp,\n };\n } catch (err) {\n return {\n success: false,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\n/**\n * 获取已知用户统计\n */\nexport function getKnownUsersStats(accountId?: string): {\n total: number;\n c2c: number;\n group: number;\n channel: number;\n} {\n const users = listKnownUsers({ accountId });\n \n return {\n total: users.length,\n c2c: users.filter(u => u.type === \"c2c\").length,\n group: users.filter(u => u.type === \"group\").length,\n channel: users.filter(u => u.type === \"channel\").length,\n };\n}\n\n/**\n * 获取用户的所有群组(某用户在哪些群里交互过)\n *\n * @param accountId 机器人账户 ID\n * @param openid 用户 openid\n * @returns 群组 openid 列表\n */\nexport function getUserGroups(accountId: string, openid: string): string[] {\n const users = loadKnownUsers();\n const groups: string[] = [];\n for (const [, user] of users) {\n if (user.accountId !== accountId) continue;\n if (user.type !== 'group') continue;\n // group 类型的 openid 是 groupOpenid,检查该群中是否有此用户交互记录\n // 注:当前数据模型 group 记录的 openid 是 groupOpenid 本身\n // 要实现精确的\"用户在哪些群\",需要在 recordKnownUser 时同时记录 senderOpenid\n // 这里降级为:列出该账户的所有群 openid\n groups.push(user.openid);\n }\n return [...new Set(groups)];\n}\n\n/**\n * 获取群组的所有已知成员\n *\n * @param accountId 机器人账户 ID\n * @param groupOpenid 群组 openid\n * @returns 在该群中交互过的用户列表\n */\nexport function getGroupMembers(accountId: string, groupOpenid: string): KnownUser[] {\n const users = loadKnownUsers();\n const members: KnownUser[] = [];\n for (const [, user] of users) {\n if (user.accountId !== accountId) continue;\n if (user.type === 'group' && user.openid === groupOpenid) {\n members.push(user);\n }\n }\n return members;\n}\n","/**\n * 消息 ID 缓存 — 记录最近处理的 msgId,供被动回复使用。\n *\n * 场景:外部调用发送消息时没有上下文,\n * 从缓存获取最近一条未过期的 msgId 作为被动回复目标。\n */\n\ninterface CachedMsgId {\n msgId: string;\n timestamp: number;\n}\n\nconst MAX_PER_TARGET = 10;\nconst TTL_GROUP = 5 * 60 * 1000; // 5 分钟\nconst TTL_C2C = 30 * 60 * 1000; // 30 分钟\nconst MAX_TARGETS = 200;\n\nconst cache = new Map<string, CachedMsgId[]>();\n\nexport function cacheMsgId(scope: string, targetId: string, msgId: string): void {\n if (!scope || !targetId || !msgId) return;\n const key = `${scope}:${targetId}`;\n const existing = cache.get(key);\n if (existing) {\n // LRU: 删除再重新插入\n cache.delete(key);\n existing.push({ msgId, timestamp: Date.now() });\n if (existing.length > MAX_PER_TARGET) existing.shift();\n cache.set(key, existing);\n } else {\n cache.set(key, [{ msgId, timestamp: Date.now() }]);\n if (cache.size > MAX_TARGETS) {\n const oldest = cache.keys().next().value;\n if (oldest) cache.delete(oldest);\n }\n }\n}\n\nexport function getCachedMsgId(scope: string, targetId: string): string | undefined {\n const key = `${scope}:${targetId}`;\n const list = cache.get(key);\n if (!list || list.length === 0) return undefined;\n const now = Date.now();\n const ttl = scope === 'group' ? TTL_GROUP : TTL_C2C;\n for (let i = list.length - 1; i >= 0; i--) {\n if (now - list[i].timestamp < ttl) {\n return list[i].msgId;\n }\n }\n return undefined;\n}\n\nexport function clearMsgIdCache(scope: string, targetId: string): void {\n cache.delete(`${scope}:${targetId}`);\n}\n","/**\n * QQBotGateway 事件处理\n *\n * 处理 SDK 的 message / interaction 事件:\n * - message: 中间件处理完毕后,将消息转发到 OpenClaw AI\n * - interaction: 配置更新 / 审批按钮\n */\n\nimport type { MiddlewareContext, QQBotInboundMessage, InteractionEvent } from '@tencent-connect/qqbot-nodejs';\nimport type { PluginRuntime } from 'openclaw/plugin-sdk';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport type { PluginLogger } from '../utils/plugin-logger.js';\nimport { dispatchToOpenClaw } from '../dispatch/index.js';\nimport { runWithRequestContext } from '../request-context.js';\nimport { getApprovalHandler } from '../features/approval-handler.js';\nimport { recordKnownUser } from '../features/proactive.js';\nimport { cacheMsgId } from '../features/msgid-cache.js';\nimport { getAdapters } from '../adapter/resolve.js';\nimport { resolveGroupConfigFromAccount, resolveGroupPolicy, resolveMentionPatterns } from '../config.js';\nimport { getPackageVersion } from '../utils/pkg-version.js';\nimport { getOpenClawVersion } from '../bot-instance.js';\n\nexport async function handleMessage(\n ctx: MiddlewareContext,\n msg: QQBotInboundMessage,\n account: ResolvedQQBotAccount,\n runtime: PluginRuntime,\n log: PluginLogger,\n): Promise<void> {\n const hlog = log.child('handle');\n const scope = msg.replyTarget.scope;\n const targetId = scope === 'group'\n ? `qqbot:group:${msg.replyTarget.targetId}`\n : `qqbot:c2c:${msg.replyTarget.targetId}`;\n\n const mergedCount = (ctx.state.mergedMessages as unknown[] | undefined)?.length;\n if (mergedCount) {\n hlog.info(`merged batch count=${mergedCount} msgId=${msg.messageId}`);\n } else {\n hlog.debug(`enter msgId=${msg.messageId} scope=${scope} contentLen=${(msg.content ?? '').length}`);\n }\n\n try {\n cacheMsgId(scope, msg.replyTarget.targetId, msg.messageId);\n\n recordKnownUser({\n type: scope === 'group' ? 'group' : 'c2c',\n openid: scope === 'group' ? msg.replyTarget.targetId : msg.senderId,\n accountId: account.accountId,\n nickname: msg.senderName,\n lastInteractionAt: Date.now(),\n });\n\n await runWithRequestContext(\n {\n accountId: account.accountId,\n messageId: msg.messageId,\n openId: msg.senderId,\n target: targetId,\n },\n () => dispatchToOpenClaw(ctx, msg, account, runtime, log),\n );\n } catch (err) {\n hlog.error(`dispatch error: ${err}`);\n }\n hlog.debug(`done msgId=${msg.messageId}`);\n}\n\nconst INTERACTION_QUERY = 2001;\nconst INTERACTION_UPDATE = 2002;\n\nexport async function handleInteraction(\n event: InteractionEvent,\n account: ResolvedQQBotAccount,\n runtime: PluginRuntime,\n log: PluginLogger,\n acknowledgeInteraction: (id: string, code?: number, data?: Record<string, unknown>) => Promise<void>,\n): Promise<void> {\n if (event.data?.type === INTERACTION_QUERY) {\n await handleConfigQuery(event, account, runtime, log, acknowledgeInteraction);\n return;\n }\n if (event.data?.type === INTERACTION_UPDATE) {\n await handleConfigUpdate(event, account, runtime, log);\n try {\n const adapters = getAdapters(runtime);\n const cfg = adapters.getConfig?.() ?? {};\n const groupOpenid = (event as any).group_openid ?? '';\n const updatedCfg = groupOpenid ? resolveGroupConfigFromAccount(account, groupOpenid) : null;\n const requireMention = updatedCfg?.requireMention ?? true;\n const clawCfg = buildClawCfg(requireMention, [], resolveGroupPolicy(cfg, account.accountId));\n await acknowledgeInteraction(event.id, 0, { claw_cfg: clawCfg });\n } catch {\n try { await acknowledgeInteraction(event.id); } catch { /* ignore */ }\n }\n return;\n }\n\n await handleApproval(event, account, log, acknowledgeInteraction);\n}\n\n// ── Interaction 子处理 ──\n\nasync function handleConfigQuery(\n event: InteractionEvent,\n account: ResolvedQQBotAccount,\n runtime: PluginRuntime,\n log: PluginLogger,\n ack: (id: string, code?: number, data?: Record<string, unknown>) => Promise<void>,\n): Promise<void> {\n const groupOpenid = (event as any).group_openid ?? '';\n try {\n const adapters = getAdapters(runtime);\n const cfg = adapters.getConfig?.() ?? {};\n const groupCfg = groupOpenid ? resolveGroupConfigFromAccount(account, groupOpenid) : null;\n const requireMention = groupCfg?.requireMention ?? true;\n const agentId = groupOpenid\n ? adapters.resolveAgentRoute?.({ cfg, channel: 'qqbot', accountId: account.accountId, peer: { kind: 'group', id: groupOpenid } })?.agentId\n : undefined;\n const mentionPatterns = resolveMentionPatterns(cfg, agentId);\n const clawCfg = buildClawCfg(requireMention, mentionPatterns, resolveGroupPolicy(cfg, account.accountId));\n log.info(`interaction query: group=${groupOpenid} requireMention=${requireMention}`);\n await ack(event.id, 0, { claw_cfg: clawCfg });\n } catch (err) {\n log.warn(`interaction query failed: ${(err as Error)?.message ?? err}, ack without data`);\n try { await ack(event.id); } catch { /* ignore */ }\n }\n}\n\nasync function handleConfigUpdate(\n event: InteractionEvent,\n account: ResolvedQQBotAccount,\n runtime: PluginRuntime,\n log: PluginLogger,\n): Promise<void> {\n const resolved = (event.data as any)?.resolved;\n const update = resolved?.claw_cfg;\n const groupOpenid = (event as any).group_openid ?? '';\n\n if (update?.require_mention !== undefined && groupOpenid) {\n try {\n await setGroupRequireMention(runtime, account.accountId, groupOpenid, update.require_mention === 'mention');\n log.info(`interaction: group=${groupOpenid} requireMention=${update.require_mention}`);\n } catch (err) {\n log.error(`interaction update failed: ${err}`);\n }\n }\n}\n\nasync function handleApproval(\n event: InteractionEvent,\n account: ResolvedQQBotAccount,\n log: PluginLogger,\n ack: (id: string) => Promise<void>,\n): Promise<void> {\n try { await ack(event.id); } catch { /* ignore */ }\n\n const buttonData = event.data?.resolved?.button_data;\n if (!buttonData?.startsWith('approve:')) return;\n\n // 身份授权校验:操作者需在 allowFrom 白名单中\n const operatorId = resolveOperatorId(event);\n if (!isApprovalAuthorized(account, operatorId)) {\n log.warn(`[approval] unauthorized operator=${operatorId ?? 'unknown'} account=${account.accountId}`);\n return;\n }\n\n const parts = buttonData.split(':');\n if (parts.length < 3) return;\n\n const handler = getApprovalHandler(account.accountId);\n if (!handler) return;\n\n const approvalId = parts[1];\n const decision = parts[2] as 'allow-once' | 'allow-always' | 'deny';\n try { await handler.resolveApproval(approvalId, decision); } catch (err) {\n log.error(`interaction approve error: ${err instanceof Error ? err.message : String(err)}`);\n }\n}\n\nconst CHANNEL_VER = getPackageVersion();\n\n// ── 审批授权校验 ──\n\n/**\n * 从交互事件中提取操作者身份标识。\n * QQ Bot 按钮回调事件中,操作者 openid 通常在 `user_openid` 或 `data.resolved.user_id` 字段。\n */\nfunction resolveOperatorId(event: InteractionEvent): string | undefined {\n const evt = event as any;\n return evt.user_openid\n ?? evt.data?.resolved?.user_id\n ?? evt.data?.resolved?.user_openid\n ?? evt.openid;\n}\n\n/**\n * 校验操作者是否有权限执行审批操作。\n * 规则:allowFrom 为空或含 \"*\" → 开放;否则操作者必须在 allowFrom 中。\n */\nfunction isApprovalAuthorized(account: ResolvedQQBotAccount, operatorId?: string): boolean {\n if (!operatorId) return false;\n const allowFrom = account.config?.allowFrom ?? [];\n if (!allowFrom.length || allowFrom.includes('*')) return true;\n return allowFrom.includes(operatorId);\n}\n\nfunction buildClawCfg(\n requireMention: boolean,\n mentionPatterns: string[],\n groupPolicy: string,\n): Record<string, unknown> {\n return {\n channel_type: 'qqbot',\n channel_ver: CHANNEL_VER,\n claw_type: 'openclaw',\n claw_ver: getOpenClawVersion(),\n require_mention: requireMention ? 'mention' : 'always',\n group_policy: groupPolicy,\n mention_patterns: mentionPatterns.join(','),\n online_state: 'online',\n };\n}\n\n// ── Config 写入 ──\n\nfunction setGroupRequireMention(\n runtime: PluginRuntime,\n accountId: string,\n groupOpenid: string,\n requireMention: boolean,\n): Promise<void> {\n const adapters = getAdapters(runtime);\n return adapters.persistConfig?.((cfg: any) => {\n const qqbot = (cfg.channels ?? {})?.qqbot ?? {};\n const groups = accountId !== 'default' && qqbot.accounts?.[accountId]\n ? (qqbot.accounts[accountId].groups = { ...qqbot.accounts[accountId].groups })\n : (qqbot.groups = { ...qqbot.groups });\n groups[groupOpenid] = { ...groups[groupOpenid], requireMention };\n }) ?? Promise.resolve();\n}\n","/**\n * PluginWebhookAdapter — 将 OpenClaw Gateway HTTP 路由适配为 SDK WebhookServerAdapter。\n *\n * 复用 openclaw/plugin-sdk/webhook-ingress 的 target registry、rate limiting 和\n * resolveWebhookTargetWithAuthOrRejectSync 签名匹配,对齐旧版本插件行为。\n *\n * 多账号同路径:通过 resolveWebhookTargetWithAuthOrRejectSync 遍历 targets 做\n * Ed25519 签名匹配,找到正确的 target 后调用其 SDK handler 处理事件。\n */\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport type { PluginLogger } from '../utils/plugin-logger.js';\nimport type { WebhookServerAdapter, WebhookRequestHandler } from '@tencent-connect/qqbot-nodejs';\nimport { verifyWebhookSignature } from '@tencent-connect/qqbot-nodejs/protocol';\n\n// ── 模块级共享状态 ──\ninterface WebhookTarget {\n path: string;\n accountId: string;\n appId: string;\n clientSecret: string;\n handler: WebhookRequestHandler;\n}\nconst sharedTargets = new Map<string, WebhookTarget[]>();\nconst _routeUnregisters = new Map<string, () => void>();\n\nexport function createPluginWebhookAdapter(params: {\n account: ResolvedQQBotAccount;\n log: PluginLogger;\n}): WebhookServerAdapter {\n let unregistered = false;\n let storedPath = '';\n const accountId = params.account.accountId;\n\n return {\n async listen(_port: number, path: string, handler: WebhookRequestHandler): Promise<void> {\n const ingress = await import('openclaw/plugin-sdk/webhook-ingress').catch(() => null);\n if (!ingress?.registerWebhookTargetWithPluginRoute) {\n params.log.error('Webhook ingress not available');\n return;\n }\n\n const webhookPath = storedPath = path && path !== '/' ? path : (params.account.config.webhook?.path ?? '/qqbot/webhook');\n\n // 去重:reload 时避免同一 accountId 重复注册\n const existing = sharedTargets.get(webhookPath);\n if (existing) {\n const dupIdx = existing.findIndex((t) => t.accountId === accountId);\n if (dupIdx >= 0) {\n existing[dupIdx] = { path: webhookPath, accountId, appId: params.account.appId, clientSecret: params.account.clientSecret, handler };\n params.log.info(`Webhook target refreshed for ${accountId} on ${webhookPath}`);\n return;\n }\n }\n\n // 注册 target(框架管理 targetsByPath + 首次触发路由注册)\n const target: WebhookTarget = {\n path: webhookPath,\n accountId,\n appId: params.account.appId,\n clientSecret: params.account.clientSecret,\n handler,\n };\n\n const result = ingress.registerWebhookTargetWithPluginRoute({\n targetsByPath: sharedTargets as any,\n target,\n route: {\n auth: 'plugin',\n match: 'exact',\n pluginId: 'openclaw-qqbot',\n source: 'qqbot-webhook',\n accountId,\n replaceExisting: true,\n log: (msg: string) => params.log.info(msg),\n handler: createSharedHandler(webhookPath, params.log),\n },\n onLastPathTargetRemoved: () => {\n params.log.info(`Last webhook target removed from ${webhookPath}`);\n },\n });\n _routeUnregisters.set(webhookPath, result.unregister);\n\n params.log.info(`Webhook target added on ${webhookPath} (${sharedTargets.get(webhookPath)!.length} account(s))`);\n },\n\n close(): void {\n if (unregistered) return;\n unregistered = true;\n if (storedPath) {\n _routeUnregisters.get(storedPath)?.();\n _routeUnregisters.delete(storedPath);\n }\n },\n };\n}\n\nfunction createSharedHandler(path: string, log: PluginLogger) {\n return async (req: any, res: any) => {\n try {\n const ingress = await import('openclaw/plugin-sdk/webhook-ingress').catch(() => null);\n\n if (!ingress?.withResolvedWebhookRequestPipeline) {\n await handleSimple(req, res, path, log);\n return;\n }\n\n await ingress.withResolvedWebhookRequestPipeline({\n req,\n res,\n targetsByPath: sharedTargets,\n rateLimiter: ingress.createFixedWindowRateLimiter?.({\n windowMs: 60_000, maxRequests: 600, maxTrackedKeys: 4096,\n }),\n inFlightLimiter: ingress.createWebhookInFlightLimiter?.({\n maxInFlightPerKey: 8, maxTrackedKeys: 4096,\n }),\n requireJsonContentType: true,\n handle: async ({ targets }) => {\n const bodyResult = await ingress.readWebhookBodyOrReject({\n req, res, maxBytes: 1_048_576, timeoutMs: 30_000,\n });\n if (!bodyResult.ok) {\n log.warn?.(`[webhook] body rejected`);\n return;\n }\n\n const rawBody = Buffer.from(bodyResult.value, 'utf-8');\n\n let payload: any;\n try { payload = JSON.parse(bodyResult.value); } catch (err) {\n log.warn?.(`[webhook] invalid json: ${(err as Error).message}`);\n res.statusCode = 400; res.end(JSON.stringify({ error: 'invalid json' })); return;\n }\n\n // op:13 → 用第一个 target 的 SDK handler(无需签名)\n if (payload.op === 13) {\n const t = targets[0];\n if (!t) {\n log.warn?.(`[webhook] op:13 no target on ${path}`);\n res.statusCode = 500; res.end(JSON.stringify({ error: 'no target' })); return;\n }\n const h = resolveTargetHandler(path, t.accountId);\n if (!h) {\n log.warn?.(`[webhook] op:13 no handler for ${t.accountId}`);\n res.statusCode = 500; res.end(JSON.stringify({ error: 'no handler' })); return;\n }\n await delegateToHandler(h, req, res, rawBody);\n return;\n }\n\n // op:0 → 签名匹配 target → SDK handler 处理事件\n const timestamp = getHeader(req, 'x-signature-timestamp') ?? '';\n const signature = getHeader(req, 'x-signature-ed25519') ?? '';\n if (!timestamp || !signature) {\n log.warn?.(`[webhook] missing signature headers on ${req.url}`);\n res.statusCode = 401; res.end(JSON.stringify({ error: 'missing signature' })); return;\n }\n\n const matched = ingress.resolveWebhookTargetWithAuthOrRejectSync({\n targets, res,\n isMatch: (t: any) => verifyWebhookSignature({\n body: rawBody, timestamp, signature, botSecret: t.clientSecret as string,\n }),\n unauthorizedStatusCode: 401,\n unauthorizedMessage: JSON.stringify({ error: 'invalid signature' }),\n });\n if (!matched) {\n log.warn?.(`[webhook] signature mismatch on ${path} (${targets.length} target(s))`);\n return;\n }\n\n const h = resolveTargetHandler(path, matched.accountId);\n if (!h) {\n log.error?.(`[webhook] no handler for matched target ${matched.accountId}`);\n res.statusCode = 500; res.end(JSON.stringify({ error: 'no handler' })); return;\n }\n await delegateToHandler(h, req, res, rawBody);\n },\n });\n } catch (err) {\n log.error(`Webhook handler error: ${(err as Error).message}`);\n if (!res.headersSent) {\n res.statusCode = 500;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'internal error' }));\n }\n }\n };\n}\n\nfunction resolveTargetHandler(path: string, accountId: string): WebhookRequestHandler | undefined {\n return sharedTargets.get(path)?.find((t) => t.accountId === accountId)?.handler;\n}\n\nasync function delegateToHandler(handler: WebhookRequestHandler, req: any, res: any, rawBody: Buffer) {\n const headers: Record<string, string | string[]> = {};\n for (const [k, v] of Object.entries(req.headers)) {\n headers[k.toLowerCase()] = v as string | string[];\n }\n const resp = await handler({ body: rawBody, headers });\n res.statusCode = resp.status;\n if (resp.headers) {\n for (const [k, v] of Object.entries(resp.headers)) res.setHeader(k, v as string);\n }\n res.end(resp.body);\n}\n\nasync function handleSimple(req: any, res: any, path: string, log: PluginLogger) {\n try {\n const ct = String(req.headers['content-type'] ?? '');\n if (!ct.includes('application/json')) { res.statusCode = 400; res.end(JSON.stringify({ error: 'unsupported content type' })); return; }\n\n const chunks: Buffer[] = [];\n let total = 0;\n for await (const chunk of req) {\n const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n total += buf.length;\n if (total > 1_048_576) { res.statusCode = 413; res.end(JSON.stringify({ error: 'too large' })); return; }\n chunks.push(buf);\n }\n const rawBody = Buffer.concat(chunks);\n const entries = sharedTargets.get(path) ?? [];\n for (const entry of entries) {\n const resp = await entry.handler({ body: rawBody, headers: mapHeaders(req) });\n if (resp.status < 400) {\n res.statusCode = resp.status;\n if (resp.headers) for (const [k, v] of Object.entries(resp.headers)) res.setHeader(k, v as string);\n res.end(resp.body);\n return;\n }\n }\n res.statusCode = 401; res.end(JSON.stringify({ error: 'invalid signature' }));\n } catch (err) {\n log.error(`Webhook simple handler error: ${(err as Error).message}`);\n if (!res.headersSent) { res.statusCode = 500; res.end(JSON.stringify({ error: 'internal error' })); }\n }\n}\n\nfunction mapHeaders(req: any): Record<string, string | string[]> {\n const h: Record<string, string | string[]> = {};\n for (const [k, v] of Object.entries(req.headers ?? {})) h[k.toLowerCase()] = v as string | string[];\n return h;\n}\n\nfunction getHeader(req: any, key: string): string | undefined {\n const val = req.headers[key];\n return Array.isArray(val) ? val[0] : val;\n}\n","/**\n * 凭证暂存与恢复\n *\n * 解决热更新被打断时 openclaw.json 中 appId/secret 丢失的问题。\n *\n * 存储路径:~/.openclaw/qqbot/data/credential-backup/current.json\n * 使用子目录避免被框架 SQLite 迁移扫描(迁移只匹配 data/credential-backup.json)。\n */\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { getQQBotDataDir } from '../utils/platform.js';\n\nconst BACKUP_DIR = 'credential-backup';\nconst BACKUP_FILENAME = 'current.json';\nconst LEGACY_FILENAME = 'credential-backup.json';\n\ninterface CredentialBackup {\n accountId: string;\n appId: string;\n clientSecret: string;\n savedAt: string;\n}\n\nfunction getBackupPath(): string {\n return path.join(getQQBotDataDir('data'), BACKUP_DIR, BACKUP_FILENAME);\n}\n\n/**\n * 保存凭证快照到暂存文件(gateway 成功启动后调用)\n */\nexport function saveCredentialBackup(accountId: string, appId: string, clientSecret: string): void {\n if (!appId || !clientSecret) return;\n try {\n const backupPath = getBackupPath();\n const dir = path.dirname(backupPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n const data: CredentialBackup = {\n accountId,\n appId,\n clientSecret,\n savedAt: new Date().toISOString(),\n };\n const tmpPath = backupPath + '.tmp';\n fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2) + '\\n', 'utf8');\n fs.renameSync(tmpPath, backupPath);\n } catch {\n // 非关键操作,静默忽略\n }\n}\n\n/**\n * 从暂存文件读取凭证(仅在配置为空时调用)\n * 返回 null 表示无可用备份\n */\nexport function loadCredentialBackup(accountId?: string): CredentialBackup | null {\n try {\n // 优先读新路径\n const backupPath = getBackupPath();\n if (fs.existsSync(backupPath)) {\n const data = readBackupFile(backupPath, accountId);\n if (data) return data;\n }\n // 兼容旧路径(升级过渡)\n const legacyPath = path.join(getQQBotDataDir('data'), LEGACY_FILENAME);\n if (fs.existsSync(legacyPath)) {\n return readBackupFile(legacyPath, accountId);\n }\n return null;\n } catch {\n return null;\n }\n}\n\nfunction readBackupFile(filePath: string, accountId?: string): CredentialBackup | null {\n const raw = fs.readFileSync(filePath, 'utf8');\n const data: CredentialBackup = JSON.parse(raw);\n if (!data.appId || !data.clientSecret) return null;\n if (accountId && data.accountId !== accountId) return null;\n return data;\n}\n","/**\n * 审批相关工具函数\n *\n * 审批 payload 判断逻辑,供 channel.ts 和 features/approval-handler 使用。\n */\n\n/** 检查 payload 是否为审批消息(execApproval / plugin approval) */\nexport function isApprovalPayload(payload: unknown): boolean {\n if (!payload || typeof payload !== 'object') return false;\n const p = payload as Record<string, unknown>;\n const cd = p.channelData;\n if (cd && typeof cd === 'object' && !Array.isArray(cd)) {\n const execApproval = (cd as Record<string, unknown>).execApproval;\n if (execApproval && typeof execApproval === 'object' && !Array.isArray(execApproval)) {\n return true;\n }\n }\n const text = typeof p.text === 'string' ? p.text : '';\n return /(?:Plugin|Exec) approval (?:required|allowed|denied|expired)/i.test(text);\n}\n\n/** 审批 ChannelPlugin stub — 空壳实现(实际审批由 features/approval-handler 处理) */\nexport const approvalStubs = {\n execApprovals: {\n getInitiatingSurfaceState: () => ({ kind: 'enabled' as const }),\n shouldSuppressForwardingFallback: () => true,\n shouldSuppressLocalPrompt: ({ payload }: { payload: unknown }) => isApprovalPayload(payload),\n buildPendingPayload: () => null,\n buildResolvedPayload: () => null,\n },\n approvals: {\n delivery: {\n hasConfiguredDmRoute: () => true,\n shouldSuppressForwardingFallback: () => true,\n },\n render: {\n exec: { buildPendingPayload: () => null, buildResolvedPayload: () => null },\n plugin: { buildPendingPayload: () => null, buildResolvedPayload: () => null },\n },\n },\n} as const;\n","/**\n * QQBot CLI Onboarding Adapter\n *\n * 提供 openclaw onboard 命令的交互式配置支持。\n * 从原 src/onboarding.ts 迁移,保持接口兼容。\n */\nimport type {\n ChannelOnboardingAdapter,\n OpenClawConfig,\n} from 'openclaw/plugin-sdk';\nimport { DEFAULT_ACCOUNT_ID, listQQBotAccountIds, resolveQQBotAccount } from '../config.js';\n\n/**\n * Onboarding adapter — 导出给 ChannelPlugin 使用\n *\n * 注:完整 onboarding 交互逻辑较复杂(约 300 行),\n * 此处仅保留 adapter 骨架,完整实现从原文件迁移。\n */\nexport const qqbotOnboardingAdapter: ChannelOnboardingAdapter = {\n getStatus: (ctx) => {\n const cfg = ctx.config as OpenClawConfig;\n const accountIds = listQQBotAccountIds(cfg);\n if (accountIds.length === 0) {\n return { configured: false, accountCount: 0 };\n }\n const firstAccount = resolveQQBotAccount(cfg, accountIds[0]);\n return {\n configured: Boolean(firstAccount.appId && firstAccount.clientSecret),\n accountCount: accountIds.length,\n defaultAccountId: accountIds[0],\n };\n },\n configure: async (ctx) => {\n // 完整实现需要从原 onboarding.ts 迁移交互逻辑\n // 此处返回默认结果\n return { success: false, message: 'Onboarding not yet migrated to new architecture' };\n },\n};\n","import type { OpenClawPluginApi } from 'openclaw/plugin-sdk';\nimport { listQQBotAccountIds } from '../config.js';\nimport { getBotForAccount } from '../bot-instance.js';\nimport { getRequestAccountId } from '../request-context.js';\n\n// ========== JSON Schema ==========\n\nconst PlatformApiSchema = {\n type: 'object',\n properties: {\n method: {\n type: 'string',\n description:\n 'HTTP 请求方法。可选值:GET, POST, PUT, PATCH, DELETE',\n enum: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],\n },\n path: {\n type: 'string',\n description:\n 'API 路径(不含域名),占位符需替换为实际值。' +\n '示例:/users/@me/guilds, /guilds/{guild_id}/channels, ' +\n '/v2/groups/{group_id}/bot_state',\n },\n body: {\n type: 'object',\n description:\n '请求体(JSON),用于 POST/PUT/PATCH 请求。' +\n 'GET/DELETE 请求不需要此参数。',\n },\n query: {\n type: 'object',\n description:\n 'URL 查询参数(键值对),会拼接到路径后面。' +\n '如 { \"limit\": \"100\", \"after\": \"0\" } 会拼接为 ?limit=100&after=0',\n additionalProperties: { type: 'string' },\n },\n },\n required: ['method', 'path'],\n} as const;\n\n// ========== 工具函数 ==========\n\nfunction json(data: unknown) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(data, null, 2) }],\n details: data,\n };\n}\n\nfunction validatePath(path: string): string | null {\n if (!path.startsWith('/')) return \"path 必须以 / 开头\";\n if (path.includes('..') || path.includes('//')) return \"path 不允许包含 .. 或 //\";\n if (!/^\\/[a-zA-Z0-9\\-._~:@!$&'()*+,;=/%]+$/.test(path) && path !== '/') {\n return 'path 包含非法字符';\n }\n return null;\n}\n\n// ========== 注册入口 ==========\n\n/**\n * 注册 QQBot 平台统一 API 网关工具。\n * 通过 SDK 的 bot.api 网关代理所有 QQ 开放平台 HTTP 接口 — 自动鉴权、重试、结构化错误。\n *\n * 覆盖范围:\n * - 频道(Guild/Channel/Member/Announce/Forum/Schedule)→ 见 qqbot-channel skill\n * - 群(Group/Member)→ 见 qqbot-group skill\n */\nexport function registerPlatformTool(api: OpenClawPluginApi): void {\n const cfg = api.config;\n if (!cfg) return;\n\n const accountIds = listQQBotAccountIds(cfg);\n if (accountIds.length === 0) return;\n\n api.registerTool(\n {\n name: 'qqbot_platform_api',\n label: 'QQBot Platform API Gateway',\n description:\n 'QQ 开放平台统一 HTTP API 网关,自动填充鉴权 Token。' +\n '常用接口速查:' +\n '【频道】GET /users/@me/guilds | /guilds/{guild_id}/channels | /channels/{channel_id} | ' +\n '【群】GET /v2/groups/{group_id}/bot_state | /v2/groups/{group_id}/members/{member_id} | /v2/groups/{group_id}/info。' +\n '更多接口和参数详情请阅读 qqbot-channel 和 qqbot-group skill。',\n parameters: PlatformApiSchema,\n async execute(_toolCallId, params) {\n const p = params as {\n method: string;\n path: string;\n body?: Record<string, unknown>;\n query?: Record<string, string>;\n };\n\n if (!p.method) return json({ error: 'method 为必填参数' });\n if (!p.path) return json({ error: 'path 为必填参数' });\n\n const method = p.method.toUpperCase();\n if (!['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {\n return json({ error: `不支持的 HTTP 方法: ${method}` });\n }\n\n const pathError = validatePath(p.path);\n if (pathError) return json({ error: pathError });\n\n const accountId = getRequestAccountId();\n if (!accountId) {\n return json({ error: '无法获取当前请求的账号信息,此工具仅支持在消息会话中使用' });\n }\n\n try {\n const bot = getBotForAccount(accountId);\n const apiGateway = bot.api;\n\n let data: unknown;\n switch (method) {\n case 'GET':\n data = await apiGateway.get(p.path, p.query);\n break;\n case 'POST':\n data = await apiGateway.post(p.path, p.body);\n break;\n case 'PUT':\n data = await apiGateway.put(p.path, p.body);\n break;\n case 'PATCH':\n data = await apiGateway.patch(p.path, p.body);\n break;\n case 'DELETE':\n data = await apiGateway.delete(p.path);\n break;\n }\n\n return json(data);\n } catch (err: unknown) {\n const errMsg = err instanceof Error ? err.message : String(err);\n const apiErr = err as { httpStatus?: number; bizCode?: number; path?: string };\n return json({\n error: errMsg,\n status: apiErr.httpStatus,\n code: apiErr.bizCode,\n path: p.path,\n });\n }\n },\n },\n { name: 'qqbot_platform_api' },\n );\n}\n","import type { OpenClawPluginApi } from \"openclaw/plugin-sdk\";\nimport { getRequestTarget, getRequestAccountId } from \"../request-context.js\";\n\n// ========== 类型定义 ==========\n\ninterface RemindParams {\n action: \"add\" | \"list\" | \"remove\";\n /** 提醒内容(action=add 时必填) */\n content?: string;\n /**\n * 投递目标地址(可选,系统会自动从当前会话上下文获取)。\n * 仅在需要手动指定时填写。\n */\n to?: string;\n /**\n * 时间描述(action=add 时必填)\n * - 一次性:相对时间如 \"5m\"、\"1h30m\"、\"2h\",或绝对毫秒时间戳\n * - 周期性:cron 表达式如 \"0 8 * * *\"\n */\n time?: string;\n /** 时区(周期提醒时使用,默认 Asia/Shanghai) */\n timezone?: string;\n /** 提醒名称(可选,默认自动生成) */\n name?: string;\n /** jobId(action=remove 时必填) */\n jobId?: string;\n}\n\n// ========== JSON Schema ==========\n\nconst RemindSchema = {\n type: \"object\",\n properties: {\n action: {\n type: \"string\",\n description:\n \"操作类型。add=创建提醒, list=查看已有提醒, remove=删除提醒\",\n enum: [\"add\", \"list\", \"remove\"],\n },\n content: {\n type: \"string\",\n description: '提醒内容,如\"喝水\"、\"开会\"。action=add 时必填。',\n },\n to: {\n type: \"string\",\n description:\n \"投递目标地址(可选)。系统会自动从当前会话获取,通常无需手动填写。\" +\n \"私聊格式:qqbot:c2c:user_openid,群聊格式:qqbot:group:group_openid。\",\n },\n time: {\n type: \"string\",\n description:\n \"时间描述。支持两种格式:\\n\" +\n \"1. 相对时间:如 \\\"5m\\\"(5分钟后)、\\\"1h\\\"(1小时后)、\\\"1h30m\\\"(1.5小时后)、\\\"2d\\\"(2天后)\\n\" +\n \"2. cron 表达式:如 \\\"0 8 * * *\\\"(每天8点)、\\\"0 9 * * 1-5\\\"(工作日9点)\\n\" +\n \"系统会自动判断:包含空格的视为 cron 表达式(周期提醒),否则视为相对时间(一次性提醒)。\\n\" +\n \"action=add 时必填。\",\n },\n timezone: {\n type: \"string\",\n description:\n \"时区,仅周期提醒(cron)时需要。默认 \\\"Asia/Shanghai\\\"。\",\n },\n name: {\n type: \"string\",\n description: \"提醒任务名称(可选)。默认自动从 content 截取前 20 字。\",\n },\n jobId: {\n type: \"string\",\n description: \"要删除的任务 ID。action=remove 时必填,先用 list 获取。\",\n },\n },\n required: [\"action\"],\n} as const;\n\n// ========== 工具函数 ==========\n\nfunction json(data: unknown) {\n return {\n content: [{ type: \"text\" as const, text: JSON.stringify(data, null, 2) }],\n details: data,\n };\n}\n\n/**\n * 解析相对时间字符串为毫秒数\n * 支持格式:5m, 1h, 1h30m, 2d, 30s, 1d2h30m 等\n */\nfunction parseRelativeTime(timeStr: string): number | null {\n const s = timeStr.trim().toLowerCase();\n\n // 纯数字 → 视为分钟\n if (/^\\d+$/.test(s)) {\n return parseInt(s, 10) * 60_000;\n }\n\n let totalMs = 0;\n let matched = false;\n const regex = /(\\d+(?:\\.\\d+)?)\\s*(d|h|m|s)/g;\n let match: RegExpExecArray | null;\n\n while ((match = regex.exec(s)) !== null) {\n matched = true;\n const value = parseFloat(match[1]);\n const unit = match[2];\n switch (unit) {\n case \"d\": totalMs += value * 86_400_000; break;\n case \"h\": totalMs += value * 3_600_000; break;\n case \"m\": totalMs += value * 60_000; break;\n case \"s\": totalMs += value * 1_000; break;\n }\n }\n\n return matched ? Math.round(totalMs) : null;\n}\n\n/**\n * 判断是否为 cron 表达式(包含空格且有 3~6 段)\n */\nfunction isCronExpression(timeStr: string): boolean {\n const parts = timeStr.trim().split(/\\s+/);\n return parts.length >= 3 && parts.length <= 6;\n}\n\n/**\n * 自动生成任务名称\n */\nfunction generateJobName(content: string): string {\n const trimmed = content.trim();\n const short = trimmed.length > 20 ? trimmed.slice(0, 20) + \"…\" : trimmed;\n return `提醒: ${short}`;\n}\n\n/**\n * 构建一次性提醒的 cron 工具参数\n */\nfunction buildOnceJob(params: RemindParams, delayMs: number, to: string, accountId: string) {\n const atMs = Date.now() + delayMs;\n const content = params.content!;\n const name = params.name || generateJobName(content);\n\n return {\n action: \"add\",\n job: {\n name,\n schedule: { kind: \"at\", atMs },\n sessionTarget: \"isolated\",\n wakeMode: \"now\",\n deleteAfterRun: true,\n payload: {\n kind: \"agentTurn\",\n message: buildReminderPrompt(content),\n },\n delivery: {\n mode: \"announce\",\n channel: \"qqbot\",\n to,\n accountId,\n },\n },\n };\n}\n\n/**\n * 构建周期提醒的 cron 工具参数\n */\nfunction buildCronJob(params: RemindParams, to: string, accountId: string) {\n const content = params.content!;\n const name = params.name || generateJobName(content);\n const tz = params.timezone || \"Asia/Shanghai\";\n\n return {\n action: \"add\",\n job: {\n name,\n schedule: { kind: \"cron\", expr: params.time!.trim(), tz },\n sessionTarget: \"isolated\",\n wakeMode: \"now\",\n payload: {\n kind: \"agentTurn\",\n message: buildReminderPrompt(content),\n },\n delivery: {\n mode: \"announce\",\n channel: \"qqbot\",\n to,\n accountId,\n },\n },\n };\n}\n\n/**\n * 构建提醒 payload 中的 AI prompt\n */\nfunction buildReminderPrompt(content: string): string {\n return (\n `你是一个暖心的提醒助手。请用温暖、有趣的方式提醒用户:${content}。` +\n `要求:(1) 不要回复HEARTBEAT_OK (2) 不要解释你是谁 ` +\n `(3) 直接输出一条暖心的提醒消息 (4) 可以加一句简短的鸡汤或关怀的话 ` +\n `(5) 控制在2-3句话以内 (6) 用emoji点缀`\n );\n}\n\n/**\n * 格式化延迟时间为人类可读文本\n */\nfunction formatDelay(ms: number): string {\n const totalSeconds = Math.round(ms / 1000);\n if (totalSeconds < 60) return `${totalSeconds}秒`;\n const totalMinutes = Math.round(ms / 60_000);\n if (totalMinutes < 60) return `${totalMinutes}分钟`;\n const hours = Math.floor(totalMinutes / 60);\n const minutes = totalMinutes % 60;\n if (minutes === 0) return `${hours}小时`;\n return `${hours}小时${minutes}分钟`;\n}\n\n// ========== 注册入口 ==========\n\nexport function registerRemindTool(api: OpenClawPluginApi): void {\n api.registerTool(\n {\n name: \"qqbot_remind\",\n label: \"QQBot 定时提醒\",\n description:\n \"创建、查询、删除 QQ 定时提醒。\" +\n \"使用简单参数即可,无需手动构造 cron JSON。\\n\" +\n \"创建提醒:action=add, content=提醒内容, to=目标地址, time=时间\\n\" +\n \"查看提醒:action=list\\n\" +\n \"删除提醒:action=remove, jobId=任务ID(先 list 获取)\\n\" +\n \"时间格式示例:\\\"5m\\\"(5分钟后) \\\"1h\\\"(1小时后) \\\"0 8 * * *\\\"(每天8点)\",\n parameters: RemindSchema,\n execute(_toolCallId, params) {\n const p = params as RemindParams;\n\n // ===== list =====\n if (p.action === \"list\") {\n return json({\n _instruction: \"请立即使用 cron 工具执行以下参数\",\n cronParams: { action: \"list\" },\n });\n }\n\n // ===== remove =====\n if (p.action === \"remove\") {\n if (!p.jobId) {\n return json({ error: \"action=remove 时 jobId 为必填参数。请先用 action=list 获取 jobId。\" });\n }\n return json({\n _instruction: \"请立即使用 cron 工具执行以下参数\",\n cronParams: { action: \"remove\", jobId: p.jobId },\n });\n }\n\n // ===== add =====\n if (!p.content) {\n return json({ error: \"action=add 时 content(提醒内容)为必填参数\" });\n }\n // 优先使用 AI 传入的 to,否则自动从请求级上下文获取(AsyncLocalStorage)\n const resolvedTo = p.to || getRequestTarget();\n if (!resolvedTo) {\n return json({ error: \"action=add 时无法确定投递目标地址。请稍后重试。\" });\n }\n // 从请求上下文获取当前账户 ID(多账户场景),fallback 到 \"default\"\n const resolvedAccountId = getRequestAccountId() || \"default\";\n if (!p.time) {\n return json({ error: \"action=add 时 time(时间)为必填参数。示例:\\\"5m\\\"、\\\"1h30m\\\"、\\\"0 8 * * *\\\"\" });\n }\n\n // 判断是 cron 表达式还是相对时间\n if (isCronExpression(p.time)) {\n // 周期提醒\n const cronJob = buildCronJob(p, resolvedTo, resolvedAccountId);\n return json({\n _instruction: \"请立即使用 cron 工具,将 cronParams 的值原样作为参数传入(不要修改或省略任何字段,特别是 delivery.accountId),然后回复用户提醒已设置\",\n cronParams: cronJob,\n summary: `⏰ 周期提醒: \"${p.content}\" (${p.time}, tz=${p.timezone || \"Asia/Shanghai\"})`,\n });\n }\n\n // 一次性提醒\n const delayMs = parseRelativeTime(p.time);\n if (!delayMs || delayMs <= 0) {\n return json({\n error: `无法解析时间 \"${p.time}\"。支持格式:` +\n `相对时间如 \"5m\"、\"1h\"、\"1h30m\"、\"2d\";` +\n `cron 表达式如 \"0 8 * * *\"(每天8点)`,\n });\n }\n\n if (delayMs < 30_000) {\n return json({ error: \"提醒时间不能少于 30 秒\" });\n }\n\n const onceJob = buildOnceJob(p, delayMs, resolvedTo, resolvedAccountId);\n return json({\n _instruction: \"请立即使用 cron 工具,将 cronParams 的值原样作为参数传入(不要修改或省略任何字段,特别是 delivery.accountId),然后回复用户提醒已设置\",\n cronParams: onceJob,\n summary: `⏰ ${formatDelay(delayMs)}后提醒: \"${p.content}\"`,\n });\n },\n },\n { name: \"qqbot_remind\" },\n );\n\n}\n\n","/**\n * Runtime Contract Check — 启动时校验必需 API 可用性。\n *\n * 在 plugin.register() 中立刻调用,console 直接输出(register 阶段\n * runtime.logging 的 getChildLogger 存在但输出管道未接通)。\n */\n\nimport type { PluginRuntime } from 'openclaw/plugin-sdk';\nimport { createPluginLogger } from '../utils/plugin-logger.js';\n\nconst log = createPluginLogger({ prefix: '[contract]', forceConsole: false });\n\ninterface ApiProbe {\n name: string;\n probe: (rt: PluginRuntime) => boolean;\n}\n\nconst REQUIRED: ApiProbe[] = [\n {\n name: 'channel.reply.dispatchReplyWithBufferedBlockDispatcher',\n probe: (rt) => typeof (rt as any).channel?.reply?.dispatchReplyWithBufferedBlockDispatcher === 'function',\n },\n];\n\nconst OPTIONAL: ApiProbe[] = [\n { name: 'channel.inbound.run (degraded)', probe: (rt) => {\n const c = (rt as any).channel;\n return typeof c?.inbound?.run === 'function' || typeof c?.turn?.run === 'function';\n }},\n { name: 'channel.inbound.buildContext', probe: (rt) => typeof (rt as any).channel?.inbound?.buildContext === 'function' },\n { name: 'channel.reply.formatAgentEnvelope', probe: (rt) => typeof (rt as any).channel?.reply?.formatAgentEnvelope === 'function' },\n { name: 'channel.text.chunkMarkdownText', probe: (rt) => typeof (rt as any).channel?.text?.chunkMarkdownText === 'function' },\n { name: 'channel.routing.resolveAgentRoute', probe: (rt) => typeof (rt as any).channel?.routing?.resolveAgentRoute === 'function' },\n { name: 'channel.session.resolveStorePath (deprecated)', probe: (rt) => typeof (rt as any).channel?.session?.resolveStorePath === 'function' },\n { name: 'channel.session.recordInboundSession (deprecated)', probe: (rt) => typeof (rt as any).channel?.session?.recordInboundSession === 'function' },\n { name: 'channel.reply.finalizeInboundContext (deprecated)', probe: (rt) => typeof (rt as any).channel?.reply?.finalizeInboundContext === 'function' },\n { name: 'channel.reply.formatInboundEnvelope (deprecated)', probe: (rt) => typeof (rt as any).channel?.reply?.formatInboundEnvelope === 'function' },\n { name: 'config.current', probe: (rt) => typeof (rt as any).config?.current === 'function' },\n];\n\nexport interface ContractResult {\n ok: boolean;\n version: string;\n missing: string[];\n degraded: string[];\n}\n\nexport function verifyRuntimeContract(rt: PluginRuntime): ContractResult {\n const version = (rt as any).version ?? 'unknown';\n const missing: string[] = [];\n const degraded: string[] = [];\n\n for (const r of REQUIRED) {\n if (!r.probe(rt)) missing.push(r.name);\n }\n for (const r of OPTIONAL) {\n if (!r.probe(rt)) degraded.push(r.name);\n }\n\n log.debug(`openclaw=${version} required=${REQUIRED.length - missing.length}/${REQUIRED.length} degraded=${degraded.length}/${OPTIONAL.length}`);\n if (missing.length) {\n log.error(`BROKEN — missing: ${missing.join(', ')}`);\n }\n if (degraded.length) {\n log.debug(`degraded: ${degraded.join(', ')}`);\n }\n\n return { ok: missing.length === 0, version, missing, degraded };\n}\n","// ── QQ 消息类型常量(message_type 枚举值) ──\n/** 普通文本消息 */\nexport const MSG_TYPE_TEXT = 0;\n/** 引用(回复)消息 */\nexport const MSG_TYPE_QUOTE = 103;\n\n/**\n * QQ Bot 配置类型\n */\nexport interface QQBotConfig {\n appId: string;\n clientSecret?: string;\n clientSecretFile?: string;\n}\n\n/**\n * 解析后的 QQ Bot 账户\n */\nexport interface ResolvedQQBotAccount {\n accountId: string;\n name?: string;\n enabled: boolean;\n appId: string;\n clientSecret: string;\n secretSource: \"config\" | \"file\" | \"env\" | \"none\";\n /** 系统提示词 */\n systemPrompt?: string;\n /** 是否支持 markdown 消息(默认 true) */\n markdownSupport: boolean;\n /** User-Agent 尾部追加内容 */\n userAgentSuffix: string;\n /**\n * 单条消息最大处理时间(ms)。超时后 concurrencyGuard 会 abort 处理链,\n * 释放锁并排空缓冲消息。0 表示不限制。默认 0(不限制)。\n */\n processingTimeoutMs: number;\n config: QQBotAccountConfig;\n}\n\n/** 群消息策略:open=全响应 | allowlist=白名单 | disabled=不响应 */\nexport type GroupPolicy = \"open\" | \"allowlist\" | \"disabled\";\n\n/** 工具策略:full=全部 | restricted=限制敏感工具 | none=禁止 */\nexport type ToolPolicy = \"full\" | \"restricted\" | \"none\";\n\n/** 单个群的配置 */\nexport interface GroupConfig {\n /** 是否需要 @机器人才响应(默认 true) */\n requireMention?: boolean;\n /**\n * 是否忽略 @了其他用户但没有 @机器人的消息(默认 false)。\n * 开启后,消息中 @了其他人但未 @bot 时直接丢弃(不记录历史、不触发 AI)。\n */\n ignoreOtherMentions?: boolean;\n /** 群聊中 AI 可使用的工具范围(默认 restricted) */\n toolPolicy?: ToolPolicy;\n /** 群名称 */\n name?: string;\n /** 群消息行为 PE(未配置时使用内置默认值) */\n prompt?: string;\n /** 群历史消息缓存条数(0 禁用,默认 20) */\n historyLimit?: number;\n}\n\n/** 消息接收传输方式 */\nexport type TransportMode = \"websocket\" | \"webhook\";\n\n/** Webhook 传输配置 */\nexport interface WebhookTransportConfig {\n /** 监听路径(默认 /qqbot/webhook) */\n path?: string;\n}\n\n/**\n * QQ Bot 账户配置\n */\nexport interface QQBotAccountConfig {\n enabled?: boolean;\n name?: string;\n appId?: string;\n clientSecret?: string;\n clientSecretFile?: string;\n dmPolicy?: \"open\" | \"pairing\" | \"allowlist\" | \"disabled\";\n allowFrom?: string[];\n /** 消息接收传输方式:websocket(默认)| webhook */\n transport?: TransportMode;\n /** webhook 传输配置(transport=\"webhook\" 时生效) */\n webhook?: WebhookTransportConfig;\n /** 群消息策略(默认 allowlist) */\n groupPolicy?: GroupPolicy;\n /** 群白名单(groupPolicy 为 allowlist 时生效) */\n groupAllowFrom?: string[];\n /** 群配置映射(按 groupOpenid 索引,\"*\" 为默认) */\n groups?: Record<string, GroupConfig>;\n /** 系统提示词,会添加在用户消息前面 */\n systemPrompt?: string;\n /** 是否支持 markdown 消息(默认 true,设为 false 可禁用) */\n markdownSupport?: boolean;\n /**\n * @deprecated 请使用 audioFormatPolicy.uploadDirectFormats\n * 可直接上传的音频格式(不转换为 SILK),向后兼容\n */\n voiceDirectUploadFormats?: string[];\n /**\n * 音频格式策略配置\n * 统一管理入站(STT)和出站(上传)的音频格式转换行为\n */\n audioFormatPolicy?: AudioFormatPolicy;\n /**\n * 是否启用公网 URL 直传 QQ 平台(默认 true)\n * 启用时:公网 URL 先直传给 QQ 开放平台的富媒体 API,平台自行拉取;失败后自动 fallback 到插件下载再 Base64 上传\n * 禁用时:公网 URL 始终由插件先下载到本地,再以 Base64 上传(适用于 QQ 平台无法访问目标 URL 的场景)\n */\n urlDirectUpload?: boolean;\n /**\n * /bot-upgrade 指令返回的升级指引网址\n * 默认: https://doc.weixin.qq.com/doc/w3_AKEAGQaeACgCNHrh1CbHzTAKtT2gB?scode=AJEAIQdfAAozxFEnLZAKEAGQaeACg\n */\n upgradeUrl?: string;\n /**\n * /bot-upgrade 指令的行为模式\n * - \"doc\":展示升级文档链接(安全模式)\n * - \"hot-reload\":检测到新版本时直接执行 npm 升级脚本进行热更新(默认)\n */\n upgradeMode?: \"doc\" | \"hot-reload\";\n /**\n * /bot-upgrade 热更新时使用的 npm 包名\n * 支持 \"scope/name\"(自动补 @)或 \"@scope/name\" 格式\n * 默认: \"@tencent-connect/openclaw-qqbot\"\n * 示例: \"ryantest/openclaw-qqbot\"\n */\n upgradePkg?: string;\n /**\n * 群消息是否默认需要 @机器人才响应(默认 true)\n * 优先级低于 groups.{groupId}.requireMention 和 groups.\"*\".requireMention\n * 设为 false 时,所有群默认无需 @ 即触发回复(仍可被群级配置覆盖)\n */\n defaultRequireMention?: boolean;\n /**\n * 出站消息合并回复(debounce)配置\n * 当短时间内收到多次 deliver 时,将文本合并为一条消息发送,避免消息轰炸\n */\n deliverDebounce?: DeliverDebounceConfig;\n /**\n * 是否启用流式消息(默认 false)\n * 启用后,AI 的回复会以流式形式逐步显示在 QQ 聊天中,\n * 用户可以看到文字逐字出现的打字机效果。\n *\n * 兼容布尔值和对象格式,对齐框架 schema:\n * - true / false 旧版布尔格式(自动转换为对象)\n * - { mode: \"partial\" } 开启(对齐 StreamingMode.partial)\n * - { mode: \"off\" } 关闭\n *\n * 注意:仅 C2C(私聊)支持流式消息 API。\n */\n streaming?: boolean | { mode: 'partial' | 'off' };\n /**\n * STT (语音转文字) 配置\n * 配置后,收到语音消息时会自动调用 STT 服务转录为文字\n */\n stt?: STTChannelConfig;\n /**\n * 单条消息最大处理时间(毫秒)。\n * 超时后 concurrencyGuard 会 abort 处理链(取消 LLM 调用、工具执行等),\n * 释放并发锁并排空缓冲消息。\n *\n * 设为 0 表示不限制超时。默认 0(不限制)。\n *\n * 可通过环境变量 OPENCLAW_PROCESSING_TIMEOUT_MS 覆盖全局默认值,\n * 账户级配置优先级高于环境变量。\n */\n processingTimeoutMs?: number;\n /**\n * User-Agent 尾部追加内容(用于私有化部署标识等场景)\n * 追加在 `QQBotPlugin/{version} (Node/{nodeVersion}; {os}; OpenClaw/{version})` 之后\n */\n userAgentSuffix?: string;\n}\n\n/**\n * 出站消息合并回复配置\n */\nexport interface DeliverDebounceConfig {\n /**\n * 是否启用合并回复(默认 true)\n */\n enabled?: boolean;\n /**\n * 合并窗口时长(毫秒),在此时间内的连续 deliver 会被合并\n * 默认 1500ms\n */\n windowMs?: number;\n /**\n * 最大等待时长(毫秒),从第一条 deliver 开始计算,超过此时间强制发送\n * 防止持续有新 deliver 导致一直不发送\n * 默认 8000ms\n */\n maxWaitMs?: number;\n /**\n * 合并文本之间的分隔符\n * 默认 \"\\n\\n---\\n\\n\"\n */\n separator?: string;\n}\n\n/**\n * 音频格式策略:控制哪些格式可跳过转换\n */\nexport interface AudioFormatPolicy {\n /**\n * STT 模型直接支持的音频格式(入站:跳过 SILK→WAV 转换)\n * 如果 STT 服务支持直接处理某些格式(如 silk/amr),可将其加入此列表\n * 例如: [\".silk\", \".amr\", \".wav\", \".mp3\", \".ogg\"]\n * 默认为空(所有语音都先转换为 WAV 再送 STT)\n */\n sttDirectFormats?: string[];\n /**\n * QQ 平台支持直传的音频格式(出站:跳过→SILK 转换)\n * 默认为 [\".wav\", \".mp3\", \".silk\"](QQ Bot API 原生支持的三种格式)\n * 仅当需要覆盖默认值时才配置此项\n */\n uploadDirectFormats?: string[];\n /**\n * 是否启用语音转码(默认 true)\n * 设为 false 可在环境无 ffmpeg 时跳过转码,直接以文件形式发送\n * 当禁用时,非原生格式的音频会 fallback 到 sendDocument(文件发送)\n */\n transcodeEnabled?: boolean;\n}\n\n/**\n * STT (语音转文字) 配置\n */\nexport interface STTChannelConfig {\n /** 是否启用 STT(默认 true,配置了 baseUrl+apiKey 即自动启用) */\n enabled?: boolean;\n /** STT 服务提供商 ID(对应 models.providers 中的 key,默认 \"openai\") */\n provider?: string;\n /** STT API 地址(如 https://api.openai.com/v1) */\n baseUrl?: string;\n /** STT API 密钥 */\n apiKey?: string;\n /** STT 模型名称(默认 \"whisper-1\") */\n model?: string;\n}\n\n/**\n * 富媒体附件\n */\nexport interface MessageAttachment {\n content_type: string; // 如 \"image/png\"\n filename?: string;\n height?: number;\n width?: number;\n size?: number;\n url: string;\n voice_wav_url?: string; // QQ 提供的 WAV 格式语音直链,有值时优先使用以避免 SILK→WAV 转换\n asr_refer_text?: string; // QQ 事件内置 ASR 语音识别文本\n}\n\n/**\n * C2C 消息事件\n */\nexport interface C2CMessageEvent {\n author: {\n id: string;\n union_openid: string;\n user_openid: string;\n };\n content: string;\n id: string;\n timestamp: string;\n message_scene?: {\n source: string;\n /** ext 数组,可能包含 ref_msg_idx=REFIDX_xxx(引用的消息)和 msg_idx=REFIDX_xxx(自身索引) */\n ext?: string[];\n };\n attachments?: MessageAttachment[];\n /** 消息类型,参见 MSG_TYPE_* */\n message_type?: number;\n /** 消息元素列表,引用消息时 [0] 为被引用的原始消息 */\n msg_elements?: MsgElement[];\n}\n\n/**\n * 频道 AT 消息事件\n */\nexport interface GuildMessageEvent {\n id: string;\n channel_id: string;\n guild_id: string;\n content: string;\n timestamp: string;\n author: {\n id: string;\n username?: string;\n bot?: boolean;\n };\n member?: {\n nick?: string;\n joined_at?: string;\n };\n attachments?: MessageAttachment[];\n}\n\n/** 消息元素结点,引用消息时 msg_elements[0] 为被引用的原始消息 */\nexport interface MsgElement {\n /** 消息索引标识 */\n msg_idx?: string;\n /** 消息类型,参见 MSG_TYPE_* 常量 */\n message_type?: number;\n /** 文本内容 */\n content?: string;\n /** 附件列表 */\n attachments?: MessageAttachment[];\n /** 嵌套消息元素(引用消息场景下可能存在) */\n msg_elements?: MsgElement[];\n}\n\n/**\n * 群聊 AT 消息事件\n */\nexport interface GroupMessageEvent {\n author: {\n id: string;\n member_openid: string;\n username?: string;\n bot?: boolean;\n };\n content: string;\n id: string;\n timestamp: string;\n group_id: string;\n group_openid: string;\n message_scene?: {\n source: string;\n ext?: string[];\n };\n attachments?: MessageAttachment[];\n /** @提及列表 */\n mentions?: Array<{\n scope?: \"all\" | \"single\";\n id?: string;\n user_openid?: string;\n member_openid?: string;\n nickname?: string;\n bot?: boolean;\n /** 是否 @机器人自身 */\n is_you?: boolean;\n }>;\n /** 消息类型,参见 MSG_TYPE_* */\n message_type?: number;\n /** 消息元素列表,引用消息时 [0] 为被引用的原始消息 */\n msg_elements?: MsgElement[];\n}\n\n/**\n * 按钮交互事件(INTERACTION_CREATE)\n */\nexport interface InteractionEvent {\n /** 事件 ID,用于回应交互(PUT /interactions/{id}) */\n id: string;\n /** 事件类型:11=消息按钮 12=单聊快捷菜单 */\n type: number;\n /** 场景:c2c / group / guild */\n scene?: string;\n /** 场景类型:0=频道 1=群聊 2=单聊 */\n chat_type?: number;\n /** 触发时间 RFC3339 */\n timestamp?: string;\n /** 频道 openid(仅频道场景) */\n guild_id?: string;\n /** 子频道 openid(仅频道场景) */\n channel_id?: string;\n /** 单聊用户 openid(仅 c2c 场景) */\n user_openid?: string;\n /** 群 openid(仅群聊场景) */\n group_openid?: string;\n /** 群内触发用户 openid(仅群聊场景) */\n group_member_openid?: string;\n version: number;\n data: {\n type: number;\n resolved: {\n /** 按钮 action.data 值 */\n button_data?: string;\n /** 按钮 id */\n button_id?: string;\n /** 操作用户 userid(仅频道场景) */\n user_id?: string;\n /** 自定义菜单 id(仅菜单场景) */\n feature_id?: string;\n /** 操作的消息 id(仅频道场景) */\n message_id?: string;\n /** 配置更新:群消息模式 \"mention\"=@机器人时激活 \"always\"=总是激活 */\n require_mention?: string;\n /** 配置更新:群消息策略 */\n group_policy?: GroupPolicy;\n /** 配置更新:@文本的名称提及BOT名,多个使用,分隔 */\n mention_patterns?: string;\n };\n };\n}\n\n// ---- Keyboard 类型 ----\n\n/**\n * 按钮 Action 类型\n * 0=跳转链接 1=回调型(INTERACTION_CREATE) 2=指令型(直接发文本) 3=mqqapi\n */\nexport type KeyboardActionType = 0 | 1 | 2 | 3;\n\n/** 按钮权限 */\nexport interface KeyboardPermission {\n /** 0=全体 1=管理员 2=按钮指定 3=身份组 */\n type: 0 | 1 | 2 | 3;\n specify_role_ids?: string[];\n specify_user_ids?: string[];\n}\n\n/** 二次确认弹窗 */\nexport interface KeyboardModal {\n content: string;\n confirm_text?: string;\n cancel_text?: string;\n}\n\n/** 按钮 Action */\nexport interface KeyboardAction {\n type: KeyboardActionType;\n data?: string;\n /** true = 点击后直接发出(Enter)*/\n enter?: boolean;\n /** 仅指令型(type=2):是否把指令发到输入框(reply=true)还是静默发出 */\n reply?: boolean;\n permission?: KeyboardPermission;\n click_limit?: number;\n unsupport_tips?: string;\n modal?: KeyboardModal;\n}\n\n/** 按钮渲染数据 */\nexport interface KeyboardRenderData {\n label: string;\n visited_label?: string;\n /** 0=灰色线框 1=蓝色线框 2=推荐回复专用 3=红色字体 4=蓝色背景 */\n style?: 0 | 1 | 2 | 3 | 4;\n}\n\n/** 单个按钮 */\nexport interface KeyboardButton {\n id: string;\n render_data?: KeyboardRenderData;\n action?: KeyboardAction;\n group_id?: string;\n}\n\n/** 一行按钮 */\nexport interface KeyboardRow {\n buttons: KeyboardButton[];\n}\n\n/** CustomKeyboard(自定义按钮内容) */\nexport interface CustomKeyboard {\n rows: KeyboardRow[];\n}\n\n/** MessageKeyboard(keyboard / prompt_keyboard.keyboard 共用) */\nexport interface MessageKeyboard {\n /** 模板 ID(与 content 二选一) */\n id?: string;\n /** 自定义内容 */\n content?: CustomKeyboard;\n}\n\n/**\n * Inline Keyboard(消息内嵌按钮,需平台审核)\n * 发送字段:keyboard\n * JSON: { \"keyboard\": { \"id\": \"...\", \"content\": { \"rows\": [...] } } }\n */\nexport type InlineKeyboard = MessageKeyboard;\n\n/**\n * WebSocket 事件负载\n */\nexport interface WSPayload {\n op: number;\n d?: unknown;\n s?: number;\n t?: string;\n}\n\n\n\n// ---- 流式消息常量 ----\n\n/** 流式消息输入模式 */\nexport const StreamInputMode = {\n /** 每次发送的 content_raw 替换整条消息内容 */\n REPLACE: \"replace\",\n} as const;\nexport type StreamInputMode = (typeof StreamInputMode)[keyof typeof StreamInputMode];\n\n/** 流式消息输入状态 */\nexport const StreamInputState = {\n /** 正文生成中 */\n GENERATING: 1,\n /** 正文生成结束(终结状态) */\n DONE: 10,\n} as const;\nexport type StreamInputState = (typeof StreamInputState)[keyof typeof StreamInputState];\n\n/** 流式消息内容类型 */\nexport const StreamContentType = {\n MARKDOWN: \"markdown\",\n} as const;\nexport type StreamContentType = (typeof StreamContentType)[keyof typeof StreamContentType];\n\n/**\n * 流式消息请求体\n * 对应 StreamReq proto\n */\nexport interface StreamMessageRequest {\n /** 输入模式 */\n input_mode: StreamInputMode;\n /** 输入状态 */\n input_state: StreamInputState;\n /** 内容类型 */\n content_type: StreamContentType;\n /** markdown 内容 */\n content_raw: string;\n /** 事件 ID */\n event_id: string;\n /** 原始消息 ID */\n msg_id: string;\n /** 流式消息 ID,首次发送后返回,后续分片需携带 */\n stream_msg_id?: string;\n /** 递增序号 */\n msg_seq: number;\n /** 同一条流式会话内的发送索引,从 0 开始,每次发送前递增;新流式会话重新从 0 开始 */\n index: number;\n}\n\n\n"],"mappings":"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","names":["exports","module","exports","module","exports","module","exports","module","PerMessageDeflate","data","exports","module","exports","module","PerMessageDeflate","Receiver","exports","module","PerMessageDeflate","Sender","_","exports","module","exports","module","extension","exports","module","https","http","net","randomBytes","createHash","URL","PerMessageDeflate","Receiver","Sender","WebSocket","request","req","key","exports","module","WebSocket","createWebSocketStream","err","exports","module","protocol","exports","module","http","createHash","extension","PerMessageDeflate","subprotocol","WebSocket","WebSocketServer","req","h","path","log","runtime","homedir","join","writeFileSync","req","import_node_module","DEFAULT_ACCOUNT_ID","exports","module","exports","module","exports","module","exports","module","exports","module","exports","module","d","exports","module","exports","module","exports","module","exports","module","y","h","exports","module","row","m","_","p","E","d","l","u","m","m","DEFAULT_ACCOUNT_ID","MSG_TYPE_QUOTE","StreamContentType","StreamInputMode","StreamInputState","path","fs","path","path","MediaFileType","path","resolve","path","resolve","req","sleep","fs","path","path","req","resolve","WebSocket","d","log","resolve","log","d","crypto","path","resolve","req","path","req","resolve","d","crypto","req","path","p","parseFaceTags","w","resolve","decision","m","h","m","p","m","path","fs","l","result","import_node_path","import_node_fs","path","fs","os","import_node_fs","import_node_path","os","path","fs","C","m","resolve","path","fs","l","b","path","fs","os","net","dns","path","fs","os","p","p","sendMedia","log","resolved","resolveVoiceSource","runtime","resolve","l","applyQQBotAccountConfig","persistAuthConfig","applyAccountDefaults","resolveAccountKey","runtime","result","import_node_os","PLUGIN_VERSION","import_node_https","resolve","req","https","log","json","b","PLUGIN_VERSION","p","runtime","PLUGIN_VERSION","GITHUB_URL","import_node_fs","import_node_path","import_node_os","fs","path","b","os","files","import_node_fs","import_node_path","import_node_os","import_node_crypto","runtime","path","os","p","searchRoots","fs","b","d","crypto","runtime","import_node_module","import_node_path","req","path","fs","p","path","b","import_node_fs","DEFAULT_TTL_MS","import_node_fs","import_node_path","fs","fs","path","import_node_child_process","fs","path","p","fs","path","sanitizeFileName","formatVoiceText","formatDuration","import_node_module","dns","path","fs","crypto","req","runtime","log","formatVoiceText","formatDuration","m","h","d","p","m","buildEnvelope","path","os","p","sendMedia","result","_","p","u","resolve","b","runtime","log","buildEnvelope","sendMedia","u","p","import_node_module","import_node_path","req","path","fs","p","request","m","log","fs","path","log","runtime","log","path","log","req","h","getHeader","_","runtime","log","result","p","os","import_node_fs","import_node_path","path","fs","log","runtime","h","p","TEXT_CHUNK_LIMIT","sendMedia","runtime","log","path","p","json","p","log","MSG_TYPE_QUOTE","StreamInputMode","StreamInputState","StreamContentType"]}