57 lines
1.7 KiB
Python
57 lines
1.7 KiB
Python
"""Email client for sending files via Gmail SMTP."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import smtplib
|
|
from email import encoders
|
|
from email.mime.base import MIMEBase
|
|
from email.mime.multipart import MIMEMultipart
|
|
from email.mime.text import MIMEText
|
|
from pathlib import Path
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
class EmailClient:
|
|
"""Send emails with attachments via SMTP_SSL (Gmail app-password friendly)."""
|
|
|
|
def __init__(self, smtp_host: str, smtp_port: int, username: str, password: str):
|
|
self.smtp_host = smtp_host
|
|
self.smtp_port = smtp_port
|
|
self.username = username
|
|
self.password = password
|
|
|
|
def send(
|
|
self,
|
|
to: str,
|
|
subject: str,
|
|
body: str,
|
|
attachments: list[Path] | None = None,
|
|
) -> None:
|
|
"""Send an email, optionally with file attachments.
|
|
|
|
Raises smtplib.SMTPException on failure.
|
|
"""
|
|
msg = MIMEMultipart()
|
|
msg["From"] = self.username
|
|
msg["To"] = to
|
|
msg["Subject"] = subject
|
|
msg.attach(MIMEText(body, "plain"))
|
|
|
|
for file_path in attachments or []:
|
|
part = MIMEBase("application", "octet-stream")
|
|
part.set_payload(file_path.read_bytes())
|
|
encoders.encode_base64(part)
|
|
part.add_header(
|
|
"Content-Disposition",
|
|
f"attachment; filename={file_path.name}",
|
|
)
|
|
msg.attach(part)
|
|
|
|
log.info("Sending email to %s — subject: %s", to, subject)
|
|
with smtplib.SMTP_SSL(self.smtp_host, self.smtp_port) as server:
|
|
server.login(self.username, self.password)
|
|
server.send_message(msg)
|
|
log.info("Email sent successfully to %s", to)
|