Initial commit: VoIdeaAI - voice-first AI idea assistant
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user