"""Best-effort SMTP email via the germainebrown.com relay (mail.germainebrown.com:2525, STARTTLS). Email failure must NEVER fail the API request — wrap every send in try/except, log, continue. Per conductor decision #3. """ from __future__ import annotations import logging import os import smtplib import ssl from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart logger = logging.getLogger("dre.email") def _env(name: str, default: str = "") -> str: return os.environ.get(name, default) def send_email(to_addr: str, subject: str, body_text: str, html: str | None = None) -> bool: """Send an email via the configured SMTP relay. Returns True on success, False on failure. Never raises — caller proceeds regardless.""" host = _env("DRE_SMTP_HOST", "mail.germainebrown.com") port = int(_env("DRE_SMTP_PORT", "2525")) user = _env("DRE_SMTP_USER", "") pw = _env("DRE_SMTP_PASS", "") sender = _env("DRE_SMTP_FROM", "dre@debtrecoveryexperts.com") try: msg = MIMEMultipart("alternative") msg["From"] = sender msg["To"] = to_addr msg["Subject"] = subject msg.attach(MIMEText(body_text, "plain", "utf-8")) if html: msg.attach(MIMEText(html, "html", "utf-8")) with smtplib.SMTP(host, port, timeout=15) as server: server.starttls(context=ssl.create_default_context()) if user and pw: server.login(user, pw) server.sendmail(sender, [to_addr], msg.as_string()) return True except Exception as exc: # noqa: BLE001 — best-effort logger.error("email send failed to=%s subject=%s err=%s", to_addr, subject, exc) return False def notify_team_intake(claim_number: str, client_number: str, company_name: str, amount_cents: int, debtor_name: str) -> bool: team = _env("DRE_TEAM_NOTIFY", "dre@debtrecoveryexperts.com") base = _env("DRE_BASE_URL", "https://portal.debtrecoveryexperts.com") dollars = amount_cents / 100.0 body = ( f"New claim submitted via portal.\n\n" f"Claim: {claim_number}\n" f"Client: {client_number} — {company_name}\n" f"Debtor: {debtor_name}\n" f"Amount: ${dollars:,.2f}\n\n" f"Review at: {base}/\n" ) html = ( f"

New claim submitted

" f"

Claim: {claim_number}
" f"Client: {client_number} — {company_name}
" f"Debtor: {debtor_name}
" f"Amount: ${dollars:,.2f}

" f"

Review in portal

" ) return send_email(team, f"New DRE Claim: {claim_number}", body, html) def send_magic_link(to_addr: str, raw_token: str, client_number: str) -> bool: base = _env("DRE_BASE_URL", "https://portal.debtrecoveryexperts.com") link = f"{base}/portal/verify?token={raw_token}" body = ( f"Hello,\n\n" f"Click the link below to log in to your DRE client portal. " f"This link expires in 15 minutes and can only be used once.\n\n" f"{link}\n\n" f"If you did not request this link, you can ignore this email.\n" ) html = ( f"

Hello,

" f"

Click the button below to log in to your DRE client portal. " f"This link expires in 15 minutes and can only be used once.

" f"

Log In

" f"

If the button doesn't work, copy this link: {link}

" f"

If you did not request this link, you can ignore this email.

" ) return send_email(to_addr, "Your DRE Portal Login Link", body, html) def notify_team_message(claim_number: str, subject: str, content: str, author: str) -> bool: team = _env("DRE_TEAM_NOTIFY", "dre@debtrecoveryexperts.com") body = ( f"New client message on claim {claim_number}.\n\n" f"From: {author}\n" f"Subject: {subject}\n\n" f"{content}\n" ) return send_email(team, f"Client message on {claim_number}: {subject}", body)