You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
mail-yubao/scripts/patch-shell-subject-noise.ts

153 lines
5.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* Fix: ignore subject/主旨 lines when detecting forward shells & kinds pollution
*/
import fs from "fs";
{
const path = "src/services/parse/instruction-lexicon.ts";
let src = fs.readFileSync(path, "utf8");
if (!src.includes("function stripSubjectLines")) {
const insertAt = src.indexOf("export function isForwardShellSegment");
if (insertAt < 0) throw new Error("isForwardShellSegment missing");
const helper = `/** Drop subject/\\u4e3b\\u65e8 lines so thread titles do not count as body actions */\nfunction stripSubjectLines(text: string): string {\n return text\n .split(/\\n/)\n .filter((line) => !/(?:\\u4e3b\\u9898|\\u4e3b\\u65e8|Subject)\\s*[\\uff1a:]/i.test(line))\n .join("\\n");\n}\n\n`;
// Use real Chinese in helper via unicode escapes already - wait use actual:
const helper2 = `/** Drop subject lines so thread titles do not count as body actions */\nfunction stripSubjectLines(text: string): string {\n return text\n .split(/\\n/)\n .filter((line) => !/(?:\u4e3b\u9898|\u4e3b\u65e8|Subject)\\s*[\uFF1A:]/i.test(line))\n .join("\\n");\n}\n\n`;
src = src.slice(0, insertAt) + helper2 + src.slice(insertAt);
}
// Rewrite isForwardShellSegment body
const start = src.indexOf("export function isForwardShellSegment");
const end = src.indexOf("export function isNonInstructionSegment");
if (start < 0 || end < 0) throw new Error("shell funcs missing");
const replacement = `export function isForwardShellSegment(text: string): boolean {
const compact = text.replace(/\\s+/g, " ").trim();
if (/\u53d1\u81ea\u6211\u7684iPhone/.test(compact)) return true;
const bodyOnly = stripSubjectLines(text);
const bodyCompact = bodyOnly.replace(/\\s+/g, " ").trim();
// date / QQ pure forward wrapper (keywords only in \u4e3b\u65e8)
if (
/date@usasinogroup\\.com/i.test(compact) &&
!/Dear[,,]/.test(bodyOnly) &&
!BODY_ACTION_RE.test(bodyOnly)
) {
return true;
}
if (
/\u53d1\u81ea\u6211\u7684iPhone|\u5927\u5c0f\\s*\\d/.test(compact) &&
!BODY_ACTION_RE.test(bodyOnly) &&
!/\u65b0\u589e\u9884\u62a5|\u8bf7\u67e5\u6536\u65b0\u589e\u9884\u62a5|\u65b0\u589e\u8f6c\u4ed3|DO\u8bf7\u67e5\u6536/.test(
bodyOnly,
)
) {
return true;
}
// short date forward: only headers + signature
if (
/\u5bc4\u4ef6\u4eba/.test(compact) &&
/date@usasinogroup\\.com/i.test(compact) &&
bodyCompact.length < 120 &&
!/Dear[,,]/.test(bodyOnly)
) {
return true;
}
return false;
}
`;
src = src.slice(0, start) + replacement + src.slice(end);
// Also update isNonInstructionSegment QQ shell to use stripSubjectLines
src = src.replace(
` // QQ \u5916\u58f3\uff1a\u4ec5\u8f6c\u53d1\u5143\u6570\u636e\u3001\u65e0\u4e1a\u52a1\u52a8\u4f5c
if (
/\u53d1\u81ea\u6211\u7684iPhone|\u5927\u5c0f\\s*\\d/.test(compact) &&
!BODY_ACTION_RE.test(compact) &&
!/\u65b0\u589e\u9884\u62a5|\u8bf7\u67e5\u6536\u65b0\u589e\u9884\u62a5|\u65b0\u589e\u8f6c\u4ed3|DO\u8bf7\u67e5\u6536/.test(compact)
) {
return true;
}`,
` // QQ shell without body actions (ignore subject title noise)
{
const bodyOnly = stripSubjectLines(text);
if (
/\u53d1\u81ea\u6211\u7684iPhone|\u5927\u5c0f\\s*\\d/.test(compact) &&
!BODY_ACTION_RE.test(bodyOnly) &&
!/\u65b0\u589e\u9884\u62a5|\u8bf7\u67e5\u6536\u65b0\u589e\u9884\u62a5|\u65b0\u589e\u8f6c\u4ed3|DO\u8bf7\u67e5\u6536/.test(
bodyOnly,
)
) {
return true;
}
}`,
);
fs.writeFileSync(path, src);
console.log("lexicon shell ok");
}
{
const path = "src/services/parse/split-instructions.ts";
let src = fs.readFileSync(path, "utf8");
// Strengthen bodyForKindDetect: strip ALL subject lines + Re:新增预报 pollution lines
const old = `function bodyForKindDetect(segBody: string): string {
if (!BODY_ACTION_RE.test(segBody)) return segBody;
return segBody
.split(/\\n/)
.filter(
(line) =>
!/(?:\u4e3b\u9898|\u4e3b\u65e8|Subject)\\s*[\uFF1A:].*(?:\u65b0\u589e\u9884\u62a5|Fw:|\u8f6c\u53d1)/i.test(line),
)
.join("\\n");
}`;
const neu = `function bodyForKindDetect(segBody: string): string {
// Always strip subject/Re title lines; they carry historical \u65b0\u589e\u9884\u62a5
const stripped = segBody
.split(/\\n/)
.filter((line) => {
if (/(?:\u4e3b\u9898|\u4e3b\u65e8|Subject)\\s*[\uFF1A:]/i.test(line)) return false;
if (/^Re:\\s*Re:/i.test(line.trim()) && /\u65b0\u589e\u9884\u62a5/.test(line))
return false;
return true;
})
.join("\\n");
return stripped;
}`;
if (!src.includes("function bodyForKindDetect")) throw new Error("no bodyForKindDetect");
// replace by function bounds
const a = src.indexOf("function bodyForKindDetect");
const b = src.indexOf("export function detectKindsForSegment");
if (a < 0 || b < 0) throw new Error("bounds");
src = src.slice(0, a) + neu + "\n\n" + src.slice(b);
// useSubject: only when body (stripped) has no action words
src = src.replace(
` const useSubject =
!BODY_ACTION_RE.test(segBody) ||
/\\u8bf7\\u67e5\\u6536\\u65b0\\u589e\\u9884\\u62a5/.test(bodyText);`,
` const useSubject =
!BODY_ACTION_RE.test(bodyText) ||
/\u8bf7\u67e5\u6536\u65b0\u589e\u9884\u62a5/.test(bodyText);`,
);
// Also fix the actual Chinese version if unicode escape didn't match
src = src.replace(
/const useSubject =\s*!BODY_ACTION_RE\.test\(segBody\) \|\|\s*\/请查收新增预报\/\.test\(bodyText\);/,
`const useSubject =
!BODY_ACTION_RE.test(bodyText) ||
/\u8bf7\u67e5\u6536\u65b0\u589e\u9884\u62a5/.test(bodyText);`,
);
fs.writeFileSync(path, src);
console.log("split bodyForKindDetect ok");
}