diff --git a/homelab-ai-bot/core/mail_client.py b/homelab-ai-bot/core/mail_client.py index 3c6010fd5..929afc9cd 100644 --- a/homelab-ai-bot/core/mail_client.py +++ b/homelab-ai-bot/core/mail_client.py @@ -199,6 +199,23 @@ MARKETING_FROM_PATTERNS = [ "promotion@", "promo@", "no-reply@mail.", ] +# Booking.com: noreply@ mischt echte Buchungsmails mit Genius-/Rabatt-Spam. +BOOKING_REAL_SUBJECT = [ + "confirmation", "confirmed", "bestaetigung", "bestätigung", "buchung", + "reservation", "reservierung", "booking number", "buchungsnummer", + "cancelled", "canceled", "storn", "modified", "geändert", "geaendert", + "new message", "neue nachricht", "guest message", "nachricht von", + "payment", "zahlung", "invoice", "rechnung", "receipt", "beleg", + "check-in", "checkin", "pin code", "türcode", "tuercode", "door code", + "cancellation", "refund", "erstattung", +] +BOOKING_MARKETING_SUBJECT = [ + "genius", "discount", "rabatt", "deal", "angebot", "% off", "%off", + "extending", "fancy", "inspired", "wishlist", "save on", "sparen", + "reminder:", "ready to use", "special offer", "last chance", + "you might like", "entdecken", "explore", "trip ideas", +] + def _is_marketing_mail(frm_lower: str) -> bool: """Werbemails von sonst wichtigen Absendern (z.B. booking.com campaign@).""" @@ -210,6 +227,19 @@ def _is_spam_sender(frm_lower: str) -> bool: return any(s in frm_lower for s in SPAM_SENDERS) +def _is_booking_noise(frm_lower: str, subj_lower: str) -> bool: + """Booking.com Marketing ausfiltern; echte Buchungs-/Gast-Mails behalten.""" + if "booking.com" not in frm_lower: + return False + if any(p in subj_lower for p in BOOKING_REAL_SUBJECT): + return False + if any(p in subj_lower for p in BOOKING_MARKETING_SUBJECT): + return True + if "noreply@" in frm_lower or "campaign@" in frm_lower or "email.campaign@" in frm_lower: + return True + return False + + def get_important_mails(hours: int = 24) -> list[dict]: """Mails von wichtigen Absendern (Bank, Hoster, etc.), Spam-Sender ausgefiltert.""" m = _connect() @@ -226,10 +256,13 @@ def get_important_mails(hours: int = 24) -> list[dict]: parsed = _parse_mail(msg_data) if parsed: frm_lower = parsed["from"].lower() + subj_lower = (parsed.get("subject") or "").lower() if _is_spam_sender(frm_lower): continue if _is_marketing_mail(frm_lower): continue + if _is_booking_noise(frm_lower, subj_lower): + continue if any(s in frm_lower for s in IMPORTANT_SENDERS): results.append(parsed) results.reverse() diff --git a/homelab-ai-bot/monitor.py b/homelab-ai-bot/monitor.py index bccbdc73e..623099bed 100644 --- a/homelab-ai-bot/monitor.py +++ b/homelab-ai-bot/monitor.py @@ -409,8 +409,9 @@ def check_all(state: dict | None = None) -> list[str]: mail_client.init(cfg) important = mail_client.get_important_mails(hours=1) if important and "error" not in important[0]: - state = _load_alert_state() - seen = state.get("seen_mails", {}) + # Am gemeinsamen state-Objekt arbeiten (kein Reload/Zwischen-Save), + # sonst ueberschreibt run_check_and_alert() die seen_mails wieder. + seen = state.setdefault("seen_mails", {}) now = datetime.now(timezone.utc).timestamp() new_mails = [] @@ -422,9 +423,7 @@ def check_all(state: dict | None = None) -> list[str]: new_mails.append(m) seen[fp] = now - seen = {k: v for k, v in seen.items() if now - v < 172800} - state["seen_mails"] = seen - _save_alert_state(state) + state["seen_mails"] = {k: v for k, v in seen.items() if now - v < 172800} if new_mails: senders = [m["from"][:30] for m in new_mails]