fix(mail): classifier v1.2 — FS-token + extreme-percent + casino-name + threshold 40

- SUBJECT_PERCENT_PATTERN weight 10 → 15
- SCORE_BLOCK_MIDRANGE 50 → 40
- new FS-token pattern (+20): \b\d+\s*FS\b (free-spins abbreviation)
- new extreme-percent pattern (+20): \b[1-9]\d{2,}\s*% (>=100% signals gambling)
- casino in sender display-name (+30): targeted v1.2 reactivation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chahinebrini 2026-06-01 10:17:25 +02:00
parent f59d1800c7
commit 0533fcad71

View File

@ -77,7 +77,7 @@ export const SCORE_WEIGHTS = {
// Subject-Indikatoren
SUBJECT_GAMBLING_KEYWORD: 50, // Keyword im Betreff (casino, jackpot, freispiel …)
SUBJECT_MONEY_PATTERN: 20, // €/$ + Zahl (z.B. "100€ Bonus")
SUBJECT_PERCENT_PATTERN: 10, // Zahl+% im Betreff (z.B. "400% Bonus") — allein nie Block-fähig
SUBJECT_PERCENT_PATTERN: 15, // Zahl+% im Betreff (z.B. "400% Bonus") — allein nie Block-fähig
SUBJECT_URGENCY: 15, // "Nur heute", "Letzte Chance", "Ablaufdatum"
SUBJECT_ALL_CAPS_WORD: 5, // EINZELNES ALL-CAPS-WORT im Betreff
@ -94,8 +94,8 @@ export const SCORE_WEIGHTS = {
const SCORE_HARD_BLOCK_THRESHOLD = 80;
// Pass-Below: Score < 25 → PASS (no-signal)
const SCORE_PASS_BELOW = 25;
// Mid-range Block-Threshold: Score in [25, 80) → BLOCK ab 50, sonst PASS
const SCORE_BLOCK_MIDRANGE = 50;
// Mid-range Block-Threshold: Score in [25, 80) → BLOCK ab 40, sonst PASS
const SCORE_BLOCK_MIDRANGE = 40;
// ─── Bekannte Gambling-Brands (für Brand-Match-Normalisierung) ─────────────────
// Abgeleitet aus GAMBLING_KEYWORDS + typischen Blocklist-Domains.
@ -270,7 +270,12 @@ export function computeScore(
// ── Sender-Name-Keywords: entfernt in v1.0 (Score-Beitrag via Display-Name
// ist zu False-Positive-anfällig, Display-Name-Blocking nicht supported).
// keywordHitsName bleibt im ScoreResult für v1.1-Reaktivierung (immer leer).
// keywordHitsName bleibt im ScoreResult für v1.1-Reaktivierung.
// v1.2: "casino" im Display-Name ist stark genug für gezielten Score-Beitrag.
if (senderNameLower.includes("casino")) {
keywordHitsName.push("casino");
score += 30;
}
// ── Geld-Pattern im Betreff (€/$ + Zahl) ──
if (/[€$£]\s*\d|\d\s*[€$£]/.test(subject)) {
@ -279,14 +284,28 @@ export function computeScore(
}
// ── Prozent-Pattern im Betreff (z.B. "400% Bonus", "200% Einzahlung") ──
// Separates Flag mit reduziertem Score: % allein (10 Punkte) reicht nie
// für einen Block (Threshold 50) — verhindert FP bei "10% Rabatt"-Mails.
// Separates Flag mit reduziertem Score: % allein (15 Punkte) reicht nie
// für einen Block allein — verhindert FP bei "10% Rabatt"-Mails.
// Ergänzt das Currency-Pattern oben, das nur € / $ / £ kennt.
if (/\d\s*%/.test(subject)) {
styleFlags.push("percent-pattern");
score += SCORE_WEIGHTS.SUBJECT_PERCENT_PATTERN;
}
// ── FS-Token im Betreff (z.B. "400 FS", "100FS") — Free-Spins-Abkürzung ──
// Word-Boundary sichert, dass "FFS" oder "OFS" nicht fälschlich matchen.
if (/\b\d+\s*FS\b/.test(subject)) {
styleFlags.push("free-spins-token");
score += 20;
}
// ── Extrem-Prozent im Betreff (>= 100%, z.B. "200%", "1000% Bonus") ──
// Retailer nutzen selten dreistellige Prozentwerte — Gambling-Mails sehr oft.
if (/\b[1-9]\d{2,}\s*%/.test(subject)) {
styleFlags.push("extreme-percent");
score += 20;
}
// ── Urgency-Wörter im Betreff ──
const URGENCY_PATTERNS = [
"nur heute", "letzte chance", "läuft ab", "ablaufdatum",