|
| 1 | +from attrs import define, field |
1 | 2 | import smtplib |
2 | 3 | from email.mime.multipart import MIMEMultipart |
3 | 4 | from email.mime.text import MIMEText |
|
15 | 16 | logger = AppLogger().get_logger() |
16 | 17 |
|
17 | 18 |
|
| 19 | +@define |
18 | 20 | class SMTPEmailService(metaclass=SingletonMetaNoArgs): |
19 | | - def __init__(self): |
20 | | - self.server = smtplib.SMTP( |
21 | | - global_settings.smtp.server, global_settings.smtp.port |
22 | | - ) |
| 21 | + # SMTP configuration |
| 22 | + server_host: str = field(default=global_settings.smtp.server) |
| 23 | + server_port: int = field(default=global_settings.smtp.port) |
| 24 | + username: str = field(default=global_settings.smtp.username) |
| 25 | + password: str = field(default=global_settings.smtp.password) |
| 26 | + |
| 27 | + # Dependencies |
| 28 | + templates: Jinja2Templates = field( |
| 29 | + factory=lambda: Jinja2Templates(global_settings.templates_dir) |
| 30 | + ) |
| 31 | + server: smtplib.SMTP = field(init=False) # Deferred initialization in post-init |
| 32 | + |
| 33 | + def __attrs_post_init__(self): |
| 34 | + """Initialize the SMTP server connection after object creation.""" |
| 35 | + self.server = smtplib.SMTP(self.server_host, self.server_port) |
23 | 36 | self.server.starttls() |
24 | | - self.server.login(global_settings.smtp.username, global_settings.smtp.password) |
25 | | - self.templates = Jinja2Templates("templates") |
| 37 | + self.server.login(self.username, self.password) |
26 | 38 |
|
27 | | - def send_email( |
| 39 | + def _prepare_email( |
28 | 40 | self, |
29 | 41 | sender: EmailStr, |
30 | 42 | recipients: list[EmailStr], |
31 | 43 | subject: str, |
32 | | - body_text: str = "", |
33 | | - body_html=None, |
34 | | - ): |
| 44 | + body_text: str, |
| 45 | + body_html: str, |
| 46 | + ) -> MIMEMultipart: |
| 47 | + """Prepare the email message.""" |
35 | 48 | msg = MIMEMultipart() |
36 | 49 | msg["From"] = sender |
37 | 50 | msg["To"] = ",".join(recipients) |
38 | 51 | msg["Subject"] = subject |
39 | 52 | msg.attach(MIMEText(body_text, "plain")) |
40 | 53 | if body_html: |
41 | 54 | msg.attach(MIMEText(body_html, "html")) |
| 55 | + return msg |
| 56 | + |
| 57 | + def send_email( |
| 58 | + self, |
| 59 | + sender: EmailStr, |
| 60 | + recipients: list[EmailStr], |
| 61 | + subject: str, |
| 62 | + body_text: str = "", |
| 63 | + body_html: str = None, |
| 64 | + ): |
| 65 | + """Send a regular email (plain text or HTML).""" |
| 66 | + msg = self._prepare_email(sender, recipients, subject, body_text, body_html) |
42 | 67 | self.server.sendmail(sender, recipients, msg.as_string()) |
43 | 68 |
|
44 | 69 | def send_template_email( |
45 | 70 | self, |
46 | 71 | recipients: list[EmailStr], |
47 | 72 | subject: str, |
48 | | - template: str = None, |
49 | | - context: dict = None, |
50 | | - sender: EmailStr = global_settings.smtp.from_email, |
| 73 | + template: str, |
| 74 | + context: dict, |
| 75 | + sender: EmailStr, |
51 | 76 | ): |
| 77 | + """Send an email using a template with the provided context.""" |
52 | 78 | template_str = self.templates.get_template(template) |
53 | 79 | body_html = template_str.render(context) |
54 | 80 | self.send_email(sender, recipients, subject, body_html=body_html) |
0 commit comments