99 lines
3 KiB
Python
99 lines
3 KiB
Python
import logging
|
|
|
|
from django.conf import settings
|
|
from django.core.mail import EmailMessage, get_connection
|
|
|
|
from infrasynth.shared.enums import ChannelType
|
|
from infrasynth.shared.results import Result
|
|
from infrasynth.shared.settings_utils import get_setting
|
|
|
|
from .base import BaseChannel
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class SMTPChannel(BaseChannel):
|
|
"""Email channel that sends via a configurable SMTP backend.
|
|
|
|
Configuration keys (read from ``ChannelConfig.config``):
|
|
- ``host``, ``port``, ``username``, ``password``
|
|
- ``use_tls``, ``use_ssl``, ``timeout``
|
|
- ``backend`` (dotted path, defaults to Django's SMTP backend)
|
|
- ``from_email`` (falls back to DEFAULT_FROM_EMAIL / INFRASYNTH_NOTIFICATIONS)
|
|
"""
|
|
|
|
channel_type = ChannelType.EMAIL
|
|
|
|
def __init__(
|
|
self,
|
|
host=None,
|
|
port=None,
|
|
username=None,
|
|
password=None,
|
|
use_tls=True,
|
|
use_ssl=False,
|
|
timeout=30,
|
|
backend=None,
|
|
from_email=None,
|
|
):
|
|
self.host = host
|
|
self.port = port or 587
|
|
self.username = username
|
|
self.password = password
|
|
self.use_tls = use_tls
|
|
self.use_ssl = use_ssl
|
|
self.timeout = timeout
|
|
self.backend = backend or "django.core.mail.backends.smtp.EmailBackend"
|
|
self.from_email = (
|
|
from_email
|
|
or getattr(settings, "DEFAULT_FROM_EMAIL", None)
|
|
or get_setting("INFRASYNTH_NOTIFICATIONS", "DEFAULT_FROM_EMAIL", "noreply@example.com")
|
|
)
|
|
|
|
@classmethod
|
|
def from_config(cls, config: dict) -> "SMTPChannel":
|
|
config = {str(key).lower(): value for key, value in (config or {}).items()}
|
|
return cls(**config)
|
|
|
|
def send(
|
|
self,
|
|
recipient: str,
|
|
subject: str,
|
|
body: str,
|
|
is_html: bool = True,
|
|
attachments=None,
|
|
) -> Result[bool, str]:
|
|
if not self.host:
|
|
return Result.err("SMTP host not configured")
|
|
try:
|
|
message = EmailMessage(
|
|
subject=subject,
|
|
body=body,
|
|
from_email=self.from_email,
|
|
to=[recipient],
|
|
connection=self._get_connection(),
|
|
)
|
|
if is_html:
|
|
message.content_subtype = "html"
|
|
for attachment in attachments or []:
|
|
message.attach(attachment.filename, attachment.content, attachment.mime_type)
|
|
message.send()
|
|
except Exception as exc: # noqa: BLE001
|
|
logger.exception("SMTP send failed for %s", recipient)
|
|
return Result.err(str(exc))
|
|
return Result.ok(True)
|
|
|
|
def health_check(self) -> bool:
|
|
return bool(self.host)
|
|
|
|
def _get_connection(self):
|
|
return get_connection(
|
|
backend=self.backend,
|
|
host=self.host or None,
|
|
port=self.port,
|
|
username=self.username or "",
|
|
password=self.password or "",
|
|
use_tls=self.use_tls,
|
|
use_ssl=self.use_ssl,
|
|
timeout=self.timeout,
|
|
)
|