@sfmc-bds/qq-bridge 0.2.0-beta.12 → 0.2.0-beta.13

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 (70) hide show
  1. package/README.md +68 -1
  2. package/deploy/install-nginx-webhook.sh +34 -0
  3. package/deploy/nginx-webhook-location.conf +10 -0
  4. package/deploy/qq-bridge.service +23 -0
  5. package/deploy/qq-config-sync.ps1 +106 -0
  6. package/deploy/qq-tunnel.ps1 +15 -0
  7. package/deploy/sync-qq-config.mjs +229 -0
  8. package/dist/commands/account-format.js +41 -0
  9. package/dist/commands/account-format.js.map +7 -0
  10. package/dist/commands/content-format.js +114 -0
  11. package/dist/commands/content-format.js.map +7 -0
  12. package/dist/commands/db-api.js +21 -6
  13. package/dist/commands/db-api.js.map +2 -2
  14. package/dist/commands/handlers.js +392 -524
  15. package/dist/commands/handlers.js.map +2 -2
  16. package/dist/commands/index.js +5 -5
  17. package/dist/commands/index.js.map +3 -3
  18. package/dist/commands/interaction-router.js +38 -133
  19. package/dist/commands/interaction-router.js.map +2 -2
  20. package/dist/commands/join-settings-ui.js +1 -24
  21. package/dist/commands/join-settings-ui.js.map +2 -2
  22. package/dist/commands/menu-format.js +36 -33
  23. package/dist/commands/menu-format.js.map +2 -2
  24. package/dist/commands/menu-panel-sync.js +39 -13
  25. package/dist/commands/menu-panel-sync.js.map +2 -2
  26. package/dist/commands/message-text.js +91 -0
  27. package/dist/commands/message-text.js.map +7 -0
  28. package/dist/commands/pending.js +23 -16
  29. package/dist/commands/pending.js.map +2 -2
  30. package/dist/commands/permissions.js +17 -0
  31. package/dist/commands/permissions.js.map +7 -0
  32. package/dist/commands/registry.js +1 -1
  33. package/dist/commands/registry.js.map +2 -2
  34. package/dist/commands/render.js +7 -16
  35. package/dist/commands/render.js.map +2 -2
  36. package/dist/commands/reply-llbot.js +18 -7
  37. package/dist/commands/reply-llbot.js.map +2 -2
  38. package/dist/commands/reply-official.js +55 -39
  39. package/dist/commands/reply-official.js.map +3 -3
  40. package/dist/commands/router.js +116 -23
  41. package/dist/commands/router.js.map +2 -2
  42. package/dist/config.js +35 -27
  43. package/dist/config.js.map +2 -2
  44. package/dist/console.js +3 -3
  45. package/dist/console.js.map +2 -2
  46. package/dist/index.js +30 -16
  47. package/dist/index.js.map +2 -2
  48. package/dist/llbot-ws-api.js +2 -2
  49. package/dist/llbot-ws-api.js.map +2 -2
  50. package/dist/official/events.js +32 -7
  51. package/dist/official/events.js.map +2 -2
  52. package/dist/official/gateway.js +10 -1
  53. package/dist/official/gateway.js.map +2 -2
  54. package/dist/official/webhook.js +165 -0
  55. package/dist/official/webhook.js.map +7 -0
  56. package/dist/onebot.js +0 -4
  57. package/dist/onebot.js.map +2 -2
  58. package/package.json +4 -4
  59. package/dist/commands/join-settings-ui.test.js +0 -43
  60. package/dist/commands/join-settings-ui.test.js.map +0 -7
  61. package/dist/commands/menu-panel-sync.test.js +0 -27
  62. package/dist/commands/menu-panel-sync.test.js.map +0 -7
  63. package/dist/commands/render.test.js +0 -44
  64. package/dist/commands/render.test.js.map +0 -7
  65. package/dist/commands/router.test.js +0 -92
  66. package/dist/commands/router.test.js.map +0 -7
  67. package/dist/llbot-ws-api.test.js +0 -32
  68. package/dist/llbot-ws-api.test.js.map +0 -7
  69. package/dist/official/events.test.js +0 -123
  70. package/dist/official/events.test.js.map +0 -7
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/config.ts"],
4
- "sourcesContent": ["/**\n * config.ts \u2014 QQ \u6865\u63A5\u670D\u52A1\u914D\u7F6E\u52A0\u8F7D\u4E0E\u70ED\u91CD\u8F7D\u7BA1\u7406\n *\n * \u914D\u7F6E\u6E90\uFF1A`configs/qq_config.json`\n * \u6838\u5FC3\u673A\u5236\uFF1A\n * - \u8FDB\u7A0B\u542F\u52A8\u65F6\u8C03\u7528 `loadInitialConfig()` \u52A0\u8F7D\u5E76\u6821\u9A8C\u914D\u7F6E\uFF1B\u82E5\u914D\u7F6E\u6587\u4EF6\u7F3A\u5931\u6216\u635F\u574F\u5219\u9000\u51FA\n * - \u7F3A\u7701\u5B57\u6BB5\u81EA\u52A8\u56DE\u9000\u81F3 SDK `DEFAULT_QQ_CONFIG` \u6743\u5A01\u9ED8\u8BA4\u503C\n * - \u652F\u6301 `reloadInto(cfg)` \u5C31\u5730\u5408\u5E76\u66F4\u65B0\u8FD0\u884C\u65F6\u5BF9\u8C61\uFF0C\u4FDD\u6301\u5185\u5B58\u5F15\u7528\u4E00\u81F4\n */\n\nimport {\n configPath,\n DEFAULT_QQ_CONFIG,\n loadEnsuredConfig,\n stripConfigMeta,\n} from \"@sfmc-bds/sdk/node/config\";\nimport { log } from \"./log.js\";\nimport { PROJECT_ROOT } from \"./project-root.js\";\nimport type { QQBridgeConfig } from \"./types.js\";\n\n/** \u7EDF\u4E00\u901A\u8FC7 SDK \u89E3\u6790\u9879\u76EE\u6839\u76EE\u5F55\uFF08SFMC_ROOT \u4F18\u5148\u4E8E\u4E0A\u6EAF\u67E5\u627E\uFF09\u3002 */\nexport const ROOT_DIR: string = PROJECT_ROOT;\nexport const CFG_PATH: string = configPath(ROOT_DIR, \"qq_config.json\");\n\nfunction applyDefaults(raw: Partial<QQBridgeConfig>): QQBridgeConfig {\n /* \u4EE5 SDK DEFAULT_QQ_CONFIG \u4E3A\u552F\u4E00\u7F3A\u7701\u6743\u5A01\uFF08DRY/LSP\uFF09\uFF0C\u518D\u53E0\u8FD0\u884C\u65F6\u6D3E\u751F\u5B57\u6BB5 */\n const merged = { ...DEFAULT_QQ_CONFIG, ...raw } as Partial<QQBridgeConfig> & Record<string, unknown>;\n const stripped = stripConfigMeta(merged);\n const backendRaw = String(stripped.qq_backend ?? DEFAULT_QQ_CONFIG.qq_backend ?? \"official\");\n const qq_backend = backendRaw === \"llbot\" ? (\"llbot\" as const) : (\"official\" as const);\n return {\n ...stripped,\n qq_enabled: stripped.qq_enabled !== false,\n qq_backend,\n qq_app_id: String(stripped.qq_app_id ?? DEFAULT_QQ_CONFIG.qq_app_id ?? \"\"),\n qq_app_secret: String(stripped.qq_app_secret ?? DEFAULT_QQ_CONFIG.qq_app_secret ?? \"\"),\n qq_sandbox: stripped.qq_sandbox === true,\n qq_group_openid: String(stripped.qq_group_openid ?? DEFAULT_QQ_CONFIG.qq_group_openid ?? \"\"),\n qq_group_panel_id: String(stripped.qq_group_panel_id ?? DEFAULT_QQ_CONFIG.qq_group_panel_id ?? \"\"),\n qq_sync_menu_panel: stripped.qq_sync_menu_panel !== false,\n qq_admin_openids: Array.isArray(stripped.qq_admin_openids)\n ? (stripped.qq_admin_openids as unknown[]).map((x) => String(x).trim()).filter(Boolean)\n : [],\n qq_ws_port: parseInt(String(stripped.qq_ws_port ?? DEFAULT_QQ_CONFIG.qq_ws_port ?? 3002), 10),\n qq_group_id: String(stripped.qq_group_id ?? DEFAULT_QQ_CONFIG.qq_group_id ?? \"0\"),\n bridge_channel_id: String(stripped.bridge_channel_id ?? DEFAULT_QQ_CONFIG.bridge_channel_id ?? \"\"),\n db_host: String(stripped.db_host ?? \"127.0.0.1\"),\n db_port: parseInt(String(stripped.db_port ?? \"3001\"), 10),\n mctoqq_prefix: String(stripped.mctoqq_prefix ?? DEFAULT_QQ_CONFIG.mctoqq_prefix ?? \"[MC]\"),\n };\n}\n\nfunction readFromDisk(): QQBridgeConfig {\n const raw = loadEnsuredConfig(\n ROOT_DIR,\n \"qq_config.json\",\n \"qq_config\",\n { ...DEFAULT_QQ_CONFIG } as Record<string, unknown>\n );\n return applyDefaults(raw as Partial<QQBridgeConfig>);\n}\n\n/**\n * \u8FDB\u7A0B\u542F\u52A8\u65F6\u52A0\u8F7D\u521D\u59CB\u914D\u7F6E\u3002\n *\n * @returns \u89C4\u6574\u5316\u540E\u7684 QQ \u6865\u63A5\u914D\u7F6E\u5BF9\u8C61\u3002\n */\nexport function loadInitialConfig(): QQBridgeConfig {\n try {\n return readFromDisk();\n } catch (e) {\n log.error(`\u65E0\u6CD5\u8BFB\u53D6\u914D\u7F6E: ${CFG_PATH}: ${(e as Error).message}`);\n process.exit(1);\n }\n}\n\n/**\n * \u91CD\u65B0\u4ECE\u78C1\u76D8\u8BFB\u53D6\u914D\u7F6E\u6587\u4EF6\uFF0C\u5E76\u5C31\u5730\u5408\u5E76\u5230\u5F53\u524D\u914D\u7F6E\u5BF9\u8C61\u4E2D\uFF08\u4FDD\u6301\u8FD0\u884C\u65F6\u5BF9\u8C61\u5F15\u7528\u4E0D\u53D8\uFF09\u3002\n *\n * @param cfg \u9700\u540C\u6B65\u66F4\u65B0\u7684\u76EE\u6807\u914D\u7F6E\u5BF9\u8C61\u3002\n */\nexport function reloadInto(cfg: QQBridgeConfig): void {\n try {\n const fresh = readFromDisk();\n Object.assign(cfg, fresh);\n } catch (e) {\n log.error(`\u91CD\u8F7D\u914D\u7F6E\u5931\u8D25: ${(e as Error).message}`);\n }\n}\n\n"],
5
- "mappings": "AAUA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,WAAW;AACpB,SAAS,oBAAoB;AAItB,MAAM,WAAmB;AACzB,MAAM,WAAmB,WAAW,UAAU,gBAAgB;AAErE,SAAS,cAAc,KAA8C;AAEnE,QAAM,SAAS,EAAE,GAAG,mBAAmB,GAAG,IAAI;AAC9C,QAAM,WAAW,gBAAgB,MAAM;AACvC,QAAM,aAAa,OAAO,SAAS,cAAc,kBAAkB,cAAc,UAAU;AAC3F,QAAM,aAAa,eAAe,UAAW,UAAqB;AAClE,SAAO;AAAA,IACL,GAAG;AAAA,IACH,YAAY,SAAS,eAAe;AAAA,IACpC;AAAA,IACA,WAAW,OAAO,SAAS,aAAa,kBAAkB,aAAa,EAAE;AAAA,IACzE,eAAe,OAAO,SAAS,iBAAiB,kBAAkB,iBAAiB,EAAE;AAAA,IACrF,YAAY,SAAS,eAAe;AAAA,IACpC,iBAAiB,OAAO,SAAS,mBAAmB,kBAAkB,mBAAmB,EAAE;AAAA,IAC3F,mBAAmB,OAAO,SAAS,qBAAqB,kBAAkB,qBAAqB,EAAE;AAAA,IACjG,oBAAoB,SAAS,uBAAuB;AAAA,IACpD,kBAAkB,MAAM,QAAQ,SAAS,gBAAgB,IACpD,SAAS,iBAA+B,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,IACpF,CAAC;AAAA,IACL,YAAY,SAAS,OAAO,SAAS,cAAc,kBAAkB,cAAc,IAAI,GAAG,EAAE;AAAA,IAC5F,aAAa,OAAO,SAAS,eAAe,kBAAkB,eAAe,GAAG;AAAA,IAChF,mBAAmB,OAAO,SAAS,qBAAqB,kBAAkB,qBAAqB,EAAE;AAAA,IACjG,SAAS,OAAO,SAAS,WAAW,WAAW;AAAA,IAC/C,SAAS,SAAS,OAAO,SAAS,WAAW,MAAM,GAAG,EAAE;AAAA,IACxD,eAAe,OAAO,SAAS,iBAAiB,kBAAkB,iBAAiB,MAAM;AAAA,EAC3F;AACF;AAEA,SAAS,eAA+B;AACtC,QAAM,MAAM;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA,EAAE,GAAG,kBAAkB;AAAA,EACzB;AACA,SAAO,cAAc,GAA8B;AACrD;AAOO,SAAS,oBAAoC;AAClD,MAAI;AACF,WAAO,aAAa;AAAA,EACtB,SAAS,GAAG;AACV,QAAI,MAAM,yCAAW,QAAQ,KAAM,EAAY,OAAO,EAAE;AACxD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAOO,SAAS,WAAW,KAA2B;AACpD,MAAI;AACF,UAAM,QAAQ,aAAa;AAC3B,WAAO,OAAO,KAAK,KAAK;AAAA,EAC1B,SAAS,GAAG;AACV,QAAI,MAAM,yCAAY,EAAY,OAAO,EAAE;AAAA,EAC7C;AACF;",
4
+ "sourcesContent": ["/**\n * config.ts \u2014 QQ \u6865\u63A5\u670D\u52A1\u914D\u7F6E\u52A0\u8F7D\u4E0E\u70ED\u91CD\u8F7D\u7BA1\u7406\n *\n * \u914D\u7F6E\u6E90\uFF1A`configs/qq_config.json`\n * \u6838\u5FC3\u673A\u5236\uFF1A\n * - \u8FDB\u7A0B\u542F\u52A8\u65F6\u8C03\u7528 `loadInitialConfig()` \u52A0\u8F7D\u5E76\u6821\u9A8C\u914D\u7F6E\uFF1B\u82E5\u914D\u7F6E\u6587\u4EF6\u7F3A\u5931\u6216\u635F\u574F\u5219\u9000\u51FA\n * - \u7F3A\u7701\u5B57\u6BB5\u81EA\u52A8\u56DE\u9000\u81F3 SDK `DEFAULT_QQ_CONFIG` \u6743\u5A01\u9ED8\u8BA4\u503C\n * - \u652F\u6301 `reloadInto(cfg)` \u5C31\u5730\u5408\u5E76\u66F4\u65B0\u8FD0\u884C\u65F6\u5BF9\u8C61\uFF0C\u4FDD\u6301\u5185\u5B58\u5F15\u7528\u4E00\u81F4\n */\n\nimport { configPath, DEFAULT_QQ_CONFIG, loadEnsuredConfig, stripConfigMeta, type QQBridgeConfig as DiskQQConfig } from \"@sfmc-bds/sdk/node/config\";\nimport { log } from \"./log.js\";\nimport { PROJECT_ROOT } from \"./project-root.js\";\nimport type { QQBridgeConfig } from \"./types.js\";\n\n/** \u7EDF\u4E00\u901A\u8FC7 SDK \u89E3\u6790\u9879\u76EE\u6839\u76EE\u5F55\uFF08SFMC_ROOT \u4F18\u5148\u4E8E\u4E0A\u6EAF\u67E5\u627E\uFF09\u3002 */\nexport const ROOT_DIR: string = PROJECT_ROOT;\nexport const CFG_PATH: string = configPath(ROOT_DIR, \"qq_config.json\");\n\nfunction applyDefaults(raw: DiskQQConfig): QQBridgeConfig {\n /* \u4EE5 SDK DEFAULT_QQ_CONFIG \u4E3A\u552F\u4E00\u7F3A\u7701\u6743\u5A01\uFF08DRY/LSP\uFF09\uFF0C\u518D\u53E0\u8FD0\u884C\u65F6\u6D3E\u751F\u5B57\u6BB5 */\n const merged = { ...DEFAULT_QQ_CONFIG, ...raw } as DiskQQConfig & Record<string, unknown>;\n const stripped = stripConfigMeta(merged);\n const backendRaw = String(stripped.qq_backend ?? DEFAULT_QQ_CONFIG.qq_backend ?? \"official\");\n const qq_backend = backendRaw === \"llbot\" ? (\"llbot\" as const) : (\"official\" as const);\n const official = raw.official ?? {};\n const officialDefaults = DEFAULT_QQ_CONFIG.official ?? {};\n const llbot = raw.llbot ?? {};\n const llbotDefaults = DEFAULT_QQ_CONFIG.llbot ?? {};\n const publicRaw = raw.public_server;\n const publicServer = publicRaw && typeof publicRaw === \"object\" ? publicRaw : {};\n return {\n public_server: {\n address: typeof publicServer.address === \"string\" ? publicServer.address.trim().slice(0, 253) : \"\",\n version: typeof publicServer.version === \"string\" ? publicServer.version.trim().slice(0, 120) : \"\",\n port:\n Number.isInteger(publicServer.port) && publicServer.port! > 0 && publicServer.port! <= 65535\n ? publicServer.port!\n : 19132,\n },\n qq_enabled: raw.qq_enabled !== false,\n qq_backend,\n qq_official_transport: official.transport === \"webhook\" ? \"webhook\" : \"websocket\",\n qq_webhook_port: Number(official.webhook?.port ?? officialDefaults.webhook?.port ?? 3005),\n qq_webhook_path: String(official.webhook?.path ?? officialDefaults.webhook?.path ?? \"/qqbot/webhook\"),\n qq_app_id: String(official.app_id ?? officialDefaults.app_id ?? \"\"),\n qq_app_secret: String(official.app_secret ?? officialDefaults.app_secret ?? \"\"),\n qq_sandbox: official.sandbox === true,\n qq_group_openid: String(official.group_openid ?? officialDefaults.group_openid ?? \"\"),\n qq_group_panel_id: String(official.group_panel_id ?? officialDefaults.group_panel_id ?? \"\"),\n qq_sync_menu_panel: official.sync_menu_panel !== false,\n qq_admin_openids: Array.isArray(official.admin_openids)\n ? official.admin_openids.map((x) => String(x).trim()).filter(Boolean)\n : [],\n qq_ws_port: Number(llbot.ws_port ?? llbotDefaults.ws_port ?? 3002),\n qq_group_id: String(llbot.group_id ?? llbotDefaults.group_id ?? \"0\"),\n llbot_enabled: llbot.enabled ?? llbotDefaults.enabled ?? false,\n llbot_path: llbot.path ?? llbotDefaults.path ?? \"\",\n llbot_cwd: llbot.cwd ?? llbotDefaults.cwd ?? \"\",\n llbot_host: llbot.host ?? llbotDefaults.host ?? \"127.0.0.1\",\n llbot_port: llbot.port ?? llbotDefaults.port ?? 3004,\n llbot_token: llbot.token ?? llbotDefaults.token ?? \"\",\n llbot_http: llbot.http ?? llbotDefaults.http ?? \"\",\n db_host: String(raw.db_host ?? \"127.0.0.1\"),\n db_port: Number(raw.db_port ?? 3001),\n };\n}\n\nfunction readFromDisk(): QQBridgeConfig {\n const raw = loadEnsuredConfig(ROOT_DIR, \"qq_config.json\", \"qq_config\", { ...DEFAULT_QQ_CONFIG } as Record<\n string,\n unknown\n >);\n return applyDefaults(raw as DiskQQConfig);\n}\n\n/**\n * \u8FDB\u7A0B\u542F\u52A8\u65F6\u52A0\u8F7D\u521D\u59CB\u914D\u7F6E\u3002\n *\n * @returns \u89C4\u6574\u5316\u540E\u7684 QQ \u6865\u63A5\u914D\u7F6E\u5BF9\u8C61\u3002\n */\nexport function loadInitialConfig(): QQBridgeConfig {\n try {\n return readFromDisk();\n } catch (e) {\n log.error(`\u65E0\u6CD5\u8BFB\u53D6\u914D\u7F6E: ${CFG_PATH}: ${(e as Error).message}`);\n process.exit(1);\n }\n}\n\n/**\n * \u91CD\u65B0\u4ECE\u78C1\u76D8\u8BFB\u53D6\u914D\u7F6E\u6587\u4EF6\uFF0C\u5E76\u5C31\u5730\u5408\u5E76\u5230\u5F53\u524D\u914D\u7F6E\u5BF9\u8C61\u4E2D\uFF08\u4FDD\u6301\u8FD0\u884C\u65F6\u5BF9\u8C61\u5F15\u7528\u4E0D\u53D8\uFF09\u3002\n *\n * @param cfg \u9700\u540C\u6B65\u66F4\u65B0\u7684\u76EE\u6807\u914D\u7F6E\u5BF9\u8C61\u3002\n */\nexport function reloadInto(cfg: QQBridgeConfig): void {\n try {\n const fresh = readFromDisk();\n Object.assign(cfg, fresh);\n } catch (e) {\n log.error(`\u91CD\u8F7D\u914D\u7F6E\u5931\u8D25: ${(e as Error).message}`);\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/console.js CHANGED
@@ -17,6 +17,7 @@ function showStatus(opts) {
17
17
  console.log(` \u542F\u7528: ${initialEnabled}`);
18
18
  console.log(` \u540E\u7AEF: ${backend}`);
19
19
  if (backend === "official") {
20
+ console.log(` \u63A5\u6536\u65B9\u5F0F: ${cfg.qq_official_transport}`);
20
21
  console.log(` \u6C99\u7BB1: ${cfg.qq_sandbox}`);
21
22
  console.log(` AppID: ${cfg.qq_app_id || "\u672A\u914D\u7F6E"}`);
22
23
  console.log(` \u7FA4 openid: ${cfg.qq_group_openid || "\u672A\u914D\u7F6E"}`);
@@ -27,9 +28,8 @@ function showStatus(opts) {
27
28
  console.log(` QQ \u7FA4\u53F7: ${cfg.qq_group_id || "\u672A\u914D\u7F6E"}`);
28
29
  console.log(` Bot self_id: ${botSelfIdRef.value || "\u672A\u6355\u83B7"}`);
29
30
  }
30
- console.log(` Channel: ${cfg.bridge_channel_id || "\u672A\u914D\u7F6E"}`);
31
+ console.log(" Channel: qq");
31
32
  console.log(` db-server: ${cfg.db_host}:${cfg.db_port}`);
32
- console.log(` MC\u2192QQ \u524D\u7F00: ${cfg.mctoqq_prefix}`);
33
33
  console.log(` MC\u2192QQ \u51FA\u7AD9: (\u7531 db-server \u6309 qq_backend \u76F4\u8FDE)`);
34
34
  }
35
35
  function handleCommand(line, opts, rl) {
@@ -41,7 +41,7 @@ function handleCommand(line, opts, rl) {
41
41
  case "reload": {
42
42
  reloadInto(opts.config);
43
43
  log.info(
44
- `\u914D\u7F6E\u5DF2\u91CD\u8F7D (backend=${opts.config.qq_backend}, channel=${opts.config.bridge_channel_id}, db=${opts.config.db_host}:${opts.config.db_port})`
44
+ `\u914D\u7F6E\u5DF2\u91CD\u8F7D (backend=${opts.config.qq_backend}, channel=qq, db=${opts.config.db_host}:${opts.config.db_port})`
45
45
  );
46
46
  log.warn("reload \u53EA\u66F4\u65B0\u5185\u5B58\u914D\u7F6E\u5F15\u7528\uFF1B\u5207\u6362 backend / AppID \u9700\u91CD\u542F\u8FDB\u7A0B");
47
47
  return false;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/console.ts"],
4
- "sourcesContent": ["/**\n * console.ts \u2014 \u4EA4\u4E92\u5F0F\u63A7\u5236\u53F0 (readline)\n *\n * \u547D\u4EE4:\n * help \u663E\u793A\u5E2E\u52A9\n * reload \u91CD\u65B0\u8BFB\u53D6 configs/qq_config.json\n * status \u663E\u793A\u5F53\u524D\u72B6\u6001\n * sync-menu \u91CD\u63A8\u5B98\u65B9\u83DC\u5355/\u6307\u4EE4\u9762\u677F\uFF08\u4EC5 official\uFF09\n * stop \u9000\u51FA\u8FDB\u7A0B\n */\n\nimport { createInterface, type Interface as RLInterface } from \"node:readline\";\nimport { log } from \"./log.js\";\nimport { reloadInto } from \"./config.js\";\nimport type { QQBridgeConfig } from \"./types.js\";\n\nexport interface ConsoleOptions {\n config: QQBridgeConfig;\n initialEnabled: boolean;\n wsPort: number;\n botSelfIdRef: { value: string | null };\n backend: \"official\" | \"llbot\";\n onSyncMenu?: () => Promise<void>;\n}\n\nfunction showHelp(backend: \"official\" | \"llbot\"): void {\n console.log(\"\u53EF\u7528\u547D\u4EE4:\");\n console.log(\" help \u2014 \u663E\u793A\u672C\u5E2E\u52A9\");\n console.log(\" reload \u2014 \u91CD\u65B0\u8BFB\u53D6 qq_config.json\");\n console.log(\" status \u2014 \u663E\u793A\u5F53\u524D\u72B6\u6001\");\n if (backend === \"official\") {\n console.log(\" sync-menu \u2014 \u91CD\u63A8\u5B98\u65B9\u81EA\u5B9A\u4E49\u83DC\u5355 / \u7FA4\u6307\u4EE4\u9762\u677F\");\n }\n console.log(\" stop \u2014 \u505C\u6B62\u670D\u52A1\");\n}\n\nfunction showStatus(opts: ConsoleOptions): void {\n const { config: cfg, initialEnabled, wsPort, botSelfIdRef, backend } = opts;\n console.log(\"=== QQBridge \u72B6\u6001 ===\");\n console.log(` \u542F\u7528: ${initialEnabled}`);\n console.log(` \u540E\u7AEF: ${backend}`);\n if (backend === \"official\") {\n console.log(` \u6C99\u7BB1: ${cfg.qq_sandbox}`);\n console.log(` AppID: ${cfg.qq_app_id || \"\u672A\u914D\u7F6E\"}`);\n console.log(` \u7FA4 openid: ${cfg.qq_group_openid || \"\u672A\u914D\u7F6E\"}`);\n console.log(` \u9762\u677F id: ${cfg.qq_group_panel_id || \"\u672A\u914D\u7F6E\"}`);\n console.log(` sync \u83DC\u5355: ${cfg.qq_sync_menu_panel !== false}`);\n } else {\n console.log(` WS \u7AEF\u53E3: ${wsPort}`);\n console.log(` QQ \u7FA4\u53F7: ${cfg.qq_group_id || \"\u672A\u914D\u7F6E\"}`);\n console.log(` Bot self_id: ${botSelfIdRef.value || \"\u672A\u6355\u83B7\"}`);\n }\n console.log(` Channel: ${cfg.bridge_channel_id || \"\u672A\u914D\u7F6E\"}`);\n console.log(` db-server: ${cfg.db_host}:${cfg.db_port}`);\n console.log(` MC\u2192QQ \u524D\u7F00: ${cfg.mctoqq_prefix}`);\n console.log(` MC\u2192QQ \u51FA\u7AD9: (\u7531 db-server \u6309 qq_backend \u76F4\u8FDE)`);\n}\n\nfunction handleCommand(line: string, opts: ConsoleOptions, rl: RLInterface): boolean {\n const cmd = line.trim().toLowerCase();\n switch (cmd) {\n case \"help\":\n showHelp(opts.backend);\n return false;\n case \"reload\": {\n reloadInto(opts.config);\n log.info(\n `\u914D\u7F6E\u5DF2\u91CD\u8F7D (backend=${opts.config.qq_backend}, channel=${opts.config.bridge_channel_id}, db=${opts.config.db_host}:${opts.config.db_port})`\n );\n log.warn(\"reload \u53EA\u66F4\u65B0\u5185\u5B58\u914D\u7F6E\u5F15\u7528\uFF1B\u5207\u6362 backend / AppID \u9700\u91CD\u542F\u8FDB\u7A0B\");\n return false;\n }\n case \"status\":\n showStatus(opts);\n return false;\n case \"sync-menu\": {\n if (opts.backend !== \"official\") {\n log.info(\"sync-menu \u4EC5 official \u540E\u7AEF\u53EF\u7528\");\n return false;\n }\n if (!opts.onSyncMenu) {\n log.warn(\"sync-menu \u672A\u63A5\u7EBF\");\n return false;\n }\n void opts.onSyncMenu().then(\n () => log.info(\"sync-menu \u5B8C\u6210\"),\n (e) => log.warn(`sync-menu \u5931\u8D25: ${(e as Error).message}`)\n );\n return false;\n }\n case \"stop\":\n case \"exit\":\n case \"quit\":\n log.info(\"\u6B63\u5728\u505C\u6B62...\");\n rl.close();\n process.exit(0);\n return true;\n case \"\":\n return false;\n default:\n log.info(`\u672A\u77E5\u547D\u4EE4: ${cmd} (\u8F93\u5165 help \u67E5\u770B\u5E2E\u52A9)`);\n return false;\n }\n}\n\nexport function startConsole(opts: ConsoleOptions): void {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n rl.on(\"line\", (line) => {\n handleCommand(line, opts, rl);\n });\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * console.ts \u2014 \u4EA4\u4E92\u5F0F\u63A7\u5236\u53F0 (readline)\n *\n * \u547D\u4EE4:\n * help \u663E\u793A\u5E2E\u52A9\n * reload \u91CD\u65B0\u8BFB\u53D6 configs/qq_config.json\n * status \u663E\u793A\u5F53\u524D\u72B6\u6001\n * sync-menu \u91CD\u63A8\u5B98\u65B9\u83DC\u5355/\u6307\u4EE4\u9762\u677F\uFF08\u4EC5 official\uFF09\n * stop \u9000\u51FA\u8FDB\u7A0B\n */\n\nimport { createInterface, type Interface as RLInterface } from \"node:readline\";\nimport { log } from \"./log.js\";\nimport { reloadInto } from \"./config.js\";\nimport type { QQBridgeConfig } from \"./types.js\";\n\nexport interface ConsoleOptions {\n config: QQBridgeConfig;\n initialEnabled: boolean;\n wsPort: number;\n botSelfIdRef: { value: string | null };\n backend: \"official\" | \"llbot\";\n onSyncMenu?: () => Promise<void>;\n}\n\nfunction showHelp(backend: \"official\" | \"llbot\"): void {\n console.log(\"\u53EF\u7528\u547D\u4EE4:\");\n console.log(\" help \u2014 \u663E\u793A\u672C\u5E2E\u52A9\");\n console.log(\" reload \u2014 \u91CD\u65B0\u8BFB\u53D6 qq_config.json\");\n console.log(\" status \u2014 \u663E\u793A\u5F53\u524D\u72B6\u6001\");\n if (backend === \"official\") {\n console.log(\" sync-menu \u2014 \u91CD\u63A8\u5B98\u65B9\u81EA\u5B9A\u4E49\u83DC\u5355 / \u7FA4\u6307\u4EE4\u9762\u677F\");\n }\n console.log(\" stop \u2014 \u505C\u6B62\u670D\u52A1\");\n}\n\nfunction showStatus(opts: ConsoleOptions): void {\n const { config: cfg, initialEnabled, wsPort, botSelfIdRef, backend } = opts;\n console.log(\"=== QQBridge \u72B6\u6001 ===\");\n console.log(` \u542F\u7528: ${initialEnabled}`);\n console.log(` \u540E\u7AEF: ${backend}`);\n if (backend === \"official\") {\n console.log(` \u63A5\u6536\u65B9\u5F0F: ${cfg.qq_official_transport}`);\n console.log(` \u6C99\u7BB1: ${cfg.qq_sandbox}`);\n console.log(` AppID: ${cfg.qq_app_id || \"\u672A\u914D\u7F6E\"}`);\n console.log(` \u7FA4 openid: ${cfg.qq_group_openid || \"\u672A\u914D\u7F6E\"}`);\n console.log(` \u9762\u677F id: ${cfg.qq_group_panel_id || \"\u672A\u914D\u7F6E\"}`);\n console.log(` sync \u83DC\u5355: ${cfg.qq_sync_menu_panel !== false}`);\n } else {\n console.log(` WS \u7AEF\u53E3: ${wsPort}`);\n console.log(` QQ \u7FA4\u53F7: ${cfg.qq_group_id || \"\u672A\u914D\u7F6E\"}`);\n console.log(` Bot self_id: ${botSelfIdRef.value || \"\u672A\u6355\u83B7\"}`);\n }\n console.log(\" Channel: qq\");\n console.log(` db-server: ${cfg.db_host}:${cfg.db_port}`);\n console.log(` MC\u2192QQ \u51FA\u7AD9: (\u7531 db-server \u6309 qq_backend \u76F4\u8FDE)`);\n}\n\nfunction handleCommand(line: string, opts: ConsoleOptions, rl: RLInterface): boolean {\n const cmd = line.trim().toLowerCase();\n switch (cmd) {\n case \"help\":\n showHelp(opts.backend);\n return false;\n case \"reload\": {\n reloadInto(opts.config);\n log.info(\n `\u914D\u7F6E\u5DF2\u91CD\u8F7D (backend=${opts.config.qq_backend}, channel=qq, db=${opts.config.db_host}:${opts.config.db_port})`\n );\n log.warn(\"reload \u53EA\u66F4\u65B0\u5185\u5B58\u914D\u7F6E\u5F15\u7528\uFF1B\u5207\u6362 backend / AppID \u9700\u91CD\u542F\u8FDB\u7A0B\");\n return false;\n }\n case \"status\":\n showStatus(opts);\n return false;\n case \"sync-menu\": {\n if (opts.backend !== \"official\") {\n log.info(\"sync-menu \u4EC5 official \u540E\u7AEF\u53EF\u7528\");\n return false;\n }\n if (!opts.onSyncMenu) {\n log.warn(\"sync-menu \u672A\u63A5\u7EBF\");\n return false;\n }\n void opts.onSyncMenu().then(\n () => log.info(\"sync-menu \u5B8C\u6210\"),\n (e) => log.warn(`sync-menu \u5931\u8D25: ${(e as Error).message}`)\n );\n return false;\n }\n case \"stop\":\n case \"exit\":\n case \"quit\":\n log.info(\"\u6B63\u5728\u505C\u6B62...\");\n rl.close();\n process.exit(0);\n return true;\n case \"\":\n return false;\n default:\n log.info(`\u672A\u77E5\u547D\u4EE4: ${cmd} (\u8F93\u5165 help \u67E5\u770B\u5E2E\u52A9)`);\n return false;\n }\n}\n\nexport function startConsole(opts: ConsoleOptions): void {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n rl.on(\"line\", (line) => {\n handleCommand(line, opts, rl);\n });\n}\n"],
5
+ "mappings": "AAWA,SAAS,uBAAsD;AAC/D,SAAS,WAAW;AACpB,SAAS,kBAAkB;AAY3B,SAAS,SAAS,SAAqC;AACrD,UAAQ,IAAI,2BAAO;AACnB,UAAQ,IAAI,oDAAsB;AAClC,UAAQ,IAAI,6DAAoC;AAChD,UAAQ,IAAI,0DAAuB;AACnC,MAAI,YAAY,YAAY;AAC1B,YAAQ,IAAI,6GAAkC;AAAA,EAChD;AACA,UAAQ,IAAI,8CAAqB;AACnC;AAEA,SAAS,WAAW,MAA4B;AAC9C,QAAM,EAAE,QAAQ,KAAK,gBAAgB,QAAQ,cAAc,QAAQ,IAAI;AACvE,UAAQ,IAAI,+BAAqB;AACjC,UAAQ,IAAI,0BAAgB,cAAc,EAAE;AAC5C,UAAQ,IAAI,0BAAgB,OAAO,EAAE;AACrC,MAAI,YAAY,YAAY;AAC1B,YAAQ,IAAI,kCAAc,IAAI,qBAAqB,EAAE;AACrD,YAAQ,IAAI,0BAAgB,IAAI,UAAU,EAAE;AAC5C,YAAQ,IAAI,kBAAkB,IAAI,aAAa,oBAAK,EAAE;AACtD,YAAQ,IAAI,sBAAiB,IAAI,mBAAmB,oBAAK,EAAE;AAC3D,YAAQ,IAAI,0BAAgB,IAAI,qBAAqB,oBAAK,EAAE;AAC5D,YAAQ,IAAI,0BAAgB,IAAI,uBAAuB,KAAK,EAAE;AAAA,EAChE,OAAO;AACL,YAAQ,IAAI,0BAAgB,MAAM,EAAE;AACpC,YAAQ,IAAI,0BAAgB,IAAI,eAAe,oBAAK,EAAE;AACtD,YAAQ,IAAI,kBAAkB,aAAa,SAAS,oBAAK,EAAE;AAAA,EAC7D;AACA,UAAQ,IAAI,mBAAmB;AAC/B,UAAQ,IAAI,kBAAkB,IAAI,OAAO,IAAI,IAAI,OAAO,EAAE;AAC1D,UAAQ,IAAI,+EAA4C;AAC1D;AAEA,SAAS,cAAc,MAAc,MAAsB,IAA0B;AACnF,QAAM,MAAM,KAAK,KAAK,EAAE,YAAY;AACpC,UAAQ,KAAK;AAAA,IACX,KAAK;AACH,eAAS,KAAK,OAAO;AACrB,aAAO;AAAA,IACT,KAAK,UAAU;AACb,iBAAW,KAAK,MAAM;AACtB,UAAI;AAAA,QACF,2CAAkB,KAAK,OAAO,UAAU,oBAAoB,KAAK,OAAO,OAAO,IAAI,KAAK,OAAO,OAAO;AAAA,MACxG;AACA,UAAI,KAAK,gIAA2C;AACpD,aAAO;AAAA,IACT;AAAA,IACA,KAAK;AACH,iBAAW,IAAI;AACf,aAAO;AAAA,IACT,KAAK,aAAa;AAChB,UAAI,KAAK,YAAY,YAAY;AAC/B,YAAI,KAAK,oDAA2B;AACpC,eAAO;AAAA,MACT;AACA,UAAI,CAAC,KAAK,YAAY;AACpB,YAAI,KAAK,8BAAe;AACxB,eAAO;AAAA,MACT;AACA,WAAK,KAAK,WAAW,EAAE;AAAA,QACrB,MAAM,IAAI,KAAK,wBAAc;AAAA,QAC7B,CAAC,MAAM,IAAI,KAAK,2BAAkB,EAAY,OAAO,EAAE;AAAA,MACzD;AACA,aAAO;AAAA,IACT;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,UAAI,KAAK,6BAAS;AAClB,SAAG,MAAM;AACT,cAAQ,KAAK,CAAC;AACd,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,UAAI,KAAK,6BAAS,GAAG,+CAAiB;AACtC,aAAO;AAAA,EACX;AACF;AAEO,SAAS,aAAa,MAA4B;AACvD,QAAM,KAAK,gBAAgB,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,OAAO,CAAC;AAC3E,KAAG,GAAG,QAAQ,CAAC,SAAS;AACtB,kBAAc,MAAM,MAAM,EAAE;AAAA,EAC9B,CAAC;AACH;",
6
6
  "names": []
7
7
  }
package/dist/index.js CHANGED
@@ -3,13 +3,14 @@ import { createLlbotCommandRouter, createOfficialCommandRouter } from "./command
3
3
  import { createInteractionRouter } from "./commands/interaction-router.js";
4
4
  import { persistPanelIdToConfig, syncMenuAndPanel } from "./commands/menu-panel-sync.js";
5
5
  import { loadInitialConfig } from "./config.js";
6
- import { OneBotDispatcher } from "./onebot.js";
7
- import { startWsServer } from "./ws-server.js";
8
6
  import { startConsole } from "./console.js";
7
+ import { log } from "./log.js";
9
8
  import { OfficialAtMessageDispatcher } from "./official/events.js";
10
9
  import { startOfficialGateway } from "./official/gateway.js";
10
+ import { startOfficialWebhook } from "./official/webhook.js";
11
+ import { OneBotDispatcher } from "./onebot.js";
11
12
  import { installQqRuntimeStatusHooks } from "./runtime-status.js";
12
- import { log } from "./log.js";
13
+ import { startWsServer } from "./ws-server.js";
13
14
  async function main() {
14
15
  const cfg = loadInitialConfig();
15
16
  const startedAt = Date.now();
@@ -21,11 +22,11 @@ async function main() {
21
22
  const db = {
22
23
  host: cfg.db_host,
23
24
  port: cfg.db_port,
24
- channelId: cfg.bridge_channel_id
25
+ channelId: "qq"
25
26
  };
26
27
  if (cfg.qq_backend === "official") {
27
28
  if (!cfg.qq_app_id || !cfg.qq_app_secret) {
28
- log.error("\u5B98\u65B9\u540E\u7AEF\u9700\u8981\u914D\u7F6E qq_app_id \u4E0E qq_app_secret");
29
+ log.error("\u5B98\u65B9\u540E\u7AEF\u9700\u8981\u914D\u7F6E official.app_id \u4E0E official.app_secret");
29
30
  process.exit(1);
30
31
  }
31
32
  const creds = {
@@ -39,11 +40,12 @@ async function main() {
39
40
  {
40
41
  sandbox: cfg.qq_sandbox,
41
42
  appIdHint: appId.length > 8 ? `${appId.slice(0, 4)}\u2026${appId.slice(-4)}` : appId,
43
+ publicServer: cfg.public_server,
42
44
  dbHost: cfg.db_host,
43
45
  dbPort: cfg.db_port,
44
46
  groupOpenid: cfg.qq_group_openid,
45
47
  adminOpenids: Array.isArray(cfg.qq_admin_openids) ? cfg.qq_admin_openids : [],
46
- bridgeChannelId: cfg.bridge_channel_id,
48
+ bridgeChannelId: "qq",
47
49
  officialCreds: creds
48
50
  },
49
51
  startedAt
@@ -71,18 +73,29 @@ async function main() {
71
73
  });
72
74
  const interactionRouter = createInteractionRouter({
73
75
  creds,
74
- db: { host: cfg.db_host, port: cfg.db_port },
75
- adminOpenids: Array.isArray(cfg.qq_admin_openids) ? cfg.qq_admin_openids : []
76
- });
77
- await startOfficialGateway({
78
- creds,
79
- dispatcher,
80
- interactionRouter
76
+ commandRouter,
77
+ groupOpenid: cfg.qq_group_openid
81
78
  });
79
+ if (cfg.qq_official_transport === "webhook") {
80
+ await startOfficialWebhook({
81
+ appId: creds.appId,
82
+ appSecret: creds.appSecret,
83
+ port: cfg.qq_webhook_port,
84
+ path: cfg.qq_webhook_path,
85
+ dispatcher,
86
+ interactionRouter
87
+ });
88
+ } else {
89
+ await startOfficialGateway({
90
+ creds,
91
+ dispatcher,
92
+ interactionRouter
93
+ });
94
+ }
82
95
  void runSyncMenu().catch((e) => log.warn(`menu/panel sync \u5F02\u5E38: ${e.message}`));
83
96
  installQqRuntimeStatusHooks("official");
84
97
  log.info(
85
- `\u5B98\u65B9\u540E\u7AEF\u5DF2\u542F\u52A8 (sandbox=${cfg.qq_sandbox}, group_openid=${cfg.qq_group_openid || "\u672A\u914D\u7F6E"}, channel=${cfg.bridge_channel_id || "\u672A\u914D\u7F6E"}, db=${cfg.db_host}:${cfg.db_port})`
98
+ `\u5B98\u65B9\u540E\u7AEF\u5DF2\u542F\u52A8 (transport=${cfg.qq_official_transport}, sandbox=${cfg.qq_sandbox}, group_openid=${cfg.qq_group_openid || "\u672A\u914D\u7F6E"}, channel=qq, db=${cfg.db_host}:${cfg.db_port})`
86
99
  );
87
100
  startConsole({
88
101
  config: cfg,
@@ -101,10 +114,11 @@ async function main() {
101
114
  groupId: cfg.qq_group_id || "0"
102
115
  },
103
116
  {
117
+ publicServer: cfg.public_server,
104
118
  dbHost: cfg.db_host,
105
119
  dbPort: cfg.db_port,
106
120
  adminOpenids: Array.isArray(cfg.qq_admin_openids) ? cfg.qq_admin_openids : [],
107
- bridgeChannelId: cfg.bridge_channel_id
121
+ bridgeChannelId: "qq"
108
122
  },
109
123
  startedAt
110
124
  );
@@ -119,7 +133,7 @@ async function main() {
119
133
  await startWsServer({ port: cfg.qq_ws_port, dispatcher });
120
134
  installQqRuntimeStatusHooks("llbot");
121
135
  log.info(
122
- `LLBot \u540E\u7AEF\u7B49\u5F85\u8FDE\u63A5 (\u4E3B\u7FA4: ${cfg.qq_group_id || "\u672A\u914D\u7F6E"}, channel: ${cfg.bridge_channel_id || "\u672A\u914D\u7F6E"}, db: ${cfg.db_host}:${cfg.db_port})`
136
+ `LLBot \u540E\u7AEF\u7B49\u5F85\u8FDE\u63A5 (\u4E3B\u7FA4: ${cfg.qq_group_id || "\u672A\u914D\u7F6E"}, channel: qq, db: ${cfg.db_host}:${cfg.db_port})`
123
137
  );
124
138
  startConsole({
125
139
  config: cfg,
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["#!/usr/bin/env node\n/**\n * index.ts \u2014 QQ \u2194 MC \u6865\u63A5\u8FDB\u7A0B\u5165\u53E3\n *\n * \u53CC\u540E\u7AEF:\n * official \u2014 \u8FDE\u63A5 QQ \u5F00\u653E\u5E73\u53F0 Gateway\uFF0C\u53EA\u8F6C\u53D1 GROUP_AT_MESSAGE_CREATE\uFF1BC2C \u8D70\u6307\u4EE4\n * llbot \u2014 \u76D1\u542C WS:3002 \u63A5 LLBot reverse-ws\uFF08OneBot 11\uFF09\n *\n * \u5171\u540C\u51FA\u7AD9\u4FE1\u5C01: POST db-server:3001/api/sfmc/messages\n * MC\u2192QQ \u7531 db-server \u6309\u540C\u4E00 qq_backend \u76F4\u8FDE\u5B98\u65B9 OpenAPI \u6216 LLBot HTTP\n *\n * QQ \u4FA7\u6307\u4EE4\u5728\u6865\u5185\u62E6\u622A\uFF1Bofficial \u53EF sync \u81EA\u5B9A\u4E49\u83DC\u5355 / \u7FA4\u6307\u4EE4\u9762\u677F\u3002\n */\n\nimport { createLlbotCommandRouter, createOfficialCommandRouter } from \"./commands/index.js\";\nimport { createInteractionRouter } from \"./commands/interaction-router.js\";\nimport { persistPanelIdToConfig, syncMenuAndPanel } from \"./commands/menu-panel-sync.js\";\nimport { loadInitialConfig } from \"./config.js\";\nimport { OneBotDispatcher } from \"./onebot.js\";\nimport { startWsServer } from \"./ws-server.js\";\nimport { startConsole } from \"./console.js\";\nimport { OfficialAtMessageDispatcher } from \"./official/events.js\";\nimport { startOfficialGateway } from \"./official/gateway.js\";\nimport { installQqRuntimeStatusHooks } from \"./runtime-status.js\";\nimport { log } from \"./log.js\";\nimport type { QQBridgeConfig } from \"./types.js\";\n\nasync function main(): Promise<void> {\n const cfg: QQBridgeConfig = loadInitialConfig();\n const startedAt = Date.now();\n\n if (!cfg.qq_enabled) {\n log.info(\"\u5DF2\u7981\u7528 (qq_enabled = false)\");\n process.exit(0);\n }\n\n const botSelfIdRef: { value: string | null } = { value: null };\n const db = {\n host: cfg.db_host,\n port: cfg.db_port,\n channelId: cfg.bridge_channel_id,\n };\n\n if (cfg.qq_backend === \"official\") {\n if (!cfg.qq_app_id || !cfg.qq_app_secret) {\n log.error(\"\u5B98\u65B9\u540E\u7AEF\u9700\u8981\u914D\u7F6E qq_app_id \u4E0E qq_app_secret\");\n process.exit(1);\n }\n\n const creds = {\n appId: cfg.qq_app_id,\n appSecret: cfg.qq_app_secret,\n sandbox: cfg.qq_sandbox,\n };\n const appId = cfg.qq_app_id;\n const commandRouter = createOfficialCommandRouter(\n creds,\n {\n sandbox: cfg.qq_sandbox,\n appIdHint: appId.length > 8 ? `${appId.slice(0, 4)}\u2026${appId.slice(-4)}` : appId,\n dbHost: cfg.db_host,\n dbPort: cfg.db_port,\n groupOpenid: cfg.qq_group_openid,\n adminOpenids: Array.isArray(cfg.qq_admin_openids) ? cfg.qq_admin_openids : [],\n bridgeChannelId: cfg.bridge_channel_id,\n officialCreds: creds,\n },\n startedAt\n );\n\n const runSyncMenu = async (): Promise<void> => {\n if (cfg.qq_sync_menu_panel === false) {\n log.info(\"\u5DF2\u8DF3\u8FC7 menu/panel sync\uFF08qq_sync_menu_panel=false\uFF09\");\n return;\n }\n await syncMenuAndPanel({\n creds,\n registry: commandRouter.registry,\n groupOpenid: cfg.qq_group_openid,\n panelId: String(cfg.qq_group_panel_id ?? \"\"),\n persistPanelId: (id) => {\n cfg.qq_group_panel_id = id;\n persistPanelIdToConfig(id);\n },\n });\n };\n\n const dispatcher = new OfficialAtMessageDispatcher({\n groupOpenid: cfg.qq_group_openid,\n db,\n commandRouter,\n });\n\n const interactionRouter = createInteractionRouter({\n creds,\n db: { host: cfg.db_host, port: cfg.db_port },\n adminOpenids: Array.isArray(cfg.qq_admin_openids) ? cfg.qq_admin_openids : [],\n });\n\n await startOfficialGateway({\n creds,\n dispatcher,\n interactionRouter,\n });\n\n // \u4E0D\u963B\u65AD Gateway\uFF1A\u5931\u8D25\u53EA warn\n void runSyncMenu().catch((e) => log.warn(`menu/panel sync \u5F02\u5E38: ${(e as Error).message}`));\n\n installQqRuntimeStatusHooks(\"official\");\n\n log.info(\n `\u5B98\u65B9\u540E\u7AEF\u5DF2\u542F\u52A8 (sandbox=${cfg.qq_sandbox}, group_openid=${cfg.qq_group_openid || \"\u672A\u914D\u7F6E\"}, channel=${cfg.bridge_channel_id || \"\u672A\u914D\u7F6E\"}, db=${cfg.db_host}:${cfg.db_port})`\n );\n\n startConsole({\n config: cfg,\n initialEnabled: cfg.qq_enabled,\n wsPort: 0,\n botSelfIdRef,\n backend: \"official\",\n onSyncMenu: runSyncMenu,\n });\n } else {\n const commandRouter = createLlbotCommandRouter(\n {\n host: cfg.llbot_host || \"127.0.0.1\",\n port: cfg.llbot_port || 3004,\n token: cfg.llbot_token || \"\",\n groupId: cfg.qq_group_id || \"0\",\n },\n {\n dbHost: cfg.db_host,\n dbPort: cfg.db_port,\n adminOpenids: Array.isArray(cfg.qq_admin_openids) ? cfg.qq_admin_openids : [],\n bridgeChannelId: cfg.bridge_channel_id,\n },\n startedAt\n );\n\n const dispatcher = new OneBotDispatcher({\n qqGroupId: cfg.qq_group_id,\n db,\n commandRouter,\n });\n\n Object.defineProperty(botSelfIdRef, \"value\", {\n get: () => dispatcher.selfId,\n });\n\n await startWsServer({ port: cfg.qq_ws_port, dispatcher });\n\n installQqRuntimeStatusHooks(\"llbot\");\n\n log.info(\n `LLBot \u540E\u7AEF\u7B49\u5F85\u8FDE\u63A5 (\u4E3B\u7FA4: ${cfg.qq_group_id || \"\u672A\u914D\u7F6E\"}, channel: ${cfg.bridge_channel_id || \"\u672A\u914D\u7F6E\"}, db: ${cfg.db_host}:${cfg.db_port})`\n );\n\n startConsole({\n config: cfg,\n initialEnabled: cfg.qq_enabled,\n wsPort: cfg.qq_ws_port,\n botSelfIdRef,\n backend: \"llbot\",\n });\n }\n\n log.info(\"\u542F\u52A8\u5B8C\u6210\");\n}\n\nmain().catch((e) => {\n log.error(`\u672A\u6355\u83B7\u5F02\u5E38: ${(e as Error).message}`);\n process.exit(1);\n});\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["#!/usr/bin/env node\n/**\n * index.ts \u2014 QQ \u2194 MC \u6865\u63A5\u8FDB\u7A0B\u5165\u53E3\n *\n * \u53CC\u540E\u7AEF:\n * official \u2014 \u7FA4\u5168\u91CF\u6D88\u606F\u53EA\u5904\u7406\u6307\u4EE4\uFF1B\u7FA4 @ \u6D88\u606F\u53EF\u8F6C\u53D1\u6E38\u620F\uFF1BC2C \u8D70\u6307\u4EE4\n * llbot \u2014 \u76D1\u542C WS:3002 \u63A5 LLBot reverse-ws\uFF08OneBot 11\uFF09\n *\n * \u5171\u540C\u51FA\u7AD9\u4FE1\u5C01: POST db-server:3001/api/sfmc/messages\n * MC\u2192QQ \u7531 db-server \u6309\u540C\u4E00 qq_backend \u76F4\u8FDE\u5B98\u65B9 OpenAPI \u6216 LLBot HTTP\n *\n * QQ \u4FA7\u6307\u4EE4\u5728\u6865\u5185\u62E6\u622A\uFF1Bofficial \u53EF sync \u81EA\u5B9A\u4E49\u83DC\u5355 / \u7FA4\u6307\u4EE4\u9762\u677F\u3002\n */\n\nimport { createLlbotCommandRouter, createOfficialCommandRouter } from \"./commands/index.js\";\nimport { createInteractionRouter } from \"./commands/interaction-router.js\";\nimport { persistPanelIdToConfig, syncMenuAndPanel } from \"./commands/menu-panel-sync.js\";\nimport { loadInitialConfig } from \"./config.js\";\nimport { startConsole } from \"./console.js\";\nimport { log } from \"./log.js\";\nimport { OfficialAtMessageDispatcher } from \"./official/events.js\";\nimport { startOfficialGateway } from \"./official/gateway.js\";\nimport { startOfficialWebhook } from \"./official/webhook.js\";\nimport { OneBotDispatcher } from \"./onebot.js\";\nimport { installQqRuntimeStatusHooks } from \"./runtime-status.js\";\nimport type { QQBridgeConfig } from \"./types.js\";\nimport { startWsServer } from \"./ws-server.js\";\n\nasync function main(): Promise<void> {\n const cfg: QQBridgeConfig = loadInitialConfig();\n const startedAt = Date.now();\n\n if (!cfg.qq_enabled) {\n log.info(\"\u5DF2\u7981\u7528 (qq_enabled = false)\");\n process.exit(0);\n }\n\n const botSelfIdRef: { value: string | null } = { value: null };\n const db = {\n host: cfg.db_host,\n port: cfg.db_port,\n channelId: \"qq\",\n };\n\n if (cfg.qq_backend === \"official\") {\n if (!cfg.qq_app_id || !cfg.qq_app_secret) {\n log.error(\"\u5B98\u65B9\u540E\u7AEF\u9700\u8981\u914D\u7F6E official.app_id \u4E0E official.app_secret\");\n process.exit(1);\n }\n\n const creds = {\n appId: cfg.qq_app_id,\n appSecret: cfg.qq_app_secret,\n sandbox: cfg.qq_sandbox,\n };\n const appId = cfg.qq_app_id;\n const commandRouter = createOfficialCommandRouter(\n creds,\n {\n sandbox: cfg.qq_sandbox,\n appIdHint: appId.length > 8 ? `${appId.slice(0, 4)}\u2026${appId.slice(-4)}` : appId,\n publicServer: cfg.public_server,\n dbHost: cfg.db_host,\n dbPort: cfg.db_port,\n groupOpenid: cfg.qq_group_openid,\n adminOpenids: Array.isArray(cfg.qq_admin_openids) ? cfg.qq_admin_openids : [],\n bridgeChannelId: \"qq\",\n officialCreds: creds,\n },\n startedAt\n );\n\n const runSyncMenu = async (): Promise<void> => {\n if (cfg.qq_sync_menu_panel === false) {\n log.info(\"\u5DF2\u8DF3\u8FC7 menu/panel sync\uFF08qq_sync_menu_panel=false\uFF09\");\n return;\n }\n await syncMenuAndPanel({\n creds,\n registry: commandRouter.registry,\n groupOpenid: cfg.qq_group_openid,\n panelId: String(cfg.qq_group_panel_id ?? \"\"),\n persistPanelId: (id) => {\n cfg.qq_group_panel_id = id;\n persistPanelIdToConfig(id);\n },\n });\n };\n\n const dispatcher = new OfficialAtMessageDispatcher({\n groupOpenid: cfg.qq_group_openid,\n db,\n commandRouter,\n });\n\n const interactionRouter = createInteractionRouter({\n creds,\n commandRouter,\n groupOpenid: cfg.qq_group_openid,\n });\n\n if (cfg.qq_official_transport === \"webhook\") {\n await startOfficialWebhook({\n appId: creds.appId,\n appSecret: creds.appSecret,\n port: cfg.qq_webhook_port,\n path: cfg.qq_webhook_path,\n dispatcher,\n interactionRouter,\n });\n } else {\n await startOfficialGateway({\n creds,\n dispatcher,\n interactionRouter,\n });\n }\n\n // \u4E0D\u963B\u65AD Gateway\uFF1A\u5931\u8D25\u53EA warn\n void runSyncMenu().catch((e) => log.warn(`menu/panel sync \u5F02\u5E38: ${(e as Error).message}`));\n\n installQqRuntimeStatusHooks(\"official\");\n\n log.info(\n `\u5B98\u65B9\u540E\u7AEF\u5DF2\u542F\u52A8 (transport=${cfg.qq_official_transport}, sandbox=${cfg.qq_sandbox}, group_openid=${cfg.qq_group_openid || \"\u672A\u914D\u7F6E\"}, channel=qq, db=${cfg.db_host}:${cfg.db_port})`\n );\n\n startConsole({\n config: cfg,\n initialEnabled: cfg.qq_enabled,\n wsPort: 0,\n botSelfIdRef,\n backend: \"official\",\n onSyncMenu: runSyncMenu,\n });\n } else {\n const commandRouter = createLlbotCommandRouter(\n {\n host: cfg.llbot_host || \"127.0.0.1\",\n port: cfg.llbot_port || 3004,\n token: cfg.llbot_token || \"\",\n groupId: cfg.qq_group_id || \"0\",\n },\n {\n publicServer: cfg.public_server,\n dbHost: cfg.db_host,\n dbPort: cfg.db_port,\n adminOpenids: Array.isArray(cfg.qq_admin_openids) ? cfg.qq_admin_openids : [],\n bridgeChannelId: \"qq\",\n },\n startedAt\n );\n\n const dispatcher = new OneBotDispatcher({\n qqGroupId: cfg.qq_group_id,\n db,\n commandRouter,\n });\n\n Object.defineProperty(botSelfIdRef, \"value\", {\n get: () => dispatcher.selfId,\n });\n\n await startWsServer({ port: cfg.qq_ws_port, dispatcher });\n\n installQqRuntimeStatusHooks(\"llbot\");\n\n log.info(\n `LLBot \u540E\u7AEF\u7B49\u5F85\u8FDE\u63A5 (\u4E3B\u7FA4: ${cfg.qq_group_id || \"\u672A\u914D\u7F6E\"}, channel: qq, db: ${cfg.db_host}:${cfg.db_port})`\n );\n\n startConsole({\n config: cfg,\n initialEnabled: cfg.qq_enabled,\n wsPort: cfg.qq_ws_port,\n botSelfIdRef,\n backend: \"llbot\",\n });\n }\n\n log.info(\"\u542F\u52A8\u5B8C\u6210\");\n}\n\nmain().catch((e) => {\n log.error(`\u672A\u6355\u83B7\u5F02\u5E38: ${(e as Error).message}`);\n process.exit(1);\n});\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -28,8 +28,8 @@ class LlbotWsApi {
28
28
  tryHandleResponse(raw) {
29
29
  if (!raw || typeof raw !== "object") return false;
30
30
  const obj = raw;
31
- if (obj.post_type != null) return false;
32
- const echo = obj.echo;
31
+ if (obj["post_type"] != null) return false;
32
+ const echo = obj["echo"];
33
33
  if (echo == null || echo === "") return false;
34
34
  const key = String(echo);
35
35
  const p = this.pending.get(key);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/llbot-ws-api.ts"],
4
- "sourcesContent": ["/**\n * llbot-ws-api.ts \u2014 \u7ECF LLBot reverse-WS \u8C03\u7528 OneBot API\n *\n * \u591A\u6570 LLBot \u90E8\u7F72\u53EA\u5F00 reverse-ws\u3001\u4E0D\u5F00\u6B63\u5411 HTTP(3004)\u3002\n * \u6307\u4EE4\u56DE\u590D\u5E94\u8D70\u5DF2\u8FDE\u63A5\u7684 WebSocket\uFF0C\u800C\u4E0D\u662F\u5047\u5B9A 3004 \u53EF\u8FBE\u3002\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport type { WebSocket } from \"ws\";\nimport { log } from \"./log.js\";\n\nexport type LlbotApiResult = {\n status?: string;\n retcode?: number;\n data?: unknown;\n message?: string;\n wording?: string;\n echo?: string;\n};\n\ntype Pending = {\n resolve: (v: LlbotApiResult) => void;\n reject: (e: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n};\n\nconst DEFAULT_TIMEOUT_MS = 8000;\n\nexport class LlbotWsApi {\n private sock: WebSocket | null = null;\n private readonly pending = new Map<string, Pending>();\n\n /** \u5F53\u524D\u662F\u5426\u6709\u53EF\u7528\u7684 LLBot reverse-ws \u8FDE\u63A5 */\n get connected(): boolean {\n return !!this.sock && this.sock.readyState === 1; // OPEN\n }\n\n attach(sock: WebSocket): void {\n if (this.sock && this.sock !== sock) {\n this.rejectAll(new Error(\"LLBot WebSocket \u88AB\u65B0\u8FDE\u63A5\u66FF\u6362\"));\n }\n this.sock = sock;\n log.info(\"LLBot WS API \u5DF2\u6302\u63A5\uFF08\u53EF\u7528 reverse-ws \u53D1\u6D88\u606F\uFF09\");\n }\n\n detach(sock: WebSocket): void {\n if (this.sock !== sock) return;\n this.sock = null;\n this.rejectAll(new Error(\"LLBot WebSocket \u5DF2\u65AD\u5F00\"));\n log.info(\"LLBot WS API \u5DF2\u5378\u8F7D\");\n }\n\n /**\n * \u82E5\u5E27\u662F API \u56DE\u5305\uFF08\u6709 echo\u3001\u65E0 post_type\uFF09\uFF0C\u6D88\u8D39\u5E76\u8FD4\u56DE true\u3002\n * \u4E8B\u4EF6\u5E27\u8FD4\u56DE false\uFF0C\u4EA4\u7ED9 dispatcher\u3002\n */\n tryHandleResponse(raw: unknown): boolean {\n if (!raw || typeof raw !== \"object\") return false;\n const obj = raw as Record<string, unknown>;\n if (obj.post_type != null) return false;\n const echo = obj.echo;\n if (echo == null || echo === \"\") return false;\n const key = String(echo);\n const p = this.pending.get(key);\n if (!p) return false;\n this.pending.delete(key);\n clearTimeout(p.timer);\n p.resolve(obj as LlbotApiResult);\n return true;\n }\n\n async call(action: string, params: Record<string, unknown>, timeoutMs = DEFAULT_TIMEOUT_MS): Promise<LlbotApiResult> {\n if (!this.connected || !this.sock) {\n throw new Error(\"LLBot reverse-ws \u672A\u8FDE\u63A5\uFF0C\u65E0\u6CD5\u8C03\u7528 API\");\n }\n const echo = `sfmc_${Date.now().toString(36)}_${randomBytes(4).toString(\"hex\")}`;\n const payload = JSON.stringify({ action, params, echo });\n\n return new Promise<LlbotApiResult>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pending.delete(echo);\n reject(new Error(`LLBot WS API \u8D85\u65F6 (${action})`));\n }, timeoutMs);\n this.pending.set(echo, { resolve, reject, timer });\n try {\n this.sock!.send(payload);\n } catch (e) {\n this.pending.delete(echo);\n clearTimeout(timer);\n reject(e instanceof Error ? e : new Error(String(e)));\n }\n });\n }\n\n async sendGroupMsg(groupId: string | number, text: string): Promise<void> {\n const gid = typeof groupId === \"number\" ? groupId : parseInt(String(groupId), 10);\n if (!Number.isFinite(gid)) throw new Error(`\u65E0\u6548 group_id: ${groupId}`);\n const result = await this.call(\"send_group_msg\", {\n group_id: gid,\n message: [{ type: \"text\", data: { text } }],\n });\n const ret = result.retcode ?? (result.status === \"ok\" ? 0 : -1);\n if (ret !== 0) {\n throw new Error(\n `LLBot send_group_msg retcode=${ret}: ${result.message || result.wording || result.status || \"fail\"}`\n );\n }\n }\n\n private rejectAll(err: Error): void {\n for (const [k, p] of this.pending) {\n clearTimeout(p.timer);\n p.reject(err);\n this.pending.delete(k);\n }\n }\n}\n\n/** \u8FDB\u7A0B\u5185\u5355\u4F8B\uFF1Aws-server \u6302\u63A5\uFF0Creply-llbot / \u5176\u5B83\u51FA\u53E3\u590D\u7528 */\nexport const llbotWsApi = new LlbotWsApi();\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * llbot-ws-api.ts \u2014 \u7ECF LLBot reverse-WS \u8C03\u7528 OneBot API\n *\n * \u591A\u6570 LLBot \u90E8\u7F72\u53EA\u5F00 reverse-ws\u3001\u4E0D\u5F00\u6B63\u5411 HTTP(3004)\u3002\n * \u6307\u4EE4\u56DE\u590D\u5E94\u8D70\u5DF2\u8FDE\u63A5\u7684 WebSocket\uFF0C\u800C\u4E0D\u662F\u5047\u5B9A 3004 \u53EF\u8FBE\u3002\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport type { WebSocket } from \"ws\";\nimport { log } from \"./log.js\";\n\nexport type LlbotApiResult = {\n status?: string;\n retcode?: number;\n data?: unknown;\n message?: string;\n wording?: string;\n echo?: string;\n};\n\ntype Pending = {\n resolve: (v: LlbotApiResult) => void;\n reject: (e: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n};\n\nconst DEFAULT_TIMEOUT_MS = 8000;\n\nexport class LlbotWsApi {\n private sock: WebSocket | null = null;\n private readonly pending = new Map<string, Pending>();\n\n /** \u5F53\u524D\u662F\u5426\u6709\u53EF\u7528\u7684 LLBot reverse-ws \u8FDE\u63A5 */\n get connected(): boolean {\n return !!this.sock && this.sock.readyState === 1; // OPEN\n }\n\n attach(sock: WebSocket): void {\n if (this.sock && this.sock !== sock) {\n this.rejectAll(new Error(\"LLBot WebSocket \u88AB\u65B0\u8FDE\u63A5\u66FF\u6362\"));\n }\n this.sock = sock;\n log.info(\"LLBot WS API \u5DF2\u6302\u63A5\uFF08\u53EF\u7528 reverse-ws \u53D1\u6D88\u606F\uFF09\");\n }\n\n detach(sock: WebSocket): void {\n if (this.sock !== sock) return;\n this.sock = null;\n this.rejectAll(new Error(\"LLBot WebSocket \u5DF2\u65AD\u5F00\"));\n log.info(\"LLBot WS API \u5DF2\u5378\u8F7D\");\n }\n\n /**\n * \u82E5\u5E27\u662F API \u56DE\u5305\uFF08\u6709 echo\u3001\u65E0 post_type\uFF09\uFF0C\u6D88\u8D39\u5E76\u8FD4\u56DE true\u3002\n * \u4E8B\u4EF6\u5E27\u8FD4\u56DE false\uFF0C\u4EA4\u7ED9 dispatcher\u3002\n */\n tryHandleResponse(raw: unknown): boolean {\n if (!raw || typeof raw !== \"object\") return false;\n const obj = raw as Record<string, unknown>;\n if (obj[\"post_type\"] != null) return false;\n const echo = obj[\"echo\"];\n if (echo == null || echo === \"\") return false;\n const key = String(echo);\n const p = this.pending.get(key);\n if (!p) return false;\n this.pending.delete(key);\n clearTimeout(p.timer);\n p.resolve(obj as LlbotApiResult);\n return true;\n }\n\n async call(action: string, params: Record<string, unknown>, timeoutMs = DEFAULT_TIMEOUT_MS): Promise<LlbotApiResult> {\n if (!this.connected || !this.sock) {\n throw new Error(\"LLBot reverse-ws \u672A\u8FDE\u63A5\uFF0C\u65E0\u6CD5\u8C03\u7528 API\");\n }\n const echo = `sfmc_${Date.now().toString(36)}_${randomBytes(4).toString(\"hex\")}`;\n const payload = JSON.stringify({ action, params, echo });\n\n return new Promise<LlbotApiResult>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pending.delete(echo);\n reject(new Error(`LLBot WS API \u8D85\u65F6 (${action})`));\n }, timeoutMs);\n this.pending.set(echo, { resolve, reject, timer });\n try {\n this.sock!.send(payload);\n } catch (e) {\n this.pending.delete(echo);\n clearTimeout(timer);\n reject(e instanceof Error ? e : new Error(String(e)));\n }\n });\n }\n\n async sendGroupMsg(groupId: string | number, text: string): Promise<void> {\n const gid = typeof groupId === \"number\" ? groupId : parseInt(String(groupId), 10);\n if (!Number.isFinite(gid)) throw new Error(`\u65E0\u6548 group_id: ${groupId}`);\n const result = await this.call(\"send_group_msg\", {\n group_id: gid,\n message: [{ type: \"text\", data: { text } }],\n });\n const ret = result.retcode ?? (result.status === \"ok\" ? 0 : -1);\n if (ret !== 0) {\n throw new Error(\n `LLBot send_group_msg retcode=${ret}: ${result.message || result.wording || result.status || \"fail\"}`\n );\n }\n }\n\n private rejectAll(err: Error): void {\n for (const [k, p] of this.pending) {\n clearTimeout(p.timer);\n p.reject(err);\n this.pending.delete(k);\n }\n }\n}\n\n/** \u8FDB\u7A0B\u5185\u5355\u4F8B\uFF1Aws-server \u6302\u63A5\uFF0Creply-llbot / \u5176\u5B83\u51FA\u53E3\u590D\u7528 */\nexport const llbotWsApi = new LlbotWsApi();\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -40,7 +40,9 @@ function attachmentsPlaceholder(attachments) {
40
40
  return parts.join("");
41
41
  }
42
42
  function extractOfficialText(msg) {
43
- const base = stripOfficialAtMention(String(msg.content ?? ""));
43
+ let base = stripOfficialAtMention(String(msg.content ?? ""));
44
+ const botName = msg.mentions?.find((mention) => mention.is_you)?.username;
45
+ if (botName && base.startsWith(`@${botName}`)) base = base.slice(botName.length + 1).trim();
44
46
  const suffix = attachmentsPlaceholder(msg.attachments);
45
47
  return `${base}${suffix ? (base ? " " : "") + suffix : ""}`.trim();
46
48
  }
@@ -53,6 +55,33 @@ class OfficialAtMessageDispatcher {
53
55
  this.opts = opts;
54
56
  this.forward = opts.forward ?? tryForward;
55
57
  }
58
+ /** 全量群消息只处理指令;普通聊天仍需 @机器人 才转发到游戏。 */
59
+ async handleGroupMessage(msg) {
60
+ if (!msg || typeof msg !== "object" || msg.author?.bot) return;
61
+ const groupOpenid = String(msg.group_openid ?? "");
62
+ if (!groupOpenid || !this.opts.groupOpenid || groupOpenid !== this.opts.groupOpenid) return;
63
+ if (/^<@!?/.test(String(msg.content ?? "").trim()) && msg.mentions?.length && !msg.mentions.some((mention) => mention.is_you)) return;
64
+ const router = this.opts.commandRouter;
65
+ if (!router) return;
66
+ const text = extractOfficialText(msg);
67
+ if (!text) return;
68
+ const normalized = text.replace(/^[//]+/, "").trim();
69
+ const isConfirmation = /^(confirm|确认|cancel|取消)(?:\s+\S+)?$/i.test(normalized);
70
+ if (!router.registry.resolve(normalized) && !isConfirmation && !/^[//]/.test(text)) return;
71
+ if (this.dedup.seen(msg.id)) return;
72
+ const memberId = String(msg.author?.member_openid || msg.author?.id || "unknown");
73
+ const fromName = String(msg.author?.username || `QQ_${memberId.slice(0, 8)}`);
74
+ await router.handle({
75
+ backend: "official",
76
+ scene: "group",
77
+ groupId: groupOpenid,
78
+ userId: memberId,
79
+ userName: fromName,
80
+ text,
81
+ isGroupAdmin: detectOfficialGroupAdmin(msg),
82
+ ...msg.id ? { msgId: String(msg.id) } : {}
83
+ });
84
+ }
56
85
  async handleGroupAtMessage(msg) {
57
86
  if (!msg || typeof msg !== "object") return;
58
87
  if (msg.author?.bot) return;
@@ -64,11 +93,11 @@ class OfficialAtMessageDispatcher {
64
93
  if (!this.opts.groupOpenid) {
65
94
  if (!this.loggedMissingOpenid) {
66
95
  log.warn(
67
- `qq_group_openid \u672A\u914D\u7F6E\u3002\u6536\u5230\u7FA4 openid=${groupOpenid}\uFF0C\u8BF7\u5199\u5165 configs/qq_config.json \u540E\u91CD\u542F\uFF08\u672C\u6761\u4E0D\u8F6C\u53D1\uFF09`
96
+ `official.group_openid \u672A\u914D\u7F6E\u3002\u6536\u5230\u7FA4 openid=${groupOpenid}\uFF0C\u8BF7\u5199\u5165 configs/qq_config.json \u540E\u91CD\u542F\uFF08\u672C\u6761\u4E0D\u8F6C\u53D1\uFF09`
68
97
  );
69
98
  this.loggedMissingOpenid = true;
70
99
  } else {
71
- log.info(`qq_group_openid \u672A\u914D\u7F6E\uFF0C\u4E22\u5F03\u7FA4\u6D88\u606F openid=${groupOpenid}`);
100
+ log.info(`official.group_openid \u672A\u914D\u7F6E\uFF0C\u4E22\u5F03\u7FA4\u6D88\u606F openid=${groupOpenid}`);
72
101
  }
73
102
  return;
74
103
  }
@@ -95,10 +124,6 @@ class OfficialAtMessageDispatcher {
95
124
  };
96
125
  if (await this.opts.commandRouter.handle(inbound)) return;
97
126
  }
98
- if (!this.opts.db.channelId) {
99
- log.warn("bridge_channel_id \u672A\u914D\u7F6E\uFF0C\u8DF3\u8FC7");
100
- return;
101
- }
102
127
  await this.forward(this.opts.db, fromId, fromName, text);
103
128
  }
104
129
  /**
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/official/events.ts"],
4
- "sourcesContent": ["/**\n * official/events.ts \u2014 \u5B98\u65B9 GROUP_AT / C2C \u89E3\u6790\u4E0E\u8F6C\u53D1\n */\n\nimport { stripOfficialAtMention } from \"@sfmc-bds/sdk/node/qq-official\";\nimport type { CommandRouter } from \"../commands/index.js\";\nimport { RecentMessageDedup } from \"../onebot.js\";\nimport { tryForward, type DBServerConfig } from \"../dbserver.js\";\nimport { log } from \"../log.js\";\n\nexport type OfficialAttachment = {\n url?: string;\n content_type?: string;\n filename?: string;\n};\n\nexport type OfficialGroupAtMessage = {\n id?: string;\n content?: string;\n group_openid?: string;\n timestamp?: string;\n author?: {\n id?: string;\n member_openid?: string;\n username?: string;\n bot?: boolean;\n /** \u82E5\u5E73\u53F0\u4E0B\u53D1\u5219\u89E3\u6790\uFF1B\u65E0\u5219\u65E0\u6CD5\u5224\u5B9A\u7FA4\u7BA1 */\n roles?: unknown;\n member_role?: unknown;\n };\n member?: {\n roles?: unknown;\n role?: unknown;\n };\n attachments?: OfficialAttachment[];\n};\n\n/** \u4ECE\u5B98\u65B9\u4E8B\u4EF6\u53EF\u9009\u5B57\u6BB5\u63A8\u65AD\u7FA4\u4E3B/\u7FA4\u7BA1\uFF08\u65E0\u53EF\u9760\u5B57\u6BB5\u5219 false\uFF09 */\nfunction detectOfficialGroupAdmin(msg: OfficialGroupAtMessage): boolean {\n const candidates: unknown[] = [\n msg.author?.member_role,\n msg.author?.roles,\n msg.member?.role,\n msg.member?.roles,\n ];\n for (const c of candidates) {\n if (c == null) continue;\n if (Array.isArray(c)) {\n const joined = c.map((x) => String(x).toLowerCase()).join(\",\");\n if (/\\b(owner|admin|2|3)\\b/.test(joined)) return true;\n continue;\n }\n const s = String(c).toLowerCase();\n if (s === \"owner\" || s === \"admin\" || s === \"2\" || s === \"3\") return true;\n }\n return false;\n}\n\n/** C2C \u5355\u804A\u4E8B\u4EF6\u4F53 */\nexport type OfficialC2cMessage = {\n id?: string;\n content?: string;\n timestamp?: string;\n author?: {\n id?: string;\n user_openid?: string;\n username?: string;\n bot?: boolean;\n };\n attachments?: OfficialAttachment[];\n};\n\nexport type OfficialForwardFn = (\n db: DBServerConfig,\n fromId: string,\n fromName: string,\n content: string\n) => Promise<void>;\n\nexport type OfficialDispatcherOptions = {\n /** \u5DF2\u914D\u7F6E\u7684\u7FA4 openid\uFF1B\u7A7A = \u53EA\u6253\u65E5\u5FD7\u4E0D\u8F6C\u53D1 */\n groupOpenid: string;\n db: DBServerConfig;\n /** \u53EF\u6CE8\u5165\uFF0C\u9ED8\u8BA4 tryForward\uFF08\u4FBF\u4E8E\u5355\u6D4B\uFF09 */\n forward?: OfficialForwardFn;\n /** QQ \u4FA7\u6307\u4EE4\u8DEF\u7531\uFF1B\u547D\u4E2D\u5219\u4E0D\u8F6C\u53D1 MC */\n commandRouter?: CommandRouter;\n};\n\n/**\n * \u628A attachments \u8F6C\u6210\u4E0E OneBot \u4E00\u81F4\u7684\u5360\u4F4D\u7B26\u540E\u7F00\u3002\n */\nexport function attachmentsPlaceholder(attachments: OfficialAttachment[] | undefined): string {\n if (!Array.isArray(attachments) || attachments.length === 0) return \"\";\n const parts: string[] = [];\n for (const att of attachments) {\n const ct = String(att.content_type ?? \"\").toLowerCase();\n const name = String(att.filename ?? \"\").toLowerCase();\n if (ct.startsWith(\"image/\") || /\\.(png|jpe?g|gif|webp|bmp)$/i.test(name)) {\n parts.push(\"[\u56FE\u7247]\");\n } else if (ct.startsWith(\"video/\") || /\\.(mp4|mov|avi|mkv)$/i.test(name)) {\n parts.push(\"[\u89C6\u9891]\");\n } else if (ct.startsWith(\"audio/\") || /\\.(mp3|wav|amr|silk)$/i.test(name)) {\n parts.push(\"[\u8BED\u97F3]\");\n } else {\n parts.push(\"[\u6587\u4EF6]\");\n }\n }\n return parts.join(\"\");\n}\n\n/** \u4ECE\u5B98\u65B9\u4E8B\u4EF6\u62BD\u51FA\u7EAF\u6587\u672C\uFF08\u53BB @bot + \u9644\u4EF6\u5360\u4F4D\uFF09 */\nexport function extractOfficialText(msg: {\n content?: string;\n attachments?: OfficialAttachment[];\n}): string {\n const base = stripOfficialAtMention(String(msg.content ?? \"\"));\n const suffix = attachmentsPlaceholder(msg.attachments);\n return `${base}${suffix ? (base ? \" \" : \"\") + suffix : \"\"}`.trim();\n}\n\nexport class OfficialAtMessageDispatcher {\n private readonly opts: OfficialDispatcherOptions;\n private readonly dedup = new RecentMessageDedup();\n private readonly forward: OfficialForwardFn;\n private loggedMissingOpenid = false;\n\n constructor(opts: OfficialDispatcherOptions) {\n this.opts = opts;\n this.forward = opts.forward ?? tryForward;\n }\n\n async handleGroupAtMessage(msg: OfficialGroupAtMessage): Promise<void> {\n if (!msg || typeof msg !== \"object\") return;\n if (msg.author?.bot) return;\n\n const groupOpenid = String(msg.group_openid ?? \"\");\n if (!groupOpenid) {\n log.warn(\"\u5B98\u65B9\u4E8B\u4EF6\u7F3A group_openid\uFF0C\u8DF3\u8FC7\");\n return;\n }\n\n if (!this.opts.groupOpenid) {\n if (!this.loggedMissingOpenid) {\n log.warn(\n `qq_group_openid \u672A\u914D\u7F6E\u3002\u6536\u5230\u7FA4 openid=${groupOpenid}\uFF0C\u8BF7\u5199\u5165 configs/qq_config.json \u540E\u91CD\u542F\uFF08\u672C\u6761\u4E0D\u8F6C\u53D1\uFF09`\n );\n this.loggedMissingOpenid = true;\n } else {\n log.info(`qq_group_openid \u672A\u914D\u7F6E\uFF0C\u4E22\u5F03\u7FA4\u6D88\u606F openid=${groupOpenid}`);\n }\n return;\n }\n\n if (groupOpenid !== this.opts.groupOpenid) {\n log.info(`\u975E\u76EE\u6807\u7FA4 openid=${groupOpenid}\uFF0C\u8DF3\u8FC7`);\n return;\n }\n\n if (this.dedup.seen(msg.id)) return;\n\n const text = extractOfficialText(msg);\n if (!text) return;\n\n const memberId = String(msg.author?.member_openid || msg.author?.id || \"unknown\");\n const fromName = String(msg.author?.username || `QQ_${memberId.slice(0, 8)}`);\n const fromId = `qq_${memberId}`;\n\n if (this.opts.commandRouter) {\n const inbound = {\n backend: \"official\" as const,\n scene: \"group\" as const,\n groupId: groupOpenid,\n userId: memberId,\n userName: fromName,\n text,\n isGroupAdmin: detectOfficialGroupAdmin(msg),\n ...(msg.id ? { msgId: String(msg.id) } : {}),\n };\n if (await this.opts.commandRouter.handle(inbound)) return;\n }\n\n if (!this.opts.db.channelId) {\n log.warn(\"bridge_channel_id \u672A\u914D\u7F6E\uFF0C\u8DF3\u8FC7\");\n return;\n }\n\n await this.forward(this.opts.db, fromId, fromName, text);\n }\n\n /**\n * \u5355\u804A\uFF1A\u4EC5\u8D70\u6307\u4EE4\u8DEF\u7531\uFF08\u83DC\u5355\u70B9\u51FB\u843D\u70B9\uFF09\uFF0C\u4E0D\u8F6C\u53D1 MC\u3002\n */\n async handleC2cMessage(msg: OfficialC2cMessage): Promise<void> {\n if (!msg || typeof msg !== \"object\") return;\n if (msg.author?.bot) return;\n if (this.dedup.seen(msg.id)) return;\n\n const text = extractOfficialText(msg);\n if (!text) return;\n\n const userId = String(msg.author?.user_openid || msg.author?.id || \"unknown\");\n const fromName = String(msg.author?.username || `QQ_${userId.slice(0, 8)}`);\n\n if (!this.opts.commandRouter) {\n log.info(`C2C \u65E0\u6307\u4EE4\u8DEF\u7531\uFF0C\u5FFD\u7565 text=${text.slice(0, 40)}`);\n return;\n }\n\n const inbound = {\n backend: \"official\" as const,\n scene: \"c2c\" as const,\n groupId: `c2c:${userId}`,\n userId,\n userName: fromName,\n text,\n ...(msg.id ? { msgId: String(msg.id) } : {}),\n };\n await this.opts.commandRouter.handle(inbound);\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * official/events.ts \u2014 \u5B98\u65B9\u7FA4\u6D88\u606F / C2C \u89E3\u6790\u4E0E\u8F6C\u53D1\n */\n\nimport { stripOfficialAtMention } from \"@sfmc-bds/sdk/node/qq-official\";\nimport type { CommandRouter } from \"../commands/index.js\";\nimport { RecentMessageDedup } from \"../onebot.js\";\nimport { tryForward, type DBServerConfig } from \"../dbserver.js\";\nimport { log } from \"../log.js\";\n\nexport type OfficialAttachment = {\n url?: string;\n content_type?: string;\n filename?: string;\n};\n\nexport type OfficialGroupAtMessage = {\n id?: string;\n content?: string;\n group_openid?: string;\n timestamp?: string;\n author?: {\n id?: string;\n member_openid?: string;\n username?: string;\n bot?: boolean;\n /** \u82E5\u5E73\u53F0\u4E0B\u53D1\u5219\u89E3\u6790\uFF1B\u65E0\u5219\u65E0\u6CD5\u5224\u5B9A\u7FA4\u7BA1 */\n roles?: unknown;\n member_role?: unknown;\n };\n member?: {\n roles?: unknown;\n role?: unknown;\n };\n attachments?: OfficialAttachment[];\n mentions?: Array<{ is_you?: boolean; username?: string }>;\n};\n\n/** \u4ECE\u5B98\u65B9\u4E8B\u4EF6\u53EF\u9009\u5B57\u6BB5\u63A8\u65AD\u7FA4\u4E3B/\u7FA4\u7BA1\uFF08\u65E0\u53EF\u9760\u5B57\u6BB5\u5219 false\uFF09 */\nfunction detectOfficialGroupAdmin(msg: OfficialGroupAtMessage): boolean {\n const candidates: unknown[] = [\n msg.author?.member_role,\n msg.author?.roles,\n msg.member?.role,\n msg.member?.roles,\n ];\n for (const c of candidates) {\n if (c == null) continue;\n if (Array.isArray(c)) {\n const joined = c.map((x) => String(x).toLowerCase()).join(\",\");\n if (/\\b(owner|admin|2|3)\\b/.test(joined)) return true;\n continue;\n }\n const s = String(c).toLowerCase();\n if (s === \"owner\" || s === \"admin\" || s === \"2\" || s === \"3\") return true;\n }\n return false;\n}\n\n/** C2C \u5355\u804A\u4E8B\u4EF6\u4F53 */\nexport type OfficialC2cMessage = {\n id?: string;\n content?: string;\n timestamp?: string;\n author?: {\n id?: string;\n user_openid?: string;\n username?: string;\n bot?: boolean;\n };\n attachments?: OfficialAttachment[];\n};\n\nexport type OfficialForwardFn = (\n db: DBServerConfig,\n fromId: string,\n fromName: string,\n content: string\n) => Promise<void>;\n\nexport type OfficialDispatcherOptions = {\n /** \u5DF2\u914D\u7F6E\u7684\u7FA4 openid\uFF1B\u7A7A = \u53EA\u6253\u65E5\u5FD7\u4E0D\u8F6C\u53D1 */\n groupOpenid: string;\n db: DBServerConfig;\n /** \u53EF\u6CE8\u5165\uFF0C\u9ED8\u8BA4 tryForward\uFF08\u4FBF\u4E8E\u5355\u6D4B\uFF09 */\n forward?: OfficialForwardFn;\n /** QQ \u4FA7\u6307\u4EE4\u8DEF\u7531\uFF1B\u547D\u4E2D\u5219\u4E0D\u8F6C\u53D1 MC */\n commandRouter?: CommandRouter;\n};\n\n/**\n * \u628A attachments \u8F6C\u6210\u4E0E OneBot \u4E00\u81F4\u7684\u5360\u4F4D\u7B26\u540E\u7F00\u3002\n */\nexport function attachmentsPlaceholder(attachments: OfficialAttachment[] | undefined): string {\n if (!Array.isArray(attachments) || attachments.length === 0) return \"\";\n const parts: string[] = [];\n for (const att of attachments) {\n const ct = String(att.content_type ?? \"\").toLowerCase();\n const name = String(att.filename ?? \"\").toLowerCase();\n if (ct.startsWith(\"image/\") || /\\.(png|jpe?g|gif|webp|bmp)$/i.test(name)) {\n parts.push(\"[\u56FE\u7247]\");\n } else if (ct.startsWith(\"video/\") || /\\.(mp4|mov|avi|mkv)$/i.test(name)) {\n parts.push(\"[\u89C6\u9891]\");\n } else if (ct.startsWith(\"audio/\") || /\\.(mp3|wav|amr|silk)$/i.test(name)) {\n parts.push(\"[\u8BED\u97F3]\");\n } else {\n parts.push(\"[\u6587\u4EF6]\");\n }\n }\n return parts.join(\"\");\n}\n\n/** \u4ECE\u5B98\u65B9\u4E8B\u4EF6\u62BD\u51FA\u7EAF\u6587\u672C\uFF08\u53BB @bot + \u9644\u4EF6\u5360\u4F4D\uFF09 */\nexport function extractOfficialText(msg: {\n content?: string;\n attachments?: OfficialAttachment[];\n mentions?: Array<{ is_you?: boolean; username?: string }>;\n}): string {\n let base = stripOfficialAtMention(String(msg.content ?? \"\"));\n const botName = msg.mentions?.find((mention) => mention.is_you)?.username;\n if (botName && base.startsWith(`@${botName}`)) base = base.slice(botName.length + 1).trim();\n const suffix = attachmentsPlaceholder(msg.attachments);\n return `${base}${suffix ? (base ? \" \" : \"\") + suffix : \"\"}`.trim();\n}\n\nexport class OfficialAtMessageDispatcher {\n private readonly opts: OfficialDispatcherOptions;\n private readonly dedup = new RecentMessageDedup();\n private readonly forward: OfficialForwardFn;\n private loggedMissingOpenid = false;\n\n constructor(opts: OfficialDispatcherOptions) {\n this.opts = opts;\n this.forward = opts.forward ?? tryForward;\n }\n\n /** \u5168\u91CF\u7FA4\u6D88\u606F\u53EA\u5904\u7406\u6307\u4EE4\uFF1B\u666E\u901A\u804A\u5929\u4ECD\u9700 @\u673A\u5668\u4EBA \u624D\u8F6C\u53D1\u5230\u6E38\u620F\u3002 */\n async handleGroupMessage(msg: OfficialGroupAtMessage): Promise<void> {\n if (!msg || typeof msg !== \"object\" || msg.author?.bot) return;\n const groupOpenid = String(msg.group_openid ?? \"\");\n if (!groupOpenid || !this.opts.groupOpenid || groupOpenid !== this.opts.groupOpenid) return;\n if (\n /^<@!?/.test(String(msg.content ?? \"\").trim()) &&\n msg.mentions?.length &&\n !msg.mentions.some((mention) => mention.is_you)\n ) return;\n const router = this.opts.commandRouter;\n if (!router) return;\n const text = extractOfficialText(msg);\n if (!text) return;\n const normalized = text.replace(/^[/\uFF0F]+/, \"\").trim();\n const isConfirmation = /^(confirm|\u786E\u8BA4|cancel|\u53D6\u6D88)(?:\\s+\\S+)?$/i.test(normalized);\n if (!router.registry.resolve(normalized) && !isConfirmation && !/^[/\uFF0F]/.test(text)) return;\n // \u5168\u91CF\u4E0E @ \u4E8B\u4EF6\u53EF\u80FD\u540C\u65F6\u5230\u8FBE\uFF1B\u5148\u5360\u7528\u6D88\u606F ID\uFF0C\u518D\u6267\u884C\u5F02\u6B65\u6307\u4EE4\u3002\n if (this.dedup.seen(msg.id)) return;\n\n const memberId = String(msg.author?.member_openid || msg.author?.id || \"unknown\");\n const fromName = String(msg.author?.username || `QQ_${memberId.slice(0, 8)}`);\n await router.handle({\n backend: \"official\",\n scene: \"group\",\n groupId: groupOpenid,\n userId: memberId,\n userName: fromName,\n text,\n isGroupAdmin: detectOfficialGroupAdmin(msg),\n ...(msg.id ? { msgId: String(msg.id) } : {}),\n });\n }\n\n async handleGroupAtMessage(msg: OfficialGroupAtMessage): Promise<void> {\n if (!msg || typeof msg !== \"object\") return;\n if (msg.author?.bot) return;\n\n const groupOpenid = String(msg.group_openid ?? \"\");\n if (!groupOpenid) {\n log.warn(\"\u5B98\u65B9\u4E8B\u4EF6\u7F3A group_openid\uFF0C\u8DF3\u8FC7\");\n return;\n }\n\n if (!this.opts.groupOpenid) {\n if (!this.loggedMissingOpenid) {\n log.warn(\n `official.group_openid \u672A\u914D\u7F6E\u3002\u6536\u5230\u7FA4 openid=${groupOpenid}\uFF0C\u8BF7\u5199\u5165 configs/qq_config.json \u540E\u91CD\u542F\uFF08\u672C\u6761\u4E0D\u8F6C\u53D1\uFF09`\n );\n this.loggedMissingOpenid = true;\n } else {\n log.info(`official.group_openid \u672A\u914D\u7F6E\uFF0C\u4E22\u5F03\u7FA4\u6D88\u606F openid=${groupOpenid}`);\n }\n return;\n }\n\n if (groupOpenid !== this.opts.groupOpenid) {\n log.info(`\u975E\u76EE\u6807\u7FA4 openid=${groupOpenid}\uFF0C\u8DF3\u8FC7`);\n return;\n }\n\n if (this.dedup.seen(msg.id)) return;\n\n const text = extractOfficialText(msg);\n if (!text) return;\n\n const memberId = String(msg.author?.member_openid || msg.author?.id || \"unknown\");\n const fromName = String(msg.author?.username || `QQ_${memberId.slice(0, 8)}`);\n const fromId = `qq_${memberId}`;\n\n if (this.opts.commandRouter) {\n const inbound = {\n backend: \"official\" as const,\n scene: \"group\" as const,\n groupId: groupOpenid,\n userId: memberId,\n userName: fromName,\n text,\n isGroupAdmin: detectOfficialGroupAdmin(msg),\n ...(msg.id ? { msgId: String(msg.id) } : {}),\n };\n if (await this.opts.commandRouter.handle(inbound)) return;\n }\n\n await this.forward(this.opts.db, fromId, fromName, text);\n }\n\n /**\n * \u5355\u804A\uFF1A\u4EC5\u8D70\u6307\u4EE4\u8DEF\u7531\uFF08\u83DC\u5355\u70B9\u51FB\u843D\u70B9\uFF09\uFF0C\u4E0D\u8F6C\u53D1 MC\u3002\n */\n async handleC2cMessage(msg: OfficialC2cMessage): Promise<void> {\n if (!msg || typeof msg !== \"object\") return;\n if (msg.author?.bot) return;\n if (this.dedup.seen(msg.id)) return;\n\n const text = extractOfficialText(msg);\n if (!text) return;\n\n const userId = String(msg.author?.user_openid || msg.author?.id || \"unknown\");\n const fromName = String(msg.author?.username || `QQ_${userId.slice(0, 8)}`);\n\n if (!this.opts.commandRouter) {\n log.info(`C2C \u65E0\u6307\u4EE4\u8DEF\u7531\uFF0C\u5FFD\u7565 text=${text.slice(0, 40)}`);\n return;\n }\n\n const inbound = {\n backend: \"official\" as const,\n scene: \"c2c\" as const,\n groupId: `c2c:${userId}`,\n userId,\n userName: fromName,\n text,\n ...(msg.id ? { msgId: String(msg.id) } : {}),\n };\n await this.opts.commandRouter.handle(inbound);\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -31,6 +31,7 @@ async function startOfficialGateway(opts) {
31
31
  let lastSeq = null;
32
32
  let reconnectAttempt = 0;
33
33
  let botSelfId = null;
34
+ let fullGroupMessageSeen = false;
34
35
  const clearHeartbeat = () => {
35
36
  if (heartbeatTimer) {
36
37
  clearInterval(heartbeatTimer);
@@ -50,7 +51,11 @@ async function startOfficialGateway(opts) {
50
51
  };
51
52
  const handleDispatch = async (t, d, s) => {
52
53
  if (typeof s === "number") lastSeq = s;
53
- if (t && t !== "READY" && t !== "RESUMED") {
54
+ if (t === "GROUP_MESSAGE_CREATE" && !fullGroupMessageSeen) {
55
+ fullGroupMessageSeen = true;
56
+ log.info("\u5B98\u65B9 Gateway \u5DF2\u6536\u5230\u7FA4\u5168\u91CF\u6D88\u606F\u4E8B\u4EF6\uFF0C\u53EF\u5904\u7406\u65E0\u9700 @ \u7684\u6307\u4EE4");
57
+ }
58
+ if (t && t !== "READY" && t !== "RESUMED" && t !== "GROUP_MESSAGE_CREATE") {
54
59
  const preview = t === "GROUP_AT_MESSAGE_CREATE" || t === "C2C_MESSAGE_CREATE" ? (() => {
55
60
  const msg = d;
56
61
  return ` group=${msg.group_openid ?? "\u2014"} author=${msg.author?.id ?? "\u2014"} content=${JSON.stringify(String(msg.content ?? "").slice(0, 80))}`;
@@ -76,6 +81,10 @@ async function startOfficialGateway(opts) {
76
81
  await opts.dispatcher.handleGroupAtMessage(d);
77
82
  return;
78
83
  }
84
+ if (t === "GROUP_MESSAGE_CREATE") {
85
+ await opts.dispatcher.handleGroupMessage(d);
86
+ return;
87
+ }
79
88
  if (t === "C2C_MESSAGE_CREATE") {
80
89
  await opts.dispatcher.handleC2cMessage(d);
81
90
  return;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/official/gateway.ts"],
4
- "sourcesContent": ["/**\n * official/gateway.ts \u2014 QQ \u5F00\u653E\u5E73\u53F0 Gateway\uFF08WebSocket\uFF09\u5BA2\u6237\u7AEF\n *\n * \u6D41\u7A0B: getAccessToken \u2192 GET /gateway/bot \u2192 Hello \u2192 Identify \u2192 \u5FC3\u8DF3 / Resume\n * \u4EC5\u8BA2\u9605 GROUP_AND_C2C_EVENT\uFF0C\u5904\u7406 GROUP_AT_MESSAGE_CREATE\u3002\n */\n\nimport {\n QQ_INTENT_GROUP_C2C_INTERACTION,\n QqAccessTokenManager,\n fetchGatewayUrl,\n type QqOfficialCredentials,\n} from \"@sfmc-bds/sdk/node/qq-official\";\nimport { createRequire } from \"node:module\";\nimport { log } from \"../log.js\";\nimport type { InteractionRouter } from \"../commands/interaction-router.js\";\nimport type { OfficialAtMessageDispatcher, OfficialC2cMessage, OfficialGroupAtMessage } from \"./events.js\";\n\nconst localRequire = createRequire(import.meta.url);\n\nconst OP_DISPATCH = 0;\nconst OP_HEARTBEAT = 1;\nconst OP_IDENTIFY = 2;\nconst OP_RESUME = 6;\nconst OP_RECONNECT = 7;\nconst OP_INVALID_SESSION = 9;\nconst OP_HELLO = 10;\nconst OP_HEARTBEAT_ACK = 11;\n\ntype WsPayload = {\n op: number;\n d?: unknown;\n s?: number | null;\n t?: string;\n};\n\nexport type OfficialGatewayOptions = {\n creds: QqOfficialCredentials;\n dispatcher: OfficialAtMessageDispatcher;\n /** INTERACTION \u56DE\u8C03\u5206\u53D1 */\n interactionRouter?: InteractionRouter;\n /** \u6D4B\u8BD5\u53EF\u6CE8\u5165\uFF1B\u9ED8\u8BA4\u7528 tokenManager */\n tokenManager?: QqAccessTokenManager;\n};\n\nasync function loadWs(): Promise<typeof import(\"ws\")> {\n try {\n return localRequire(\"ws\") as typeof import(\"ws\");\n } catch {\n return localRequire(\"../db-server/node_modules/ws\") as typeof import(\"ws\");\n }\n}\n\n/**\n * \u542F\u52A8\u5B98\u65B9 Gateway\uFF1B\u8FD4\u56DE stop()\u3002\u65AD\u7EBF\u81EA\u52A8\u91CD\u8FDE\uFF08\u6307\u6570\u9000\u907F\uFF0C\u4E0A\u9650 60s\uFF09\u3002\n */\nexport async function startOfficialGateway(opts: OfficialGatewayOptions): Promise<{ stop: () => void }> {\n const tokenManager =\n opts.tokenManager ?? new QqAccessTokenManager(opts.creds);\n let stopped = false;\n let ws: import(\"ws\").WebSocket | null = null;\n let heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n let sessionId: string | null = null;\n let lastSeq: number | null = null;\n let reconnectAttempt = 0;\n let botSelfId: string | null = null;\n\n const clearHeartbeat = (): void => {\n if (heartbeatTimer) {\n clearInterval(heartbeatTimer);\n heartbeatTimer = null;\n }\n };\n\n const sendJson = (payload: WsPayload): void => {\n if (!ws || ws.readyState !== 1 /* OPEN */) return;\n ws.send(JSON.stringify(payload));\n };\n\n const startHeartbeat = (intervalMs: number): void => {\n clearHeartbeat();\n const period = Math.max(5000, intervalMs);\n heartbeatTimer = setInterval(() => {\n sendJson({ op: OP_HEARTBEAT, d: lastSeq });\n }, period);\n };\n\n const handleDispatch = async (t: string | undefined, d: unknown, s: number | null | undefined): Promise<void> => {\n if (typeof s === \"number\") lastSeq = s;\n // \u8BCA\u65AD\uFF1A\u4EFB\u4F55 Dispatch \u90FD\u6253\u4E00\u884C\uFF08\u6392\u969C\u300C@\u4E86\u4F46\u6CA1\u65E5\u5FD7\u300D\uFF09\n if (t && t !== \"READY\" && t !== \"RESUMED\") {\n const preview =\n t === \"GROUP_AT_MESSAGE_CREATE\" || t === \"C2C_MESSAGE_CREATE\"\n ? (() => {\n const msg = d as { group_openid?: string; content?: string; author?: { id?: string } };\n return ` group=${msg.group_openid ?? \"\u2014\"} author=${msg.author?.id ?? \"\u2014\"} content=${JSON.stringify(String(msg.content ?? \"\").slice(0, 80))}`;\n })()\n : \"\";\n log.info(`\u5B98\u65B9 Dispatch t=${t}${preview}`);\n }\n if (t === \"READY\") {\n const ready = d as { session_id?: string; user?: { id?: string; username?: string } };\n sessionId = String(ready.session_id ?? \"\") || null;\n botSelfId = ready.user?.id ? String(ready.user.id) : null;\n reconnectAttempt = 0;\n log.info(\n `\u5B98\u65B9 Gateway READY (session=${sessionId ?? \"?\"}, bot=${ready.user?.username ?? botSelfId ?? \"?\"}, intents=${QQ_INTENT_GROUP_C2C_INTERACTION})`\n );\n return;\n }\n if (t === \"RESUMED\") {\n reconnectAttempt = 0;\n log.info(\"\u5B98\u65B9 Gateway \u4F1A\u8BDD\u5DF2\u6062\u590D (RESUMED)\");\n return;\n }\n if (t === \"GROUP_AT_MESSAGE_CREATE\") {\n await opts.dispatcher.handleGroupAtMessage(d as OfficialGroupAtMessage);\n return;\n }\n if (t === \"C2C_MESSAGE_CREATE\") {\n await opts.dispatcher.handleC2cMessage(d as OfficialC2cMessage);\n return;\n }\n if (t === \"INTERACTION_CREATE\") {\n if (opts.interactionRouter) {\n await opts.interactionRouter.handle(d);\n } else {\n log.warn(\"\u6536\u5230 INTERACTION_CREATE \u4F46\u672A\u914D\u7F6E interactionRouter\");\n }\n return;\n }\n };\n\n const scheduleReconnect = (): void => {\n if (stopped) return;\n clearHeartbeat();\n const delay = Math.min(60_000, 1000 * 2 ** Math.min(reconnectAttempt, 5));\n reconnectAttempt += 1;\n log.warn(`\u5B98\u65B9 Gateway \u5C06\u5728 ${delay}ms \u540E\u91CD\u8FDE (attempt=${reconnectAttempt})`);\n reconnectTimer = setTimeout(() => {\n void connect();\n }, delay);\n };\n\n const connect = async (): Promise<void> => {\n if (stopped) return;\n try {\n const token = await tokenManager.getAccessToken();\n const gatewayUrl = await fetchGatewayUrl(opts.creds, { tokenManager });\n const wsMod = await loadWs();\n const { WebSocket } = wsMod;\n\n await new Promise<void>((resolve, reject) => {\n const sock = new WebSocket(gatewayUrl);\n ws = sock;\n let helloDone = false;\n\n const fail = (err: Error): void => {\n try {\n sock.close();\n } catch {\n /* ignore */\n }\n reject(err);\n };\n\n sock.on(\"open\", () => {\n log.info(`\u5B98\u65B9 Gateway \u5DF2\u8FDE\u63A5 ${gatewayUrl}`);\n });\n\n sock.on(\"message\", (raw) => {\n void (async () => {\n let payload: WsPayload;\n try {\n payload = JSON.parse(raw.toString(\"utf-8\")) as WsPayload;\n } catch (e) {\n log.error(`\u5B98\u65B9 Gateway \u89E3\u6790\u5931\u8D25: ${(e as Error).message}`);\n return;\n }\n\n if (payload.op === OP_HELLO) {\n const interval = Number(\n (payload.d as { heartbeat_interval?: number } | undefined)?.heartbeat_interval ?? 41250\n );\n startHeartbeat(interval);\n if (sessionId && lastSeq !== null) {\n sendJson({\n op: OP_RESUME,\n d: {\n token: `QQBot ${token}`,\n session_id: sessionId,\n seq: lastSeq,\n },\n });\n } else {\n log.info(`\u5B98\u65B9 Gateway Identify intents=${QQ_INTENT_GROUP_C2C_INTERACTION} (GROUP_C2C+INTERACTION)`);\n sendJson({\n op: OP_IDENTIFY,\n d: {\n token: `QQBot ${token}`,\n intents: QQ_INTENT_GROUP_C2C_INTERACTION,\n shard: [0, 1],\n properties: {\n $os: process.platform,\n $browser: \"sfmc-qq-bridge\",\n $device: \"sfmc-qq-bridge\",\n },\n },\n });\n }\n helloDone = true;\n resolve();\n return;\n }\n\n if (payload.op === OP_HEARTBEAT_ACK) return;\n\n if (payload.op === OP_RECONNECT) {\n log.warn(\"\u5B98\u65B9 Gateway \u8981\u6C42\u91CD\u8FDE (op=7)\");\n try {\n sock.close();\n } catch {\n /* ignore */\n }\n return;\n }\n\n if (payload.op === OP_INVALID_SESSION) {\n log.warn(\"\u5B98\u65B9 Gateway Invalid Session\uFF0C\u6E05\u7A7A session \u540E\u91CD\u8FDE\");\n sessionId = null;\n lastSeq = null;\n try {\n sock.close();\n } catch {\n /* ignore */\n }\n return;\n }\n\n if (payload.op === OP_DISPATCH) {\n await handleDispatch(payload.t, payload.d, payload.s ?? null);\n }\n })();\n });\n\n sock.on(\"close\", () => {\n clearHeartbeat();\n ws = null;\n if (!helloDone) {\n fail(new Error(\"Gateway \u5728 Hello \u524D\u5173\u95ED\"));\n return;\n }\n if (!stopped) scheduleReconnect();\n });\n\n sock.on(\"error\", (err) => {\n log.error(`\u5B98\u65B9 Gateway \u9519\u8BEF: ${err.message}`);\n if (!helloDone) fail(err);\n });\n });\n } catch (e) {\n log.error(`\u5B98\u65B9 Gateway \u8FDE\u63A5\u5931\u8D25: ${(e as Error).message}`);\n scheduleReconnect();\n }\n };\n\n await connect();\n\n return {\n stop: () => {\n stopped = true;\n clearHeartbeat();\n if (reconnectTimer) clearTimeout(reconnectTimer);\n try {\n ws?.close();\n } catch {\n /* ignore */\n }\n ws = null;\n },\n };\n}\n\n/** \u4F9B status \u547D\u4EE4\u8BFB\u53D6\uFF08\u5F53\u524D\u8FDB\u7A0B\u5185\u65E0\u5168\u5C40 self_id \u5BFC\u51FA\u65F6\u7531 index \u6301\u6709\uFF09 */\nexport type OfficialGatewayHandle = Awaited<ReturnType<typeof startOfficialGateway>>;\n\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * official/gateway.ts \u2014 QQ \u5F00\u653E\u5E73\u53F0 Gateway\uFF08WebSocket\uFF09\u5BA2\u6237\u7AEF\n *\n * \u6D41\u7A0B: getAccessToken \u2192 GET /gateway/bot \u2192 Hello \u2192 Identify \u2192 \u5FC3\u8DF3 / Resume\n * \u8BA2\u9605 GROUP_AND_C2C_EVENT\uFF0C\u5904\u7406\u7FA4\u5168\u91CF\u6D88\u606F\u3001\u7FA4 @ \u6D88\u606F\u4E0E\u5355\u804A\u3002\n */\n\nimport {\n QQ_INTENT_GROUP_C2C_INTERACTION,\n QqAccessTokenManager,\n fetchGatewayUrl,\n type QqOfficialCredentials,\n} from \"@sfmc-bds/sdk/node/qq-official\";\nimport { createRequire } from \"node:module\";\nimport { log } from \"../log.js\";\nimport type { InteractionRouter } from \"../commands/interaction-router.js\";\nimport type { OfficialAtMessageDispatcher, OfficialC2cMessage, OfficialGroupAtMessage } from \"./events.js\";\n\nconst localRequire = createRequire(import.meta.url);\n\nconst OP_DISPATCH = 0;\nconst OP_HEARTBEAT = 1;\nconst OP_IDENTIFY = 2;\nconst OP_RESUME = 6;\nconst OP_RECONNECT = 7;\nconst OP_INVALID_SESSION = 9;\nconst OP_HELLO = 10;\nconst OP_HEARTBEAT_ACK = 11;\n\ntype WsPayload = {\n op: number;\n d?: unknown;\n s?: number | null;\n t?: string;\n};\n\nexport type OfficialGatewayOptions = {\n creds: QqOfficialCredentials;\n dispatcher: OfficialAtMessageDispatcher;\n /** INTERACTION \u56DE\u8C03\u5206\u53D1 */\n interactionRouter?: InteractionRouter;\n /** \u6D4B\u8BD5\u53EF\u6CE8\u5165\uFF1B\u9ED8\u8BA4\u7528 tokenManager */\n tokenManager?: QqAccessTokenManager;\n};\n\nasync function loadWs(): Promise<typeof import(\"ws\")> {\n try {\n return localRequire(\"ws\") as typeof import(\"ws\");\n } catch {\n return localRequire(\"../db-server/node_modules/ws\") as typeof import(\"ws\");\n }\n}\n\n/**\n * \u542F\u52A8\u5B98\u65B9 Gateway\uFF1B\u8FD4\u56DE stop()\u3002\u65AD\u7EBF\u81EA\u52A8\u91CD\u8FDE\uFF08\u6307\u6570\u9000\u907F\uFF0C\u4E0A\u9650 60s\uFF09\u3002\n */\nexport async function startOfficialGateway(opts: OfficialGatewayOptions): Promise<{ stop: () => void }> {\n const tokenManager =\n opts.tokenManager ?? new QqAccessTokenManager(opts.creds);\n let stopped = false;\n let ws: import(\"ws\").WebSocket | null = null;\n let heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n let sessionId: string | null = null;\n let lastSeq: number | null = null;\n let reconnectAttempt = 0;\n let botSelfId: string | null = null;\n let fullGroupMessageSeen = false;\n\n const clearHeartbeat = (): void => {\n if (heartbeatTimer) {\n clearInterval(heartbeatTimer);\n heartbeatTimer = null;\n }\n };\n\n const sendJson = (payload: WsPayload): void => {\n if (!ws || ws.readyState !== 1 /* OPEN */) return;\n ws.send(JSON.stringify(payload));\n };\n\n const startHeartbeat = (intervalMs: number): void => {\n clearHeartbeat();\n const period = Math.max(5000, intervalMs);\n heartbeatTimer = setInterval(() => {\n sendJson({ op: OP_HEARTBEAT, d: lastSeq });\n }, period);\n };\n\n const handleDispatch = async (t: string | undefined, d: unknown, s: number | null | undefined): Promise<void> => {\n if (typeof s === \"number\") lastSeq = s;\n // \u5168\u91CF\u6A21\u5F0F\u53EF\u80FD\u4E0B\u53D1\u7FA4\u5185\u6BCF\u6761\u6D88\u606F\uFF0C\u53EA\u8BB0\u5F55\u9996\u6B21\u5230\u8FBE\uFF0C\u907F\u514D\u65E5\u5FD7\u5199\u5165\u666E\u901A\u804A\u5929\u3002\n if (t === \"GROUP_MESSAGE_CREATE\" && !fullGroupMessageSeen) {\n fullGroupMessageSeen = true;\n log.info(\"\u5B98\u65B9 Gateway \u5DF2\u6536\u5230\u7FA4\u5168\u91CF\u6D88\u606F\u4E8B\u4EF6\uFF0C\u53EF\u5904\u7406\u65E0\u9700 @ \u7684\u6307\u4EE4\");\n }\n if (t && t !== \"READY\" && t !== \"RESUMED\" && t !== \"GROUP_MESSAGE_CREATE\") {\n const preview =\n t === \"GROUP_AT_MESSAGE_CREATE\" || t === \"C2C_MESSAGE_CREATE\"\n ? (() => {\n const msg = d as { group_openid?: string; content?: string; author?: { id?: string } };\n return ` group=${msg.group_openid ?? \"\u2014\"} author=${msg.author?.id ?? \"\u2014\"} content=${JSON.stringify(String(msg.content ?? \"\").slice(0, 80))}`;\n })()\n : \"\";\n log.info(`\u5B98\u65B9 Dispatch t=${t}${preview}`);\n }\n if (t === \"READY\") {\n const ready = d as { session_id?: string; user?: { id?: string; username?: string } };\n sessionId = String(ready.session_id ?? \"\") || null;\n botSelfId = ready.user?.id ? String(ready.user.id) : null;\n reconnectAttempt = 0;\n log.info(\n `\u5B98\u65B9 Gateway READY (session=${sessionId ?? \"?\"}, bot=${ready.user?.username ?? botSelfId ?? \"?\"}, intents=${QQ_INTENT_GROUP_C2C_INTERACTION})`\n );\n return;\n }\n if (t === \"RESUMED\") {\n reconnectAttempt = 0;\n log.info(\"\u5B98\u65B9 Gateway \u4F1A\u8BDD\u5DF2\u6062\u590D (RESUMED)\");\n return;\n }\n if (t === \"GROUP_AT_MESSAGE_CREATE\") {\n await opts.dispatcher.handleGroupAtMessage(d as OfficialGroupAtMessage);\n return;\n }\n if (t === \"GROUP_MESSAGE_CREATE\") {\n await opts.dispatcher.handleGroupMessage(d as OfficialGroupAtMessage);\n return;\n }\n if (t === \"C2C_MESSAGE_CREATE\") {\n await opts.dispatcher.handleC2cMessage(d as OfficialC2cMessage);\n return;\n }\n if (t === \"INTERACTION_CREATE\") {\n if (opts.interactionRouter) {\n await opts.interactionRouter.handle(d);\n } else {\n log.warn(\"\u6536\u5230 INTERACTION_CREATE \u4F46\u672A\u914D\u7F6E interactionRouter\");\n }\n return;\n }\n };\n\n const scheduleReconnect = (): void => {\n if (stopped) return;\n clearHeartbeat();\n const delay = Math.min(60_000, 1000 * 2 ** Math.min(reconnectAttempt, 5));\n reconnectAttempt += 1;\n log.warn(`\u5B98\u65B9 Gateway \u5C06\u5728 ${delay}ms \u540E\u91CD\u8FDE (attempt=${reconnectAttempt})`);\n reconnectTimer = setTimeout(() => {\n void connect();\n }, delay);\n };\n\n const connect = async (): Promise<void> => {\n if (stopped) return;\n try {\n const token = await tokenManager.getAccessToken();\n const gatewayUrl = await fetchGatewayUrl(opts.creds, { tokenManager });\n const wsMod = await loadWs();\n const { WebSocket } = wsMod;\n\n await new Promise<void>((resolve, reject) => {\n const sock = new WebSocket(gatewayUrl);\n ws = sock;\n let helloDone = false;\n\n const fail = (err: Error): void => {\n try {\n sock.close();\n } catch {\n /* ignore */\n }\n reject(err);\n };\n\n sock.on(\"open\", () => {\n log.info(`\u5B98\u65B9 Gateway \u5DF2\u8FDE\u63A5 ${gatewayUrl}`);\n });\n\n sock.on(\"message\", (raw) => {\n void (async () => {\n let payload: WsPayload;\n try {\n payload = JSON.parse(raw.toString(\"utf-8\")) as WsPayload;\n } catch (e) {\n log.error(`\u5B98\u65B9 Gateway \u89E3\u6790\u5931\u8D25: ${(e as Error).message}`);\n return;\n }\n\n if (payload.op === OP_HELLO) {\n const interval = Number(\n (payload.d as { heartbeat_interval?: number } | undefined)?.heartbeat_interval ?? 41250\n );\n startHeartbeat(interval);\n if (sessionId && lastSeq !== null) {\n sendJson({\n op: OP_RESUME,\n d: {\n token: `QQBot ${token}`,\n session_id: sessionId,\n seq: lastSeq,\n },\n });\n } else {\n log.info(`\u5B98\u65B9 Gateway Identify intents=${QQ_INTENT_GROUP_C2C_INTERACTION} (GROUP_C2C+INTERACTION)`);\n sendJson({\n op: OP_IDENTIFY,\n d: {\n token: `QQBot ${token}`,\n intents: QQ_INTENT_GROUP_C2C_INTERACTION,\n shard: [0, 1],\n properties: {\n $os: process.platform,\n $browser: \"sfmc-qq-bridge\",\n $device: \"sfmc-qq-bridge\",\n },\n },\n });\n }\n helloDone = true;\n resolve();\n return;\n }\n\n if (payload.op === OP_HEARTBEAT_ACK) return;\n\n if (payload.op === OP_RECONNECT) {\n log.warn(\"\u5B98\u65B9 Gateway \u8981\u6C42\u91CD\u8FDE (op=7)\");\n try {\n sock.close();\n } catch {\n /* ignore */\n }\n return;\n }\n\n if (payload.op === OP_INVALID_SESSION) {\n log.warn(\"\u5B98\u65B9 Gateway Invalid Session\uFF0C\u6E05\u7A7A session \u540E\u91CD\u8FDE\");\n sessionId = null;\n lastSeq = null;\n try {\n sock.close();\n } catch {\n /* ignore */\n }\n return;\n }\n\n if (payload.op === OP_DISPATCH) {\n await handleDispatch(payload.t, payload.d, payload.s ?? null);\n }\n })();\n });\n\n sock.on(\"close\", () => {\n clearHeartbeat();\n ws = null;\n if (!helloDone) {\n fail(new Error(\"Gateway \u5728 Hello \u524D\u5173\u95ED\"));\n return;\n }\n if (!stopped) scheduleReconnect();\n });\n\n sock.on(\"error\", (err) => {\n log.error(`\u5B98\u65B9 Gateway \u9519\u8BEF: ${err.message}`);\n if (!helloDone) fail(err);\n });\n });\n } catch (e) {\n log.error(`\u5B98\u65B9 Gateway \u8FDE\u63A5\u5931\u8D25: ${(e as Error).message}`);\n scheduleReconnect();\n }\n };\n\n await connect();\n\n return {\n stop: () => {\n stopped = true;\n clearHeartbeat();\n if (reconnectTimer) clearTimeout(reconnectTimer);\n try {\n ws?.close();\n } catch {\n /* ignore */\n }\n ws = null;\n },\n };\n}\n\n/** \u4F9B status \u547D\u4EE4\u8BFB\u53D6\uFF08\u5F53\u524D\u8FDB\u7A0B\u5185\u65E0\u5168\u5C40 self_id \u5BFC\u51FA\u65F6\u7531 index \u6301\u6709\uFF09 */\nexport type OfficialGatewayHandle = Awaited<ReturnType<typeof startOfficialGateway>>;\n\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }