From 43efa37c6a473af59ea94a0f18bd3e2ca9fe708f Mon Sep 17 00:00:00 2001 From: mdecimus <11444311+mdecimus@users.noreply.github.com> Date: Thu, 18 Dec 2025 09:24:22 +0100 Subject: [PATCH] Update spam autolearn behaviour --- crates/common/src/config/spamfilter.rs | 16 ++++++------ crates/spam-filter/src/analysis/score.rs | 32 +++++++++++++----------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/crates/common/src/config/spamfilter.rs b/crates/common/src/config/spamfilter.rs index 35688b39..2c228a05 100644 --- a/crates/common/src/config/spamfilter.rs +++ b/crates/common/src/config/spamfilter.rs @@ -87,8 +87,8 @@ pub struct ClassifierConfig { pub min_spam_samples: u64, pub auto_learn_reply_ham: bool, pub auto_learn_card_is_ham: bool, - pub auto_learn_spam_score: f32, - pub auto_learn_ham_score: f32, + pub auto_learn_spam_trap: bool, + pub auto_learn_spam_rbl_count: u32, pub hold_samples_for: u64, pub train_frequency: Option, pub log_scale: bool, @@ -489,12 +489,12 @@ impl ClassifierConfig { auto_learn_reply_ham: config .property_or_default("spam-filter.trusted-reply.learn", "true") .unwrap_or(true), - auto_learn_spam_score: config - .property_or_default("spam-filter.classifier.auto-learn.spam-score", "8.0") - .unwrap_or(8.0), - auto_learn_ham_score: config - .property_or_default("spam-filter.classifier.auto-learn.ham-score", "-8.0") - .unwrap_or(-8.0), + auto_learn_spam_trap: config + .property_or_default("spam-filter.classifier.auto-learn.spam-trap", "true") + .unwrap_or(true), + auto_learn_spam_rbl_count: config + .property_or_default("spam-filter.classifier.auto-learn.spam-rbl-count", "2") + .unwrap_or(2), hold_samples_for: config .property_or_default::("spam-filter.classifier.samples.hold-for", "180d") .unwrap_or(Duration::from_secs(180 * 24 * 60 * 60)) diff --git a/crates/spam-filter/src/analysis/score.rs b/crates/spam-filter/src/analysis/score.rs index 8a96a40b..c1c1bcab 100644 --- a/crates/spam-filter/src/analysis/score.rs +++ b/crates/spam-filter/src/analysis/score.rs @@ -56,6 +56,8 @@ impl SpamFilterAnalyzeScore for Server { // Calculate final score let mut results = vec![]; let mut header_len = 60; + let mut is_spam_trap = false; + let mut rbl_count = 0; for tag in &ctx.result.tags { let score = match self.core.spam.lists.scores.get(tag) { @@ -68,6 +70,11 @@ impl SpamFilterAnalyzeScore for Server { } None | Some(SpamFilterAction::Disabled) => 0.0, }; + if tag == "SPAM_TRAP" { + is_spam_trap = true; + } else if score > 1.0 && tag.starts_with("RBL_") { + rbl_count += 1; + } ctx.result.score += score; header_len += tag.len() + 10; if score != 0.0 || !tag.starts_with("X_") { @@ -145,11 +152,8 @@ impl SpamFilterAnalyzeScore for Server { let _ = write!(&mut headers, "X-Spam-LLM: {category} ({explanation})\r\n",); } - let class = if final_score >= self.core.spam.scores.spam_threshold { - "spam" - } else { - "ham" - }; + let is_spam = final_score >= self.core.spam.scores.spam_threshold; + let class = if is_spam { "spam" } else { "ham" }; if avg_confidence != 0.0 { let _ = write!( @@ -163,18 +167,16 @@ impl SpamFilterAnalyzeScore for Server { ); } - // Autolearn + // Autolearn SPAM let mut train_spam = None; - let config = self.core.spam.classifier.as_ref().unwrap(); - if config.auto_learn_spam_score > 0.0 && final_score >= config.auto_learn_spam_score { - if !ctx.result.has_tag("PROB_SPAM_HIGH") { - train_spam = Some(true); - } - } else if config.auto_learn_ham_score < 0.0 - && final_score <= config.auto_learn_ham_score - && !ctx.result.has_tag("PROB_HAM_HIGH") + if is_spam + && self.core.spam.classifier.as_ref().is_some_and(|c| { + (c.auto_learn_spam_trap && is_spam_trap) + || (c.auto_learn_spam_rbl_count > 0 + && rbl_count >= c.auto_learn_spam_rbl_count) + }) { - train_spam = Some(false); + train_spam = Some(true); } SpamFilterAction::Allow(SpamFilterScore {