@sfmc-bds/qq-bridge 0.1.1-beta.4 → 0.2.0-beta.4
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.
- package/README.md +14 -3
- package/dist/commands/db-api.js +89 -0
- package/dist/commands/db-api.js.map +7 -0
- package/dist/commands/handlers.js +660 -0
- package/dist/commands/handlers.js.map +7 -0
- package/dist/commands/index.js +55 -0
- package/dist/commands/index.js.map +7 -0
- package/dist/commands/interaction-router.js +149 -0
- package/dist/commands/interaction-router.js.map +7 -0
- package/dist/commands/join-settings-ui.js +108 -0
- package/dist/commands/join-settings-ui.js.map +7 -0
- package/dist/commands/join-settings-ui.test.js +43 -0
- package/dist/commands/join-settings-ui.test.js.map +7 -0
- package/dist/commands/menu-format.js +45 -0
- package/dist/commands/menu-format.js.map +7 -0
- package/dist/commands/menu-panel-sync.js +130 -0
- package/dist/commands/menu-panel-sync.js.map +7 -0
- package/dist/commands/menu-panel-sync.test.js +27 -0
- package/dist/commands/menu-panel-sync.test.js.map +7 -0
- package/dist/commands/pending.js +33 -0
- package/dist/commands/pending.js.map +7 -0
- package/dist/commands/registry.js +40 -0
- package/dist/commands/registry.js.map +7 -0
- package/dist/commands/render.js +46 -0
- package/dist/commands/render.js.map +7 -0
- package/dist/commands/render.test.js +44 -0
- package/dist/commands/render.test.js.map +7 -0
- package/dist/commands/reply-llbot.js +63 -0
- package/dist/commands/reply-llbot.js.map +7 -0
- package/dist/commands/reply-official.js +66 -0
- package/dist/commands/reply-official.js.map +7 -0
- package/dist/commands/router.js +53 -0
- package/dist/commands/router.js.map +7 -0
- package/dist/commands/router.test.js +92 -0
- package/dist/commands/router.test.js.map +7 -0
- package/dist/commands/types.js +1 -0
- package/dist/commands/types.js.map +7 -0
- package/dist/config.js +12 -2
- package/dist/config.js.map +2 -2
- package/dist/console.js +40 -20
- package/dist/console.js.map +2 -2
- package/dist/index.js +117 -20
- package/dist/index.js.map +2 -2
- package/dist/llbot-ws-api.js +90 -0
- package/dist/llbot-ws-api.js.map +7 -0
- package/dist/llbot-ws-api.test.js +32 -0
- package/dist/llbot-ws-api.test.js.map +7 -0
- package/dist/official/events.js +136 -0
- package/dist/official/events.js.map +7 -0
- package/dist/official/events.test.js +123 -0
- package/dist/official/events.test.js.map +7 -0
- package/dist/official/gateway.js +226 -0
- package/dist/official/gateway.js.map +7 -0
- package/dist/onebot.js +23 -5
- package/dist/onebot.js.map +2 -2
- package/dist/project-root.js +2 -2
- package/dist/project-root.js.map +2 -2
- package/dist/runtime-status.js +45 -0
- package/dist/runtime-status.js.map +7 -0
- package/dist/ws-server.js +4 -0
- package/dist/ws-server.js.map +2 -2
- package/package.json +6 -5
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/commands/router.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * commands/router.ts \u2014 \u89E3\u6790\u89E6\u53D1\u8BCD / \u7F16\u53F7\u4F1A\u8BDD\uFF0C\u6267\u884C handler\n */\n\nimport { log } from \"../log.js\";\nimport type { PendingChoiceStore } from \"./pending.js\";\nimport { normalizeTrigger, type CommandRegistry } from \"./registry.js\";\nimport type {\n CommandContext,\n CommandResult,\n InboundMessage,\n ReplyPort,\n ReplyTarget,\n} from \"./types.js\";\n\nexport type CommandRouterOptions = {\n registry: CommandRegistry;\n pending: PendingChoiceStore;\n reply: ReplyPort;\n startedAt: number;\n runtimeInfo: CommandContext[\"runtimeInfo\"];\n};\n\n/**\n * \u82E5\u6587\u672C\u4E3A\u7EAF\u6570\u5B57 1\u20139\uFF0C\u5C1D\u8BD5 pending\uFF1B\u5426\u5219\u6309\u89E6\u53D1\u8BCD\u89E3\u6790\u3002\n * \u547D\u4E2D\u5E76\u56DE\u590D\u6210\u529F\u8FD4\u56DE true\uFF08\u8C03\u7528\u65B9\u5E94\u62E6\u622A\u8F6C\u53D1\uFF09\u3002\n */\nexport class CommandRouter {\n private readonly opts: CommandRouterOptions;\n\n constructor(opts: CommandRouterOptions) {\n this.opts = opts;\n }\n\n /** \u4F9B menu/panel sync \u8BFB\u53D6\u6743\u5A01\u547D\u4EE4\u8868 */\n get registry(): CommandRegistry {\n return this.opts.registry;\n }\n\n async handle(inbound: InboundMessage): Promise<boolean> {\n const raw = String(inbound.text ?? \"\").trim();\n if (!raw) return false;\n\n // \u7F16\u53F7\u4F1A\u8BDD\uFF1A\u7528\u7F13\u5B58\u7684\u5B8C\u6574\u547D\u4EE4\u66FF\u6362\u6B63\u6587\uFF08\u542B\u53C2\u6570\uFF0C\u5982\u300C\u914D\u7F6E \u767D\u540D\u5355 \u5173\u300D\uFF09\n let effective: InboundMessage = inbound;\n const digit = /^[1-9]$/.exec(raw);\n if (digit) {\n const chosen = this.opts.pending.take(inbound.backend, inbound.groupId, inbound.userId, Number(digit[0]));\n if (!chosen) return false;\n effective = { ...inbound, text: chosen };\n }\n\n const resolved = this.opts.registry.resolve(effective.text);\n if (!resolved) return false;\n\n const ctx: CommandContext = {\n inbound: effective,\n startedAt: this.opts.startedAt,\n runtimeInfo: this.opts.runtimeInfo,\n };\n const result: CommandResult = await resolved.handler(ctx);\n\n // llbot\uFF1A\u6709\u6309\u94AE\u65F6\u5199\u5165\u7F16\u53F7\u4F1A\u8BDD\n if (effective.backend === \"llbot\" && result.buttons && result.buttons.length > 0) {\n const choices = new Map<number, string>();\n result.buttons.forEach((b, i) => {\n choices.set(i + 1, b.command);\n });\n this.opts.pending.set(effective.backend, effective.groupId, effective.userId, choices);\n }\n\n const target: ReplyTarget = { groupId: effective.groupId };\n if (effective.msgId) target.msgId = effective.msgId;\n\n try {\n await this.opts.reply.send(target, result, effective);\n log.info(`cmd=${resolved.name} handled user=${effective.userId} group=${effective.groupId}`);\n return true;\n } catch (e) {\n log.warn(`cmd=${resolved.name} \u56DE\u590D\u5931\u8D25: ${(e as Error).message}`);\n return true; // \u4ECD\u89C6\u4E3A\u5DF2\u5904\u7406\uFF0C\u907F\u514D\u518D\u8F6C\u53BB MC\n }\n }\n}\n\nexport { normalizeTrigger };\n"],
|
|
5
|
+
"mappings": "AAIA,SAAS,WAAW;AAEpB,SAAS,wBAA8C;AAqBhD,MAAM,cAAc;AAAA,EACR;AAAA,EAEjB,YAAY,MAA4B;AACtC,SAAK,OAAO;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,WAA4B;AAC9B,WAAO,KAAK,KAAK;AAAA,EACnB;AAAA,EAEA,MAAM,OAAO,SAA2C;AACtD,UAAM,MAAM,OAAO,QAAQ,QAAQ,EAAE,EAAE,KAAK;AAC5C,QAAI,CAAC,IAAK,QAAO;AAGjB,QAAI,YAA4B;AAChC,UAAM,QAAQ,UAAU,KAAK,GAAG;AAChC,QAAI,OAAO;AACT,YAAM,SAAS,KAAK,KAAK,QAAQ,KAAK,QAAQ,SAAS,QAAQ,SAAS,QAAQ,QAAQ,OAAO,MAAM,CAAC,CAAC,CAAC;AACxG,UAAI,CAAC,OAAQ,QAAO;AACpB,kBAAY,EAAE,GAAG,SAAS,MAAM,OAAO;AAAA,IACzC;AAEA,UAAM,WAAW,KAAK,KAAK,SAAS,QAAQ,UAAU,IAAI;AAC1D,QAAI,CAAC,SAAU,QAAO;AAEtB,UAAM,MAAsB;AAAA,MAC1B,SAAS;AAAA,MACT,WAAW,KAAK,KAAK;AAAA,MACrB,aAAa,KAAK,KAAK;AAAA,IACzB;AACA,UAAM,SAAwB,MAAM,SAAS,QAAQ,GAAG;AAGxD,QAAI,UAAU,YAAY,WAAW,OAAO,WAAW,OAAO,QAAQ,SAAS,GAAG;AAChF,YAAM,UAAU,oBAAI,IAAoB;AACxC,aAAO,QAAQ,QAAQ,CAAC,GAAG,MAAM;AAC/B,gBAAQ,IAAI,IAAI,GAAG,EAAE,OAAO;AAAA,MAC9B,CAAC;AACD,WAAK,KAAK,QAAQ,IAAI,UAAU,SAAS,UAAU,SAAS,UAAU,QAAQ,OAAO;AAAA,IACvF;AAEA,UAAM,SAAsB,EAAE,SAAS,UAAU,QAAQ;AACzD,QAAI,UAAU,MAAO,QAAO,QAAQ,UAAU;AAE9C,QAAI;AACF,YAAM,KAAK,KAAK,MAAM,KAAK,QAAQ,QAAQ,SAAS;AACpD,UAAI,KAAK,OAAO,SAAS,IAAI,iBAAiB,UAAU,MAAM,UAAU,UAAU,OAAO,EAAE;AAC3F,aAAO;AAAA,IACT,SAAS,GAAG;AACV,UAAI,KAAK,OAAO,SAAS,IAAI,8BAAW,EAAY,OAAO,EAAE;AAC7D,aAAO;AAAA,IACT;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { registerBuiltinCommands } from "./handlers.js";
|
|
4
|
+
import { PendingChoiceStore } from "./pending.js";
|
|
5
|
+
import { CommandRegistry } from "./registry.js";
|
|
6
|
+
import { CommandRouter } from "./router.js";
|
|
7
|
+
function makeReplyCapture() {
|
|
8
|
+
const sent = [];
|
|
9
|
+
return {
|
|
10
|
+
sent,
|
|
11
|
+
port: {
|
|
12
|
+
async send(_t, result) {
|
|
13
|
+
sent.push(result);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function makeRouter(reply) {
|
|
19
|
+
const registry = new CommandRegistry();
|
|
20
|
+
registerBuiltinCommands(registry);
|
|
21
|
+
return new CommandRouter({
|
|
22
|
+
registry,
|
|
23
|
+
pending: new PendingChoiceStore(6e4),
|
|
24
|
+
reply,
|
|
25
|
+
startedAt: Date.now() - 5e3,
|
|
26
|
+
runtimeInfo: { sandbox: false, appIdHint: "1905\u20266765" }
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
test("router \u547D\u4E2D ping", async () => {
|
|
30
|
+
const { port, sent } = makeReplyCapture();
|
|
31
|
+
const router = makeRouter(port);
|
|
32
|
+
const inbound = {
|
|
33
|
+
backend: "official",
|
|
34
|
+
groupId: "G",
|
|
35
|
+
userId: "U",
|
|
36
|
+
userName: "Alice",
|
|
37
|
+
text: "/ping",
|
|
38
|
+
msgId: "m1"
|
|
39
|
+
};
|
|
40
|
+
assert.equal(await router.handle(inbound), true);
|
|
41
|
+
assert.equal(sent.length, 1);
|
|
42
|
+
assert.match(sent[0].text, /^pong/);
|
|
43
|
+
});
|
|
44
|
+
test("registry \u9996\u8BCD\u5339\u914D\u300C\u7533\u8BF7\u5165\u670D Steve\u300D", () => {
|
|
45
|
+
const registry = new CommandRegistry();
|
|
46
|
+
registerBuiltinCommands(registry);
|
|
47
|
+
const cmd = registry.resolve("\u7533\u8BF7\u5165\u670D Steve");
|
|
48
|
+
assert.ok(cmd);
|
|
49
|
+
assert.equal(cmd.name, "join");
|
|
50
|
+
assert.equal(registry.resolve("\u8E22\u4EBA Alex")?.name, "kick");
|
|
51
|
+
});
|
|
52
|
+
test("router \u672A\u547D\u4E2D\u900F\u4F20", async () => {
|
|
53
|
+
const { port, sent } = makeReplyCapture();
|
|
54
|
+
const router = makeRouter(port);
|
|
55
|
+
assert.equal(
|
|
56
|
+
await router.handle({
|
|
57
|
+
backend: "official",
|
|
58
|
+
groupId: "G",
|
|
59
|
+
userId: "U",
|
|
60
|
+
userName: "A",
|
|
61
|
+
text: "hello world"
|
|
62
|
+
}),
|
|
63
|
+
false
|
|
64
|
+
);
|
|
65
|
+
assert.equal(sent.length, 0);
|
|
66
|
+
});
|
|
67
|
+
test("router llbot \u7F16\u53F7\u4F1A\u8BDD", async () => {
|
|
68
|
+
const { port, sent } = makeReplyCapture();
|
|
69
|
+
const router = makeRouter(port);
|
|
70
|
+
const base = {
|
|
71
|
+
backend: "llbot",
|
|
72
|
+
groupId: "123",
|
|
73
|
+
userId: "99",
|
|
74
|
+
userName: "Bob",
|
|
75
|
+
text: "\u83DC\u5355"
|
|
76
|
+
};
|
|
77
|
+
assert.equal(await router.handle(base), true);
|
|
78
|
+
assert.equal(sent.length, 1);
|
|
79
|
+
assert.ok(sent[0].buttons && sent[0].buttons.length >= 2);
|
|
80
|
+
assert.equal(
|
|
81
|
+
await router.handle({ ...base, text: "1" }),
|
|
82
|
+
true
|
|
83
|
+
);
|
|
84
|
+
assert.equal(sent.length, 2);
|
|
85
|
+
assert.match(sent[1].text, /^pong/);
|
|
86
|
+
});
|
|
87
|
+
test("registry normalizeTrigger", async () => {
|
|
88
|
+
const { normalizeTrigger } = await import("./registry.js");
|
|
89
|
+
assert.equal(normalizeTrigger(" /Ping "), "ping");
|
|
90
|
+
assert.equal(normalizeTrigger("\u83DC\u5355"), "\u83DC\u5355");
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=router.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/commands/router.test.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * commands/router.test.ts\n */\nimport assert from \"node:assert/strict\";\nimport test from \"node:test\";\nimport { registerBuiltinCommands } from \"./handlers.js\";\nimport { PendingChoiceStore } from \"./pending.js\";\nimport { CommandRegistry } from \"./registry.js\";\nimport { CommandRouter } from \"./router.js\";\nimport type { CommandResult, InboundMessage, ReplyPort } from \"./types.js\";\n\nfunction makeReplyCapture(): { port: ReplyPort; sent: CommandResult[] } {\n const sent: CommandResult[] = [];\n return {\n sent,\n port: {\n async send(_t, result) {\n sent.push(result);\n },\n },\n };\n}\n\nfunction makeRouter(reply: ReplyPort): CommandRouter {\n const registry = new CommandRegistry();\n registerBuiltinCommands(registry);\n return new CommandRouter({\n registry,\n pending: new PendingChoiceStore(60_000),\n reply,\n startedAt: Date.now() - 5000,\n runtimeInfo: { sandbox: false, appIdHint: \"1905\u20266765\" },\n });\n}\n\ntest(\"router \u547D\u4E2D ping\", async () => {\n const { port, sent } = makeReplyCapture();\n const router = makeRouter(port);\n const inbound: InboundMessage = {\n backend: \"official\",\n groupId: \"G\",\n userId: \"U\",\n userName: \"Alice\",\n text: \"/ping\",\n msgId: \"m1\",\n };\n assert.equal(await router.handle(inbound), true);\n assert.equal(sent.length, 1);\n assert.match(sent[0]!.text, /^pong/);\n});\n\ntest(\"registry \u9996\u8BCD\u5339\u914D\u300C\u7533\u8BF7\u5165\u670D Steve\u300D\", () => {\n const registry = new CommandRegistry();\n registerBuiltinCommands(registry);\n const cmd = registry.resolve(\"\u7533\u8BF7\u5165\u670D Steve\");\n assert.ok(cmd);\n assert.equal(cmd!.name, \"join\");\n assert.equal(registry.resolve(\"\u8E22\u4EBA Alex\")?.name, \"kick\");\n});\n\ntest(\"router \u672A\u547D\u4E2D\u900F\u4F20\", async () => {\n const { port, sent } = makeReplyCapture();\n const router = makeRouter(port);\n assert.equal(\n await router.handle({\n backend: \"official\",\n groupId: \"G\",\n userId: \"U\",\n userName: \"A\",\n text: \"hello world\",\n }),\n false\n );\n assert.equal(sent.length, 0);\n});\n\ntest(\"router llbot \u7F16\u53F7\u4F1A\u8BDD\", async () => {\n const { port, sent } = makeReplyCapture();\n const router = makeRouter(port);\n const base: InboundMessage = {\n backend: \"llbot\",\n groupId: \"123\",\n userId: \"99\",\n userName: \"Bob\",\n text: \"\u83DC\u5355\",\n };\n assert.equal(await router.handle(base), true);\n assert.equal(sent.length, 1);\n assert.ok(sent[0]!.buttons && sent[0]!.buttons.length >= 2);\n\n assert.equal(\n await router.handle({ ...base, text: \"1\" }),\n true\n );\n assert.equal(sent.length, 2);\n // \u7B2C 1 \u4E2A\u6309\u94AE\u5BF9\u5E94 ping\uFF08\u4E3B\u83DC\u5355\u5DF2\u53BB\u6389\u81EA\u6307 menu\uFF09\n assert.match(sent[1]!.text, /^pong/);\n});\n\ntest(\"registry normalizeTrigger\", async () => {\n const { normalizeTrigger } = await import(\"./registry.js\");\n assert.equal(normalizeTrigger(\" /Ping \"), \"ping\");\n assert.equal(normalizeTrigger(\"\u83DC\u5355\"), \"\u83DC\u5355\");\n});\n"],
|
|
5
|
+
"mappings": "AAGA,OAAO,YAAY;AACnB,OAAO,UAAU;AACjB,SAAS,+BAA+B;AACxC,SAAS,0BAA0B;AACnC,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAG9B,SAAS,mBAA+D;AACtE,QAAM,OAAwB,CAAC;AAC/B,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AAAA,MACJ,MAAM,KAAK,IAAI,QAAQ;AACrB,aAAK,KAAK,MAAM;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,WAAW,OAAiC;AACnD,QAAM,WAAW,IAAI,gBAAgB;AACrC,0BAAwB,QAAQ;AAChC,SAAO,IAAI,cAAc;AAAA,IACvB;AAAA,IACA,SAAS,IAAI,mBAAmB,GAAM;AAAA,IACtC;AAAA,IACA,WAAW,KAAK,IAAI,IAAI;AAAA,IACxB,aAAa,EAAE,SAAS,OAAO,WAAW,iBAAY;AAAA,EACxD,CAAC;AACH;AAEA,KAAK,4BAAkB,YAAY;AACjC,QAAM,EAAE,MAAM,KAAK,IAAI,iBAAiB;AACxC,QAAM,SAAS,WAAW,IAAI;AAC9B,QAAM,UAA0B;AAAA,IAC9B,SAAS;AAAA,IACT,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,MAAM;AAAA,IACN,OAAO;AAAA,EACT;AACA,SAAO,MAAM,MAAM,OAAO,OAAO,OAAO,GAAG,IAAI;AAC/C,SAAO,MAAM,KAAK,QAAQ,CAAC;AAC3B,SAAO,MAAM,KAAK,CAAC,EAAG,MAAM,OAAO;AACrC,CAAC;AAED,KAAK,+EAA6B,MAAM;AACtC,QAAM,WAAW,IAAI,gBAAgB;AACrC,0BAAwB,QAAQ;AAChC,QAAM,MAAM,SAAS,QAAQ,gCAAY;AACzC,SAAO,GAAG,GAAG;AACb,SAAO,MAAM,IAAK,MAAM,MAAM;AAC9B,SAAO,MAAM,SAAS,QAAQ,mBAAS,GAAG,MAAM,MAAM;AACxD,CAAC;AAED,KAAK,yCAAgB,YAAY;AAC/B,QAAM,EAAE,MAAM,KAAK,IAAI,iBAAiB;AACxC,QAAM,SAAS,WAAW,IAAI;AAC9B,SAAO;AAAA,IACL,MAAM,OAAO,OAAO;AAAA,MAClB,SAAS;AAAA,MACT,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,MAAM;AAAA,IACR,CAAC;AAAA,IACD;AAAA,EACF;AACA,SAAO,MAAM,KAAK,QAAQ,CAAC;AAC7B,CAAC;AAED,KAAK,yCAAqB,YAAY;AACpC,QAAM,EAAE,MAAM,KAAK,IAAI,iBAAiB;AACxC,QAAM,SAAS,WAAW,IAAI;AAC9B,QAAM,OAAuB;AAAA,IAC3B,SAAS;AAAA,IACT,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,MAAM;AAAA,EACR;AACA,SAAO,MAAM,MAAM,OAAO,OAAO,IAAI,GAAG,IAAI;AAC5C,SAAO,MAAM,KAAK,QAAQ,CAAC;AAC3B,SAAO,GAAG,KAAK,CAAC,EAAG,WAAW,KAAK,CAAC,EAAG,QAAQ,UAAU,CAAC;AAE1D,SAAO;AAAA,IACL,MAAM,OAAO,OAAO,EAAE,GAAG,MAAM,MAAM,IAAI,CAAC;AAAA,IAC1C;AAAA,EACF;AACA,SAAO,MAAM,KAAK,QAAQ,CAAC;AAE3B,SAAO,MAAM,KAAK,CAAC,EAAG,MAAM,OAAO;AACrC,CAAC;AAED,KAAK,6BAA6B,YAAY;AAC5C,QAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,eAAe;AACzD,SAAO,MAAM,iBAAiB,SAAS,GAAG,MAAM;AAChD,SAAO,MAAM,iBAAiB,cAAI,GAAG,cAAI;AAC3C,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.js.map
|
package/dist/config.js
CHANGED
|
@@ -11,15 +11,25 @@ const CFG_PATH = configPath(ROOT_DIR, "qq_config.json");
|
|
|
11
11
|
function applyDefaults(raw) {
|
|
12
12
|
const merged = { ...DEFAULT_QQ_CONFIG, ...raw };
|
|
13
13
|
const stripped = stripConfigMeta(merged);
|
|
14
|
+
const backendRaw = String(stripped.qq_backend ?? DEFAULT_QQ_CONFIG.qq_backend ?? "official");
|
|
15
|
+
const qq_backend = backendRaw === "llbot" ? "llbot" : "official";
|
|
14
16
|
return {
|
|
17
|
+
...stripped,
|
|
15
18
|
qq_enabled: stripped.qq_enabled !== false,
|
|
19
|
+
qq_backend,
|
|
20
|
+
qq_app_id: String(stripped.qq_app_id ?? DEFAULT_QQ_CONFIG.qq_app_id ?? ""),
|
|
21
|
+
qq_app_secret: String(stripped.qq_app_secret ?? DEFAULT_QQ_CONFIG.qq_app_secret ?? ""),
|
|
22
|
+
qq_sandbox: stripped.qq_sandbox === true,
|
|
23
|
+
qq_group_openid: String(stripped.qq_group_openid ?? DEFAULT_QQ_CONFIG.qq_group_openid ?? ""),
|
|
24
|
+
qq_group_panel_id: String(stripped.qq_group_panel_id ?? DEFAULT_QQ_CONFIG.qq_group_panel_id ?? ""),
|
|
25
|
+
qq_sync_menu_panel: stripped.qq_sync_menu_panel !== false,
|
|
26
|
+
qq_admin_openids: Array.isArray(stripped.qq_admin_openids) ? stripped.qq_admin_openids.map((x) => String(x).trim()).filter(Boolean) : [],
|
|
16
27
|
qq_ws_port: parseInt(String(stripped.qq_ws_port ?? DEFAULT_QQ_CONFIG.qq_ws_port ?? 3002), 10),
|
|
17
28
|
qq_group_id: String(stripped.qq_group_id ?? DEFAULT_QQ_CONFIG.qq_group_id ?? "0"),
|
|
18
29
|
bridge_channel_id: String(stripped.bridge_channel_id ?? DEFAULT_QQ_CONFIG.bridge_channel_id ?? ""),
|
|
19
30
|
db_host: String(stripped.db_host ?? "127.0.0.1"),
|
|
20
31
|
db_port: parseInt(String(stripped.db_port ?? "3001"), 10),
|
|
21
|
-
mctoqq_prefix: String(stripped.mctoqq_prefix ?? DEFAULT_QQ_CONFIG.mctoqq_prefix ?? "[MC]")
|
|
22
|
-
...stripped
|
|
32
|
+
mctoqq_prefix: String(stripped.mctoqq_prefix ?? DEFAULT_QQ_CONFIG.mctoqq_prefix ?? "[MC]")
|
|
23
33
|
};
|
|
24
34
|
}
|
|
25
35
|
function readFromDisk() {
|
package/dist/config.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/config.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * config.ts \u2014 \u52A0\u8F7D configs/qq_config.json\n *\n * \u884C\u4E3A\u4E0E\u65E7 index.js \u4FDD\u6301\u5B8C\u5168\u4E00\u81F4:\n * - \u6587\u4EF6\u4E0D\u5B58\u5728\u6216\u89E3\u6790\u5931\u8D25: \u8FDB\u7A0B\u9000\u51FA (\u65E7\u7248 process.exit(1))\n * - \u5B57\u6BB5\u7F3A\u5931: \u8D70\u9ED8\u8BA4\u503C\n * - reload \u65F6\u4EC5\u8986\u76D6\u539F\u5BF9\u8C61 (mutate), \u4FDD\u7559\u8FD0\u884C\u65F6\u5F15\u7528\u7684\u540C\u4E00\u4EFD\u5BF9\u8C61\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:env SFMC_ROOT > project-root \u4E0A\u6EAF\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 return {\n qq_enabled: stripped.qq_enabled !== false,\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
|
|
5
|
-
"mappings": "AASA;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,SAAO;AAAA,IACL,YAAY,SAAS,eAAe;AAAA,IACpC,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,
|
|
4
|
+
"sourcesContent": ["/**\n * config.ts \u2014 \u52A0\u8F7D configs/qq_config.json\n *\n * \u884C\u4E3A\u4E0E\u65E7 index.js \u4FDD\u6301\u5B8C\u5168\u4E00\u81F4:\n * - \u6587\u4EF6\u4E0D\u5B58\u5728\u6216\u89E3\u6790\u5931\u8D25: \u8FDB\u7A0B\u9000\u51FA (\u65E7\u7248 process.exit(1))\n * - \u5B57\u6BB5\u7F3A\u5931: \u8D70\u9ED8\u8BA4\u503C\n * - reload \u65F6\u4EC5\u8986\u76D6\u539F\u5BF9\u8C61 (mutate), \u4FDD\u7559\u8FD0\u884C\u65F6\u5F15\u7528\u7684\u540C\u4E00\u4EFD\u5BF9\u8C61\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:env SFMC_ROOT > project-root \u4E0A\u6EAF\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/** \u8FDB\u7A0B\u542F\u52A8\u65F6\u52A0\u8F7D\u4E00\u6B21\u3002\u5931\u8D25\u76F4\u63A5\u9000\u51FA,\u4E0E\u65E7\u5B9E\u73B0\u4E00\u81F4\u3002 */\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\u8BFB\u53D6\u914D\u7F6E\u6587\u4EF6,\u5408\u5E76\u5230\u4F20\u5165\u5BF9\u8C61\u4E0A (mutate)\u3002\n * \u65E7\u5B9E\u73B0\u662F Object.assign(cfg, newCfg),\u4FDD\u7559\u8FD0\u884C\u65F6\u5BF9\u539F cfg \u5BF9\u8C61\u7684\u5F15\u7528 \u2014 \u540C\u6837\u884C\u4E3A\u3002\n */\nexport function reloadInto(cfg: QQBridgeConfig): void {\n try {\n const fresh = readFromDisk();\n Object.assign(cfg, fresh);\n } catch (e) {\n // reload \u5931\u8D25\u4E0D\u629B,\u65E7\u5B9E\u73B0\u4E5F\u662F\u53EA log\n log.error(`\u91CD\u8F7D\u914D\u7F6E\u5931\u8D25: ${(e as Error).message}`);\n }\n}\n"],
|
|
5
|
+
"mappings": "AASA;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;AAGO,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;AAMO,SAAS,WAAW,KAA2B;AACpD,MAAI;AACF,UAAM,QAAQ,aAAa;AAC3B,WAAO,OAAO,KAAK,KAAK;AAAA,EAC1B,SAAS,GAAG;AAEV,QAAI,MAAM,yCAAY,EAAY,OAAO,EAAE;AAAA,EAC7C;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/console.js
CHANGED
|
@@ -1,48 +1,69 @@
|
|
|
1
1
|
import { createInterface } from "node:readline";
|
|
2
2
|
import { log } from "./log.js";
|
|
3
3
|
import { reloadInto } from "./config.js";
|
|
4
|
-
function showHelp() {
|
|
4
|
+
function showHelp(backend) {
|
|
5
5
|
console.log("\u53EF\u7528\u547D\u4EE4:");
|
|
6
|
-
console.log(" help
|
|
7
|
-
console.log(" reload
|
|
8
|
-
console.log(" status
|
|
9
|
-
|
|
6
|
+
console.log(" help \u2014 \u663E\u793A\u672C\u5E2E\u52A9");
|
|
7
|
+
console.log(" reload \u2014 \u91CD\u65B0\u8BFB\u53D6 qq_config.json");
|
|
8
|
+
console.log(" status \u2014 \u663E\u793A\u5F53\u524D\u72B6\u6001");
|
|
9
|
+
if (backend === "official") {
|
|
10
|
+
console.log(" sync-menu \u2014 \u91CD\u63A8\u5B98\u65B9\u81EA\u5B9A\u4E49\u83DC\u5355 / \u7FA4\u6307\u4EE4\u9762\u677F");
|
|
11
|
+
}
|
|
12
|
+
console.log(" stop \u2014 \u505C\u6B62\u670D\u52A1");
|
|
10
13
|
}
|
|
11
14
|
function showStatus(opts) {
|
|
12
|
-
const { config: cfg, initialEnabled, wsPort, botSelfIdRef } = opts;
|
|
15
|
+
const { config: cfg, initialEnabled, wsPort, botSelfIdRef, backend } = opts;
|
|
13
16
|
console.log("=== QQBridge \u72B6\u6001 ===");
|
|
14
17
|
console.log(` \u542F\u7528: ${initialEnabled}`);
|
|
15
|
-
console.log(`
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
console.log(` \u540E\u7AEF: ${backend}`);
|
|
19
|
+
if (backend === "official") {
|
|
20
|
+
console.log(` \u6C99\u7BB1: ${cfg.qq_sandbox}`);
|
|
21
|
+
console.log(` AppID: ${cfg.qq_app_id || "\u672A\u914D\u7F6E"}`);
|
|
22
|
+
console.log(` \u7FA4 openid: ${cfg.qq_group_openid || "\u672A\u914D\u7F6E"}`);
|
|
23
|
+
console.log(` \u9762\u677F id: ${cfg.qq_group_panel_id || "\u672A\u914D\u7F6E"}`);
|
|
24
|
+
console.log(` sync \u83DC\u5355: ${cfg.qq_sync_menu_panel !== false}`);
|
|
25
|
+
} else {
|
|
26
|
+
console.log(` WS \u7AEF\u53E3: ${wsPort}`);
|
|
27
|
+
console.log(` QQ \u7FA4\u53F7: ${cfg.qq_group_id || "\u672A\u914D\u7F6E"}`);
|
|
28
|
+
console.log(` Bot self_id: ${botSelfIdRef.value || "\u672A\u6355\u83B7"}`);
|
|
29
|
+
}
|
|
18
30
|
console.log(` Channel: ${cfg.bridge_channel_id || "\u672A\u914D\u7F6E"}`);
|
|
19
31
|
console.log(` db-server: ${cfg.db_host}:${cfg.db_port}`);
|
|
20
|
-
console.log(` Bot self_id: ${botSelfIdRef.value || "\u672A\u6355\u83B7"}`);
|
|
21
32
|
console.log(` MC\u2192QQ \u524D\u7F00: ${cfg.mctoqq_prefix}`);
|
|
33
|
+
console.log(` MC\u2192QQ \u51FA\u7AD9: (\u7531 db-server \u6309 qq_backend \u76F4\u8FDE)`);
|
|
22
34
|
}
|
|
23
35
|
function handleCommand(line, opts, rl) {
|
|
24
36
|
const cmd = line.trim().toLowerCase();
|
|
25
37
|
switch (cmd) {
|
|
26
38
|
case "help":
|
|
27
|
-
showHelp();
|
|
39
|
+
showHelp(opts.backend);
|
|
28
40
|
return false;
|
|
29
41
|
case "reload": {
|
|
30
|
-
const before = {
|
|
31
|
-
qq_group_id: opts.config.qq_group_id,
|
|
32
|
-
bridge_channel_id: opts.config.bridge_channel_id,
|
|
33
|
-
db_host: opts.config.db_host,
|
|
34
|
-
db_port: opts.config.db_port
|
|
35
|
-
};
|
|
36
42
|
reloadInto(opts.config);
|
|
37
43
|
log.info(
|
|
38
|
-
`\u914D\u7F6E\u5DF2\u91CD\u8F7D (
|
|
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})`
|
|
39
45
|
);
|
|
40
|
-
|
|
46
|
+
log.warn("reload \u53EA\u66F4\u65B0\u5185\u5B58\u914D\u7F6E\u5F15\u7528\uFF1B\u5207\u6362 backend / AppID \u9700\u91CD\u542F\u8FDB\u7A0B");
|
|
41
47
|
return false;
|
|
42
48
|
}
|
|
43
49
|
case "status":
|
|
44
50
|
showStatus(opts);
|
|
45
51
|
return false;
|
|
52
|
+
case "sync-menu": {
|
|
53
|
+
if (opts.backend !== "official") {
|
|
54
|
+
log.info("sync-menu \u4EC5 official \u540E\u7AEF\u53EF\u7528");
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
if (!opts.onSyncMenu) {
|
|
58
|
+
log.warn("sync-menu \u672A\u63A5\u7EBF");
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
void opts.onSyncMenu().then(
|
|
62
|
+
() => log.info("sync-menu \u5B8C\u6210"),
|
|
63
|
+
(e) => log.warn(`sync-menu \u5931\u8D25: ${e.message}`)
|
|
64
|
+
);
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
46
67
|
case "stop":
|
|
47
68
|
case "exit":
|
|
48
69
|
case "quit":
|
|
@@ -50,7 +71,6 @@ function handleCommand(line, opts, rl) {
|
|
|
50
71
|
rl.close();
|
|
51
72
|
process.exit(0);
|
|
52
73
|
return true;
|
|
53
|
-
// 不可达,但满足 noImplicitReturns
|
|
54
74
|
case "":
|
|
55
75
|
return false;
|
|
56
76
|
default:
|
package/dist/console.js.map
CHANGED
|
@@ -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
|
|
5
|
-
"mappings": "
|
|
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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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,37 +1,134 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createLlbotCommandRouter, createOfficialCommandRouter } from "./commands/index.js";
|
|
3
|
+
import { createInteractionRouter } from "./commands/interaction-router.js";
|
|
4
|
+
import { persistPanelIdToConfig, syncMenuAndPanel } from "./commands/menu-panel-sync.js";
|
|
2
5
|
import { loadInitialConfig } from "./config.js";
|
|
3
6
|
import { OneBotDispatcher } from "./onebot.js";
|
|
4
7
|
import { startWsServer } from "./ws-server.js";
|
|
5
8
|
import { startConsole } from "./console.js";
|
|
9
|
+
import { OfficialAtMessageDispatcher } from "./official/events.js";
|
|
10
|
+
import { startOfficialGateway } from "./official/gateway.js";
|
|
11
|
+
import { installQqRuntimeStatusHooks } from "./runtime-status.js";
|
|
6
12
|
import { log } from "./log.js";
|
|
7
13
|
async function main() {
|
|
8
14
|
const cfg = loadInitialConfig();
|
|
15
|
+
const startedAt = Date.now();
|
|
9
16
|
if (!cfg.qq_enabled) {
|
|
10
17
|
log.info("\u5DF2\u7981\u7528 (qq_enabled = false)");
|
|
11
18
|
process.exit(0);
|
|
12
19
|
}
|
|
13
20
|
const botSelfIdRef = { value: null };
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
const db = {
|
|
22
|
+
host: cfg.db_host,
|
|
23
|
+
port: cfg.db_port,
|
|
24
|
+
channelId: cfg.bridge_channel_id
|
|
25
|
+
};
|
|
26
|
+
if (cfg.qq_backend === "official") {
|
|
27
|
+
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
|
+
process.exit(1);
|
|
20
30
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
const creds = {
|
|
32
|
+
appId: cfg.qq_app_id,
|
|
33
|
+
appSecret: cfg.qq_app_secret,
|
|
34
|
+
sandbox: cfg.qq_sandbox
|
|
35
|
+
};
|
|
36
|
+
const appId = cfg.qq_app_id;
|
|
37
|
+
const commandRouter = createOfficialCommandRouter(
|
|
38
|
+
creds,
|
|
39
|
+
{
|
|
40
|
+
sandbox: cfg.qq_sandbox,
|
|
41
|
+
appIdHint: appId.length > 8 ? `${appId.slice(0, 4)}\u2026${appId.slice(-4)}` : appId,
|
|
42
|
+
dbHost: cfg.db_host,
|
|
43
|
+
dbPort: cfg.db_port,
|
|
44
|
+
groupOpenid: cfg.qq_group_openid,
|
|
45
|
+
adminOpenids: Array.isArray(cfg.qq_admin_openids) ? cfg.qq_admin_openids : [],
|
|
46
|
+
bridgeChannelId: cfg.bridge_channel_id,
|
|
47
|
+
officialCreds: creds
|
|
48
|
+
},
|
|
49
|
+
startedAt
|
|
50
|
+
);
|
|
51
|
+
const runSyncMenu = async () => {
|
|
52
|
+
if (cfg.qq_sync_menu_panel === false) {
|
|
53
|
+
log.info("\u5DF2\u8DF3\u8FC7 menu/panel sync\uFF08qq_sync_menu_panel=false\uFF09");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
await syncMenuAndPanel({
|
|
57
|
+
creds,
|
|
58
|
+
registry: commandRouter.registry,
|
|
59
|
+
groupOpenid: cfg.qq_group_openid,
|
|
60
|
+
panelId: String(cfg.qq_group_panel_id ?? ""),
|
|
61
|
+
persistPanelId: (id) => {
|
|
62
|
+
cfg.qq_group_panel_id = id;
|
|
63
|
+
persistPanelIdToConfig(id);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
const dispatcher = new OfficialAtMessageDispatcher({
|
|
68
|
+
groupOpenid: cfg.qq_group_openid,
|
|
69
|
+
db,
|
|
70
|
+
commandRouter
|
|
71
|
+
});
|
|
72
|
+
const interactionRouter = createInteractionRouter({
|
|
73
|
+
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
|
|
81
|
+
});
|
|
82
|
+
void runSyncMenu().catch((e) => log.warn(`menu/panel sync \u5F02\u5E38: ${e.message}`));
|
|
83
|
+
installQqRuntimeStatusHooks("official");
|
|
84
|
+
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})`
|
|
86
|
+
);
|
|
87
|
+
startConsole({
|
|
88
|
+
config: cfg,
|
|
89
|
+
initialEnabled: cfg.qq_enabled,
|
|
90
|
+
wsPort: 0,
|
|
91
|
+
botSelfIdRef,
|
|
92
|
+
backend: "official",
|
|
93
|
+
onSyncMenu: runSyncMenu
|
|
94
|
+
});
|
|
95
|
+
} else {
|
|
96
|
+
const commandRouter = createLlbotCommandRouter(
|
|
97
|
+
{
|
|
98
|
+
host: cfg.llbot_host || "127.0.0.1",
|
|
99
|
+
port: cfg.llbot_port || 3004,
|
|
100
|
+
token: cfg.llbot_token || "",
|
|
101
|
+
groupId: cfg.qq_group_id || "0"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
dbHost: cfg.db_host,
|
|
105
|
+
dbPort: cfg.db_port,
|
|
106
|
+
adminOpenids: Array.isArray(cfg.qq_admin_openids) ? cfg.qq_admin_openids : [],
|
|
107
|
+
bridgeChannelId: cfg.bridge_channel_id
|
|
108
|
+
},
|
|
109
|
+
startedAt
|
|
110
|
+
);
|
|
111
|
+
const dispatcher = new OneBotDispatcher({
|
|
112
|
+
qqGroupId: cfg.qq_group_id,
|
|
113
|
+
db,
|
|
114
|
+
commandRouter
|
|
115
|
+
});
|
|
116
|
+
Object.defineProperty(botSelfIdRef, "value", {
|
|
117
|
+
get: () => dispatcher.selfId
|
|
118
|
+
});
|
|
119
|
+
await startWsServer({ port: cfg.qq_ws_port, dispatcher });
|
|
120
|
+
installQqRuntimeStatusHooks("llbot");
|
|
121
|
+
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})`
|
|
123
|
+
);
|
|
124
|
+
startConsole({
|
|
125
|
+
config: cfg,
|
|
126
|
+
initialEnabled: cfg.qq_enabled,
|
|
127
|
+
wsPort: cfg.qq_ws_port,
|
|
128
|
+
botSelfIdRef,
|
|
129
|
+
backend: "llbot"
|
|
130
|
+
});
|
|
131
|
+
}
|
|
35
132
|
log.info("\u542F\u52A8\u5B8C\u6210");
|
|
36
133
|
}
|
|
37
134
|
main().catch((e) => {
|
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 * \u7AEF
|
|
5
|
-
"mappings": ";
|
|
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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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { log } from "./log.js";
|
|
3
|
+
const DEFAULT_TIMEOUT_MS = 8e3;
|
|
4
|
+
class LlbotWsApi {
|
|
5
|
+
sock = null;
|
|
6
|
+
pending = /* @__PURE__ */ new Map();
|
|
7
|
+
/** 当前是否有可用的 LLBot reverse-ws 连接 */
|
|
8
|
+
get connected() {
|
|
9
|
+
return !!this.sock && this.sock.readyState === 1;
|
|
10
|
+
}
|
|
11
|
+
attach(sock) {
|
|
12
|
+
if (this.sock && this.sock !== sock) {
|
|
13
|
+
this.rejectAll(new Error("LLBot WebSocket \u88AB\u65B0\u8FDE\u63A5\u66FF\u6362"));
|
|
14
|
+
}
|
|
15
|
+
this.sock = sock;
|
|
16
|
+
log.info("LLBot WS API \u5DF2\u6302\u63A5\uFF08\u53EF\u7528 reverse-ws \u53D1\u6D88\u606F\uFF09");
|
|
17
|
+
}
|
|
18
|
+
detach(sock) {
|
|
19
|
+
if (this.sock !== sock) return;
|
|
20
|
+
this.sock = null;
|
|
21
|
+
this.rejectAll(new Error("LLBot WebSocket \u5DF2\u65AD\u5F00"));
|
|
22
|
+
log.info("LLBot WS API \u5DF2\u5378\u8F7D");
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 若帧是 API 回包(有 echo、无 post_type),消费并返回 true。
|
|
26
|
+
* 事件帧返回 false,交给 dispatcher。
|
|
27
|
+
*/
|
|
28
|
+
tryHandleResponse(raw) {
|
|
29
|
+
if (!raw || typeof raw !== "object") return false;
|
|
30
|
+
const obj = raw;
|
|
31
|
+
if (obj.post_type != null) return false;
|
|
32
|
+
const echo = obj.echo;
|
|
33
|
+
if (echo == null || echo === "") return false;
|
|
34
|
+
const key = String(echo);
|
|
35
|
+
const p = this.pending.get(key);
|
|
36
|
+
if (!p) return false;
|
|
37
|
+
this.pending.delete(key);
|
|
38
|
+
clearTimeout(p.timer);
|
|
39
|
+
p.resolve(obj);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
async call(action, params, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
43
|
+
if (!this.connected || !this.sock) {
|
|
44
|
+
throw new Error("LLBot reverse-ws \u672A\u8FDE\u63A5\uFF0C\u65E0\u6CD5\u8C03\u7528 API");
|
|
45
|
+
}
|
|
46
|
+
const echo = `sfmc_${Date.now().toString(36)}_${randomBytes(4).toString("hex")}`;
|
|
47
|
+
const payload = JSON.stringify({ action, params, echo });
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
const timer = setTimeout(() => {
|
|
50
|
+
this.pending.delete(echo);
|
|
51
|
+
reject(new Error(`LLBot WS API \u8D85\u65F6 (${action})`));
|
|
52
|
+
}, timeoutMs);
|
|
53
|
+
this.pending.set(echo, { resolve, reject, timer });
|
|
54
|
+
try {
|
|
55
|
+
this.sock.send(payload);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
this.pending.delete(echo);
|
|
58
|
+
clearTimeout(timer);
|
|
59
|
+
reject(e instanceof Error ? e : new Error(String(e)));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async sendGroupMsg(groupId, text) {
|
|
64
|
+
const gid = typeof groupId === "number" ? groupId : parseInt(String(groupId), 10);
|
|
65
|
+
if (!Number.isFinite(gid)) throw new Error(`\u65E0\u6548 group_id: ${groupId}`);
|
|
66
|
+
const result = await this.call("send_group_msg", {
|
|
67
|
+
group_id: gid,
|
|
68
|
+
message: [{ type: "text", data: { text } }]
|
|
69
|
+
});
|
|
70
|
+
const ret = result.retcode ?? (result.status === "ok" ? 0 : -1);
|
|
71
|
+
if (ret !== 0) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`LLBot send_group_msg retcode=${ret}: ${result.message || result.wording || result.status || "fail"}`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
rejectAll(err) {
|
|
78
|
+
for (const [k, p] of this.pending) {
|
|
79
|
+
clearTimeout(p.timer);
|
|
80
|
+
p.reject(err);
|
|
81
|
+
this.pending.delete(k);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const llbotWsApi = new LlbotWsApi();
|
|
86
|
+
export {
|
|
87
|
+
LlbotWsApi,
|
|
88
|
+
llbotWsApi
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=llbot-ws-api.js.map
|