from __future__ import annotations from dataclasses import dataclass from email.message import EmailMessage import smtplib import ssl @dataclass class MailServerSettings: smtp_host: str smtp_port: int smtp_username: str | None smtp_password: str | None from_email: str from_name: str use_starttls: bool use_ssl: bool verify_tls: bool timeout_seconds: int = 15 def _build_context(verify_tls: bool) -> ssl.SSLContext: context = ssl.create_default_context() if verify_tls: return context context.check_hostname = False context.verify_mode = ssl.CERT_NONE return context def send_email( *, settings: MailServerSettings, to_email: str, subject: str, text_body: str, ) -> None: if not settings.smtp_host.strip(): raise ValueError("SMTP host is empty") if not settings.from_email.strip(): raise ValueError("From email is empty") msg = EmailMessage() msg["Subject"] = subject msg["From"] = f"{settings.from_name} <{settings.from_email}>" msg["To"] = to_email msg.set_content(text_body) ssl_context = _build_context(settings.verify_tls) if settings.use_ssl: with smtplib.SMTP_SSL( settings.smtp_host, settings.smtp_port, timeout=settings.timeout_seconds, context=ssl_context, ) as smtp: if settings.smtp_username: smtp.login(settings.smtp_username, settings.smtp_password or "") smtp.send_message(msg) return with smtplib.SMTP(settings.smtp_host, settings.smtp_port, timeout=settings.timeout_seconds) as smtp: smtp.ehlo() if settings.use_starttls: smtp.starttls(context=ssl_context) smtp.ehlo() if settings.smtp_username: smtp.login(settings.smtp_username, settings.smtp_password or "") smtp.send_message(msg)