77 lines
2.0 KiB
Python
77 lines
2.0 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from email.message import EmailMessage
|
|
from pathlib import Path
|
|
|
|
import aiosmtplib
|
|
|
|
|
|
@dataclass
|
|
class MailSendResult:
|
|
ok: bool
|
|
error: str | None
|
|
|
|
|
|
class MailNotifier:
|
|
def __init__(
|
|
self,
|
|
smtp_host: str,
|
|
smtp_port: int,
|
|
username: str,
|
|
password: str,
|
|
from_addr: str,
|
|
to_addr: str,
|
|
use_starttls: bool = True,
|
|
use_tls: bool = False,
|
|
timeout_seconds: float = 15,
|
|
) -> None:
|
|
self.smtp_host = smtp_host
|
|
self.smtp_port = smtp_port
|
|
self.username = username
|
|
self.password = password
|
|
self.from_addr = from_addr
|
|
self.to_addr = to_addr
|
|
self.use_starttls = use_starttls
|
|
self.use_tls = use_tls
|
|
self.timeout_seconds = timeout_seconds
|
|
|
|
async def send_message(
|
|
self,
|
|
subject: str,
|
|
body: str,
|
|
attachments: list[str] | None = None,
|
|
) -> MailSendResult:
|
|
message = EmailMessage()
|
|
message["From"] = self.from_addr
|
|
message["To"] = self.to_addr
|
|
message["Subject"] = subject
|
|
message.set_content(body)
|
|
|
|
for attachment in attachments or []:
|
|
path = Path(attachment)
|
|
if not path.exists() or not path.is_file():
|
|
continue
|
|
|
|
data = path.read_bytes()
|
|
message.add_attachment(
|
|
data,
|
|
maintype="image",
|
|
subtype="png",
|
|
filename=path.name,
|
|
)
|
|
|
|
try:
|
|
await aiosmtplib.send(
|
|
message,
|
|
hostname=self.smtp_host,
|
|
port=self.smtp_port,
|
|
username=self.username,
|
|
password=self.password,
|
|
start_tls=self.use_starttls,
|
|
use_tls=self.use_tls,
|
|
timeout=self.timeout_seconds,
|
|
)
|
|
return MailSendResult(ok=True, error=None)
|
|
except Exception as exc:
|
|
return MailSendResult(ok=False, error=str(exc)) |