@sfmc-bds/qq-bridge 0.1.1-beta.3 → 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
package/README.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# @sfmc-bds/qq-bridge
|
|
2
2
|
|
|
3
|
-
SFMC QQ ↔ MC
|
|
3
|
+
SFMC QQ ↔ MC 桥接,支持双后端:
|
|
4
|
+
|
|
5
|
+
- **official**(默认):QQ 开放平台 Gateway,转发群内 @机器人消息
|
|
6
|
+
- **llbot**:LLBot OneBot 11 reverse-WS(端口 3002)
|
|
7
|
+
|
|
8
|
+
消息统一 POST 到 db-server `/api/sfmc/messages`。MC→QQ 出站由 db-server 按同一 `qq_backend` 处理。
|
|
9
|
+
|
|
10
|
+
QQ 侧指令(`菜单` / `ping` / `whoami`)在桥内拦截,不依赖 MC;official 用 Markdown+键盘,llbot 用编号菜单。
|
|
11
|
+
|
|
12
|
+
配置见 `configs/qq_config.json` 与文档 [QQ 互通](../../docs/zh/guide/qq-bridge.md)。
|
|
4
13
|
|
|
5
14
|
## 安装
|
|
6
15
|
|
|
@@ -13,9 +22,11 @@ node node_modules/@sfmc-bds/qq-bridge/dist/index.js
|
|
|
13
22
|
|
|
14
23
|
## 依赖
|
|
15
24
|
|
|
16
|
-
- `@sfmc-bds/sdk`
|
|
25
|
+
- `@sfmc-bds/sdk`
|
|
26
|
+
- `ws`
|
|
17
27
|
- Node.js >= 22.13
|
|
18
28
|
|
|
19
29
|
## 仓库
|
|
20
30
|
|
|
21
|
-
<https://github.com/DogeLakeDev/ScriptsForMinecraftServer/tree/main/qq-bridge>
|
|
31
|
+
<https://github.com/DogeLakeDev/ScriptsForMinecraftServer/tree/main/packages/qq-bridge>
|
|
32
|
+
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { request as httpRequest } from "node:http";
|
|
2
|
+
const TIMEOUT_MS = 5e3;
|
|
3
|
+
function requestJson(ep, method, pathAndQuery, body) {
|
|
4
|
+
const payload = body === void 0 ? void 0 : JSON.stringify(body);
|
|
5
|
+
const options = {
|
|
6
|
+
hostname: ep.host,
|
|
7
|
+
port: ep.port,
|
|
8
|
+
path: pathAndQuery,
|
|
9
|
+
method,
|
|
10
|
+
headers: {
|
|
11
|
+
Accept: "application/json",
|
|
12
|
+
...payload ? {
|
|
13
|
+
"Content-Type": "application/json",
|
|
14
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
15
|
+
} : {}
|
|
16
|
+
},
|
|
17
|
+
timeout: TIMEOUT_MS
|
|
18
|
+
};
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const req = httpRequest(options, (res) => {
|
|
21
|
+
let buf = "";
|
|
22
|
+
res.setEncoding("utf-8");
|
|
23
|
+
res.on("data", (c) => {
|
|
24
|
+
buf += c;
|
|
25
|
+
});
|
|
26
|
+
res.on("end", () => {
|
|
27
|
+
const status = res.statusCode ?? 0;
|
|
28
|
+
try {
|
|
29
|
+
resolve({ status, data: JSON.parse(buf) });
|
|
30
|
+
} catch {
|
|
31
|
+
reject(new Error(`db-server \u975E JSON (${status}): ${buf.slice(0, 120)}`));
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
req.on("timeout", () => req.destroy(new Error(`db-server \u8D85\u65F6 ${TIMEOUT_MS}ms`)));
|
|
36
|
+
req.on("error", reject);
|
|
37
|
+
if (payload) req.write(payload);
|
|
38
|
+
req.end();
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async function fetchSfmcStatus(ep) {
|
|
42
|
+
const { data } = await requestJson(ep, "GET", "/api/sfmc/status");
|
|
43
|
+
return data;
|
|
44
|
+
}
|
|
45
|
+
async function postJoinRequest(ep, body) {
|
|
46
|
+
return requestJson(ep, "POST", "/api/sfmc/qq/join/request", body);
|
|
47
|
+
}
|
|
48
|
+
async function fetchJoinSettings(ep) {
|
|
49
|
+
const { data } = await requestJson(ep, "GET", "/api/sfmc/qq/join/settings");
|
|
50
|
+
return data;
|
|
51
|
+
}
|
|
52
|
+
async function postJoinSettings(ep, body) {
|
|
53
|
+
return requestJson(ep, "POST", "/api/sfmc/qq/join/settings", body);
|
|
54
|
+
}
|
|
55
|
+
async function postJoinDecide(ep, body) {
|
|
56
|
+
return requestJson(ep, "POST", "/api/sfmc/qq/join/decide", body);
|
|
57
|
+
}
|
|
58
|
+
async function fetchJoinPending(ep, openid, asGroupAdmin = false) {
|
|
59
|
+
const q = `/api/sfmc/qq/join/pending?openid=${encodeURIComponent(openid)}` + (asGroupAdmin ? "&as_group_admin=true" : "");
|
|
60
|
+
const { data } = await requestJson(ep, "GET", q);
|
|
61
|
+
return data;
|
|
62
|
+
}
|
|
63
|
+
async function postAdminKick(ep, body) {
|
|
64
|
+
return requestJson(ep, "POST", "/api/sfmc/qq/admin/kick", body);
|
|
65
|
+
}
|
|
66
|
+
async function fetchBindMe(ep, openid) {
|
|
67
|
+
const q = `/api/sfmc/qq/bind/me?openid=${encodeURIComponent(openid)}`;
|
|
68
|
+
const { data } = await requestJson(ep, "GET", q);
|
|
69
|
+
return data;
|
|
70
|
+
}
|
|
71
|
+
async function postBindRequest(ep, body) {
|
|
72
|
+
return requestJson(ep, "POST", "/api/sfmc/qq/bind/request", body);
|
|
73
|
+
}
|
|
74
|
+
async function postBindUnbind(ep, body) {
|
|
75
|
+
return requestJson(ep, "POST", "/api/sfmc/qq/bind/unbind", body);
|
|
76
|
+
}
|
|
77
|
+
export {
|
|
78
|
+
fetchBindMe,
|
|
79
|
+
fetchJoinPending,
|
|
80
|
+
fetchJoinSettings,
|
|
81
|
+
fetchSfmcStatus,
|
|
82
|
+
postAdminKick,
|
|
83
|
+
postBindRequest,
|
|
84
|
+
postBindUnbind,
|
|
85
|
+
postJoinDecide,
|
|
86
|
+
postJoinRequest,
|
|
87
|
+
postJoinSettings
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=db-api.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/commands/db-api.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * commands/db-api.ts \u2014 qq-bridge \u8C03 db-server \u5E73\u53F0 API\uFF08status / bind\uFF09\n */\n\nimport { request as httpRequest, type RequestOptions } from \"node:http\";\n\nexport type DbEndpoint = { host: string; port: number };\n\nconst TIMEOUT_MS = 5_000;\n\nfunction requestJson<T>(\n ep: DbEndpoint,\n method: string,\n pathAndQuery: string,\n body?: unknown\n): Promise<{ status: number; data: T }> {\n const payload = body === undefined ? undefined : JSON.stringify(body);\n const options: RequestOptions = {\n hostname: ep.host,\n port: ep.port,\n path: pathAndQuery,\n method,\n headers: {\n Accept: \"application/json\",\n ...(payload\n ? {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(payload),\n }\n : {}),\n },\n timeout: TIMEOUT_MS,\n };\n return new Promise((resolve, reject) => {\n const req = httpRequest(options, (res) => {\n let buf = \"\";\n res.setEncoding(\"utf-8\");\n res.on(\"data\", (c: string) => {\n buf += c;\n });\n res.on(\"end\", () => {\n const status = res.statusCode ?? 0;\n try {\n resolve({ status, data: JSON.parse(buf) as T });\n } catch {\n reject(new Error(`db-server \u975E JSON (${status}): ${buf.slice(0, 120)}`));\n }\n });\n });\n req.on(\"timeout\", () => req.destroy(new Error(`db-server \u8D85\u65F6 ${TIMEOUT_MS}ms`)));\n req.on(\"error\", reject);\n if (payload) req.write(payload);\n req.end();\n });\n}\n\nexport type SfmcHostStatus = {\n hostname?: string;\n platform?: string;\n arch?: string;\n release?: string;\n uptimeSec?: number;\n uptimeText?: string;\n cpu?: { model?: string; cores?: number };\n memory?: { totalMb?: number; freeMb?: number; usedMb?: number; usedPercent?: number };\n loadavg?: [number, number, number];\n};\n\nexport type SfmcProcessStatus = {\n pid?: number;\n running?: boolean;\n uptimeSec?: number | null;\n uptimeText?: string;\n state?: \"running\" | \"stopped\";\n};\n\nexport type SfmcStatusResponse = {\n online?: Array<{ id?: string; name?: string }>;\n world?: { day?: number; difficulty?: string } | null;\n updatedAt?: number;\n note?: string;\n host?: SfmcHostStatus | null;\n processes?: {\n db?: SfmcProcessStatus;\n bds?: SfmcProcessStatus;\n } | null;\n};\n\nexport async function fetchSfmcStatus(ep: DbEndpoint): Promise<SfmcStatusResponse> {\n const { data } = await requestJson<SfmcStatusResponse>(ep, \"GET\", \"/api/sfmc/status\");\n return data;\n}\n\nexport type JoinRequestResponse = {\n success?: boolean;\n id?: string;\n player_name?: string;\n status?: string;\n auto_approved?: boolean;\n admin_notify?: boolean;\n note?: string;\n error?: string;\n};\n\nexport async function postJoinRequest(\n ep: DbEndpoint,\n body: { openid: string; player_name: string; qq_backend: string }\n): Promise<{ status: number; data: JoinRequestResponse }> {\n return requestJson(ep, \"POST\", \"/api/sfmc/qq/join/request\", body);\n}\n\nexport type JoinSettingsResponse = {\n success?: boolean;\n allowlist_enabled?: boolean;\n require_approval?: boolean;\n treat_group_admins_as_admins?: boolean;\n error?: string;\n note?: string;\n};\n\nexport async function fetchJoinSettings(ep: DbEndpoint): Promise<JoinSettingsResponse> {\n const { data } = await requestJson<JoinSettingsResponse>(ep, \"GET\", \"/api/sfmc/qq/join/settings\");\n return data;\n}\n\nexport async function postJoinSettings(\n ep: DbEndpoint,\n body: {\n openid: string;\n as_group_admin?: boolean;\n allowlist_enabled?: boolean;\n require_approval?: boolean;\n }\n): Promise<{ status: number; data: JoinSettingsResponse }> {\n return requestJson(ep, \"POST\", \"/api/sfmc/qq/join/settings\", body);\n}\n\nexport type JoinDecideResponse = {\n success?: boolean;\n id?: string;\n status?: string;\n player_name?: string;\n applicant_openid?: string;\n error?: string;\n};\n\nexport async function postJoinDecide(\n ep: DbEndpoint,\n body: {\n id: string;\n decision: \"approve\" | \"reject\";\n decided_by: string;\n as_group_admin?: boolean;\n }\n): Promise<{ status: number; data: JoinDecideResponse }> {\n return requestJson(ep, \"POST\", \"/api/sfmc/qq/join/decide\", body);\n}\n\nexport async function fetchJoinPending(\n ep: DbEndpoint,\n openid: string,\n asGroupAdmin = false\n): Promise<{\n success?: boolean;\n pending?: Array<{ id?: string; player_name?: string; applicant_openid?: string }>;\n error?: string;\n}> {\n const q =\n `/api/sfmc/qq/join/pending?openid=${encodeURIComponent(openid)}` +\n (asGroupAdmin ? \"&as_group_admin=true\" : \"\");\n const { data } = await requestJson(ep, \"GET\", q);\n return data;\n}\n\nexport async function postAdminKick(\n ep: DbEndpoint,\n body: { openid: string; target_name: string; reason?: string; as_group_admin?: boolean }\n): Promise<{ status: number; data: { success?: boolean; id?: string; error?: string } }> {\n return requestJson(ep, \"POST\", \"/api/sfmc/qq/admin/kick\", body);\n}\n\nexport type BindMeResponse = {\n success?: boolean;\n bound?: boolean;\n binding?: {\n player_name?: string;\n player_xuid?: string;\n qq_user_openid?: string;\n bound_at?: number;\n } | null;\n};\n\nexport async function fetchBindMe(ep: DbEndpoint, openid: string): Promise<BindMeResponse> {\n const q = `/api/sfmc/qq/bind/me?openid=${encodeURIComponent(openid)}`;\n const { data } = await requestJson<BindMeResponse>(ep, \"GET\", q);\n return data;\n}\n\nexport type BindRequestResponse = {\n success?: boolean;\n code?: string;\n expires_at?: number;\n ttl_ms?: number;\n error?: string;\n player_name?: string;\n};\n\nexport async function postBindRequest(\n ep: DbEndpoint,\n body: { openid: string; qq_backend: string }\n): Promise<{ status: number; data: BindRequestResponse }> {\n return requestJson<BindRequestResponse>(ep, \"POST\", \"/api/sfmc/qq/bind/request\", body);\n}\n\nexport type BindUnbindResponse = {\n success?: boolean;\n unbound?: boolean;\n error?: string;\n};\n\nexport async function postBindUnbind(\n ep: DbEndpoint,\n body: { openid: string }\n): Promise<{ status: number; data: BindUnbindResponse }> {\n return requestJson<BindUnbindResponse>(ep, \"POST\", \"/api/sfmc/qq/bind/unbind\", body);\n}\n"],
|
|
5
|
+
"mappings": "AAIA,SAAS,WAAW,mBAAwC;AAI5D,MAAM,aAAa;AAEnB,SAAS,YACP,IACA,QACA,cACA,MACsC;AACtC,QAAM,UAAU,SAAS,SAAY,SAAY,KAAK,UAAU,IAAI;AACpE,QAAM,UAA0B;AAAA,IAC9B,UAAU,GAAG;AAAA,IACb,MAAM,GAAG;AAAA,IACT,MAAM;AAAA,IACN;AAAA,IACA,SAAS;AAAA,MACP,QAAQ;AAAA,MACR,GAAI,UACA;AAAA,QACE,gBAAgB;AAAA,QAChB,kBAAkB,OAAO,WAAW,OAAO;AAAA,MAC7C,IACA,CAAC;AAAA,IACP;AAAA,IACA,SAAS;AAAA,EACX;AACA,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,MAAM,YAAY,SAAS,CAAC,QAAQ;AACxC,UAAI,MAAM;AACV,UAAI,YAAY,OAAO;AACvB,UAAI,GAAG,QAAQ,CAAC,MAAc;AAC5B,eAAO;AAAA,MACT,CAAC;AACD,UAAI,GAAG,OAAO,MAAM;AAClB,cAAM,SAAS,IAAI,cAAc;AACjC,YAAI;AACF,kBAAQ,EAAE,QAAQ,MAAM,KAAK,MAAM,GAAG,EAAO,CAAC;AAAA,QAChD,QAAQ;AACN,iBAAO,IAAI,MAAM,0BAAqB,MAAM,MAAM,IAAI,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC;AAAA,QACxE;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AACD,QAAI,GAAG,WAAW,MAAM,IAAI,QAAQ,IAAI,MAAM,0BAAgB,UAAU,IAAI,CAAC,CAAC;AAC9E,QAAI,GAAG,SAAS,MAAM;AACtB,QAAI,QAAS,KAAI,MAAM,OAAO;AAC9B,QAAI,IAAI;AAAA,EACV,CAAC;AACH;AAkCA,eAAsB,gBAAgB,IAA6C;AACjF,QAAM,EAAE,KAAK,IAAI,MAAM,YAAgC,IAAI,OAAO,kBAAkB;AACpF,SAAO;AACT;AAaA,eAAsB,gBACpB,IACA,MACwD;AACxD,SAAO,YAAY,IAAI,QAAQ,6BAA6B,IAAI;AAClE;AAWA,eAAsB,kBAAkB,IAA+C;AACrF,QAAM,EAAE,KAAK,IAAI,MAAM,YAAkC,IAAI,OAAO,4BAA4B;AAChG,SAAO;AACT;AAEA,eAAsB,iBACpB,IACA,MAMyD;AACzD,SAAO,YAAY,IAAI,QAAQ,8BAA8B,IAAI;AACnE;AAWA,eAAsB,eACpB,IACA,MAMuD;AACvD,SAAO,YAAY,IAAI,QAAQ,4BAA4B,IAAI;AACjE;AAEA,eAAsB,iBACpB,IACA,QACA,eAAe,OAKd;AACD,QAAM,IACJ,oCAAoC,mBAAmB,MAAM,CAAC,MAC7D,eAAe,yBAAyB;AAC3C,QAAM,EAAE,KAAK,IAAI,MAAM,YAAY,IAAI,OAAO,CAAC;AAC/C,SAAO;AACT;AAEA,eAAsB,cACpB,IACA,MACuF;AACvF,SAAO,YAAY,IAAI,QAAQ,2BAA2B,IAAI;AAChE;AAaA,eAAsB,YAAY,IAAgB,QAAyC;AACzF,QAAM,IAAI,+BAA+B,mBAAmB,MAAM,CAAC;AACnE,QAAM,EAAE,KAAK,IAAI,MAAM,YAA4B,IAAI,OAAO,CAAC;AAC/D,SAAO;AACT;AAWA,eAAsB,gBACpB,IACA,MACwD;AACxD,SAAO,YAAiC,IAAI,QAAQ,6BAA6B,IAAI;AACvF;AAQA,eAAsB,eACpB,IACA,MACuD;AACvD,SAAO,YAAgC,IAAI,QAAQ,4BAA4B,IAAI;AACrF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|