@jerryliang122/openclaw-qqbot 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +967 -0
  3. package/README.zh.md +790 -0
  4. package/dist/index.cjs +18072 -0
  5. package/dist/index.cjs.map +1 -0
  6. package/dist/index.d.cts +1222 -0
  7. package/index.ts +92 -0
  8. package/openclaw.plugin.json +38 -0
  9. package/package.json +69 -0
  10. package/preload.cjs +19 -0
  11. package/scripts/link-sdk-core.cjs +268 -0
  12. package/scripts/proactive-api-server.ts +369 -0
  13. package/scripts/send-proactive.ts +293 -0
  14. package/scripts/test-sendmedia.ts +116 -0
  15. package/skills/qqbot-channel/SKILL.md +285 -0
  16. package/skills/qqbot-channel/references/api_references.md +521 -0
  17. package/skills/qqbot-remind/SKILL.md +159 -0
  18. package/skills/qqbot-upgrade/SKILL.md +56 -0
  19. package/src/adapter/contract.ts +63 -0
  20. package/src/adapter/lint.ts +144 -0
  21. package/src/adapter/media.ts +40 -0
  22. package/src/adapter/pairing.ts +95 -0
  23. package/src/adapter/resolve.ts +255 -0
  24. package/src/adapter/setup.ts +13 -0
  25. package/src/adapter/webhook.ts +248 -0
  26. package/src/adapter/workspace.ts +21 -0
  27. package/src/agent-prompt-adapter.ts +26 -0
  28. package/src/bot-instance.ts +60 -0
  29. package/src/channel.ts +230 -0
  30. package/src/commands/bot-approve.ts +143 -0
  31. package/src/commands/bot-clear-storage.ts +114 -0
  32. package/src/commands/bot-group-always.ts +62 -0
  33. package/src/commands/bot-group-info.ts +48 -0
  34. package/src/commands/bot-help.ts +40 -0
  35. package/src/commands/bot-logs.ts +248 -0
  36. package/src/commands/bot-me.ts +18 -0
  37. package/src/commands/bot-pairing.ts +50 -0
  38. package/src/commands/bot-ping.ts +33 -0
  39. package/src/commands/bot-streaming.ts +55 -0
  40. package/src/commands/bot-upgrade.ts +56 -0
  41. package/src/commands/bot-version.ts +41 -0
  42. package/src/commands/config-util.ts +96 -0
  43. package/src/commands/index.ts +51 -0
  44. package/src/config.ts +403 -0
  45. package/src/constants.ts +6 -0
  46. package/src/dispatch/body-assembler.ts +308 -0
  47. package/src/dispatch/ctx-builder.ts +127 -0
  48. package/src/dispatch/dispatch.ts +667 -0
  49. package/src/dispatch/envelope-builder.ts +112 -0
  50. package/src/dispatch/index.ts +2 -0
  51. package/src/features/approval-capability.ts +302 -0
  52. package/src/features/approval-helpers.ts +271 -0
  53. package/src/features/approval-utils.ts +21 -0
  54. package/src/features/command-panel.ts +301 -0
  55. package/src/features/credential-backup.ts +74 -0
  56. package/src/features/group-mode-store.ts +79 -0
  57. package/src/features/history-store.ts +75 -0
  58. package/src/features/msgid-cache.ts +55 -0
  59. package/src/features/outbound-echo-store.ts +46 -0
  60. package/src/features/proactive-budget.ts +57 -0
  61. package/src/features/proactive.ts +549 -0
  62. package/src/features/question-helpers.ts +771 -0
  63. package/src/features/quota-manager.ts +173 -0
  64. package/src/features/ref-index-store.ts +289 -0
  65. package/src/features/secret-input-store.ts +118 -0
  66. package/src/features/secret-store-cli.ts +324 -0
  67. package/src/features/typing-refresh.ts +51 -0
  68. package/src/features/update-checker.ts +166 -0
  69. package/src/gateway/event-handlers.ts +456 -0
  70. package/src/gateway/index.ts +3 -0
  71. package/src/gateway/lifecycle.ts +236 -0
  72. package/src/gateway/middleware-setup.ts +173 -0
  73. package/src/gateway/qqbot-gateway.ts +458 -0
  74. package/src/gateway-adapter.ts +44 -0
  75. package/src/heartbeat-adapter.ts +57 -0
  76. package/src/message-adapter.ts +40 -0
  77. package/src/messaging-adapter.ts +78 -0
  78. package/src/middleware/access-control.ts +125 -0
  79. package/src/middleware/attachment.ts +373 -0
  80. package/src/middleware/inbound-guard.ts +102 -0
  81. package/src/middleware/policy-injector.ts +71 -0
  82. package/src/middleware/secret-capture.ts +161 -0
  83. package/src/middleware/typing.ts +110 -0
  84. package/src/openclaw-plugin-sdk.d.ts +543 -0
  85. package/src/outbound/chunker.ts +80 -0
  86. package/src/outbound/debounce.ts +102 -0
  87. package/src/outbound/deliver-pipeline.ts +235 -0
  88. package/src/outbound/index.ts +3 -0
  89. package/src/outbound/local-file-router.ts +145 -0
  90. package/src/outbound/media-send.ts +408 -0
  91. package/src/outbound/outbound-service.ts +298 -0
  92. package/src/outbound/reply-limiter.ts +139 -0
  93. package/src/outbound/sanitize.ts +32 -0
  94. package/src/outbound/streaming-controller.ts +332 -0
  95. package/src/outbound/target.ts +109 -0
  96. package/src/outbound-adapter.ts +323 -0
  97. package/src/plugin-base.ts +42 -0
  98. package/src/request-context.ts +50 -0
  99. package/src/runtime.ts +42 -0
  100. package/src/setup/account-key.ts +41 -0
  101. package/src/setup/finalize.ts +110 -0
  102. package/src/setup/login.ts +197 -0
  103. package/src/setup/surface.ts +40 -0
  104. package/src/status-adapter.ts +56 -0
  105. package/src/tools/platform.ts +149 -0
  106. package/src/tools/remind.ts +308 -0
  107. package/src/tools/secret-input.ts +185 -0
  108. package/src/types-augment.d.ts +54 -0
  109. package/src/types-plugin.ts +82 -0
  110. package/src/types.ts +620 -0
  111. package/src/typing-lifecycle.ts +182 -0
  112. package/src/utils/mention.ts +52 -0
  113. package/src/utils/pkg-version.ts +23 -0
  114. package/src/utils/platform.ts +459 -0
  115. package/src/utils/plugin-logger.ts +104 -0
  116. package/src/utils/ssrf-guard.ts +132 -0
  117. package/src/utils/stt.ts +150 -0
  118. package/src/utils/voice-text.ts +61 -0
  119. package/tsconfig.json +17 -0
  120. package/tsup.config.ts +64 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts","../src/utils/platform.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","../src/adapter/resolve.ts","../src/features/quota-manager.ts","../src/outbound/target.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/retry.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/api/routes.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/api/media-chunked.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/utils/file-utils.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/api/media.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/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","../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/QQBot.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/access-policy.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/middleware/index.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/storage/kv-store.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/storage/session-adapter.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/storage/index.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/index.ts","../src/outbound/outbound-service.ts","../src/utils/pkg-version.ts","../src/bot-instance.ts","../src/request-context.ts","../src/utils/plugin-logger.ts","../src/features/ref-index-store.ts","../src/runtime.ts","../src/adapter/workspace.ts","../src/utils/ssrf-guard.ts","../src/outbound/local-file-router.ts","../src/outbound/media-send.ts","../index.ts","../src/channel.ts","../src/features/credential-backup.ts","../src/setup/surface.ts","../src/setup/login.ts","../src/plugin-base.ts","../src/outbound-adapter.ts","../src/features/msgid-cache.ts","../src/outbound/sanitize.ts","../src/outbound/chunker.ts","../src/features/approval-utils.ts","../src/constants.ts","../src/message-adapter.ts","../src/messaging-adapter.ts","../src/status-adapter.ts","../src/gateway/lifecycle.ts","../src/gateway/qqbot-gateway.ts","../src/gateway/middleware-setup.ts","../src/commands/bot-help.ts","../src/commands/bot-ping.ts","../src/commands/bot-version.ts","../src/features/update-checker.ts","../src/commands/bot-me.ts","../src/commands/bot-upgrade.ts","../src/commands/config-util.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/commands/bot-group-info.ts","../src/features/group-mode-store.ts","../src/features/proactive-budget.ts","../src/adapter/pairing.ts","../src/features/secret-store-cli.ts","../src/commands/bot-pairing.ts","../src/commands/index.ts","../src/middleware/attachment.ts","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/index.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","../node_modules/@tencent-connect/qqbot-nodejs/src/protocol/transport/index.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/features/outbound-echo-store.ts","../src/middleware/inbound-guard.ts","../src/features/question-helpers.ts","../src/middleware/secret-capture.ts","../src/features/secret-input-store.ts","../src/middleware/typing.ts","../src/features/typing-refresh.ts","../src/utils/mention.ts","../src/dispatch/envelope-builder.ts","../src/dispatch/dispatch.ts","../src/outbound/deliver-pipeline.ts","../src/dispatch/ctx-builder.ts","../src/outbound/debounce.ts","../src/outbound/streaming-controller.ts","../src/gateway/event-handlers.ts","../src/features/approval-capability.ts","../src/features/approval-helpers.ts","../src/features/proactive.ts","../src/adapter/webhook.ts","../src/features/command-panel.ts","../src/typing-lifecycle.ts","../src/gateway-adapter.ts","../src/heartbeat-adapter.ts","../src/agent-prompt-adapter.ts","../src/tools/platform.ts","../src/tools/remind.ts","../src/tools/secret-input.ts","../src/adapter/contract.ts","../src/types.ts","../src/outbound/reply-limiter.ts"],"sourcesContent":["import type { ResolvedQQBotAccount, QQBotAccountConfig, ToolPolicy, GroupConfig, GroupCoalesceConfig } 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/** 群消息合并默认配置 */\nconst DEFAULT_GROUP_COALESCE_CONFIG: Required<GroupCoalesceConfig> = {\n enabled: true,\n};\n\nconst DEFAULT_GROUP_CONFIG: Omit<Required<GroupConfig>, \"prompt\" | \"coalesce\" | \"historyMode\" | \"unmentionedInbound\"> & { coalesce: Required<GroupCoalesceConfig>; historyMode: 'clear' | 'rolling'; unmentionedInbound: 'user_request' | 'room_event' } = {\n requireMention: true,\n ignoreOtherMentions: false,\n toolPolicy: \"restricted\",\n name: \"\",\n historyLimit: DEFAULT_GROUP_HISTORY_LIMIT,\n historyMode: \"clear\",\n unmentionedInbound: \"user_request\",\n coalesce: DEFAULT_GROUP_COALESCE_CONFIG,\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 = Omit<Required<GroupConfig>, \"prompt\" | \"coalesce\" | \"historyMode\" | \"unmentionedInbound\"> & {\n prompt: string;\n coalesce: Required<GroupCoalesceConfig>;\n historyMode: 'clear' | 'rolling';\n unmentionedInbound: 'user_request' | 'room_event';\n};\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 const accountDefaultCoalesce = account.config?.groupCoalesce ?? DEFAULT_GROUP_COALESCE_CONFIG;\n\n const coalesce = {\n enabled: specificCfg.coalesce?.enabled ?? wildcardCfg.coalesce?.enabled ?? accountDefaultCoalesce.enabled ?? DEFAULT_GROUP_COALESCE_CONFIG.enabled,\n };\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 historyMode: specificCfg.historyMode ?? wildcardCfg.historyMode ?? DEFAULT_GROUP_CONFIG.historyMode,\n unmentionedInbound: specificCfg.unmentionedInbound ?? wildcardCfg.unmentionedInbound ?? DEFAULT_GROUP_CONFIG.unmentionedInbound,\n coalesce,\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\nfunction firstNonEmptyEnv(...values: Array<string | undefined>): string {\n for (const value of values) {\n if (value?.trim()) return value.trim();\n }\n return \"\";\n}\n\n/** 文档变量名优先,同时兼容旧的下划线变量名。 */\nexport function resolveQQBotEnvAppId(): string {\n return firstNonEmptyEnv(process.env.QQBOT_APPID);\n}\n\nexport function resolveQQBotEnvClientSecret(): string {\n return firstNonEmptyEnv(process.env.QQBOT_SECRET);\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 || resolveQQBotEnvAppId()) {\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 || resolveQQBotEnvAppId()) {\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 * 解析 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 (resolvedAccountId === DEFAULT_ACCOUNT_ID && resolveQQBotEnvClientSecret()) {\n clientSecret = resolveQQBotEnvClientSecret();\n secretSource = \"env\";\n }\n\n // AppId 也可以从环境变量读取\n if (!appId && resolvedAccountId === DEFAULT_ACCOUNT_ID) {\n appId = normalizeAppId(resolveQQBotEnvAppId());\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 commandPanelNative: accountConfig.commands?.native !== false,\n userAgentSuffix: resolveUserAgentSuffix(cfg),\n config: accountConfig,\n };\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\n/** 解析群消息合并配置 */\nexport function resolveGroupCoalesceConfig(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): Required<GroupCoalesceConfig> {\n return resolveGroupConfig(cfg, groupOpenid, accountId).coalesce;\n}\n\n/** 解析群消息合并是否启用 */\nexport function resolveGroupCoalesceEnabled(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): boolean {\n return resolveGroupCoalesceConfig(cfg, groupOpenid, accountId).enabled;\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 * Setup 工具 re-export(plugin-sdk/setup + plugin-sdk/setup-tools)\n *\n * 构建基线 openclaw 2026.9.2,两个 subpath 均为正式导出,直接转发。\n */\nexport type { ChannelSetupWizard } from 'openclaw/plugin-sdk/setup';\n\nexport {\n createStandardChannelSetupStatus,\n setSetupChannelEnabled,\n} from 'openclaw/plugin-sdk/setup';\n\nexport { formatDocsLink } from 'openclaw/plugin-sdk/setup-tools';\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 { formatDocsLink } from '../adapter/setup.js';\nimport { qrConnect } from '@tencent-connect/qqbot-connector';\nimport { DEFAULT_ACCOUNT_ID, 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 * Runtime Adapters — 一次 resolve,全程复用。\n *\n * Capability Probe 模式:按候选 API 路径探测,选出第一个可用函数。\n * 构建基线为 openclaw 2026.9.2(peer >=2026.9.2),不再保留对更旧版本的回退。\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 /** 入站事件处理 */\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 /** 构建入站上下文 */\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 */\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 * 接受一个 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'],\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 const rawBuildContext = probeFunction(rt, [\n ['channel', 'inbound', 'buildContext'],\n ]);\n\n const buildInboundContext: RuntimeAdapters['buildInboundContext'] = rawBuildContext\n ? (params) => rawBuildContext(params)\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 const formatEnvelope = probeFunction(rt, [\n ['channel', 'reply', 'formatAgentEnvelope'],\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 ]);\n\n const rawMutateConfig = probeFunction(rt, [['config', 'mutateConfigFile']]);\n\n const persistConfig: RuntimeAdapters['persistConfig'] = rawMutateConfig\n ? async (mutator: (cfg: any) => any) => {\n await rawMutateConfig({\n afterWrite: 'hot-reload',\n mutate: mutator,\n });\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',\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 * 走 config.mutateConfigFile(原子操作,支持 afterWrite 策略)。\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 throw new Error('persistAuthConfig: runtime.config.mutateConfigFile is unavailable (requires openclaw >= 2026.9.2)');\n }\n await (config.mutateConfigFile as Function)({\n mutate: () => cfg,\n afterWrite,\n });\n}\n\n/**\n * 强制清除 adapter 缓存(仅测试用)\n */\nexport function _resetAdaptersCache(): void {\n _cachedAdapters = null;\n _cachedRuntimeRef = null;\n}\n\n","/**\n * QQBot 被动回复配额管理\n *\n * C2C: 4 次/msg_id, 60 分钟\n * Group: 5 次/msg_id, 5 分钟\n *\n * 重要说明:\n * - QQ Bot 平台的 msg_id 有时效性限制(C2C: 60分钟,Group: 5分钟)\n * - 过期后的 msg_id 不能再用于被动回复(API 会返回错误 40034128)\n * - 这是平台限制,不是\"配额恢复\"\n * - 因此 checkPassiveReplyQuota 在 msg_id 过期时返回 false,而非重置配额\n *\n * 使用方式:\n * 1. 推荐:使用 checkAndConsumePassiveReplyQuota 进行原子操作(失败用 rollback 回滚)\n * 2. 纯探测(不消耗配额):checkPassiveReplyQuota\n */\n\nimport type { QuotaState, QuotaCheckParams } from '../types-plugin.js';\n\nconst quotaCache = new Map<string, QuotaState>();\nconst MAX_CACHE_SIZE = 10000;\n\nconst QUOTA_LIMITS = {\n c2c: { count: 4, ttlMs: 60 * 60 * 1000 },\n group: { count: 5, ttlMs: 5 * 60 * 1000 },\n};\n\n/**\n * 检查被动回复配额(纯探测,不消耗配额)。\n * 需要同时消耗配额时使用 checkAndConsumePassiveReplyQuota。\n */\nexport function checkPassiveReplyQuota(params: QuotaCheckParams): boolean {\n const { accountId, msgId, scope } = params;\n\n if (!msgId) {\n return false;\n }\n\n const key = `${accountId}:${scope}:${msgId}`;\n const now = Date.now();\n\n const cached = quotaCache.get(key);\n if (cached) {\n // msg_id 过期后不能再用于被动回复\n // 这是 QQ Bot 平台限制,API 会返回错误 40034128\n if (now > cached.expiresAt) {\n quotaCache.delete(key);\n return false;\n }\n\n const limit = QUOTA_LIMITS[scope].count;\n if (cached.count >= limit) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * 回滚被动回复配额(API 调用失败时使用)\n */\nexport function rollbackPassiveReplyQuota(params: {\n accountId: string;\n msgId: string;\n scope: 'c2c' | 'group';\n}): void {\n const { accountId, msgId, scope } = params;\n const key = `${accountId}:${scope}:${msgId}`;\n\n const cached = quotaCache.get(key);\n if (cached && cached.count > 0) {\n cached.count -= 1;\n quotaCache.set(key, cached);\n }\n}\n\n/**\n * 原子操作:检查并消耗被动回复配额\n * 推荐使用此函数,避免 check-then-consume 竞态\n */\nexport function checkAndConsumePassiveReplyQuota(\n params: QuotaCheckParams & { log?: { debug?: (message: string) => void } },\n): { canReply: boolean; rollback: () => void } {\n const { accountId, msgId, scope, log } = params;\n\n if (!msgId) {\n return { canReply: false, rollback: () => {} };\n }\n\n const key = `${accountId}:${scope}:${msgId}`;\n const now = Date.now();\n const ttl = QUOTA_LIMITS[scope].ttlMs;\n\n let cached = quotaCache.get(key);\n \n // 检查过期:msg_id 过期后不能用于被动回复\n // 这是 QQ Bot 平台限制,API 会返回错误 40034128\n if (cached && now > cached.expiresAt) {\n // msg_id 已过期,不能用于被动回复\n return { canReply: false, rollback: () => {} };\n }\n\n // 检查配额\n const limit = QUOTA_LIMITS[scope].count;\n if (cached && cached.count >= limit) {\n return { canReply: false, rollback: () => {} };\n }\n\n // 原子消耗\n cached = cached || { count: 0, expiresAt: now + ttl };\n cached.count += 1;\n quotaCache.set(key, cached);\n\n // 缓存大小控制\n if (quotaCache.size > MAX_CACHE_SIZE) {\n const oldestKey = quotaCache.keys().next().value;\n if (oldestKey) {\n quotaCache.delete(oldestKey);\n }\n }\n\n log?.debug?.(`[${accountId}] consumed passive quota: ${key} count=${cached.count}`);\n\n // 返回回滚函数\n const rollback = () => {\n const state = quotaCache.get(key);\n if (state && state.count > 0) {\n state.count -= 1;\n quotaCache.set(key, state);\n }\n };\n\n return { canReply: true, rollback };\n}\n\nexport function inferQQBotScope(to: string): 'c2c' | 'group' {\n const parts = to.split(':');\n const scope = parts[1];\n return scope === 'group' ? 'group' : 'c2c';\n}\n\nexport function clearQuotaCache(): void {\n quotaCache.clear();\n}\n\nexport function clearQuotaCacheForAccount(accountId: string): void {\n const prefix = `${accountId}:`;\n for (const key of quotaCache.keys()) {\n if (key.startsWith(prefix)) quotaCache.delete(key);\n }\n}\n\nexport function getQuotaStats(accountId: string, scope: 'c2c' | 'group'): {\n activeSessions: number;\n totalUsage: number;\n} {\n let activeSessions = 0;\n let totalUsage = 0;\n\n for (const [key, state] of quotaCache.entries()) {\n if (key.startsWith(`${accountId}:${scope}:`)) {\n activeSessions += 1;\n totalUsage += state.count;\n }\n }\n\n return { activeSessions, totalUsage };\n}\n\nexport function __test_getQuotaCache(): Map<string, QuotaState> {\n return quotaCache;\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 * \n * 注意:此函数总是返回 ReplyTarget,即使输入无效(targetId 可能为空字符串)\n * 如果需要严格验证,请使用 tryParseTarget\n */\nexport function parseTarget(to: string): ReplyTarget {\n // 空字符串或无效输入返回默认值\n if (!to || typeof to !== 'string') {\n return { scope: 'c2c', targetId: '' };\n }\n\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 // channel 不被 SDK ChatScope 支持,当作 c2c 处理\n if (id.startsWith('channel:')) {\n return { scope: 'c2c', targetId: id.slice(8) };\n }\n\n // 默认当作 c2c(32 位十六进制 / UUID 格式的 openid)\n return { scope: 'c2c', targetId: id };\n}\n\n/**\n * 严格解析目标地址字符串,验证输入有效性\n * @returns ReplyTarget 或 null(如果输入无效)\n */\nexport function tryParseTarget(to: string): ReplyTarget | null {\n // 空字符串或无效输入返回 null\n if (!to || typeof to !== 'string') {\n return null;\n }\n\n const id = to.replace(/^qqbot:/i, '');\n\n // 检查是否是有效的目标格式\n if (id.startsWith('c2c:')) {\n const targetId = id.slice(4);\n if (!targetId) return null; // c2c: 后面必须有内容\n return { scope: 'c2c', targetId };\n }\n if (id.startsWith('group:')) {\n const targetId = id.slice(6);\n if (!targetId) return null; // group: 后面必须有内容\n return { scope: 'group', targetId };\n }\n\n // 默认当作 c2c(32 位十六进制 / UUID 格式的 openid)\n // 但必须匹配有效的 openid 格式\n if (OPENID_HEX_RE.test(id) || OPENID_UUID_RE.test(id)) {\n return { scope: 'c2c', targetId: id };\n }\n\n // 无效格式返回 null\n return null;\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 * 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 * 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 * 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 * 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 * 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","'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 (typeof params.client_max_window_bits === 'number'\n ? opts.clientMaxWindowBits > params.client_max_window_bits\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._numFragments = 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._maxFragments > 0 && ++this._numFragments > this._maxFragments) {\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 if (this._compressed) {\n this._state = INFLATING;\n this.decompress(data, cb);\n return;\n }\n\n if (data.length) {\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 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._numFragments = 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=262144] The maximum number of\n * buffered data chunks\n * @param {Number} [options.maxFragments=16384] 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: 256 * 1024,\n maxFragments: 16 * 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=262144] The maximum number of\n * buffered data chunks\n * @param {Number} [options.maxFragments=16384] 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: 256 * 1024,\n maxFragments: 16 * 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","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 * 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 * 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 * Access policy middleware — gate inbound messages by sender / group allow-list.\n *\n * QQ Bot has no native concept of \"allow-list\"; this is a SDK-side mechanism\n * commonly required by production deployments (allow only specific group ids,\n * specific QQ openids, etc).\n *\n * The middleware is **purely declarative** — it simply evaluates the policy\n * against `ctx.message` and short-circuits the chain when blocked.\n */\n\nimport type { Middleware, MiddlewareContext } from \"./types.js\";\n\n// ============ Policy Types ============\n\n/** Match rules for `allowFrom` / `denyFrom`. */\nexport type AccessMatcher =\n | \"*\" // Match anything\n | string // Exact id (groupId / senderId)\n | RegExp // Regex match\n | ((ctx: MiddlewareContext) => boolean);\n\n/** Policy applied to one chat scope. */\nexport interface ScopePolicy {\n /** \"open\": allow all; \"allowlist\": only allow listed; \"disabled\": block all. */\n mode?: \"open\" | \"allowlist\" | \"disabled\";\n /** Patterns whose match → allow. Used with mode=allowlist. */\n allow?: AccessMatcher[];\n /** Patterns whose match → block. Evaluated before allow. */\n deny?: AccessMatcher[];\n}\n\n/** Full access policy. */\nexport interface AccessPolicy {\n /** Default policy for direct messages (C2C / DM). Defaults to `open`. */\n c2c?: ScopePolicy;\n /** Policy for group messages. Defaults to `open`. */\n group?: ScopePolicy;\n /** Policy for guild channel messages. Defaults to `open`. */\n guild?: ScopePolicy;\n /**\n * Optional callback when a message is blocked. Use for telemetry.\n */\n onBlock?: (ctx: MiddlewareContext, reason: string) => void;\n}\n\n// ============ Helpers ============\n\nfunction matcherMatches(matcher: AccessMatcher, value: string, ctx: MiddlewareContext): boolean {\n if (matcher === \"*\") return true;\n if (typeof matcher === \"string\") return matcher === value;\n if (matcher instanceof RegExp) return matcher.test(value);\n if (typeof matcher === \"function\") return matcher(ctx);\n return false;\n}\n\nfunction evaluateScope(\n policy: ScopePolicy | undefined,\n identifier: string,\n ctx: MiddlewareContext,\n): { allowed: boolean; reason: string } {\n const mode = policy?.mode ?? \"open\";\n\n // Deny list always evaluated first\n if (policy?.deny) {\n for (const m of policy.deny) {\n if (matcherMatches(m, identifier, ctx)) {\n return { allowed: false, reason: `denied by deny-list (${identifier})` };\n }\n }\n }\n\n if (mode === \"disabled\") {\n return { allowed: false, reason: \"scope disabled by policy\" };\n }\n\n if (mode === \"open\") {\n return { allowed: true, reason: \"\" };\n }\n\n // mode === \"allowlist\"\n if (!policy?.allow || policy.allow.length === 0) {\n return { allowed: false, reason: \"allowlist is empty\" };\n }\n for (const m of policy.allow) {\n if (matcherMatches(m, identifier, ctx)) {\n return { allowed: true, reason: \"\" };\n }\n }\n return { allowed: false, reason: `not in allowlist (${identifier})` };\n}\n\n// ============ Middleware ============\n\n/**\n * Build an access-policy middleware.\n *\n * Identifier used for matching:\n * - `c2c` / `dm` → senderId (user openid)\n * - `group` → groupOpenid\n * - `guild` → channelId\n *\n * @example\n * ```ts\n * bot.use(accessPolicy({\n * c2c: { mode: \"open\" },\n * group: { mode: \"allowlist\", allow: [\"group-openid-1\", \"group-openid-2\"] },\n * onBlock: (ctx, reason) => console.warn(\"blocked\", reason),\n * }));\n * ```\n */\nexport function accessPolicy(policy: AccessPolicy = {}): Middleware {\n return async (ctx, next) => {\n const { kind, senderId, groupOpenid, channelId } = ctx.message;\n\n let result: { allowed: boolean; reason: string };\n\n if (kind === \"c2c\" || kind === \"dm\") {\n result = evaluateScope(policy.c2c, senderId, ctx);\n } else if (kind === \"group\") {\n result = evaluateScope(policy.group, groupOpenid ?? \"\", ctx);\n } else if (kind === \"guild\") {\n result = evaluateScope(policy.guild, channelId ?? \"\", ctx);\n } else {\n result = { allowed: true, reason: \"\" };\n }\n\n if (!result.allowed) {\n ctx.log.debug?.(\n `[access] blocked ${kind} message from ${senderId}: ${result.reason}`,\n );\n policy.onBlock?.(ctx, result.reason);\n ctx.stop(`access:${result.reason}`);\n return;\n }\n\n await next();\n };\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 * Middleware module barrel.\n *\n * Provides a Koa-style middleware chain plus a curated set of built-in\n * middleware that cover the most common QQ Bot needs:\n *\n * **Core pipeline (dataflow stages):**\n * - {@link selfEchoFilter} — drop bot's own echo messages\n * - {@link deduplication} — drop duplicate messages (QQ retry overlap)\n * - {@link contentSanitizer} — strip @markers, face tags, whitespace\n * - {@link accessPolicy} — gate by allow / deny lists\n * - {@link mentionGate} — only respond to @mentions in groups\n * - {@link refIndexRecorder} — record messages for quote resolution\n * - {@link quoteResolver} — resolve quoted/referenced messages\n * - {@link slashCommand} — `/cmd` style command dispatch\n * - {@link historyBuffer} — buffer recent group messages\n * - {@link envelopeFormatter} — assemble LLM prompt context\n * - {@link typingIndicator} — auto-show typing for C2C\n * - {@link errorHandler} — catch errors and reply\n *\n * **Policy / protection:**\n * - {@link rateLimiter} — multi-tier sliding-window rate limiting\n *\n * Compose them or replace them — `bot.use(...)` accepts any\n * {@link Middleware} function.\n */\n\n// Core types\nexport type { Middleware, MiddlewareContext } from \"./types.js\";\nexport { runMiddlewareChain, createMiddlewareContext, resolvePolicy } from \"./types.js\";\n\n// ── P0: Filters & protection ──\n\nexport { messageFilter } from \"./message-filter.js\";\nexport type { MessageFilterOptions } from \"./message-filter.js\";\n\nexport { contentSanitizer } from \"./content-sanitizer.js\";\nexport type { ContentSanitizerOptions } from \"./content-sanitizer.js\";\n\nexport { rateLimiter } from \"./rate-limiter.js\";\nexport type { RateLimiterOptions, RateLimitTier } from \"./rate-limiter.js\";\n\nexport { concurrencyGuard } from \"./concurrency-guard.js\";\nexport type { ConcurrencyGuardOptions, ConcurrencyStrategy } from \"./concurrency-guard.js\";\n\n// ── Access & gating ──\n\nexport { accessPolicy } from \"./access-policy.js\";\nexport type {\n AccessMatcher,\n AccessPolicy,\n ScopePolicy,\n} from \"./access-policy.js\";\n\nexport { mentionGate } from \"./mention-gate.js\";\nexport type { MentionDecision, MentionGateOptions } from \"./mention-gate.js\";\n\n// ── Protocol (dataflow stages) ──\n\n// ── Quote & ref-index (combined) ──\n\nexport { quoteRef, MemoryRefIndexStore } from \"./quote-ref.js\";\nexport type {\n QuoteRefOptions,\n RefEntry,\n RefIndexStore,\n ResolvedQuote,\n QuotedAttachment,\n} from \"./quote-ref.js\";\n\nexport { envelopeFormatter } from \"./envelope-formatter.js\";\nexport type { EnvelopeFormatterOptions } from \"./envelope-formatter.js\";\n\n// ── Commands ──\n\nexport { slashCommand } from \"./slash-command.js\";\nexport type {\n ParsedCommand,\n SlashCommand,\n SlashCommandHandlerContext,\n SlashCommandOptions,\n SlashCommandResult,\n} from \"./slash-command.js\";\n\n// ── History ──\n\nexport {\n historyBuffer,\n MemoryHistoryStore,\n} from \"./history-buffer.js\";\nexport type {\n HistoryBufferOptions,\n HistoryEntry,\n HistoryStore,\n} from \"./history-buffer.js\";\n\n// ── UX ──\n\nexport { typingIndicator } from \"./typing-indicator.js\";\nexport type { TypingIndicatorOptions } from \"./typing-indicator.js\";\n\n// ── Error handling ──\n\nexport { errorHandler } from \"./error-handler.js\";\nexport type { ErrorHandlerOptions } from \"./error-handler.js\";\n\n// ============ State augmentation ============\n// Declared here so IDE autocomplete surfaces the well-known keys filled\n// by built-in middleware.\n\nimport type { MentionDecision } from \"./mention-gate.js\";\nimport type { ParsedCommand } from \"./slash-command.js\";\nimport type { HistoryEntry } from \"./history-buffer.js\";\nimport type { ResolvedQuote } from \"./quote-ref.js\";\n\ndeclare module \"./types.js\" {\n interface MiddlewareState {\n /** Set by {@link mentionGate} middleware. */\n mention?: MentionDecision;\n /** Set by {@link slashCommand} middleware when a command matches. */\n command?: ParsedCommand;\n /** Set by {@link historyBuffer} middleware. */\n history?: HistoryEntry[];\n /** Set by {@link quoteResolver} middleware. */\n quote?: ResolvedQuote;\n /** Set by {@link envelopeFormatter} middleware. Formatted LLM context string. */\n envelope?: string;\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 * Pluggable storage backends for the SDK.\n *\n * The SDK ships:\n * - {@link MemoryKVStore} — in-memory (default for tests)\n * - {@link FileKVStore} — JSON-file backed (single-process)\n *\n * Anything that implements {@link KVStore} can be plugged in:\n * - Redis: `new RedisKVStore(redisClient)` (user-provided)\n * - SQL: `new SqlKVStore(db)` (user-provided)\n * - Cloud: `new D1KVStore(d1)` (user-provided)\n *\n * Use {@link kvSessionPersistence} to plug a KVStore into the gateway's\n * session persistence slot. For ref-index storage, the SDK's existing\n * {@link JsonlRefIndexStore} (in `protocol/utils/ref-index-store.js`) is\n * still recommended for file-based deployments — but you can implement\n * the same interface against any backend.\n */\n\nexport { MemoryKVStore, FileKVStore } from \"./kv-store.js\";\nexport type { KVStore, FileKVStoreOptions } from \"./kv-store.js\";\n\nexport { kvSessionPersistence } from \"./session-adapter.js\";\nexport type { KVSessionPersistenceOptions } from \"./session-adapter.js\";\n","/**\n * @tencent-connect/qqbot-nodejs\n *\n * Node.js SDK for the QQ Open Platform.\n *\n * The high-level entry is the {@link QQBot} class:\n *\n * ```ts\n * import { QQBot, MediaFileType } 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 * logger: console,\n * });\n *\n * bot.on(\"message\", async (ctx, msg) => {\n * if (msg.replyTarget.scope === \"c2c\") {\n * await bot.sendText(msg.replyTarget, `Echo: ${msg.content}`);\n * }\n * });\n *\n * await bot.start();\n * ```\n *\n * For lower-level access to the protocol primitives (HTTP client, gateway\n * connection, retry policies, etc.) see the `protocol/` sub-export:\n *\n * ```ts\n * import { ApiClient, GatewayConnection } from \"@tencent-connect/qqbot-nodejs/protocol\";\n * ```\n */\n\nexport { QQBot } from \"./QQBot.js\";\nexport { MsgType } from \"./QQBot.js\";\nexport type {\n QQBotOptions,\n QQBotEventMap,\n QQBotInboundMessage,\n ReplyTarget,\n SendMessageOptions,\n ApiGateway,\n InteractionContext,\n RawEventContext,\n} from \"./QQBot.js\";\n\nexport { StreamSession } from \"./streaming.js\";\nexport type { StreamSessionOptions } from \"./streaming.js\";\n\n// ============ Middleware ============\nexport {\n // Core\n runMiddlewareChain,\n // P0: Filters & protection\n messageFilter,\n contentSanitizer,\n rateLimiter,\n concurrencyGuard,\n // Access & gating\n accessPolicy,\n mentionGate,\n // Protocol (dataflow stages)\n quoteRef,\n MemoryRefIndexStore,\n envelopeFormatter,\n // Commands\n slashCommand,\n // History\n historyBuffer,\n MemoryHistoryStore,\n // UX\n typingIndicator,\n // Error handling\n errorHandler,\n} from \"./middleware/index.js\";\nexport type {\n Middleware,\n MiddlewareContext,\n // Message filter\n MessageFilterOptions,\n // Content sanitizer\n ContentSanitizerOptions,\n // Rate limiter\n RateLimiterOptions,\n RateLimitTier,\n // Concurrency guard\n ConcurrencyGuardOptions,\n ConcurrencyStrategy,\n // Access policy\n AccessMatcher,\n AccessPolicy,\n ScopePolicy,\n // Mention gate\n MentionDecision,\n MentionGateOptions,\n // Quote ref\n QuoteRefOptions,\n RefEntry,\n RefIndexStore,\n ResolvedQuote,\n QuotedAttachment,\n // Envelope formatter\n EnvelopeFormatterOptions,\n // Slash command\n ParsedCommand,\n SlashCommand,\n SlashCommandHandlerContext,\n SlashCommandOptions,\n SlashCommandResult,\n // History buffer\n HistoryBufferOptions,\n HistoryEntry,\n HistoryStore,\n // Typing indicator\n TypingIndicatorOptions,\n // Error handler\n ErrorHandlerOptions,\n} from \"./middleware/index.js\";\n\n// ============ Pluggable Storage ============\nexport {\n MemoryKVStore,\n FileKVStore,\n kvSessionPersistence,\n} from \"./storage/index.js\";\nexport type {\n KVStore,\n FileKVStoreOptions,\n KVSessionPersistenceOptions,\n} from \"./storage/index.js\";\n\n// ============ Re-exports from protocol ============\n// Re-export the public types from the protocol layer that users routinely\n// need at the high level. This avoids forcing users to import from two\n// places for common cases.\nexport {\n ApiError,\n MediaFileType,\n StreamContentType,\n StreamInputMode,\n StreamInputState,\n type ChatScope,\n type Credentials,\n type GatewayAccount,\n type InlineKeyboard,\n type InteractionEvent,\n type Logger,\n type MessageResponse,\n type OutboundMeta,\n type UploadMediaResponse,\n} from \"./protocol/types.js\";\n\nexport type { InboundMessage } from \"./protocol/gateway/event-dispatcher.js\";\n\nexport type {\n WebhookRequest,\n WebhookResponse,\n WebhookRequestHandler,\n WebhookServerAdapter,\n} from \"./protocol/transport/types.js\";\n\n// Useful runtime helpers + constants.\nexport {\n CHUNKED_UPLOAD_MAX_SIZE,\n LARGE_FILE_THRESHOLD,\n MAX_UPLOAD_SIZE,\n getFileTypeName,\n getMaxUploadSize,\n sanitizeFileName,\n} from \"./protocol/utils/file-utils.js\";\n\nexport { UploadCache } from \"./protocol/utils/upload-cache.js\";\nexport { formatErrorMessage, formatFileSize } from \"./protocol/utils/format.js\";\n\nexport { UploadDailyLimitExceededError } from \"./protocol/api/media-chunked.js\";\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 {\n checkAndConsumePassiveReplyQuota,\n clearQuotaCacheForAccount,\n rollbackPassiveReplyQuota,\n} from '../features/quota-manager.js';\n\n// ── Gateway 注册表(生命周期由 channel.ts 管理)──\n\nconst gateways = new Map<string, QQBotGateway>();\n\n/**\n * 为一次实际 API 调用预留被动回复配额。\n * quotaReserved=true 表示上层 adapter 已经原子预留,避免同一发送重复计数。\n */\nfunction reservePassiveReply(params: {\n replyToId?: string;\n accountId: string;\n scope: 'c2c' | 'group';\n quotaReserved?: boolean;\n}): { msgId?: string; rollback: () => void } {\n if (!params.replyToId) return { rollback: () => {} };\n if (params.quotaReserved) {\n return { msgId: params.replyToId, rollback: () => {} };\n }\n const reservation = checkAndConsumePassiveReplyQuota({\n accountId: params.accountId,\n msgId: params.replyToId,\n scope: params.scope,\n });\n return {\n msgId: reservation.canReply ? params.replyToId : undefined,\n rollback: reservation.canReply ? reservation.rollback : () => {},\n };\n}\n\n/**\n * 尝试为 typing 指示器占用一个被动回复配额(带 msg_id 发送)。\n *\n * typing 通知与回复消息共享同一 msg_id 的被动回复配额,必须经统一\n * quota-manager 记账。配额不可用时调用方应降级为主动发送(不带 msg_id)。\n *\n * @returns 是否占得被动配额;false 表示应不带 msg_id 主动发送\n */\nexport function tryAcquirePassiveSlot(accountId: string, msgId: string | undefined): boolean {\n if (!msgId) return false; // 无 msg_id 无法走被动通道\n return checkAndConsumePassiveReplyQuota({ accountId, msgId, scope: 'c2c' }).canReply;\n}\n\nexport function rollbackPassiveSlot(accountId: string, msgId: string | undefined): void {\n if (!msgId) return;\n rollbackPassiveReplyQuota({ accountId, msgId, scope: 'c2c' });\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 clearQuotaCacheForAccount(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 quotaReserved?: boolean;\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 const target = parseTarget(params.to);\n const reservation = reservePassiveReply({\n replyToId: params.replyToId,\n accountId,\n scope: target.scope,\n quotaReserved: params.quotaReserved,\n });\n try {\n const result = await gw.sendText(target, params.text, { msgId: reservation.msgId });\n return { messageId: result.id };\n } catch (err: unknown) {\n reservation.rollback();\n return formatError(err);\n }\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 quotaReserved?: boolean;\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 const target = parseTarget(params.to);\n const reservation = reservePassiveReply({\n replyToId: params.replyToId,\n accountId,\n scope: target.scope,\n quotaReserved: params.quotaReserved,\n });\n try {\n const kind = params.mediaKind ?? 'image';\n const msgId = reservation.msgId;\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) {\n reservation.rollback();\n return formatError(err);\n }\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 quotaReserved?: boolean;\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 const target = parseTarget(params.to);\n const reservation = reservePassiveReply({\n replyToId: params.replyToId,\n accountId,\n scope: target.scope,\n quotaReserved: params.quotaReserved,\n });\n try {\n const result = await gw.sendVoice(target, params.source, { msgId: reservation.msgId });\n return { messageId: result.id };\n } catch (err: unknown) {\n reservation.rollback();\n return formatError(err);\n }\n}\n\nexport async function sendVideo(params: {\n to: string;\n videoUrl: string;\n accountId?: string;\n replyToId?: string;\n account: ResolvedQQBotAccount;\n quotaReserved?: boolean;\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 const target = parseTarget(params.to);\n const reservation = reservePassiveReply({\n replyToId: params.replyToId,\n accountId,\n scope: target.scope,\n quotaReserved: params.quotaReserved,\n });\n try {\n const result = await gw.sendVideo(target, params.videoUrl, { msgId: reservation.msgId });\n return { messageId: result.id };\n } catch (err: unknown) {\n reservation.rollback();\n return formatError(err);\n }\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 const target = parseTarget(to);\n const reservation = reservePassiveReply({\n replyToId: msgId,\n accountId: this.accountId,\n scope: target.scope,\n });\n try {\n const result = await this.gw.sendText(target, text, { msgId: reservation.msgId });\n return { messageId: result.id };\n } catch (err: unknown) {\n reservation.rollback();\n return formatError(err);\n }\n }\n\n async sendMedia(to: string, source: string, opts?: { text?: string; msgId?: string; mediaKind?: MediaKind }): Promise<SendResult> {\n const target = parseTarget(to);\n const reservation = reservePassiveReply({\n replyToId: opts?.msgId,\n accountId: this.accountId,\n scope: target.scope,\n });\n try {\n const kind = opts?.mediaKind ?? 'image';\n const resolvedMsgId = reservation.msgId;\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) {\n reservation.rollback();\n return formatError(err);\n }\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 * 版本号获取工具。\n *\n * - getPackageVersion(): 插件自身版本(@jerryliang122/openclaw-qqbot)\n * - getOpenclawVersion(): OpenClaw 框架版本(PluginRuntime.version)\n */\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/**\n * OpenClaw 框架版本(PluginRuntime.version,peer >=2026.9.2 恒存在)。\n */\nexport function getOpenclawVersion(runtimeVersion?: string): string {\n return runtimeVersion && runtimeVersion !== 'unknown' ? runtimeVersion : 'unknown';\n}\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 * 请求级上下文(基于 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 * 持久化 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, type PluginLogger } from '../utils/plugin-logger.js';\n\n// 惰性初始化:plugin-logger → runtime → 本模块 → plugin-logger 存在循环导入,\n// 顶层直接 createPluginLogger 会在部分求值顺序下踩到未初始化的 tsx helper\n// (__name is not a function,2026-09-09 群聊架构改造触发)。首次使用时再建。\nlet _log: PluginLogger | undefined;\nfunction log(): PluginLogger {\n return (_log ??= createPluginLogger({ prefix: '[ref-index]' }));\n}\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 * 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 * Agent workspace 目录解析(plugin-sdk/health)\n */\nimport { createRequire } from 'node:module';\nimport { fileURLToPath } from 'node:url';\n\n// CJS 兼容:__filename 在 CJS 中可用,ESM 中使用 import.meta.url\nconst __file = typeof __filename !== 'undefined'\n ? __filename\n : fileURLToPath(import.meta.url);\nconst req = createRequire(__file);\n\nlet health: {\n resolveAgentWorkspaceDir: (cfg: any, agentId: string) => string;\n resolveDefaultAgentId: (cfg: any) => string;\n} | undefined;\n\nexport function resolveAgentWorkspace(cfg: any, agentId?: string): string {\n const h = (health ??= req('openclaw/plugin-sdk/health') as NonNullable<typeof health>);\n return h.resolveAgentWorkspaceDir(cfg, agentId ?? h.resolveDefaultAgentId(cfg));\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 * 统一富媒体发送入口\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 { checkAndConsumePassiveReplyQuota } from '../features/quota-manager.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 quotaReserved?: boolean;\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 const reservation = params.quotaReserved || !params.replyToId\n ? { canReply: Boolean(params.replyToId), rollback: () => {} }\n : checkAndConsumePassiveReplyQuota({\n accountId,\n msgId: params.replyToId,\n scope: target.scope,\n log,\n });\n const sendParams: SendMediaParams = {\n ...params,\n replyToId: reservation.canReply ? params.replyToId : undefined,\n };\n\n // 4. 路由分发\n let result: SendMediaResult;\n switch (kind) {\n case 'voice':\n result = await sendVoiceMedia(gw, target, resolved.path, sendParams);\n break;\n case 'video':\n result = await sendVideoMedia(gw, target, resolved.path, sendParams);\n break;\n case 'file':\n result = await sendFileMedia(gw, target, resolved.path, sendParams);\n break;\n case 'image':\n default:\n result = await sendImageMedia(gw, target, resolved.path, sendParams);\n break;\n }\n if (result.error) reservation.rollback();\n return result;\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 resolveAllowedLocalPath(resolved, workspaceDir, log);\n }\n return { ok: true, path: normalized, isLocal: false };\n }\n\n return resolveAllowedLocalPath(normalized, workspaceDir, log);\n}\n\n/** 所有本地路径(包括纯文件名兜底)统一经过 realpath + 根目录白名单校验。 */\nfunction resolveAllowedLocalPath(\n source: string,\n workspaceDir?: string,\n log?: SendMediaParams['log'],\n): ResolveResult | ResolveError {\n const resolved = path.resolve(source);\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 * @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/core';\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 { registerSecretInputTool } from './src/tools/secret-input.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 registerSecretInputTool(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 { 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';\nexport { qqbotChannelOutbound } from './src/outbound-adapter.js';\n\n// Quota management\nexport { ReplyLimiter } from './src/outbound/reply-limiter.js';\nexport {\n checkPassiveReplyQuota,\n inferQQBotScope,\n clearQuotaCache,\n getQuotaStats,\n} from './src/features/quota-manager.js';\n\n// Typing lifecycle\nexport {\n c2cTypingIndicator,\n resolveTypingIntervalMs,\n MIN_TYPING_INTERVAL_MS,\n POST_MESSAGE_REFRESH_DELAY_MS,\n} from './src/middleware/typing.js';\nexport {\n subscribeOutboundMessage,\n notifyOutboundMessageSent,\n} from './src/features/typing-refresh.js';\n","/**\n * QQ Bot ChannelPlugin 定义\n *\n * 使用 createChatChannelPlugin 构建标准插件\n */\n\nimport { createChatChannelPlugin } from 'openclaw/plugin-sdk/channel-core';\nimport { DEFAULT_ACCOUNT_ID } from 'openclaw/plugin-sdk/account-id';\nimport type { OpenClawConfig } from 'openclaw/plugin-sdk/config-contracts';\nimport {\n setAccountEnabledInConfigSection,\n deleteAccountFromConfigSection,\n applyAccountNameToChannelSection,\n} from 'openclaw/plugin-sdk/core';\n\nimport type { ResolvedQQBotAccount } from './types.js';\nimport {\n listQQBotAccountIds,\n resolveQQBotAccount,\n resolveDefaultQQBotAccountId,\n resolveRequireMention,\n resolveToolPolicy,\n resolveGroupConfig,\n applyQQBotAccountConfig,\n} from './config.js';\nimport { loadCredentialBackup } from './features/credential-backup.js';\nimport { qqbotSetupWizard } from './setup/surface.js';\nimport { qqbotLogin } from './setup/login.js';\nimport { createQQBotPluginBase } from './plugin-base.js';\nimport { qqbotMessageAdapter } from './message-adapter.js';\nimport { qqbotMessagingAdapter } from './messaging-adapter.js';\nimport { qqbotStatusAdapter } from './status-adapter.js';\nimport { qqbotGatewayAdapter } from './gateway-adapter.js';\nimport { qqbotChannelOutbound } from './outbound-adapter.js';\nimport { qqbotHeartbeatAdapter } from './heartbeat-adapter.js';\nimport { qqbotAgentPromptAdapter } from './agent-prompt-adapter.js';\nimport { getQQBotApprovalCapability } from './features/approval-capability.js';\nimport { stripMentionText } from './utils/mention.js';\n\n/**\n * QQBot Threading Adapter\n *\n * QQBot 无 thread/topic(replyToMode 恒 off),但 buildToolContext **不能**\n * 返回 undefined:框架在 message_tool_only(room_event)模式下,靠它提供的\n * currentChannelId/currentMessagingTarget 解析 message 工具的隐式当前来源\n * 路由——缺失时发送会落入 internal-ui sink(只进 openclaw 会话记录/WebUI,\n * QQ 侧无消息、无出站 HTTP 请求;2026-09-09 用户反馈事故)。context.To 即\n * 限定可路由目标(qqbot:group:{gid} / qqbot:c2c:{openid}),直接透传即可。\n */\nexport const qqbotThreadingAdapter = {\n resolveReplyToMode: () => 'off' as const,\n buildToolContext: ({ context }: {\n context: { To?: string; From?: string; ChatType?: string };\n }) => {\n const target = context.To || context.From;\n if (!target) return undefined;\n return {\n currentChannelId: target,\n currentMessagingTarget: target,\n ...(context.ChatType === 'group' || context.ChatType === 'direct'\n ? { currentChatType: context.ChatType as 'group' | 'direct' }\n : {}),\n replyToMode: 'off' as const,\n };\n },\n resolveAutoThreadId: () => undefined,\n};\n\n/**\n * QQBot Groups Adapter\n */\nconst qqbotGroupsAdapter = {\n resolveRequireMention: ({ cfg, accountId, groupId }: {\n cfg: OpenClawConfig;\n accountId?: string | null;\n groupId?: string | null;\n }) => {\n if (!groupId) return undefined;\n return resolveRequireMention(cfg, groupId, accountId ?? undefined);\n },\n\n resolveToolPolicy: ({ cfg, accountId, groupId }: {\n cfg: OpenClawConfig;\n accountId?: string | null;\n groupId?: string | null;\n }) => {\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\n resolveGroupIntroHint: ({ cfg, accountId, groupId }: {\n cfg: OpenClawConfig;\n accountId?: string | null;\n groupId?: string | null;\n }) => {\n if (!groupId) return undefined;\n const groupCfg = resolveGroupConfig(cfg, groupId, accountId ?? undefined);\n return groupCfg.name ? `当前群: ${groupCfg.name}` : undefined;\n },\n};\n\n/**\n * QQBot Mentions Adapter\n */\nconst qqbotMentionsAdapter = {\n stripMentions: ({ text, ctx }: { text: string; ctx: unknown }) => {\n const mentions = (ctx as any)?.mentions;\n return stripMentionText(text, mentions);\n },\n};\n\n/**\n * QQBot Setup Adapter\n */\nconst qqbotSetupAdapter = {\n resolveAccountId: ({ accountId }: { cfg: OpenClawConfig; accountId?: string }) =>\n accountId?.trim().toLowerCase() || DEFAULT_ACCOUNT_ID,\n applyAccountName: ({ cfg, accountId, name }: {\n cfg: OpenClawConfig;\n accountId: string;\n name?: string;\n }) =>\n applyAccountNameToChannelSection({ cfg, channelKey: 'qqbot', accountId, name }),\n validateInput: ({ input }: {\n cfg: OpenClawConfig;\n accountId: string;\n input: { token?: string; tokenFile?: string; useEnv?: boolean };\n }) => {\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 cfg: OpenClawConfig;\n accountId: string;\n input: { token?: string; tokenFile?: string; name?: string; useEnv?: boolean };\n }) => {\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/**\n * QQBot Config Adapter\n */\nconst qqbotConfigAdapter = {\n listAccountIds: (cfg: OpenClawConfig) => listQQBotAccountIds(cfg),\n resolveAccount: (cfg: OpenClawConfig, accountId?: string | null) =>\n resolveQQBotAccount(cfg, accountId),\n defaultAccountId: (cfg: OpenClawConfig) => resolveDefaultQQBotAccountId(cfg),\n isConfigured: (account: ResolvedQQBotAccount, _cfg: OpenClawConfig) => {\n if (account?.appId && account?.clientSecret) return true;\n return loadCredentialBackup(account?.accountId) !== null;\n },\n describeAccount: (account: ResolvedQQBotAccount, _cfg: OpenClawConfig) => ({\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 setAccountEnabled: ({ cfg, accountId, enabled }: {\n cfg: OpenClawConfig;\n accountId: string;\n enabled: boolean;\n }) =>\n setAccountEnabledInConfigSection({\n cfg, sectionKey: 'qqbot', accountId, enabled, allowTopLevel: true\n }),\n deleteAccount: ({ cfg, accountId }: { cfg: OpenClawConfig; accountId: string }) =>\n deleteAccountFromConfigSection({\n cfg, sectionKey: 'qqbot', accountId,\n clearBaseFields: ['appId', 'clientSecret', 'clientSecretFile', 'name'],\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: ({ cfg, allowFrom }: { cfg: OpenClawConfig; 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/**\n * QQBot Channel Plugin\n */\nexport const qqbotPlugin = createChatChannelPlugin({\n base: {\n ...createQQBotPluginBase(),\n\n config: qqbotConfigAdapter,\n\n message: qqbotMessageAdapter,\n messaging: qqbotMessagingAdapter,\n status: qqbotStatusAdapter,\n gateway: qqbotGatewayAdapter,\n outbound: qqbotChannelOutbound,\n\n agentPrompt: qqbotAgentPromptAdapter,\n heartbeat: qqbotHeartbeatAdapter,\n threading: qqbotThreadingAdapter,\n groups: qqbotGroupsAdapter,\n mentions: qqbotMentionsAdapter,\n\n setup: qqbotSetupAdapter,\n // setupWizard 类型断言:ChannelSetupWizard 与框架期望的接口存在差异\n // 主要差异在于 credentials 字段的结构,运行时行为正确\n setupWizard: qqbotSetupWizard as unknown as Parameters<typeof createChatChannelPlugin>[0]['base']['setupWizard'],\n // auth.login 类型断言:框架接口可能不包含 login 字段\n // 运行时行为正确,类型断言确保类型安全\n auth: { login: qqbotLogin as unknown as NonNullable<Parameters<typeof createChatChannelPlugin>[0]['base']['auth']>['login'] },\n\n approvalCapability: getQQBotApprovalCapability(),\n },\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';\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 const backupPath = getBackupPath();\n if (fs.existsSync(backupPath)) {\n return readBackupFile(backupPath, 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 * 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 * 参考 Telegram 插件的 createTelegramPluginBase\n */\n\nimport { qqbotSetupWizard } from './setup/surface.js';\n\nexport interface QQBotPluginBaseParams {\n setupWizard?: typeof qqbotSetupWizard;\n}\n\nexport function createQQBotPluginBase(params: QQBotPluginBaseParams = {}) {\n const { setupWizard = qqbotSetupWizard } = params;\n\n return {\n id: 'qqbot' as const,\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'] as Array<'direct' | 'group'>,\n media: true,\n reactions: false,\n threads: false,\n },\n setupWizard: setupWizard as any,\n reload: {\n configPrefixes: ['channels.qqbot'],\n },\n defaults: {\n queue: {\n debounceMs: 1000,\n },\n },\n };\n}\n","/**\n * QQBot Outbound 适配器\n *\n * 封装 sendText/sendMedia 流式等出站操作,提供配额感知的发送能力。\n *\n * 两层结构:\n * - createQQBotOutboundAdapter —— 内部配额包装(sendTextWithQuota / sendMediaWithQuota),\n * 供中间件/流式路径使用;\n * - createQQBotChannelOutbound —— 框架 ChannelOutboundAdapter 契约入口\n * (sendText / sendMedia / chunker / sanitizeText 等)。\n * openclaw 的 channel-selection 通过 outbound.sendText(或 message.send.text /\n * deliveryMode === \"gateway\")判定通道可用,缺失会导致\n * \"Channel is unavailable: qqbot\"。\n */\n\nimport { checkAndConsumePassiveReplyQuota, checkPassiveReplyQuota, inferQQBotScope } from './features/quota-manager.js';\nimport type { PluginLogger } from './utils/plugin-logger.js';\nimport type { ResolvedQQBotAccount } from './types.js';\nimport type { SendMediaParams, SendMediaResult } from './outbound/media-send.js';\nimport type { SendResult } from './outbound/outbound-service.js';\nimport type { OpenClawConfig } from 'openclaw/plugin-sdk';\nimport { resolveQQBotAccount } from './config.js';\nimport { parseTarget } from './outbound/target.js';\nimport { getCachedMsgId } from './features/msgid-cache.js';\nimport { sanitizeQQBotText } from './outbound/sanitize.js';\nimport { chunkQQBotMarkdown } from './outbound/chunker.js';\nimport { isApprovalPayload } from './features/approval-utils.js';\nimport { TEXT_CHUNK_LIMIT } from './constants.js';\nimport { tryGetQQBotRuntime } from './runtime.js';\nimport { getAdapters } from './adapter/resolve.js';\n\nexport type SendTextFn = (params: {\n to: string;\n text: string;\n accountId?: string;\n replyToId?: string;\n account: ResolvedQQBotAccount;\n quotaReserved?: boolean;\n}) => Promise<SendResult>;\n\nexport type SendMediaFn = (params: SendMediaParams) => Promise<SendMediaResult>;\n\nexport interface QQBotOutboundAdapterParams {\n sendText?: SendTextFn;\n sendMedia?: SendMediaFn;\n shouldSuppressLocalPayloadPrompt?: (params: { payload: unknown }) => boolean;\n shouldTreatDeliveredTextAsVisible?: (params: { kind: string; text?: string }) => boolean;\n preferFinalAssistantVisibleText?: boolean;\n beforeDeliverPayload?: (params: {\n cfg: unknown;\n target: { to: string; accountId?: string; replyToId?: string };\n hint?: unknown;\n payload?: unknown;\n }) => Promise<{ mode: 'passive' | 'proactive'; msgId?: string } | void>;\n}\n\nexport interface QQBotOutboundAdapter {\n sendTextWithQuota: (params: {\n to: string;\n text: string;\n accountId?: string;\n replyToId?: string;\n account: ResolvedQQBotAccount;\n log?: PluginLogger;\n }) => Promise<{ messageId?: string; error?: string }>;\n sendMediaWithQuota: (params: {\n to: string;\n source: string;\n text?: string;\n accountId?: string;\n replyToId?: string;\n account: ResolvedQQBotAccount;\n agentId?: string;\n log?: PluginLogger;\n }) => Promise<{ messageId?: string; error?: string }>;\n canSendTyping: (params: {\n to: string;\n accountId: string;\n replyToId: string;\n log?: PluginLogger;\n }) => Promise<boolean>;\n shouldSuppressLocalPayloadPrompt: (params: { payload: unknown }) => boolean;\n shouldTreatDeliveredTextAsVisible: (params: { kind: string; text?: string }) => boolean;\n preferFinalAssistantVisibleText: boolean;\n}\n\nlet lazySendText: SendTextFn | undefined;\nlet lazySendMedia: SendMediaFn | undefined;\n\nasync function getSendText(): Promise<SendTextFn> {\n if (!lazySendText) {\n const mod = await import('./outbound/outbound-service.js');\n lazySendText = mod.sendText;\n }\n return lazySendText;\n}\n\nasync function getSendMedia(): Promise<SendMediaFn> {\n if (!lazySendMedia) {\n const mod = await import('./outbound/media-send.js');\n lazySendMedia = mod.sendMedia;\n }\n return lazySendMedia;\n}\n\n/**\n * 创建 QQBot Outbound 适配器\n */\nexport function createQQBotOutboundAdapter(params: QQBotOutboundAdapterParams): QQBotOutboundAdapter {\n const {\n sendText: injectedSendText,\n sendMedia: injectedSendMedia,\n shouldSuppressLocalPayloadPrompt = () => false,\n shouldTreatDeliveredTextAsVisible = () => true,\n preferFinalAssistantVisibleText = true,\n beforeDeliverPayload,\n } = params;\n\n return {\n sendTextWithQuota: async ({ to, text, accountId, replyToId, account, log }) => {\n const scope = inferQQBotScope(to);\n const resolvedAccountId = accountId || account.accountId;\n\n // 使用原子操作检查并消耗配额\n const { canReply, rollback } = await checkAndConsumePassiveReplyQuota({\n accountId: resolvedAccountId,\n msgId: replyToId,\n scope,\n log,\n });\n\n const sendTextFn = injectedSendText || await getSendText();\n const result = await sendTextFn({\n to,\n text,\n accountId: resolvedAccountId,\n replyToId: canReply ? replyToId : undefined,\n account,\n quotaReserved: canReply,\n });\n\n // 发送失败时回滚配额\n if (canReply && replyToId && result.error) {\n rollback();\n log?.debug?.(`[${resolvedAccountId}] rollback quota: send failed`);\n } else if (!canReply) {\n log?.info?.(`[${resolvedAccountId}] [quota] fallback to proactive send: quota exhausted or no msgId`);\n }\n\n return result;\n },\n\n sendMediaWithQuota: async ({ to, source, text, accountId, replyToId, account, agentId, log }) => {\n const scope = inferQQBotScope(to);\n const resolvedAccountId = accountId || account.accountId;\n\n // 使用原子操作检查并消耗配额\n const { canReply, rollback } = await checkAndConsumePassiveReplyQuota({\n accountId: resolvedAccountId,\n msgId: replyToId,\n scope,\n log,\n });\n\n const sendMediaFn = injectedSendMedia || await getSendMedia();\n const result = await sendMediaFn({\n to,\n source,\n text,\n replyToId: canReply ? replyToId : undefined,\n accountId: resolvedAccountId,\n agentId,\n log,\n quotaReserved: canReply,\n });\n\n // 发送失败时回滚配额\n if (canReply && replyToId && result.error) {\n rollback();\n log?.debug?.(`[${resolvedAccountId}] rollback quota: send failed`);\n }\n\n return result;\n },\n\n canSendTyping: async ({ to, accountId, replyToId, log }) => {\n const scope = inferQQBotScope(to);\n\n if (scope !== 'c2c' || !replyToId) {\n return false;\n }\n\n const canPassiveReply = await checkPassiveReplyQuota({\n accountId,\n msgId: replyToId,\n scope: 'c2c',\n });\n\n if (!canPassiveReply) {\n return false;\n }\n\n return true;\n },\n\n shouldSuppressLocalPayloadPrompt,\n shouldTreatDeliveredTextAsVisible,\n preferFinalAssistantVisibleText,\n };\n}\n\n// ── 框架契约入口(openclaw ChannelOutboundAdapter)──\n\n/** 框架 sendText / sendMedia 上下文(openclaw ChannelOutboundContext 子集) */\nexport interface ChannelOutboundSendContext {\n to: string;\n text?: string;\n mediaUrl?: string;\n accountId?: string | null;\n replyToId?: string | null;\n cfg: OpenClawConfig;\n}\n\nexport interface ChannelOutboundDeliveryResult {\n channel: 'qqbot';\n messageId: string;\n}\n\n/** 恢复 MCP agentId 路由:media-send 用它解析相对路径的工作区 */\nfunction resolveMCPAgentId(\n to: string,\n accountId: string,\n cfg: unknown,\n log?: PluginLogger,\n): 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\n/**\n * 被动优先的 replyToId 解析:框架未显式给 replyToId 时(message 工具 / 框架\n * 直发路径),从 msgid-cache 取该会话最新未过期 msg_id,让发送走配额感知的\n * 被动通道(保每日主动预算)。无缓存(静群超 TTL)→ undefined → 主动发送。\n */\nfunction resolvePassiveFirstReplyToId(to: string, explicitReplyToId?: string | null): string | undefined {\n if (explicitReplyToId) return explicitReplyToId;\n try {\n const { scope, targetId } = parseTarget(to);\n if (!targetId) return undefined;\n return getCachedMsgId(scope, targetId);\n } catch {\n return undefined;\n }\n}\n\n/**\n * 创建框架 ChannelOutboundAdapter 契约入口。\n *\n * 框架可用性判定要求 outbound.sendText 存在(channel-resolution.ts /\n * channel-bootstrap.runtime.ts),sendText/sendMedia 内部走配额感知包装,\n * 配额耗尽时自动降级主动消息。\n */\nexport function createQQBotChannelOutbound(params: QQBotOutboundAdapterParams = {}) {\n const quotaAdapter = createQQBotOutboundAdapter(params);\n\n return {\n ...quotaAdapter,\n deliveryMode: 'direct' as const,\n\n sendText: async (ctx: ChannelOutboundSendContext): Promise<ChannelOutboundDeliveryResult> => {\n const account = resolveQQBotAccount(ctx.cfg, ctx.accountId ?? undefined);\n const result = await quotaAdapter.sendTextWithQuota({\n to: ctx.to,\n text: ctx.text ?? '',\n accountId: ctx.accountId ?? undefined,\n replyToId: resolvePassiveFirstReplyToId(ctx.to, ctx.replyToId),\n account,\n });\n if (result.error) throw new Error(result.error);\n return { channel: 'qqbot', messageId: result.messageId ?? '' };\n },\n\n sendMedia: async (ctx: ChannelOutboundSendContext): Promise<ChannelOutboundDeliveryResult> => {\n const account = resolveQQBotAccount(ctx.cfg, ctx.accountId ?? undefined);\n const resolvedAccountId = ctx.accountId ?? account.accountId;\n const result = await quotaAdapter.sendMediaWithQuota({\n to: ctx.to,\n source: ctx.mediaUrl ?? '',\n text: ctx.text,\n accountId: resolvedAccountId,\n replyToId: resolvePassiveFirstReplyToId(ctx.to, ctx.replyToId),\n account,\n agentId: resolveMCPAgentId(ctx.to, resolvedAccountId, ctx.cfg),\n });\n if (result.error) throw new Error(result.error);\n return { channel: 'qqbot', messageId: result.messageId ?? '' };\n },\n\n sanitizeText: ({ text }: { text: string }) => sanitizeQQBotText(text),\n chunker: (text: string, limit: number) => chunkQQBotMarkdown(text, limit),\n chunkerMode: 'markdown' as const,\n textChunkLimit: TEXT_CHUNK_LIMIT,\n shouldSuppressLocalPayloadPrompt: ({ payload }: { payload: unknown }) => isApprovalPayload(payload),\n };\n}\n\nexport const qqbotChannelOutbound = createQQBotChannelOutbound({\n shouldSuppressLocalPayloadPrompt: () => false,\n shouldTreatDeliveredTextAsVisible: () => true,\n preferFinalAssistantVisibleText: true,\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 * 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 * QQBot Markdown 文本分块\n *\n * 优先使用框架 runtime 提供的 chunkMarkdownText(高版本);\n * 不可用时回退到本地 GFM 表格感知 chunker —— 保证不在表格内部切分。\n */\n\nimport { getAdapters } from '../adapter/resolve.js';\nimport { tryGetQQBotRuntime } from '../runtime.js';\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\n/**\n * 表格感知 fallback chunker:按换行边界切分,整块缓冲表格行。\n */\nexport function chunkMarkdownPreservingTables(text: string, limit: number): string[] {\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\n/**\n * chunker 入口(框架 ChannelOutboundAdapter.chunker 契约):\n * runtime adapter 的 chunkMarkdownText 优先,低版本降级到本地实现。\n */\nexport function chunkQQBotMarkdown(text: string, limit: number): string[] {\n const rt = tryGetQQBotRuntime();\n const adapterChunker = rt ? getAdapters(rt).chunkMarkdownText : null;\n if (adapterChunker) return adapterChunker(text, limit);\n return chunkMarkdownPreservingTables(text, limit);\n}\n","/**\n * 审批 payload 判断逻辑。\n *\n * 仅保留 isApprovalPayload —— 用于在出站链路中识别审批类 payload,\n * 从而抑制框架侧重复的审批 prompt(native 审批由 approval-capability 处理)。\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 * QQBot 常量定义\n */\n\n/** QQ Bot 单条消息文本长度上限 */\nexport const TEXT_CHUNK_LIMIT = 5000;\n","/**\n * QQBot Message 适配器\n *\n * 定义消息生命周期能力\n */\n\nimport { qqbotChannelOutbound } from './outbound-adapter.js';\n\n/**\n * QQBot Message Adapter\n *\n * 能力声明:\n * - draftPreview: false (QQBot 不支持)\n * - previewFinalization: false\n * - progressUpdates: true (流式支持)\n * - finalEdit: false (QQBot 流式限制)\n */\nexport const qqbotMessageAdapter = {\n id: 'qqbot',\n live: {\n capabilities: {\n draftPreview: false,\n previewFinalization: false,\n progressUpdates: true,\n },\n finalizer: {\n capabilities: {\n finalEdit: false,\n normalFallback: true,\n previewReceipt: false,\n retainOnAmbiguousFailure: true,\n },\n },\n },\n receive: {\n defaultAckPolicy: 'after_agent_dispatch' as const,\n supportedAckPolicies: ['after_receive_record', 'after_agent_dispatch'] as const,\n },\n outbound: qqbotChannelOutbound,\n};\n","/**\n * QQBot Messaging 适配器\n *\n * 处理目标解析、会话路由\n */\n\nimport { normalizeTarget, isQQBotTarget, tryParseTarget } from './outbound/target.js';\n\n/**\n * 解析会话目标\n */\nfunction resolveQQBotInboundConversation(params: {\n to?: string;\n conversationId?: string;\n threadId?: string | number;\n}): { conversationId: string; parentConversationId: string } | null {\n const rawTarget = params.to || params.conversationId || '';\n if (!rawTarget) {\n return null;\n }\n\n const parsed = tryParseTarget(rawTarget);\n if (!parsed) {\n return null;\n }\n\n if (parsed.scope !== 'c2c' && parsed.scope !== 'group') {\n return null;\n }\n\n // 保存完整目标字符串(包括 scope),避免后续解析时丢失 scope 信息\n const fullTarget = `qqbot:${parsed.scope}:${parsed.targetId}`;\n\n return {\n conversationId: fullTarget,\n parentConversationId: fullTarget,\n };\n}\n\n/**\n * 解析投递目标\n */\nfunction resolveQQBotDeliveryTarget(params: {\n conversationId: string;\n parentConversationId?: string;\n}): { to: string } | null {\n const targetStr = params.parentConversationId || params.conversationId;\n if (!targetStr) {\n return null;\n }\n\n const parsed = tryParseTarget(targetStr);\n if (!parsed) {\n return null;\n }\n\n if (parsed.scope !== 'c2c' && parsed.scope !== 'group') {\n return null;\n }\n\n return {\n to: `qqbot:${parsed.scope}:${parsed.targetId}`,\n };\n}\n\n/**\n * QQBot Messaging Adapter\n */\nexport const qqbotMessagingAdapter = {\n targetPrefixes: ['qqbot'],\n normalizeTarget: (target: string) => normalizeTarget(target),\n resolveInboundConversation: resolveQQBotInboundConversation,\n resolveDeliveryTarget: resolveQQBotDeliveryTarget,\n targetResolver: {\n looksLikeId: isQQBotTarget,\n hint: 'QQ Bot 目标格式: qqbot:c2c:openid (私聊) 或 qqbot:group:groupid (群聊)',\n },\n};\n","/**\n * QQBot Status 适配器\n *\n * 账户状态探测、审计、快照构建\n */\n\nimport { DEFAULT_ACCOUNT_ID } from './config.js';\nimport type { ResolvedQQBotAccount } from './types.js';\n\n/**\n * 默认运行时状态\n */\nfunction createDefaultChannelRuntimeState(accountId: string) {\n return {\n accountId,\n running: false,\n connected: false,\n lastConnectedAt: null as number | null,\n lastError: null as string | null,\n lastInboundAt: null as number | null,\n lastOutboundAt: null as number | null,\n };\n}\n\n/**\n * QQBot Status Adapter\n */\nexport const qqbotStatusAdapter = {\n defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID),\n\n buildChannelSummary: ({ snapshot }: { snapshot: any }) => ({\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\n resolveAccountSnapshot: ({ account, runtime }: {\n account?: ResolvedQQBotAccount;\n runtime?: any;\n }) => ({\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 * Gateway 生命周期管理\n *\n * 封装 startAccount / logoutAccount 的业务逻辑:\n * - 凭证恢复\n * - QQBotGateway 实例创建与注册\n * - 注册 \"approval.native\" 运行时上下文(框架据此自动引导 native 审批 handler)\n * - 登出时凭证清除\n */\nimport type { OpenClawConfig } from 'openclaw/plugin-sdk/config-contracts';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport {\n DEFAULT_ACCOUNT_ID,\n resolveQQBotAccount,\n applyQQBotAccountConfig,\n resolveQQBotEnvClientSecret,\n} 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 { syncCommandPanels } from '../features/command-panel.js';\nimport { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from '../features/approval-capability.js';\nimport { cleanupTypingByAccount } from '../typing-lifecycle.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, ctx, 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(\n account: ResolvedQQBotAccount,\n ctx: StartAccountContext,\n log: PluginLogger,\n): Promise<void> {\n // 1. 版本更新检测(后台预热,fire-and-forget)\n triggerUpdateCheck(log);\n\n // 2. 注册 \"approval.native\" 运行时上下文。框架的 approval bootstrap 监听此上下文,\n // 据此为该账户自动 spawn native 审批 handler(消费 exec/plugin approval 事件,\n // 回调 qqbotPlugin.approvalCapability.nativeRuntime 的 availability/presentation/transport)。\n // 账户停止时 abortSignal 触发,上下文自动注销。\n registerApprovalNativeContext(account.accountId, ctx);\n\n // 3. 指令面板同步:把 openclaw essential 原生指令注册到 QQ Bot 指令面板(/v2/panels)。\n // 每进程每账号仅一次;失败只记日志并由 once-guard 释放等待重连重试,不影响消息收发。\n syncCommandPanels(account, ctx.cfg, log).catch((e) => {\n log?.error(`[qqbot:${account.accountId}] command panel sync error: ${e}`);\n });\n}\n\n/**\n * 注册 qqbot 的 native approval 运行时上下文(若框架已提供 channelRuntime)。\n */\nfunction registerApprovalNativeContext(accountId: string, ctx: StartAccountContext): void {\n const channelRuntime = (ctx as { channelRuntime?: { runtimeContexts?: any } }).channelRuntime;\n const registry = channelRuntime?.runtimeContexts;\n if (!registry) {\n return;\n }\n try {\n registry.register({\n channelId: 'qqbot',\n accountId,\n capability: CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY,\n context: { accountId },\n abortSignal: ctx.abortSignal,\n });\n } catch {\n // 重复注册或 registry 异常时静默 — 框架会按 accountId 去重。\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. 注销 gateway(native 审批上下文由 abortSignal 自动注销,无需手动清理)\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. 清理 typing session(防止账户重启后旧 session 阻塞新 session)\n cleanupTypingByAccount(accountId);\n\n // 2. 主动停止 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}\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\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(resolveQQBotEnvClientSecret());\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 { recordOutboundMessageId } from '../features/outbound-echo-store.js';\nimport { recordOutboundToGroupHistory } from '../features/history-store.js';\nimport { resolveGroupConfigFromAccount } from '../config.js';\nimport { checkAndConsumePassiveReplyQuota } from '../features/quota-manager.js';\nimport { recordProactiveSend } from '../features/proactive-budget.js';\nimport { getCachedMsgId } from '../features/msgid-cache.js';\nimport { notifyOutboundMessageSent } from '../features/typing-refresh.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 已移除,消息不再在此处合并\n // 所有消息都直接走完中间件链,由 bot.on('message') 处理转发\n // 并发控制由 OpenClaw 框架的 session lane 机制处理\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 // 平台原始事件观测:一切未被 SDK 映射为 message/interaction 的推送\n // (入群申请、好友变动、reaction、media_upload_finish、未来新增类型)\n // 都走这里 —— 此前被静默丢弃,是入站诊断的主要盲区\n this.bot.on('rawEvent', (evt: { eventType?: string; data?: unknown }) => {\n let preview = '{}';\n try {\n preview = JSON.stringify(evt?.data ?? {});\n } catch {\n preview = '<unserializable>';\n }\n if (preview.length > 300) preview = `${preview.slice(0, 300)}…`;\n this.log.info(`[rawEvent] t=${evt?.eventType ?? '?'} payload=${preview}`);\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 const { target: resolved, rollback } = this.attachMsgIdWithQuota(target, opts);\n if (!resolved.msgId) recordProactiveSend(this.account.accountId);\n try {\n const result = await withTimeout(\n this.bot.sendText(resolved, text),\n this.textTimeout, 'sendText',\n );\n this.notifyTypingRefresh(target);\n return result;\n } catch (err) {\n rollback();\n throw err;\n }\n }\n\n async sendMedia(\n target: ReplyTarget,\n source: string,\n opts?: SendOptions & { fileType?: MediaFileType },\n ): Promise<MessageResponse> {\n const { target: resolvedTarget, rollback } = this.attachMsgIdWithQuota(target, opts);\n if (!resolvedTarget.msgId) recordProactiveSend(this.account.accountId);\n const fileType = opts?.fileType ?? MediaFileType.IMAGE;\n const sourceOpts = resolveMediaSource(source);\n try {\n const result = await withTimeout(\n this.bot.sendMedia({ target: resolvedTarget, fileType, ...sourceOpts, content: opts?.text }),\n this.mediaTimeout, 'sendMedia',\n );\n this.notifyTypingRefresh(target);\n return result.message ?? { id: '', timestamp: Date.now() };\n } catch (err) {\n rollback();\n throw err;\n }\n }\n\n async sendVoice(\n target: ReplyTarget,\n source: { url?: string; base64?: string; localPath?: string },\n opts?: SendOptions,\n ): Promise<MessageResponse> {\n const { target: resolvedTarget, rollback } = this.attachMsgIdWithQuota(target, opts);\n if (!resolvedTarget.msgId) recordProactiveSend(this.account.accountId);\n\n const send = (params: Record<string, unknown>, label: string) =>\n withTimeout(\n this.bot.sendMedia({\n target: resolvedTarget,\n fileType: MediaFileType.VOICE,\n ...params,\n content: opts?.text,\n } as any),\n this.mediaTimeout, label,\n );\n try {\n let result: { message?: MessageResponse };\n if (source.base64) {\n result = await send({ fileData: source.base64 }, 'sendVoice(base64)');\n } else if (source.localPath) {\n result = await send({ localPath: source.localPath }, 'sendVoice(path)');\n } else {\n result = await send({ url: source.url! }, 'sendVoice(url)');\n }\n this.notifyTypingRefresh(target);\n return result.message ?? { id: '', timestamp: Date.now() };\n } catch (err) {\n rollback();\n throw err;\n }\n }\n\n async sendVideo(\n target: ReplyTarget,\n source: string,\n opts?: SendOptions,\n ): Promise<MessageResponse> {\n const { target: resolvedTarget, rollback } = this.attachMsgIdWithQuota(target, opts);\n if (!resolvedTarget.msgId) recordProactiveSend(this.account.accountId);\n const sourceOpts = resolveMediaSource(source);\n try {\n const result = await withTimeout(\n this.bot.sendMedia({ target: resolvedTarget, fileType: MediaFileType.VIDEO, ...sourceOpts, content: opts?.text }),\n this.mediaTimeout, 'sendVideo',\n );\n this.notifyTypingRefresh(target);\n return result.message ?? { id: '', timestamp: Date.now() };\n } catch (err) {\n rollback();\n throw err;\n }\n }\n\n async sendFile(\n target: ReplyTarget,\n source: string,\n opts?: SendOptions & { fileName?: string },\n ): Promise<MessageResponse> {\n const { target: resolvedTarget, rollback } = this.attachMsgIdWithQuota(target, opts);\n if (!resolvedTarget.msgId) recordProactiveSend(this.account.accountId);\n const sourceOpts = resolveMediaSource(source);\n try {\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 this.notifyTypingRefresh(target);\n return result.message ?? { id: '', timestamp: Date.now() };\n } catch (err) {\n rollback();\n throw err;\n }\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 /**\n * 消息发送成功后通知活跃的 typing 会话补发续期。\n * QQ 客户端收到机器人消息(含思维链等中间输出)会终止\"正在输入\"\n * 显示;若框架任务仍在进行,typing 中间件会在 5s 后补发恢复显示。\n */\n private notifyTypingRefresh(target: ReplyTarget): void {\n notifyOutboundMessageSent(this.account.accountId, target.scope, target.targetId);\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, target: { scope: string; targetId?: string }, mediaKind?: string): void => {\n // 出站 id 先登记回声表(不依赖 ext_info.ref_idx 是否返回),\n // 供 inboundGuard 识别平台回投的 bot 自身消息\n recordOutboundMessageId(accountId, msg.id);\n\n // 修复运算符优先级问题:当 content 非空时直接使用,否则回退到媒体标签\n let finalContent = content;\n if (!content && mediaKind) {\n finalContent = mediaKind === 'voice' ? '[语音]'\n : mediaKind === 'image' ? '[图片]'\n : mediaKind === 'video' ? '[视频]'\n : mediaKind === 'file' ? '[文件]'\n : `[${mediaKind}]`;\n }\n\n // rolling 历史模式:群出站记入 historyBuffer(isBot 标记,\n // 供\"裁剪到最后一条 bot 发言\"定位;historyLimit=0 或 room_event 群\n // ——上下文以框架 transcript 为准——时 no-op)\n if (\n target.scope === 'group' &&\n target.targetId &&\n resolveGroupConfigFromAccount(this.account, target.targetId).unmentionedInbound !== 'room_event'\n ) {\n recordOutboundToGroupHistory(accountId, target.targetId, {\n messageId: msg.id,\n senderId: appId,\n senderName,\n content: finalContent || '[消息]',\n }, resolveGroupConfigFromAccount(this.account, target.targetId).historyLimit);\n }\n\n const refIdx = msg.ext_info?.ref_idx;\n if (!refIdx) return;\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: target.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);\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 recordOutboundMessageId(accountId, result?.id);\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 /**\n * 配额感知的 msg_id 挂接(被动优先)。\n *\n * - 显式 msgId(opts.msgId):上游 adapter 已做配额记账(quotaReserved),\n * 直接透传,不重复消费。\n * - 无显式 msgId:尝试挂 msgid-cache 最新条目;挂接前经 quota-manager\n * 原子预检+扣减——配额耗尽则不挂(降级主动,避免平台 40034128 硬失败),\n * API 抛错时由调用方 rollback 释放已扣减的额度。\n */\n private attachMsgIdWithQuota(\n target: ReplyTarget,\n opts?: SendOptions,\n ): { target: ReplyTarget; rollback: () => void } {\n if (opts?.msgId) {\n return { target: { ...target, msgId: opts.msgId }, rollback: () => {} };\n }\n const cached = getCachedMsgId(target.scope, target.targetId);\n if (!cached) return { target, rollback: () => {} };\n\n const reservation = checkAndConsumePassiveReplyQuota({\n accountId: this.account.accountId,\n msgId: cached,\n scope: target.scope,\n });\n if (!reservation.canReply) {\n // INFO:降级主动消耗每日预算,是出站行为的可观测分界点\n this.log.info?.(`[quota] passive quota exhausted for msgId=${cached}; falling back to proactive send`);\n return { target, rollback: () => {} };\n }\n return { target: { ...target, msgId: cached }, rollback: reservation.rollback };\n }\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","/**\n * SDK 中间件编排\n *\n * 根据账户配置组装 SDK 内置中间件链。\n * 中间件负责过滤和上下文富化。\n * \n * 并发控制说明(群聊/私聊差异化):\n * - 群聊:使用消息合并中间件,所有消息都应该被处理,快速消息应该被合并\n * - 私聊:依赖 OpenClaw 框架的 session lane 机制,用户可以\"插嘴\"(新消息取消旧消息)\n * - 这样与 Telegram 等其他 channel 保持一致\n */\nimport type { QQBot, MiddlewareContext } from '@tencent-connect/qqbot-nodejs';\nimport {\n messageFilter,\n contentSanitizer,\n rateLimiter,\n mentionGate,\n quoteRef,\n historyBuffer,\n envelopeFormatter,\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 { inboundGuard } from '../middleware/inbound-guard.js';\nimport { secretCapture } from '../middleware/secret-capture.js';\nimport { c2cTypingIndicator } from '../middleware/typing.js';\nimport { stripMentionText } from '../utils/mention.js';\nimport { resolveGroupConfigFromAccount } from '../config.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 // skipSelfEcho 必须开启:SDK 注明 QQ 会把 bot 自身出站消息回投为入站\n // 事件(群聊尤其常见,按 author.bot 识别);私聊回声无该标记,\n // 由 inboundGuard 的出站 id 比对兜底\n bot.use(messageFilter({ skipSelfEcho: true }));\n\n // 2.5 入站事件守卫:出站回声(c2c)+ 重复推送长窗口去重 + 空内容事件\n // 三个检查都打 INFO 日志留痕;详见 src/middleware/inbound-guard.ts\n bot.use(inboundGuard({ accountId: account.accountId }));\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 // room_event 群返回 undefined 跳过记录——该模式下每条消息已作为入站事件\n // 进框架持久化 transcript,插件历史快照会造成双份上下文\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 if (resolveGroupConfigFromAccount(account, gid).unmentionedInbound === 'room_event') {\n return undefined;\n }\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 // isImplicitMention:引用 bot 出站消息 = 隐式被点名(对齐 telegram\n // reply-to-bot 语义)。ref-index get 为同步内存查询,可直接放同步回调;\n // 仅全量模式(GROUP_MESSAGE_CREATE)下未 @ 消息才可能带引用进到这里。\n bot.use(mentionGate({\n isImplicitMention: (ctx) => {\n const msg = ctx.message as { refMsgIdx?: string };\n if (!msg?.refMsgIdx) return false;\n try {\n const entry = getPersistedRefIndexStore(account.accountId).get(msg.refMsgIdx) as\n | { isBot?: boolean }\n | undefined;\n return entry?.isBot === true;\n } catch (err) {\n ctx.log.debug?.(`[mention] implicit quote-bot check failed: ${err instanceof Error ? err.message : String(err)}`);\n return false;\n }\n },\n }));\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 // 默认启用保守阈值(20/min、60/min、300/min),正常使用不会触发;\n // 触发即 INFO 留痕(不自动回复,避免烧被动配额/刷屏)。账号级\n // channels.qqbot.rateLimit 可覆盖或 {enabled:false} 关闭。\n {\n const rl = account.config.rateLimit ?? {};\n if (rl.enabled !== false) {\n bot.use(rateLimiter({\n perSender: rl.perSender ?? { max: 20, windowMs: 60_000 },\n perGroup: rl.perGroup ?? { max: 60, windowMs: 60_000 },\n global: rl.global ?? { max: 300, windowMs: 60_000 },\n onLimit: (ctx, tier) => {\n ctx.log.info?.(\n `[rate-limit] dropped (${tier}) sender=${(ctx.message as any).senderId ?? '-'} group=${(ctx.message as any).groupOpenid ?? '-'}`,\n );\n },\n }));\n }\n }\n\n // 9. 斜杠命令(命令匹配后直接 reply + stop)\n // 依赖:ctx.state.policy(policyInjector, #3)、ctx.message.*(原始消息)\n const slash = slashCommand({ commands: buildCommandList(account, { getRuntime: opts.getRuntime }) });\n bot.use(slash.middleware);\n\n // 9.5 密钥捕获 — pending 密钥输入时拦截该 c2c 用户的下一条文本消息,\n // 就地执行 openclaw secrets store set 并直接回执,消息不进框架\n // (密钥绝不进入 AI 转录)。放在斜杠命令之后:/bot-* 优先可用;\n // 拦截后不再触发 typing / envelope;多问题 ask_user 答案消息红线放行\n bot.use(secretCapture({ accountId: account.accountId }));\n\n // 10. 群聊消息排队已完全交给框架队列(dispatch 侧按 coalesce.enabled 传\n // queueModeOverride collect/followup),插件内不再做 busy-buffering。\n\n // 11. C2C 输入状态指示器(配额感知:优先占被动回复配额,耗尽后与回复\n // 消息一样降级为主动发送;续期间隔默认 20s 且不低于 20s,\n // 详见 src/middleware/typing.ts)\n const typingCfg = account.config.typing;\n if (typingCfg?.enabled !== false) {\n bot.use(c2cTypingIndicator({\n accountId: account.accountId,\n intervalMs: typingCfg?.intervalMs,\n }));\n }\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","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","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","/**\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 = \"@jerryliang122/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';\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 { 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://github.com/jerryliang122/qqbot-openclaw/blob/main/CHANGELOG.md';\nconst GITHUB_URL = 'https://github.com/jerryliang122/qqbot-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 { 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 { 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'];\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']) {\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","import type { SlashCommand, SlashCommandHandlerContext } from '@tencent-connect/qqbot-nodejs';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport { resolveGroupConfigFromAccount } from '../config.js';\nimport { getGroupModeFacts } from '../features/group-mode-store.js';\nimport { getProactiveUsage } from '../features/proactive-budget.js';\n\n/** /bot-group-info — 展示当前群的推送模式推断与生效配置(排障用) */\nexport function botGroupInfo(account: ResolvedQQBotAccount): SlashCommand {\n return {\n name: 'bot-group-info',\n description: '查看当前群的推送模式与生效配置',\n scope: 'group',\n usage: '/bot-group-info 在群内发送,查看该群的推送模式推断与生效配置',\n handler: async (ctx: SlashCommandHandlerContext) => {\n const groupOpenid = (ctx.message as { groupOpenid?: string }).groupOpenid;\n if (!groupOpenid) return '❌ 仅可在群聊中使用';\n\n const facts = getGroupModeFacts(account.accountId, groupOpenid);\n const cfg = resolveGroupConfigFromAccount(account, groupOpenid);\n const usage = getProactiveUsage(account.accountId);\n\n const modeLine = !facts\n ? '推送模式:尚未观测到事件(发一条群消息后可推断)'\n : facts.mode === 'full'\n ? '推送模式:**全量**(群主开启了\"接收所有消息\",bot 收到群里每条消息)'\n : '推送模式:**AT 系**(仅被 @ 时收到消息;可能带最近 N 条上下文,也可能不带)';\n\n const contextLine = facts?.sawMsgElements\n ? '上下文证据:曾收到 msg_elements(最近消息记录/引用内容)'\n : '上下文证据:未收到过 msg_elements(纯 AT 模式或用户从未引用)';\n\n return [\n `🤖 群信息(${groupOpenid.slice(0, 8)}…)`,\n '',\n modeLine,\n contextLine,\n '',\n `requireMention:${cfg.requireMention ? '是(需 @ 才响应)' : '否(所有消息都响应)'}`,\n `未 @ 消息入站:${cfg.unmentionedInbound === 'room_event' ? '**room_event**(被动房间事件进框架,AI 只读、想发言走主动 message 工具)' : '拦截(只进历史,不上报)'}`,\n `排队策略:框架队列(${cfg.coalesce.enabled ? 'collect 合并批处理' : 'followup 排队不合并'})`,\n `历史缓存:${cfg.historyLimit} 条(0=禁用,模式 ${cfg.historyMode === 'rolling' ? 'rolling(bot 发言计入)' : 'clear(回复后清空)'})`,\n `工具策略:${cfg.toolPolicy}`,\n '',\n `今日主动消息用量:${usage.count}/${usage.limit}(被动回复不计数;异常增长说明 msgid-cache 常过期或被动配额常耗尽)`,\n ].join('\\n');\n },\n };\n}\n","/**\n * 群推送模式推断存储(内存,跨重启不保留)\n *\n * 群主把 bot 拉进群时会选推送模式(AT 纯 / AT+最近N≤10 / 全量),这是群主侧\n * 的设置,插件不可控、同账号各群混合存在。平台只通过事件类型暴露这个事实:\n * - GROUP_AT_MESSAGE_CREATE → AT 系(纯 AT 或 AT+最近N,两者无法区分)\n * - GROUP_MESSAGE_CREATE → 全量模式(bot 像普通群成员收到所有消息)\n *\n * 推断结果供 /bot-group-info 展示与排障(\"这个群为什么没上下文\"——群主设的\n * 是纯 AT 模式)。模式变化打 INFO 日志留痕。\n */\n\n/** 平台推送模式(按事件类型推断) */\nexport type GroupPushMode = 'at' | 'full';\n\nexport interface GroupModeFacts {\n /** 推断的推送模式(按最近一次事件类型) */\n mode: GroupPushMode;\n /** 最近一次见到的事件类型(原文保留) */\n lastEventType: string;\n /** 曾见过 msg_elements(AT+最近N 的上下文载体,或用户引用/转发消息) */\n sawMsgElements: boolean;\n /** 首次/最近观测时间(epoch ms) */\n firstSeenAt: number;\n updatedAt: number;\n}\n\nconst MAX_TRACKED_GROUPS = 1000;\n\nconst store = new Map<string, GroupModeFacts>();\n\nfunction storeKey(accountId: string, groupOpenid: string): string {\n return `${accountId}:${groupOpenid}`;\n}\n\n/** 记录一次群事件观测;推断模式变化时返回新模式(供调用方打日志),否则 null */\nexport function recordGroupEvent(\n accountId: string,\n groupOpenid: string,\n eventType: string,\n hasMsgElements: boolean,\n): GroupPushMode | null {\n const key = storeKey(accountId, groupOpenid);\n const now = Date.now();\n const mode: GroupPushMode = eventType === 'GROUP_MESSAGE_CREATE' ? 'full' : 'at';\n const prev = store.get(key);\n\n if (prev) {\n const modeChanged = prev.mode !== mode;\n prev.lastEventType = eventType;\n prev.mode = mode;\n prev.sawMsgElements = prev.sawMsgElements || hasMsgElements;\n prev.updatedAt = now;\n return modeChanged ? mode : null;\n }\n\n if (store.size >= MAX_TRACKED_GROUPS) {\n // 简单防膨胀:丢弃最早观测的一半(推断数据可随时重建,不值得引入 LRU 复杂度)\n const keys = [...store.entries()].sort((a, b) => a[1].updatedAt - b[1].updatedAt);\n for (const [k] of keys.slice(0, Math.floor(keys.length / 2))) store.delete(k);\n }\n store.set(key, {\n mode,\n lastEventType: eventType,\n sawMsgElements: hasMsgElements,\n firstSeenAt: now,\n updatedAt: now,\n });\n return null; // 首次观测不视为「变化」\n}\n\nexport function getGroupModeFacts(accountId: string, groupOpenid: string): GroupModeFacts | undefined {\n return store.get(storeKey(accountId, groupOpenid));\n}\n\n/** 测试用:清空存储 */\nexport function _resetGroupModeStore(): void {\n store.clear();\n}\n","/**\n * 主动消息每日预算观测\n *\n * QQ Bot 平台主动消息(不带 msg_id)有每日额度(接收方维度约 1000 条/天),\n * 被动回复(带 msg_id)不占此预算。插件全链路被动优先(msgid-cache 挂接 +\n * 配额预检),主动仅在「静群超 TTL」或「被动配额耗尽」时触发——本模块对\n * 这些残余的主动发送按账号计数,接近阈值告警,供排障与预算复核。\n */\nimport { createPluginLogger } from '../utils/plugin-logger.js';\n\nconst log = createPluginLogger({ prefix: '[proactive-budget]' });\n\n/** 平台每日主动消息额度(保守取 1000) */\nexport const DAILY_PROACTIVE_LIMIT = 1000;\n\n/** 告警阈值比例(80%)与之后的步进 */\nconst WARN_RATIO = 0.8;\nconst WARN_EVERY = 100;\n\nconst counters = new Map<string, { date: string; count: number }>();\n\nfunction today(): string {\n return new Date().toISOString().slice(0, 10);\n}\n\n/** 记录一次主动发送(无 msg_id 的出站);跨天自动重置 */\nexport function recordProactiveSend(accountId: string): void {\n let counter = counters.get(accountId);\n if (!counter || counter.date !== today()) {\n counter = { date: today(), count: 0 };\n counters.set(accountId, counter);\n }\n counter.count += 1;\n\n const warnAt = Math.floor(DAILY_PROACTIVE_LIMIT * WARN_RATIO);\n if (counter.count === warnAt || (counter.count > warnAt && counter.count % WARN_EVERY === 0)) {\n log.warn(\n `proactive daily budget ${counter.count}/${DAILY_PROACTIVE_LIMIT} used (accountId=${accountId}); ` +\n `passive replies (msg_id) don't count — check msgid-cache TTL / passive quota if unexpected`,\n );\n }\n}\n\n/** 查询当日用量(诊断/命令展示用) */\nexport function getProactiveUsage(accountId: string): { date: string; count: number; limit: number } {\n const counter = counters.get(accountId);\n return {\n date: counter?.date ?? today(),\n count: counter?.date === today() ? counter.count : 0,\n limit: DAILY_PROACTIVE_LIMIT,\n };\n}\n\n/** 测试用:清空计数 */\nexport function _resetProactiveBudget(): void {\n counters.clear();\n}\n","/**\n * Pairing Runtime — openclaw/plugin-sdk/conversation-runtime\n *\n * readAllowFromStore / issueChallenge / buildReply 直接来自稳定 subpath。\n *\n * approveCode 例外:`approveChannelPairingCode` 在 2026.9.2 未从任何\n * plugin-sdk subpath 导出(只存在于内部 pairing-store chunk 与\n * `openclaw pairing approve` CLI),走与网关同源的 CLI 执行\n * (见 secret-store-cli 的 resolveOpenClawCli 同源约束)。\n */\nimport { spawn } from 'node:child_process';\nimport { createRequire } from 'node:module';\nimport * as path from 'node:path';\nimport { resolveOpenClawCli } from '../features/secret-store-cli.js';\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 /** 经 `openclaw pairing approve` 执行;返回是否成功 */\n approveCode: (params: { channel: string; code: string; accountId?: string }) => Promise<{ approved: boolean }>;\n}\n\nlet _req: NodeRequire | undefined;\n\nfunction getReq(): NodeRequire {\n _req ??= createRequire(\n typeof __filename !== 'undefined' ? __filename : path.join(process.cwd(), 'noop.js'),\n );\n return _req;\n}\n\nlet _mod: {\n readChannelAllowFromStore: (channel: string, env?: unknown, accountId?: string) => Promise<string[]>;\n upsertChannelPairingRequest: (params: { channel: string; id: string; accountId?: string }) => Promise<{ code: string }>;\n buildPairingReply: (params: { channel: string; idLine?: string; code: string }) => string;\n} | undefined;\n\nfunction loadMod(): NonNullable<typeof _mod> {\n _mod ??= getReq()('openclaw/plugin-sdk/conversation-runtime');\n return _mod!;\n}\n\n/** 获取 Pairing API(conversation-runtime 为稳定导出,加载失败即抛错) */\nexport function getPairingApi(): PairingApi {\n const mod = loadMod();\n return {\n readAllowFromStore: (params) =>\n mod.readChannelAllowFromStore(params.channel, undefined, params.accountId),\n issueChallenge: (params) =>\n mod.upsertChannelPairingRequest({\n channel: params.channel,\n id: params.id,\n accountId: params.accountId,\n }).then((r) => ({ code: r.code })),\n buildReply: (params) =>\n mod.buildPairingReply({\n channel: params.channel,\n idLine: '', // qqbot 无额外 ID 信息,留空即可\n code: params.code,\n }),\n approveCode: (params) => approveViaCli(params),\n };\n}\n\nconst APPROVE_TIMEOUT_MS = 30_000;\n\nasync function approveViaCli(params: {\n channel: string;\n code: string;\n accountId?: string;\n}): Promise<{ approved: boolean }> {\n const cli = resolveOpenClawCli();\n const args = [\n ...cli.args,\n 'pairing',\n 'approve',\n '--channel',\n params.channel,\n ...(params.accountId ? ['--account', params.accountId] : []),\n params.code,\n ];\n return new Promise((resolve) => {\n const child = spawn(cli.cmd, args, { stdio: ['ignore', 'pipe', 'pipe'] });\n const timer = setTimeout(() => child.kill('SIGKILL'), APPROVE_TIMEOUT_MS);\n child.on('error', () => {\n clearTimeout(timer);\n resolve({ approved: false });\n });\n child.on('close', (codeNum) => {\n clearTimeout(timer);\n resolve({ approved: codeNum === 0 });\n });\n });\n}\n","/**\n * openclaw secrets store CLI 执行器\n *\n * 把用户在聊天中发送的密钥值通过 `openclaw secrets store set` 写入\n * OpenClaw 密钥库(CLI 直写本地共享状态,绕过 Gateway)。\n *\n * kind 规则(openclaw 2026.9+ 两级模型):\n * - env:代理可读环境变量,`--value` 传参,事后可 `secrets store get` 读回\n * - secret:「受保护的机密」,write-only 代理不可读,CLI 拒绝 `--value`,\n * 必须走 `--value-file -`(stdin)写入\n *\n * 聊天卡片流程(qqbot_secret_input → secretCapture)**恒用 env**:\n * 卡片收来的值本就留在 QQ 聊天记录里,且 AI 随后要读取使用,存成\n * write-only 的 secret 反而用不了(2026-08-30 用户反馈)。执行器保留\n * secret/stdin 分支仅作通用能力,当前没有调用方产生该 kind。\n *\n * 安全约束:\n * - spawn 恒为 args 数组 + shell 缺省(不经过 shell),值不参与任何\n * 字符串拼接 → 无注入面;name 另有白名单正则双保险\n * - 所有输出/错误经 scrubSecret() 清洗后才会返回或落日志,值绝不外泄\n *\n * 注:本文件直接 import node:child_process。openclaw 插件安装扫描器\n * (dangerous-exec 规则)会记一条 critical 告警,但本地安装不会被拦截\n * (硬拦截仅依赖包名黑名单)——见 AGENTS.md 已知项。\n */\nimport { spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { createRequire } from 'node:module';\nimport { fileURLToPath } from 'node:url';\n\n// CJS 兼容:__filename 在 CJS 中可用,ESM 中使用 import.meta.url\nconst __file = typeof __filename !== 'undefined'\n ? __filename\n : fileURLToPath(import.meta.url);\nconst require_ = createRequire(__file);\n\nexport type SecretStoreKind = 'env' | 'secret';\n\n/** CLI 同款命名规则:^[A-Z][A-Z0-9_]{0,127}$ */\nconst SECRET_NAME_RE = /^[A-Z][A-Z0-9_]{0,127}$/;\n\n/** CLI 单值上限 64 KiB(超出 CLI 直接 exit 2,插件侧提前拦截) */\nexport const SECRET_VALUE_MAX_BYTES = 65_536;\n\nconst SET_TIMEOUT_MS = 30_000;\nconst RELOAD_TIMEOUT_MS = 15_000;\n/** 子进程输出保留上限(诊断用,已 scrub) */\nconst OUTPUT_CAP_CHARS = 2_000;\n\n// ── 校验 / 掩码 ────────────────────────────────────────────────────────────\n\nexport function isValidSecretName(name: string): boolean {\n return SECRET_NAME_RE.test(name);\n}\n\n/** 展示用掩码:>12 字符显示前 4 + … + 后 4,否则 *** */\nexport function maskSecret(value: string): string {\n if (value.length <= 12) return '***';\n return `${value.slice(0, 4)}…${value.slice(-4)}`;\n}\n\n/** 把文本中出现的密钥值整体替换为 ***(错误输出/日志清洗用) */\nexport function scrubSecret(text: string, value: string): string {\n if (!value) return text;\n return text.split(value).join('***');\n}\n\n/** 纯函数:拼装 secrets store set 的 argv(不含 CLI 入口本身),便于测试 */\nexport function buildSecretsStoreSetArgs(\n name: string,\n kind: SecretStoreKind,\n value: string,\n): string[] {\n const args = ['secrets', 'store', 'set', name, '--kind', kind];\n // secret 强制 stdin(CLI 拒绝 --value);env 走 --value\n return kind === 'secret' ? [...args, '--value-file', '-'] : [...args, '--value', value];\n}\n\n// ── CLI 入口解析 ───────────────────────────────────────────────────────────\n\nexport interface CliCommand {\n cmd: string;\n /** CLI 入口自身的前置参数(如 [cli.js]) */\n args: string[];\n}\n\nlet cachedCli: CliCommand | undefined;\n\n/**\n * 解析 openclaw CLI 命令形态。\n *\n * 优先级:\n * 1. 正在运行的网关自身的 openclaw 安装(process.argv[1] 向上定位包根)。\n * CLI 直写本地 state 库,而库 schema 归网关版本所有,CLI 必须与网关同源;\n * dev 场景插件仓库 node_modules 里常 pin 着旧版副本,旧 CLI 会拒写新\n * schema 库(2026-08-30:dev 副本 2026.8.1-beta.3/schema 9 拒写全局\n * 2026.9.1-beta.1/schema 12 的库,exit 1)。\n * 2. require.resolve('openclaw')(生产安装经 preload symlink 指向全局安装)。\n * 两种解析均得 `[node, cli.mjs]`(跨平台、不依赖 PATH);兜底 PATH 上的\n * `openclaw`。\n */\nexport function resolveOpenClawCli(): CliCommand {\n if (cachedCli) return cachedCli;\n const gatewayEntry = process.argv[1];\n if (gatewayEntry) {\n const cli = cliFromPackageRoot(\n findOpenClawPackageRoot(path.dirname(path.resolve(gatewayEntry))),\n );\n if (cli) {\n cachedCli = cli;\n return cachedCli;\n }\n }\n try {\n const entryPath = require_.resolve('openclaw');\n const cli = cliFromPackageRoot(findOpenClawPackageRoot(path.dirname(entryPath)));\n if (cli) {\n cachedCli = cli;\n return cachedCli;\n }\n } catch {\n // openclaw 不可解析(理论上不会——preload 保证 peer 可用)→ 走 PATH\n }\n cachedCli = { cmd: 'openclaw', args: [] };\n return cachedCli;\n}\n\n/** 从起始目录向上查找 name 为 openclaw 的包根(最多 6 层,跳过无关包) */\nfunction findOpenClawPackageRoot(startDir: string): string | undefined {\n let dir = startDir;\n for (let i = 0; i < 6; i += 1) {\n const pkgPath = path.join(dir, 'package.json');\n if (fs.existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')) as { name?: string };\n if (pkg.name === 'openclaw') return dir;\n } catch {\n // 损坏的 package.json → 继续向上\n }\n }\n const parent = path.dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n return undefined;\n}\n\n/** 读取包根 bin 字段,构造 [node, <bin>] 形态的 CLI 命令;不可用返回 undefined */\nfunction cliFromPackageRoot(pkgRoot: string | undefined): CliCommand | undefined {\n if (!pkgRoot) return undefined;\n try {\n const pkg = JSON.parse(fs.readFileSync(path.join(pkgRoot, 'package.json'), 'utf8')) as {\n bin?: Record<string, string> | string;\n };\n const binField = pkg.bin;\n const binEntry =\n typeof binField === 'string'\n ? binField\n : binField?.openclaw ?? Object.values(binField ?? {})[0];\n if (binEntry) {\n return { cmd: process.execPath, args: [path.resolve(pkgRoot, binEntry)] };\n }\n } catch {\n // 不可读 → 落下一优先级\n }\n return undefined;\n}\n\n/** 测试辅助:清除缓存的 CLI 解析结果 */\nexport function __resetCachedCliForTest(): void {\n cachedCli = undefined;\n}\n\n// ── 子进程执行 ─────────────────────────────────────────────────────────────\n\n/** 子进程流的最小结构约定(不依赖 @types/node 全局命名空间) */\ninterface MinimalReadable {\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n on(event: string, listener: (...args: any[]) => void): unknown;\n}\n\ninterface MinimalWritable {\n write(chunk: string | Uint8Array): unknown;\n end(cb?: () => void): unknown;\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n on?(event: string, listener: (...args: any[]) => void): unknown;\n}\n\n/** spawn 返回对象的最小结构约定(真实 ChildProcess 结构兼容) */\nexport interface SpawnLikeChild {\n stdin: MinimalWritable | null;\n stdout: MinimalReadable | null;\n stderr: MinimalReadable | null;\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n on(event: string, listener: (...args: any[]) => void): SpawnLikeChild;\n kill(signal?: string | number): void;\n}\n\nexport type SpawnFn = (\n cmd: string,\n args: readonly string[],\n options: { stdio: ['pipe', 'pipe', 'pipe'] },\n) => SpawnLikeChild;\n\nexport interface SecretsStoreSetResult {\n ok: boolean;\n exitCode: number | null;\n timedOut: boolean;\n /** spawn 本身失败(ENOENT 等),已 scrub */\n error?: string;\n /** stdout + stderr 合并摘要,已 scrub */\n output: string;\n}\n\ninterface ExecCliParams {\n cliArgs: readonly string[];\n /** secret kind 时经 stdin 写入的值 */\n stdinValue?: string;\n timeoutMs: number;\n /** 参与输出清洗的密钥值 */\n secret: string;\n /** 测试注入:覆盖 CLI 入口解析 */\n cliOverride?: CliCommand;\n}\n\nfunction execCli(\n params: ExecCliParams,\n deps: { spawnFn?: SpawnFn } = {},\n): Promise<SecretsStoreSetResult> {\n const { spawnFn = spawn as unknown as SpawnFn } = deps;\n const cli = params.cliOverride ?? resolveOpenClawCli();\n return new Promise((resolve) => {\n let child: SpawnLikeChild;\n try {\n child = spawnFn(cli.cmd, [...cli.args, ...params.cliArgs], {\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n } catch (err) {\n resolve({\n ok: false,\n exitCode: null,\n timedOut: false,\n error: scrubSecret(\n `无法启动 openclaw CLI: ${err instanceof Error ? err.message : String(err)}`,\n params.secret,\n ),\n output: '',\n });\n return;\n }\n\n let stdout = '';\n let stderr = '';\n let timedOut = false;\n let settled = false;\n\n const timer = setTimeout(() => {\n timedOut = true;\n child.kill('SIGKILL');\n }, params.timeoutMs);\n timer.unref?.();\n\n const finish = (ok: boolean, exitCode: number | null, error?: string) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n const output = scrubSecret(\n `${stdout}${stderr ? (stdout ? '\\n' : '') + stderr : ''}`.trim().slice(0, OUTPUT_CAP_CHARS),\n params.secret,\n );\n resolve({ ok, exitCode, timedOut, error: error ? scrubSecret(error, params.secret) : undefined, output });\n };\n\n child.stdout?.on('data', (chunk: Buffer | string) => {\n if (stdout.length < OUTPUT_CAP_CHARS) stdout += String(chunk);\n });\n child.stderr?.on('data', (chunk: Buffer | string) => {\n if (stderr.length < OUTPUT_CAP_CHARS) stderr += String(chunk);\n });\n child.on('error', (err: unknown) => {\n finish(false, null, `openclaw CLI 执行失败: ${err instanceof Error ? err.message : String(err)}`);\n });\n child.on('close', (code: unknown) => {\n finish(!timedOut && code === 0, typeof code === 'number' ? code : null);\n });\n\n if (params.stdinValue !== undefined) {\n child.stdin?.on?.('error', () => {/* EPIPE:CLI 提前退出,交给 close 收尾 */});\n child.stdin?.write(params.stdinValue);\n child.stdin?.end();\n } else {\n child.stdin?.end();\n }\n });\n}\n\n/** 执行 openclaw secrets store set(值的合法性校验由调用方先行完成) */\nexport async function runSecretsStoreSet(\n params: { name: string; kind: SecretStoreKind; value: string },\n deps: { spawnFn?: SpawnFn; cli?: CliCommand } = {},\n): Promise<SecretsStoreSetResult> {\n return execCli(\n {\n cliArgs: buildSecretsStoreSetArgs(params.name, params.kind, params.value),\n stdinValue: params.kind === 'secret' ? params.value : undefined,\n timeoutMs: SET_TIMEOUT_MS,\n secret: params.value,\n cliOverride: deps.cli,\n },\n deps,\n );\n}\n\n/** best-effort 执行 openclaw secrets reload(写入后被 SecretRef 引用的名字才会即时生效) */\nexport async function runSecretsReload(\n deps: { spawnFn?: SpawnFn; cli?: CliCommand } = {},\n): Promise<boolean> {\n const result = await execCli(\n { cliArgs: ['secrets', 'reload'], timeoutMs: RELOAD_TIMEOUT_MS, secret: '', cliOverride: deps.cli },\n deps,\n );\n return result.ok;\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\n try {\n const result = await api.approveCode({\n channel: 'qqbot',\n code,\n });\n\n if (!result?.approved) {\n return [\n `⚠️ 配对码 \\`${code}\\` 无效或已过期。`,\n '',\n '每个配对码有效期为 1 小时。',\n ].join('\\n');\n }\n\n return '✅ 已批准用户访问。';\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 { botGroupInfo } from './bot-group-info.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 botGroupInfo(account),\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, shouldUsePlatformAsr } 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 * 结构化媒体清单(全部附件,保持原始顺序)。\n * 供 ctx-builder 组装 core 正规媒体通道的 media facts(MediaKind: image|audio|video|document)。\n */\n media: InboundMediaEntry[];\n}\n\n/**\n * 单个入站媒体条目(对齐 core MediaKind/ChannelInboundMediaInput 语义)。\n * localPath 与 remoteUrl 二选一可能都没有(下载失败且无 URL 时)。\n */\nexport interface InboundMediaEntry {\n kind: 'image' | 'audio' | 'video' | 'document';\n localPath?: string;\n remoteUrl?: string;\n contentType?: string;\n filename?: string;\n /** 仅语音:STT/平台 ASR 产出真实转写文本时为 true */\n transcribed?: boolean;\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\nexport async function processAttachments(\n attachments: MessageAttachment[],\n cfg: Record<string, unknown>,\n log?: Log,\n): Promise<ProcessedAttachments> {\n const sttCfg = resolveSTTConfig(cfg);\n const usePlatformAsr = shouldUsePlatformAsr(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 const media: InboundMediaEntry[] = [];\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', filename: att.filename };\n }\n\n if (isVoice) {\n const transcript = await processVoiceAttachment(att, sttCfg, usePlatformAsr, 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, contentType: att.content_type };\n }\n return { type: 'other' as const, localPath: null, url: '', filename: att.filename ?? att.content_type, contentType: 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 media.push({\n kind: 'image',\n ...(result.localPath ? { localPath: result.localPath } : { remoteUrl: result.url }),\n contentType: result.contentType,\n filename: result.filename,\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 media.push({\n kind: 'audio',\n ...(result.transcript.localPath\n ? { localPath: result.transcript.localPath }\n : result.transcript.remoteUrl\n ? { remoteUrl: result.transcript.remoteUrl }\n : {}),\n contentType: 'audio/wav',\n transcribed: result.transcript.source !== 'fallback',\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 media.push({\n kind: kindFromContentType(result.contentType),\n ...(result.localPath ? { localPath: result.localPath } : result.url ? { remoteUrl: result.url } : {}),\n ...(result.contentType ? { contentType: result.contentType } : {}),\n ...(result.filename ? { filename: 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 media,\n };\n}\n\n/** content_type → core MediaKind(无法识别时归为 document) */\nfunction kindFromContentType(contentType: string | undefined): InboundMediaEntry['kind'] {\n if (contentType?.startsWith('video/')) return 'video';\n if (contentType?.startsWith('image/')) return 'image';\n if (contentType?.startsWith('audio/')) return 'audio';\n return 'document';\n}\n\n// ── 语音处理 ──\n\nasync function processVoiceAttachment(\n att: MessageAttachment,\n sttCfg: ReturnType<typeof resolveSTTConfig>,\n usePlatformAsr: boolean,\n audioPolicy: AudioPolicyResolved,\n log?: Log,\n): Promise<VoiceTranscript> {\n // 平台转写(asr_refer_text)仅在显式 asrFallback: true 时参与;\n // 缺省/false 时在所有场景下丢弃——包括 STT 未配置(语音落占位文本)\n // 与 STT 失败(不当兜底),三条泄漏路径(转写成功携带 / 转写失败回退 /\n // 下载失败回退)一并堵死。\n const rawAsrText = att.asr_refer_text?.trim() || undefined;\n const asrReferText = usePlatformAsr ? rawAsrText : undefined;\n // 远端 URL 兜底:优先 wav_url,其次原始 url\n const remoteUrl = normalizeUrl(att.voice_wav_url) || normalizeUrl(att.url) || undefined;\n\n // STT 未配置:占位文本;显式 asrFallback: true 时退回平台转写\n if (!sttCfg) {\n if (!usePlatformAsr && rawAsrText) {\n log?.info(`Voice: STT not configured; platform asr_refer_text discarded (asrFallback not enabled)`);\n }\n if (asrReferText) {\n log?.debug?.(`Voice: using asr_refer_text (STT not configured, asrFallback enabled)`);\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[]) ?? ['.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 * Low-level protocol exports for the QQ Open Platform.\n *\n * Most users should consume the high-level {@link QQBot} facade from\n * the package root. This entry point is intended for advanced users who\n * need to compose primitives directly.\n */\n\n// ---- Types ----\nexport * from \"./types.js\";\n\n// ---- API ----\nexport { ApiClient, type RequestOptions } from \"./api/api-client.js\";\nexport { TokenManager, type BackgroundRefreshOptions } from \"./api/token.js\";\nexport {\n MessageApi,\n type MessageApiConfig,\n getNextMsgSeq,\n} from \"./api/messages.js\";\nexport {\n MediaApi,\n type MediaApiConfig,\n type SanitizeFileNameFn,\n type UploadCacheAdapter,\n} from \"./api/media.js\";\nexport {\n ChunkedMediaApi,\n type ChunkedMediaApiConfig,\n type ChunkedMediaSource,\n type ChunkedUploadProgress,\n type UploadChunkedOptions,\n UploadDailyLimitExceededError,\n} from \"./api/media-chunked.js\";\nexport {\n withRetry,\n buildPartFinishPersistentPolicy,\n COMPLETE_UPLOAD_RETRY_POLICY,\n PART_FINISH_RETRY_POLICY,\n PART_FINISH_RETRYABLE_CODES,\n UPLOAD_PREPARE_FALLBACK_CODE,\n UPLOAD_RETRY_POLICY,\n type PersistentRetryPolicy,\n type RetryPolicy,\n} from \"./api/retry.js\";\nexport {\n channelMessagePath,\n dmMessagePath,\n gatewayPath,\n interactionPath,\n mediaUploadPath,\n messagePath,\n streamMessagePath,\n uploadCompletePath,\n uploadPartFinishPath,\n uploadPreparePath,\n} from \"./api/routes.js\";\n\n// ---- Gateway ----\nexport {\n FULL_INTENTS,\n GatewayCloseCode,\n GatewayEvent,\n GatewayOp,\n InteractionType,\n MAX_QUICK_DISCONNECT_COUNT,\n MAX_RECONNECT_ATTEMPTS,\n QUICK_DISCONNECT_THRESHOLD,\n RATE_LIMIT_DELAY,\n RECONNECT_DELAYS,\n} from \"./gateway/constants.js\";\nexport { decodeGatewayMessageData, readOptionalMessageSceneExt } from \"./gateway/codec.js\";\nexport { ReconnectState, type CloseAction } from \"./gateway/reconnect.js\";\nexport {\n dispatchEvent,\n type DispatchResult,\n type InboundMessage,\n type InboundAttachment,\n type InboundMsgElement,\n} from \"./gateway/event-dispatcher.js\";\nexport {\n GatewayConnection,\n type GatewayConnectionOptions,\n type PersistedSession,\n type SessionPersistencePort,\n} from \"./gateway/gateway-connection.js\";\n\n// ---- Utils ----\nexport { formatDuration, formatErrorMessage, formatFileSize } from \"./utils/format.js\";\nexport {\n CHUNKED_UPLOAD_MAX_SIZE,\n LARGE_FILE_THRESHOLD,\n MAX_UPLOAD_SIZE,\n MEDIA_FILE_TYPE_INFO,\n getFileTypeName,\n getImageMimeType,\n getMaxUploadSize,\n getMimeType,\n sanitizeFileName,\n} from \"./utils/file-utils.js\";\nexport { UploadCache, computeFileHash } from \"./utils/upload-cache.js\";\nexport {\n ReplyLimiter,\n type ReplyLimitResult,\n type ReplyLimiterConfig,\n} from \"./utils/reply-limiter.js\";\nexport {\n FUZZY_MEDIA_TAG_REGEX,\n SELF_CLOSING_TAG_REGEX,\n normalizeMediaTags,\n} from \"./utils/media-tags.js\";\nexport {\n TRANSCRIPT_SOURCE_LABELS,\n formatAttachmentTags,\n formatRefEntryForAgent,\n renderAttachmentTags,\n type AttachmentSummary,\n type RefAttachmentSummary,\n type RefIndexEntry,\n type RenderMode,\n type RenderOptions,\n} from \"./utils/ref-attachments.js\";\nexport {\n JsonlRefIndexStore,\n type JsonlRefIndexStoreOptions,\n type RefIndexStoreLogger,\n} from \"./utils/ref-index-store.js\";\nexport {\n FileSessionStore,\n type FileSessionStoreOptions,\n type SessionState,\n type SessionStoreLogger,\n} from \"./utils/session-store.js\";\nexport {\n MSG_TYPE_QUOTE,\n buildAttachmentSummaries,\n filterInternalMarkers,\n parseFaceTags,\n parseRefIndices,\n} from \"./utils/text-parsing.js\";\nexport {\n materializeForOneShotUpload,\n normalizeSource,\n openLocalFile,\n tryParseDataUrl,\n type MediaSource,\n type OpenedLocalFile,\n type RawMediaSource,\n} from \"./utils/media-source.js\";\nexport {\n DEFAULT_IMAGE_SIZE,\n extractQQBotImageSize,\n formatQQBotMarkdownImage,\n getImageSizeFromDataUrl,\n getImageSizeFromUrl,\n hasQQBotImageSize,\n parseImageSize,\n type ImageBytesFetcher,\n type ImageSize,\n} from \"./utils/image-size.js\";\nexport {\n checkSilkWasmAvailable,\n detectFfmpeg,\n isWindows,\n resetFfmpegCache,\n} from \"./utils/ffmpeg.js\";\nexport {\n audioFileToSilkBase64,\n convertSilkToWav,\n ffmpegToPCM,\n isAudioFile,\n isVoiceAttachment,\n loadSilkWasm,\n parseWavFallback,\n pcmToSilk,\n pcmToWav,\n shouldTranscodeVoice,\n stripAmrHeader,\n waitForFile,\n wasmDecodeMp3ToPCM,\n type AudioLogger,\n} from \"./utils/audio.js\";\nexport {\n decodeCronPayload,\n encodePayloadForCron,\n isCronReminderPayload,\n isMediaPayload,\n parseQQBotPayload,\n type CronReminderPayload,\n type MediaPayload,\n type ParseResult,\n type QQBotPayload,\n} from \"./utils/payload.js\";\nexport {\n TEXT_CHUNK_LIMIT,\n chunkText,\n type ChunkTextFn,\n} from \"./utils/text-chunk.js\";\nexport { formatVoiceText } from \"./utils/voice-text.js\";\nexport {\n decodeMediaPath,\n type MediaPathDecodeLogger,\n} from \"./utils/decode-media-path.js\";\nexport {\n IMAGE_MIME_TYPES,\n MEDIA_KIND_LABELS,\n detectMediaKind,\n getCleanExtension,\n isDataSource,\n isHttpSource,\n isImageFile,\n isRemoteOrDataSource,\n isVideoFile,\n type MediaKind,\n} from \"./utils/media-type.js\";\nexport {\n looksLikeQQBotTarget,\n normalizeTarget,\n parseTarget,\n targetToChatScope,\n type ParsedTarget,\n type TargetType,\n} from \"./utils/target.js\";\n\n// ---- Transport ----\nexport {\n WebhookTransport,\n NodeHttpWebhookServer,\n verifyWebhookSignature,\n signValidationResponse,\n ed25519Sign,\n type EventTransport,\n type EventTransportCallbacks,\n type WebhookTransportOptions,\n type WebhookServerAdapter,\n type WebhookRequest,\n type WebhookResponse,\n type WebhookRequestHandler,\n} from \"./transport/index.js\";\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 * Transport module — event receiving mechanisms.\n */\nexport type {\n EventTransport,\n EventTransportCallbacks,\n WebhookTransportOptions,\n WebSocketTransportOptions,\n WebhookServerAdapter,\n WebhookRequest,\n WebhookResponse,\n WebhookRequestHandler,\n} from \"./types.js\";\n\nexport { WebhookTransport } from \"./webhook.js\";\nexport { NodeHttpWebhookServer } from \"./webhook-server-node.js\";\nexport { verifyWebhookSignature, signValidationResponse, ed25519Sign } from \"./webhook-verify.js\";\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 * 平台转写(asr_refer_text)参与判定。\n * 仅当显式配置 channels.qqbot.stt.asrFallback: true 时保留平台转写;\n * 缺省、false 或 stt 块整体不存在时一律丢弃——包括 STT 未配置的场景\n * (此时语音消息落占位文本,而不是退回平台转写)。\n * 读取独立于 STT 凭证解析成败:stt 块无凭证但 asrFallback: true 仍生效。\n */\nexport function shouldUsePlatformAsr(cfg: Record<string, unknown>): boolean {\n const channels = asRecord(cfg.channels);\n const qqbot = asRecord(channels?.qqbot);\n return asRecord(qqbot?.stt)?.asrFallback === true;\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)\n *\n * 直接走 openclaw 的 saveRemoteMedia(含 SSRF 防护/重试/大小限制)。\n */\nimport { createRequire } from 'node:module';\nimport * as path from 'node:path';\n\nlet _req: NodeRequire | undefined;\n\n/**\n * 惰性 require 工厂。\n * CJS(tsup 产物)下用 __filename 锚定,与插件 node_modules 的解析路径一致;\n * ESM(tsx 直跑测试)下 __filename 不存在,锚定 process.cwd()。\n */\nfunction getReq(): NodeRequire {\n _req ??= createRequire(\n typeof __filename !== 'undefined' ? __filename : path.join(process.cwd(), 'noop.js'),\n );\n return _req;\n}\n\ntype SaveRemoteMedia = (opts: {\n url: string;\n subdir?: string;\n originalFilename?: string;\n maxBytes?: number;\n timeoutMs?: number;\n}) => Promise<{ path: string }>;\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 const mod = getReq()('openclaw/plugin-sdk/media-runtime') as { saveRemoteMedia: SaveRemoteMedia };\n return mod.saveRemoteMedia(opts);\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 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\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 = 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 ──\n const bodyContent = `${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(合并版):消息合并处理已移除——群消息合并完全交给框架队列\n * (coalesce.enabled → dispatch 侧 queueModeOverride collect/followup)。\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 上下文(引用内容本身来自 msg_elements[0] 时跳过该元素避免重复,\n // 其余元素——如 AT+最近N 模式平台附带的消息记录——仍照常渲染)\n const quoteConsumedFirstElement = quote?.source === 'msg_elements';\n const elementsCtx = buildMsgElementsContext(msg, quoteConsumedFirstElement);\n if (elementsCtx.length > 0) {\n lines.push(REF_BEGIN, ...elementsCtx, REF_END);\n }\n\n return lines.length > 0 ? `${lines.join('\\n')}\\n\\n` : '';\n}\n\n/** 检测平台预渲染文本(示例3形态:`=== 消息 1 ===` + `[消息内容]` 行) */\nfunction isPlatformPrerenderedContent(content: string): boolean {\n return /^===\\s*消息\\s*\\d+\\s*===/.test(content.trimStart()) && content.includes('[消息内容]');\n}\n\n/** 从 msg_elements 提取上下文(非引用消息也解析,如被回复的 bot 消息等) */\nfunction buildMsgElementsContext(msg: QQBotInboundMessage, skipFirstElement = false): string[] {\n const elements = msg.msgElements;\n if (!elements || elements.length === 0) return [];\n\n const lines: string[] = [];\n let index = 0;\n for (let i = 0; i < elements.length; i++) {\n if (skipFirstElement && i === 0) continue;\n const el = elements[i]!;\n const content = el.content?.trim();\n const attachments = Array.isArray(el.attachments) ? el.attachments : [];\n if (!content && attachments.length === 0) continue;\n index += 1;\n\n // 平台已预渲染的多消息文本(如聊天记录转发)直接透传,避免二次包裹\n if (content && isPlatformPrerenderedContent(content)) {\n lines.push(`=== 消息记录 ${index} ===`, content);\n continue;\n }\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 );\n for (const att of attachments) {\n const a = att as { url?: string; content_type?: string };\n const kind = a.content_type ?? 'attachment';\n const url = a.url ?? '';\n lines.push(`[附件] ${kind}${url ? `: ${url}` : ''}`);\n }\n lines.push(`[发送者] ${sender}`);\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 // room_event 群:门控放行全部消息(mentionGate 以 requireMention=false\n // 通过并保留 wasMentioned 标记),未 @/称呼/引用的消息在 dispatch 侧\n // 分类为 room_event 被动房间事件(仅全量模式群实际会有这类消息到达)\n const roomEvent = groupCfg.unmentionedInbound === 'room_event';\n policy.group = {\n requireMention: roomEvent ? false : groupCfg.requireMention,\n ignoreOtherMentions: groupCfg.ignoreOtherMentions,\n historyLimit: groupCfg.historyLimit,\n prompt: groupCfg.prompt,\n unmentionedInbound: groupCfg.unmentionedInbound,\n };\n }\n\n ctx.state.policy = policy;\n await next();\n };\n}\n","import { MemoryHistoryStore } from '@tencent-connect/qqbot-nodejs';\nimport type { HistoryStore, HistoryEntry } from '@tencent-connect/qqbot-nodejs';\n\n/** 出站历史条目(在 HistoryEntry 上附加 isBot 标记,rolling 模式裁剪用) */\nexport type OutboundHistoryEntry = HistoryEntry & { isBot: true };\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 完成后调用,historyMode=clear 语义) */\nexport function clearGroupHistory(accountId: string, groupId: string): void {\n _store?.clear?.(historyGroupKey(accountId, groupId));\n}\n\n/**\n * bot 出站消息记入群历史(historyMode=rolling 语义)。\n * 仅群消息回复需要;条目带 isBot=true,供裁剪定位\"bot 最后一条发言\"。\n */\nexport function recordOutboundToGroupHistory(\n accountId: string,\n groupOpenid: string,\n entry: { messageId: string; senderId: string; senderName?: string; content: string },\n limit: number,\n): void {\n if (limit <= 0) return;\n const historyEntry: OutboundHistoryEntry = {\n messageId: entry.messageId,\n senderId: entry.senderId,\n senderName: entry.senderName,\n content: entry.content,\n timestamp: Date.now(),\n isBot: true,\n };\n void _store?.append?.(historyGroupKey(accountId, groupOpenid), historyEntry, limit);\n}\n\n/**\n * rolling 模式裁剪:删除最后一条 bot 出站(含)之前的全部条目,\n * 保留其后的人类消息(下次 @ 时组包,AI 由此知道自己上次说到哪)。\n * 无 bot 出站记录时不动(保守:等价于不清)。返回保留的条数(供日志)。\n */\nexport function trimGroupHistoryAfterLastBot(accountId: string, groupId: string, limit: number): number {\n const store = _store;\n if (!store?.list || !store.clear || !store.append) return 0;\n const key = historyGroupKey(accountId, groupId);\n const listed = store.list(key, 1_000_000);\n if (listed instanceof Promise) return 0; // 异步后端不支持同步裁剪(当前为 MemoryHistoryStore 同步实现)\n const entries: HistoryEntry[] = listed;\n if (entries.length === 0) return 0;\n\n let lastBotIdx = -1;\n for (let i = entries.length - 1; i >= 0; i--) {\n if ((entries[i] as { isBot?: boolean }).isBot === true) {\n lastBotIdx = i;\n break;\n }\n }\n if (lastBotIdx < 0) return 0;\n\n const kept = entries.slice(lastBotIdx + 1);\n store.clear(key);\n for (const e of kept) {\n store.append(key, e, Math.max(limit, kept.length));\n }\n return kept.length;\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\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 * 出站消息回声登记\n *\n * SDK message-filter 文档注明:QQ 可能把机器人自己的出站消息回投为入站\n * 事件(群聊尤其常见)。c2c 事件不映射 author.bot,SDK 的 skipSelfEcho\n * 过滤对私聊回声无效 —— 这里按「近期出站消息 id」自行比对。\n *\n * 出站 id 来源:wrapBotSendForRefIndex(sendText / sendMedia / 流式\n * complete 的返回值 msg.id)。若平台回声携带新 id 而非原 id,比对会\n * miss —— 由 inbound-guard 的空内容/去重检查兜底。\n */\n\nconst TTL_MS = 30 * 60 * 1000; // 与 msgid-cache 的 c2c TTL 对齐\nconst MAX_ENTRIES = 2000;\n\nconst seen = new Map<string, number>(); // `${accountId}:${messageId}` -> 记录时间\n\nfunction prune(now: number): void {\n for (const [key, ts] of seen) {\n if (seen.size <= MAX_ENTRIES && now - ts <= TTL_MS) break;\n seen.delete(key);\n }\n}\n\nexport function recordOutboundMessageId(accountId: string, messageId: string | undefined | null): void {\n if (!messageId) return;\n seen.set(`${accountId}:${messageId}`, Date.now());\n prune(Date.now());\n}\n\nexport function isOutboundEcho(accountId: string, messageId: string | undefined | null): boolean {\n if (!messageId) return false;\n const key = `${accountId}:${messageId}`;\n const ts = seen.get(key);\n if (ts === undefined) return false;\n if (Date.now() - ts > TTL_MS) {\n seen.delete(key);\n return false;\n }\n return true;\n}\n\n/** 测试用:清空登记表 */\nexport function clearOutboundEchoStore(): void {\n seen.clear();\n}\n","/**\n * 入站事件守卫\n *\n * 拦截三类「非真实用户输入」的入站推送(均有平台文档/SDK 注释背书,\n * 详见 AGENTS.md「入站事件守卫」条目):\n * 1. 出站回声 —— QQ 会把 bot 自己发出的消息回投为入站事件\n * (SDK message-filter 注释;c2c 事件无 author.bot,SDK 过滤不到),\n * 按 outbound-echo-store 登记的近期出站消息 id 比对。\n * 2. 重复推送 —— 平台事件文档要求按 msg_seq / message_scene.ext 的\n * msg_idx 去重(「为确保消息可达,相同 msg_id 可能重复推送」),\n * SDK 内置去重窗口仅 5s,这里补 30 分钟长窗口。\n * 3. 空内容事件 —— content 空白且无附件、无 msg_elements 的推送\n * (重推副本 / 结构化回执形态)。注意:带 msg_elements 的 103\n * 引用/转发消息是真实用户操作,必须放行(quoteRef 会渲染引用块)。\n *\n * 每次拦截打 INFO 日志(含 msgType / scene / payload 摘要)——这是\n * 入站事件唯一的非 DEBUG 级痕迹,用于事后取证定位事件真身。\n *\n * 挂载位置:messageFilter 之后、policyInjector 之前(链条最前端,\n * 避免污染群历史、烧 typing 被动回复配额)。\n */\nimport type { Middleware } from '@tencent-connect/qqbot-nodejs';\nimport { isOutboundEcho } from '../features/outbound-echo-store.js';\n\nconst DEDUP_TTL_MS = 30 * 60 * 1000; // 覆盖 c2c msg_id 30 分钟被动回复有效期\nconst DEDUP_MAX = 5000;\nconst PAYLOAD_PREVIEW_CHARS = 300;\n\nconst dedupSeen = new Map<string, number>(); // dedupKey -> 首次见到的时间\n\nfunction truncateText(text: string, max: number): string {\n return text.length > max ? `${text.slice(0, max)}…` : text;\n}\n\nfunction payloadPreview(raw: unknown): string {\n try {\n return truncateText(JSON.stringify(raw ?? {}), PAYLOAD_PREVIEW_CHARS);\n } catch {\n return '<unserializable>';\n }\n}\n\nexport interface InboundGuardParams {\n accountId: string;\n}\n\nexport function inboundGuard(params: InboundGuardParams): Middleware {\n return async (ctx, next) => {\n const msg = ctx.message as Record<string, any> | undefined;\n const accountId = params.accountId;\n const messageId: string | undefined = msg?.messageId;\n const kind: string = msg?.kind ?? '?';\n\n // 1. 出站回声:入站 id 命中近期出站消息 id\n if (isOutboundEcho(accountId, messageId)) {\n ctx.log.info(`[guard] dropped outbound-echo msgId=${messageId ?? '?'} kind=${kind}`);\n ctx.stop('outbound-echo');\n return;\n }\n\n // 2. 重复推送:msgId + msgIdx 长窗口去重\n const msgIdx: string | undefined = msg?.msgIdx;\n const dedupKey = `${accountId}:${kind}:${messageId ?? ''}:${msgIdx ?? ''}`;\n const now = Date.now();\n const firstSeen = dedupSeen.get(dedupKey);\n if (firstSeen !== undefined && now - firstSeen <= DEDUP_TTL_MS) {\n ctx.log.info(\n `[guard] dropped duplicate-push msgId=${messageId ?? '?'} msgIdx=${msgIdx ?? ''} kind=${kind} ageMs=${now - firstSeen}`,\n );\n ctx.stop('duplicate-push');\n return;\n }\n dedupSeen.set(dedupKey, now);\n if (dedupSeen.size > DEDUP_MAX) {\n for (const [key, ts] of dedupSeen) {\n if (dedupSeen.size <= DEDUP_MAX && now - ts <= DEDUP_TTL_MS) break;\n dedupSeen.delete(key);\n }\n }\n\n // 3. 空内容事件:无文本、无附件、无消息元素\n const contentBlank = !String(msg?.content ?? '').trim();\n const hasAttachments = Array.isArray(msg?.attachments) && msg.attachments.length > 0;\n const hasElements = Array.isArray(msg?.msgElements) && msg.msgElements.length > 0;\n if (contentBlank && !hasAttachments && !hasElements) {\n ctx.log.info(\n `[guard] dropped contentless msgId=${messageId ?? '?'} kind=${kind}` +\n ` msgType=${msg?.msgType ?? '?'} scene=${msg?.messageScene?.source ?? '?'}` +\n ` payload=${payloadPreview(msg?.raw)}`,\n );\n ctx.stop('contentless');\n return;\n }\n\n await next();\n };\n}\n\n/** 测试用:清空去重表 */\nexport function clearInboundGuardDedup(): void {\n dedupSeen.clear();\n}\n","/**\n * ask_user 按钮辅助函数\n *\n * 与 approval-helpers.ts 模式一致:\n * - buildQuestionKeyboard: 构建 inline keyboard(单问题)\n * - buildMultiQuestionKeyboard: 构建多问题场景单题的 inline keyboard\n * - parseQuestionButtonData / parseMultiQuestionButtonData: 解析 INTERACTION_CREATE 的 button_data\n * - isAskUserPayload / isNonSingleAskUserPayload: 判断 payload 是否为 ask_user(单/非单问题)\n * - parseMultiQuestionPrompt: 从多问题投递文本反解题目结构\n * - registerPendingMultiQuestion 等: 多问题答案暂存 store\n * - buildMultiQuestionConfirmKeyboard 等: 集齐后发\"确认卡\"——指令按钮\n * (type=2, enter=true) 携带完整答案文本,由客户端以真实用户消息发出,\n * 走框架原生的 keyed 文本认领通道(不程序化提交,不合成入站消息)\n */\n\nimport type { InlineKeyboard, KeyboardButton } from '../types.js';\n\n// ============ Constants ============\n\nconst QUESTION_CALLBACK_PREFIX = 'qqbot:q:';\nconst MULTI_QUESTION_CALLBACK_PREFIX = 'qqbot:qm:';\n\n/** questionId 格式: ask_<32位hex> */\nconst QUESTION_ID_PATTERN = /^ask_[a-f0-9]{32}$/;\n\n/** gateway 协议上限:单次 ask_user 最多 3 个问题,每题最多 4 个选项 */\nconst MAX_MULTI_QUESTIONS = 3;\nconst MAX_OPTIONS_PER_QUESTION = 4;\n\n/** gateway 默认问题超时 15min,这里留 10min 余量兜住投递延迟 */\nconst DEFAULT_MULTI_QUESTION_TTL_MS = 25 * 60 * 1000;\n\n// ============ Types ============\n\nexport interface ParsedQuestionAction {\n questionId: string;\n optionIndex: number;\n}\n\n/**\n * ask_user 多问题场景中从投递文本解析出的单题结构。\n * 框架的多问题 payload 只有 questionId + 格式化文本(无结构化选项数据),\n * 题目结构需从文本反解。\n */\nexport interface MultiQuestionDef {\n header: string;\n question: string;\n options: string[];\n /** 题目声明了 \"Other: reply with your own answer.\"(允许自由文本作答) */\n isOther?: boolean;\n}\n\nexport interface ParsedMultiQuestionAction {\n questionId: string;\n questionIndex: number;\n optionIndex: number;\n}\n\n/** 一次多问题整单的答案(题号 -> 按钮选项序号或自由文本) */\nexport type MultiQuestionAnswer = { optionIndex: number } | { text: string };\n\nexport interface QuestionGatewayRuntime {\n resolveOption: (params: {\n cfg: unknown;\n questionId: string;\n optionIndex: number;\n senderId?: string;\n gatewayUrl?: string;\n clientDisplayName?: string;\n }) => Promise<{ status: string }>;\n}\n\nlet questionRuntimePromise: Promise<QuestionGatewayRuntime> | undefined;\n\n/**\n * question gateway runtime(openclaw/plugin-sdk/question-gateway-runtime,\n * 2026.8.1+ 稳定导出,peer 已要求 >=2026.9.2)。\n */\nexport function getQuestionGatewayRuntime(): Promise<QuestionGatewayRuntime> {\n questionRuntimePromise ??= import('openclaw/plugin-sdk/question-gateway-runtime')\n .then((mod) => mod.questionGatewayRuntime as QuestionGatewayRuntime);\n return questionRuntimePromise;\n}\n\n// ============ Payload Detection ============\n\n/**\n * 判断 DeliverPayload 是否为 ask_user 单问题单选场景。\n * 要求:有 channelData.askUser.questionId,且 optionValues 长度 2-4。\n */\nexport function isAskUserPayload(payload: {\n channelData?: { askUser?: { questionId?: unknown; optionValues?: unknown } };\n}): payload is { channelData: { askUser: { questionId: string; optionValues: string[] } } } {\n const askUser = payload.channelData?.askUser;\n if (!askUser || typeof askUser !== 'object') return false;\n const { questionId, optionValues } = askUser as { questionId?: unknown; optionValues?: unknown };\n return (\n typeof questionId === 'string' &&\n QUESTION_ID_PATTERN.test(questionId) &&\n Array.isArray(optionValues) &&\n optionValues.length >= 2 &&\n optionValues.length <= 4 &&\n optionValues.every((v: unknown) => typeof v === 'string')\n );\n}\n\n/**\n * 判断 DeliverPayload 是否为 ask_user「非单问题」场景。\n *\n * 框架只在恰好 1 个问题(且单选、非 secret、2-4 个唯一选项)时附带\n * optionValues;其余情况 payload 只有 questionId + 纯文本。多问题结构\n * 需要调用 parseMultiQuestionPrompt 从文本反解。\n */\nexport function isNonSingleAskUserPayload(payload: {\n channelData?: { askUser?: { questionId?: unknown; optionValues?: unknown } };\n}): payload is { channelData: { askUser: { questionId: string } } } {\n const askUser = payload.channelData?.askUser;\n if (!askUser || typeof askUser !== 'object') return false;\n const { questionId, optionValues } = askUser as { questionId?: unknown; optionValues?: unknown };\n if (typeof questionId !== 'string' || !QUESTION_ID_PATTERN.test(questionId)) return false;\n // 单问题场景由 isAskUserPayload 优先认领\n return !Array.isArray(optionValues);\n}\n\n// ============ 多问题投递文本解析 ============\n\nconst NUMBERED_LINE_PATTERN = /^(\\d+)\\.\\s+(.+)$/;\n\n/**\n * 从 ask_user 多问题投递文本中解析题目结构。\n *\n * 文本由框架 formatAgentHarnessUserInputPrompt 生成(多问题格式):\n * ```\n * [intro]\n * <空行>\n * 1. <header>\n * <题干>\n * 1. <label> - <description>\n * ...\n * <空行>\n * 2. <header>\n * ...\n * <空行>\n * Reply by number or question id...\n * ```\n *\n * 判别依赖生成器的两个不变量:\n * - header 行总是紧跟空行之后(每个 question 前都有空行)\n * - 选项行紧跟题干/上一选项(块内无空行)\n *\n * 解析失败返回 null(secret 警告行 / isOther 行 / 结构异常都会失败),\n * 调用方回退纯文本展示。multiSelect 题无法从文本区分,按钮单选属可接受降级。\n */\nexport function parseMultiQuestionPrompt(text: string): MultiQuestionDef[] | null {\n const lines = text.split(/\\r?\\n/);\n\n // 定位第一个 \"1. \" header(其后必须紧跟空行或行首,排除 intro 干扰)\n let start = -1;\n for (let i = 0; i < lines.length; i++) {\n const m = lines[i]!.match(NUMBERED_LINE_PATTERN);\n if (m && Number(m[1]) === 1 && (i === 0 || !lines[i - 1]!.trim())) {\n start = i;\n break;\n }\n }\n if (start < 0) return null;\n\n const questions: MultiQuestionDef[] = [];\n let header: string | undefined;\n let questionText: string | undefined;\n let options: string[] = [];\n let isOther = false;\n let afterBlank = true; // start 位置的 header 已由扫描保证满足\"空行在前\"\n\n const finishQuestion = (): boolean => {\n if (header === undefined || questionText === undefined) return false;\n if (options.length < 2 || options.length > MAX_OPTIONS_PER_QUESTION) return false;\n questions.push({ header, question: questionText, options, ...(isOther ? { isOther: true } : {}) });\n header = undefined;\n questionText = undefined;\n options = [];\n isOther = false;\n return true;\n };\n\n for (let i = start; i < lines.length; i++) {\n const trimmed = lines[i]!.trim();\n if (!trimmed) {\n afterBlank = true;\n continue;\n }\n const m = trimmed.match(NUMBERED_LINE_PATTERN);\n const num = m ? Number(m[1]) : NaN;\n\n if (header === undefined) {\n // 期待下一题 header:序号连续且前面是空行\n if (!m || num !== questions.length + 1 || !afterBlank) return null;\n header = m[2];\n afterBlank = false;\n continue;\n }\n if (questionText === undefined) {\n // 期待题干:编号行说明结构异常\n if (m) return null;\n questionText = trimmed;\n afterBlank = false;\n continue;\n }\n // 选项后出现 isOther 声明行(框架默认文案 \"Other: reply with your own answer.\")\n if (trimmed.startsWith('Other:') && options.length >= 2) {\n isOther = true;\n afterBlank = false;\n continue;\n }\n // 期待选项 / 下一题 header / 终止(引导语等尾部内容)\n if (m && num === 1 && options.length === 0 && !afterBlank) {\n options.push(m[2]);\n afterBlank = false;\n continue;\n }\n if (\n m &&\n num === options.length + 1 &&\n options.length > 0 &&\n options.length < MAX_OPTIONS_PER_QUESTION &&\n !afterBlank\n ) {\n options.push(m[2]);\n afterBlank = false;\n continue;\n }\n // 当前题尚未入列,下一题 header 的序号 = 已入列数 + 当前题 + 1\n if (m && num === questions.length + 2 && afterBlank) {\n if (!finishQuestion()) return null;\n header = m[2];\n afterBlank = false;\n continue;\n }\n // 其余内容:收尾并停止(正常情况是尾部引导语)\n if (!finishQuestion()) return null;\n break;\n }\n if (header !== undefined || questionText !== undefined || options.length > 0) {\n // 循环耗尽时仍有未收尾的题目\n if (!finishQuestion()) return null;\n }\n if (questions.length < 2 || questions.length > MAX_MULTI_QUESTIONS) return null;\n return questions;\n}\n\n// ============ Keyboard Builder ============\n\n/**\n * 为 ask_user 构建 inline keyboard。\n *\n * 与审批按钮结构完全一致:\n * - action.type=1 (回调型)\n * - permission.type=2 (所有人可点)\n * - click_limit=1 (每人一次)\n * - group_id=\"question\" (互斥)\n *\n * button_data 格式: `qqbot:q:<questionId>:<optionIndex>`\n */\nexport function buildQuestionKeyboard(\n questionId: string,\n optionLabels: string[],\n): InlineKeyboard {\n const buttons: KeyboardButton[] = optionLabels.map((label, index) => ({\n id: `q${index}`,\n render_data: {\n label,\n visited_label: '已回答',\n style: 1 as const,\n },\n action: {\n type: 1 as const,\n data: `${QUESTION_CALLBACK_PREFIX}${questionId}:${index}`,\n permission: { type: 2 as const },\n click_limit: 1,\n },\n group_id: 'question',\n }));\n\n return {\n content: {\n rows: [{ buttons }],\n },\n };\n}\n\n/** 选项按钮的序号前缀,与正文里的加粗序号 1./2./3./4. 一一对应 */\nconst OPTION_NUMBER_PREFIXES = ['①', '②', '③', '④'] as const;\n\n/**\n * 为 ask_user 多问题场景构建第 questionIndex 题的 inline keyboard。\n *\n * - 选项按钮:action.type=1(回调),button_data `qqbot:qm:<id>:<题号>:<选项号>`,\n * group_id 按题隔离(`question-<i>`)保证同题互斥、跨题不互斥;\n * 标签带 ①②③ 序号前缀(截断时也能与正文选项对上号)\n * - isOther 题追加「✍️ 其他」指令按钮(action.type=2):点击后客户端在\n * 输入框预填 `<题号>: `,用户续写自由答案后发送,天然就是 keyed 答案格式\n * - 按钮标签超过宽度预算时自动改为每行 2 个,避免文字显示不全\n */\nexport function buildMultiQuestionKeyboard(\n questionId: string,\n questionIndex: number,\n question: MultiQuestionDef,\n): InlineKeyboard {\n const buttons: KeyboardButton[] = question.options.map((optionLine, optionIndex) => {\n const prefix = OPTION_NUMBER_PREFIXES[optionIndex] ?? '';\n // 前缀 + 空格的显示宽度(全角序号 2 + 空格 1)\n const reserve = estimateLabelWidth(`${prefix} `);\n return {\n id: `q${questionIndex}_${optionIndex}`,\n render_data: {\n label: `${prefix} ${buildButtonLabel(optionLine, reserve)}`,\n visited_label: '已选',\n style: 1 as const,\n },\n action: {\n type: 1 as const,\n data: `${MULTI_QUESTION_CALLBACK_PREFIX}${questionId}:${questionIndex}:${optionIndex}`,\n permission: { type: 2 as const },\n click_limit: 1,\n },\n group_id: `question-${questionIndex}`,\n };\n });\n\n if (question.isOther) {\n buttons.push({\n id: `q${questionIndex}_other`,\n render_data: {\n label: '✍️ 其他',\n visited_label: '✍️ 其他',\n style: 0 as const,\n },\n action: {\n type: 2 as const,\n // 点击后输入框预填 \"题号: \",用户续写后手动发送\n data: `${questionIndex + 1}: `,\n enter: false,\n permission: { type: 2 as const },\n unsupport_tips: '当前客户端版本不支持,请直接回复「题号: 答案」',\n },\n // 指令按钮不属于互斥选项组\n });\n }\n\n return {\n content: {\n rows: layoutButtonRows(buttons),\n },\n };\n}\n\n/** 自适应分行:标签全部较短时一行放满,否则每行 2 个 */\nfunction layoutButtonRows(buttons: KeyboardButton[]): Array<{ buttons: KeyboardButton[] }> {\n const widest = buttons.reduce(\n (max, b) => Math.max(max, estimateLabelWidth(b.render_data?.label ?? '')),\n 0,\n );\n if (widest <= SINGLE_ROW_MAX_LABEL_WIDTH || buttons.length <= 2) {\n return [{ buttons }];\n }\n const rows: Array<{ buttons: KeyboardButton[] }> = [];\n for (let i = 0; i < buttons.length; i += 2) {\n rows.push({ buttons: buttons.slice(i, i + 2) });\n }\n return rows;\n}\n\n/**\n * 选项整行形如 \"调研/方案 (Recommended) - 搜索、分析、对比、出方案。\",\n * 按第一个 \" - \" 拆出标签部分(拆不准时退回整行,不影响答案正确性——\n * 答案靠题号/选项号定位,不依赖标签文本)。\n */\nexport function splitOptionLabel(optionLine: string): string {\n const sep = optionLine.indexOf(' - ');\n if (sep > 0) return optionLine.slice(0, sep).trim();\n return optionLine.trim();\n}\n\nfunction splitOptionDescription(optionLine: string): string {\n const label = splitOptionLabel(optionLine);\n if (optionLine.length > label.length && optionLine.startsWith(label)) {\n return optionLine.slice(label.length).replace(/^\\s*-\\s*/, '').trim();\n }\n return '';\n}\n\n/** 估算文字显示宽度:CJK/全角/emoji 记 2,其余记 1 */\nexport function estimateLabelWidth(text: string): number {\n let width = 0;\n for (const ch of text) {\n const code = ch.codePointAt(0) ?? 0;\n const wide =\n (code >= 0x1100 && code <= 0x115f) ||\n (code >= 0x2e80 && code <= 0xa4cf) ||\n (code >= 0xac00 && code <= 0xd7a3) ||\n (code >= 0xf900 && code <= 0xfaff) ||\n (code >= 0xfe30 && code <= 0xfe6f) ||\n (code >= 0xff00 && code <= 0xff60) ||\n (code >= 0xffe0 && code <= 0xffe6) ||\n code >= 0x1f300;\n width += wide ? 2 : 1;\n }\n return width;\n}\n\n/** 按钮标签安全宽度(约 12 个汉字),超出截断加省略号 */\nconst MAX_BUTTON_LABEL_WIDTH = 24;\n/** 一行放满时允许的最大标签宽度,超过改为每行 2 个 */\nconst SINGLE_ROW_MAX_LABEL_WIDTH = 20;\n\n/** 从选项整行生成按钮短标签:剥描述 → 剥 \"(Recommended)\" 后缀 → 超宽截断。\n * reserveWidth 为前缀(如选项序号)预留的显示宽度。 */\nexport function buildButtonLabel(optionLine: string, reserveWidth = 0): string {\n let label = stripRecommendedSuffix(splitOptionLabel(optionLine));\n const max = MAX_BUTTON_LABEL_WIDTH - reserveWidth;\n if (estimateLabelWidth(label) <= max) return label;\n let truncated = '';\n let width = 0;\n for (const ch of label) {\n const w = estimateLabelWidth(ch);\n if (width + w > max - 2) break;\n truncated += ch;\n width += w;\n }\n return `${truncated.trimEnd()}…`;\n}\n\n/** 剥掉框架渲染在选项标签尾部的 \"(Recommended)\" 标记 */\nexport function stripRecommendedSuffix(label: string): string {\n return label.replace(/\\s*\\(\\s*recommended\\s*\\)\\s*$/i, '').trim();\n}\n\n/**\n * 格式化多问题卡片中单题的文本部分(QQ 客户端原生 Markdown 渲染)。\n * 完整选项(含描述)放在正文里,按钮只放截断后的短标签。\n * 正文选项用加粗序号,与按钮上的 ①②③ 前缀一一对应。\n */\nexport function formatMultiQuestionCard(\n question: MultiQuestionDef,\n questionIndex: number,\n total: number,\n): string {\n const lines = [`**第 ${questionIndex + 1}/${total} 题 · ${question.header}**`];\n if (question.question.trim()) {\n lines.push('', question.question.trim());\n }\n for (const [optionIndex, optionLine] of question.options.entries()) {\n const label = stripRecommendedSuffix(splitOptionLabel(optionLine));\n const desc = splitOptionDescription(optionLine);\n lines.push('', `**${optionIndex + 1}.** ${label}${desc ? ` —— ${desc}` : ''}`);\n }\n lines.push('', '👇 点下方按钮作答');\n if (question.isOther) {\n lines.push('', `选项不合适?点「✍️ 其他」后输入,或直接回复 \"${questionIndex + 1}: 你的答案\"`);\n }\n return lines.join('\\n');\n}\n\n// ============ Button Data Parser ============\n\n/**\n * 解析 INTERACTION_CREATE 事件中的 button_data。\n *\n * 格式: `qqbot:q:<questionId>:<optionIndex>`\n * 返回 null 表示不是 question 按钮。\n */\nexport function parseQuestionButtonData(buttonData: string): ParsedQuestionAction | null {\n if (!buttonData.startsWith(QUESTION_CALLBACK_PREFIX)) return null;\n\n const payload = buttonData.slice(QUESTION_CALLBACK_PREFIX.length);\n const sepIndex = payload.lastIndexOf(':');\n if (sepIndex <= 0) return null;\n\n const questionId = payload.slice(0, sepIndex);\n const indexStr = payload.slice(sepIndex + 1);\n\n if (!QUESTION_ID_PATTERN.test(questionId)) return null;\n if (!indexStr) return null; // 防止 'qqbot:q:ask_xxx:' 被解析为 index 0\n\n const optionIndex = Number(indexStr);\n if (!Number.isInteger(optionIndex) || optionIndex < 0 || optionIndex > 3) return null;\n\n return { questionId, optionIndex };\n}\n\n/**\n * 解析多问题按钮的 button_data。\n *\n * 格式: `qqbot:qm:<questionId>:<questionIndex>:<optionIndex>`\n * 返回 null 表示不是多问题按钮。\n */\nexport function parseMultiQuestionButtonData(buttonData: string): ParsedMultiQuestionAction | null {\n if (!buttonData.startsWith(MULTI_QUESTION_CALLBACK_PREFIX)) return null;\n\n const payload = buttonData.slice(MULTI_QUESTION_CALLBACK_PREFIX.length);\n const parts = payload.split(':');\n if (parts.length !== 3) return null;\n\n const [questionId, questionIndexStr, optionIndexStr] = parts;\n if (!QUESTION_ID_PATTERN.test(questionId)) return null;\n if (!questionIndexStr || !optionIndexStr) return null;\n\n const questionIndex = Number(questionIndexStr);\n const optionIndex = Number(optionIndexStr);\n if (!Number.isInteger(questionIndex) || questionIndex < 0 || questionIndex >= MAX_MULTI_QUESTIONS) {\n return null;\n }\n if (!Number.isInteger(optionIndex) || optionIndex < 0 || optionIndex >= MAX_OPTIONS_PER_QUESTION) {\n return null;\n }\n\n return { questionId, questionIndex, optionIndex };\n}\n\n// ============ 多问题答案暂存 Store ============\n\ninterface PendingMultiQuestion {\n scope: 'c2c' | 'group';\n targetId: string;\n questions: MultiQuestionDef[];\n /** questionIndex -> 答案(同题重复作答,最后一次为准) */\n answers: Map<number, MultiQuestionAnswer>;\n terminal: boolean;\n /** 正在合成入站提交(防并发重复提交) */\n resolving: boolean;\n expiresAtMs: number;\n cleanupTimer: ReturnType<typeof setTimeout>;\n}\n\nconst pendingMultiQuestions = new Map<string, PendingMultiQuestion>();\n/** `${scope}:${targetId}` -> questionId,供入站文字答案按会话定位 */\nconst pendingByConversation = new Map<string, string>();\n\nfunction conversationKey(scope: 'c2c' | 'group', targetId: string): string {\n return `${scope}:${targetId}`;\n}\n\n/**\n * 登记一次多问题 ask_user 投递,供按钮回调 / 入站文字按题缓冲答案。\n * 必须在发送按钮消息之前调用,避免「先点后登记」竞态。\n */\nexport function registerPendingMultiQuestion(\n questionId: string,\n scope: 'c2c' | 'group',\n targetId: string,\n questions: MultiQuestionDef[],\n ttlMs: number = DEFAULT_MULTI_QUESTION_TTL_MS,\n): void {\n const existing = pendingMultiQuestions.get(questionId);\n if (existing) clearTimeout(existing.cleanupTimer);\n\n const entry: PendingMultiQuestion = {\n scope,\n targetId,\n questions,\n answers: new Map(),\n terminal: false,\n resolving: false,\n expiresAtMs: Date.now() + ttlMs,\n cleanupTimer: setTimeout(() => {\n if (pendingMultiQuestions.get(questionId) === entry) {\n pendingMultiQuestions.delete(questionId);\n }\n if (pendingByConversation.get(conversationKey(scope, targetId)) === questionId) {\n pendingByConversation.delete(conversationKey(scope, targetId));\n }\n }, ttlMs),\n };\n entry.cleanupTimer.unref?.();\n pendingMultiQuestions.set(questionId, entry);\n pendingByConversation.set(conversationKey(scope, targetId), questionId);\n}\n\n/** 按会话查找进行中的多问题单(终态/提交中/已过期不算) */\nexport function findPendingMultiQuestionByConversation(\n scope: 'c2c' | 'group',\n targetId: string,\n): { questionId: string } | undefined {\n const questionId = pendingByConversation.get(conversationKey(scope, targetId));\n if (!questionId) return undefined;\n const entry = pendingMultiQuestions.get(questionId);\n if (!entry || entry.terminal || entry.resolving || entry.expiresAtMs <= Date.now()) {\n return undefined;\n }\n return { questionId };\n}\n\nexport type MultiQuestionTapResult =\n | { status: 'unknown' }\n | { status: 'terminal' }\n | { status: 'resolving' }\n | { status: 'nomatch' }\n | {\n status: 'buffered';\n total: number;\n answeredCount: number;\n /** 尚未作答的题目 */\n pendingQuestions: MultiQuestionDef[];\n }\n | {\n status: 'complete';\n total: number;\n /**\n * 题号 -> 答案 的快照(complete 时刻的拷贝)。\n * 最终提交直接走 questionGatewayRuntime.resolveAnswers 整单 resolve,\n * 不再合成入站文本——合成消息会被框架当成 steering 注入挂起的 run,\n * 永远到不了 pending ask_user 的 claim(生产 2026-08-24 事故)。\n */\n answers: ReadonlyMap<number, MultiQuestionAnswer>;\n };\n\nfunction bufferedOrComplete(entry: PendingMultiQuestion): MultiQuestionTapResult {\n if (entry.answers.size < entry.questions.length) {\n const pendingQuestions = entry.questions.filter((_, index) => !entry.answers.has(index));\n return {\n status: 'buffered',\n total: entry.questions.length,\n answeredCount: entry.answers.size,\n pendingQuestions,\n };\n }\n entry.resolving = true;\n return {\n status: 'complete',\n total: entry.questions.length,\n answers: new Map(entry.answers),\n };\n}\n\n/**\n * 记录一次多问题按钮点选。\n * - 未登记/已过期 -> unknown\n * - 已终态(已提交/已在框架侧结束)-> terminal\n * - 正在提交中 -> resolving\n * - 还有题目未作答 -> buffered(附带剩余题目)\n * - 全部作答 -> complete(同步置 resolving,调用方拿到回复文本后走入站派发)\n */\nexport function recordMultiQuestionTap(\n questionId: string,\n questionIndex: number,\n optionIndex: number,\n): MultiQuestionTapResult {\n const entry = pendingMultiQuestions.get(questionId);\n if (!entry) return { status: 'unknown' };\n if (entry.expiresAtMs <= Date.now()) {\n if (pendingMultiQuestions.get(questionId) === entry) {\n pendingMultiQuestions.delete(questionId);\n }\n clearTimeout(entry.cleanupTimer);\n return { status: 'unknown' };\n }\n if (entry.terminal) return { status: 'terminal' };\n if (entry.resolving) return { status: 'resolving' };\n\n const question = entry.questions[questionIndex];\n if (!question) return { status: 'unknown' };\n if (typeof question.options[optionIndex] !== 'string') return { status: 'unknown' };\n\n entry.answers.set(questionIndex, { optionIndex });\n return bufferedOrComplete(entry);\n}\n\n/** 入站派发已受理(或问题已在框架侧结束)后标记终态,后续点选按 terminal 处理 */\nexport function markMultiQuestionResolved(questionId: string): void {\n const entry = pendingMultiQuestions.get(questionId);\n if (entry) {\n entry.terminal = true;\n entry.resolving = false;\n }\n}\n\n/**\n * 复位 complete 时置上的 resolving 锁(确认卡已发出或发送失败后调用),\n * 允许用户继续点选修改答案并重新拿到确认卡。\n */\nexport function markMultiQuestionResolveFailed(questionId: string): void {\n const entry = pendingMultiQuestions.get(questionId);\n if (entry) {\n entry.resolving = false;\n }\n}\n\n/** 读取登记的题目定义(确认卡正文按题展示已选项) */\nexport function getPendingMultiQuestions(questionId: string): MultiQuestionDef[] | null {\n const entry = pendingMultiQuestions.get(questionId);\n if (!entry || entry.expiresAtMs <= Date.now()) return null;\n return entry.questions;\n}\n\n/**\n * 合成整单答案文本:\"1: 3\\n2: 1\"(数字选项走框架原生 keyed 文本解析,\n * 由客户端以真实用户消息发出后,框架认领并 resolve 挂起的 ask_user)。\n */\nexport function buildMultiQuestionAnswerText(\n answers: ReadonlyMap<number, MultiQuestionAnswer>,\n): string {\n return [...answers.entries()]\n .sort((a, b) => a[0] - b[0])\n .map(([questionIdx, answer]) =>\n `${questionIdx + 1}: ${'optionIndex' in answer ? answer.optionIndex + 1 : answer.text}`)\n .join('\\n');\n}\n\n/** 确认卡正文:按题展示人话版的已选内容 */\nexport function formatMultiQuestionConfirmCard(\n questions: readonly MultiQuestionDef[],\n answers: ReadonlyMap<number, MultiQuestionAnswer>,\n): string {\n const lines = ['**✅ 答案确认**'];\n for (const [index, question] of questions.entries()) {\n const answer = answers.get(index);\n const display = !answer\n ? '(未作答)'\n : 'optionIndex' in answer\n ? stripRecommendedSuffix(splitOptionLabel(question.options[answer.optionIndex] ?? ''))\n : answer.text;\n lines.push('', `**${index + 1}.** ${question.header}:${display}`);\n }\n lines.push('', '点「✅ 提交」自动发送;想改动就点「✏️ 改一改」编辑后发送。');\n return lines.join('\\n');\n}\n\n/**\n * 确认卡键盘:两个指令按钮(action.type=2)携带完整答案文本。\n * - 提交:enter=true,客户端点击后自动以用户身份发出(真实消息,框架可认领)\n * - 改一改:enter=false,答案填入输入框,用户编辑后手动发送\n */\nexport function buildMultiQuestionConfirmKeyboard(answerText: string): InlineKeyboard {\n const buttons: KeyboardButton[] = [\n {\n id: 'confirm_submit',\n render_data: {\n label: '✅ 提交',\n visited_label: '已提交',\n style: 1 as const,\n },\n action: {\n type: 2 as const,\n data: answerText,\n enter: true,\n permission: { type: 2 as const },\n unsupport_tips: '当前客户端不支持,请手动回复上方答案文本',\n },\n },\n {\n id: 'confirm_edit',\n render_data: {\n label: '✏️ 改一改',\n visited_label: '✏️ 改一改',\n style: 0 as const,\n },\n action: {\n type: 2 as const,\n data: answerText,\n enter: false,\n permission: { type: 2 as const },\n unsupport_tips: '当前客户端不支持,请手动回复上方答案文本',\n },\n },\n ];\n return {\n content: {\n rows: [{ buttons }],\n },\n };\n}\n","/**\n * 密钥捕获中间件\n *\n * 当会话存在 pending 密钥输入(secret-input-store)时,拦截该 c2c 用户的\n * 下一条文本消息:就地执行 `openclaw secrets store set` 并把结果直接回复\n * 给用户,消息本身不进入框架 —— 密钥值绝不进入 AI 转录 / 框架日志。\n *\n * 挂载位置:slashCommand 之后、groupMessageCoalescer 之前\n * (/bot-* 命令优先可用;拦截后不再触发 typing 与 envelope 组装)。\n *\n * 红线:多问题 ask_user 答案 / 确认卡消息必须放行 —— 吞掉真实用户消息\n * 会导致挂起的 ask_user 永不成单(AGENTS.md 2026-08-24 事故)。\n */\nimport type { Middleware } from '@tencent-connect/qqbot-nodejs';\nimport { findPendingMultiQuestionByConversation } from '../features/question-helpers.js';\nimport { checkAndConsumePassiveReplyQuota } from '../features/quota-manager.js';\nimport {\n runSecretsReload,\n runSecretsStoreSet,\n maskSecret,\n type SecretsStoreSetResult,\n} from '../features/secret-store-cli.js';\nimport {\n cancelPendingSecretInput,\n findPendingSecretInput,\n takePendingSecretInput,\n} from '../features/secret-input-store.js';\n\nconst CANCEL_KEYWORDS = new Set(['取消', 'cancel', '#cancel', '/cancel']);\n\nexport function isSecretInputCancelKeyword(text: string): boolean {\n return CANCEL_KEYWORDS.has(text.trim().toLowerCase());\n}\n\n/** 配额感知的直接回复:优先被动(msg_id),配额耗尽/发送失败降级主动 */\nasync function replyToUser(\n ctx: any,\n accountId: string,\n text: string,\n): Promise<void> {\n const msgId = ctx.replyTarget?.msgId as string | undefined;\n if (msgId) {\n const { canReply, rollback } = checkAndConsumePassiveReplyQuota({\n accountId,\n msgId,\n scope: 'c2c',\n });\n if (canReply) {\n try {\n await ctx.bot.sendText(ctx.replyTarget, text);\n return;\n } catch {\n rollback();\n // 落到主动降级\n }\n }\n }\n const proactiveTarget = { scope: 'c2c' as const, targetId: ctx.message.senderId as string };\n await ctx.bot.sendText(proactiveTarget, text).catch(() => {/* 尽力而为 */});\n}\n\nfunction formatSetFailure(name: string, result: SecretsStoreSetResult): string {\n const reason = result.timedOut\n ? '执行超时'\n : result.error\n ? result.error\n : `退出码 ${result.exitCode ?? '无'}`;\n const lines = [`❌ 保存 \\`${name}\\` 失败(${reason})。`];\n if (!result.error && result.output) {\n lines.push('', '```', result.output, '```');\n }\n lines.push(\n '',\n '可能原因:openclaw 版本过旧(需支持 `openclaw secrets store set`),或本机 CLI 不可用。',\n '密钥未被保存,可稍后重试或改在终端执行。',\n );\n return lines.join('\\n');\n}\n\nexport interface SecretCaptureParams {\n accountId: string;\n /** 测试注入:替换 secrets store set 执行器 */\n runSet?: typeof runSecretsStoreSet;\n /** 测试注入:替换 secrets reload */\n runReload?: typeof runSecretsReload;\n}\n\nexport function secretCapture(params: SecretCaptureParams): Middleware {\n const { accountId, runSet = runSecretsStoreSet, runReload = runSecretsReload } = params;\n\n return async (ctx, next) => {\n // 仅 c2c:密钥不应出现在群聊(工具侧发卡时已限制,此处双保险)\n if (ctx.message.kind !== 'c2c') {\n await next();\n return;\n }\n const senderId = ctx.message.senderId as string;\n const pending = findPendingSecretInput(accountId, senderId);\n if (!pending) {\n await next();\n return;\n }\n\n // 红线:多问题 ask_user 进行中 → 其答案/确认卡消息优先,绝不拦截\n if (findPendingMultiQuestionByConversation('c2c', senderId)) {\n await next();\n return;\n }\n\n const text = ((ctx.message.content as string | undefined) ?? '').trim();\n\n if (isSecretInputCancelKeyword(text)) {\n cancelPendingSecretInput(accountId, senderId);\n ctx.log?.info?.(`[secret-input] cancelled by user (${pending.name})`);\n await replyToUser(ctx, accountId, `已取消 \\`${pending.name}\\` 的密钥输入。需要时请重新发起。`);\n ctx.stop('secret-capture:cancelled');\n return;\n }\n\n // 空文本 / 纯附件:提示重发,保留 pending 继续等待\n if (!text) {\n await replyToUser(\n ctx,\n accountId,\n `请直接以文本形式发送 **${pending.name}** 的值(可发送「取消」退出)。`,\n );\n ctx.stop('secret-capture:empty_input');\n return;\n }\n\n // 一次性消费,防重复处理\n const entry = takePendingSecretInput(accountId, senderId);\n if (!entry) {\n await next();\n return;\n }\n\n const result = await runSet({ name: entry.name, kind: entry.kind, value: text });\n let reloadOk = false;\n if (result.ok) {\n reloadOk = await runReload();\n }\n const masked = maskSecret(text);\n const kindLabel = entry.kind === 'env' ? '代理可读环境变量' : `kind: ${entry.kind}`;\n const reply = result.ok\n ? [\n `✅ 已保存 \\`${entry.name}\\`(${kindLabel},值已隐藏:\\`${masked}\\`)`,\n reloadOk\n ? '已触发 `secrets reload`。'\n : '提示:若该名称被 SecretRef 引用,可执行 `openclaw secrets reload` 使其立即生效。',\n ].join('\\n')\n : formatSetFailure(entry.name, result);\n\n ctx.log?.info?.(\n `[secret-input] store set ${entry.name} kind=${entry.kind} ok=${result.ok}` +\n ` exit=${result.exitCode ?? 'n/a'}${result.timedOut ? ' (timeout)' : ''}`,\n );\n await replyToUser(ctx, accountId, reply);\n ctx.stop('secret-capture:consumed');\n };\n}\n","/**\n * 密钥输入 pending 状态机\n *\n * qqbot_secret_input 工具发卡时登记,secretCapture 中间件消费。\n * 与 multi-question 暂存(question-helpers.ts)同款模式:模块级 Map +\n * TTL setTimeout(unref) 清理。\n *\n * key 绑定 `accountId + c2c senderOpenid` —— pending 天然 per-user:\n * 只有发起密钥输入的同一私聊用户的消息会被捕获,群聊不参与。\n */\nimport type { SecretStoreKind } from './secret-store-cli.js';\n\nexport const DEFAULT_SECRET_INPUT_TTL_MS = 10 * 60 * 1000;\n\nexport interface PendingSecretInput {\n accountId: string;\n senderOpenid: string;\n /** 环境变量名(已通过 isValidSecretName 校验) */\n name: string;\n /** 已判定写入方式 */\n kind: SecretStoreKind;\n /** 用途说明(展示在卡片上,可选) */\n description?: string;\n createdAt: number;\n expiresAtMs: number;\n}\n\ninterface StoredPendingSecretInput extends PendingSecretInput {\n cleanupTimer: ReturnType<typeof setTimeout>;\n}\n\nconst pendingSecretInputs = new Map<string, StoredPendingSecretInput>();\n\nexport function secretInputKey(accountId: string, senderOpenid: string): string {\n return `${accountId}:c2c:${senderOpenid}`;\n}\n\n/**\n * 登记一次密钥输入等待。必须在发送卡片之前调用(避免「先发后登」竞态)。\n * 同一用户重复发起直接覆盖旧条目(并清理旧 timer)。\n */\nexport function registerPendingSecretInput(\n entry: Omit<PendingSecretInput, 'expiresAtMs'>,\n ttlMs: number = DEFAULT_SECRET_INPUT_TTL_MS,\n): void {\n const key = secretInputKey(entry.accountId, entry.senderOpenid);\n const existing = pendingSecretInputs.get(key);\n if (existing) clearTimeout(existing.cleanupTimer);\n\n const stored: StoredPendingSecretInput = {\n ...entry,\n expiresAtMs: Date.now() + ttlMs,\n cleanupTimer: setTimeout(() => {\n if (pendingSecretInputs.get(key) === stored) {\n pendingSecretInputs.delete(key);\n }\n }, ttlMs),\n };\n stored.cleanupTimer.unref?.();\n pendingSecretInputs.set(key, stored);\n}\n\nfunction lookup(accountId: string, senderOpenid: string): StoredPendingSecretInput | undefined {\n const key = secretInputKey(accountId, senderOpenid);\n const entry = pendingSecretInputs.get(key);\n if (!entry) return undefined;\n if (entry.expiresAtMs <= Date.now()) {\n clearTimeout(entry.cleanupTimer);\n pendingSecretInputs.delete(key);\n return undefined;\n }\n return entry;\n}\n\n/** 查询进行中的密钥输入(过期条目惰性清除),不消费 */\nexport function findPendingSecretInput(\n accountId: string,\n senderOpenid: string,\n): PendingSecretInput | undefined {\n const entry = lookup(accountId, senderOpenid);\n if (!entry) return undefined;\n const { cleanupTimer: _timer, ...rest } = entry;\n return rest;\n}\n\n/** 一次性消费:取出并删除(防同一条 pending 被处理两次) */\nexport function takePendingSecretInput(\n accountId: string,\n senderOpenid: string,\n): PendingSecretInput | undefined {\n const entry = lookup(accountId, senderOpenid);\n if (!entry) return undefined;\n clearTimeout(entry.cleanupTimer);\n pendingSecretInputs.delete(secretInputKey(accountId, senderOpenid));\n const { cleanupTimer: _timer, ...rest } = entry;\n return rest;\n}\n\n/** 主动取消(用户发送「取消」/ 发卡失败回滚) */\nexport function cancelPendingSecretInput(accountId: string, senderOpenid: string): boolean {\n const key = secretInputKey(accountId, senderOpenid);\n const entry = pendingSecretInputs.get(key);\n if (!entry) return false;\n clearTimeout(entry.cleanupTimer);\n pendingSecretInputs.delete(key);\n return true;\n}\n\n/** 测试辅助 */\nexport function pendingSecretInputCount(): number {\n return pendingSecretInputs.size;\n}\n\n/** 测试辅助:清空全部 pending(不触发 timer 回调) */\nexport function clearPendingSecretInputs(): void {\n for (const entry of pendingSecretInputs.values()) clearTimeout(entry.cleanupTimer);\n pendingSecretInputs.clear();\n}\n","/**\n * C2C \"正在输入\"指示器中间件(替代 SDK 的 typingIndicator)。\n *\n * 与 SDK 版的三点差异:\n * 1. 续期间隔默认 20s 且强制不低于 20s(QPS 约束,事件触发的续期\n * 同样受此间距保护)\n * 2. 配额感知:typing 通知携带入站 msg_id 时属于被动回复、消耗该\n * msg_id 的被动配额,与真正的回复消息共享额度(经同一 limiter\n * 记账)。被动配额耗尽后与回复消息一样自动降级为主动发送\n * (不带 msg_id),续期不会中断\n * 3. 出站消息后续期:QQ 客户端收到机器人消息(如思维链中间输出)\n * 会自动终止\"正在输入\"显示。若框架任务仍在进行,本中间件在\n * 每条消息发出 POST_MESSAGE_REFRESH_DELAY_MS 后补发一次续期;\n * 若该消息是最终回复(处理链完成),会话已结束,自然不再续期\n *\n * QQ 客户端行为:退出聊天界面再进入后指示器会消失,只有收到新的\n * input_notify 推送才会重新显示 —— 这是需要周期性续期的原因。\n */\nimport type { Middleware } from '@tencent-connect/qqbot-nodejs';\nimport { rollbackPassiveSlot, tryAcquirePassiveSlot } from '../outbound/outbound-service.js';\nimport { subscribeOutboundMessage } from '../features/typing-refresh.js';\n\n/** QPS 约束:续期间隔不得低于 20s */\nexport const MIN_TYPING_INTERVAL_MS = 20_000;\nconst DEFAULT_TYPING_INTERVAL_MS = 20_000;\n/** 服务端输入状态窗口(秒),QQ 平台窗口上限约 60s */\nconst TYPING_DURATION_SEC = 60;\n/** 出站消息后延迟多久补发续期 */\nexport const POST_MESSAGE_REFRESH_DELAY_MS = 5_000;\n\nexport function resolveTypingIntervalMs(input: number | undefined): number {\n return Math.max(input ?? DEFAULT_TYPING_INTERVAL_MS, MIN_TYPING_INTERVAL_MS);\n}\n\nexport interface TypingIndicatorMiddlewareOptions {\n accountId: string;\n /** 续期间隔 ms,默认 20_000,低于 20_000 会被钳制到 20_000 */\n intervalMs?: number;\n}\n\nexport function c2cTypingIndicator(opts: TypingIndicatorMiddlewareOptions): Middleware {\n const accountId = opts.accountId;\n const intervalMs = resolveTypingIntervalMs(opts.intervalMs);\n\n return async (ctx, next) => {\n // QQ 开放平台限制:input_notify 仅支持 C2C(私聊)\n if (ctx.message.kind !== 'c2c') {\n await next();\n return;\n }\n\n let periodic: NodeJS.Timeout | null = null;\n let refresh: NodeJS.Timeout | null = null;\n let lastSentAt = 0;\n let active = true;\n\n const sendNow = (): void => {\n if (!active) return;\n // 被动配额可用 → 占用并带 msg_id 发送;耗尽 → 不带 msg_id 主动发送,\n // 与回复消息的降级策略一致(见 outbound-service 的统一配额预留)\n const passive = tryAcquirePassiveSlot(accountId, ctx.replyTarget.msgId);\n const target = passive\n ? ctx.replyTarget\n : { scope: 'c2c' as const, targetId: ctx.replyTarget.targetId };\n ctx.bot.sendTyping(target, TYPING_DURATION_SEC).catch((err: unknown) => {\n if (passive) rollbackPassiveSlot(accountId, ctx.replyTarget.msgId);\n ctx.log?.debug?.(`[typing] failed: ${err instanceof Error ? err.message : String(err)}`);\n });\n lastSentAt = Date.now();\n // 以本次发送为锚点重置周期续期,避免与事件续期撞车\n if (periodic) clearInterval(periodic);\n periodic = setInterval(sendNow, intervalMs);\n };\n\n // 出站消息会终止客户端的输入状态显示 → 5s 后补发;\n // 与上次 typing 发送不足 intervalMs 时顺延(QPS 间距保护)\n const schedulePostMessageRefresh = (): void => {\n if (!active) return;\n if (refresh) clearTimeout(refresh);\n refresh = setTimeout(() => {\n refresh = null;\n const wait = intervalMs - (Date.now() - lastSentAt);\n if (wait > 0) {\n refresh = setTimeout(() => {\n refresh = null;\n sendNow();\n }, wait);\n } else {\n sendNow();\n }\n }, POST_MESSAGE_REFRESH_DELAY_MS);\n };\n\n // 订阅本会话的出站消息信号( accountId + targetId 对应同一 C2C 用户)\n const unsubscribe = subscribeOutboundMessage(\n accountId, 'c2c', ctx.replyTarget.targetId, schedulePostMessageRefresh,\n );\n\n sendNow();\n\n try {\n await next();\n } finally {\n active = false;\n unsubscribe();\n if (periodic) clearInterval(periodic);\n if (refresh) clearTimeout(refresh);\n }\n };\n}\n","/**\n * 出站消息 → typing 续期信号\n *\n * QQ 客户端收到机器人消息后会自动终止\"正在输入\"显示。若框架任务\n * 仍在进行(如思维链等中间消息),typing 中间件需要在消息发出后\n * 延迟数秒补发一次续期,恢复指示器显示。\n *\n * 本模块提供进程内信号:出站层(QQBotGateway)在消息发送成功后\n * notify,活跃的 typing 会话按 accountId + scope + targetId 订阅。\n * 任务已完成的会话已取消订阅,notify 自然不产生任何效果。\n */\n\ntype Listener = () => void;\n\nconst listeners = new Map<string, Set<Listener>>();\n\nfunction key(accountId: string, scope: string, targetId: string): string {\n return `${accountId}:${scope}:${targetId}`;\n}\n\n/**\n * 订阅指定会话的出站消息信号。\n * @returns 取消订阅函数\n */\nexport function subscribeOutboundMessage(\n accountId: string,\n scope: string,\n targetId: string,\n listener: Listener,\n): () => void {\n const k = key(accountId, scope, targetId);\n let set = listeners.get(k);\n if (!set) {\n set = new Set();\n listeners.set(k, set);\n }\n set.add(listener);\n return () => {\n set.delete(listener);\n if (set.size === 0) listeners.delete(k);\n };\n}\n\n/**\n * 消息发送成功后由出站层调用:通知该会话活跃的 typing 会话补发续期。\n */\nexport function notifyOutboundMessageSent(accountId: string, scope: string, targetId: string): void {\n const set = listeners.get(key(accountId, scope, targetId));\n if (!set) return;\n for (const listener of [...set]) listener();\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 * 信封构建器\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 * 消息转发 — 入站消息 → 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, trimGroupHistoryAfterLastBot } from '../features/history-store.js';\nimport {\n isAskUserPayload,\n isNonSingleAskUserPayload,\n buildQuestionKeyboard,\n buildMultiQuestionKeyboard,\n formatMultiQuestionCard,\n parseMultiQuestionPrompt,\n registerPendingMultiQuestion,\n getQuestionGatewayRuntime,\n} from '../features/question-helpers.js';\nimport { tryGetBotForAccount } from '../bot-instance.js';\nimport { resolveGroupConfigFromAccount, resolveMentionPatterns } from '../config.js';\nimport { detectWasMentioned } from '../utils/mention.js';\n\n/** 失败兜底文案(对齐 telegram:Something went wrong while processing your request.) */\nconst FAILURE_FALLBACK_TEXT = 'Something went wrong while processing your request. Please try again.';\n\n/** 每进程只打一次「框架排队已接管线」INFO(排障锚点,确认 collect 真实生效) */\nlet frameworkQueueAnnounced = false;\n\n/**\n * 合并 AbortSignal(Node >= 20.3 使用 AbortSignal.any,低版本手工 fan-in)。\n * turnAdoptionLifecycle 的 pre-adoption abort 需要与请求级 signal 共同生效。\n */\nfunction combineAbortSignals(\n requestSignal: AbortSignal | undefined,\n turnSignal: AbortSignal,\n): AbortSignal {\n const any = (AbortSignal as unknown as { any?: (signals: AbortSignal[]) => AbortSignal }).any;\n if (typeof any === 'function') {\n return any.call(AbortSignal, requestSignal ? [requestSignal, turnSignal] : [turnSignal]);\n }\n if (!requestSignal) return turnSignal;\n\n const controller = new AbortController();\n const cleanup = () => {\n requestSignal.removeEventListener('abort', onRequestAbort);\n turnSignal.removeEventListener('abort', onTurnAbort);\n };\n const abortFrom = (signal: AbortSignal) => {\n cleanup();\n controller.abort(signal.reason);\n };\n const onRequestAbort = () => abortFrom(requestSignal);\n const onTurnAbort = () => abortFrom(turnSignal);\n\n if (requestSignal.aborted) abortFrom(requestSignal);\n else if (turnSignal.aborted) abortFrom(turnSignal);\n else {\n requestSignal.addEventListener('abort', onRequestAbort, { once: true });\n turnSignal.addEventListener('abort', onTurnAbort, { once: true });\n }\n return controller.signal;\n}\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.inboundRun || !adapters.dispatchReply) {\n dlog?.error(`runtime adapter inboundRun/dispatchReply not available (openclaw=${adapters.version}, requires >=2026.9.2)`);\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 isGroup = envelope.chatScope === 'group';\n\n // 群聊/私聊差异化 sessionKey:\n // - 群聊:group:{groupId}:coalescing 后缀(历史遗留命名,保留以延续存量群会话 lane;\n // 排队/合并现已由框架 followup 队列按该 sessionKey 处理)\n // - 私聊:保持原有格式,允许用户\"插嘴\"(新消息取消旧消息)\n const peerId = envelope.chatScope === 'group' \n ? (envelope.groupId ?? envelope.senderId) \n : envelope.senderId;\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: peerId,\n },\n }) ?? { \n sessionKey: isGroup \n ? `qqbot:${account.accountId}:group:${peerId}:coalescing`\n : `qqbot:${account.accountId}:${peerId}`, \n accountId: account.accountId \n };\n\n const qualifiedTarget = envelope.targetId;\n const agentId = route.agentId ?? 'default';\n\n // ── 群聊排队策略(排队/合并交给框架 followup 队列)──\n // - enabled=true → collect:活动 turn 期间到达的消息排队并在其后合并批处理\n // - enabled=false → followup:排队但不合并(尊重「关闭合并」的意图,\n // 仍保证不打断活动 turn——远比框架默认 steer 插嘴安全)\n const groupCfg = isGroup && envelope.groupId\n ? resolveGroupConfigFromAccount(account, envelope.groupId)\n : undefined;\n const queueModeOverride = groupCfg\n ? (groupCfg.coalesce.enabled ? 'collect' as const : 'followup' as const)\n : undefined;\n\n // ── room_event 分类(全量模式群的被动房间事件)──\n // wasMentioned 三种唤醒方式:@(AT 事件 / mentions.is_you / 内容标记,由\n // mentionGate 判定)、称呼(mentionPatterns,如「沈处」)、引用 bot 出站\n // (mentionGate isImplicitMention)。均未命中且群开启 room_event 时,\n // 该消息作为被动房间事件进框架:AI 只读上下文,最终文本不投递\n // (message_tool_only),想发言走主动 message 工具;框架自动压 typing/\n // 流式、排队不 steer。斜杠命令始终是显式用户意图 → user_request。\n const mentionState = (ctx.state as { mention?: { wasMentioned?: boolean; implicit?: boolean } }).mention;\n const nameMentioned = isGroup\n ? detectWasMentioned({\n eventType: (msg as { rawEventType?: string }).rawEventType,\n mentions: (msg as { mentions?: Array<{ is_you?: boolean }> }).mentions,\n content: msg.content,\n mentionPatterns: resolveMentionPatterns(cfg, agentId),\n })\n : false;\n const wasMentioned = !!(mentionState?.wasMentioned || mentionState?.implicit || nameMentioned);\n const isSlash = /^\\//.test(assembled.rawBody ?? '');\n const inboundEventKind =\n isGroup && groupCfg?.unmentionedInbound === 'room_event' && !wasMentioned && !isSlash\n ? 'room_event' as const\n : 'user_request' as const;\n\n // 分类可观测性:room_event 事件触发一次推理 pass(成本排障命门),必须留痕;\n // room_event 群里被唤醒为 user_request 时标注唤醒来源(@/称呼/引用),\n // 便于观察 mentionPatterns 与引用唤醒的实际命中\n if (inboundEventKind === 'room_event') {\n dlog?.info(\n `[room-event] passive room event group=${envelope.groupId} sender=${envelope.senderId} contentLen=${(msg.content ?? '').length}`,\n );\n } else if (isGroup && groupCfg?.unmentionedInbound === 'room_event' && !isSlash) {\n const wakeSource = mentionState?.wasMentioned\n ? 'mention'\n : mentionState?.implicit\n ? 'quote-bot'\n : nameMentioned\n ? 'name-pattern'\n : 'unknown';\n dlog?.info(`[wake] ${wakeSource} → user_request group=${envelope.groupId} sender=${envelope.senderId}`);\n }\n\n if (queueModeOverride && !frameworkQueueAnnounced) {\n frameworkQueueAnnounced = true;\n dlog?.info(`[queue] group turn queueing delegated to framework followup queue (mode=${queueModeOverride}); plugin coalescer bypassed`);\n }\n const storePath = adapters.resolveStorePath?.((cfg as any)?.session?.store, { agentId }) ?? '';\n\n const ctxPayload = buildCtxPayload({ assembled, envelope, route, msg, ctx, adapters });\n // room_event 分类标记:框架据此压制自动回复投递 / typing / steer\n // (对齐 telegram 的 ctxPayload.InboundEventKind 约定)\n ctxPayload.InboundEventKind = inboundEventKind;\n\n // TTS 扩展点探测(runtime.tts / runtimeContexts),非核心 channel API\n const ttsRuntime = (runtime as any)?.tts ?? (runtime as any)?.channel?.runtimeContexts?.get?.('tts'); // @adapter-bypass: TTS extension point probe\n\n const debounceConfig = account.config?.deliverDebounce;\n const debouncer = debounceConfig?.enabled !== false\n ? new DeliverDebouncer(debounceConfig, async (targetId, mergedText) => {\n const result = await sendText({ to: targetId, text: mergedText, accountId: account.accountId, replyToId: envelope.messageId, account });\n trackOutbound(result, 'debounce');\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 .then((result) => trackOutbound(result, 'deliverCtx.sendText')),\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 }).then((result) => trackOutbound(result, 'deliverCtx.sendMedia')),\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 let deliverErrorCount = 0;\n // 出站发送成功/失败计数:deliver-pipeline 对 sendText {error} 只记日志不抛错,\n // 这里在 dispatch 拥有的发送闭包边界上统计,作为\"用户是否收到可见回复\"的判据。\n let outboundSendOk = 0;\n let outboundSendFail = 0;\n const trackOutbound = <T extends { error?: string }>(result: T, via: string): T => {\n if (result.error) {\n outboundSendFail++;\n dlog?.error(`outbound send failed via ${via}: ${String(result.error)}`);\n } else {\n outboundSendOk++;\n }\n return result;\n };\n\n /**\n * deliver 回调(两个分支共用)。\n *\n * 失败语义(对齐 telegram):捕获后计数并记录日志,不中断后续 payload;\n * dispatch 结束后若 (deliver 失败 || dispatch 抛错) 且用户未收到任何可见回复,\n * 发送兜底消息,避免静默失败。\n */\n const deliverHandler = async (payload: DeliverPayload, info?: DeliverInfo): Promise<void> => {\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 // ── 0. ask_user 按钮投递(优先于所有其他处理)──\n // 单问题单选场景:用 inline keyboard 替代纯文本\n const payloadWithChannelData = payload as DeliverPayload & { channelData?: unknown };\n if (isAskUserPayload(payloadWithChannelData as any) && text) {\n const { questionId, optionValues } = (payloadWithChannelData as any).channelData.askUser;\n const questionRuntime = await getQuestionGatewayRuntime();\n {\n const keyboard = buildQuestionKeyboard(questionId, optionValues);\n const bot = tryGetBotForAccount(account.accountId);\n if (bot) {\n const replyTarget = {\n scope: envelope.chatScope === 'group' ? 'group' as const : 'c2c' as const,\n targetId: peerId,\n };\n try {\n await bot.sendTextWithKeyboard(replyTarget, text, keyboard as never);\n outboundSendOk++;\n dlog?.debug(`[question] sent ask_user with keyboard questionId=${questionId} options=${optionValues.length}`);\n return;\n } catch (err) {\n outboundSendFail++;\n dlog?.error(`[question] sendTextWithKeyboard failed: ${err instanceof Error ? err.message : String(err)}`);\n // fallback 到纯文本发送\n }\n }\n }\n }\n\n // ── 0b. ask_user 多问题投递:每题一条带按钮的消息。\n // 框架对多问题只投递纯文本(无结构化选项),这里从文本反解题目结构;\n // 按钮点选 / isOther 题的文字回复在回调与入站侧缓冲,\n // 集齐后合成一条\"用户文本回复\"走入站通道,由框架的文本应答\n // 解析器 resolve 挂起的 ask_user ──\n if (isNonSingleAskUserPayload(payloadWithChannelData as any) && text) {\n const { questionId } = (payloadWithChannelData as any).channelData.askUser;\n const questions = parseMultiQuestionPrompt(text);\n if (questions) {\n const bot = tryGetBotForAccount(account.accountId);\n if (bot) {\n const scope = envelope.chatScope === 'group' ? 'group' as const : 'c2c' as const;\n const replyTarget = { scope, targetId: peerId };\n // 先登记再发送,避免用户先点按钮时查无此单\n registerPendingMultiQuestion(questionId, scope, peerId, questions);\n try {\n for (const [index, question] of questions.entries()) {\n const cardText = formatMultiQuestionCard(question, index, questions.length);\n const keyboard = buildMultiQuestionKeyboard(questionId, index, question);\n await bot.sendTextWithKeyboard(replyTarget, cardText, keyboard as never);\n outboundSendOk++;\n }\n dlog?.debug(`[question] sent multi-question ask_user questionId=${questionId} questions=${questions.length}`);\n return;\n } catch (err) {\n outboundSendFail++;\n dlog?.error(`[question] multi-question send failed: ${err instanceof Error ? err.message : String(err)}`);\n // 已发出的卡片仍可点选;此处走纯文本兜底补全未送达部分\n }\n }\n } else {\n dlog?.debug(`[question] multi-question prompt unparseable questionId=${questionId}; plain text`);\n }\n }\n\n // ── 1. block: 媒体/语音立即发送,文本留给流式 ──\n // 注:static 模式已显式 disableBlockStreaming:true,kind:'block' 不会再触发;\n // 此分支保留以兼容 stream 模式与未来变化。\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 (streamingController.isStaticSendMode) {\n // static 模式:flush 主路径由 onToolStart 驱动(工具开始前,绕开 SDK\n // block streaming 的 coalescer,避免 minChars=800/idleMs=1000 buffer 延迟)。\n // 这里仅兜底:deliver(kind:'tool') 时再 flush 一次(controller 内部去重,\n // buffer 已空时 flushSegment 无副作用)。\n if (kind === 'tool') {\n await streamingController.flushSegment();\n }\n // static 模式不 finalize(finalize 会进入终态并造成后续丢失)\n } else if (kind !== 'block') {\n // stream 模式:tool/final 时收尾当前打字机流(原行为)\n await streamingController.finalize();\n }\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 媒体:立即转发(static 流式路径已在上方 flush 文本)──\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 deliverErrorCount++;\n dlog?.error(`deliver error: ${err instanceof Error ? err.message : String(err)}`);\n }\n };\n\n // Turn adoption lifecycle(群聊/私聊差异化):\n // - 私聊:exclusive + abortSignal,新消息取消旧消息(用户可\"插嘴\")\n // - 群聊:exclusive(框架 durable ingress 约定,对齐 telegram;cancel-only 是\n // gateway chat.send 的取消身份语义,不适用于通道入站)且不传 abortSignal——\n // 新消息由框架 followup 队列排队,不打断正在处理的 turn\n const turnAbort = new AbortController();\n const admission = 'exclusive' as const;\n // 仅私聊允许插嘴取消;群聊不打断(排队由框架 followup 队列处理)\n const interruptible = !isGroup;\n\n const turnAdoptionLifecycle = {\n admission,\n abortSignal: interruptible ? turnAbort.signal : undefined,\n onAdopted: () => {\n dlog?.debug(`turn adopted (exclusive, ${isGroup ? 'group' : 'c2c'}) sessionKey=${route.sessionKey}`);\n },\n onDeferred: () => {\n dlog?.debug(`turn deferred behind active turn sessionKey=${route.sessionKey}`);\n },\n onAbandoned: () => {\n if (interruptible) {\n dlog?.info(`turn abandoned (superseded) — aborting sessionKey=${route.sessionKey}`);\n turnAbort.abort();\n } else {\n dlog?.info(`group turn abandoned without owning reply lane sessionKey=${route.sessionKey}`);\n }\n },\n };\n const combinedAbortSignal = interruptible\n ? combineAbortSignals(ctx.signal, turnAbort.signal)\n : ctx.signal;\n\n let dispatchError: unknown;\n const hadDispatchError = () => dispatchError !== undefined;\n\n try {\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 runDispatchLifecycle: {\n // 同一 lifecycle 对象必须同时出现在 runDispatchLifecycle 与\n // replyOptions.turnAdoptionLifecycle(框架校验所有权一致性)。\n turnAdoptionLifecycle,\n onDispatchSkipped: (reason: string) => {\n dlog?.info(`dispatch skipped reason=${reason} sessionKey=${route.sessionKey}`);\n },\n },\n runDispatch: () => {\n return adapters.dispatchReply!({\n ctx: ctxPayload,\n cfg,\n dispatcherOptions: {\n deliver: deliverHandler,\n },\n replyOptions: {\n abortSignal: combinedAbortSignal,\n runId: envelope.messageId,\n turnAdoptionLifecycle,\n ...(queueModeOverride ? { queueModeOverride } : {}),\n ...(inboundEventKind === 'room_event' ? { sourceReplyDeliveryMode: 'message_tool_only' as const } : {}),\n ...(streamingController?.isStaticSendMode\n ? {\n // 对齐 telegram 模式一:显式关掉 SDK block streaming,绕开 coalescer\n // (minChars=800/idleMs=1000 的 buffer 会造成文本延迟)。\n // 文本由 onPartialReply 累积,边界由 onToolStart 自己监听 flush。\n disableBlockStreaming: true,\n // 让 onToolStart 在 verbose 关闭时也能触发\n // (默认受 requiresToolSummaryVisibility 门控,verbose off 时不触发)\n allowToolLifecycleWhenProgressHidden: true,\n // 工具【开始执行前】触发:把已累积的上一段文本立即发出\n // (不等工具执行完,对齐 telegram prepareAnswerLaneForToolProgress)\n onToolStart: async () => { await streamingController.flushSegment(); },\n }\n : {}),\n ...(streamingController\n ? {\n onPartialReply: async (p: { text?: string }) => {\n if (p.text) await streamingController.onPartialReply(p.text);\n },\n // 兜底:block 信号未覆盖的边界(如部分 provider 不发 text_end)\n // 仍由 onAssistantMessageStart 触发分段。stream 模式不传,保持原行为。\n onAssistantMessageStart: streamingController.isStaticSendMode\n ? async () => { await streamingController.flushSegment(); }\n : undefined,\n }\n : {}),\n },\n });\n },\n }),\n },\n });\n } catch (err) {\n dispatchError = err;\n dlog?.error(`dispatch failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n dlog?.debug(`dispatch completed sessionKey=${route.sessionKey}`);\n\n // 群消息回复后处理历史缓存:\n // - clear(默认):整清,下次 @ 时组包\"自上次回复以来\"的窗口\n // - rolling:裁剪到最后一条 bot 出站之后(bot 发言也计入历史,\n // AI 下次可看到自己上次说到哪;对齐 telegram selectAfterLastSelf)\n if (envelope.chatScope === 'group' && envelope.groupId) {\n const groupCfg = resolveGroupConfigFromAccount(account, envelope.groupId);\n if (groupCfg.historyMode === 'rolling') {\n const kept = trimGroupHistoryAfterLastBot(account.accountId, envelope.groupId, groupCfg.historyLimit);\n dlog?.debug(`[history] rolling trim group=${envelope.groupId} kept=${kept}`);\n } else {\n clearGroupHistory(account.accountId, envelope.groupId);\n }\n } else if (envelope.chatScope === 'group') {\n clearGroupHistory(account.accountId, 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 // 失败兜底(对齐 telegram):dispatch 抛错、deliver 抛错或底层发送失败,\n // 且用户未收到任何可见回复时,发送兜底消息而非静默失败。\n if ((hadDispatchError() || deliverErrorCount > 0 || outboundSendFail > 0) && !turnAbort.signal.aborted) {\n const streamedVisible = !!streamingController\n && streamingController.currentPhase !== 'failed'\n && (streamingController.currentPhase === 'done' || streamingController.hasSentChunks);\n const deliveredVisible =\n streamedVisible || outboundSendOk > 0 || deliveredMediaUrls.size > 0;\n if (!deliveredVisible) {\n dlog?.warn(\n `sending failure fallback (deliverErrors=${deliverErrorCount} sendFails=${outboundSendFail} dispatchError=${hadDispatchError()}) to ${qualifiedTarget}`,\n );\n try {\n const result = await sendText({\n to: qualifiedTarget,\n text: FAILURE_FALLBACK_TEXT,\n accountId: account.accountId,\n replyToId: envelope.messageId,\n account,\n });\n if (result.error) {\n dlog?.error(`failure fallback sendText failed: ${result.error}`);\n }\n } catch (err) {\n dlog?.error(`failure fallback failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n\n // 保持原有错误传播语义:错误上报给 event-handlers 记录日志\n if (hadDispatchError()) {\n throw dispatchError;\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\n // sendMode: 默认 'stream'(QQ 流式打印机),可选 'static'(普通 sendText 收尾)\n const streamingCfg = account.config?.streaming as\n | { sendMode?: 'stream' | 'static' }\n | undefined;\n const sendMode = streamingCfg?.sendMode === 'static' ? 'static' : 'stream';\n\n // static 模式:finalize 收尾时用一条普通 sendText 发完整文本\n const sendStatic = sendMode === 'static'\n ? async (fullText: string) => {\n const result = await sendText({\n to: envelope.senderId,\n text: fullText,\n accountId: account.accountId,\n replyToId: envelope.messageId,\n account,\n });\n if (result.error) {\n log?.error(`static sendText failed: ${result.error}`);\n }\n }\n : undefined;\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 sendMode,\n sendStatic,\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 * 出站 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\n /**\n * 正规媒体通道(对齐 core ChannelInboundMediaInput):\n * 全部附件(图片/语音/文件)按原始顺序作为 media facts 传入,\n * 字段名必须是 path(不是 localPath);url 用本地路径(QQ CDN URL 有时效),\n * 与 telegram toInboundMedia 的 { path, url: path, contentType, kind, transcribed } 一致。\n * media facts 经 core 归一化后生成 MediaPath/MediaPaths/MediaTypes/MediaTranscribedIndexes。\n */\n const mediaFacts = (processed?.media as Array<{\n kind: 'image' | 'audio' | 'video' | 'document';\n localPath?: string;\n remoteUrl?: string;\n contentType?: string;\n transcribed?: boolean;\n }> | undefined)?.map((m) => ({\n kind: m.kind,\n ...(m.localPath\n ? { path: m.localPath, url: m.localPath }\n : m.remoteUrl\n ? { url: m.remoteUrl }\n : {}),\n ...(m.contentType ? { contentType: m.contentType } : {}),\n ...(m.transcribed ? { transcribed: true } : {}),\n })) ?? [];\n\n const msgTimestamp = (msg as any).timestamp ?? (msg as any).Timestamp;\n\n // 群成员角色(平台 member_role:admin/owner)→ sender.roles,\n // 供框架群权限/工具策略分级使用;c2c / 未标注为空\n const memberRole = (msg as { raw?: { author?: { member_role?: string } } }).raw?.author?.member_role;\n const senderRoles = memberRole === 'admin' || memberRole === 'owner' ? [memberRole] : undefined;\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, ...(senderRoles ? { roles: senderRoles } : {}) },\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: mediaFacts.length > 0 ? mediaFacts : 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 type StreamingSendMode = 'stream' | 'static';\n\nexport interface StreamingControllerDeps {\n gateway: QQBotGateway;\n target: ReplyTarget;\n accountId: string;\n replyToId: string;\n log?: PluginLogger;\n /**\n * 文本下发通道:\n * - 'stream'(默认)QQ 流式打印机:openStream/update/complete\n * - 'static' 流结束时用一条普通 sendText 发完整文本\n * partial 接收逻辑(状态机/串行/去重)在两种模式下完全一致。\n */\n sendMode?: StreamingSendMode;\n /**\n * static 模式专用:在 finalize 收尾时把累积的完整文本一次性发出。\n * stream 模式下不使用。未提供时 static 模式将降级为 shouldFallbackToStatic。\n */\n sendStatic?: (fullText: string) => Promise<void>;\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 /** 是否走静态(普通 sendText)下发通道 */\n private get isStaticMode(): boolean { return this.deps.sendMode === 'static'; }\n\n /**\n * 是否处于 static 下发模式(公开访问器,供 dispatch 协同判断)。\n * static 模式下 dispatch 不应在 tool/final 事件提前 finalize,\n * 由 controller 靠 new_reply 检测自主分段,避免终态与发送延迟。\n */\n get isStaticSendMode(): boolean { return this.isStaticMode; }\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 /**\n * static 模式:把当前累积段立即发出并重置缓冲,**不进入终态**。\n * 供框架 onAssistantMessageStart 回调调用(工具调用后新一段推理开始时触发)。\n * 对齐 telegram rotateLaneForNewMessage:固化旧段 + 开始新段。\n * 无累积内容时跳过(第一段开始时 lastAcceptedFull 为空)。\n */\n flushSegment(): Promise<void> {\n this.chain = this.chain.then(async () => {\n if (this.isTerminal || !this.isStaticMode) return;\n if (this.lastAcceptedFull && this.deps.sendStatic) {\n this.deps.log?.info(`flush segment chars=${this.lastAcceptedFull.length}`);\n try {\n await this.deps.sendStatic(this.lastAcceptedFull);\n } catch (err) {\n this.deps.log?.error(`flushSegment send failed: ${err instanceof Error ? err.message : String(err)}`);\n this.transition('failed', 'flush_send_error');\n return;\n }\n // 重置缓冲,开始新一段累积(保持 streaming 态,不进终态)\n this.lastAcceptedFull = '';\n }\n }).catch((err) => {\n this.deps.log?.error(`flushSegment error: ${err instanceof Error ? err.message : String(err)}`);\n this.transition('failed', 'flush_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 // static 模式:分段由框架 onAssistantMessageStart 明确驱动(flushSegment)。\n // 前缀不匹配时视为新一段开始 → 直接覆盖累积(不发送,发送由 flushSegment 负责)。\n // 不依赖\"长度回退\"启发式(长度相等时会误判为追加导致文本粘连)。\n if (this.isStaticMode) {\n this.deps.log?.info(`static new segment: lastAccepted=${this.lastAcceptedFull.length}→chunk=${text.length}`);\n this.lastAcceptedFull = text;\n this.sentChunkCount++;\n return;\n }\n\n // stream 模式:前缀不匹配 — 长度回退 → 新回复(工具调用后)\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 // 静态模式:用累积的完整文本一次性 sendStatic 收尾\n if (this.isStaticMode) {\n if (this.sentChunkCount > 0 && this.lastAcceptedFull && this.deps.sendStatic) {\n await this.completeSession();\n // completeSession 内部 sendStatic 失败会先转 failed,此处不可回退为 done\n if (!this.isTerminal) {\n this.transition('done', 'finalize');\n this.deps.log?.info(`static done chunks=${this.sentChunkCount} chars=${this.lastAcceptedFull.length}`);\n }\n } else if (this.sentChunkCount > 0 && this.lastAcceptedFull) {\n // 未提供 sendStatic → 降级标记,由上层兜底发送\n this.transition('failed', 'finalize:no_sendstatic');\n } else {\n this.transition('done', 'finalize:fallback');\n }\n return;\n }\n\n // stream 模式:用已下发文本收尾 — 不用框架 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('done', 'finalize:fallback');\n }\n }\n\n // ── QQ 交互 ──\n\n private async sendUpdate(text: string): Promise<void> {\n // 静态模式:仅累积文本到内存,不发任何网络请求;\n // 收尾时由 completeSession 一次性调用 sendStatic 发送完整文本。\n if (this.isStaticMode) {\n if (this.phase === 'idle') {\n this.transition('streaming', 'first_chunk');\n this.deps.log?.info(`static accumulate (firstChunk=${text.length})`);\n }\n this.lastAcceptedFull = text;\n this.sentChunkCount++;\n return;\n }\n\n // stream 模式:打开/复用流式会话并下发分片\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 // 静态模式:把累积的完整文本一次性发出\n if (this.isStaticMode) {\n if (this.deps.sendStatic && this.lastAcceptedFull) {\n this.deps.log?.info(`static send (sent=${this.sentChunkCount} chars=${this.lastAcceptedFull.length} reason=${reason ?? 'done'})`);\n try {\n await this.deps.sendStatic(this.lastAcceptedFull);\n } catch (err) {\n this.deps.log?.error(`static send failed: ${err instanceof Error ? err.message : String(err)}`);\n this.transition('failed', 'static_send_error');\n }\n }\n return;\n }\n\n // stream 模式:关闭流式会话,发送 DONE 帧\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 return !!streaming && streaming.mode !== 'off';\n}\n","/**\n * QQBotGateway 事件处理\n *\n * 处理 SDK 的 message / interaction 事件:\n * - message: 中间件处理完毕后,将消息转发到 OpenClaw AI\n * - interaction: 配置更新 / 审批按钮 / ask_user 按钮\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 { authorizeQQBotApprovalAction } from '../features/approval-capability.js';\nimport { parseApprovalButtonData } from '../features/approval-helpers.js';\nimport {\n getQuestionGatewayRuntime,\n parseQuestionButtonData,\n parseMultiQuestionButtonData,\n recordMultiQuestionTap,\n markMultiQuestionResolveFailed,\n getPendingMultiQuestions,\n buildMultiQuestionAnswerText,\n formatMultiQuestionConfirmCard,\n buildMultiQuestionConfirmKeyboard,\n type MultiQuestionAnswer,\n} from '../features/question-helpers.js';\nimport { recordKnownUser } from '../features/proactive.js';\nimport { cacheMsgId } from '../features/msgid-cache.js';\nimport { recordGroupEvent, getGroupModeFacts } from '../features/group-mode-store.js';\nimport { getAdapters } from '../adapter/resolve.js';\nimport { resolveGroupConfigFromAccount, resolveGroupPolicy, resolveMentionPatterns } from '../config.js';\nimport { getPackageVersion } from '../utils/pkg-version.js';\nimport { getOpenClawVersion, tryGetBotForAccount } from '../bot-instance.js';\nimport type { ParsedMultiQuestionAction } from '../features/question-helpers.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 hlog.debug(`enter msgId=${msg.messageId} scope=${scope} contentLen=${(msg.content ?? '').length}`);\n\n try {\n // 群推送模式推断(AT 系 vs 全量):按事件类型记录;首次观测与模式变化都打 INFO\n // 留痕(模式由群主设置、可随时变,事后排障靠这两条日志定位「为什么收得到/收不到消息」)\n if (scope === 'group') {\n const seenBefore = getGroupModeFacts(account.accountId, msg.replyTarget.targetId);\n const modeChange = recordGroupEvent(\n account.accountId,\n msg.replyTarget.targetId,\n (msg as { rawEventType?: string }).rawEventType ?? '',\n Array.isArray((msg as { msgElements?: unknown[] }).msgElements),\n );\n const current = getGroupModeFacts(account.accountId, msg.replyTarget.targetId);\n if (modeChange) {\n hlog.info(`[group-mode] push mode changed to ${modeChange} for group=${msg.replyTarget.targetId}`);\n } else if (!seenBefore && current) {\n hlog.info(`[group-mode] first observation mode=${current.mode} group=${msg.replyTarget.targetId}`);\n }\n }\n\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 // ── 多问题 ask_user 的最终答案不在此拦截 ──\n // 确认卡的指令按钮代发(或用户手打)的 \"1: 3\\n2: 1\" 是真实用户消息,\n // 必须原样放行走框架原生 keyed 文本认领;任何拦截都会破坏认领链路。\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 // question 按钮(ask_user,含单问题 qqbot:q: 与多问题 qqbot:qm:)优先于审批按钮\n const buttonData = event.data?.resolved?.button_data;\n if (buttonData?.startsWith('qqbot:q:') || buttonData?.startsWith('qqbot:qm:')) {\n await handleQuestion(event, account, runtime, log, acknowledgeInteraction);\n return;\n }\n\n await handleApproval(event, account, runtime, 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 runtime: PluginRuntime,\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 const parsed = parseApprovalButtonData(buttonData);\n if (!parsed) return;\n\n // Live config snapshot so allowFrom changes apply without a restart.\n // getConfig returns `any` — keep it untyped here to avoid crossing the\n // project's hand-written ambient SDK types with the real SDK types.\n const cfg = getAdapters(runtime).getConfig?.() ?? {};\n\n // 身份授权校验:操作者需在 allowFrom 白名单中\n const operatorId = resolveOperatorId(event);\n const authorization = authorizeQQBotApprovalAction({\n cfg,\n accountId: account.accountId,\n senderId: operatorId,\n });\n if (!authorization.authorized) {\n log.warn(`[approval] unauthorized operator=${operatorId ?? 'unknown'} account=${account.accountId}${authorization.reason ? ` reason=${authorization.reason}` : ''}`);\n return;\n }\n\n try {\n const { resolveApprovalOverGateway } = await import('openclaw/plugin-sdk/approval-gateway-runtime');\n await resolveApprovalOverGateway({\n cfg,\n approvalId: parsed.approvalId,\n approvalKind: parsed.approvalKind,\n decision: parsed.decision,\n senderId: operatorId,\n clientDisplayName: 'QQBot Approval Handler',\n });\n } catch (err) {\n log.error(`interaction approve error: ${err instanceof Error ? err.message : String(err)}`);\n }\n}\n\n// ── Question 按钮处理(ask_user)──\n\nasync function handleQuestion(\n event: InteractionEvent,\n account: ResolvedQQBotAccount,\n runtime: PluginRuntime,\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) return;\n\n const parsed = parseQuestionButtonData(buttonData);\n if (!parsed) {\n const multiParsed = parseMultiQuestionButtonData(buttonData);\n if (multiParsed) {\n await handleMultiQuestionTap(multiParsed, event, account, runtime, log);\n }\n return;\n }\n\n // 无授权检查:ask_user 问题面向所有参与者(与审批不同,question 不是安全敏感操作)。\n // 框架侧 questionGatewayRuntime.resolveOption 已处理过期/重复提交等边界情况。\n const operatorId = resolveOperatorId(event);\n const cfg = getAdapters(runtime).getConfig?.() ?? {};\n\n try {\n const questionGatewayRuntime = await getQuestionGatewayRuntime();\n const result = await questionGatewayRuntime.resolveOption({\n cfg,\n questionId: parsed.questionId,\n optionIndex: parsed.optionIndex,\n senderId: operatorId,\n clientDisplayName: 'QQBot question',\n });\n log.debug(`[question] resolved questionId=${parsed.questionId} optionIndex=${parsed.optionIndex} status=${result.status}`);\n } catch (err) {\n log.error(`[question] resolve error: ${err instanceof Error ? err.message : String(err)}`);\n }\n}\n\n// ── 多问题按钮处理(ask_user questions >= 2)──\n// 框架对多问题记录没有单题按钮解析通道(resolveOption 仅支持单问题),\n// 这里按题缓冲点选,集齐后发一张\"确认卡\":指令按钮(action.type=2,\n// enter=true)携带完整答案文本 \"1: 3\\n2: 1\",由 QQ 客户端以真实用户\n// 消息发出,走框架原生的 keyed 文本认领通道 resolve 挂起的 ask_user。\n// 注意两条红线(都出过生产事故,勿回退):\n// 1. 不要程序化提交:官方 openclaw 无多问题提交 API,改官方代码要背\n// fork 维护成本;\n// 2. 不要合成入站消息\"假装用户回答\":合成消息会被框架当成 steering\n// 注入挂起的 run,claim 不触发,模型等答案直到超时(2026-08-24 事故)。\n// 只有真实用户消息(客户端代发或手打)能被框架认领。\n\nasync function handleMultiQuestionTap(\n parsed: ParsedMultiQuestionAction,\n event: InteractionEvent,\n account: ResolvedQQBotAccount,\n runtime: PluginRuntime,\n log: PluginLogger,\n): Promise<void> {\n const operatorId = resolveOperatorId(event);\n const scope: 'group' | 'c2c' = event.group_openid ? 'group' : 'c2c';\n const peerId = (scope === 'group' ? event.group_openid : event.user_openid) ?? '';\n if (!peerId) return; // 缺少会话定位信息,无法回执或发确认卡\n\n log.info(\n `[question] multi tap id=${parsed.questionId} q=${parsed.questionIndex + 1} opt=${parsed.optionIndex + 1} operator=${operatorId ?? 'unknown'}`,\n );\n\n const tap = recordMultiQuestionTap(parsed.questionId, parsed.questionIndex, parsed.optionIndex);\n\n if (tap.status === 'unknown' || tap.status === 'terminal') {\n log.info(`[question] multi tap ignored id=${parsed.questionId} status=${tap.status}`);\n await sendMultiQuestionFeedback(event, account, '该问题已提交或已过期');\n return;\n }\n if (tap.status === 'resolving') {\n log.debug(`[question] multi tap dropped while resolving id=${parsed.questionId}`);\n return;\n }\n if (tap.status === 'buffered') {\n const pendingTitles = tap.pendingQuestions.map((q) => q.header);\n log.info(`[question] multi buffered id=${parsed.questionId} answered=${tap.answeredCount}/${tap.total}`);\n await sendMultiQuestionFeedback(\n event,\n account,\n `✅ 已记录 ${tap.answeredCount}/${tap.total}\\n还剩:${pendingTitles.join('、')}`,\n );\n return;\n }\n // 按钮点选路径不会产生 nomatch(选项序号来自键盘本身)\n if (tap.status !== 'complete') return;\n\n // complete:发确认卡,由用户一键代发真实答案消息\n await sendMultiQuestionConfirmCard(\n parsed.questionId,\n tap.answers,\n scope,\n peerId,\n account,\n log,\n );\n}\n\n/**\n * 集齐后发确认卡。正文按题展示已选内容,键盘带两个指令按钮:\n * 「✅ 提交」(enter=true,客户端自动以用户身份发出答案文本)与\n * 「✏️ 改一改」(填入输入框,用户编辑后手动发送)。\n * 发出后复位 resolving 锁——用户可以继续点选修改答案,再拿到新确认卡。\n */\nasync function sendMultiQuestionConfirmCard(\n questionId: string,\n answers: ReadonlyMap<number, MultiQuestionAnswer>,\n scope: 'c2c' | 'group',\n peerId: string,\n account: ResolvedQQBotAccount,\n log: PluginLogger,\n): Promise<void> {\n const answerText = buildMultiQuestionAnswerText(answers);\n const questions = getPendingMultiQuestions(questionId);\n const cardText = questions\n ? formatMultiQuestionConfirmCard(questions, answers)\n : `**✅ 答案确认**\\n\\n${answerText}`;\n\n const bot = tryGetBotForAccount(account.accountId);\n if (!bot) {\n markMultiQuestionResolveFailed(questionId);\n log.error(`[question] confirm card send failed: bot unavailable id=${questionId}`);\n await sendMultiQuestionFeedbackText(scope, peerId, account, '⚠️ 提交卡发送失败,请直接文字回复答案');\n return;\n }\n try {\n await bot.sendTextWithKeyboard({ scope, targetId: peerId }, cardText, buildMultiQuestionConfirmKeyboard(answerText) as never);\n markMultiQuestionResolveFailed(questionId); // 复位锁,允许继续改答案\n log.info(`[question] confirm card sent id=${questionId} answers=${JSON.stringify(answerText)}`);\n } catch (err) {\n markMultiQuestionResolveFailed(questionId);\n log.error(`[question] confirm card send error id=${questionId}: ${err instanceof Error ? err.message : String(err)}`);\n await sendMultiQuestionFeedbackText(scope, peerId, account, '⚠️ 提交卡发送失败,请直接文字回复答案');\n }\n}\n\n/** 尽力而为的状态回执(主动消息),失败仅记日志不影响主流程 */\nasync function sendMultiQuestionFeedback(\n event: InteractionEvent,\n account: ResolvedQQBotAccount,\n text: string,\n): Promise<void> {\n const scope = event.group_openid ? 'group' as const : 'c2c' as const;\n const targetId = event.group_openid ?? event.user_openid;\n if (!targetId) return;\n await sendMultiQuestionFeedbackText(scope, targetId, account, text);\n}\n\nasync function sendMultiQuestionFeedbackText(\n scope: 'c2c' | 'group',\n targetId: string,\n account: ResolvedQQBotAccount,\n text: string,\n): Promise<void> {\n const bot = tryGetBotForAccount(account.accountId);\n if (!bot) return;\n try {\n await bot.sendText({ scope, targetId }, text);\n } catch {\n // 主动消息可能触发平台限频;回执是锦上添花,吞掉即可\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\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 * QQ Bot Approval Capability — SDK-native approval handling.\n *\n * Replaces the former hand-rolled QQBotApprovalHandler. The framework\n * auto-bootstraps a native approval handler from this capability (registered\n * on qqbotPlugin.approvalCapability + the \"approval.native\" runtime context\n * declared in gateway/lifecycle.ts), and feeds exec/plugin approval requests\n * back to the availability / presentation / transport callbacks below.\n *\n * Model: QQBot has no separate `execApprovals` approver config — it uses the\n * account `allowFrom` list. With no allowFrom, any participant in the\n * originating conversation may approve (open mode). This is the \"fallback\"\n * path from the in-tree extension, intentionally simpler than the profile\n * path.\n */\n\nimport { createChannelApprovalCapability } from \"openclaw/plugin-sdk/approval-delivery-runtime\";\nimport { resolveApprovalRequestSessionConversation } from \"openclaw/plugin-sdk/approval-native-runtime\";\nimport {\n createChannelApprovalNativeRuntimeAdapter,\n createLazyChannelApprovalNativeRuntimeAdapter,\n CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY,\n} from \"openclaw/plugin-sdk/approval-handler-runtime\";\nimport type {\n ChannelApprovalNativeRuntimeAdapter,\n} from \"openclaw/plugin-sdk/approval-handler-runtime\";\nimport type {\n ChannelApprovalCapability,\n} from \"openclaw/plugin-sdk/channel-contract\";\nimport { normalizeOptionalString } from \"openclaw/plugin-sdk/string-coerce-runtime\";\nimport type { OpenClawConfig } from \"openclaw/plugin-sdk/core\";\nimport { DEFAULT_ACCOUNT_ID, resolveQQBotAccount } from \"../config.js\";\nimport { tryGetBotForAccount } from \"../bot-instance.js\";\nimport { getQQBotRuntime } from \"../runtime.js\";\nimport { getAdapters } from \"../adapter/resolve.js\";\nimport type { InlineKeyboard, ResolvedQQBotAccount } from \"../types.js\";\nimport {\n buildApprovalKeyboard,\n buildExecApprovalText,\n buildPluginApprovalText,\n buildSystemAgentApprovalText,\n resolveApprovalTarget,\n} from \"./approval-helpers.js\";\n\nexport { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY };\n\n// ─── helpers ────────────────────────────────────────────────\n\nfunction resolveActiveConfig(): OpenClawConfig {\n // Prefer the live runtime config snapshot (reflects config reloads); fall\n // back to an empty object if the runtime is not yet registered.\n const rt = getQQBotRuntime();\n const cfg = getAdapters(rt).getConfig?.();\n return (cfg ?? {}) as OpenClawConfig;\n}\n\nfunction resolveAccountById(cfg: OpenClawConfig, accountId?: string | null): ResolvedQQBotAccount {\n return resolveQQBotAccount(cfg, accountId && accountId !== DEFAULT_ACCOUNT_ID ? accountId : undefined);\n}\n\n/** account is enabled and has usable credentials → can deliver native approvals */\nfunction isNativeDeliveryEnabled(cfg: OpenClawConfig, accountId?: string | null): boolean {\n const account = resolveAccountById(cfg, accountId);\n return account.enabled && account.secretSource !== \"none\";\n}\n\n/** Resolve the QQ delivery target for an approval request. */\nfunction resolveQQTarget(request: {\n request: { sessionKey?: string | null; turnSourceTo?: string | null };\n}): { type: \"c2c\" | \"group\"; id: string } | null {\n const sessionKey = request.request.sessionKey ?? null;\n const turnSourceTo = request.request.turnSourceTo ?? null;\n\n const target = resolveApprovalTarget(sessionKey, turnSourceTo);\n if (target) return target;\n\n const sessionConversation = resolveApprovalRequestSessionConversation({\n request: request as never,\n channel: \"qqbot\",\n bundledFallback: true,\n });\n if (sessionConversation?.id) {\n const kind = sessionConversation.kind;\n return { type: kind === \"group\" ? \"group\" : \"c2c\", id: sessionConversation.id };\n }\n return null;\n}\n\n/**\n * Per-account ownership: only the account that originated the turn should\n * deliver (openids are account-scoped — cross-account delivery fails with\n * HTTP 500 on the QQ Bot API).\n */\nfunction matchesAccount(\n cfg: OpenClawConfig,\n accountId: string | null | undefined,\n request: { request: { turnSourceAccountId?: string | null } },\n): boolean {\n const reqAccountId = normalizeOptionalString(request.request.turnSourceAccountId);\n if (!reqAccountId) return true; // no account hint → accept (ownership left to caller)\n const normalized = (accountId && accountId !== DEFAULT_ACCOUNT_ID) ? accountId : DEFAULT_ACCOUNT_ID;\n return reqAccountId === normalized;\n}\n\n// ─── authorization (allowFrom) ──────────────────────────────\n\n/**\n * Decide whether a button-clicker may approve. Mirrors the legacy\n * isApprovalAuthorized rule: empty allowFrom (or [\"*\"]) → open; otherwise the\n * operator must be in the account's allowFrom list.\n */\nexport function authorizeQQBotApprovalAction(params: {\n cfg: OpenClawConfig;\n accountId?: string | null;\n senderId?: string | null;\n}): { authorized: boolean; reason?: string } {\n const operatorId = normalizeOptionalString(params.senderId);\n if (!operatorId) {\n return { authorized: false, reason: \"Could not identify the operator.\" };\n }\n const account = resolveAccountById(params.cfg, params.accountId);\n const allowFrom = account.config?.allowFrom ?? [];\n if (!allowFrom.length || allowFrom.includes(\"*\")) {\n return { authorized: true };\n }\n if (allowIncludes(allowFrom, operatorId)) {\n return { authorized: true };\n }\n return { authorized: false, reason: \"You are not authorized to approve this request.\" };\n}\n\nfunction allowIncludes(allowFrom: Array<string | number>, operatorId: string): boolean {\n const normalized = operatorId.toUpperCase().replace(/^qqbot:/i, \"\");\n return allowFrom.some((entry) => {\n const e = String(entry).trim().toUpperCase().replace(/^qqbot:/i, \"\");\n return e === normalized;\n });\n}\n\n// ─── native runtime spec (availability / presentation / transport) ──\n\ntype QQBotPendingPayload = { text: string; keyboard: InlineKeyboard };\ntype QQBotPreparedTarget = { type: \"c2c\" | \"group\"; id: string };\ntype QQBotPendingEntry = { messageId?: string; targetType: \"c2c\" | \"group\"; targetId: string };\n\nconst qqbotApprovalNativeRuntime = createChannelApprovalNativeRuntimeAdapter<\n QQBotPendingPayload,\n QQBotPreparedTarget,\n QQBotPendingEntry\n>({\n eventKinds: [\"exec\", \"plugin\", \"system-agent\"],\n\n availability: {\n isConfigured: ({ cfg, accountId }) =>\n isNativeDeliveryEnabled(cfg, accountId),\n\n shouldHandle: ({ cfg, accountId, request }) => {\n const target = resolveQQTarget(request as never);\n if (!target) return false;\n return matchesAccount(cfg, accountId, request as never);\n },\n },\n presentation: {\n buildPendingPayload: ({ view, nowMs }) => {\n const text =\n view.approvalKind === \"exec\"\n ? buildExecApprovalText(view, nowMs)\n : view.approvalKind === \"system-agent\"\n ? buildSystemAgentApprovalText(view, nowMs)\n : buildPluginApprovalText(view, nowMs);\n const keyboard = buildApprovalKeyboard(\n view.approvalId,\n view.approvalKind,\n view.actions.map((action) => action.decision),\n );\n return { text, keyboard };\n },\n buildResolvedResult: () => ({ kind: \"leave\" }),\n buildExpiredResult: () => ({ kind: \"leave\" }),\n },\n\n transport: {\n prepareTarget: ({ request }) => {\n const target = resolveQQTarget(request as never);\n if (!target) return null;\n return { target, dedupeKey: `${target.type}:${target.id}` };\n },\n\n deliverPending: async ({ accountId, preparedTarget, pendingPayload }) => {\n // Resolve the bot for this account (gateway must be running — it is, because\n // the \"approval.native\" runtime context only exists while the account is started).\n const resolvedAccountId =\n accountId && accountId !== DEFAULT_ACCOUNT_ID ? accountId : DEFAULT_ACCOUNT_ID;\n const bot = tryGetBotForAccount(resolvedAccountId);\n if (!bot) {\n throw new Error(\n `QQ Bot gateway not running for account \"${resolvedAccountId}\" — cannot deliver approval`,\n );\n }\n\n const replyTarget = {\n scope: preparedTarget.type as \"c2c\" | \"group\",\n targetId: preparedTarget.id,\n };\n const result = await bot.sendTextWithKeyboard(\n replyTarget,\n pendingPayload.text,\n pendingPayload.keyboard as never,\n );\n const messageId = (result as { id?: string } | undefined)?.id;\n return {\n messageId,\n targetType: preparedTarget.type,\n targetId: preparedTarget.id,\n };\n },\n },\n});\n\n// ─── capability assembly ────────────────────────────────────\n\nfunction createQQBotApprovalCapability(): ChannelApprovalCapability {\n return createChannelApprovalCapability({\n authorizeActorAction: ({ cfg, accountId, senderId }) =>\n authorizeQQBotApprovalAction({ cfg, accountId, senderId }),\n\n getActionAvailabilityState: ({ cfg, accountId }) =>\n isNativeDeliveryEnabled(cfg, accountId)\n ? { kind: \"enabled\" as const }\n : { kind: \"disabled\" as const },\n\n getExecInitiatingSurfaceState: ({ cfg, accountId }) =>\n isNativeDeliveryEnabled(cfg, accountId)\n ? { kind: \"enabled\" as const }\n : { kind: \"disabled\" as const },\n\n describeExecApprovalSetup: ({ accountId }: { accountId?: string | null }) => {\n const prefix =\n accountId && accountId !== DEFAULT_ACCOUNT_ID\n ? `channels.qqbot.accounts.${accountId}`\n : \"channels.qqbot\";\n return `QQBot native approvals are enabled by default. To restrict who can approve, configure \\`${prefix}.allowFrom\\` with QQ user OpenIDs.`;\n },\n\n delivery: {\n hasConfiguredDmRoute: () => true,\n shouldSuppressForwardingFallback: (input) => {\n const channel = normalizeOptionalString(input.target?.channel);\n if (channel !== \"qqbot\") return false;\n const accountId =\n normalizeOptionalString(input.target?.accountId) ??\n normalizeOptionalString(input.request?.request?.turnSourceAccountId);\n return isNativeDeliveryEnabled(resolveActiveConfig(), accountId);\n },\n },\n\n native: {\n describeDeliveryCapabilities: ({ cfg, accountId }) => ({\n enabled: isNativeDeliveryEnabled(cfg, accountId),\n preferredSurface: \"origin\" as const,\n supportsOriginSurface: true,\n supportsApproverDmSurface: false,\n notifyOriginWhenDmOnly: false,\n }),\n resolveOriginTarget: ({ request }) => {\n const sessionKey = request.request.sessionKey ?? null;\n const turnSourceTo = request.request.turnSourceTo ?? null;\n const target = resolveApprovalTarget(sessionKey, turnSourceTo);\n if (target) return { to: `${target.type}:${target.id}` };\n const sessionConversation = resolveApprovalRequestSessionConversation({\n request: request as never,\n channel: \"qqbot\",\n bundledFallback: true,\n });\n if (sessionConversation?.id) {\n const kind = sessionConversation.kind === \"group\" ? \"group\" : \"c2c\";\n return { to: `${kind}:${sessionConversation.id}` };\n }\n return null;\n },\n },\n\n nativeRuntime: createLazyChannelApprovalNativeRuntimeAdapter({\n eventKinds: [\"exec\", \"plugin\", \"system-agent\"],\n isConfigured: ({ cfg, accountId }) => isNativeDeliveryEnabled(cfg, accountId),\n shouldHandle: ({ cfg, accountId, request }) => {\n const target = resolveQQTarget(request as never);\n if (!target) return false;\n return matchesAccount(cfg, accountId, request as never);\n },\n load: async () =>\n qqbotApprovalNativeRuntime as unknown as ChannelApprovalNativeRuntimeAdapter,\n }),\n });\n}\n\nlet cachedCapability: ChannelApprovalCapability | undefined;\n\nexport function getQQBotApprovalCapability(): ChannelApprovalCapability {\n cachedCapability ??= createQQBotApprovalCapability();\n return cachedCapability;\n}\n","/**\n * Approval helpers — pure functions shared by the approval capability and the\n * interaction (button-click) handler.\n *\n * Ported from the in-tree openclaw qqbot extension so the external plugin stays\n * byte-compatible with the framework's approval view model:\n * - buildApprovalKeyboard reads `allowedDecisions` from the SDK view (fixes\n * the hardcoded-3-button bug where skill_workshop approvals rejected\n * `allow-always`).\n * - button_data uses the v2 format `approve:v2:<kind>:<encodedId>:<decision>`\n * so the kind is recoverable at click time without ID-prefix sniffing.\n */\n\nimport type {\n ExecApprovalPendingView,\n PendingApprovalView,\n PluginApprovalPendingView,\n} from \"openclaw/plugin-sdk/approval-handler-runtime\";\nimport { resolveExecApprovalCommandDisplay } from \"openclaw/plugin-sdk/approval-runtime\";\nimport { truncateUtf16Safe } from \"openclaw/plugin-sdk/text-utility-runtime\";\nimport type { InlineKeyboard, KeyboardButton } from \"../types.js\";\n\n// ============ Types ============\n\nexport type ApprovalKind = \"exec\" | \"plugin\" | \"system-agent\";\n\n// The SDK re-exports only the pending-view union, not the system-agent member.\nexport type SystemAgentApprovalPendingView = Extract<\n PendingApprovalView,\n { approvalKind: \"system-agent\" }\n>;\nexport type ApprovalDecision = \"allow-once\" | \"allow-always\" | \"deny\";\n\nexport interface ApprovalTarget {\n type: \"c2c\" | \"group\";\n id: string;\n}\n\nexport interface ParsedApprovalAction {\n approvalId: string;\n approvalKind: ApprovalKind;\n decision: ApprovalDecision;\n}\n\n// ============ Text Builders ============\n\nconst COMMAND_PREVIEW_MAX_LENGTH = 300;\nconst COMMAND_PREVIEW_GRAPHEMES_PER_LINE = 24;\nconst COMMAND_PREVIEW_WRAP_MARKER = \"↩\";\nconst commandPreviewSegmenter =\n typeof Intl !== \"undefined\" && \"Segmenter\" in Intl\n ? new Intl.Segmenter(undefined, { granularity: \"grapheme\" })\n : null;\n\nfunction splitCommandPreviewGraphemes(commandText: string): string[] {\n return commandPreviewSegmenter\n ? Array.from(commandPreviewSegmenter.segment(commandText), ({ segment }) => segment)\n : Array.from(commandText);\n}\n\nfunction formatCommandPreview(commandText: string): string {\n // QQ Desktop does not wrap fenced blocks. The sanitized view has already escaped real command\n // newlines, so these grapheme-safe line breaks are presentation-only and unambiguous. Limiting\n // each line to 24 graphemes also bounds common double-width text to roughly 48 columns.\n const lines = [\"\"];\n const displayText = commandText.replaceAll(COMMAND_PREVIEW_WRAP_MARKER, \"\\\\u{21A9}\");\n let previewLength = 0;\n let lineGraphemes = 0;\n let truncated = false;\n let wrapped = false;\n for (const grapheme of splitCommandPreviewGraphemes(displayText)) {\n if (previewLength + grapheme.length > COMMAND_PREVIEW_MAX_LENGTH) {\n if (previewLength === 0) {\n lines[0] = truncateUtf16Safe(grapheme, COMMAND_PREVIEW_MAX_LENGTH);\n }\n truncated = true;\n break;\n }\n previewLength += grapheme.length;\n if (lineGraphemes === COMMAND_PREVIEW_GRAPHEMES_PER_LINE) {\n lines[lines.length - 1] += COMMAND_PREVIEW_WRAP_MARKER;\n lines.push(\"\");\n lineGraphemes = 0;\n wrapped = true;\n }\n lines[lines.length - 1] += grapheme;\n lineGraphemes += 1;\n }\n const preview = `${lines.join(\"\\n\")}${truncated ? \"\\n…[truncated]\" : \"\"}`;\n const longestBacktickRun = Math.max(0, ...(preview.match(/`+/g)?.map((run) => run.length) ?? []));\n const fence = \"`\".repeat(Math.max(3, longestBacktickRun + 1));\n const block = `${fence}\\n${preview}\\n${fence}`;\n return wrapped\n ? `${COMMAND_PREVIEW_WRAP_MARKER} = display wrap only; not command text\\n${block}`\n : block;\n}\n\nfunction formatApprovalMetadata(value: string): string {\n const sanitized = resolveExecApprovalCommandDisplay({ command: value }).commandText;\n return formatCommandPreview(sanitized);\n}\n\nexport function buildExecApprovalText(view: ExecApprovalPendingView, nowMs = Date.now()): string {\n const expiresIn = Math.max(0, Math.round((view.expiresAtMs - nowMs) / 1000));\n const lines: string[] = [\"🔐 命令执行审批\", \"\"];\n if (view.commandText) {\n lines.push(formatCommandPreview(view.commandText));\n }\n if (view.cwd) {\n lines.push(`📁 目录:\\n${formatApprovalMetadata(view.cwd)}`);\n }\n if (view.agentId) {\n lines.push(`🤖 Agent:\\n${formatApprovalMetadata(view.agentId)}`);\n }\n lines.push(\"\", `⏱️ 超时: ${expiresIn} 秒`);\n return lines.join(\"\\n\");\n}\n\nexport function buildPluginApprovalText(\n view: PluginApprovalPendingView,\n nowMs = Date.now(),\n): string {\n const expiresIn = Math.max(0, Math.round((view.expiresAtMs - nowMs) / 1000));\n const severityIcon =\n view.severity === \"critical\" ? \"🔴\"\n : view.severity === \"info\" ? \"🔵\"\n : \"🟡\";\n\n const lines: string[] = [`${severityIcon} 审批请求`, \"\"];\n lines.push(`📋 ${view.title}`);\n if (view.description) lines.push(`📝 ${view.description}`);\n if (view.toolName) lines.push(`🔧 工具: ${view.toolName}`);\n if (view.pluginId) lines.push(`🔌 插件: ${view.pluginId}`);\n if (view.agentId) lines.push(`🤖 Agent: ${view.agentId}`);\n lines.push(\"\", `⏱️ 超时: ${expiresIn} 秒`);\n return lines.join(\"\\n\");\n}\n\nexport function buildSystemAgentApprovalText(\n view: SystemAgentApprovalPendingView,\n nowMs = Date.now(),\n): string {\n const expiresIn = Math.max(0, Math.round((view.expiresAtMs - nowMs) / 1000));\n\n const lines: string[] = [\"🛡️ 系统变更审批\", \"\"];\n lines.push(`📋 ${view.title}`);\n if (view.description) lines.push(`📝 ${view.description}`);\n if (view.operationSummary) lines.push(`📊 ${view.operationSummary}`);\n if (view.commandText) {\n lines.push(formatCommandPreview(view.commandText));\n }\n if (view.cwd) {\n lines.push(`📁 目录:\\n${formatApprovalMetadata(view.cwd)}`);\n }\n if (view.host) lines.push(`🖥️ 主机: ${view.host}`);\n if (view.agentId) lines.push(`🤖 Agent:\\n${formatApprovalMetadata(view.agentId)}`);\n lines.push(\"\", `⏱️ 超时: ${expiresIn} 秒`);\n return lines.join(\"\\n\");\n}\n\n// ============ Keyboard Builder ============\n\n/**\n * Build the inline keyboard for approval messages.\n *\n * type=1 (Callback): click triggers INTERACTION_CREATE, button_data = data field.\n * group_id \"approval\": clicking one button grays out the others (mutual exclusion).\n * click_limit=1: each user can only click once.\n * permission.type=2: all users can interact.\n *\n * Only buttons whose decision is in `allowedDecisions` are rendered — the SDK\n * view drives this, so e.g. skill_workshop approvals (which restrict to\n * [\"allow-once\",\"deny\"]) no longer offer an invalid \"allow-always\" button.\n */\nexport function buildApprovalKeyboard(\n approvalId: string,\n approvalKind: ApprovalKind,\n allowedDecisions: readonly ApprovalDecision[] = [\"allow-once\", \"allow-always\", \"deny\"],\n): InlineKeyboard {\n const actionPrefix = `approve:v2:${approvalKind}:${encodeURIComponent(approvalId)}`;\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\n const buttons: KeyboardButton[] = [];\n if (allowedDecisions.includes(\"allow-once\")) {\n buttons.push(makeBtn(\"allow\", \"✅ 允许一次\", \"已处理\", `${actionPrefix}:allow-once`, 1));\n }\n if (allowedDecisions.includes(\"allow-always\")) {\n buttons.push(makeBtn(\"always\", \"⭐ 始终允许\", \"已处理\", `${actionPrefix}:allow-always`, 1));\n }\n if (allowedDecisions.includes(\"deny\")) {\n buttons.push(makeBtn(\"deny\", \"❌ 拒绝\", \"已处理\", `${actionPrefix}:deny`, 0));\n }\n\n return {\n content: {\n rows: [{ buttons }],\n },\n };\n}\n\n// ============ Target Resolver ============\n\n/**\n * Extract the delivery target from a sessionKey or turnSourceTo string.\n *\n * Expected formats:\n * agent:main:qqbot:c2c:OPENID -> { type: \"c2c\", id: \"OPENID\" }\n * agent:main:qqbot:group:GROUPID -> { type: \"group\", id: \"GROUPID\" }\n *\n * Returns null if neither field matches the expected pattern.\n */\nexport function resolveApprovalTarget(\n sessionKey: string | null | undefined,\n turnSourceTo: string | null | undefined,\n): ApprovalTarget | null {\n const sk = sessionKey ?? turnSourceTo;\n if (!sk) return null;\n const m = sk.match(/qqbot:(c2c|group):([A-F0-9]+)/i);\n if (!m) return null;\n const scope = m[1];\n const id = m[2];\n if (scope === undefined || id === undefined) return null;\n const type: \"c2c\" | \"group\" = scope.toLowerCase() === \"group\" ? \"group\" : \"c2c\";\n return { type, id };\n}\n\n// ============ Interaction Parser ============\n\n/**\n * Parse the button_data string from an INTERACTION_CREATE event.\n *\n * Expected format: `approve:v2:<approvalKind>:<encodedApprovalId>:<decision>`.\n * The approvalKind is baked into the payload so the resolve path doesn't need\n * to infer it from the ID prefix, and the ID is URL-encoded so colons in\n * `exec:<uuid>` / `plugin:<uuid>` / `system-agent:<uuid>` IDs survive\n * unambiguously.\n *\n * Returns null if the data does not match the approval button format.\n */\nexport function parseApprovalButtonData(buttonData: string): ParsedApprovalAction | null {\n const m = buttonData.match(/^approve:v2:(exec|plugin|system-agent):([^:]+):(allow-once|allow-always|deny)$/);\n if (!m || m[0] !== buttonData) return null;\n const kind = m[1] as ApprovalKind;\n const encodedId = m[2];\n const decision = m[3] as ApprovalDecision;\n if (!encodedId) return null;\n let approvalId: string;\n try {\n approvalId = decodeURIComponent(encodedId);\n } catch {\n return null;\n }\n if (!approvalId) return null;\n return { approvalId, approvalKind: kind, decision };\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 * 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 * 指令面板自动同步(QQ Bot 指令面板 API,/v2/panels,2026-08-12 上线)\n *\n * gateway ready 后(initFeatures 触发)把 openclaw tier=essential 的原生指令\n * 注册到 QQ Bot 指令面板,c2c / group 各一个(target_type=all)。\n * 用户点击面板项 → 指令文本填入输入框 → 用户发送 → 以普通文本进入框架 →\n * 框架文本斜杠指令路由执行(本插件不声明 capabilities.nativeCommands,\n * 避免与 commands.text=false 配置冲突)。\n *\n * 平台约束(官方文档):\n * - 面板项 name ≤14 字符、desc ≤30 字符、每面板 ≤20 项、每 bot ≤20 个面板\n * - POST /v2/panels 限 10 QPM — 常规同步 2 GET(c2c+group 各一页)+ 2 POST,余量充足\n * - GET /v2/panels 限 30 QPM,**scope 为必填查询参数**(缺失报 40030011 生效场景不合法),\n * 列表分页(next_cursor / is_end)\n *\n * 幂等识别:自家面板用 panel.remark = \"openclaw-qqbot:auto:<scope>\" 打标;\n * remark 不匹配的面板(用户在开放平台手动创建的)绝不修改或删除。\n *\n * 同步时机:每进程每账号仅一次(防 WebSocket resumed 重连时 onReady 重复触发);\n * 同步失败会释放 once-guard,下次重连可重试。全程错误只记日志,绝不阻断启动。\n */\nimport type { OpenClawConfig } from 'openclaw/plugin-sdk/config-contracts';\nimport {\n findCommandByNativeName,\n listNativeCommandSpecsForConfig,\n} from 'openclaw/plugin-sdk/native-command-registry';\nimport type { NativeCommandSpec } from 'openclaw/plugin-sdk/native-command-registry';\nimport { tryGetBotForAccount } from '../bot-instance.js';\nimport type { ResolvedQQBotAccount } from '../types.js';\nimport type { PluginLogger } from '../utils/plugin-logger.js';\n\n/** 自家面板的 remark 前缀(后接 scope:c2c / group) */\nconst PANEL_REMARK_PREFIX = 'openclaw-qqbot:auto:';\n/** 同步的面板 scope(channel/dm 属于频道产品,本插件不涉及) */\nconst PANEL_SCOPES = ['c2c', 'group'] as const;\n/** 平台限制:面板项 name 最大长度(约 7 个汉字) */\nconst PANEL_ITEM_NAME_MAX = 14;\n/** 平台限制:面板项 desc 最大长度(约 15 个汉字) */\nconst PANEL_ITEM_DESC_MAX = 30;\n/** 平台限制:每面板最大项数 */\nconst PANEL_ITEM_LIMIT = 20;\n/** 列表分页:每页条数(官方上限 50);循环上限防意外无限翻页 */\nconst PANEL_LIST_PAGE_SIZE = 50;\nconst PANEL_LIST_MAX_PAGES = 5;\n/** openclaw 命令注册表的 provider 标识 */\nconst QQBOT_PROVIDER = 'qqbot';\n\n/** 面板项(type=command:点击后 name 文本填入聊天输入框) */\nexport interface CommandPanelItem {\n type: 'command';\n name: string;\n desc: string;\n}\n\n/** SDK ApiGateway 的结构子集(便于测试注入 fake) */\nexport interface CommandPanelApi {\n get<T = unknown>(path: string, query?: Record<string, string | number | boolean>): Promise<T>;\n post<T = unknown>(path: string, body?: unknown): Promise<T>;\n put<T = unknown>(path: string, body?: unknown): Promise<T>;\n delete<T = unknown>(path: string): Promise<T>;\n}\n\n/** GET /v2/panels 返回的面板记录(字段位置防御式兼容两种嵌套) */\ninterface PanelRecord {\n panel_id?: string;\n id?: string;\n remark?: string;\n panel?: { remark?: string; version?: number } | null;\n}\n\n/** 单个 scope 的同步结果 */\nexport interface CommandPanelSyncResult {\n scope: string;\n action: 'created' | 'updated';\n itemCount: number;\n duplicatesRemoved: number;\n}\n\n// ── 纯函数:指令选取 ──\n\n/**\n * 从原生指令 spec 列表选取 essential 指令并映射为面板项。\n * tierOf 为指令名 → tier 的回查函数(NativeCommandSpec 不携带 tier,\n * 需经 findCommandByNativeName 回查 ChatCommandDefinition;undefined 视为 standard)。\n */\nexport function selectEssentialCommands(\n specs: readonly NativeCommandSpec[],\n tierOf: (nativeName: string) => string | undefined,\n log?: PluginLogger,\n): CommandPanelItem[] {\n const items: CommandPanelItem[] = [];\n for (const spec of specs) {\n if (spec.isAlias) continue;\n if (tierOf(spec.name) !== 'essential') continue;\n const name = `/${spec.name}`;\n if (name.length > PANEL_ITEM_NAME_MAX) {\n log?.warn(\n `[command-panel] 指令 ${name} 超过面板 name 上限 ${PANEL_ITEM_NAME_MAX} 字符,未注册(用户仍可手动输入)`,\n );\n continue;\n }\n items.push({ type: 'command', name, desc: truncateDesc(spec.description) });\n if (items.length >= PANEL_ITEM_LIMIT) break;\n }\n return items;\n}\n\n/** desc 归一为单行并截断到平台上限 */\nfunction truncateDesc(desc: string): string {\n const cleaned = (desc ?? '').replace(/\\s+/g, ' ').trim();\n return cleaned.length > PANEL_ITEM_DESC_MAX ? cleaned.slice(0, PANEL_ITEM_DESC_MAX) : cleaned;\n}\n\n// ── 同步核心(可独立测试) ──\n\n/**\n * 把 items 整包同步到 c2c / group 两个面板(幂等):\n * 无自家面板 → POST 创建;有一个 → PUT 覆盖;有重复 → PUT 第一个并 DELETE 其余。\n * remark 不匹配的面板不触碰。\n */\nexport async function syncAccountCommandPanels(\n api: CommandPanelApi,\n items: readonly CommandPanelItem[],\n log?: PluginLogger,\n): Promise<CommandPanelSyncResult[]> {\n const byScope = await listPanels(api, log);\n const results: CommandPanelSyncResult[] = [];\n for (const scope of PANEL_SCOPES) {\n const remark = panelRemark(scope);\n const mine = byScope[scope].filter((p) => panelRemarkOf(p) === remark);\n if (mine.length === 0) {\n await api.post('/v2/panels', {\n scope,\n target_type: 'all',\n panel: { items, remark },\n });\n results.push({ scope, action: 'created', itemCount: items.length, duplicatesRemoved: 0 });\n continue;\n }\n const [keep, ...duplicates] = mine;\n await api.put(`/v2/panels/${panelIdOf(keep)}`, {\n panel: { items, remark },\n });\n let removed = 0;\n for (const dup of duplicates) {\n const id = panelIdOf(dup);\n if (!id) continue;\n await api.delete(`/v2/panels/${id}`);\n removed++;\n }\n results.push({ scope, action: 'updated', itemCount: items.length, duplicatesRemoved: removed });\n }\n return results;\n}\n\n/** 列表按 scope 分桶:GET 必传 scope,每个 scope 的同步只看自己场景的面板 */\ntype PanelsByScope = Record<(typeof PANEL_SCOPES)[number], PanelRecord[]>;\n\nasync function listPanels(api: CommandPanelApi, log?: PluginLogger): Promise<PanelsByScope> {\n // 官方文档(bot.q.qq.com GET /v2/panels):scope 为必填查询参数,缺失时服务端报\n // 40030011「生效场景不合法」;列表按 next_cursor / is_end 分页。故按 scope 各查一遍。\n const byScope: PanelsByScope = { c2c: [], group: [] };\n for (const scope of PANEL_SCOPES) {\n let cursor = '';\n for (let page = 0; page < PANEL_LIST_MAX_PAGES; page++) {\n const query: Record<string, string | number | boolean> = { scope, limit: PANEL_LIST_PAGE_SIZE };\n if (cursor) query.cursor = cursor;\n const raw: unknown = await api.get('/v2/panels', query);\n const records = extractPanelRecords(raw);\n byScope[scope].push(...records);\n if (records.length === 0 && page === 0) {\n log?.debug(\n `[command-panel] GET /v2/panels?scope=${scope} 拉到 0 条记录,响应形状: ${JSON.stringify(raw).slice(0, 500)}`,\n );\n }\n cursor = extractNextCursor(raw);\n if (!cursor || extractIsEnd(raw)) break;\n }\n }\n return byScope;\n}\n\n/** 官方列表响应形如 { records, next_cursor, is_end };旧/未知形状无分页字段时返回空串 */\nfunction extractNextCursor(raw: unknown): string {\n if (raw && typeof raw === 'object' && !Array.isArray(raw)) {\n const v = (raw as Record<string, unknown>).next_cursor;\n if (typeof v === 'string') return v;\n }\n return '';\n}\n\nfunction extractIsEnd(raw: unknown): boolean {\n if (raw && typeof raw === 'object' && !Array.isArray(raw)) {\n const v = (raw as Record<string, unknown>).is_end;\n if (typeof v === 'boolean') return v;\n }\n return false;\n}\n\nfunction extractPanelRecords(raw: unknown): PanelRecord[] {\n if (Array.isArray(raw)) return raw.filter(isPanelRecord);\n if (raw && typeof raw === 'object') {\n for (const key of ['records', 'panels', 'data', 'list', 'items'] as const) {\n const value = (raw as Record<string, unknown>)[key];\n if (Array.isArray(value)) return value.filter(isPanelRecord);\n }\n }\n return [];\n}\n\nfunction isPanelRecord(value: unknown): value is PanelRecord {\n return Boolean(value) && typeof value === 'object';\n}\n\nfunction panelRemark(scope: string): string {\n return PANEL_REMARK_PREFIX + scope;\n}\n\n/** remark 可能扁平在记录上,也可能嵌在 panel 对象里 */\nfunction panelRemarkOf(record: PanelRecord): string | undefined {\n return record.remark ?? record.panel?.remark ?? undefined;\n}\n\nfunction panelIdOf(record: PanelRecord): string | undefined {\n return record.panel_id ?? record.id ?? undefined;\n}\n\n// ── 编排入口(initFeatures 调用) ──\n\n/** 每进程每账号只同步一次(防 resumed 重连重复触发) */\nconst syncedAccounts = new Set<string>();\n\n/** 测试钩子:清除 once-guard */\nexport function clearCommandPanelSynced(accountId?: string): void {\n if (accountId === undefined) syncedAccounts.clear();\n else syncedAccounts.delete(accountId);\n}\n\n/**\n * 同步账号的指令面板(fire-and-forget,错误只记日志)。\n * 任何异常都不抛出,绝不影响消息收发。\n */\nexport async function syncCommandPanels(\n account: ResolvedQQBotAccount,\n cfg: unknown,\n log?: PluginLogger,\n): Promise<void> {\n if (!account.commandPanelNative) return;\n if (syncedAccounts.has(account.accountId)) return;\n\n const bot = tryGetBotForAccount(account.accountId);\n if (!bot) {\n log?.debug(`[command-panel] bot 未就绪,跳过本次面板同步: ${account.accountId}`);\n return;\n }\n\n syncedAccounts.add(account.accountId);\n try {\n const items = buildEssentialItems(cfg, log);\n if (items.length === 0) {\n log?.warn('[command-panel] 未选取到任何 essential 指令,跳过面板同步(避免清空现有面板)');\n return;\n }\n const results = await syncAccountCommandPanels(bot.api, items, log);\n for (const r of results) {\n log?.info(\n `[command-panel] ${r.scope} 面板已${r.action === 'created' ? '创建' : '更新'}:${r.itemCount} 项指令` +\n (r.duplicatesRemoved > 0 ? `,清理重复面板 ${r.duplicatesRemoved} 个` : ''),\n );\n }\n } catch (err) {\n // 失败释放 once-guard:resumed 重连会再次触发 initFeatures,提供一次重试机会\n syncedAccounts.delete(account.accountId);\n log?.warn(`[command-panel] 指令面板同步失败(不影响消息收发): ${formatError(err)}`);\n }\n}\n\n/** 枚举 openclaw 原生指令并选取 essential 子集 */\nfunction buildEssentialItems(cfg: unknown, log?: PluginLogger): CommandPanelItem[] {\n let specs: NativeCommandSpec[];\n try {\n specs = listNativeCommandSpecsForConfig(cfg as OpenClawConfig, {\n provider: QQBOT_PROVIDER,\n includeBundledChannelFallback: false,\n });\n } catch (err) {\n log?.warn(`[command-panel] 枚举 openclaw 原生指令失败: ${formatError(err)}`);\n return [];\n }\n return selectEssentialCommands(\n specs,\n (name) =>\n findCommandByNativeName(name, QQBOT_PROVIDER, { includeBundledChannelFallback: false })?.tier,\n log,\n );\n}\n\nfunction formatError(err: unknown): string {\n if (err instanceof Error) return err.message;\n return String(err);\n}\n","/**\n * QQBot Typing 续期管理\n *\n * 续期时机:50 秒后\n * 最大续期:10 次(约 10 分钟)\n */\n\nimport type { TypingParams, TypingState } from './types-plugin.js';\nimport { checkAndConsumePassiveReplyQuota, inferQQBotScope } from './features/quota-manager.js';\n\nconst activeTypingSessions = new Map<string, TypingState>();\n\nconst TYPING_RENEWAL_MS = 50 * 1000;\nconst MAX_RENEWALS = 10;\n\nexport async function startTypingWithRenewal(\n params: TypingParams & {\n sendTyping: (params: { to: string; msgId?: string }) => Promise<boolean>;\n },\n): Promise<void> {\n const { accountId, to, replyToId, log, sendTyping } = params;\n const sessionKey = `${accountId}:${to}:${replyToId}`;\n\n if (activeTypingSessions.has(sessionKey)) {\n return;\n }\n\n const scope = inferQQBotScope(to);\n\n if (scope !== 'c2c') {\n log?.debug?.(`[${accountId}] typing not supported for scope: ${scope}`);\n return;\n }\n\n if (!replyToId) {\n log?.debug?.(`[${accountId}] typing requires replyToId`);\n return;\n }\n\n const { canReply: canPassiveReply, rollback } = checkAndConsumePassiveReplyQuota({\n accountId,\n msgId: replyToId,\n scope: 'c2c',\n log,\n });\n\n const msgIdToSend = canPassiveReply ? replyToId : undefined;\n\n if (!canPassiveReply) {\n log?.debug?.(`[${accountId}] typing falling back to proactive mode: quota exhausted`);\n }\n\n const sent = await sendTyping({ to, msgId: msgIdToSend });\n if (!sent) {\n rollback();\n log?.debug?.(`[${accountId}] typing start failed: send failed`);\n return;\n }\n\n log?.debug?.(`[${accountId}] typing started: ${sessionKey}`);\n\n const timer = setTimeout(async () => {\n await handleTypingRenewal({\n sessionKey,\n accountId,\n to,\n replyToId,\n log,\n sendTyping,\n });\n }, TYPING_RENEWAL_MS);\n\n activeTypingSessions.set(sessionKey, {\n timer,\n startedAt: Date.now(),\n renewalCount: 0,\n });\n}\n\nasync function handleTypingRenewal(\n params: TypingParams & {\n sessionKey: string;\n sendTyping: (params: { to: string; msgId?: string }) => Promise<boolean>;\n },\n): Promise<void> {\n const { sessionKey, accountId, to, replyToId, log, sendTyping } = params;\n\n const state = activeTypingSessions.get(sessionKey);\n if (!state) {\n return;\n }\n\n if (state.renewalCount >= MAX_RENEWALS) {\n log?.debug?.(`[${accountId}] typing stopped: max renewals reached`);\n activeTypingSessions.delete(sessionKey);\n return;\n }\n\n const { canReply: canPassiveReply, rollback } = checkAndConsumePassiveReplyQuota({\n accountId,\n msgId: replyToId,\n scope: 'c2c',\n log,\n });\n\n const msgIdToSend = canPassiveReply ? replyToId : undefined;\n\n if (!canPassiveReply) {\n log?.debug?.(`[${accountId}] typing renewal falling back to proactive mode: quota exhausted`);\n }\n\n const renewed = await sendTyping({ to, msgId: msgIdToSend });\n if (!renewed) {\n rollback();\n log?.debug?.(`[${accountId}] typing renewal failed: send failed`);\n activeTypingSessions.delete(sessionKey);\n return;\n }\n\n state.renewalCount += 1;\n log?.debug?.(`[${accountId}] typing renewed #${state.renewalCount}: ${sessionKey}`);\n\n state.timer = setTimeout(async () => {\n await handleTypingRenewal(params);\n }, TYPING_RENEWAL_MS);\n\n activeTypingSessions.set(sessionKey, state);\n}\n\nexport function stopTyping(params: {\n accountId: string;\n to: string;\n replyToId: string;\n log?: {\n debug?: (message: string) => void;\n };\n}): void {\n const { accountId, to, replyToId, log } = params;\n const sessionKey = `${accountId}:${to}:${replyToId}`;\n\n const state = activeTypingSessions.get(sessionKey);\n if (state) {\n clearTimeout(state.timer);\n activeTypingSessions.delete(sessionKey);\n log?.debug?.(`[${accountId}] typing stopped: ${sessionKey}`);\n }\n}\n\nexport function cleanupAllTyping(): void {\n for (const [, state] of activeTypingSessions.entries()) {\n clearTimeout(state.timer);\n }\n activeTypingSessions.clear();\n}\n\nexport function isTypingActive(accountId: string, replyToId: string): boolean {\n for (const [key] of activeTypingSessions.entries()) {\n if (key.startsWith(`${accountId}:`) && key.endsWith(`:${replyToId}`)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * 清理指定账户的所有 typing session\n * 在账户停止时调用,防止旧 session 阻塞新 session\n */\nexport function cleanupTypingByAccount(accountId: string): void {\n const keysToDelete: string[] = [];\n \n for (const [key, state] of activeTypingSessions.entries()) {\n if (key.startsWith(`${accountId}:`)) {\n clearTimeout(state.timer);\n keysToDelete.push(key);\n }\n }\n \n for (const key of keysToDelete) {\n activeTypingSessions.delete(key);\n }\n}\n","/**\n * QQBot Gateway 适配器\n *\n * 账户启动/停止/登出\n */\n\nimport type { ResolvedQQBotAccount } from './types.js';\nimport {\n startAccountWithCredentialRecovery,\n stopAccountGracefully,\n logoutAndClearCredentials,\n} from './gateway/lifecycle.js';\nimport type { OpenClawConfig } from 'openclaw/plugin-sdk/config-contracts';\n\n/**\n * QQBot Gateway Adapter\n */\nexport const qqbotGatewayAdapter = {\n startAccount: async (ctx: {\n account: ResolvedQQBotAccount;\n accountId: string;\n abortSignal: AbortSignal;\n cfg: OpenClawConfig;\n log?: any;\n getStatus: () => Record<string, unknown>;\n setStatus: (status: any) => void;\n }) => {\n return startAccountWithCredentialRecovery(ctx);\n },\n\n stopAccount: async (ctx: { accountId: string; log?: any }) => {\n await stopAccountGracefully({\n accountId: ctx.accountId,\n log: ctx.log,\n });\n },\n\n logoutAccount: async (params: {\n accountId: string;\n cfg: OpenClawConfig;\n }) => {\n return logoutAndClearCredentials(params);\n },\n};\n","/**\n * QQBot Heartbeat 适配器\n *\n * Typing 指示器\n */\n\nimport { startTypingWithRenewal, stopTyping } from './typing-lifecycle.js';\nimport { inferQQBotScope } from './features/quota-manager.js';\nimport { getGateway } from './outbound/outbound-service.js';\nimport { tryParseTarget } from './outbound/target.js';\n\n/**\n * QQBot Heartbeat Adapter\n */\nexport const qqbotHeartbeatAdapter = {\n sendTyping: async (params: {\n cfg: any;\n to: string;\n accountId?: string | null;\n threadId?: string | number | null;\n replyToId?: string;\n }) => {\n const { to, accountId, replyToId } = params;\n\n if (!accountId || !replyToId) {\n return;\n }\n\n const scope = inferQQBotScope(to);\n if (scope !== 'c2c') {\n return;\n }\n\n await startTypingWithRenewal({\n accountId,\n to,\n replyToId,\n sendTyping: async ({ to, msgId }) => {\n const gw = getGateway(accountId);\n if (!gw) return false;\n\n const target = tryParseTarget(to);\n if (!target) {\n return false;\n }\n\n const targetWithMsgId = msgId ? { ...target, msgId } : target;\n try {\n await gw.sendTyping(targetWithMsgId);\n return true;\n } catch {\n return false;\n }\n },\n });\n },\n};\n","/**\n * QQBot Agent Prompt 适配器\n *\n * 提供 inboundFormattingHints 等提示信息\n */\n\n/**\n * QQBot Agent Prompt Adapter\n */\nexport const qqbotAgentPromptAdapter = {\n messageToolCapabilities: ({ cfg, accountId }: { cfg?: any; accountId?: string | null }) => {\n return ['inlineButtons'];\n },\n\n inboundFormattingHints: ({ accountId }: { accountId?: string | null }) => {\n return {\n text_markup: 'markdown',\n rules: [\n 'QQ Bot 原生支持 Markdown 渲染',\n '支持标题、列表、代码块、引用等',\n '不支持 HTML 标签',\n '媒体使用 https URL',\n ],\n };\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 * qqbot_secret_input 工具 — 聊天内密钥输入卡片\n *\n * 用户想交给 AI 一个新密钥/凭据时,host AI 调用本工具(而非让用户把\n * 密钥粘贴进对话)。工具给当前 c2c 用户发一张输入卡片并登记 pending,\n * 用户下一条消息由 secretCapture 中间件拦截,插件就地执行\n * `openclaw secrets store set <NAME> --kind env --value ...` 并直接回执。\n *\n * kind 恒为 env(代理可读环境变量):聊天卡片收来的值本就落在 QQ 聊天\n * 记录里,且 AI 随后要读取使用;secret kind 是「受保护的机密」\n * (write-only,代理不可读),存进去 AI 反而用不了(2026-08-30 用户反馈)。\n *\n * 非阻塞:工具发卡后立即返回,AI 应结束本轮;执行与回执完全由插件自治。\n */\nimport type { OpenClawPluginApi } from 'openclaw/plugin-sdk';\nimport { tryGetBotForAccount } from '../bot-instance.js';\nimport { isValidSecretName } from '../features/secret-store-cli.js';\nimport {\n DEFAULT_SECRET_INPUT_TTL_MS,\n cancelPendingSecretInput,\n registerPendingSecretInput,\n} from '../features/secret-input-store.js';\nimport { getRequestAccountId, getRequestTarget } from '../request-context.js';\nimport { parseTarget } from '../outbound/target.js';\nimport type { InlineKeyboard, KeyboardButton } from '../types.js';\n\n// ========== JSON Schema ==========\n\nconst SecretInputSchema = {\n type: 'object',\n properties: {\n name: {\n type: 'string',\n description:\n '环境变量名。命名规则:以大写字母开头,仅含大写字母/数字/下划线,' +\n '最长 128 字符。推荐语义化命名,如 GITHUB_TOKEN、OPENWEATHER_API_KEY、' +\n 'DEEPSEEK_API_KEY。值将以代理可读环境变量(kind=env)保存,AI 之后可直接读取使用。',\n },\n description: {\n type: 'string',\n description: '该密钥的用途说明(可选),会展示在输入卡片上,帮助用户确认。',\n },\n },\n required: ['name'],\n} as const;\n\n// ========== 卡片构建 ==========\n\nfunction buildSecretInputCard(\n name: string,\n description: string | undefined,\n ttlMinutes: number,\n): string {\n const lines = [`**🔒 密钥输入 · ${name}**`];\n if (description) lines.push('', `用途:${description}`);\n lines.push(\n '',\n `请在下方输入框直接粘贴并发送 **${name}** 的值:`,\n '',\n '- 将保存为**代理可读环境变量**(AI 之后可直接读取使用),' +\n `写入方式:\\`openclaw secrets store set ${name} --kind env\\``,\n '- 你发送的消息会被插件直接拦截执行,**不会进入 AI 对话**',\n '- 密钥会留存在你的聊天记录中,请留意所在环境安全',\n `- ${ttlMinutes} 分钟内有效;发送「取消」可退出`,\n );\n return lines.join('\\n');\n}\n\n/** 「取消」指令按钮:action.type=2 + enter=true,客户端点击后以真实用户消息发出「取消」 */\nfunction buildSecretInputKeyboard(): InlineKeyboard {\n const buttons: KeyboardButton[] = [\n {\n id: 'secret_input_cancel',\n render_data: {\n label: '❌ 取消',\n visited_label: '已取消',\n style: 1 as const,\n },\n action: {\n type: 2 as const,\n data: '取消',\n enter: true,\n permission: { type: 2 as const },\n unsupport_tips: '当前客户端不支持,请手动发送 取消',\n },\n },\n ];\n return { content: { rows: [{ buttons }] } };\n}\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\nexport function registerSecretInputTool(api: OpenClawPluginApi): void {\n api.registerTool(\n {\n name: 'qqbot_secret_input',\n label: 'QQBot Secret Input Card',\n description:\n '当用户要交付一个新密钥/凭据(API Key、Token 等)给系统保存时调用:' +\n '向用户私聊发送一张密钥输入卡片,用户把密钥粘贴在输入框发送后,' +\n '插件会自动拦截该消息并执行 openclaw secrets store set 完成保存,' +\n '结果由插件直接回复用户。密钥内容不会进入对话。' +\n '必须由用户提供 name 参数(环境变量名)。',\n parameters: SecretInputSchema,\n async execute(_toolCallId, params) {\n const p = params as { name?: string; description?: string };\n const name = (p.name ?? '').trim();\n if (!name) return json({ error: 'name 为必填参数(环境变量名)' });\n if (!isValidSecretName(name)) {\n return json({\n error:\n '环境变量名不合法:必须以大写字母开头,仅含大写字母/数字/下划线,最长 128 字符' +\n `(收到 \"${name}\")。示例:OPENWEATHER_API_KEY。请修正后重试。`,\n });\n }\n // 恒为 env:见文件头注释(卡片值 = 代理可读环境,secret 是 write-only 代理不可读)\n const kind = 'env' as const;\n\n const target = getRequestTarget();\n const accountId = getRequestAccountId() ?? 'default';\n if (!target) {\n return json({ error: '无法获取当前会话目标,此工具仅支持在 QQ Bot 消息会话中使用' });\n }\n const parsed = parseTarget(target);\n if (parsed.scope !== 'c2c' || !parsed.targetId) {\n return json({\n error:\n `密钥输入卡片仅支持私聊(c2c)会话,当前目标 ${target} 不适用` +\n '——密钥不应出现在群聊中。请引导用户在私聊中发起。',\n });\n }\n\n const bot = tryGetBotForAccount(accountId);\n if (!bot) {\n return json({ error: `账户 ${accountId} 的 gateway 尚未启动,无法发送密钥输入卡片` });\n }\n\n const description = p.description?.trim() || undefined;\n // 先登记后发卡(避免「先发后登」竞态,同 multi-question 约定)\n registerPendingSecretInput({\n accountId,\n senderOpenid: parsed.targetId,\n name,\n kind,\n description,\n createdAt: Date.now(),\n });\n try {\n await bot.sendTextWithKeyboard(\n { scope: 'c2c', targetId: parsed.targetId },\n buildSecretInputCard(name, description, DEFAULT_SECRET_INPUT_TTL_MS / 60_000),\n buildSecretInputKeyboard() as never,\n );\n } catch (err) {\n cancelPendingSecretInput(accountId, parsed.targetId);\n return json({\n error: `密钥输入卡片发送失败:${err instanceof Error ? err.message : String(err)}`,\n });\n }\n\n return json({\n ok: true,\n name,\n kind,\n command: `openclaw secrets store set ${name} --kind env --value <用户输入>`,\n instruction:\n '卡片已发送给用户。用户下一条私聊消息将被插件自动拦截(不会进入对话),' +\n `执行上述命令保存密钥,成功/失败由插件直接回复用户。` +\n '请直接结束本轮(可告知用户按卡片提示发送密钥),不要等待用户输入,' +\n '也不要让用户把密钥内容发到对话里。',\n });\n },\n },\n { name: 'qqbot_secret_input' },\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\nconst OPTIONAL: ApiProbe[] = [\n {\n name: 'channel.reply.dispatchReplyWithBufferedBlockDispatcher',\n probe: (rt) => typeof (rt as any).channel?.reply?.dispatchReplyWithBufferedBlockDispatcher === 'function',\n },\n { name: 'channel.inbound.run (degraded)', probe: (rt) => typeof (rt as any).channel?.inbound?.run === 'function' },\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', probe: (rt) => typeof (rt as any).channel?.session?.resolveStorePath === 'function' },\n { name: 'channel.session.recordInboundSession', probe: (rt) => typeof (rt as any).channel?.session?.recordInboundSession === '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 /** 是否自动同步 openclaw essential 原生指令到 QQ Bot 指令面板(默认 true) */\n commandPanelNative: boolean;\n /** User-Agent 尾部追加内容 */\n userAgentSuffix: string;\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 GroupCoalesceConfig {\n /**\n * 是否启用消息合并(默认 true):\n * - true:框架 collect 合并批处理\n * - false:followup 排队不合并\n * 排队/合并完全由 OpenClaw 框架的 followup 队列承担。\n */\n enabled?: boolean;\n}\n\n/** 指令面板配置 */\nexport interface CommandsConfig {\n /**\n * 是否把 openclaw essential 原生指令自动注册到 QQ Bot 指令面板(默认 true)。\n * 面板项点击后指令文本填入输入框,以普通文本进入框架,走文本斜杠指令路由执行。\n */\n native?: boolean;\n}\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 * 群历史清理时机(默认 clear):\n * - clear:每次回复后整清(旧语义,\"自上次回复以来的窗口\")\n * - rolling:回复后裁剪到最后一条 bot 出站之后(bot 发言也计入历史,\n * 对齐 telegram selectAfterLastSelf 的滚动窗口语义)\n */\n historyMode?: 'clear' | 'rolling';\n /**\n * 未被 @ 的群消息入站策略(默认 user_request,仅全量模式群有实际意义):\n * - user_request:维持现状——mentionGate 拦截未 @ 消息(只进历史,不上报)\n * - room_event:门控放行全部消息,未被 @/称呼/引用的作为 room_event 被动\n * 房间事件进框架(AI 只读上下文,不自动回复,想发言走主动 message 工具;\n * 框架自动压制 typing/流式/steer)。成本提示:每条消息一次推理 pass。\n */\n unmentionedInbound?: 'user_request' | 'room_event';\n /** 群消息合并配置(覆盖账号级配置) */\n coalesce?: GroupCoalesceConfig;\n}\n\n/** 限流单层配置(滑动窗口) */\nexport interface RateLimitTierConfig {\n /** 窗口内最大消息数 */\n max: number;\n /** 窗口时长(毫秒) */\n windowMs: number;\n}\n\n/** 三层限流配置(sender / group / global) */\nexport interface RateLimitConfig {\n /** 是否启用(默认 true;保守默认阈值,正常使用不会触发) */\n enabled?: boolean;\n /** 单发送者限流(默认 20 条/分钟) */\n perSender?: RateLimitTierConfig;\n /** 单群限流(默认 60 条/分钟,c2c 按 sender 归组) */\n perGroup?: RateLimitTierConfig;\n /** 全局限流(默认 300 条/分钟) */\n global?: RateLimitTierConfig;\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 /** 三层限流(sender/group/global,默认启用保守阈值) */\n rateLimit?: RateLimitConfig;\n /** 系统提示词,会添加在用户消息前面 */\n systemPrompt?: string;\n /** 是否支持 markdown 消息(默认 true,设为 false 可禁用) */\n markdownSupport?: boolean;\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 * 默认: 本仓库 CHANGELOG(含各版本升级说明)\n */\n upgradeUrl?: string;\n /**\n * 群消息是否默认需要 @机器人才响应(默认 true)\n * 优先级低于 groups.{groupId}.requireMention 和 groups.\"*\".requireMention\n * 设为 false 时,所有群默认无需 @ 即触发回复(仍可被群级配置覆盖)\n */\n defaultRequireMention?: boolean;\n /**\n * 群消息合并配置(账号级)\n * 启用后,群聊中快速发送的多条消息会被合并处理,而不是取消之前的任务\n * 与私聊的\"插嘴\"行为相反,群聊中所有消息都应该被处理\n */\n groupCoalesce?: GroupCoalesceConfig;\n /**\n * 出站消息合并回复(debounce)配置\n * 当短时间内收到多次 deliver 时,将文本合并为一条消息发送,避免消息轰炸\n */\n deliverDebounce?: DeliverDebounceConfig;\n /**\n * \"正在输入\"指示器配置(仅 C2C 私聊生效)\n */\n typing?: TypingIndicatorConfig;\n /**\n * 指令面板配置(openclaw 原生指令 → QQ Bot 指令面板自动注册)\n */\n commands?: CommandsConfig;\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 * sendMode 控制文本下发通道(仅 mode=\"partial\" 时生效):\n * - \"stream\" QQ 流式打印机(默认):同一条消息内容不断变长,打字机效果\n * - \"static\" 流结束时用一条普通 sendText 发送完整文本:无打字机,\n * partial 接收逻辑(状态机/串行/去重)保持不变,仅替换下发通道。\n * 适合不想要打字机效果、只想收到一条完整回复的场景。\n */\n streaming?: {\n mode: 'partial' | 'off';\n sendMode?: 'stream' | 'static';\n };\n /**\n * STT (语音转文字) 配置\n * 配置后,收到语音消息时会自动调用 STT 服务转录为文字\n */\n stt?: STTChannelConfig;\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 *\n * QQ 客户端行为:退出聊天界面再进入后,指示器会消失,只有收到新的\n * input_notify 推送才会重新显示,因此处理期间需要周期性续期。\n *\n * 配额说明:typing 通知与回复消息共享同一 msg_id 的被动回复配额\n * (QQ 开放平台同一条消息被动回复上限约 5 条)。被动配额耗尽后,\n * typing 与回复消息一样自动降级为主动发送(不带 msg_id),续期不中断。\n *\n * 中间消息:机器人发出消息(如思维链中间输出)后,QQ 客户端会终止\n * 指示器显示。若框架任务仍在进行,插件会在消息发出 5 秒后补发一次\n * 续期;若是最终回复(任务完成),则不再补发。\n */\nexport interface TypingIndicatorConfig {\n /**\n * 是否启用指示器(默认 true)\n */\n enabled?: boolean;\n /**\n * 续期间隔(毫秒),默认 20000\n * 受 QPS 限制,低于 20000 会被钳制到 20000\n */\n intervalMs?: number;\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","/**\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 * 用于 typing 等非回复用途:占用失败时调用方应降级为主动发送(不带 msg_id)。\n */\n tryAcquire(messageId: string): boolean {\n const result = this.checkLimit(messageId);\n if (!result.allowed) return false;\n this.record(messageId);\n return true;\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"],"mappings":"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","names":["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","key","path","log","runtime","key","log","path","key","init_types","path","MediaFileType","seen","init_types","path","key","resolve","resolve","req","sleep","fs","init_types","path","init_types","fs","init_types","path","path","req","resolve","exports","module","exports","module","exports","module","exports","module","PerMessageDeflate","key","data","exports","module","exports","module","PerMessageDeflate","Receiver","exports","module","PerMessageDeflate","Sender","_","exports","module","exports","module","extension","exports","module","https","http","net","createHash","URL","PerMessageDeflate","Receiver","Sender","WebSocket","key","request","req","exports","module","WebSocket","createWebSocketStream","err","exports","module","protocol","exports","module","http","createHash","extension","PerMessageDeflate","subprotocol","WebSocket","WebSocketServer","req","key","h","WebSocket","key","_log","d","log","resolve","log","d","crypto","path","resolve","req","key","key","path","req","resolve","d","crypto","MAX_CACHE_SIZE","key","init_types","req","fs","path","init_types","p","seen","key","parseFaceTags","key","decision","m","store","key","h","key","seen","m","p","store","m","key","init_types","import_node_fs","import_node_path","path","key","fs","key","init_types","result","path","os","C","m","resolve","store","path","import_node_fs","import_node_path","fs","l","b","key","h","net","dns","p","path","fs","os","sendMedia","p","log","resolveVoiceSource","path","fs","os","MSG_TYPE_QUOTE","ReplyLimiter","StreamContentType","StreamInputMode","StreamInputState","import_core","path","fs","runtime","key","resolve","l","applyQQBotAccountConfig","persistAuthConfig","applyAccountDefaults","resolveAccountKey","runtime","key","result","key","p","log","runtime","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","key","fs","b","d","crypto","runtime","key","b","log","import_node_child_process","import_node_module","path","import_node_fs","import_node_path","import_node_module","import_node_url","import_meta","__file","path","fs","resolve","resolve","path","init_types","b","import_node_fs","DEFAULT_TTL_MS","import_node_fs","import_node_path","fs","import_node_child_process","fs","path","import_node_child_process","fs","path","p","fs","path","sanitizeFileName","key","formatVoiceText","formatDuration","import_node_module","path","_req","getReq","runtime","log","formatVoiceText","formatDuration","h","d","store","key","p","key","key","m","b","sep","w","_","b","key","m","buildEnvelope","path","os","p","sendMedia","result","m","resolve","b","runtime","log","buildEnvelope","sendMedia","p","groupCfg","u","m","request","fs","path","log","key","runtime","log","path","log","req","h","getHeader","key","_","runtime","log","p","os","log","p","key","log","formatError","log","key","log","runtime","to","path","p","json","p","json","p","log","MSG_TYPE_QUOTE","StreamInputMode","StreamInputState","StreamContentType","ReplyLimiter"]}