Files
remnawave-bedolaga-telegram…/app/cabinet/services/email_templates.py
T
c0mrade 9a2aea038a chore: add uv package manager and ruff linter configuration
- Add pyproject.toml with uv and ruff configuration
- Pin Python version to 3.13 via .python-version
- Add Makefile commands: lint, format, fix
- Apply ruff formatting to entire codebase
- Remove unused imports (base64 in yookassa/simple_subscription)
- Update .gitignore for new config files
2026-01-24 17:45:27 +03:00

912 lines
38 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 notification templates for different notification types.
Supports multiple languages: ru, en, zh, ua
"""
from typing import Any
from app.config import settings
class EmailNotificationTemplates:
"""HTML email templates for user notifications."""
def __init__(self):
self.service_name = settings.SMTP_FROM_NAME or 'VPN Service'
self.cabinet_url = getattr(settings, 'CABINET_URL', '')
def get_template(
self,
notification_type: 'NotificationType',
language: str,
context: dict[str, Any],
) -> dict[str, str] | None:
"""
Get email template for notification type.
Args:
notification_type: Type of notification
language: Language code (ru, en, zh, ua)
context: Context data for template rendering
Returns:
Dict with 'subject', 'body_html', and optionally 'body_text'
"""
# Import here to avoid circular imports
from app.services.notification_delivery_service import NotificationType
template_map = {
NotificationType.BALANCE_TOPUP: self._balance_topup_template,
NotificationType.BALANCE_CHANGE: self._balance_change_template,
NotificationType.SUBSCRIPTION_EXPIRING: self._subscription_expiring_template,
NotificationType.SUBSCRIPTION_EXPIRED: self._subscription_expired_template,
NotificationType.SUBSCRIPTION_RENEWED: self._subscription_renewed_template,
NotificationType.SUBSCRIPTION_ACTIVATED: self._subscription_activated_template,
NotificationType.AUTOPAY_SUCCESS: self._autopay_success_template,
NotificationType.AUTOPAY_FAILED: self._autopay_failed_template,
NotificationType.AUTOPAY_INSUFFICIENT_FUNDS: self._autopay_insufficient_funds_template,
NotificationType.DAILY_DEBIT: self._daily_debit_template,
NotificationType.DAILY_INSUFFICIENT_FUNDS: self._daily_insufficient_funds_template,
NotificationType.BAN_NOTIFICATION: self._ban_template,
NotificationType.UNBAN_NOTIFICATION: self._unban_template,
NotificationType.WARNING_NOTIFICATION: self._warning_template,
NotificationType.REFERRAL_BONUS: self._referral_bonus_template,
NotificationType.REFERRAL_REGISTERED: self._referral_registered_template,
NotificationType.TRAFFIC_RESET: self._traffic_reset_template,
NotificationType.PAYMENT_RECEIVED: self._payment_received_template,
}
template_func = template_map.get(notification_type)
if not template_func:
return None
return template_func(language, context)
def _get_base_template(self, content: str) -> str:
"""Wrap content in base HTML template."""
return f"""
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
line-height: 1.6;
color: #333;
background-color: #f5f5f5;
margin: 0;
padding: 0;
}}
.container {{
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: #ffffff;
}}
.header {{
text-align: center;
padding: 20px 0;
border-bottom: 2px solid #007bff;
}}
.header h1 {{
color: #007bff;
margin: 0;
font-size: 24px;
}}
.content {{
padding: 30px 20px;
}}
.highlight {{
background-color: #f8f9fa;
border-left: 4px solid #007bff;
padding: 15px;
margin: 20px 0;
}}
.success {{
border-left-color: #28a745;
}}
.warning {{
border-left-color: #ffc107;
}}
.danger {{
border-left-color: #dc3545;
}}
.button {{
display: inline-block;
padding: 12px 24px;
background-color: #007bff;
color: white !important;
text-decoration: none;
border-radius: 5px;
margin: 20px 0;
font-weight: bold;
}}
.button:hover {{
background-color: #0056b3;
}}
.footer {{
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #eee;
font-size: 12px;
color: #666;
text-align: center;
}}
.amount {{
font-size: 24px;
font-weight: bold;
color: #28a745;
}}
.amount.negative {{
color: #dc3545;
}}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>{self.service_name}</h1>
</div>
<div class="content">
{content}
</div>
<div class="footer">
<p>&copy; {self.service_name}. All rights reserved.</p>
<p>This is an automated message. Please do not reply to this email.</p>
</div>
</div>
</body>
</html>
"""
def _get_cabinet_button(self, language: str) -> str:
"""Get cabinet link button HTML."""
if not self.cabinet_url:
return ''
texts = {
'ru': 'Открыть личный кабинет',
'en': 'Open Dashboard',
'zh': '打开控制面板',
'ua': 'Відкрити особистий кабінет',
}
text = texts.get(language, texts['en'])
return f'<p style="text-align: center;"><a href="{self.cabinet_url}" class="button">{text}</a></p>'
# ============================================================================
# Balance Templates
# ============================================================================
def _balance_topup_template(self, language: str, context: dict[str, Any]) -> dict[str, str]:
"""Template for balance top-up notification."""
amount = context.get('formatted_amount', f'{context.get("amount_rubles", 0):.2f}')
balance = context.get('formatted_balance', f'{context.get("new_balance_rubles", 0):.2f}')
subjects = {
'ru': f'Баланс пополнен на {amount}',
'en': f'Balance topped up by {amount}',
'zh': f'余额已充值 {amount}',
'ua': f'Баланс поповнено на {amount}',
}
bodies = {
'ru': f"""
<h2>Баланс успешно пополнен!</h2>
<div class="highlight success">
<p>Сумма пополнения: <span class="amount">+{amount}</span></p>
<p>Текущий баланс: <strong>{balance}</strong></p>
</div>
<p>Спасибо за использование нашего сервиса!</p>
{self._get_cabinet_button(language)}
""",
'en': f"""
<h2>Balance Successfully Topped Up!</h2>
<div class="highlight success">
<p>Top-up amount: <span class="amount">+{amount}</span></p>
<p>Current balance: <strong>{balance}</strong></p>
</div>
<p>Thank you for using our service!</p>
{self._get_cabinet_button(language)}
""",
'zh': f"""
<h2>充值成功!</h2>
<div class="highlight success">
<p>充值金额: <span class="amount">+{amount}</span></p>
<p>当前余额: <strong>{balance}</strong></p>
</div>
<p>感谢使用我们的服务!</p>
{self._get_cabinet_button(language)}
""",
'ua': f"""
<h2>Баланс успішно поповнено!</h2>
<div class="highlight success">
<p>Сума поповнення: <span class="amount">+{amount}</span></p>
<p>Поточний баланс: <strong>{balance}</strong></p>
</div>
<p>Дякуємо за використання нашого сервісу!</p>
{self._get_cabinet_button(language)}
""",
}
return {
'subject': subjects.get(language, subjects['en']),
'body_html': self._get_base_template(bodies.get(language, bodies['en'])),
}
def _balance_change_template(self, language: str, context: dict[str, Any]) -> dict[str, str]:
"""Template for balance change notification."""
amount = context.get('formatted_amount', f'{context.get("amount_rubles", 0):.2f}')
balance = context.get('formatted_balance', f'{context.get("new_balance_rubles", 0):.2f}')
description = context.get('description', '')
subjects = {
'ru': 'Изменение баланса',
'en': 'Balance Changed',
'zh': '余额变动',
'ua': 'Зміна балансу',
}
bodies = {
'ru': f"""
<h2>Изменение баланса</h2>
<div class="highlight">
<p>Сумма: <strong>{amount}</strong></p>
<p>Текущий баланс: <strong>{balance}</strong></p>
{f'<p>Описание: {description}</p>' if description else ''}
</div>
{self._get_cabinet_button(language)}
""",
'en': f"""
<h2>Balance Changed</h2>
<div class="highlight">
<p>Amount: <strong>{amount}</strong></p>
<p>Current balance: <strong>{balance}</strong></p>
{f'<p>Description: {description}</p>' if description else ''}
</div>
{self._get_cabinet_button(language)}
""",
}
return {
'subject': subjects.get(language, subjects['en']),
'body_html': self._get_base_template(bodies.get(language, bodies['en'])),
}
# ============================================================================
# Subscription Templates
# ============================================================================
def _subscription_expiring_template(self, language: str, context: dict[str, Any]) -> dict[str, str]:
"""Template for subscription expiring notification."""
days_left = context.get('days_left', 0)
expires_at = context.get('expires_at', '')
subjects = {
'ru': f'Подписка истекает через {days_left} дн.',
'en': f'Subscription expires in {days_left} day(s)',
'zh': f'订阅将在 {days_left} 天后到期',
'ua': f'Підписка закінчується через {days_left} дн.',
}
bodies = {
'ru': f"""
<h2>Подписка скоро истекает</h2>
<div class="highlight warning">
<p>Ваша подписка истекает через <strong>{days_left}</strong> дн.</p>
<p>Дата истечения: <strong>{expires_at}</strong></p>
</div>
<p>Продлите подписку, чтобы не потерять доступ к сервису.</p>
{self._get_cabinet_button(language)}
""",
'en': f"""
<h2>Subscription Expiring Soon</h2>
<div class="highlight warning">
<p>Your subscription expires in <strong>{days_left}</strong> day(s).</p>
<p>Expiration date: <strong>{expires_at}</strong></p>
</div>
<p>Renew your subscription to maintain access to our service.</p>
{self._get_cabinet_button(language)}
""",
'zh': f"""
<h2>订阅即将到期</h2>
<div class="highlight warning">
<p>您的订阅将在 <strong>{days_left}</strong> 天后到期。</p>
<p>到期日期: <strong>{expires_at}</strong></p>
</div>
<p>请续订以保持对服务的访问。</p>
{self._get_cabinet_button(language)}
""",
'ua': f"""
<h2>Підписка скоро закінчується</h2>
<div class="highlight warning">
<p>Ваша підписка закінчується через <strong>{days_left}</strong> дн.</p>
<p>Дата закінчення: <strong>{expires_at}</strong></p>
</div>
<p>Продовжіть підписку, щоб не втратити доступ до сервісу.</p>
{self._get_cabinet_button(language)}
""",
}
return {
'subject': subjects.get(language, subjects['en']),
'body_html': self._get_base_template(bodies.get(language, bodies['en'])),
}
def _subscription_expired_template(self, language: str, context: dict[str, Any]) -> dict[str, str]:
"""Template for subscription expired notification."""
subjects = {
'ru': 'Подписка истекла',
'en': 'Subscription Expired',
'zh': '订阅已到期',
'ua': 'Підписка закінчилась',
}
bodies = {
'ru': f"""
<h2>Подписка истекла</h2>
<div class="highlight danger">
<p>Ваша подписка истекла. Доступ к VPN отключён.</p>
</div>
<p>Оформите новую подписку, чтобы продолжить использование сервиса.</p>
{self._get_cabinet_button(language)}
""",
'en': f"""
<h2>Subscription Expired</h2>
<div class="highlight danger">
<p>Your subscription has expired. VPN access has been disabled.</p>
</div>
<p>Purchase a new subscription to continue using our service.</p>
{self._get_cabinet_button(language)}
""",
'zh': f"""
<h2>订阅已到期</h2>
<div class="highlight danger">
<p>您的订阅已到期。VPN访问已被禁用。</p>
</div>
<p>请购买新订阅以继续使用我们的服务。</p>
{self._get_cabinet_button(language)}
""",
'ua': f"""
<h2>Підписка закінчилась</h2>
<div class="highlight danger">
<p>Ваша підписка закінчилась. Доступ до VPN вимкнено.</p>
</div>
<p>Оформіть нову підписку, щоб продовжити використання сервісу.</p>
{self._get_cabinet_button(language)}
""",
}
return {
'subject': subjects.get(language, subjects['en']),
'body_html': self._get_base_template(bodies.get(language, bodies['en'])),
}
def _subscription_renewed_template(self, language: str, context: dict[str, Any]) -> dict[str, str]:
"""Template for subscription renewed notification."""
new_expires_at = context.get('new_expires_at', '')
subjects = {
'ru': 'Подписка продлена',
'en': 'Subscription Renewed',
'zh': '订阅已续订',
'ua': 'Підписку продовжено',
}
bodies = {
'ru': f"""
<h2>Подписка успешно продлена!</h2>
<div class="highlight success">
<p>Ваша подписка была успешно продлена.</p>
<p>Новая дата истечения: <strong>{new_expires_at}</strong></p>
</div>
<p>Спасибо за использование нашего сервиса!</p>
{self._get_cabinet_button(language)}
""",
'en': f"""
<h2>Subscription Successfully Renewed!</h2>
<div class="highlight success">
<p>Your subscription has been successfully renewed.</p>
<p>New expiration date: <strong>{new_expires_at}</strong></p>
</div>
<p>Thank you for using our service!</p>
{self._get_cabinet_button(language)}
""",
}
return {
'subject': subjects.get(language, subjects['en']),
'body_html': self._get_base_template(bodies.get(language, bodies['en'])),
}
def _subscription_activated_template(self, language: str, context: dict[str, Any]) -> dict[str, str]:
"""Template for subscription activated notification."""
expires_at = context.get('expires_at', '')
subjects = {
'ru': 'Подписка активирована',
'en': 'Subscription Activated',
'zh': '订阅已激活',
'ua': 'Підписку активовано',
}
bodies = {
'ru': f"""
<h2>Подписка активирована!</h2>
<div class="highlight success">
<p>Ваша VPN подписка успешно активирована.</p>
<p>Действует до: <strong>{expires_at}</strong></p>
</div>
<p>Теперь вы можете пользоваться VPN сервисом.</p>
{self._get_cabinet_button(language)}
""",
'en': f"""
<h2>Subscription Activated!</h2>
<div class="highlight success">
<p>Your VPN subscription has been successfully activated.</p>
<p>Valid until: <strong>{expires_at}</strong></p>
</div>
<p>You can now use the VPN service.</p>
{self._get_cabinet_button(language)}
""",
}
return {
'subject': subjects.get(language, subjects['en']),
'body_html': self._get_base_template(bodies.get(language, bodies['en'])),
}
# ============================================================================
# Autopay Templates
# ============================================================================
def _autopay_success_template(self, language: str, context: dict[str, Any]) -> dict[str, str]:
"""Template for successful autopay notification."""
amount = context.get('formatted_amount', f'{context.get("amount_rubles", 0):.2f}')
new_expires_at = context.get('new_expires_at', '')
subjects = {
'ru': 'Автопродление выполнено',
'en': 'Auto-renewal Successful',
'zh': '自动续订成功',
'ua': 'Автопродовження виконано',
}
bodies = {
'ru': f"""
<h2>Автопродление выполнено</h2>
<div class="highlight success">
<p>Ваша подписка была автоматически продлена.</p>
<p>Списано с баланса: <strong>{amount}</strong></p>
<p>Новая дата истечения: <strong>{new_expires_at}</strong></p>
</div>
{self._get_cabinet_button(language)}
""",
'en': f"""
<h2>Auto-renewal Successful</h2>
<div class="highlight success">
<p>Your subscription has been automatically renewed.</p>
<p>Charged from balance: <strong>{amount}</strong></p>
<p>New expiration date: <strong>{new_expires_at}</strong></p>
</div>
{self._get_cabinet_button(language)}
""",
}
return {
'subject': subjects.get(language, subjects['en']),
'body_html': self._get_base_template(bodies.get(language, bodies['en'])),
}
def _autopay_failed_template(self, language: str, context: dict[str, Any]) -> dict[str, str]:
"""Template for failed autopay notification."""
reason = context.get('reason', '')
subjects = {
'ru': 'Ошибка автопродления',
'en': 'Auto-renewal Failed',
'zh': '自动续订失败',
'ua': 'Помилка автопродовження',
}
bodies = {
'ru': f"""
<h2>Ошибка автопродления</h2>
<div class="highlight danger">
<p>Не удалось автоматически продлить подписку.</p>
{f'<p>Причина: {reason}</p>' if reason else ''}
</div>
<p>Пожалуйста, пополните баланс и продлите подписку вручную.</p>
{self._get_cabinet_button(language)}
""",
'en': f"""
<h2>Auto-renewal Failed</h2>
<div class="highlight danger">
<p>Failed to automatically renew your subscription.</p>
{f'<p>Reason: {reason}</p>' if reason else ''}
</div>
<p>Please top up your balance and renew manually.</p>
{self._get_cabinet_button(language)}
""",
}
return {
'subject': subjects.get(language, subjects['en']),
'body_html': self._get_base_template(bodies.get(language, bodies['en'])),
}
def _autopay_insufficient_funds_template(self, language: str, context: dict[str, Any]) -> dict[str, str]:
"""Template for autopay insufficient funds notification."""
required = context.get('required_amount', '')
balance = context.get('current_balance', '')
subjects = {
'ru': 'Недостаточно средств для автопродления',
'en': 'Insufficient Funds for Auto-renewal',
'zh': '余额不足无法自动续订',
'ua': 'Недостатньо коштів для автопродовження',
}
bodies = {
'ru': f"""
<h2>Недостаточно средств</h2>
<div class="highlight warning">
<p>Недостаточно средств на балансе для автопродления подписки.</p>
{f'<p>Требуется: <strong>{required}</strong></p>' if required else ''}
{f'<p>На балансе: <strong>{balance}</strong></p>' if balance else ''}
</div>
<p>Пополните баланс, чтобы подписка была продлена автоматически.</p>
{self._get_cabinet_button(language)}
""",
'en': f"""
<h2>Insufficient Funds</h2>
<div class="highlight warning">
<p>Insufficient balance for subscription auto-renewal.</p>
{f'<p>Required: <strong>{required}</strong></p>' if required else ''}
{f'<p>Balance: <strong>{balance}</strong></p>' if balance else ''}
</div>
<p>Top up your balance for automatic renewal.</p>
{self._get_cabinet_button(language)}
""",
}
return {
'subject': subjects.get(language, subjects['en']),
'body_html': self._get_base_template(bodies.get(language, bodies['en'])),
}
# ============================================================================
# Daily Subscription Templates
# ============================================================================
def _daily_debit_template(self, language: str, context: dict[str, Any]) -> dict[str, str]:
"""Template for daily subscription debit notification."""
amount = context.get('formatted_amount', f'{context.get("amount_rubles", 0):.2f}')
balance = context.get('formatted_balance', f'{context.get("new_balance_rubles", 0):.2f}')
subjects = {
'ru': f'Списание за подписку: {amount}',
'en': f'Subscription charge: {amount}',
'zh': f'订阅扣费: {amount}',
'ua': f'Списання за підписку: {amount}',
}
bodies = {
'ru': f"""
<h2>Ежедневное списание</h2>
<div class="highlight">
<p>С вашего баланса списано: <strong>{amount}</strong></p>
<p>Остаток на балансе: <strong>{balance}</strong></p>
</div>
{self._get_cabinet_button(language)}
""",
'en': f"""
<h2>Daily Charge</h2>
<div class="highlight">
<p>Charged from your balance: <strong>{amount}</strong></p>
<p>Remaining balance: <strong>{balance}</strong></p>
</div>
{self._get_cabinet_button(language)}
""",
}
return {
'subject': subjects.get(language, subjects['en']),
'body_html': self._get_base_template(bodies.get(language, bodies['en'])),
}
def _daily_insufficient_funds_template(self, language: str, context: dict[str, Any]) -> dict[str, str]:
"""Template for daily subscription insufficient funds."""
subjects = {
'ru': 'Недостаточно средств для продления',
'en': 'Insufficient Funds',
'zh': '余额不足',
'ua': 'Недостатньо коштів',
}
bodies = {
'ru': f"""
<h2>Недостаточно средств</h2>
<div class="highlight danger">
<p>На балансе недостаточно средств для продления подписки.</p>
<p>Подписка будет приостановлена.</p>
</div>
<p>Пополните баланс, чтобы продолжить использование сервиса.</p>
{self._get_cabinet_button(language)}
""",
'en': f"""
<h2>Insufficient Funds</h2>
<div class="highlight danger">
<p>Insufficient balance to continue subscription.</p>
<p>Your subscription will be suspended.</p>
</div>
<p>Please top up your balance to continue using the service.</p>
{self._get_cabinet_button(language)}
""",
}
return {
'subject': subjects.get(language, subjects['en']),
'body_html': self._get_base_template(bodies.get(language, bodies['en'])),
}
def _traffic_reset_template(self, language: str, context: dict[str, Any]) -> dict[str, str]:
"""Template for traffic reset notification."""
subjects = {
'ru': 'Трафик обновлён',
'en': 'Traffic Reset',
'zh': '流量已重置',
'ua': 'Трафік оновлено',
}
bodies = {
'ru': f"""
<h2>Трафик обновлён</h2>
<div class="highlight success">
<p>Ваш трафик был сброшен. Вы можете продолжить использование VPN.</p>
</div>
{self._get_cabinet_button(language)}
""",
'en': f"""
<h2>Traffic Reset</h2>
<div class="highlight success">
<p>Your traffic has been reset. You can continue using the VPN.</p>
</div>
{self._get_cabinet_button(language)}
""",
}
return {
'subject': subjects.get(language, subjects['en']),
'body_html': self._get_base_template(bodies.get(language, bodies['en'])),
}
# ============================================================================
# Account Status Templates
# ============================================================================
def _ban_template(self, language: str, context: dict[str, Any]) -> dict[str, str]:
"""Template for ban notification."""
reason = context.get('reason', '')
subjects = {
'ru': 'Аккаунт заблокирован',
'en': 'Account Suspended',
'zh': '账户已被封禁',
'ua': 'Обліковий запис заблоковано',
}
bodies = {
'ru': f"""
<h2>Аккаунт заблокирован</h2>
<div class="highlight danger">
<p>Ваш аккаунт был заблокирован.</p>
{f'<p>Причина: {reason}</p>' if reason else ''}
</div>
<p>Если вы считаете, что это ошибка, обратитесь в поддержку.</p>
""",
'en': f"""
<h2>Account Suspended</h2>
<div class="highlight danger">
<p>Your account has been suspended.</p>
{f'<p>Reason: {reason}</p>' if reason else ''}
</div>
<p>If you believe this is an error, please contact support.</p>
""",
}
return {
'subject': subjects.get(language, subjects['en']),
'body_html': self._get_base_template(bodies.get(language, bodies['en'])),
}
def _unban_template(self, language: str, context: dict[str, Any]) -> dict[str, str]:
"""Template for unban notification."""
subjects = {
'ru': 'Аккаунт разблокирован',
'en': 'Account Reactivated',
'zh': '账户已解封',
'ua': 'Обліковий запис розблоковано',
}
bodies = {
'ru': f"""
<h2>Аккаунт разблокирован</h2>
<div class="highlight success">
<p>Ваш аккаунт был разблокирован.</p>
<p>Вы снова можете пользоваться сервисом.</p>
</div>
{self._get_cabinet_button(language)}
""",
'en': f"""
<h2>Account Reactivated</h2>
<div class="highlight success">
<p>Your account has been reactivated.</p>
<p>You can use the service again.</p>
</div>
{self._get_cabinet_button(language)}
""",
}
return {
'subject': subjects.get(language, subjects['en']),
'body_html': self._get_base_template(bodies.get(language, bodies['en'])),
}
def _warning_template(self, language: str, context: dict[str, Any]) -> dict[str, str]:
"""Template for warning notification."""
message = context.get('message', '')
subjects = {
'ru': 'Предупреждение',
'en': 'Warning',
'zh': '警告',
'ua': 'Попередження',
}
bodies = {
'ru': f"""
<h2>Предупреждение</h2>
<div class="highlight warning">
{f'<p>{message}</p>' if message else '<p>Вы получили предупреждение от администрации.</p>'}
</div>
""",
'en': f"""
<h2>Warning</h2>
<div class="highlight warning">
{f'<p>{message}</p>' if message else '<p>You have received a warning from the administration.</p>'}
</div>
""",
}
return {
'subject': subjects.get(language, subjects['en']),
'body_html': self._get_base_template(bodies.get(language, bodies['en'])),
}
# ============================================================================
# Referral Templates
# ============================================================================
def _referral_bonus_template(self, language: str, context: dict[str, Any]) -> dict[str, str]:
"""Template for referral bonus notification."""
bonus = context.get('formatted_bonus', f'{context.get("bonus_rubles", 0):.2f}')
referral_name = context.get('referral_name', '')
subjects = {
'ru': f'Реферальный бонус: +{bonus}',
'en': f'Referral bonus: +{bonus}',
'zh': f'推荐奖励: +{bonus}',
'ua': f'Реферальний бонус: +{bonus}',
}
bodies = {
'ru': f"""
<h2>Реферальный бонус!</h2>
<div class="highlight success">
<p>Вы получили реферальный бонус: <span class="amount">+{bonus}</span></p>
{f'<p>Благодаря пользователю: {referral_name}</p>' if referral_name else ''}
</div>
<p>Продолжайте приглашать друзей и зарабатывайте больше!</p>
{self._get_cabinet_button(language)}
""",
'en': f"""
<h2>Referral Bonus!</h2>
<div class="highlight success">
<p>You received a referral bonus: <span class="amount">+{bonus}</span></p>
{f'<p>Thanks to: {referral_name}</p>' if referral_name else ''}
</div>
<p>Keep inviting friends and earn more!</p>
{self._get_cabinet_button(language)}
""",
}
return {
'subject': subjects.get(language, subjects['en']),
'body_html': self._get_base_template(bodies.get(language, bodies['en'])),
}
def _referral_registered_template(self, language: str, context: dict[str, Any]) -> dict[str, str]:
"""Template for new referral registered notification."""
referral_name = context.get('referral_name', '')
subjects = {
'ru': 'Новый реферал зарегистрирован',
'en': 'New Referral Registered',
'zh': '新推荐用户已注册',
'ua': 'Новий реферал зареєстрований',
}
bodies = {
'ru': f"""
<h2>Новый реферал!</h2>
<div class="highlight success">
<p>По вашей ссылке зарегистрировался новый пользователь{f': <strong>{referral_name}</strong>' if referral_name else ''}.</p>
</div>
<p>Вы будете получать бонусы с его пополнений!</p>
{self._get_cabinet_button(language)}
""",
'en': f"""
<h2>New Referral!</h2>
<div class="highlight success">
<p>A new user registered using your link{f': <strong>{referral_name}</strong>' if referral_name else ''}.</p>
</div>
<p>You will receive bonuses from their top-ups!</p>
{self._get_cabinet_button(language)}
""",
}
return {
'subject': subjects.get(language, subjects['en']),
'body_html': self._get_base_template(bodies.get(language, bodies['en'])),
}
# ============================================================================
# Payment Templates
# ============================================================================
def _payment_received_template(self, language: str, context: dict[str, Any]) -> dict[str, str]:
"""Template for payment received notification."""
amount = context.get('formatted_amount', f'{context.get("amount_rubles", 0):.2f}')
payment_method = context.get('payment_method', '')
subjects = {
'ru': f'Платёж получен: {amount}',
'en': f'Payment received: {amount}',
'zh': f'收到付款: {amount}',
'ua': f'Платіж отримано: {amount}',
}
bodies = {
'ru': f"""
<h2>Платёж успешно обработан</h2>
<div class="highlight success">
<p>Сумма: <span class="amount">+{amount}</span></p>
{f'<p>Способ оплаты: {payment_method}</p>' if payment_method else ''}
</div>
<p>Спасибо за оплату!</p>
{self._get_cabinet_button(language)}
""",
'en': f"""
<h2>Payment Successfully Processed</h2>
<div class="highlight success">
<p>Amount: <span class="amount">+{amount}</span></p>
{f'<p>Payment method: {payment_method}</p>' if payment_method else ''}
</div>
<p>Thank you for your payment!</p>
{self._get_cabinet_button(language)}
""",
}
return {
'subject': subjects.get(language, subjects['en']),
'body_html': self._get_base_template(bodies.get(language, bodies['en'])),
}
# Singleton instance
email_notification_templates = EmailNotificationTemplates()