Files
gogo-telefon/gogo/email/sender.py

93 lines
2.4 KiB
Python
Raw Normal View History

"""EmailProvider interface: SMTP for production, console for dev/tests."""
from __future__ import annotations
import logging
from dataclasses import dataclass, field
from email.message import EmailMessage as MimeMessage
from typing import Protocol
import aiosmtplib
from gogo.config import get_settings
log = logging.getLogger("gogo.email")
@dataclass
class EmailMessage:
to: list[str]
subject: str
text: str
html: str | None = None
# attachments: (filename, mimetype, bytes)
attachments: list[tuple[str, str, bytes]] = field(default_factory=list)
class EmailProvider(Protocol):
async def send(self, msg: EmailMessage) -> None: ...
class ConsoleEmailProvider:
"""Logs emails instead of sending; keeps them in memory for tests."""
sent: list[EmailMessage]
def __init__(self) -> None:
self.sent = []
async def send(self, msg: EmailMessage) -> None:
self.sent.append(msg)
log.info(
"EMAIL to=%s subject=%r attachments=%d\n%s",
msg.to,
msg.subject,
len(msg.attachments),
msg.text,
)
class SmtpEmailProvider:
async def send(self, msg: EmailMessage) -> None:
s = get_settings()
mime = MimeMessage()
mime["From"] = s.email_from
mime["To"] = ", ".join(msg.to)
mime["Subject"] = msg.subject
mime.set_content(msg.text)
if msg.html:
mime.add_alternative(msg.html, subtype="html")
for filename, mimetype, data in msg.attachments:
maintype, subtype = mimetype.split("/", 1)
mime.add_attachment(data, maintype=maintype, subtype=subtype, filename=filename)
await aiosmtplib.send(
mime,
hostname=s.smtp_host,
port=s.smtp_port,
username=s.smtp_user or None,
password=s.smtp_password or None,
start_tls=s.smtp_starttls,
)
_provider: EmailProvider | None = None
def get_email_provider() -> EmailProvider:
global _provider
if _provider is None:
_provider = (
SmtpEmailProvider() if get_settings().email_provider == "smtp" else ConsoleEmailProvider()
)
return _provider
def set_email_provider(p: EmailProvider | None) -> None:
"""Test hook."""
global _provider
_provider = p
async def send_email(msg: EmailMessage) -> None:
await get_email_provider().send(msg)