mioku 1.0.6 → 1.0.8
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/cli/index.cjs +3 -2
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +3 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/{exec-p6Vp158F.js → exec-3C9aJ9OX.js} +2 -2
- package/dist/{exec-p6Vp158F.js.map → exec-3C9aJ9OX.js.map} +1 -1
- package/dist/{exec-O8rCedzr.cjs → exec-BzdYBQLQ.cjs} +2 -2
- package/dist/{exec-O8rCedzr.cjs.map → exec-BzdYBQLQ.cjs.map} +1 -1
- package/dist/index.cjs +363 -105
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +86 -13
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +86 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +364 -107
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exec-O8rCedzr.cjs","names":["path","candidate: string","command: string","resolved: string | null","resolved: string","args: string[]","options: { cwd?: string; env?: NodeJS.ProcessEnv }","options: { cwd?: string }"],"sources":["../package.json","../src/internal/exec.ts"],"sourcesContent":["{\n \"name\": \"mioku\",\n \"type\": \"module\",\n \"version\": \"1.0.6\",\n \"packageManager\": \"bun@1.2.0\",\n \"description\": \"Mioku - A plugin-based QQ bot framework\",\n \"keywords\": [\n \"onebot\",\n \"framework\",\n \"bot\",\n \"mioku\"\n ],\n \"bin\": {\n \"mioku\": \"./dist/cli/index.js\"\n },\n \"engines\": {\n \"node\": \">= 22.18.0\"\n },\n \"homepage\": \"https://github.com/mioku-lab/mioku#readme\",\n \"files\": [\n \"dist\"\n ],\n \"bugs\": {\n \"url\": \"https://github.com/mioku-lab/mioku/issues\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/mioku-lab/mioku.git\",\n \"directory\": \"packages/mioku\"\n },\n \"scripts\": {\n \"dev\": \"tsdown -w\",\n \"build\": \"tsdown\",\n \"typecheck\": \"tsc --noEmit\"\n },\n \"exports\": {\n \".\": {\n \"require\": \"./dist/index.cjs\",\n \"import\": \"./dist/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"author\": \"Jerryplusy <jerryplusy@outlook.com>\",\n \"license\": \"MIT\",\n \"dependencies\": {\n \"napcat-sdk\": \"^0.16.0\",\n \"consola\": \"^3.4.2\",\n \"dayjs\": \"^1.11.19\",\n \"dedent\": \"^1.7.1\",\n \"filesize\": \"^11.0.13\",\n \"inquirer\": \"^10.0.0\",\n \"jiti\": \"^2.6.1\",\n \"lowdb\": \"^7.0.1\",\n \"mri\": \"^1.2.0\",\n \"node-cron\": \"^4.2.1\",\n \"pretty-ms\": \"^9.3.0\",\n \"string2argv\": \"^1.0.2\",\n \"lodash\": \"^4.17.21\"\n },\n \"devDependencies\": {\n \"@types/lodash\": \"^4.17.0\",\n \"@types/node\": \"^22.0.0\",\n \"tsdown\": \"^0.11.0\",\n \"typescript\": \"^5.8.0\"\n }\n}\n","import { spawn, spawnSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\n\nconst isWindows = process.platform === \"win32\";\n\nconst resolveCache = new Map<string, string | null>();\n\nfunction windowsExtensions(): string[] {\n const raw = process.env.PATHEXT || \".COM;.EXE;.BAT;.CMD\";\n return raw\n .split(\";\")\n .map((ext) => ext.trim())\n .filter(Boolean);\n}\n\n// PATH is a snapshot taken at process start, so a package manager installed\n// mid-run won't be on it. We enumerate every plausible install prefix for\n// bun / npm / scoop / chocolatey so the CLI can find bun in one shot.\nfunction searchDirs(): string[] {\n const raw = process.env.PATH || process.env.Path || \"\";\n const dirs = raw\n .split(path.delimiter)\n .map((d) => d.trim())\n .filter(Boolean);\n const home = os.homedir();\n const appData = process.env.APPDATA || path.join(home, \"AppData\", \"Roaming\");\n const localAppData =\n process.env.LOCALAPPDATA || path.join(home, \"AppData\", \"Local\");\n const programFiles = process.env.ProgramFiles || \"C:\\\\Program Files\";\n const programFilesX86 =\n process.env[\"ProgramFiles(x86)\"] || \"C:\\\\Program Files (x86)\";\n\n const extra = isWindows\n ? [\n // 官方安装器\n path.join(home, \".bun\", \"bin\"),\n // npm 全局(带 .cmd shim)\n path.join(appData, \"npm\"),\n path.join(appData, \"npm\", \"node_modules\", \".bin\"),\n // 备用 bun 位置\n path.join(localAppData, \"bun\", \"bin\"),\n path.join(programFiles, \"bun\", \"bin\"),\n path.join(programFilesX86, \"bun\", \"bin\"),\n // scoop\n path.join(home, \"scoop\", \"shims\"),\n // chocolatey\n \"C:\\\\ProgramData\\\\chocolatey\\\\bin\",\n // node 自带(很多用户 npm i -g 装到 node 目录)\n path.join(programFiles, \"nodejs\"),\n path.join(programFilesX86, \"nodejs\"),\n ]\n : [\n path.join(home, \".bun\", \"bin\"),\n \"/usr/local/bin\",\n \"/opt/homebrew/bin\",\n path.join(home, \".npm-global\", \"bin\"),\n ];\n\n return [...dirs, ...extra.filter((dir) => dir && !dir.endsWith(path.sep))];\n}\n\nfunction isExecutableFile(candidate: string): boolean {\n try {\n return fs.statSync(candidate).isFile();\n } catch {\n return false;\n }\n}\n\nfunction searchCommandViaShell(command: string): string | null {\n if (command.includes(\"/\") || command.includes(path.sep)) return null;\n const shellCmd = isWindows ? \"where\" : \"which\";\n try {\n const result = spawnSync(shellCmd, [command], {\n encoding: \"utf-8\",\n windowsHide: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n timeout: 5000,\n });\n if (result.status === 0 && result.stdout) {\n const first = result.stdout\n .split(/\\r?\\n/)\n .map((s) => s.trim())\n .find(Boolean);\n if (first && isExecutableFile(first)) return first;\n }\n } catch {}\n return null;\n}\n\n/** 在 PATH 及常见安装目录中解析命令的完整路径,结果带缓存 */\nexport function resolveCommand(command: string): string | null {\n if (resolveCache.has(command)) return resolveCache.get(command) ?? null;\n\n let resolved: string | null = null;\n\n if (command.includes(\"/\") || command.includes(path.sep)) {\n resolved = isExecutableFile(command) ? path.resolve(command) : null;\n } else {\n const exts = isWindows ? windowsExtensions() : [\"\"];\n const hasKnownExt =\n isWindows &&\n exts.some((ext) => command.toLowerCase().endsWith(ext.toLowerCase()));\n\n outer: for (const dir of searchDirs()) {\n if (hasKnownExt) {\n const direct = path.join(dir, command);\n if (isExecutableFile(direct)) {\n resolved = direct;\n break;\n }\n continue;\n }\n for (const ext of exts) {\n const candidate = path.join(dir, command + ext);\n if (isExecutableFile(candidate)) {\n resolved = candidate;\n break outer;\n }\n }\n }\n\n if (!resolved) resolved = searchCommandViaShell(command);\n }\n\n resolveCache.set(command, resolved);\n return resolved;\n}\n\nexport function clearCommandCache(): void {\n resolveCache.clear();\n}\n\nfunction needsCmdShell(resolved: string): boolean {\n if (!isWindows) return false;\n const ext = path.extname(resolved).toLowerCase();\n return ext === \".cmd\" || ext === \".bat\";\n}\n\nexport interface SpawnPlan {\n file: string;\n args: string[];\n windowsVerbatimArguments?: boolean;\n shell?: boolean | string;\n}\n\n// Windows can't CreateProcess a .cmd/.bat shim directly (npm installs bun as\n// bun.cmd), so those get routed through cmd.exe via shell: true. Previously\n// we hand-rolled a cmd.exe invocation with windowsVerbatimArguments: true,\n// but that combination misbehaves with spawnSync + stdio:\"inherit\" on\n// Node.js >= 20 (manifests as the spawned process printing \"undefined\" or\n// a spurious EINVAL), so we let Node's shell wrapper do the quoting.\n/** 生成跨平台的 spawn 参数,Windows 下 .cmd/.bat 会改经 cmd.exe 执行 */\nexport function buildSpawnPlan(command: string, args: string[]): SpawnPlan {\n const resolved = resolveCommand(command);\n\n if (!resolved) {\n return { file: command, args };\n }\n\n if (!needsCmdShell(resolved)) {\n return { file: resolved, args };\n }\n\n return {\n file: resolved,\n args,\n shell: true,\n };\n}\n\nexport interface RunCommandResult {\n stdout: string;\n stderr: string;\n code: number;\n}\n\n/** 执行命令并收集 stdout/stderr 与退出码,失败不抛异常 */\nexport function runCommand(\n command: string,\n args: string[],\n options: { cwd?: string; env?: NodeJS.ProcessEnv } = {},\n): Promise<RunCommandResult> {\n const plan = buildSpawnPlan(command, args);\n\n return new Promise((resolve) => {\n const child = spawn(plan.file, plan.args, {\n cwd: options.cwd,\n env: options.env,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n shell: plan.shell,\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n child.stdout?.on(\"data\", (chunk) => {\n stdout += String(chunk);\n });\n child.stderr?.on(\"data\", (chunk) => {\n stderr += String(chunk);\n });\n\n child.on(\"close\", (code) => {\n resolve({ stdout, stderr, code: code ?? 1 });\n });\n child.on(\"error\", (error) => {\n resolve({\n stdout,\n stderr: `${stderr}\\n${error.message}`.trim(),\n code: 1,\n });\n });\n });\n}\n\n/** 同步执行命令并继承当前终端的输入输出,非零退出码时抛错 */\nexport function runCommandInherit(\n command: string,\n args: string[],\n options: { cwd?: string } = {},\n): void {\n const plan = buildSpawnPlan(command, args);\n const result = spawnSync(plan.file, plan.args, {\n cwd: options.cwd,\n stdio: \"inherit\",\n shell: plan.shell,\n });\n\n if (result.error) throw result.error;\n if (result.status !== 0) {\n throw new Error(`${command} 退出码 ${result.status}`);\n }\n}\n\n/** 判断命令是否可用 */\nexport function commandExists(command: string): boolean {\n return resolveCommand(command) !== null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"exec-BzdYBQLQ.cjs","names":["path","candidate: string","command: string","resolved: string | null","resolved: string","args: string[]","options: { cwd?: string; env?: NodeJS.ProcessEnv }","options: { cwd?: string }"],"sources":["../package.json","../src/internal/exec.ts"],"sourcesContent":["{\n \"name\": \"mioku\",\n \"type\": \"module\",\n \"version\": \"1.0.8\",\n \"packageManager\": \"bun@1.2.0\",\n \"description\": \"Mioku - A plugin-based QQ bot framework\",\n \"keywords\": [\n \"onebot\",\n \"framework\",\n \"bot\",\n \"mioku\"\n ],\n \"bin\": {\n \"mioku\": \"./dist/cli/index.js\"\n },\n \"engines\": {\n \"node\": \">= 22.18.0\"\n },\n \"homepage\": \"https://github.com/mioku-lab/mioku#readme\",\n \"files\": [\n \"dist\"\n ],\n \"bugs\": {\n \"url\": \"https://github.com/mioku-lab/mioku/issues\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/mioku-lab/mioku.git\",\n \"directory\": \"packages/mioku\"\n },\n \"scripts\": {\n \"dev\": \"tsdown -w\",\n \"build\": \"tsdown\",\n \"typecheck\": \"tsc --noEmit\"\n },\n \"exports\": {\n \".\": {\n \"require\": \"./dist/index.cjs\",\n \"import\": \"./dist/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"author\": \"Jerryplusy <jerryplusy@outlook.com>\",\n \"license\": \"MIT\",\n \"dependencies\": {\n \"napcat-sdk\": \"^0.16.0\",\n \"consola\": \"^3.4.2\",\n \"dayjs\": \"^1.11.19\",\n \"dedent\": \"^1.7.1\",\n \"filesize\": \"^11.0.13\",\n \"inquirer\": \"^10.0.0\",\n \"jiti\": \"^2.6.1\",\n \"lowdb\": \"^7.0.1\",\n \"mri\": \"^1.2.0\",\n \"node-cron\": \"^4.2.1\",\n \"pretty-ms\": \"^9.3.0\",\n \"string2argv\": \"^1.0.2\",\n \"lodash\": \"^4.17.21\"\n },\n \"devDependencies\": {\n \"@types/lodash\": \"^4.17.0\",\n \"@types/node\": \"^22.0.0\",\n \"tsdown\": \"^0.11.0\",\n \"typescript\": \"^5.8.0\"\n }\n}\n","import { spawn, spawnSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\n\nconst isWindows = process.platform === \"win32\";\n\nconst resolveCache = new Map<string, string | null>();\n\nfunction windowsExtensions(): string[] {\n const raw = process.env.PATHEXT || \".COM;.EXE;.BAT;.CMD\";\n return raw\n .split(\";\")\n .map((ext) => ext.trim())\n .filter(Boolean);\n}\n\n// PATH is a snapshot taken at process start, so a package manager installed\n// mid-run won't be on it. We enumerate every plausible install prefix for\n// bun / npm / scoop / chocolatey so the CLI can find bun in one shot.\nfunction searchDirs(): string[] {\n const raw = process.env.PATH || process.env.Path || \"\";\n const dirs = raw\n .split(path.delimiter)\n .map((d) => d.trim())\n .filter(Boolean);\n const home = os.homedir();\n const appData = process.env.APPDATA || path.join(home, \"AppData\", \"Roaming\");\n const localAppData =\n process.env.LOCALAPPDATA || path.join(home, \"AppData\", \"Local\");\n const programFiles = process.env.ProgramFiles || \"C:\\\\Program Files\";\n const programFilesX86 =\n process.env[\"ProgramFiles(x86)\"] || \"C:\\\\Program Files (x86)\";\n\n const extra = isWindows\n ? [\n // 官方安装器\n path.join(home, \".bun\", \"bin\"),\n // npm 全局(带 .cmd shim)\n path.join(appData, \"npm\"),\n path.join(appData, \"npm\", \"node_modules\", \".bin\"),\n // 备用 bun 位置\n path.join(localAppData, \"bun\", \"bin\"),\n path.join(programFiles, \"bun\", \"bin\"),\n path.join(programFilesX86, \"bun\", \"bin\"),\n // scoop\n path.join(home, \"scoop\", \"shims\"),\n // chocolatey\n \"C:\\\\ProgramData\\\\chocolatey\\\\bin\",\n // node 自带(很多用户 npm i -g 装到 node 目录)\n path.join(programFiles, \"nodejs\"),\n path.join(programFilesX86, \"nodejs\"),\n ]\n : [\n path.join(home, \".bun\", \"bin\"),\n \"/usr/local/bin\",\n \"/opt/homebrew/bin\",\n path.join(home, \".npm-global\", \"bin\"),\n ];\n\n return [...dirs, ...extra.filter((dir) => dir && !dir.endsWith(path.sep))];\n}\n\nfunction isExecutableFile(candidate: string): boolean {\n try {\n return fs.statSync(candidate).isFile();\n } catch {\n return false;\n }\n}\n\nfunction searchCommandViaShell(command: string): string | null {\n if (command.includes(\"/\") || command.includes(path.sep)) return null;\n const shellCmd = isWindows ? \"where\" : \"which\";\n try {\n const result = spawnSync(shellCmd, [command], {\n encoding: \"utf-8\",\n windowsHide: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n timeout: 5000,\n });\n if (result.status === 0 && result.stdout) {\n const first = result.stdout\n .split(/\\r?\\n/)\n .map((s) => s.trim())\n .find(Boolean);\n if (first && isExecutableFile(first)) return first;\n }\n } catch {}\n return null;\n}\n\n/** 在 PATH 及常见安装目录中解析命令的完整路径,结果带缓存 */\nexport function resolveCommand(command: string): string | null {\n if (resolveCache.has(command)) return resolveCache.get(command) ?? null;\n\n let resolved: string | null = null;\n\n if (command.includes(\"/\") || command.includes(path.sep)) {\n resolved = isExecutableFile(command) ? path.resolve(command) : null;\n } else {\n const exts = isWindows ? windowsExtensions() : [\"\"];\n const hasKnownExt =\n isWindows &&\n exts.some((ext) => command.toLowerCase().endsWith(ext.toLowerCase()));\n\n outer: for (const dir of searchDirs()) {\n if (hasKnownExt) {\n const direct = path.join(dir, command);\n if (isExecutableFile(direct)) {\n resolved = direct;\n break;\n }\n continue;\n }\n for (const ext of exts) {\n const candidate = path.join(dir, command + ext);\n if (isExecutableFile(candidate)) {\n resolved = candidate;\n break outer;\n }\n }\n }\n\n if (!resolved) resolved = searchCommandViaShell(command);\n }\n\n resolveCache.set(command, resolved);\n return resolved;\n}\n\nexport function clearCommandCache(): void {\n resolveCache.clear();\n}\n\nfunction needsCmdShell(resolved: string): boolean {\n if (!isWindows) return false;\n const ext = path.extname(resolved).toLowerCase();\n return ext === \".cmd\" || ext === \".bat\";\n}\n\nexport interface SpawnPlan {\n file: string;\n args: string[];\n windowsVerbatimArguments?: boolean;\n shell?: boolean | string;\n}\n\n// Windows can't CreateProcess a .cmd/.bat shim directly (npm installs bun as\n// bun.cmd), so those get routed through cmd.exe via shell: true. Previously\n// we hand-rolled a cmd.exe invocation with windowsVerbatimArguments: true,\n// but that combination misbehaves with spawnSync + stdio:\"inherit\" on\n// Node.js >= 20 (manifests as the spawned process printing \"undefined\" or\n// a spurious EINVAL), so we let Node's shell wrapper do the quoting.\n/** 生成跨平台的 spawn 参数,Windows 下 .cmd/.bat 会改经 cmd.exe 执行 */\nexport function buildSpawnPlan(command: string, args: string[]): SpawnPlan {\n const resolved = resolveCommand(command);\n\n if (!resolved) {\n return { file: command, args };\n }\n\n if (!needsCmdShell(resolved)) {\n return { file: resolved, args };\n }\n\n return {\n file: resolved,\n args,\n shell: true,\n };\n}\n\nexport interface RunCommandResult {\n stdout: string;\n stderr: string;\n code: number;\n}\n\n/** 执行命令并收集 stdout/stderr 与退出码,失败不抛异常 */\nexport function runCommand(\n command: string,\n args: string[],\n options: { cwd?: string; env?: NodeJS.ProcessEnv } = {},\n): Promise<RunCommandResult> {\n const plan = buildSpawnPlan(command, args);\n\n return new Promise((resolve) => {\n const child = spawn(plan.file, plan.args, {\n cwd: options.cwd,\n env: options.env,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n shell: plan.shell,\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n child.stdout?.on(\"data\", (chunk) => {\n stdout += String(chunk);\n });\n child.stderr?.on(\"data\", (chunk) => {\n stderr += String(chunk);\n });\n\n child.on(\"close\", (code) => {\n resolve({ stdout, stderr, code: code ?? 1 });\n });\n child.on(\"error\", (error) => {\n resolve({\n stdout,\n stderr: `${stderr}\\n${error.message}`.trim(),\n code: 1,\n });\n });\n });\n}\n\n/** 同步执行命令并继承当前终端的输入输出,非零退出码时抛错 */\nexport function runCommandInherit(\n command: string,\n args: string[],\n options: { cwd?: string } = {},\n): void {\n const plan = buildSpawnPlan(command, args);\n const result = spawnSync(plan.file, plan.args, {\n cwd: options.cwd,\n stdio: \"inherit\",\n shell: plan.shell,\n });\n\n if (result.error) throw result.error;\n if (result.status !== 0) {\n throw new Error(`${command} 退出码 ${result.status}`);\n }\n}\n\n/** 判断命令是否可用 */\nexport function commandExists(command: string): boolean {\n return resolveCommand(command) !== null;\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_exec = require('./exec-
|
|
1
|
+
const require_exec = require('./exec-BzdYBQLQ.cjs');
|
|
2
2
|
const node_fs = require_exec.__toESM(require("node:fs"));
|
|
3
3
|
const node_path = require_exec.__toESM(require("node:path"));
|
|
4
4
|
const node_url = require_exec.__toESM(require("node:url"));
|
|
@@ -855,6 +855,7 @@ var AdapterContextImpl = class {
|
|
|
855
855
|
#capabilities;
|
|
856
856
|
#logger;
|
|
857
857
|
#emit;
|
|
858
|
+
#correlator;
|
|
858
859
|
#pendingStarts = new Set();
|
|
859
860
|
constructor(options) {
|
|
860
861
|
this.#state = options.state;
|
|
@@ -864,6 +865,7 @@ var AdapterContextImpl = class {
|
|
|
864
865
|
this.#capabilities = options.capabilities;
|
|
865
866
|
this.#logger = options.logger;
|
|
866
867
|
this.#emit = options.emit;
|
|
868
|
+
this.#correlator = options.correlator;
|
|
867
869
|
}
|
|
868
870
|
registerBot(bot) {
|
|
869
871
|
return this.#bots.register(bot);
|
|
@@ -927,6 +929,7 @@ var AdapterContextImpl = class {
|
|
|
927
929
|
};
|
|
928
930
|
}
|
|
929
931
|
async dispatch(event) {
|
|
932
|
+
this.#correlator?.observe(event);
|
|
930
933
|
if (event.kind === "message") {
|
|
931
934
|
const senderId = event.user_id;
|
|
932
935
|
if (senderId != null) {
|
|
@@ -1015,6 +1018,9 @@ var BotRegistry = class {
|
|
|
1015
1018
|
for (const entry of this.#bots.values()) if (entry.bot_id === key) return entry.bot;
|
|
1016
1019
|
return void 0;
|
|
1017
1020
|
}
|
|
1021
|
+
find(adapter, bot_id) {
|
|
1022
|
+
return this.#bots.get(connectedBotKey(adapter, bot_id))?.bot;
|
|
1023
|
+
}
|
|
1018
1024
|
all() {
|
|
1019
1025
|
return Array.from(this.#bots.values()).map((entry) => entry.bot);
|
|
1020
1026
|
}
|
|
@@ -1888,104 +1894,6 @@ const Services = {
|
|
|
1888
1894
|
Help: defineService("help")
|
|
1889
1895
|
};
|
|
1890
1896
|
|
|
1891
|
-
//#endregion
|
|
1892
|
-
//#region src/runtime/cross-adapter-dedup.ts
|
|
1893
|
-
const PRUNE_INTERVAL = 128;
|
|
1894
|
-
const MESSAGE_TTL_MS = 15e3;
|
|
1895
|
-
const EVENT_TTL_MS = 6e4;
|
|
1896
|
-
const MAX_SIZE = 4096;
|
|
1897
|
-
const MAX_SEGMENTS = 16;
|
|
1898
|
-
const MAX_TEXT = 256;
|
|
1899
|
-
const contentFingerprintOf = (event) => {
|
|
1900
|
-
const parts = [];
|
|
1901
|
-
for (const seg of event.message) {
|
|
1902
|
-
if (parts.length >= MAX_SEGMENTS) break;
|
|
1903
|
-
if (seg.type === "reply") continue;
|
|
1904
|
-
const data = seg.data ?? {};
|
|
1905
|
-
if (seg.type === "text") {
|
|
1906
|
-
const text$1 = typeof data.text === "string" ? data.text : "";
|
|
1907
|
-
parts.push(`t:${text$1.slice(0, MAX_TEXT)}`);
|
|
1908
|
-
} else if (seg.type === "at") {
|
|
1909
|
-
const target = data.qq ?? data.target;
|
|
1910
|
-
parts.push(`a:${target == null ? "" : String(target)}`);
|
|
1911
|
-
} else if (seg.type === "face") parts.push("f");
|
|
1912
|
-
else parts.push(seg.type);
|
|
1913
|
-
}
|
|
1914
|
-
return parts.join("|");
|
|
1915
|
-
};
|
|
1916
|
-
const scopeKeyOf = (event, content) => [
|
|
1917
|
-
event.message_type ?? "",
|
|
1918
|
-
event.sender?.nickname?.trim() || event.user_id || "",
|
|
1919
|
-
content
|
|
1920
|
-
].join("|");
|
|
1921
|
-
const noticeKeyOf = (event) => [
|
|
1922
|
-
event.kind,
|
|
1923
|
-
event.identity.event_type,
|
|
1924
|
-
event.notice_type ?? "",
|
|
1925
|
-
event.sub_type ?? "",
|
|
1926
|
-
event.group_id ?? "",
|
|
1927
|
-
event.user_id ?? "",
|
|
1928
|
-
event.operator_id ?? "",
|
|
1929
|
-
event.identity.fingerprint ?? "",
|
|
1930
|
-
event.identity.timestamp == null ? "" : Math.floor(event.identity.timestamp / 1e3)
|
|
1931
|
-
].join("|");
|
|
1932
|
-
const requestKeyOf = (event) => [
|
|
1933
|
-
event.kind,
|
|
1934
|
-
event.identity.event_type,
|
|
1935
|
-
event.request_type ?? "",
|
|
1936
|
-
event.sub_type ?? "",
|
|
1937
|
-
event.group_id ?? "",
|
|
1938
|
-
event.user_id ?? "",
|
|
1939
|
-
event.comment ?? "",
|
|
1940
|
-
event.identity.fingerprint ?? "",
|
|
1941
|
-
event.identity.timestamp == null ? "" : Math.floor(event.identity.timestamp / 1e3)
|
|
1942
|
-
].join("|");
|
|
1943
|
-
/**
|
|
1944
|
-
* 跨适配器消息去重(L2)
|
|
1945
|
-
*/
|
|
1946
|
-
var CrossAdapterEventDeduplicator = class {
|
|
1947
|
-
#entries = new Map();
|
|
1948
|
-
#inserts = 0;
|
|
1949
|
-
#dropped = 0;
|
|
1950
|
-
/** 本次运行以来被丢弃的跨适配器重复消息数 */
|
|
1951
|
-
get dropped() {
|
|
1952
|
-
return this.#dropped;
|
|
1953
|
-
}
|
|
1954
|
-
isDuplicate(event) {
|
|
1955
|
-
let key;
|
|
1956
|
-
let ttl;
|
|
1957
|
-
if (event.kind === "message") {
|
|
1958
|
-
key = scopeKeyOf(event, contentFingerprintOf(event));
|
|
1959
|
-
ttl = MESSAGE_TTL_MS;
|
|
1960
|
-
} else if (event.kind === "notice") {
|
|
1961
|
-
key = noticeKeyOf(event);
|
|
1962
|
-
ttl = EVENT_TTL_MS;
|
|
1963
|
-
} else if (event.kind === "request") {
|
|
1964
|
-
key = requestKeyOf(event);
|
|
1965
|
-
ttl = EVENT_TTL_MS;
|
|
1966
|
-
} else return false;
|
|
1967
|
-
const now = Date.now();
|
|
1968
|
-
const existing = this.#entries.get(key);
|
|
1969
|
-
if (existing?.expiresAt != null && existing.expiresAt >= now) {
|
|
1970
|
-
this.#dropped++;
|
|
1971
|
-
return true;
|
|
1972
|
-
}
|
|
1973
|
-
this.#entries.set(key, { expiresAt: now + ttl });
|
|
1974
|
-
this.#inserts++;
|
|
1975
|
-
if (this.#inserts >= PRUNE_INTERVAL || this.#entries.size > MAX_SIZE) {
|
|
1976
|
-
this.#inserts = 0;
|
|
1977
|
-
this.#prune(now);
|
|
1978
|
-
}
|
|
1979
|
-
return false;
|
|
1980
|
-
}
|
|
1981
|
-
#prune(now) {
|
|
1982
|
-
for (const [key, entry] of this.#entries) if (entry.expiresAt < now) this.#entries.delete(key);
|
|
1983
|
-
}
|
|
1984
|
-
clear() {
|
|
1985
|
-
this.#entries.clear();
|
|
1986
|
-
}
|
|
1987
|
-
};
|
|
1988
|
-
|
|
1989
1897
|
//#endregion
|
|
1990
1898
|
//#region src/runtime/mioku-context.ts
|
|
1991
1899
|
const isObject$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -2083,6 +1991,59 @@ var MiokuContext = class {
|
|
|
2083
1991
|
return this.#options.bots.pick(bot_id);
|
|
2084
1992
|
}
|
|
2085
1993
|
/**
|
|
1994
|
+
* 取事件所属的跨适配器关联记录。
|
|
1995
|
+
* 同一条逻辑消息被多个适配器/bot 投递时,可从这里读到全部参与方。
|
|
1996
|
+
*/
|
|
1997
|
+
correlation(event) {
|
|
1998
|
+
return this.#options.correlator?.observationOf(event)?.record;
|
|
1999
|
+
}
|
|
2000
|
+
/**
|
|
2001
|
+
* 观察到该事件的全部已连接 bot(首个即 primary)。
|
|
2002
|
+
* 跨适配器去重后,被标记为重复的投递不会进入普通 handler,
|
|
2003
|
+
* 但这里依然能看到全部参与方。
|
|
2004
|
+
*/
|
|
2005
|
+
botsForEvent(event) {
|
|
2006
|
+
const record = this.correlation(event);
|
|
2007
|
+
if (!record) {
|
|
2008
|
+
const bot = event.bot;
|
|
2009
|
+
return bot ? [bot] : [];
|
|
2010
|
+
}
|
|
2011
|
+
const resolved = [];
|
|
2012
|
+
for (const participant of record.participants) {
|
|
2013
|
+
const bot = participant.botId ? this.#options.bots.find(participant.adapter, participant.botId) ?? this.#options.bots.pick(participant.botId) : void 0;
|
|
2014
|
+
if (bot && !resolved.includes(bot)) resolved.push(bot);
|
|
2015
|
+
}
|
|
2016
|
+
return resolved;
|
|
2017
|
+
}
|
|
2018
|
+
/** 事件里被 @ 到的、且本运行时已连接的 bot */
|
|
2019
|
+
mentionedBots(event) {
|
|
2020
|
+
if (event.kind !== "message") return [];
|
|
2021
|
+
const targets = new Set();
|
|
2022
|
+
for (const segment$1 of event.message) {
|
|
2023
|
+
if (segment$1.type !== "at") continue;
|
|
2024
|
+
const data = segment$1.data ?? {};
|
|
2025
|
+
const target = data.qq ?? data.target;
|
|
2026
|
+
if (target != null && String(target) !== "") targets.add(String(target));
|
|
2027
|
+
}
|
|
2028
|
+
if (targets.size === 0) return [];
|
|
2029
|
+
return this.#options.bots.all().filter((bot) => targets.has(String(bot.bot_id)));
|
|
2030
|
+
}
|
|
2031
|
+
/**
|
|
2032
|
+
* 这条消息应当由哪个 bot 回应:优先「被 @ 的 bot」,其次是关联组里首个到达的 bot。
|
|
2033
|
+
* 插件可用它替代隐式的 `event.bot`,从而不受适配器到达顺序影响。
|
|
2034
|
+
*/
|
|
2035
|
+
pickReplyBot(event) {
|
|
2036
|
+
const mentioned = this.mentionedBots(event);
|
|
2037
|
+
if (mentioned.length > 0) return mentioned[0];
|
|
2038
|
+
const participants = this.botsForEvent(event);
|
|
2039
|
+
if (participants.length > 0) return participants[0];
|
|
2040
|
+
return event.bot;
|
|
2041
|
+
}
|
|
2042
|
+
/** 事件关联/去重的运行统计 */
|
|
2043
|
+
correlationStats() {
|
|
2044
|
+
return this.#options.correlator?.stats();
|
|
2045
|
+
}
|
|
2046
|
+
/**
|
|
2086
2047
|
* 获取事件引用回复的消息内容。
|
|
2087
2048
|
* 返回 null 表示没有引用、拿不到对应 bot 或消息已失效。
|
|
2088
2049
|
*/
|
|
@@ -2115,10 +2076,10 @@ var MiokuContext = class {
|
|
|
2115
2076
|
const routes = inputRoutes.map((item) => item.startsWith("!") ? item.slice(1) : item);
|
|
2116
2077
|
const source = `plugin:${this.#options.pluginName}`;
|
|
2117
2078
|
const handledEvents = new WeakSet();
|
|
2118
|
-
const
|
|
2079
|
+
const correlator = bypassDedup ? void 0 : this.#options.correlator;
|
|
2119
2080
|
const wrappedHandler = async (event) => {
|
|
2120
2081
|
if (handledEvents.has(event)) return;
|
|
2121
|
-
if (
|
|
2082
|
+
if (correlator?.isDuplicate(event)) return;
|
|
2122
2083
|
handledEvents.add(event);
|
|
2123
2084
|
await handler(event);
|
|
2124
2085
|
};
|
|
@@ -2259,6 +2220,286 @@ var MiokuContext = class {
|
|
|
2259
2220
|
}
|
|
2260
2221
|
};
|
|
2261
2222
|
|
|
2223
|
+
//#endregion
|
|
2224
|
+
//#region src/runtime/event-correlator.ts
|
|
2225
|
+
const MESSAGE_TTL_MS = 15e3;
|
|
2226
|
+
const EVENT_TTL_MS = 6e4;
|
|
2227
|
+
const PRUNE_INTERVAL = 128;
|
|
2228
|
+
const MAX_SIZE = 4096;
|
|
2229
|
+
const MAX_SEGMENTS = 16;
|
|
2230
|
+
const MAX_TEXT = 256;
|
|
2231
|
+
const HEX32 = /[0-9a-f]{32}/i;
|
|
2232
|
+
const HEX40 = /[0-9a-f]{40}/i;
|
|
2233
|
+
const MEDIA_URL_QUERY_KEYS = [
|
|
2234
|
+
"fileid",
|
|
2235
|
+
"file_id",
|
|
2236
|
+
"md5",
|
|
2237
|
+
"fid"
|
|
2238
|
+
];
|
|
2239
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2240
|
+
const hashString = (input) => (0, node_crypto.createHash)("sha1").update(input).digest("hex").slice(0, 20);
|
|
2241
|
+
/** 稳定序列化:对象键排序、无多余空白,忽略跨适配器的排版差异 */
|
|
2242
|
+
const canonicalJson = (value) => {
|
|
2243
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
|
|
2244
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
2245
|
+
const obj = value;
|
|
2246
|
+
return `{${Object.keys(obj).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(obj[key])}`).join(",")}}`;
|
|
2247
|
+
};
|
|
2248
|
+
/** 结构化载荷(json/xml/ark)的稳定摘要 */
|
|
2249
|
+
const structuredDigest = (raw) => {
|
|
2250
|
+
let value = raw;
|
|
2251
|
+
if (typeof raw === "string") {
|
|
2252
|
+
const trimmed = raw.trim();
|
|
2253
|
+
if (!trimmed) return "";
|
|
2254
|
+
try {
|
|
2255
|
+
value = JSON.parse(trimmed);
|
|
2256
|
+
} catch {
|
|
2257
|
+
return `s:${hashString(trimmed.slice(0, MAX_TEXT * 4))}`;
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
return `j:${hashString(canonicalJson(value))}`;
|
|
2261
|
+
};
|
|
2262
|
+
/** 去掉会过期/随会话变化的查询参数,保留跨适配器稳定的媒体定位 */
|
|
2263
|
+
const normalizeMediaUrl = (raw) => {
|
|
2264
|
+
try {
|
|
2265
|
+
const url = new URL(raw);
|
|
2266
|
+
const kept = new URLSearchParams();
|
|
2267
|
+
for (const key of MEDIA_URL_QUERY_KEYS) {
|
|
2268
|
+
const value = url.searchParams.get(key);
|
|
2269
|
+
if (value) kept.set(key, value);
|
|
2270
|
+
}
|
|
2271
|
+
const query = kept.toString();
|
|
2272
|
+
return `${url.host}${url.pathname}${query ? `?${query}` : ""}`;
|
|
2273
|
+
} catch {
|
|
2274
|
+
return raw;
|
|
2275
|
+
}
|
|
2276
|
+
};
|
|
2277
|
+
const firstHex = (value, pattern) => {
|
|
2278
|
+
if (typeof value !== "string" || !value) return void 0;
|
|
2279
|
+
const match$1 = value.match(pattern);
|
|
2280
|
+
return match$1 ? match$1[0].toLowerCase() : void 0;
|
|
2281
|
+
};
|
|
2282
|
+
/**
|
|
2283
|
+
* 媒体段的内容摘要:优先内容哈希(md5/sha1),其次是文件名里嵌的 md5,
|
|
2284
|
+
* 再次是规范化 URL。都拿不到时返回空串,由调用方退化为「只看段类型」。
|
|
2285
|
+
*/
|
|
2286
|
+
const mediaDigest = (data) => {
|
|
2287
|
+
const md5$1 = firstHex(data.md5, HEX32);
|
|
2288
|
+
if (md5$1) return `h:${md5$1}`;
|
|
2289
|
+
const sha1 = firstHex(data.sha1, HEX40);
|
|
2290
|
+
if (sha1) return `h:${sha1}`;
|
|
2291
|
+
for (const key of [
|
|
2292
|
+
"file_unique",
|
|
2293
|
+
"file_id",
|
|
2294
|
+
"fid",
|
|
2295
|
+
"id",
|
|
2296
|
+
"file",
|
|
2297
|
+
"name"
|
|
2298
|
+
]) {
|
|
2299
|
+
const hex = firstHex(data[key], HEX32);
|
|
2300
|
+
if (hex) return `h:${hex}`;
|
|
2301
|
+
}
|
|
2302
|
+
const url = data.url;
|
|
2303
|
+
if (typeof url === "string" && url) return `u:${normalizeMediaUrl(url)}`;
|
|
2304
|
+
for (const key of [
|
|
2305
|
+
"file",
|
|
2306
|
+
"file_id",
|
|
2307
|
+
"fid",
|
|
2308
|
+
"id",
|
|
2309
|
+
"name"
|
|
2310
|
+
]) {
|
|
2311
|
+
const value = data[key];
|
|
2312
|
+
if (typeof value === "string" && value) return `r:${value.toLowerCase()}`;
|
|
2313
|
+
}
|
|
2314
|
+
return "";
|
|
2315
|
+
};
|
|
2316
|
+
/** 只在载荷跨协议一致时才使用摘要的段类型 */
|
|
2317
|
+
const DIGEST_MEDIA_TYPES = new Set(["image", "flash"]);
|
|
2318
|
+
const segmentSignature = (segment$1) => {
|
|
2319
|
+
const data = isRecord(segment$1.data) ? segment$1.data : {};
|
|
2320
|
+
switch (segment$1.type) {
|
|
2321
|
+
case "text": {
|
|
2322
|
+
const text$1 = typeof data.text === "string" ? data.text : "";
|
|
2323
|
+
return `t:${text$1.slice(0, MAX_TEXT)}`;
|
|
2324
|
+
}
|
|
2325
|
+
case "at": {
|
|
2326
|
+
const target = data.qq ?? data.target;
|
|
2327
|
+
return `a:${target == null ? "" : String(target)}`;
|
|
2328
|
+
}
|
|
2329
|
+
case "face": {
|
|
2330
|
+
const id = data.id ?? data.face;
|
|
2331
|
+
return `f:${id == null ? "" : String(id)}`;
|
|
2332
|
+
}
|
|
2333
|
+
case "json":
|
|
2334
|
+
case "xml":
|
|
2335
|
+
case "ark": {
|
|
2336
|
+
const digest = structuredDigest(data.data ?? data.content ?? data);
|
|
2337
|
+
return digest ? `${segment$1.type}:${digest}` : segment$1.type;
|
|
2338
|
+
}
|
|
2339
|
+
default: {
|
|
2340
|
+
if (DIGEST_MEDIA_TYPES.has(segment$1.type)) {
|
|
2341
|
+
const digest = mediaDigest(data);
|
|
2342
|
+
return digest ? `${segment$1.type}:${digest}` : segment$1.type;
|
|
2343
|
+
}
|
|
2344
|
+
return segment$1.type;
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
};
|
|
2348
|
+
const contentSignatureOf = (event) => {
|
|
2349
|
+
const parts = [];
|
|
2350
|
+
for (const segment$1 of event.message) {
|
|
2351
|
+
if (parts.length >= MAX_SEGMENTS) break;
|
|
2352
|
+
if (segment$1.type === "reply") continue;
|
|
2353
|
+
parts.push(segmentSignature(segment$1));
|
|
2354
|
+
}
|
|
2355
|
+
return parts.join("|");
|
|
2356
|
+
};
|
|
2357
|
+
const messageKeyOf = (event) => {
|
|
2358
|
+
const type = event.message_type ?? "";
|
|
2359
|
+
const scope = event.group_id ?? event.user_id ?? "";
|
|
2360
|
+
const sender = event.user_id ?? event.sender?.user_id ?? "";
|
|
2361
|
+
return [
|
|
2362
|
+
"m",
|
|
2363
|
+
type,
|
|
2364
|
+
scope,
|
|
2365
|
+
sender,
|
|
2366
|
+
contentSignatureOf(event)
|
|
2367
|
+
].join("|");
|
|
2368
|
+
};
|
|
2369
|
+
const noticeKeyOf = (event) => [
|
|
2370
|
+
"n",
|
|
2371
|
+
event.identity.event_type,
|
|
2372
|
+
event.notice_type ?? "",
|
|
2373
|
+
event.sub_type ?? "",
|
|
2374
|
+
event.group_id ?? "",
|
|
2375
|
+
event.user_id ?? "",
|
|
2376
|
+
event.operator_id ?? "",
|
|
2377
|
+
event.identity.timestamp == null ? "" : Math.floor(event.identity.timestamp / 1e3)
|
|
2378
|
+
].join("|");
|
|
2379
|
+
const requestKeyOf = (event) => [
|
|
2380
|
+
"r",
|
|
2381
|
+
event.identity.event_type,
|
|
2382
|
+
event.request_type ?? "",
|
|
2383
|
+
event.sub_type ?? "",
|
|
2384
|
+
event.group_id ?? "",
|
|
2385
|
+
event.user_id ?? "",
|
|
2386
|
+
event.comment ?? "",
|
|
2387
|
+
event.identity.timestamp == null ? "" : Math.floor(event.identity.timestamp / 1e3)
|
|
2388
|
+
].join("|");
|
|
2389
|
+
const participantOf = (event) => ({
|
|
2390
|
+
adapter: event.identity.adapter,
|
|
2391
|
+
botId: event.identity.bot_id,
|
|
2392
|
+
eventType: event.identity.event_type,
|
|
2393
|
+
messageId: event.identity.message_id ?? event.identity.native_event_id
|
|
2394
|
+
});
|
|
2395
|
+
const sameParticipant = (a, b) => a.adapter === b.adapter && (a.botId ?? "") === (b.botId ?? "") && (a.messageId ?? "") === (b.messageId ?? "");
|
|
2396
|
+
/**
|
|
2397
|
+
* 跨适配器事件关联器
|
|
2398
|
+
*/
|
|
2399
|
+
var EventCorrelator = class {
|
|
2400
|
+
#groups = new Map();
|
|
2401
|
+
#byEvent = new WeakMap();
|
|
2402
|
+
#logger;
|
|
2403
|
+
#maxSize;
|
|
2404
|
+
#inserts = 0;
|
|
2405
|
+
#observed = 0;
|
|
2406
|
+
#duplicates = 0;
|
|
2407
|
+
#evicted = 0;
|
|
2408
|
+
constructor(options = {}) {
|
|
2409
|
+
this.#logger = options.logger;
|
|
2410
|
+
this.#maxSize = options.maxSize ?? MAX_SIZE;
|
|
2411
|
+
}
|
|
2412
|
+
/** 观察一个事件,登记它所属的关联组并返回其定位(同一事件只登记一次) */
|
|
2413
|
+
observe(event) {
|
|
2414
|
+
const cached = this.#byEvent.get(event);
|
|
2415
|
+
if (cached) return cached;
|
|
2416
|
+
const located = this.#locate(event);
|
|
2417
|
+
if (!located) return null;
|
|
2418
|
+
const { key, ttl } = located;
|
|
2419
|
+
const now = Date.now();
|
|
2420
|
+
this.#observed++;
|
|
2421
|
+
let group = this.#groups.get(key);
|
|
2422
|
+
let duplicate = false;
|
|
2423
|
+
if (group && group.expiresAt >= now) {
|
|
2424
|
+
duplicate = true;
|
|
2425
|
+
this.#duplicates++;
|
|
2426
|
+
const participant = participantOf(event);
|
|
2427
|
+
if (!group.participants.some((item) => sameParticipant(item, participant))) group.participants.push(participant);
|
|
2428
|
+
this.#logger?.debug(`去重命中: key=${key} adapter=${participant.adapter} bot=${participant.botId ?? ""} primary=${group.primary.adapter}:${group.primary.botId ?? ""}`);
|
|
2429
|
+
} else {
|
|
2430
|
+
const participant = participantOf(event);
|
|
2431
|
+
group = {
|
|
2432
|
+
key,
|
|
2433
|
+
expiresAt: now + ttl,
|
|
2434
|
+
firstSeenAt: now,
|
|
2435
|
+
primary: participant,
|
|
2436
|
+
participants: [participant]
|
|
2437
|
+
};
|
|
2438
|
+
this.#groups.set(key, group);
|
|
2439
|
+
this.#inserts++;
|
|
2440
|
+
if (this.#inserts >= PRUNE_INTERVAL || this.#groups.size > this.#maxSize) {
|
|
2441
|
+
this.#inserts = 0;
|
|
2442
|
+
this.#prune(now);
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
const observation = {
|
|
2446
|
+
record: group,
|
|
2447
|
+
primary: !duplicate,
|
|
2448
|
+
duplicate
|
|
2449
|
+
};
|
|
2450
|
+
this.#byEvent.set(event, observation);
|
|
2451
|
+
return observation;
|
|
2452
|
+
}
|
|
2453
|
+
/** 读取某事件此前登记的关联定位(未登记或已过期时返回 undefined) */
|
|
2454
|
+
observationOf(event) {
|
|
2455
|
+
const observation = this.#byEvent.get(event);
|
|
2456
|
+
if (!observation) return void 0;
|
|
2457
|
+
if (observation.record.expiresAt < Date.now()) return void 0;
|
|
2458
|
+
return observation;
|
|
2459
|
+
}
|
|
2460
|
+
/** 该事件是否属于关联组里后续到达的重复投递(未登记时会先登记) */
|
|
2461
|
+
isDuplicate(event) {
|
|
2462
|
+
return this.observe(event)?.duplicate === true;
|
|
2463
|
+
}
|
|
2464
|
+
stats() {
|
|
2465
|
+
return {
|
|
2466
|
+
groups: this.#groups.size,
|
|
2467
|
+
observed: this.#observed,
|
|
2468
|
+
duplicates: this.#duplicates,
|
|
2469
|
+
evicted: this.#evicted
|
|
2470
|
+
};
|
|
2471
|
+
}
|
|
2472
|
+
clear() {
|
|
2473
|
+
this.#groups.clear();
|
|
2474
|
+
}
|
|
2475
|
+
#locate(event) {
|
|
2476
|
+
if (event.kind === "message") return {
|
|
2477
|
+
key: messageKeyOf(event),
|
|
2478
|
+
ttl: MESSAGE_TTL_MS
|
|
2479
|
+
};
|
|
2480
|
+
if (event.kind === "notice") return {
|
|
2481
|
+
key: noticeKeyOf(event),
|
|
2482
|
+
ttl: EVENT_TTL_MS
|
|
2483
|
+
};
|
|
2484
|
+
if (event.kind === "request") return {
|
|
2485
|
+
key: requestKeyOf(event),
|
|
2486
|
+
ttl: EVENT_TTL_MS
|
|
2487
|
+
};
|
|
2488
|
+
return null;
|
|
2489
|
+
}
|
|
2490
|
+
#prune(now) {
|
|
2491
|
+
for (const [key, group] of this.#groups) if (group.expiresAt < now) this.#groups.delete(key);
|
|
2492
|
+
if (this.#groups.size <= this.#maxSize) return;
|
|
2493
|
+
const drop = this.#groups.size - this.#maxSize;
|
|
2494
|
+
let dropped = 0;
|
|
2495
|
+
for (const key of this.#groups.keys()) {
|
|
2496
|
+
this.#groups.delete(key);
|
|
2497
|
+
this.#evicted++;
|
|
2498
|
+
if (++dropped >= drop) break;
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
};
|
|
2502
|
+
|
|
2262
2503
|
//#endregion
|
|
2263
2504
|
//#region src/runtime/plugin-metadata.ts
|
|
2264
2505
|
const store$1 = getOrCreate("plugin-metadata", () => new Map());
|
|
@@ -4843,7 +5084,7 @@ var MiokuRuntime = class {
|
|
|
4843
5084
|
#enabledPlugins = new Map();
|
|
4844
5085
|
#driverFactory;
|
|
4845
5086
|
#builtinPlugins;
|
|
4846
|
-
#
|
|
5087
|
+
#correlator;
|
|
4847
5088
|
#started = false;
|
|
4848
5089
|
#stopped = false;
|
|
4849
5090
|
constructor(options) {
|
|
@@ -4851,7 +5092,7 @@ var MiokuRuntime = class {
|
|
|
4851
5092
|
this.#logger = options.logger;
|
|
4852
5093
|
this.#driverFactory = options.driverFactory ?? (() => createDefaultDriver());
|
|
4853
5094
|
this.#builtinPlugins = options.builtinPlugins ?? BUILTIN_PLUGINS$1;
|
|
4854
|
-
this.#
|
|
5095
|
+
this.#correlator = options.dedup?.crossAdapter === false ? void 0 : new EventCorrelator({ logger: this.#logger.child({ scope: "dedup" }) });
|
|
4855
5096
|
this.#driver = this.#driverFactory();
|
|
4856
5097
|
this.#bus = new EventBus();
|
|
4857
5098
|
this.#bus.setLogger((level, message, detail) => {
|
|
@@ -4874,6 +5115,10 @@ var MiokuRuntime = class {
|
|
|
4874
5115
|
get bus() {
|
|
4875
5116
|
return this.#bus;
|
|
4876
5117
|
}
|
|
5118
|
+
/** 事件关联器(跨适配器去重);为 undefined 时表示已禁用 */
|
|
5119
|
+
get correlator() {
|
|
5120
|
+
return this.#correlator;
|
|
5121
|
+
}
|
|
4877
5122
|
get bots() {
|
|
4878
5123
|
return this.#bots.all();
|
|
4879
5124
|
}
|
|
@@ -4981,7 +5226,8 @@ var MiokuRuntime = class {
|
|
|
4981
5226
|
driver: this.#driver,
|
|
4982
5227
|
capabilities: this.#capabilities,
|
|
4983
5228
|
logger: this.#logger.child({ adapter: state.definition.name }),
|
|
4984
|
-
emit: (event) => this.#emitLifecycle(event)
|
|
5229
|
+
emit: (event) => this.#emitLifecycle(event),
|
|
5230
|
+
correlator: this.#correlator
|
|
4985
5231
|
});
|
|
4986
5232
|
}
|
|
4987
5233
|
async #loadPlugin(plugin, type) {
|
|
@@ -4995,7 +5241,7 @@ var MiokuRuntime = class {
|
|
|
4995
5241
|
config: botConfig,
|
|
4996
5242
|
logger: this.#logger.child({ plugin: plugin.name }),
|
|
4997
5243
|
priority: plugin.priority ?? 100,
|
|
4998
|
-
|
|
5244
|
+
correlator: this.#correlator,
|
|
4999
5245
|
getAdapter: (name) => this.getAdapter(name),
|
|
5000
5246
|
listAdapters: () => this.adapters,
|
|
5001
5247
|
onUpdateConfig: async (updater) => {
|
|
@@ -5284,6 +5530,7 @@ var MiokuRuntime = class {
|
|
|
5284
5530
|
this.#enabledPlugins.clear();
|
|
5285
5531
|
this.#capabilities.clear();
|
|
5286
5532
|
this.#bots.clear();
|
|
5533
|
+
this.#correlator?.clear();
|
|
5287
5534
|
resetPluginMetadata();
|
|
5288
5535
|
try {
|
|
5289
5536
|
await this.#driver.shutdown();
|
|
@@ -5446,6 +5693,7 @@ async function start(options = {}) {
|
|
|
5446
5693
|
};
|
|
5447
5694
|
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
5448
5695
|
process.on("SIGTERM", () => void shutdown("SIGTERM"));
|
|
5696
|
+
installProcessErrorGuards();
|
|
5449
5697
|
return { stop: (reason) => stopRuntime(reason) };
|
|
5450
5698
|
}
|
|
5451
5699
|
function readVersion() {
|
|
@@ -5458,6 +5706,15 @@ function readVersion() {
|
|
|
5458
5706
|
}
|
|
5459
5707
|
}
|
|
5460
5708
|
const version$1 = readVersion();
|
|
5709
|
+
function installProcessErrorGuards() {
|
|
5710
|
+
if (process.listenerCount("unhandledRejection") === 0) process.on("unhandledRejection", (reason) => {
|
|
5711
|
+
const detail = reason instanceof Error ? reason.stack ?? reason.message : reason;
|
|
5712
|
+
rootLogger.error(`[unhandledRejection] ${String(detail)}`);
|
|
5713
|
+
});
|
|
5714
|
+
if (process.listenerCount("uncaughtException") === 0) process.on("uncaughtException", (err) => {
|
|
5715
|
+
rootLogger.error(`[uncaughtException] ${err.stack ?? err.message ?? String(err)}`);
|
|
5716
|
+
});
|
|
5717
|
+
}
|
|
5461
5718
|
|
|
5462
5719
|
//#endregion
|
|
5463
5720
|
exports.AI_GEMINI_THINKING_LEVELS = AI_GEMINI_THINKING_LEVELS;
|
|
@@ -5473,6 +5730,7 @@ exports.CapabilityRegistry = CapabilityRegistry;
|
|
|
5473
5730
|
exports.ChromeUA = ChromeUA;
|
|
5474
5731
|
exports.DriverShutdownError = DriverShutdownError;
|
|
5475
5732
|
exports.EventBus = EventBus;
|
|
5733
|
+
exports.EventCorrelator = EventCorrelator;
|
|
5476
5734
|
exports.HttpRequestError = HttpRequestError;
|
|
5477
5735
|
exports.MessageSegmentImpl = MessageSegmentImpl;
|
|
5478
5736
|
exports.MiokuContext = MiokuContext;
|