@sfmc-bds/bds-tools 0.2.0-beta.7 → 0.2.0-beta.9
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/dist/bds-manager.d.ts.map +1 -1
- package/dist/bds-manager.js +40 -65
- package/dist/bds-manager.js.map +2 -2
- package/dist/check-update.d.ts.map +1 -1
- package/dist/check-update.js +4 -6
- package/dist/check-update.js.map +2 -2
- package/dist/fsx.d.ts +2 -7
- package/dist/fsx.d.ts.map +1 -1
- package/dist/fsx.js +0 -11
- package/dist/fsx.js.map +2 -2
- package/dist/host-platform.d.ts +25 -0
- package/dist/host-platform.d.ts.map +1 -0
- package/dist/host-platform.js +50 -0
- package/dist/host-platform.js.map +7 -0
- package/dist/pack-manager.d.ts.map +1 -1
- package/dist/pack-manager.js +6 -1
- package/dist/pack-manager.js.map +2 -2
- package/dist/pack-update/bindings.d.ts +16 -0
- package/dist/pack-update/bindings.d.ts.map +1 -0
- package/dist/pack-update/bindings.js +69 -0
- package/dist/pack-update/bindings.js.map +7 -0
- package/dist/pack-update/config.d.ts +16 -0
- package/dist/pack-update/config.d.ts.map +1 -0
- package/dist/pack-update/config.js +146 -0
- package/dist/pack-update/config.js.map +7 -0
- package/dist/pack-update/deps.d.ts +28 -0
- package/dist/pack-update/deps.d.ts.map +1 -0
- package/dist/pack-update/deps.js +20 -0
- package/dist/pack-update/deps.js.map +7 -0
- package/dist/pack-update/index.d.ts +55 -0
- package/dist/pack-update/index.d.ts.map +1 -0
- package/dist/pack-update/index.js +72 -0
- package/dist/pack-update/index.js.map +7 -0
- package/dist/pack-update/providers/curseforge.d.ts +23 -0
- package/dist/pack-update/providers/curseforge.d.ts.map +1 -0
- package/dist/pack-update/providers/curseforge.js +229 -0
- package/dist/pack-update/providers/curseforge.js.map +7 -0
- package/dist/pack-update/providers/index.d.ts +16 -0
- package/dist/pack-update/providers/index.d.ts.map +1 -0
- package/dist/pack-update/providers/index.js +37 -0
- package/dist/pack-update/providers/index.js.map +7 -0
- package/dist/pack-update/service.d.ts +28 -0
- package/dist/pack-update/service.d.ts.map +1 -0
- package/dist/pack-update/service.js +570 -0
- package/dist/pack-update/service.js.map +7 -0
- package/dist/pack-update/types.d.ts +126 -0
- package/dist/pack-update/types.d.ts.map +1 -0
- package/dist/pack-update/types.js +8 -0
- package/dist/pack-update/types.js.map +7 -0
- package/dist/pack-update/version-policy.d.ts +81 -0
- package/dist/pack-update/version-policy.d.ts.map +1 -0
- package/dist/pack-update/version-policy.js +210 -0
- package/dist/pack-update/version-policy.js.map +7 -0
- package/dist/paths.d.ts +1 -1
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +3 -5
- package/dist/paths.js.map +2 -2
- package/dist/process-probe.d.ts +34 -0
- package/dist/process-probe.d.ts.map +1 -0
- package/dist/process-probe.js +152 -0
- package/dist/process-probe.js.map +7 -0
- package/dist/qq-events-notify.d.ts +16 -0
- package/dist/qq-events-notify.d.ts.map +1 -0
- package/dist/qq-events-notify.js +70 -0
- package/dist/qq-events-notify.js.map +7 -0
- package/dist/qq-events-notify.test.d.ts +5 -0
- package/dist/qq-events-notify.test.d.ts.map +1 -0
- package/dist/qq-events-notify.test.js +41 -0
- package/dist/qq-events-notify.test.js.map +7 -0
- package/dist/qqutil.d.ts +2 -1
- package/dist/qqutil.d.ts.map +1 -1
- package/dist/qqutil.js +49 -8
- package/dist/qqutil.js.map +2 -2
- package/dist/upstream.d.ts +4 -3
- package/dist/upstream.d.ts.map +1 -1
- package/dist/upstream.js +8 -7
- package/dist/upstream.js.map +2 -2
- package/dist/version.js +2 -2
- package/dist/version.js.map +2 -2
- package/dist/world-packs.d.ts +0 -2
- package/dist/world-packs.d.ts.map +1 -1
- package/dist/world-packs.js +0 -2
- package/dist/world-packs.js.map +2 -2
- package/package.json +8 -6
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { exec, execSync } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { resolveRuntimeRoot, stateDir } from "@sfmc-bds/sdk/node/config";
|
|
7
|
+
import { bdsExeName } from "./host-platform.js";
|
|
8
|
+
const execAsync = promisify(exec);
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
11
|
+
const DEFAULT_ROOT_DIR = resolveRuntimeRoot(__dirname);
|
|
12
|
+
let execImpl = async (cmd, opts) => {
|
|
13
|
+
const result = await execAsync(cmd, opts);
|
|
14
|
+
return { stdout: String(result.stdout), stderr: String(result.stderr) };
|
|
15
|
+
};
|
|
16
|
+
function setExecForTesting(fn) {
|
|
17
|
+
execImpl = fn ?? (async (cmd, opts) => {
|
|
18
|
+
const result = await execAsync(cmd, opts);
|
|
19
|
+
return { stdout: String(result.stdout), stderr: String(result.stderr) };
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function resolveRoot(rootDir) {
|
|
23
|
+
return rootDir ?? DEFAULT_ROOT_DIR;
|
|
24
|
+
}
|
|
25
|
+
function pidFilePath(rootDir) {
|
|
26
|
+
return path.join(stateDir(resolveRoot(rootDir)), "bds.pid");
|
|
27
|
+
}
|
|
28
|
+
function ensureStateDirForRoot(rootDir) {
|
|
29
|
+
fs.mkdirSync(stateDir(resolveRoot(rootDir)), { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
function readBdsPidFile(rootDir) {
|
|
32
|
+
try {
|
|
33
|
+
return parseInt(fs.readFileSync(pidFilePath(rootDir), "utf-8").trim(), 10) || 0;
|
|
34
|
+
} catch {
|
|
35
|
+
return 0;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function writeBdsPidFile(pid, rootDir) {
|
|
39
|
+
try {
|
|
40
|
+
ensureStateDirForRoot(rootDir);
|
|
41
|
+
fs.writeFileSync(pidFilePath(rootDir), String(pid));
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function clearBdsPidFile(rootDir) {
|
|
46
|
+
try {
|
|
47
|
+
const file = pidFilePath(rootDir);
|
|
48
|
+
if (fs.existsSync(file)) fs.unlinkSync(file);
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
let aliveOverride = null;
|
|
53
|
+
function setIsAliveForTesting(fn) {
|
|
54
|
+
aliveOverride = fn;
|
|
55
|
+
}
|
|
56
|
+
async function isProcessAlive(pid) {
|
|
57
|
+
if (!pid) return false;
|
|
58
|
+
if (aliveOverride) return aliveOverride(pid);
|
|
59
|
+
try {
|
|
60
|
+
if (process.platform === "win32") {
|
|
61
|
+
const { stdout } = await execImpl(`tasklist /fi "PID eq ${pid}" /nh`, { windowsHide: true });
|
|
62
|
+
return stdout.includes(String(pid));
|
|
63
|
+
}
|
|
64
|
+
process.kill(pid, 0);
|
|
65
|
+
return true;
|
|
66
|
+
} catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function isProcessAliveSync(pid) {
|
|
71
|
+
if (!pid) return false;
|
|
72
|
+
if (aliveOverride) return aliveOverride(pid);
|
|
73
|
+
try {
|
|
74
|
+
if (process.platform === "win32") {
|
|
75
|
+
const stdout = execSync(`tasklist /fi "PID eq ${pid}" /nh`, {
|
|
76
|
+
windowsHide: true,
|
|
77
|
+
encoding: "utf8"
|
|
78
|
+
});
|
|
79
|
+
return String(stdout).includes(String(pid));
|
|
80
|
+
}
|
|
81
|
+
process.kill(pid, 0);
|
|
82
|
+
return true;
|
|
83
|
+
} catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function findBedrockServerPids() {
|
|
88
|
+
try {
|
|
89
|
+
if (process.platform === "win32") {
|
|
90
|
+
const image = bdsExeName("windows");
|
|
91
|
+
const { stdout: stdout2 } = await execImpl(`tasklist /fi "IMAGENAME eq ${image}" /fo csv /nh`, {
|
|
92
|
+
windowsHide: true
|
|
93
|
+
});
|
|
94
|
+
const pids = [];
|
|
95
|
+
const re = new RegExp(`"${image.replace(".", "\\.")}","(\\d+)"`, "i");
|
|
96
|
+
for (const line of stdout2.split(/\r?\n/)) {
|
|
97
|
+
const m = line.match(re);
|
|
98
|
+
if (m) {
|
|
99
|
+
const pid = parseInt(m[1] ?? "0", 10);
|
|
100
|
+
if (pid > 0) pids.push(pid);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return pids;
|
|
104
|
+
}
|
|
105
|
+
const { stdout } = await execImpl("pgrep -x bedrock_server", { windowsHide: true });
|
|
106
|
+
return stdout.split(/\r?\n/).map((s) => parseInt(s.trim(), 10)).filter((n) => n > 0);
|
|
107
|
+
} catch {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function killBedrockServerByImage() {
|
|
112
|
+
try {
|
|
113
|
+
if (process.platform === "win32") {
|
|
114
|
+
await execImpl(`taskkill /f /im ${bdsExeName("windows")}`, { windowsHide: true });
|
|
115
|
+
} else {
|
|
116
|
+
await execImpl("pkill -x bedrock_server", { windowsHide: true });
|
|
117
|
+
}
|
|
118
|
+
} catch {
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async function probeBdsStatus(opts) {
|
|
122
|
+
const managedPid = opts.managedPid ?? 0;
|
|
123
|
+
const hasStdin = opts.hasStdin ?? false;
|
|
124
|
+
if (managedPid > 0 && hasStdin && await isProcessAlive(managedPid)) {
|
|
125
|
+
return { state: "managed", pid: managedPid };
|
|
126
|
+
}
|
|
127
|
+
const filePid = readBdsPidFile(opts.rootDir);
|
|
128
|
+
if (filePid > 0 && await isProcessAlive(filePid)) {
|
|
129
|
+
return { state: "external", pid: filePid };
|
|
130
|
+
}
|
|
131
|
+
const imagePids = await findBedrockServerPids();
|
|
132
|
+
if (imagePids.length > 0) {
|
|
133
|
+
return { state: "external", pid: imagePids[0] ?? 0 };
|
|
134
|
+
}
|
|
135
|
+
if (filePid > 0) {
|
|
136
|
+
clearBdsPidFile(opts.rootDir);
|
|
137
|
+
}
|
|
138
|
+
return { state: "stopped", pid: 0 };
|
|
139
|
+
}
|
|
140
|
+
export {
|
|
141
|
+
clearBdsPidFile,
|
|
142
|
+
findBedrockServerPids,
|
|
143
|
+
isProcessAlive,
|
|
144
|
+
isProcessAliveSync,
|
|
145
|
+
killBedrockServerByImage,
|
|
146
|
+
probeBdsStatus,
|
|
147
|
+
readBdsPidFile,
|
|
148
|
+
setExecForTesting,
|
|
149
|
+
setIsAliveForTesting,
|
|
150
|
+
writeBdsPidFile
|
|
151
|
+
};
|
|
152
|
+
//# sourceMappingURL=process-probe.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/process-probe.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * process-probe.ts \u2014 OS \u7EA7 BDS \u8FDB\u7A0B\u63A2\u6D4B\u4E0E PID \u6587\u4EF6\u7BA1\u7406\n *\n * \u4F9B bds-manager / sfmc \u5171\u7528\uFF0C\u907F\u514D\u91CD\u590D tasklist / taskkill \u903B\u8F91\u3002\n */\n\nimport { exec, execSync } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { resolveRuntimeRoot, stateDir } from \"@sfmc-bds/sdk/node/config\";\nimport { bdsExeName } from \"./host-platform.js\";\n\nconst execAsync = promisify(exec);\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n/** \u4E0E paths.ROOT_DIR \u4E00\u81F4\uFF1ASFMC_ROOT > walk-up monorepo */\nconst DEFAULT_ROOT_DIR = resolveRuntimeRoot(__dirname);\n\nexport type ExecFn = (\n cmd: string,\n opts?: { windowsHide?: boolean }\n) => Promise<{ stdout: string; stderr: string }>;\n\nlet execImpl: ExecFn = async (cmd, opts) => {\n const result = await execAsync(cmd, opts);\n return { stdout: String(result.stdout), stderr: String(result.stderr) };\n};\n\n/** \u6D4B\u8BD5\u6CE8\u5165 exec\uFF0C\u4F20 null \u6062\u590D\u9ED8\u8BA4 */\nexport function setExecForTesting(fn: ExecFn | null): void {\n execImpl =\n fn ??\n (async (cmd, opts) => {\n const result = await execAsync(cmd, opts);\n return { stdout: String(result.stdout), stderr: String(result.stderr) };\n });\n}\n\nfunction resolveRoot(rootDir?: string): string {\n return rootDir ?? DEFAULT_ROOT_DIR;\n}\n\nfunction pidFilePath(rootDir?: string): string {\n return path.join(stateDir(resolveRoot(rootDir)), \"bds.pid\");\n}\n\nfunction ensureStateDirForRoot(rootDir?: string): void {\n fs.mkdirSync(stateDir(resolveRoot(rootDir)), { recursive: true });\n}\n\nexport function readBdsPidFile(rootDir?: string): number {\n try {\n return parseInt(fs.readFileSync(pidFilePath(rootDir), \"utf-8\").trim(), 10) || 0;\n } catch {\n return 0;\n }\n}\n\nexport function writeBdsPidFile(pid: number, rootDir?: string): void {\n try {\n ensureStateDirForRoot(rootDir);\n fs.writeFileSync(pidFilePath(rootDir), String(pid));\n } catch {\n /* ignore */\n }\n}\n\nexport function clearBdsPidFile(rootDir?: string): void {\n try {\n const file = pidFilePath(rootDir);\n if (fs.existsSync(file)) fs.unlinkSync(file);\n } catch {\n /* ignore */\n }\n}\n\nlet aliveOverride: ((pid: number) => boolean) | null = null;\n\n/** \u6D4B\u8BD5\u6CE8\u5165\u63A2\u6D3B\uFF0C\u4F20 null \u6062\u590D\u9ED8\u8BA4\uFF08\u907F\u514D Linux \u4E0A process.kill \u78B0\u5230\u771F\u5B9E PID\uFF09 */\nexport function setIsAliveForTesting(fn: ((pid: number) => boolean) | null): void {\n aliveOverride = fn;\n}\n\nexport async function isProcessAlive(pid: number): Promise<boolean> {\n if (!pid) return false;\n if (aliveOverride) return aliveOverride(pid);\n try {\n if (process.platform === \"win32\") {\n const { stdout } = await execImpl(`tasklist /fi \"PID eq ${pid}\" /nh`, { windowsHide: true });\n return stdout.includes(String(pid));\n }\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/** \u540C\u6B65\u63A2\u6D3B\uFF08\u4F9B sync \u7684 sendCommand \u7B49\u8DEF\u5F84\uFF1B\u907F\u514D Promise \u88AB\u5F53 truthy\uFF09 */\nexport function isProcessAliveSync(pid: number): boolean {\n if (!pid) return false;\n if (aliveOverride) return aliveOverride(pid);\n try {\n if (process.platform === \"win32\") {\n const stdout = execSync(`tasklist /fi \"PID eq ${pid}\" /nh`, {\n windowsHide: true,\n encoding: \"utf8\",\n });\n return String(stdout).includes(String(pid));\n }\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function findBedrockServerPids(): Promise<number[]> {\n try {\n if (process.platform === \"win32\") {\n const image = bdsExeName(\"windows\");\n const { stdout } = await execImpl(`tasklist /fi \"IMAGENAME eq ${image}\" /fo csv /nh`, {\n windowsHide: true,\n });\n const pids: number[] = [];\n const re = new RegExp(`\"${image.replace(\".\", \"\\\\.\")}\",\"(\\\\d+)\"`, \"i\");\n for (const line of stdout.split(/\\r?\\n/)) {\n const m = line.match(re);\n if (m) {\n const pid = parseInt(m[1] ?? \"0\", 10);\n if (pid > 0) pids.push(pid);\n }\n }\n return pids;\n }\n const { stdout } = await execImpl(\"pgrep -x bedrock_server\", { windowsHide: true });\n return stdout\n .split(/\\r?\\n/)\n .map((s) => parseInt(s.trim(), 10))\n .filter((n) => n > 0);\n } catch {\n return [];\n }\n}\n\nexport async function killBedrockServerByImage(): Promise<void> {\n try {\n if (process.platform === \"win32\") {\n await execImpl(`taskkill /f /im ${bdsExeName(\"windows\")}`, { windowsHide: true });\n } else {\n await execImpl(\"pkill -x bedrock_server\", { windowsHide: true });\n }\n } catch {\n /* ignore \u2014 \u65E0\u8FDB\u7A0B\u65F6 taskkill/pkill \u4F1A\u975E\u96F6\u9000\u51FA */\n }\n}\n\nexport type BdsProbeState = \"stopped\" | \"managed\" | \"external\";\n\nexport interface BdsProbeResult {\n state: BdsProbeState;\n pid: number;\n}\n\nexport async function probeBdsStatus(opts: {\n managedPid?: number;\n hasStdin?: boolean;\n rootDir?: string;\n}): Promise<BdsProbeResult> {\n const managedPid = opts.managedPid ?? 0;\n const hasStdin = opts.hasStdin ?? false;\n\n if (managedPid > 0 && hasStdin && (await isProcessAlive(managedPid))) {\n return { state: \"managed\", pid: managedPid };\n }\n\n const filePid = readBdsPidFile(opts.rootDir);\n if (filePid > 0 && (await isProcessAlive(filePid))) {\n return { state: \"external\", pid: filePid };\n }\n\n const imagePids = await findBedrockServerPids();\n if (imagePids.length > 0) {\n return { state: \"external\", pid: imagePids[0] ?? 0 };\n }\n\n if (filePid > 0) {\n clearBdsPidFile(opts.rootDir);\n }\n\n return { state: \"stopped\", pid: 0 };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["stdout"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* qq-events-notify.ts — 向 db-server 上报 BDS 启停事件
|
|
3
|
+
*
|
|
4
|
+
* 只 POST 结构化事件;群 openid / 频控由 db-server 处理(DIP)。
|
|
5
|
+
* 失败仅 warn,不抛错。
|
|
6
|
+
*/
|
|
7
|
+
export type BdsLifecycleEventType = "crash" | "start";
|
|
8
|
+
/**
|
|
9
|
+
* POST /api/sfmc/qq/events;超时/非 2xx 不抛。
|
|
10
|
+
*/
|
|
11
|
+
export declare function postBdsLifecycleEvent(type: BdsLifecycleEventType, detail?: string, opts?: {
|
|
12
|
+
host?: string;
|
|
13
|
+
port?: number;
|
|
14
|
+
timeoutMs?: number;
|
|
15
|
+
}): Promise<void>;
|
|
16
|
+
//# sourceMappingURL=qq-events-notify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qq-events-notify.d.ts","sourceRoot":"","sources":["../src/qq-events-notify.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,OAAO,CAAC;AAiBtD;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,qBAAqB,EAC3B,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1D,OAAO,CAAC,IAAI,CAAC,CA6Cf"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_DB_CONFIG,
|
|
3
|
+
loadEnsuredConfig
|
|
4
|
+
} from "@sfmc-bds/sdk/node/config";
|
|
5
|
+
import http from "node:http";
|
|
6
|
+
import { log } from "./log.js";
|
|
7
|
+
import { ROOT_DIR } from "./paths.js";
|
|
8
|
+
function resolveDbPort() {
|
|
9
|
+
try {
|
|
10
|
+
const cfg = loadEnsuredConfig(
|
|
11
|
+
ROOT_DIR,
|
|
12
|
+
"db_config.json",
|
|
13
|
+
"db_config",
|
|
14
|
+
{ ...DEFAULT_DB_CONFIG }
|
|
15
|
+
);
|
|
16
|
+
const n = Number(cfg.db_port ?? 3001);
|
|
17
|
+
return Number.isFinite(n) && n > 0 ? n : 3001;
|
|
18
|
+
} catch {
|
|
19
|
+
return 3001;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function postBdsLifecycleEvent(type, detail, opts) {
|
|
23
|
+
const host = opts?.host ?? "127.0.0.1";
|
|
24
|
+
const port = opts?.port ?? resolveDbPort();
|
|
25
|
+
const timeoutMs = opts?.timeoutMs ?? 3e3;
|
|
26
|
+
const payload = JSON.stringify({
|
|
27
|
+
type,
|
|
28
|
+
detail: detail ?? void 0
|
|
29
|
+
});
|
|
30
|
+
return new Promise((resolve) => {
|
|
31
|
+
const req = http.request(
|
|
32
|
+
{
|
|
33
|
+
hostname: host,
|
|
34
|
+
port,
|
|
35
|
+
path: "/api/sfmc/qq/events",
|
|
36
|
+
method: "POST",
|
|
37
|
+
headers: {
|
|
38
|
+
"Content-Type": "application/json",
|
|
39
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
40
|
+
},
|
|
41
|
+
timeout: timeoutMs
|
|
42
|
+
},
|
|
43
|
+
(res) => {
|
|
44
|
+
res.resume();
|
|
45
|
+
res.on("end", () => {
|
|
46
|
+
const code = res.statusCode ?? 0;
|
|
47
|
+
if (code < 200 || code >= 300) {
|
|
48
|
+
log.warn(`[QQ\u4E8B\u4EF6] db-server \u8FD4\u56DE HTTP ${code} (type=${type})`);
|
|
49
|
+
}
|
|
50
|
+
resolve();
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
req.on("timeout", () => {
|
|
55
|
+
req.destroy();
|
|
56
|
+
log.warn(`[QQ\u4E8B\u4EF6] \u4E0A\u62A5\u8D85\u65F6 ${timeoutMs}ms (type=${type})`);
|
|
57
|
+
resolve();
|
|
58
|
+
});
|
|
59
|
+
req.on("error", (err) => {
|
|
60
|
+
log.warn(`[QQ\u4E8B\u4EF6] \u4E0A\u62A5\u5931\u8D25: ${err.message} (type=${type})`);
|
|
61
|
+
resolve();
|
|
62
|
+
});
|
|
63
|
+
req.write(payload);
|
|
64
|
+
req.end();
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
export {
|
|
68
|
+
postBdsLifecycleEvent
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=qq-events-notify.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/qq-events-notify.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * qq-events-notify.ts \u2014 \u5411 db-server \u4E0A\u62A5 BDS \u542F\u505C\u4E8B\u4EF6\n *\n * \u53EA POST \u7ED3\u6784\u5316\u4E8B\u4EF6\uFF1B\u7FA4 openid / \u9891\u63A7\u7531 db-server \u5904\u7406\uFF08DIP\uFF09\u3002\n * \u5931\u8D25\u4EC5 warn\uFF0C\u4E0D\u629B\u9519\u3002\n */\n\nimport {\n DEFAULT_DB_CONFIG,\n loadEnsuredConfig,\n type DBConfig,\n} from \"@sfmc-bds/sdk/node/config\";\nimport http from \"node:http\";\nimport { log } from \"./log.js\";\nimport { ROOT_DIR } from \"./paths.js\";\n\nexport type BdsLifecycleEventType = \"crash\" | \"start\";\n\nfunction resolveDbPort(): number {\n try {\n const cfg = loadEnsuredConfig(\n ROOT_DIR,\n \"db_config.json\",\n \"db_config\",\n { ...DEFAULT_DB_CONFIG } as Record<string, unknown>\n ) as DBConfig;\n const n = Number(cfg.db_port ?? 3001);\n return Number.isFinite(n) && n > 0 ? n : 3001;\n } catch {\n return 3001;\n }\n}\n\n/**\n * POST /api/sfmc/qq/events\uFF1B\u8D85\u65F6/\u975E 2xx \u4E0D\u629B\u3002\n */\nexport function postBdsLifecycleEvent(\n type: BdsLifecycleEventType,\n detail?: string,\n opts?: { host?: string; port?: number; timeoutMs?: number }\n): Promise<void> {\n const host = opts?.host ?? \"127.0.0.1\";\n const port = opts?.port ?? resolveDbPort();\n const timeoutMs = opts?.timeoutMs ?? 3_000;\n const payload = JSON.stringify({\n type,\n detail: detail ?? undefined,\n });\n\n return new Promise((resolve) => {\n const req = http.request(\n {\n hostname: host,\n port,\n path: \"/api/sfmc/qq/events\",\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(payload),\n },\n timeout: timeoutMs,\n },\n (res) => {\n res.resume();\n res.on(\"end\", () => {\n const code = res.statusCode ?? 0;\n if (code < 200 || code >= 300) {\n log.warn(`[QQ\u4E8B\u4EF6] db-server \u8FD4\u56DE HTTP ${code} (type=${type})`);\n }\n resolve();\n });\n }\n );\n req.on(\"timeout\", () => {\n req.destroy();\n log.warn(`[QQ\u4E8B\u4EF6] \u4E0A\u62A5\u8D85\u65F6 ${timeoutMs}ms (type=${type})`);\n resolve();\n });\n req.on(\"error\", (err) => {\n log.warn(`[QQ\u4E8B\u4EF6] \u4E0A\u62A5\u5931\u8D25: ${err.message} (type=${type})`);\n resolve();\n });\n req.write(payload);\n req.end();\n });\n}\n"],
|
|
5
|
+
"mappings": "AAOA;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,OAAO,UAAU;AACjB,SAAS,WAAW;AACpB,SAAS,gBAAgB;AAIzB,SAAS,gBAAwB;AAC/B,MAAI;AACF,UAAM,MAAM;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA,EAAE,GAAG,kBAAkB;AAAA,IACzB;AACA,UAAM,IAAI,OAAO,IAAI,WAAW,IAAI;AACpC,WAAO,OAAO,SAAS,CAAC,KAAK,IAAI,IAAI,IAAI;AAAA,EAC3C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKO,SAAS,sBACd,MACA,QACA,MACe;AACf,QAAM,OAAO,MAAM,QAAQ;AAC3B,QAAM,OAAO,MAAM,QAAQ,cAAc;AACzC,QAAM,YAAY,MAAM,aAAa;AACrC,QAAM,UAAU,KAAK,UAAU;AAAA,IAC7B;AAAA,IACA,QAAQ,UAAU;AAAA,EACpB,CAAC;AAED,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,MAAM,KAAK;AAAA,MACf;AAAA,QACE,UAAU;AAAA,QACV;AAAA,QACA,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,kBAAkB,OAAO,WAAW,OAAO;AAAA,QAC7C;AAAA,QACA,SAAS;AAAA,MACX;AAAA,MACA,CAAC,QAAQ;AACP,YAAI,OAAO;AACX,YAAI,GAAG,OAAO,MAAM;AAClB,gBAAM,OAAO,IAAI,cAAc;AAC/B,cAAI,OAAO,OAAO,QAAQ,KAAK;AAC7B,gBAAI,KAAK,gDAA4B,IAAI,UAAU,IAAI,GAAG;AAAA,UAC5D;AACA,kBAAQ;AAAA,QACV,CAAC;AAAA,MACH;AAAA,IACF;AACA,QAAI,GAAG,WAAW,MAAM;AACtB,UAAI,QAAQ;AACZ,UAAI,KAAK,6CAAe,SAAS,YAAY,IAAI,GAAG;AACpD,cAAQ;AAAA,IACV,CAAC;AACD,QAAI,GAAG,SAAS,CAAC,QAAQ;AACvB,UAAI,KAAK,8CAAgB,IAAI,OAAO,UAAU,IAAI,GAAG;AACrD,cAAQ;AAAA,IACV,CAAC;AACD,QAAI,MAAM,OAAO;AACjB,QAAI,IAAI;AAAA,EACV,CAAC;AACH;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qq-events-notify.test.d.ts","sourceRoot":"","sources":["../src/qq-events-notify.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import test from "node:test";
|
|
4
|
+
import { postBdsLifecycleEvent } from "./qq-events-notify.js";
|
|
5
|
+
test("postBdsLifecycleEvent\uFF1Adb \u4E0D\u53EF\u8FBE\u4E0D\u629B\u9519", async () => {
|
|
6
|
+
await assert.doesNotReject(
|
|
7
|
+
() => postBdsLifecycleEvent("start", "pid=1", {
|
|
8
|
+
host: "127.0.0.1",
|
|
9
|
+
port: 1,
|
|
10
|
+
timeoutMs: 200
|
|
11
|
+
})
|
|
12
|
+
);
|
|
13
|
+
});
|
|
14
|
+
test("postBdsLifecycleEvent\uFF1A2xx \u6210\u529F", async () => {
|
|
15
|
+
const server = http.createServer((req, res) => {
|
|
16
|
+
let body = "";
|
|
17
|
+
req.on("data", (c) => body += c);
|
|
18
|
+
req.on("end", () => {
|
|
19
|
+
assert.equal(req.method, "POST");
|
|
20
|
+
assert.equal(req.url, "/api/sfmc/qq/events");
|
|
21
|
+
const parsed = JSON.parse(body);
|
|
22
|
+
assert.equal(parsed.type, "crash");
|
|
23
|
+
assert.equal(parsed.detail, "code=1");
|
|
24
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
25
|
+
res.end(JSON.stringify({ success: true }));
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
29
|
+
const addr = server.address();
|
|
30
|
+
assert.ok(addr && typeof addr === "object");
|
|
31
|
+
try {
|
|
32
|
+
await postBdsLifecycleEvent("crash", "code=1", {
|
|
33
|
+
host: "127.0.0.1",
|
|
34
|
+
port: addr.port,
|
|
35
|
+
timeoutMs: 2e3
|
|
36
|
+
});
|
|
37
|
+
} finally {
|
|
38
|
+
await new Promise((resolve, reject) => server.close((e) => e ? reject(e) : resolve()));
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
//# sourceMappingURL=qq-events-notify.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/qq-events-notify.test.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * qq-events-notify \u5355\u6D4B\uFF1A\u5931\u8D25\u4E0D\u629B\u3001\u53EF\u6CE8\u5165 host/port\n */\n\nimport assert from \"node:assert/strict\";\nimport http from \"node:http\";\nimport test from \"node:test\";\nimport { postBdsLifecycleEvent } from \"./qq-events-notify.js\";\n\ntest(\"postBdsLifecycleEvent\uFF1Adb \u4E0D\u53EF\u8FBE\u4E0D\u629B\u9519\", async () => {\n await assert.doesNotReject(() =>\n postBdsLifecycleEvent(\"start\", \"pid=1\", {\n host: \"127.0.0.1\",\n port: 1,\n timeoutMs: 200,\n })\n );\n});\n\ntest(\"postBdsLifecycleEvent\uFF1A2xx \u6210\u529F\", async () => {\n const server = http.createServer((req, res) => {\n let body = \"\";\n req.on(\"data\", (c) => (body += c));\n req.on(\"end\", () => {\n assert.equal(req.method, \"POST\");\n assert.equal(req.url, \"/api/sfmc/qq/events\");\n const parsed = JSON.parse(body) as { type: string; detail?: string };\n assert.equal(parsed.type, \"crash\");\n assert.equal(parsed.detail, \"code=1\");\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ success: true }));\n });\n });\n await new Promise<void>((resolve) => server.listen(0, \"127.0.0.1\", resolve));\n const addr = server.address();\n assert.ok(addr && typeof addr === \"object\");\n try {\n await postBdsLifecycleEvent(\"crash\", \"code=1\", {\n host: \"127.0.0.1\",\n port: addr.port,\n timeoutMs: 2000,\n });\n } finally {\n await new Promise<void>((resolve, reject) => server.close((e) => (e ? reject(e) : resolve())));\n }\n});\n"],
|
|
5
|
+
"mappings": "AAIA,OAAO,YAAY;AACnB,OAAO,UAAU;AACjB,OAAO,UAAU;AACjB,SAAS,6BAA6B;AAEtC,KAAK,sEAAmC,YAAY;AAClD,QAAM,OAAO;AAAA,IAAc,MACzB,sBAAsB,SAAS,SAAS;AAAA,MACtC,MAAM;AAAA,MACN,MAAM;AAAA,MACN,WAAW;AAAA,IACb,CAAC;AAAA,EACH;AACF,CAAC;AAED,KAAK,+CAAgC,YAAY;AAC/C,QAAM,SAAS,KAAK,aAAa,CAAC,KAAK,QAAQ;AAC7C,QAAI,OAAO;AACX,QAAI,GAAG,QAAQ,CAAC,MAAO,QAAQ,CAAE;AACjC,QAAI,GAAG,OAAO,MAAM;AAClB,aAAO,MAAM,IAAI,QAAQ,MAAM;AAC/B,aAAO,MAAM,IAAI,KAAK,qBAAqB;AAC3C,YAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,aAAO,MAAM,OAAO,MAAM,OAAO;AACjC,aAAO,MAAM,OAAO,QAAQ,QAAQ;AACpC,UAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,UAAI,IAAI,KAAK,UAAU,EAAE,SAAS,KAAK,CAAC,CAAC;AAAA,IAC3C,CAAC;AAAA,EACH,CAAC;AACD,QAAM,IAAI,QAAc,CAAC,YAAY,OAAO,OAAO,GAAG,aAAa,OAAO,CAAC;AAC3E,QAAM,OAAO,OAAO,QAAQ;AAC5B,SAAO,GAAG,QAAQ,OAAO,SAAS,QAAQ;AAC1C,MAAI;AACF,UAAM,sBAAsB,SAAS,UAAU;AAAA,MAC7C,MAAM;AAAA,MACN,MAAM,KAAK;AAAA,MACX,WAAW;AAAA,IACb,CAAC;AAAA,EACH,UAAE;AACA,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW,OAAO,MAAM,CAAC,MAAO,IAAI,OAAO,CAAC,IAAI,QAAQ,CAAE,CAAC;AAAA,EAC/F;AACF,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/qqutil.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* qqutil.ts — QQ
|
|
2
|
+
* qqutil.ts — QQ 通知工具(官方 Bot / LLBot 双后端)
|
|
3
3
|
*
|
|
4
4
|
* 改进:
|
|
5
5
|
* - sendTimeout 提供总超时,避免通知发送挂死主流程
|
|
6
6
|
* - 静默模式 (失败不抛出),保证主流程不被通知干扰
|
|
7
|
+
* - qq_backend=official 时走 SDK 发群;llbot 仍走 OneBot HTTP
|
|
7
8
|
*/
|
|
8
9
|
/** 检查 qq-bridge 模块是否启用 */
|
|
9
10
|
export declare function isQqBridgeEnabled(): boolean;
|
package/dist/qqutil.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qqutil.d.ts","sourceRoot":"","sources":["../src/qqutil.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"qqutil.d.ts","sourceRoot":"","sources":["../src/qqutil.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAiCH,0BAA0B;AAC1B,wBAAgB,iBAAiB,IAAI,OAAO,CAQ3C;AAgED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB1D;AAED,wBAAsB,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+BlE;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMlF"}
|
package/dist/qqutil.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
modulePath,
|
|
4
4
|
readJson
|
|
5
5
|
} from "@sfmc-bds/sdk/node/config";
|
|
6
|
+
import { sendGroupTextMessage } from "@sfmc-bds/sdk/node/qq-official";
|
|
6
7
|
import http from "node:http";
|
|
7
8
|
import { log } from "./log.js";
|
|
8
9
|
import { ROOT_DIR } from "./paths.js";
|
|
@@ -19,15 +20,18 @@ function isQqBridgeEnabled() {
|
|
|
19
20
|
const mod = catalog.modules?.find((m) => m.id === "qq-bridge" || m.configKey === "qq_bridge");
|
|
20
21
|
return mod ? lock.modules?.[mod.id ?? ""]?.enabled === true : false;
|
|
21
22
|
}
|
|
23
|
+
function isOfficialBackend(cfg) {
|
|
24
|
+
return (cfg.qq_backend ?? "official") === "official";
|
|
25
|
+
}
|
|
22
26
|
function sendToLLBot(payload, timeoutMs = 5e3) {
|
|
23
27
|
const cfg = getConfig();
|
|
24
|
-
const url = new URL(cfg.llbot_http || "http://127.0.0.1:
|
|
28
|
+
const url = new URL(cfg.llbot_http || "http://127.0.0.1:3004");
|
|
25
29
|
const data = JSON.stringify(payload);
|
|
26
30
|
return new Promise((resolve, reject) => {
|
|
27
31
|
const req = http.request(
|
|
28
32
|
{
|
|
29
33
|
hostname: url.hostname,
|
|
30
|
-
port: url.port ||
|
|
34
|
+
port: url.port || 3004,
|
|
31
35
|
path: "/send_group_msg",
|
|
32
36
|
method: "POST",
|
|
33
37
|
headers: {
|
|
@@ -60,14 +64,36 @@ async function safeSend(label, fn) {
|
|
|
60
64
|
log.warn(`[QQ] ${label} \u5931\u8D25: ${e.message}`);
|
|
61
65
|
}
|
|
62
66
|
}
|
|
67
|
+
async function sendOfficialText(text) {
|
|
68
|
+
const cfg = getConfig();
|
|
69
|
+
const appId = String(cfg.qq_app_id ?? "");
|
|
70
|
+
const appSecret = String(cfg.qq_app_secret ?? "");
|
|
71
|
+
const groupOpenid = String(cfg.qq_group_openid ?? "");
|
|
72
|
+
if (!appId || !appSecret || !groupOpenid) {
|
|
73
|
+
throw new Error("\u5B98\u65B9\u540E\u7AEF\u7F3A\u5C11 qq_app_id / qq_app_secret / qq_group_openid");
|
|
74
|
+
}
|
|
75
|
+
const result = await sendGroupTextMessage(
|
|
76
|
+
{ appId, appSecret, sandbox: cfg.qq_sandbox === true },
|
|
77
|
+
{ groupOpenid, content: text }
|
|
78
|
+
);
|
|
79
|
+
if (!result.ok) throw new Error(result.error);
|
|
80
|
+
}
|
|
63
81
|
async function sendText(text) {
|
|
64
82
|
const cfg = getConfig();
|
|
65
|
-
if (!isQqBridgeEnabled()
|
|
66
|
-
log.warn("[QQ] qq-bridge \u672A\u542F\u7528
|
|
83
|
+
if (!isQqBridgeEnabled()) {
|
|
84
|
+
log.warn("[QQ] qq-bridge \u672A\u542F\u7528");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (isOfficialBackend(cfg)) {
|
|
88
|
+
await safeSend("sendText(official)", () => sendOfficialText(text));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (!cfg.qq_group_id) {
|
|
92
|
+
log.warn("[QQ] qq_group_id \u7F3A\u5931");
|
|
67
93
|
return;
|
|
68
94
|
}
|
|
69
95
|
await safeSend(
|
|
70
|
-
"sendText",
|
|
96
|
+
"sendText(llbot)",
|
|
71
97
|
() => sendToLLBot({
|
|
72
98
|
group_id: parseInt(cfg.qq_group_id ?? "0", 10),
|
|
73
99
|
message: [{ type: "text", data: { text } }]
|
|
@@ -76,12 +102,27 @@ async function sendText(text) {
|
|
|
76
102
|
}
|
|
77
103
|
async function sendMixed(segments) {
|
|
78
104
|
const cfg = getConfig();
|
|
79
|
-
if (!isQqBridgeEnabled()
|
|
80
|
-
log.warn("[QQ] qq-bridge \u672A\u542F\u7528
|
|
105
|
+
if (!isQqBridgeEnabled()) {
|
|
106
|
+
log.warn("[QQ] qq-bridge \u672A\u542F\u7528");
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (isOfficialBackend(cfg)) {
|
|
110
|
+
const text = segments.map((seg) => {
|
|
111
|
+
const s = seg;
|
|
112
|
+
if (s?.type === "text") return String(s.data?.text ?? "");
|
|
113
|
+
if (s?.type === "image") return "[\u56FE\u7247]";
|
|
114
|
+
return "";
|
|
115
|
+
}).join("").trim();
|
|
116
|
+
if (!text) return;
|
|
117
|
+
await safeSend("sendMixed(official)", () => sendOfficialText(text));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (!cfg.qq_group_id) {
|
|
121
|
+
log.warn("[QQ] qq_group_id \u7F3A\u5931");
|
|
81
122
|
return;
|
|
82
123
|
}
|
|
83
124
|
await safeSend(
|
|
84
|
-
"sendMixed",
|
|
125
|
+
"sendMixed(llbot)",
|
|
85
126
|
() => sendToLLBot({
|
|
86
127
|
group_id: parseInt(cfg.qq_group_id ?? "0", 10),
|
|
87
128
|
message: segments
|
package/dist/qqutil.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/qqutil.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * qqutil.ts \u2014 QQ \u901A\u77E5\u5DE5\u5177
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/**\r\n * qqutil.ts \u2014 QQ \u901A\u77E5\u5DE5\u5177\uFF08\u5B98\u65B9 Bot / LLBot \u53CC\u540E\u7AEF\uFF09\r\n *\r\n * \u6539\u8FDB:\r\n * - sendTimeout \u63D0\u4F9B\u603B\u8D85\u65F6\uFF0C\u907F\u514D\u901A\u77E5\u53D1\u9001\u6302\u6B7B\u4E3B\u6D41\u7A0B\r\n * - \u9759\u9ED8\u6A21\u5F0F (\u5931\u8D25\u4E0D\u629B\u51FA)\uFF0C\u4FDD\u8BC1\u4E3B\u6D41\u7A0B\u4E0D\u88AB\u901A\u77E5\u5E72\u6270\r\n * - qq_backend=official \u65F6\u8D70 SDK \u53D1\u7FA4\uFF1Bllbot \u4ECD\u8D70 OneBot HTTP\r\n */\r\n\r\nimport {\r\n configPath,\r\n modulePath,\r\n readJson,\r\n type Catalog,\r\n type ModuleLock,\r\n type QQBridgeConfig,\r\n} from \"@sfmc-bds/sdk/node/config\";\r\nimport { sendGroupTextMessage } from \"@sfmc-bds/sdk/node/qq-official\";\r\nimport http from \"node:http\";\r\nimport { log } from \"./log.js\";\r\nimport { ROOT_DIR } from \"./paths.js\";\r\n\r\ntype QqConfig = Pick<\r\n QQBridgeConfig,\r\n | \"llbot_http\"\r\n | \"qq_group_id\"\r\n | \"qq_backend\"\r\n | \"qq_app_id\"\r\n | \"qq_app_secret\"\r\n | \"qq_sandbox\"\r\n | \"qq_group_openid\"\r\n>;\r\n\r\nlet cachedCfg: QqConfig | null = null;\r\nfunction getConfig(): QqConfig {\r\n if (cachedCfg) return cachedCfg;\r\n cachedCfg = (readJson<QQBridgeConfig>(configPath(ROOT_DIR, \"qq_config.json\")) ?? {}) as QqConfig;\r\n return cachedCfg;\r\n}\r\n\r\n/** \u68C0\u67E5 qq-bridge \u6A21\u5757\u662F\u5426\u542F\u7528 */\r\nexport function isQqBridgeEnabled(): boolean {\r\n const catalog = readJson<Catalog>(modulePath(ROOT_DIR, \"catalog.json\"));\r\n const lock = readJson<ModuleLock>(modulePath(ROOT_DIR, \"module-lock.json\"));\r\n if (!catalog || !lock) return true; // \u6A21\u5757\u76EE\u5F55\u7F3A\u5931\u5219\u4FDD\u5B88\u89C6\u4E3A\u53EF\u7528\r\n const mod = catalog.modules?.find((m) => m.id === \"qq-bridge\" || m.configKey === \"qq_bridge\") as\r\n | { id?: string }\r\n | undefined;\r\n return mod ? lock.modules?.[mod.id ?? \"\"]?.enabled === true : false;\r\n}\r\n\r\nfunction isOfficialBackend(cfg: QqConfig): boolean {\r\n return (cfg.qq_backend ?? \"official\") === \"official\";\r\n}\r\n\r\nfunction sendToLLBot(payload: unknown, timeoutMs = 5_000): Promise<void> {\r\n const cfg = getConfig();\r\n const url = new URL(cfg.llbot_http || \"http://127.0.0.1:3004\");\r\n const data = JSON.stringify(payload);\r\n return new Promise((resolve, reject) => {\r\n const req = http.request(\r\n {\r\n hostname: url.hostname,\r\n port: url.port || 3004,\r\n path: \"/send_group_msg\",\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n \"Content-Length\": Buffer.byteLength(data),\r\n },\r\n timeout: timeoutMs,\r\n },\r\n (res) => {\r\n let body = \"\";\r\n res.on(\"data\", (c) => (body += c));\r\n res.on(\"end\", () => {\r\n if (res.statusCode === 200) resolve();\r\n else reject(new Error(`LLBot HTTP ${res.statusCode}: ${body.slice(0, 200)}`));\r\n });\r\n }\r\n );\r\n req.on(\"timeout\", () => {\r\n req.destroy(new Error(`LLBot \u8D85\u65F6 ${timeoutMs}ms`));\r\n });\r\n req.on(\"error\", reject);\r\n req.write(data);\r\n req.end();\r\n });\r\n}\r\n\r\nasync function safeSend(label: string, fn: () => Promise<void>): Promise<void> {\r\n try {\r\n await fn();\r\n } catch (e) {\r\n log.warn(`[QQ] ${label} \u5931\u8D25: ${(e as Error).message}`);\r\n }\r\n}\r\n\r\nasync function sendOfficialText(text: string): Promise<void> {\r\n const cfg = getConfig();\r\n const appId = String(cfg.qq_app_id ?? \"\");\r\n const appSecret = String(cfg.qq_app_secret ?? \"\");\r\n const groupOpenid = String(cfg.qq_group_openid ?? \"\");\r\n if (!appId || !appSecret || !groupOpenid) {\r\n throw new Error(\"\u5B98\u65B9\u540E\u7AEF\u7F3A\u5C11 qq_app_id / qq_app_secret / qq_group_openid\");\r\n }\r\n const result = await sendGroupTextMessage(\r\n { appId, appSecret, sandbox: cfg.qq_sandbox === true },\r\n { groupOpenid, content: text }\r\n );\r\n if (!result.ok) throw new Error(result.error);\r\n}\r\n\r\nexport async function sendText(text: string): Promise<void> {\r\n const cfg = getConfig();\r\n if (!isQqBridgeEnabled()) {\r\n log.warn(\"[QQ] qq-bridge \u672A\u542F\u7528\");\r\n return;\r\n }\r\n if (isOfficialBackend(cfg)) {\r\n await safeSend(\"sendText(official)\", () => sendOfficialText(text));\r\n return;\r\n }\r\n if (!cfg.qq_group_id) {\r\n log.warn(\"[QQ] qq_group_id \u7F3A\u5931\");\r\n return;\r\n }\r\n await safeSend(\"sendText(llbot)\", () =>\r\n sendToLLBot({\r\n group_id: parseInt(cfg.qq_group_id ?? \"0\", 10),\r\n message: [{ type: \"text\", data: { text } }],\r\n })\r\n );\r\n}\r\n\r\nexport async function sendMixed(segments: unknown[]): Promise<void> {\r\n const cfg = getConfig();\r\n if (!isQqBridgeEnabled()) {\r\n log.warn(\"[QQ] qq-bridge \u672A\u542F\u7528\");\r\n return;\r\n }\r\n if (isOfficialBackend(cfg)) {\r\n // \u5B98\u65B9\u8DEF\u5F84\u4EC5\u62FC\u7EAF\u6587\u672C\uFF1B\u56FE\u7247\u6BB5\u964D\u7EA7\u4E3A [\u56FE\u7247]\r\n const text = segments\r\n .map((seg) => {\r\n const s = seg as { type?: string; data?: { text?: string } };\r\n if (s?.type === \"text\") return String(s.data?.text ?? \"\");\r\n if (s?.type === \"image\") return \"[\u56FE\u7247]\";\r\n return \"\";\r\n })\r\n .join(\"\")\r\n .trim();\r\n if (!text) return;\r\n await safeSend(\"sendMixed(official)\", () => sendOfficialText(text));\r\n return;\r\n }\r\n if (!cfg.qq_group_id) {\r\n log.warn(\"[QQ] qq_group_id \u7F3A\u5931\");\r\n return;\r\n }\r\n await safeSend(\"sendMixed(llbot)\", () =>\r\n sendToLLBot({\r\n group_id: parseInt(cfg.qq_group_id ?? \"0\", 10),\r\n message: segments,\r\n })\r\n );\r\n}\r\n\r\nexport async function sendWithImage(text: string, base64Img: string): Promise<void> {\r\n const segments: unknown[] = [];\r\n if (text) segments.push({ type: \"text\", data: { text } });\r\n if (base64Img) segments.push({ type: \"image\", data: { file: `base64://${base64Img}` } });\r\n if (segments.length === 0) return;\r\n await sendMixed(segments);\r\n}\r\n\r"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/upstream.d.ts
CHANGED
|
@@ -6,13 +6,14 @@
|
|
|
6
6
|
* - sha1 / sha256 同时校验
|
|
7
7
|
* - 流式校验 + 边下边校验
|
|
8
8
|
*/
|
|
9
|
+
import { type BdsHostOs } from "./host-platform.js";
|
|
9
10
|
import type { BdsUpdaterConfig, VersionDetails, VersionInfo } from "./types.js";
|
|
10
11
|
/** 获取最新版本号 (含 cdn_root),3 次重试 */
|
|
11
|
-
export declare function getVersionInfo(cfg: BdsUpdaterConfig, channel: string): Promise<VersionInfo>;
|
|
12
|
+
export declare function getVersionInfo(cfg: BdsUpdaterConfig, channel: string, hostOs?: BdsHostOs): Promise<VersionInfo>;
|
|
12
13
|
/** 获取 per-version 的下载链接 + 哈希 (fallback GitHub → jsdelivr) */
|
|
13
|
-
export declare function fetchVersionDetails(cfg: BdsUpdaterConfig, channel: string, version: string): Promise<VersionDetails>;
|
|
14
|
+
export declare function fetchVersionDetails(cfg: BdsUpdaterConfig, channel: string, version: string, hostOs?: BdsHostOs): Promise<VersionDetails>;
|
|
14
15
|
/** 候选下载 URL (按优先级) */
|
|
15
|
-
export declare function buildDownloadUrls(cfg: BdsUpdaterConfig, channel: string, version: string, details: VersionDetails): string[];
|
|
16
|
+
export declare function buildDownloadUrls(cfg: BdsUpdaterConfig, channel: string, version: string, details: VersionDetails, hostOs?: BdsHostOs): string[];
|
|
16
17
|
/** 同步 / 异步校验文件哈希 */
|
|
17
18
|
export declare function verifyFileHash(filePath: string, expectedSha1: string, expectedSha256: string): Promise<boolean>;
|
|
18
19
|
/** 检查版本兼容性 (白名单) */
|
package/dist/upstream.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upstream.d.ts","sourceRoot":"","sources":["../src/upstream.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"upstream.d.ts","sourceRoot":"","sources":["../src/upstream.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAqD,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAGvG,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAchF,iCAAiC;AACjC,wBAAsB,cAAc,CAClC,GAAG,EAAE,gBAAgB,EACrB,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,SAAuB,GAC9B,OAAO,CAAC,WAAW,CAAC,CA8BtB;AAED,6DAA6D;AAC7D,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,gBAAgB,EACrB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,SAAuB,GAC9B,OAAO,CAAC,cAAc,CAAC,CA2BzB;AAED,sBAAsB;AACtB,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,gBAAgB,EACrB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,cAAc,EACvB,MAAM,GAAE,SAAuB,GAC9B,MAAM,EAAE,CAYV;AAED,oBAAoB;AACpB,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAiBrH;AAED,oBAAoB;AACpB,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAInF"}
|
package/dist/upstream.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
+
import { bdsCdnZipSuffix, bdsDetailsPlatformKey, bdsHostOs } from "./host-platform.js";
|
|
3
4
|
import { fetchJsonWithFallback } from "./http.js";
|
|
4
5
|
import { log } from "./log.js";
|
|
5
6
|
import { toVer3 } from "./version.js";
|
|
@@ -12,7 +13,7 @@ function resolveTemplate(tpl, vars) {
|
|
|
12
13
|
}
|
|
13
14
|
return s;
|
|
14
15
|
}
|
|
15
|
-
async function getVersionInfo(cfg, channel) {
|
|
16
|
+
async function getVersionInfo(cfg, channel, hostOs = bdsHostOs()) {
|
|
16
17
|
const api = cfg.version_versions || process.env["BDS_VERSIONS_API"] || DEFAULT_VERSIONS_API;
|
|
17
18
|
const mirror = cfg.version_versions_mirror || process.env["BDS_VERSIONS_MIRROR"] || DEFAULT_VERSIONS_MIRROR;
|
|
18
19
|
const sources = [api, mirror].filter(Boolean);
|
|
@@ -26,7 +27,7 @@ async function getVersionInfo(cfg, channel) {
|
|
|
26
27
|
const entry = channel === "preview" ? json.preview : json.release;
|
|
27
28
|
ver = entry?.latest;
|
|
28
29
|
} else {
|
|
29
|
-
const platform = json.windows || json.linux;
|
|
30
|
+
const platform = hostOs === "windows" ? json.windows || json.linux : json.linux || json.windows;
|
|
30
31
|
ver = channel === "preview" ? platform?.preview : platform?.stable;
|
|
31
32
|
}
|
|
32
33
|
if (!ver) throw new Error("\u672A\u627E\u5230\u6700\u65B0\u7248\u672C\u53F7");
|
|
@@ -42,8 +43,8 @@ async function getVersionInfo(cfg, channel) {
|
|
|
42
43
|
}
|
|
43
44
|
throw lastErr instanceof Error ? lastErr : new Error("\u7248\u672C\u53F7\u83B7\u53D6\u5931\u8D25");
|
|
44
45
|
}
|
|
45
|
-
async function fetchVersionDetails(cfg, channel, version) {
|
|
46
|
-
const platform = channel
|
|
46
|
+
async function fetchVersionDetails(cfg, channel, version, hostOs = bdsHostOs()) {
|
|
47
|
+
const platform = bdsDetailsPlatformKey(channel, hostOs);
|
|
47
48
|
const ch = channel === "preview" ? "preview" : "release";
|
|
48
49
|
const vars = { version, ver3: toVer3(version), platform, channel: ch };
|
|
49
50
|
const detailsTpl = cfg.version_details || "https://raw.githubusercontent.com/Bedrock-OSS/BDS-Versions/main/{platform}/{version}.json";
|
|
@@ -52,7 +53,7 @@ async function fetchVersionDetails(cfg, channel, version) {
|
|
|
52
53
|
const json = await fetchJsonWithFallback(sources);
|
|
53
54
|
const mode = cfg.version_mode || "bedrock-oss";
|
|
54
55
|
if (mode === "endstone") {
|
|
55
|
-
const bw = json.binary?.windows;
|
|
56
|
+
const bw = hostOs === "windows" ? json.binary?.windows : json.binary?.linux;
|
|
56
57
|
return {
|
|
57
58
|
downloadUrl: bw?.url ?? "",
|
|
58
59
|
sha1: "",
|
|
@@ -67,7 +68,7 @@ async function fetchVersionDetails(cfg, channel, version) {
|
|
|
67
68
|
size: json.size_in_bytes ?? 0
|
|
68
69
|
};
|
|
69
70
|
}
|
|
70
|
-
function buildDownloadUrls(cfg, channel, version, details) {
|
|
71
|
+
function buildDownloadUrls(cfg, channel, version, details, hostOs = bdsHostOs()) {
|
|
71
72
|
const urls = [];
|
|
72
73
|
if (cfg.download_mirror) {
|
|
73
74
|
const mirrorVer = channel === "preview" ? `${version}-preview` : version;
|
|
@@ -75,7 +76,7 @@ function buildDownloadUrls(cfg, channel, version, details) {
|
|
|
75
76
|
}
|
|
76
77
|
if (details.downloadUrl) urls.push(details.downloadUrl);
|
|
77
78
|
const cdnRoot = cfg.cdn_root || "https://www.minecraft.net/bedrockdedicatedserver";
|
|
78
|
-
const suffix = channel
|
|
79
|
+
const suffix = bdsCdnZipSuffix(channel, version, hostOs);
|
|
79
80
|
urls.push(cdnRoot + suffix);
|
|
80
81
|
urls.push(`https://minecraft.azureedge.net${suffix}`);
|
|
81
82
|
return [...new Set(urls)];
|