nakama/moderation/backend-py/utils/email_service.py

157 lines
6.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Email sending utilities with support for Yandex SMTP
"""
import smtplib
import logging
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from typing import Optional
from config import settings
logger = logging.getLogger(__name__)
def generate_verification_email(code: str) -> str:
"""Generate HTML email with verification code"""
return f"""
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {{ font-family: Arial, sans-serif; line-height: 1.6; color: #333; }}
.container {{ max-width: 600px; margin: 0 auto; padding: 20px; }}
.code {{ font-size: 32px; font-weight: bold; color: #007bff;
text-align: center; padding: 20px; background: #f8f9fa;
border-radius: 8px; margin: 20px 0; letter-spacing: 8px; }}
.footer {{ margin-top: 30px; font-size: 12px; color: #666; }}
</style>
</head>
<body>
<div class="container">
<h1>Код подтверждения</h1>
<p>Ваш код для регистрации в Nakama:</p>
<div class="code">{code}</div>
<p>Код действителен в течение 15 минут.</p>
<div class="footer">
<p>Если вы не запрашивали этот код, просто проигнорируйте это письмо.</p>
</div>
</div>
</body>
</html>
"""
async def send_email_smtp(to: str, subject: str, html: str, text: Optional[str] = None):
"""Send email via SMTP (Yandex, Gmail, etc.)"""
try:
# Create message
msg = MIMEMultipart('alternative')
msg['Subject'] = subject
msg['From'] = settings.EMAIL_FROM
msg['To'] = to
# Add text and HTML parts
if text:
msg.attach(MIMEText(text, 'plain', 'utf-8'))
msg.attach(MIMEText(html, 'html', 'utf-8'))
# Connect and send
# Поддерживаем 'yandex' и 'smtp' как алиасы
email_provider = settings.EMAIL_PROVIDER.lower()
if email_provider in ['yandex', 'smtp']:
logger.info(f"[Email] Отправка через SMTP ({email_provider}): {settings.YANDEX_SMTP_HOST}:{settings.YANDEX_SMTP_PORT}")
if not settings.YANDEX_SMTP_USER or not settings.YANDEX_SMTP_PASSWORD:
raise ValueError("YANDEX_SMTP_USER и YANDEX_SMTP_PASSWORD должны быть установлены в .env")
# Используем SMTP_SSL для порта 465
if settings.YANDEX_SMTP_PORT == 465:
server = smtplib.SMTP_SSL(settings.YANDEX_SMTP_HOST, settings.YANDEX_SMTP_PORT)
else:
server = smtplib.SMTP(settings.YANDEX_SMTP_HOST, settings.YANDEX_SMTP_PORT)
if settings.YANDEX_SMTP_SECURE:
server.starttls()
server.login(settings.YANDEX_SMTP_USER, settings.YANDEX_SMTP_PASSWORD)
server.send_message(msg)
server.quit()
logger.info(f"✅ Email отправлен на {to}")
return {"success": True, "to": to}
else:
raise ValueError(f"Email provider '{settings.EMAIL_PROVIDER}' не поддерживается. Используйте 'yandex' или 'smtp'")
except Exception as e:
logger.error(f"❌ Ошибка отправки email: {e}")
# More informative error messages
if "Authentication" in str(e) or "credentials" in str(e).lower():
raise ValueError(
"Неверные учетные данные SMTP. Для Yandex используйте пароль приложения "
"(https://id.yandex.ru/security), а не основной пароль аккаунта."
)
elif "Connection" in str(e):
raise ValueError(
f"Не удалось подключиться к SMTP серверу {settings.YANDEX_SMTP_HOST}:{settings.YANDEX_SMTP_PORT}"
)
raise
async def send_verification_code(email: str, code: str):
"""Send verification code to email"""
subject = "Код подтверждения регистрации - Nakama"
html = generate_verification_email(code)
text = f"Ваш код подтверждения: {code}. Код действителен 15 минут."
return await send_email_smtp(email, subject, html, text)
async def send_admin_confirmation_code(code: str, action: str, user_info: dict):
"""Send admin confirmation code to owner email"""
action_text = "добавления админа" if action == "add" else "удаления админа"
subject = f"Код подтверждения {action_text} - Nakama Moderation"
html = f"""
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {{ font-family: Arial, sans-serif; line-height: 1.6; color: #333; }}
.container {{ max-width: 600px; margin: 0 auto; padding: 20px; }}
.code {{ font-size: 32px; font-weight: bold; color: #007bff;
text-align: center; padding: 20px; background: #f8f9fa;
border-radius: 8px; margin: 20px 0; letter-spacing: 8px; }}
.info {{ background: #e7f3ff; padding: 15px; border-radius: 8px; margin: 20px 0; }}
.footer {{ margin-top: 30px; font-size: 12px; color: #666; }}
</style>
</head>
<body>
<div class="container">
<h1>Подтверждение {action_text}</h1>
<p><strong>Пользователь:</strong> @{user_info.get('username', 'не указан')} ({user_info.get('firstName', '')})</p>
{f"<p><strong>Номер админа:</strong> {user_info['adminNumber']}</p>" if 'adminNumber' in user_info else ''}
<div class="info">
<p><strong>Код подтверждения:</strong></p>
<div class="code">{code}</div>
<p>Код действителен в течение 5 минут.</p>
</div>
<div class="footer">
<p>Если вы не запрашивали это подтверждение, проигнорируйте это письмо.</p>
</div>
</div>
</body>
</html>
"""
text = f"""Код подтверждения {action_text}: {code}
Пользователь: @{user_info.get('username', 'не указан')}
Код действителен 5 минут."""
return await send_email_smtp(settings.OWNER_EMAIL, subject, html, text)