#!/usr/bin/env python3 """Check IMAP inbox for bounce-back / delivery-failure emails and alert if found. Silent (exit 0 with no output) when nothing to report. Outputs summary when bounces are found. """ import imaplib import email from email.utils import parsedate_to_datetime import os import sys from datetime import datetime, timedelta, timezone HOST = "mail.germainebrown.com" PORT = 993 USER = "g@germainebrown.com" PW_FILE = "/root/.config/himalaya/g-germainebrown.pass" CHECK_HOURS = 24 BOUNCE_KEYWORDS = [ "mail delivery failed", "delivery status notification", "undelivered", "returned mail", "mail delivery failure", "non-delivery", "delivery failed", "undeliverable", "delivery report", "returned to sender", "message bounced", ] BOUNCE_SENDERS = [ "mailer-daemon", "postmaster", "mail delivery system", ] def safe_str(val): if val is None: return "" if isinstance(val, str): return val try: return str(val) except Exception: return "" def is_bounce(msg): subject = safe_str(msg.get("Subject", "")).lower() sender = safe_str(msg.get("From", "")).lower() for kw in BOUNCE_KEYWORDS: if kw in subject: return True for s in BOUNCE_SENDERS: if s in sender: return True if msg.get_content_type() == "text/plain": payload = msg.get_payload(decode=True) if payload: body = safe_str(payload).lower() if "permanent error" in body or "could not be delivered" in body: return True return False def main(): try: pw = open(PW_FILE).read().strip() except FileNotFoundError: print("[SILENT]") return since = (datetime.now(timezone.utc) - timedelta(hours=CHECK_HOURS)).strftime("%d-%b-%Y") try: conn = imaplib.IMAP4_SSL(HOST, PORT) conn.login(USER, pw) conn.select("INBOX") except Exception as e: print(f"ERROR: IMAP connection failed: {e}") sys.exit(1) status, data = conn.search(None, f'(SINCE {since})') if status != "OK": print("[SILENT]") conn.logout() return bounces = [] for num in data[0].split(): status, msg_data = conn.fetch(num, "(RFC822)") if status != "OK": continue raw = email.message_from_bytes(msg_data[0][1]) if is_bounce(raw): subject = raw.get("Subject", "(no subject)") sender = raw.get("From", "(unknown)") date = raw.get("Date", "") bounces.append({ "subject": subject, "from": sender, "date": date, "uid": num.decode(), }) conn.logout() if not bounces: return print(f"📬 {len(bounces)} bounce-back(s) detected in the last {CHECK_HOURS}h:\n") for b in bounces: print(f" • {b['subject']}") print(f" From: {b['from']}") print(f" Date: {b['date']}") print() if __name__ == "__main__": main()