114 lines
3.6 KiB
Python
114 lines
3.6 KiB
Python
"""Noise detection helpers for the operator work queue.
|
||
|
||
These helpers are intentionally conservative. They identify messages that are
|
||
not useful ClientFlow work items because they are system delivery reports,
|
||
postmaster notifications, automatic bounces/NDRs, or similar mailbox noise.
|
||
Chatwoot/Thunderbird remain the source for reading those emails; ClientFlow
|
||
should not turn them into tasks/opportunities.
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import re
|
||
from typing import Any, Mapping
|
||
|
||
SYSTEM_SENDER_PATTERNS = [
|
||
r"\bpostmaster\b",
|
||
r"\bmailer[-_ ]daemon\b",
|
||
r"\bmail delivery subsystem\b",
|
||
r"\bmail delivery system\b",
|
||
r"\bmicrosoft exchange\b",
|
||
r"\boffice 365\b",
|
||
r"\bmicrosoft outlook\b",
|
||
]
|
||
|
||
BOUNCE_NDR_PATTERNS = [
|
||
r"\bundeliverable\b",
|
||
r"\breturned mail\b",
|
||
r"\bmail delivery failed\b",
|
||
r"\bdelivery status notification\b",
|
||
r"\bnon[- ]delivery report\b",
|
||
r"\bdelivery has failed\b",
|
||
r"\bfailure notice\b",
|
||
r"\bunknown to address\b",
|
||
r"\brecipient (?:wasn['’]?t|was not) found\b",
|
||
r"\byour message (?:to .* )?couldn['’]?t be delivered\b",
|
||
r"\bremote server returned\b",
|
||
r"\b550\s+5\.1\.1\b",
|
||
r"\b5\.1\.10\b",
|
||
r"\bwasn['’]?t found at\b",
|
||
]
|
||
|
||
TECHNICAL_NOISE_ACTION_CODES = {
|
||
"IGNORE_BOUNCE",
|
||
"IGNORE_SPAM",
|
||
"NO_ACTION",
|
||
}
|
||
|
||
LOW_VALUE_NO_OPPORTUNITY_ACTION_CODES = {
|
||
"REMOVE_FROM_LIST",
|
||
}
|
||
|
||
|
||
def normalize_noise_text(value: Any) -> str:
|
||
"""Normalize text for deterministic noise matching."""
|
||
text = str(value or "").casefold()
|
||
text = re.sub(r"<[^>]+>", " ", text)
|
||
text = re.sub(r"https?://\S+", " ", text)
|
||
text = re.sub(r"\s+", " ", text).strip()
|
||
return text
|
||
|
||
|
||
def contains_any_pattern(text: str, patterns: list[str]) -> bool:
|
||
normalized = normalize_noise_text(text)
|
||
return any(re.search(pattern, normalized, flags=re.I) for pattern in patterns)
|
||
|
||
|
||
def is_system_sender(value: Any) -> bool:
|
||
return contains_any_pattern(str(value or ""), SYSTEM_SENDER_PATTERNS)
|
||
|
||
|
||
def is_bounce_or_ndr_text(value: Any) -> bool:
|
||
return contains_any_pattern(str(value or ""), SYSTEM_SENDER_PATTERNS + BOUNCE_NDR_PATTERNS)
|
||
|
||
|
||
def _join_item_text(item: Mapping[str, Any]) -> str:
|
||
return "\n".join(
|
||
str(item.get(key) or "")
|
||
for key in (
|
||
"customer_name",
|
||
"contact_display_name",
|
||
"fiscal_customer_name",
|
||
"message_subject",
|
||
"title",
|
||
"detail",
|
||
"request_text",
|
||
"source_system",
|
||
"action_code",
|
||
"queue",
|
||
)
|
||
)
|
||
|
||
|
||
def is_noise_operation_item(item: Mapping[str, Any]) -> bool:
|
||
"""Return True for items that should not appear in Operations."""
|
||
action_code = str(item.get("action_code") or "").strip().upper()
|
||
status = str(item.get("status") or "").strip().lower()
|
||
reason = str(item.get("no_opportunity_reason") or "").strip().lower()
|
||
|
||
if status in {"ignored", "skipped", "cancelled"}:
|
||
return True
|
||
if action_code in TECHNICAL_NOISE_ACTION_CODES:
|
||
return True
|
||
if reason in {"system_or_bounce_message", "bounce_ignored", "non_commercial_system"}:
|
||
return True
|
||
return is_bounce_or_ndr_text(_join_item_text(item))
|
||
|
||
|
||
def is_low_value_no_opportunity_item(item: Mapping[str, Any]) -> bool:
|
||
"""Return True for non-commercial work that can be hidden from the main queue."""
|
||
action_code = str(item.get("action_code") or "").strip().upper()
|
||
reason = str(item.get("no_opportunity_reason") or "").strip().lower()
|
||
if action_code in LOW_VALUE_NO_OPPORTUNITY_ACTION_CODES:
|
||
return True
|
||
return reason in {"marketing_without_commercial_intent", "non_commercial"}
|