抓包对比工具补一批一次性脚本:拆预设、对请求、跑 A/B 与拒答抽样。
这些脚本都是我对着酒馆真实请求手工核对用的,留在仓库里方便下次重跑;跑出来的本地数据目录含接口 key,已经写进忽略规则。
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
// 把预设里所有「像脚本」的东西挖出来:完整路径、长度、以及它碰了哪些酒馆 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)));
|
||||
}
|
||||
Reference in New Issue
Block a user