""" MeshBay Hub — email sending via localhost Postfix. Postfix listens on loopback only (inet_interfaces = loopback-only), so no authentication is needed. See docs/MAIL-SERVER.md for the full setup. """ import logging import smtplib from email.message import EmailMessage log = logging.getLogger(__name__) _hub_domain: str = "meshbay.org" _hub_url: str = "https://meshbay.org" def configure(hub_id: str) -> None: global _hub_domain, _hub_url _hub_domain = hub_id _hub_url = f"https://{hub_id}" def _send(msg: EmailMessage) -> bool: try: with smtplib.SMTP("localhost", 25, timeout=10) as s: s.send_message(msg) return True except Exception: log.exception("Failed to send email to %s", msg["To"]) return False def send_verification_code(to: str, code: str) -> None: msg = EmailMessage() msg["From"] = f"noreply@{_hub_domain}" msg["To"] = to msg["Subject"] = f"MeshBay — Your verification code: {code}" msg.set_content( f"Your verification code is: {code}\n" "\n" "Enter this code to verify your email address.\n" "This code expires in 24 hours.\n" "\n" "If you did not create a MeshBay account, ignore this email.\n" "\n" f"{_hub_url}\n" ) _send(msg) log.info("Verification code sent to %s", _mask_email(to)) def send_email_change_code(to: str, code: str) -> None: msg = EmailMessage() msg["From"] = f"noreply@{_hub_domain}" msg["To"] = to msg["Subject"] = f"MeshBay — Confirm your new email: {code}" msg.set_content( f"Your verification code is: {code}\n" "\n" "Enter this code to confirm your new email address.\n" "This code expires in 24 hours.\n" "\n" "If you did not request this change, ignore this email.\n" "\n" f"{_hub_url}\n" ) _send(msg) log.info("Email change code sent to %s", _mask_email(to)) def send_invite_notification( to: str, code: str, inviter: str, group_name: str, ) -> None: msg = EmailMessage() msg["From"] = f"noreply@{_hub_domain}" msg["To"] = to msg["Subject"] = f"MeshBay — {inviter} invited you to {group_name}" msg.set_content( f"{inviter} invited you to the group \"{group_name}\" on MeshBay.\n" "\n" f"Your one-time code is: {code}\n" "\n" "Open the group and enter this code when prompted.\n" "The code works once and expires in 7 days.\n" "\n" f"{_hub_url}\n" ) _send(msg) log.info("Invite notification sent to %s", _mask_email(to)) def _mask_email(email: str) -> str: local, _, domain = email.partition("@") if len(local) <= 2: return f"{'*' * len(local)}@{domain}" return f"{local[0]}{'*' * (len(local) - 2)}{local[-1]}@{domain}"