diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/mail.py')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/mail.py | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/mail.py b/packages/meshbay-hub/src/meshbay_hub/mail.py new file mode 100644 index 0000000..ce6e264 --- /dev/null +++ b/packages/meshbay-hub/src/meshbay_hub/mail.py @@ -0,0 +1,97 @@ +""" +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}" |