"""Construction et remise des emails. `deliver_many` est le point d'injection unique (remplacé dans les tests).""" import logging import smtplib import ssl from collections.abc import Iterable from email.message import EmailMessage from app.config import get_settings logger = logging.getLogger("app.email") def build_message(to: str, subject: str, body: str) -> EmailMessage: message = EmailMessage() message["From"] = get_settings().smtp_from message["To"] = to message["Subject"] = subject message.set_content(body) return message def deliver_many(messages: list[EmailMessage]) -> None: settings = get_settings() if not settings.email_enabled: # RGPD : ni destinataire ni corps dans les logs. logger.info( "Emails non envoyés (EMAIL_ENABLED=false) — %d message(s)", len(messages), ) return if not messages: return context = ssl.create_default_context() if settings.smtp_ssl: client = smtplib.SMTP_SSL( settings.smtp_host, settings.smtp_port, timeout=30, context=context ) else: client = smtplib.SMTP( settings.smtp_host, settings.smtp_port, timeout=30 ) with client as smtp: if settings.smtp_starttls and not settings.smtp_ssl: smtp.starttls(context=context) if settings.smtp_user: smtp.login(settings.smtp_user, settings.smtp_password) for message in messages: try: smtp.send_message(message) except smtplib.SMTPException: logger.warning("Échec d'envoi d'un email (destinataire masqué)") def send(to: str, subject: str, body: str) -> None: deliver_many([build_message(to, subject, body)]) def send_many(items: Iterable[tuple[str, str, str]]) -> None: """`items` = itérable de `(destinataire, sujet, corps)`.""" deliver_many([build_message(*item) for item in items])