Sends HTML email via Gmail SMTP when notifications are created. Supports multi-language (en/ko/mn/ru) based on user country. Runs in background thread to avoid blocking requests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
188 lines
6.0 KiB
Python
188 lines
6.0 KiB
Python
"""
|
||
Email Notification Service
|
||
Sends email notifications to users for important events (recommendations, shipping, payments, etc.)
|
||
Reuses existing SMTP configuration (Gmail).
|
||
"""
|
||
import smtplib
|
||
import threading
|
||
from email.mime.text import MIMEText
|
||
from email.mime.multipart import MIMEMultipart
|
||
from typing import Optional
|
||
|
||
from ..config import get_settings
|
||
|
||
settings = get_settings()
|
||
|
||
# Notification types that should trigger email
|
||
EMAIL_ENABLED_TYPES = {
|
||
"vehicle_recommended",
|
||
"shipping_update",
|
||
"payment_confirmed",
|
||
"withdrawal_processed",
|
||
"inquiry_reply",
|
||
"dealer_approved",
|
||
"dealer_rejected",
|
||
"referral_reward",
|
||
"share_purchased",
|
||
}
|
||
|
||
|
||
def get_user_language(country: Optional[str]) -> str:
|
||
"""Detect language from user's country"""
|
||
if not country:
|
||
return "en"
|
||
c = country.lower().strip()
|
||
if c in ("korea", "south korea", "kr", "한국"):
|
||
return "ko"
|
||
if c in ("mongolia", "mn", "монгол"):
|
||
return "mn"
|
||
if c in ("russia", "ru", "россия"):
|
||
return "ru"
|
||
return "en"
|
||
|
||
|
||
def _build_email_content(
|
||
notification_type: str,
|
||
title: str,
|
||
message: str,
|
||
link: Optional[str],
|
||
user_name: Optional[str],
|
||
language: str,
|
||
) -> tuple[str, str]:
|
||
"""Build email subject and HTML body for a notification.
|
||
Returns (subject, html_body).
|
||
"""
|
||
base_url = "https://autonetsellcar.com"
|
||
full_link = f"{base_url}{link}" if link else base_url
|
||
|
||
# Greeting by language
|
||
greetings = {
|
||
"en": f"Hi {user_name}," if user_name else "Hello,",
|
||
"ko": f"{user_name}님, 안녕하세요." if user_name else "안녕하세요,",
|
||
"mn": f"Сайн байна уу, {user_name}." if user_name else "Сайн байна уу,",
|
||
"ru": f"Здравствуйте, {user_name}." if user_name else "Здравствуйте,",
|
||
}
|
||
|
||
button_labels = {
|
||
"en": "View Details",
|
||
"ko": "자세히 보기",
|
||
"mn": "Дэлгэрэнгүй үзэх",
|
||
"ru": "Подробнее",
|
||
}
|
||
|
||
footers = {
|
||
"en": "You received this email because you have an account on AutonetSellCar.com",
|
||
"ko": "AutonetSellCar.com 계정이 있어 이 이메일을 받으셨습니다",
|
||
"mn": "Та AutonetSellCar.com-д бүртгэлтэй тул энэ имэйлийг хүлээн авлаа",
|
||
"ru": "Вы получили это письмо, так как у вас есть аккаунт на AutonetSellCar.com",
|
||
}
|
||
|
||
greeting = greetings.get(language, greetings["en"])
|
||
button_label = button_labels.get(language, button_labels["en"])
|
||
footer = footers.get(language, footers["en"])
|
||
|
||
subject = f"AutonetSellCar - {title}"
|
||
|
||
html_body = f"""\
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head><meta charset="utf-8"></head>
|
||
<body style="margin:0;padding:0;background:#f4f4f4;font-family:Arial,sans-serif;">
|
||
<table width="100%" cellpadding="0" cellspacing="0" style="background:#f4f4f4;padding:20px 0;">
|
||
<tr><td align="center">
|
||
<table width="600" cellpadding="0" cellspacing="0" style="background:#ffffff;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.08);">
|
||
<!-- Header -->
|
||
<tr>
|
||
<td style="background:#1a1a2e;padding:24px 32px;">
|
||
<h1 style="margin:0;color:#ffffff;font-size:20px;font-weight:600;">AutonetSellCar</h1>
|
||
</td>
|
||
</tr>
|
||
<!-- Body -->
|
||
<tr>
|
||
<td style="padding:32px;">
|
||
<p style="margin:0 0 16px;color:#333;font-size:15px;">{greeting}</p>
|
||
<h2 style="margin:0 0 12px;color:#1a1a2e;font-size:18px;">{title}</h2>
|
||
<p style="margin:0 0 24px;color:#555;font-size:15px;line-height:1.6;">{message}</p>
|
||
<a href="{full_link}"
|
||
style="display:inline-block;background:#2563eb;color:#ffffff;text-decoration:none;padding:12px 28px;border-radius:6px;font-size:14px;font-weight:600;">
|
||
{button_label}
|
||
</a>
|
||
</td>
|
||
</tr>
|
||
<!-- Footer -->
|
||
<tr>
|
||
<td style="padding:20px 32px;background:#f9fafb;border-top:1px solid #e5e7eb;">
|
||
<p style="margin:0;color:#9ca3af;font-size:12px;">{footer}</p>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</td></tr>
|
||
</table>
|
||
</body>
|
||
</html>"""
|
||
|
||
return subject, html_body
|
||
|
||
|
||
def _send_smtp(to_email: str, subject: str, html_body: str) -> bool:
|
||
"""Send an email via SMTP. Returns True on success."""
|
||
try:
|
||
if not settings.SMTP_USER or not settings.SMTP_PASSWORD:
|
||
print(f"[DEV] Notification email to {to_email}: {subject}")
|
||
return True
|
||
|
||
msg = MIMEMultipart("alternative")
|
||
msg["From"] = f"{settings.SMTP_FROM_NAME} <{settings.SMTP_FROM_EMAIL or settings.SMTP_USER}>"
|
||
msg["To"] = to_email
|
||
msg["Subject"] = subject
|
||
msg.attach(MIMEText(html_body, "html", "utf-8"))
|
||
|
||
with smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT) as server:
|
||
server.starttls()
|
||
server.login(settings.SMTP_USER, settings.SMTP_PASSWORD)
|
||
server.send_message(msg)
|
||
|
||
print(f"[INFO] Notification email sent to {to_email}: {subject}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"[ERROR] Failed to send notification email to {to_email}: {e}")
|
||
return False
|
||
|
||
|
||
def send_notification_email(
|
||
to_email: str,
|
||
notification_type: str,
|
||
title: str,
|
||
message: str,
|
||
link: Optional[str] = None,
|
||
user_name: Optional[str] = None,
|
||
user_country: Optional[str] = None,
|
||
) -> None:
|
||
"""Send a notification email in a background thread (non-blocking).
|
||
Only sends for notification types in EMAIL_ENABLED_TYPES.
|
||
"""
|
||
if notification_type not in EMAIL_ENABLED_TYPES:
|
||
return
|
||
|
||
if not to_email:
|
||
return
|
||
|
||
language = get_user_language(user_country)
|
||
subject, html_body = _build_email_content(
|
||
notification_type=notification_type,
|
||
title=title,
|
||
message=message,
|
||
link=link,
|
||
user_name=user_name,
|
||
language=language,
|
||
)
|
||
|
||
# Send in background thread to avoid blocking the request
|
||
thread = threading.Thread(
|
||
target=_send_smtp,
|
||
args=(to_email, subject, html_body),
|
||
daemon=True,
|
||
)
|
||
thread.start()
|