68 lines
3.5 KiB
JavaScript
68 lines
3.5 KiB
JavaScript
// 把预设里所有「像脚本」的东西挖出来:完整路径、长度、以及它碰了哪些酒馆 API。
|
||
// 用法: node read-preset-scripts.mjs <preset.json> [输出目录]
|
||
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||
import path from "node:path";
|
||
|
||
const presetFile = process.argv[2];
|
||
const outDir = process.argv[3] || path.join(path.dirname(presetFile), "script-dump");
|
||
const preset = JSON.parse(readFileSync(presetFile, "utf8"));
|
||
mkdirSync(outDir, { recursive: true });
|
||
|
||
const APIS = [
|
||
"TavernHelper", "tavernHelper", "SillyTavern", "getContext", "eventOn", "eventOnce", "eventEmit",
|
||
"registerFunctionTool", "unregisterFunctionTool", "getPrompts", "setPrompts", "replacePrompts",
|
||
"injectPrompt", "uninjectPrompt", "getVariables", "setVariables", "getChatMessages", "setChatMessages",
|
||
"createChatMessages", "deleteChatMessages", "substituteParams", "formatAsTavernRegexedString",
|
||
"getLorebookEntries", "getCharLorebooks", "getCharacter", "getCurrentCharacterId", "triggerSlash",
|
||
"getLastMessageId", "generate", "generateRaw", "stopGeneration", "getPreset", "getPresetNames",
|
||
"loadPreset", "jQuery", "$(", "document.", "window.", "localStorage", "fetch(", "XMLHttpRequest",
|
||
"postSysPrompt", "getTavernRegexes", "getWorldbook", "setWorldbook", "toastr", "console.log",
|
||
"import(", "require(", "createElement", "addEventListener", "MutationObserver", "setTimeout",
|
||
"__TAVERN", "stscript", "getAudio", "voice", "playAudio", "getRequestHeaders", "saveSettingsDebounced",
|
||
];
|
||
|
||
const candidates = [];
|
||
function walk(node, trail, depth) {
|
||
if (node == null || depth > 8) return;
|
||
if (Array.isArray(node)) {
|
||
node.forEach((v, i) => walk(v, [...trail, String(i)], depth + 1));
|
||
return;
|
||
}
|
||
if (typeof node === "string") {
|
||
if (node.length > 200 && /(function|=>|await |const |let |var |TavernHelper|SillyTavern|\$\()/.test(node)) {
|
||
candidates.push({ path: trail.join("."), code: node });
|
||
}
|
||
return;
|
||
}
|
||
if (typeof node !== "object") return;
|
||
for (const [k, v] of Object.entries(node)) walk(v, [...trail, k], depth + 1);
|
||
}
|
||
walk(preset, ["preset"], 0);
|
||
|
||
// 也把所有 extensions 下的键结构打出来(含数组长度),判断「脚本」挂在哪个键
|
||
const extKeys = [];
|
||
(function scan(node, trail, depth) {
|
||
if (node == null || depth > 6) return;
|
||
if (Array.isArray(node)) { extKeys.push([trail.join(".") + "[]", node.length]); return; }
|
||
if (typeof node === "object") {
|
||
for (const [k, v] of Object.entries(node)) scan(v, [...trail, k], depth + 1);
|
||
return;
|
||
}
|
||
if (typeof node === "string" && node.length > 3000) extKeys.push([trail.join("."), node.length]);
|
||
})(preset.extensions, ["extensions"], 0);
|
||
|
||
console.log("=== extensions 下的大对象 / 长字符串(可能是脚本宿主) ===");
|
||
for (const [p, n] of extKeys.slice(0, 60)) console.log(" " + p + " → " + n);
|
||
console.log("\n=== 候选脚本 " + candidates.length + " 段 ===");
|
||
let k = 0;
|
||
for (const c of candidates) {
|
||
k += 1;
|
||
const hits = APIS.map((a) => [a, (c.code.match(new RegExp(a.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g")) || []).length]).filter(([, n]) => n > 0);
|
||
console.log("\n--- [" + k + "] " + c.path + " (" + c.code.length + " 字符) ---");
|
||
console.log(" 碰到: " + (hits.length ? hits.map(([a, n]) => a + "×" + n).join(", ") : "(无已知 API)"));
|
||
const file = path.join(outDir, "script-" + String(k).padStart(2, "0") + "-" + c.path.replace(/[^\w.\-]+/g, "_").slice(-60) + ".js");
|
||
writeFileSync(file, c.code, "utf8");
|
||
console.log(" 落盘: " + file);
|
||
console.log(" 头 300 字: " + JSON.stringify(c.code.slice(0, 300)));
|
||
}
|