mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-10 04:08:28 +08:00
fix(gateway): filter automated/noreply senders in email adapter
Fixes #3453 Adds noreply/automated sender filtering to the email adapter. Drops emails from noreply, mailer-daemon, postmaster addresses and bulk mail headers (Auto-Submitted, Precedence, List-Unsubscribe) before dispatching. Prevents pairing codes and AI responses being sent to automated senders.
This commit is contained in:
@@ -43,6 +43,20 @@ from gateway.platforms.base import (
|
|||||||
from gateway.config import Platform, PlatformConfig
|
from gateway.config import Platform, PlatformConfig
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
# Automated sender patterns — emails from these are silently ignored
|
||||||
|
_NOREPLY_PATTERNS = (
|
||||||
|
"noreply", "no-reply", "no_reply", "donotreply", "do-not-reply",
|
||||||
|
"mailer-daemon", "postmaster", "bounce", "notifications@",
|
||||||
|
"automated@", "auto-confirm", "auto-reply", "automailer",
|
||||||
|
)
|
||||||
|
|
||||||
|
# RFC headers that indicate bulk/automated mail
|
||||||
|
_AUTOMATED_HEADERS = {
|
||||||
|
"Auto-Submitted": lambda v: v.lower() != "no",
|
||||||
|
"Precedence": lambda v: v.lower() in ("bulk", "list", "junk"),
|
||||||
|
"X-Auto-Response-Suppress": lambda v: bool(v),
|
||||||
|
"List-Unsubscribe": lambda v: bool(v),
|
||||||
|
}
|
||||||
|
|
||||||
# Gmail-safe max length per email body
|
# Gmail-safe max length per email body
|
||||||
MAX_MESSAGE_LENGTH = 50_000
|
MAX_MESSAGE_LENGTH = 50_000
|
||||||
@@ -50,7 +64,17 @@ MAX_MESSAGE_LENGTH = 50_000
|
|||||||
# Supported image extensions for inline detection
|
# Supported image extensions for inline detection
|
||||||
_IMAGE_EXTS = {".jpg", ".jpeg", ".png", ".gif", ".webp"}
|
_IMAGE_EXTS = {".jpg", ".jpeg", ".png", ".gif", ".webp"}
|
||||||
|
|
||||||
|
def _is_automated_sender(address: str, headers: dict) -> bool:
|
||||||
|
"""Return True if this email is from an automated/noreply source."""
|
||||||
|
addr = address.lower()
|
||||||
|
if any(pattern in addr for pattern in _NOREPLY_PATTERNS):
|
||||||
|
return True
|
||||||
|
for header, check in _AUTOMATED_HEADERS.items():
|
||||||
|
value = headers.get(header, "")
|
||||||
|
if value and check(value):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def check_email_requirements() -> bool:
|
def check_email_requirements() -> bool:
|
||||||
"""Check if email platform dependencies are available."""
|
"""Check if email platform dependencies are available."""
|
||||||
addr = os.getenv("EMAIL_ADDRESS")
|
addr = os.getenv("EMAIL_ADDRESS")
|
||||||
@@ -346,6 +370,12 @@ class EmailAdapter(BasePlatformAdapter):
|
|||||||
subject = _decode_header_value(msg.get("Subject", "(no subject)"))
|
subject = _decode_header_value(msg.get("Subject", "(no subject)"))
|
||||||
message_id = msg.get("Message-ID", "")
|
message_id = msg.get("Message-ID", "")
|
||||||
in_reply_to = msg.get("In-Reply-To", "")
|
in_reply_to = msg.get("In-Reply-To", "")
|
||||||
|
# Skip automated/noreply senders before any processing
|
||||||
|
msg_headers = dict(msg.items())
|
||||||
|
if _is_automated_sender(sender_addr, msg_headers):
|
||||||
|
logger.debug("[Email] Skipping automated sender: %s", sender_addr)
|
||||||
|
self._seen_uids.add(uid)
|
||||||
|
continue
|
||||||
body = _extract_text_body(msg)
|
body = _extract_text_body(msg)
|
||||||
attachments = _extract_attachments(msg, skip_attachments=self._skip_attachments)
|
attachments = _extract_attachments(msg, skip_attachments=self._skip_attachments)
|
||||||
|
|
||||||
@@ -373,6 +403,11 @@ class EmailAdapter(BasePlatformAdapter):
|
|||||||
# Skip self-messages
|
# Skip self-messages
|
||||||
if sender_addr == self._address.lower():
|
if sender_addr == self._address.lower():
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Never reply to automated senders
|
||||||
|
if _is_automated_sender(sender_addr, {}):
|
||||||
|
logger.debug("[Email] Dropping automated sender at dispatch: %s", sender_addr)
|
||||||
|
return
|
||||||
|
|
||||||
subject = msg_data["subject"]
|
subject = msg_data["subject"]
|
||||||
body = msg_data["body"].strip()
|
body = msg_data["body"].strip()
|
||||||
|
|||||||
Reference in New Issue
Block a user