68 lines
3.9 KiB
Python
68 lines
3.9 KiB
Python
import html
|
|
import smtplib
|
|
import ssl
|
|
from email.message import EmailMessage
|
|
from email.utils import formataddr
|
|
|
|
from .models import AppSettings, User
|
|
|
|
|
|
def _settings_ready(settings: AppSettings | None) -> bool:
|
|
return bool(settings and settings.smtp_host and settings.smtp_from_email)
|
|
|
|
|
|
def send_html_email(settings: AppSettings, recipient: str, subject: str, body_html: str, body_text: str):
|
|
if not _settings_ready(settings):
|
|
raise ValueError("SMTP settings are incomplete")
|
|
|
|
message = EmailMessage()
|
|
message["Subject"] = subject
|
|
message["From"] = formataddr((settings.smtp_from_name or "Cluedo HP", settings.smtp_from_email))
|
|
message["To"] = recipient
|
|
message.set_content(body_text)
|
|
message.add_alternative(body_html, subtype="html")
|
|
|
|
context = ssl.create_default_context()
|
|
security = getattr(settings, "smtp_security", "") or ("starttls" if settings.smtp_use_tls else "none")
|
|
port = settings.smtp_port or (465 if security == "ssl" else 587)
|
|
smtp_client = smtplib.SMTP_SSL if security == "ssl" else smtplib.SMTP
|
|
connection = smtp_client(settings.smtp_host, port, timeout=20, context=context) if security == "ssl" else smtp_client(settings.smtp_host, port, timeout=20)
|
|
with connection as server:
|
|
server.ehlo()
|
|
if security == "starttls":
|
|
server.starttls(context=context)
|
|
server.ehlo()
|
|
if settings.smtp_username:
|
|
server.login(settings.smtp_username, settings.smtp_password)
|
|
server.send_message(message)
|
|
|
|
|
|
def send_user_invite(settings: AppSettings, user: User, invite_url: str):
|
|
name = html.escape(user.display_name or user.email)
|
|
safe_url = html.escape(invite_url, quote=True)
|
|
from_name = html.escape(settings.smtp_from_name or "Cluedo HP")
|
|
body_html = f"""
|
|
<!doctype html>
|
|
<html><body style="margin:0;background:#0b0b0f;color:#f5efdc;font-family:Georgia,serif;">
|
|
<div style="padding:36px 16px;background:radial-gradient(circle at top,#312b24 0,#0b0b0f 58%);">
|
|
<div style="max-width:580px;margin:0 auto;border:1px solid #6f603f;border-radius:22px;overflow:hidden;background:#17161b;box-shadow:0 18px 55px rgba(0,0,0,.55);">
|
|
<div style="padding:28px 30px;text-align:center;background:linear-gradient(135deg,#29231c,#141319);border-bottom:1px solid #6f603f;">
|
|
<div style="font-size:13px;letter-spacing:3px;text-transform:uppercase;color:#cbb982;">{from_name}</div>
|
|
<div style="margin-top:10px;font-size:28px;font-weight:bold;color:#e9d8a6;">Eine Einladung wartet</div>
|
|
</div>
|
|
<div style="padding:30px;line-height:1.55;color:#f5efdc !important;background:#17161b;">
|
|
<div style="font-size:20px;color:#e9d8a6 !important;">Hallo {name},</div>
|
|
<p style="color:#f5efdc !important;margin:16px 0;">du wurdest eingeladen, dem digitalen Zauber-Detektiv-Notizbogen beizutreten.</p>
|
|
<p style="color:#f5efdc !important;margin:16px 0;">Richte über den folgenden Button dein persönliches Passwort ein:</p>
|
|
<p style="text-align:center;margin:28px 0;"><a href="{safe_url}" style="display:inline-block;padding:14px 24px;border-radius:12px;background:#b69a5c;color:#171319;text-decoration:none;font-weight:bold;">Einladung annehmen</a></p>
|
|
<p style="font-size:13px;color:#b8ae98;">Der Link ist 48 Stunden gültig und kann nur einmal verwendet werden.</p>
|
|
<p style="font-size:12px;color:#8e8777;word-break:break-all;">{safe_url}</p>
|
|
</div>
|
|
<div style="padding:16px 30px;color:#8e8777;font-size:12px;border-top:1px solid #302c2b;text-align:center;">Deine Notizen bleiben privat.</div>
|
|
</div>
|
|
</div>
|
|
</body></html>
|
|
"""
|
|
body_text = f"Hallo {user.display_name or user.email},\n\nbitte richte dein Passwort ein: {invite_url}\n\nDer Link ist 48 Stunden gültig."
|
|
send_html_email(settings, user.email, "Deine Einladung zum Zauber-Detektiv-Notizbogen", body_html, body_text)
|