Files
voidea/app/services/email_service.py
T

103 lines
2.9 KiB
Python

"""Email notification service for VoIdea.
Uses aiosmtplib for async SMTP with Jinja2 templates.
Falls back to logging when SMTP is not configured.
"""
import logging
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from pathlib import Path
from jinja2 import Environment, FileSystemLoader, select_autoescape
from app.core.config import get_settings
logger = logging.getLogger(__name__)
TEMPLATES_DIR = Path(__file__).parent.parent / "templates" / "email"
_env = Environment(loader=FileSystemLoader(str(TEMPLATES_DIR)), autoescape=select_autoescape())
def _render(template_name: str, context: dict) -> str:
"""Render an email template.
Args:
template_name: Name of the template file (e.g. "welcome.html")
context: Template variables
Returns:
Rendered HTML string, or plain text fallback
"""
try:
template = _env.get_template(template_name)
return template.render(**context)
except Exception:
lines = [f"{k}: {v}" for k, v in context.items()]
return "\n".join(lines)
async def send_email(to: str, subject: str, html: str) -> bool:
"""Send an email via SMTP, or log to console if SMTP not configured.
Args:
to: Recipient email
subject: Email subject
html: HTML body
Returns:
True if sent (or logged) successfully, False on SMTP error
"""
settings = get_settings()
if not settings.smtp_host:
logger.info(
"SMTP not configured — email logged instead:\n"
" To: %s\n Subject: %s\n Body:\n%s",
to, subject, html,
)
return True
try:
import aiosmtplib
msg = MIMEMultipart("alternative")
msg["Subject"] = subject
msg["From"] = settings.smtp_from
msg["To"] = to
msg.attach(MIMEText(html, "html"))
await aiosmtplib.send(
msg,
hostname=settings.smtp_host,
port=settings.smtp_port,
username=settings.smtp_user or None,
password=settings.smtp_pass or None,
use_tls=settings.smtp_tls,
)
return True
except Exception:
return False
async def send_welcome_email(to: str, username: str) -> bool:
"""Send welcome email after registration.
Args:
to: Recipient email
username: User display name
"""
html = _render("welcome.html", {"username": username, "project_name": "VoIdea"})
return await send_email(to, f"Добро пожаловать в VoIdea!", html)
async def send_notification_email(to: str, subject: str, message: str) -> bool:
"""Send a notification email.
Args:
to: Recipient email
subject: Email subject
message: Plain text or HTML message
"""
html = _render("notification.html", {"subject": subject, "message": message})
return await send_email(to, subject, html)