Refactor notification service and enhance user management features
- Replaced legacy payment notification functions with a new NotificationService for improved code organization and maintainability. - Updated user management handlers to utilize the new notification system for payment confirmations. - Enhanced user card formatting with localized labels for better clarity and user experience. - Introduced context management for the PanelApiService to ensure proper session handling. - Updated localization files to include new labels and messages for user management and notifications.
This commit is contained in:
@@ -266,7 +266,8 @@ async def process_promo_set_validity(callback: types.CallbackQuery,
|
||||
async def process_promo_validity_days_handler(message: types.Message,
|
||||
state: FSMContext,
|
||||
i18n_data: dict,
|
||||
settings: Settings):
|
||||
settings: Settings,
|
||||
session: AsyncSession):
|
||||
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
|
||||
i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance")
|
||||
if not i18n:
|
||||
|
||||
@@ -142,56 +142,53 @@ async def show_statistics_handler(callback: types.CallbackQuery,
|
||||
stats_text_parts.append(f"\n<b>🖥 {_('admin_panel_stats_header', default='Статистика панели')}</b>")
|
||||
|
||||
try:
|
||||
panel_service = PanelApiService(settings)
|
||||
|
||||
# Get panel system statistics
|
||||
logging.info("Fetching panel statistics...")
|
||||
panel_stats = await panel_service.get_panel_statistics()
|
||||
nodes_stats = await panel_service.get_nodes_statistics()
|
||||
online_count = await panel_service.get_online_users_count()
|
||||
users_activity = await panel_service.get_users_activity_stats()
|
||||
|
||||
logging.info(f"Panel stats response: panel_stats={panel_stats}, nodes_stats={nodes_stats}, online_count={online_count}, users_activity={users_activity}")
|
||||
|
||||
# Online users
|
||||
if online_count is not None:
|
||||
stats_text_parts.append(f"🟢 Онлайн сейчас: <b>{online_count}</b>")
|
||||
else:
|
||||
stats_text_parts.append(f"🟢 Онлайн сейчас: <b>N/A</b>")
|
||||
|
||||
# Users activity
|
||||
if users_activity:
|
||||
today_connected = users_activity.get('today_connected', 'N/A')
|
||||
week_connected = users_activity.get('week_connected', 'N/A')
|
||||
never_connected = users_activity.get('never_connected', 'N/A')
|
||||
stats_text_parts.append(f"📅 Подключались сегодня: <b>{today_connected}</b>")
|
||||
stats_text_parts.append(f"📅 Подключались за неделю: <b>{week_connected}</b>")
|
||||
stats_text_parts.append(f"❌ Никогда не подключались: <b>{never_connected}</b>")
|
||||
else:
|
||||
stats_text_parts.append(f"📅 Активность пользователей: <b>N/A</b>")
|
||||
|
||||
# Nodes statistics
|
||||
if nodes_stats:
|
||||
active_nodes = len([node for node in nodes_stats if node.get('status') == 'active'])
|
||||
total_nodes = len(nodes_stats)
|
||||
stats_text_parts.append(f"🔗 Активных нод: <b>{active_nodes}/{total_nodes}</b>")
|
||||
else:
|
||||
stats_text_parts.append(f"🔗 Ноды: <b>N/A</b>")
|
||||
|
||||
# System statistics
|
||||
if panel_stats:
|
||||
system_info = panel_stats.get('system', {})
|
||||
if system_info:
|
||||
memory_usage = system_info.get('memory_usage_percent', 'N/A')
|
||||
cpu_usage = system_info.get('cpu_usage_percent', 'N/A')
|
||||
stats_text_parts.append(f"💾 Использование RAM: <b>{memory_usage}%</b>")
|
||||
stats_text_parts.append(f"🔄 Загрузка CPU: <b>{cpu_usage}%</b>")
|
||||
async with PanelApiService(settings) as panel_service:
|
||||
# Get panel system statistics
|
||||
logging.info("Fetching panel statistics...")
|
||||
panel_stats = await panel_service.get_panel_statistics()
|
||||
nodes_stats = await panel_service.get_nodes_statistics()
|
||||
online_count = await panel_service.get_online_users_count()
|
||||
users_activity = await panel_service.get_users_activity_stats()
|
||||
|
||||
logging.info(f"Panel stats response: panel_stats={panel_stats}, nodes_stats={nodes_stats}, online_count={online_count}, users_activity={users_activity}")
|
||||
|
||||
# Online users
|
||||
if online_count is not None:
|
||||
stats_text_parts.append(f"🟢 Онлайн сейчас: <b>{online_count}</b>")
|
||||
else:
|
||||
stats_text_parts.append(f"💾 Системная информация: <b>N/A</b>")
|
||||
else:
|
||||
stats_text_parts.append(f"💾 Системная статистика: <b>N/A</b>")
|
||||
|
||||
await panel_service.close()
|
||||
stats_text_parts.append(f"🟢 Онлайн сейчас: <b>N/A</b>")
|
||||
|
||||
# Users activity
|
||||
if users_activity:
|
||||
today_connected = users_activity.get('today_connected', 'N/A')
|
||||
week_connected = users_activity.get('week_connected', 'N/A')
|
||||
never_connected = users_activity.get('never_connected', 'N/A')
|
||||
stats_text_parts.append(f"📅 Подключались сегодня: <b>{today_connected}</b>")
|
||||
stats_text_parts.append(f"📅 Подключались за неделю: <b>{week_connected}</b>")
|
||||
stats_text_parts.append(f"❌ Никогда не подключались: <b>{never_connected}</b>")
|
||||
else:
|
||||
stats_text_parts.append(f"📅 Активность пользователей: <b>N/A</b>")
|
||||
|
||||
# Nodes statistics
|
||||
if nodes_stats:
|
||||
active_nodes = len([node for node in nodes_stats if node.get('status') == 'active'])
|
||||
total_nodes = len(nodes_stats)
|
||||
stats_text_parts.append(f"🔗 Активных нод: <b>{active_nodes}/{total_nodes}</b>")
|
||||
else:
|
||||
stats_text_parts.append(f"🔗 Ноды: <b>N/A</b>")
|
||||
|
||||
# System statistics
|
||||
if panel_stats:
|
||||
system_info = panel_stats.get('system', {})
|
||||
if system_info:
|
||||
memory_usage = system_info.get('memory_usage_percent', 'N/A')
|
||||
cpu_usage = system_info.get('cpu_usage_percent', 'N/A')
|
||||
stats_text_parts.append(f"💾 Использование RAM: <b>{memory_usage}%</b>")
|
||||
stats_text_parts.append(f"🔄 Загрузка CPU: <b>{cpu_usage}%</b>")
|
||||
else:
|
||||
stats_text_parts.append(f"💾 Системная информация: <b>N/A</b>")
|
||||
else:
|
||||
stats_text_parts.append(f"💾 Системная статистика: <b>N/A</b>")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to fetch panel statistics: {e}", exc_info=True)
|
||||
|
||||
@@ -113,27 +113,28 @@ async def format_user_card(user: User, session: AsyncSession,
|
||||
card_parts.append(f"👤 <b>{_('admin_user_card_title', default='Карточка пользователя')}</b>\n")
|
||||
|
||||
# User details
|
||||
user_name = user.first_name or "N/A"
|
||||
username_display = f"@{user.username}" if user.username else "N/A"
|
||||
registration_date = user.registration_date.strftime('%Y-%m-%d %H:%M') if user.registration_date else "N/A"
|
||||
na_value = _("admin_user_na_value", default="N/A")
|
||||
user_name = user.first_name or na_value
|
||||
username_display = f"@{user.username}" if user.username else na_value
|
||||
registration_date = user.registration_date.strftime('%Y-%m-%d %H:%M') if user.registration_date else na_value
|
||||
|
||||
card_parts.append(f"🆔 <b>ID:</b> {hcode(str(user.user_id))}")
|
||||
card_parts.append(f"👤 <b>Имя:</b> {hcode(user_name)}")
|
||||
card_parts.append(f"📱 <b>Username:</b> {hcode(username_display)}")
|
||||
card_parts.append(f"🌍 <b>Язык:</b> {hcode(user.language_code or 'N/A')}")
|
||||
card_parts.append(f"📅 <b>Регистрация:</b> {hcode(registration_date)}")
|
||||
card_parts.append(f"{_('admin_user_id_label', default='🆔 <b>ID:</b>')} {hcode(str(user.user_id))}")
|
||||
card_parts.append(f"{_('admin_user_name_label', default='👤 <b>Имя:</b>')} {hcode(user_name)}")
|
||||
card_parts.append(f"{_('admin_user_username_label', default='📱 <b>Username:</b>')} {hcode(username_display)}")
|
||||
card_parts.append(f"{_('admin_user_language_label', default='🌍 <b>Язык:</b>')} {hcode(user.language_code or na_value)}")
|
||||
card_parts.append(f"{_('admin_user_registration_label', default='📅 <b>Регистрация:</b>')} {hcode(registration_date)}")
|
||||
|
||||
# Ban status
|
||||
ban_status = "🚫 Заблокирован" if user.is_banned else "✅ Активен"
|
||||
card_parts.append(f"🛡 <b>Статус:</b> {ban_status}")
|
||||
ban_status = _("admin_user_status_banned", default="🚫 Заблокирован") if user.is_banned else _("admin_user_status_active", default="✅ Активен")
|
||||
card_parts.append(f"{_('admin_user_status_label', default='🛡 <b>Статус:</b>')} {ban_status}")
|
||||
|
||||
# Referral info
|
||||
if user.referred_by_id:
|
||||
card_parts.append(f"🎁 <b>Привлечен по реферальной программе от:</b> {hcode(str(user.referred_by_id))}")
|
||||
card_parts.append(f"{_('admin_user_referral_label', default='🎁 <b>Привлечен по реферальной программе от:</b>')} {hcode(str(user.referred_by_id))}")
|
||||
|
||||
# Panel info
|
||||
if user.panel_user_uuid:
|
||||
card_parts.append(f"🔗 <b>Panel UUID:</b> {hcode(user.panel_user_uuid[:8] + '...' if len(user.panel_user_uuid) > 8 else user.panel_user_uuid)}")
|
||||
card_parts.append(f"{_('admin_user_panel_uuid_label', default='🔗 <b>Panel UUID:</b>')} {hcode(user.panel_user_uuid[:8] + '...' if len(user.panel_user_uuid) > 8 else user.panel_user_uuid)}")
|
||||
|
||||
card_parts.append("") # Empty line
|
||||
|
||||
@@ -146,33 +147,33 @@ async def format_user_card(user: User, session: AsyncSession,
|
||||
end_date = subscription_details.get('end_date')
|
||||
if end_date:
|
||||
end_date_str = end_date.strftime('%Y-%m-%d %H:%M') if isinstance(end_date, datetime) else str(end_date)
|
||||
card_parts.append(f"⏰ <b>Действует до:</b> {hcode(end_date_str)}")
|
||||
card_parts.append(f"{_('admin_user_subscription_active_until', default='⏰ <b>Действует до:</b>')} {hcode(end_date_str)}")
|
||||
|
||||
status = subscription_details.get('status_from_panel', 'UNKNOWN')
|
||||
card_parts.append(f"📊 <b>Статус на панели:</b> {hcode(status)}")
|
||||
card_parts.append(f"{_('admin_user_panel_status_label', default='📊 <b>Статус на панели:</b>')} {hcode(status)}")
|
||||
|
||||
traffic_limit = subscription_details.get('traffic_limit_bytes')
|
||||
traffic_used = subscription_details.get('traffic_used_bytes')
|
||||
if traffic_limit and traffic_used is not None:
|
||||
traffic_limit_gb = traffic_limit / (1024**3)
|
||||
traffic_used_gb = traffic_used / (1024**3)
|
||||
card_parts.append(f"📊 <b>Трафик:</b> {hcode(f'{traffic_used_gb:.2f}GB / {traffic_limit_gb:.2f}GB')}")
|
||||
card_parts.append(f"{_('admin_user_traffic_label', default='📊 <b>Трафик:</b>')} {hcode(f'{traffic_used_gb:.2f}GB / {traffic_limit_gb:.2f}GB')}")
|
||||
else:
|
||||
card_parts.append(f"💳 <b>Подписка:</b> {hcode('Отсутствует')}")
|
||||
card_parts.append(f"{_('admin_user_subscription_label', default='💼 <b>Подписка:</b>')} {hcode(_('admin_user_subscription_none', default='Нет активной подписки'))}")
|
||||
except Exception as e:
|
||||
logging.error(f"Error getting subscription details for user {user.user_id}: {e}")
|
||||
card_parts.append(f"💳 <b>Подписка:</b> {hcode('Ошибка загрузки')}")
|
||||
card_parts.append(f"{_('admin_user_subscription_label', default='💼 <b>Подписка:</b>')} {hcode(_('admin_user_subscription_error', default='Ошибка загрузки'))}")
|
||||
|
||||
# Statistics
|
||||
try:
|
||||
# Count user logs
|
||||
logs_count = await message_log_dal.count_user_message_logs(session, user.user_id)
|
||||
card_parts.append(f"📜 <b>Всего действий:</b> {hcode(str(logs_count))}")
|
||||
card_parts.append(f"{_('admin_user_actions_count_label', default='📜 <b>Всего действий:</b>')} {hcode(str(logs_count))}")
|
||||
|
||||
# Check if user had any subscriptions
|
||||
had_subscriptions = await subscription_service.has_had_any_subscription(session, user.user_id)
|
||||
trial_status = "Использовал" if had_subscriptions else "Не использовал"
|
||||
card_parts.append(f"🆓 <b>Триал:</b> {hcode(trial_status)}")
|
||||
trial_status = _("admin_user_trial_used", default="Использовал") if had_subscriptions else _("admin_user_trial_not_used", default="Не использовал")
|
||||
card_parts.append(f"{_('admin_user_trial_label', default='🏡 <b>Триал:</b>')} {hcode(trial_status)}")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error getting user statistics for {user.user_id}: {e}")
|
||||
@@ -353,7 +354,7 @@ async def handle_toggle_ban(callback: types.CallbackQuery, user: User,
|
||||
|
||||
await session.commit()
|
||||
|
||||
status_text = "заблокирован" if new_ban_status else "разблокирован"
|
||||
status_text = _("admin_user_ban_action_banned", default="заблокирован") if new_ban_status else _("admin_user_ban_action_unbanned", default="разблокирован")
|
||||
await callback.answer(_(
|
||||
"admin_user_ban_toggle_success",
|
||||
default="✅ Пользователь {status}",
|
||||
@@ -365,9 +366,9 @@ async def handle_toggle_ban(callback: types.CallbackQuery, user: User,
|
||||
from config.settings import Settings
|
||||
from bot.services.panel_api_service import PanelApiService
|
||||
settings = Settings()
|
||||
panel_service = PanelApiService(settings)
|
||||
subscription_service = SubscriptionService(settings, panel_service)
|
||||
await handle_refresh_user_card(callback, user, subscription_service, session, i18n_instance, lang)
|
||||
async with PanelApiService(settings) as panel_service:
|
||||
subscription_service = SubscriptionService(settings, panel_service)
|
||||
await handle_refresh_user_card(callback, user, subscription_service, session, i18n_instance, lang)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error toggling ban for user {user.user_id}: {e}")
|
||||
@@ -631,16 +632,16 @@ async def process_direct_message_handler(message: types.Message, state: FSMConte
|
||||
|
||||
# Show user card again
|
||||
from bot.services.panel_api_service import PanelApiService
|
||||
panel_service = PanelApiService(settings)
|
||||
subscription_service = SubscriptionService(settings, panel_service)
|
||||
user_card_text = await format_user_card(target_user, session, subscription_service, i18n, current_lang)
|
||||
keyboard = get_user_card_keyboard(target_user.user_id, i18n, current_lang)
|
||||
|
||||
await message.answer(
|
||||
user_card_text,
|
||||
reply_markup=keyboard.as_markup(),
|
||||
parse_mode="HTML"
|
||||
)
|
||||
async with PanelApiService(settings) as panel_service:
|
||||
subscription_service = SubscriptionService(settings, panel_service)
|
||||
user_card_text = await format_user_card(target_user, session, subscription_service, i18n, current_lang)
|
||||
keyboard = get_user_card_keyboard(target_user.user_id, i18n, current_lang)
|
||||
|
||||
await message.answer(
|
||||
user_card_text,
|
||||
reply_markup=keyboard.as_markup(),
|
||||
parse_mode="HTML"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error sending direct message to user {target_user_id}: {e}")
|
||||
|
||||
@@ -20,7 +20,7 @@ from bot.services.panel_api_service import PanelApiService
|
||||
from bot.services.yookassa_service import YooKassaService
|
||||
from bot.middlewares.i18n import JsonI18n
|
||||
from config.settings import Settings
|
||||
from bot.services.notification_service import notify_admin_new_payment, NotificationService
|
||||
from bot.services.notification_service import NotificationService
|
||||
from bot.keyboards.inline.user_keyboards import get_connect_and_main_keyboard
|
||||
|
||||
payment_processing_lock = asyncio.Lock()
|
||||
@@ -209,16 +209,6 @@ async def process_successful_payment(session: AsyncSession, bot: Bot,
|
||||
)
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to send payment notification: {e}")
|
||||
|
||||
# Legacy notification for backwards compatibility
|
||||
await notify_admin_new_payment(
|
||||
bot,
|
||||
settings,
|
||||
i18n,
|
||||
user_id,
|
||||
subscription_months,
|
||||
payment_value,
|
||||
)
|
||||
|
||||
except Exception as e_process:
|
||||
logging.error(
|
||||
|
||||
@@ -237,7 +237,6 @@ async def start_command_handler(message: types.Message,
|
||||
config_link = active.get("config_link") if active else None
|
||||
config_link = config_link or _("config_link_not_available")
|
||||
|
||||
from datetime import datetime
|
||||
new_end_date = result if isinstance(result, datetime) else None
|
||||
|
||||
promo_success_text = _(
|
||||
|
||||
@@ -14,7 +14,7 @@ from bot.middlewares.i18n import JsonI18n
|
||||
from bot.services.subscription_service import SubscriptionService
|
||||
from bot.services.referral_service import ReferralService
|
||||
from bot.keyboards.inline.user_keyboards import get_connect_and_main_keyboard
|
||||
from bot.services.notification_service import notify_admin_new_payment
|
||||
from bot.services.notification_service import NotificationService
|
||||
from db.dal import payment_dal, user_dal
|
||||
|
||||
|
||||
@@ -194,15 +194,20 @@ class CryptoPayService:
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to send CryptoPay success message: {e}")
|
||||
|
||||
await notify_admin_new_payment(
|
||||
bot,
|
||||
settings,
|
||||
i18n,
|
||||
user_id,
|
||||
months,
|
||||
float(invoice.amount),
|
||||
currency=invoice.asset or settings.DEFAULT_CURRENCY_SYMBOL,
|
||||
)
|
||||
# Send notification about payment
|
||||
try:
|
||||
notification_service = NotificationService(bot, settings, i18n)
|
||||
user = await user_dal.get_user_by_id(session, user_id)
|
||||
await notification_service.notify_payment_received(
|
||||
user_id=user_id,
|
||||
amount=float(invoice.amount),
|
||||
currency=invoice.asset or settings.DEFAULT_CURRENCY_SYMBOL,
|
||||
months=months,
|
||||
payment_provider="crypto_pay",
|
||||
username=user.username if user else None
|
||||
)
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to send crypto_pay payment notification: {e}")
|
||||
|
||||
async def webhook_route(self, request: web.Request) -> web.Response:
|
||||
if not self.configured or not self.client:
|
||||
|
||||
@@ -220,20 +220,7 @@ async def notify_admin_new_trial(bot: Bot, settings: Settings, i18n: JsonI18n,
|
||||
await notification_service.notify_trial_activation(user_id, end_date)
|
||||
|
||||
|
||||
async def notify_admin_new_payment(bot: Bot, settings: Settings, i18n: JsonI18n,
|
||||
user_id: int, months: int, amount: float,
|
||||
currency: str | None = None) -> None:
|
||||
currency_symbol = currency or settings.DEFAULT_CURRENCY_SYMBOL
|
||||
await notify_admins(
|
||||
bot,
|
||||
settings,
|
||||
i18n,
|
||||
"admin_new_payment_notification",
|
||||
user_id=user_id,
|
||||
months=months,
|
||||
amount=f"{amount:.2f}",
|
||||
currency=currency_symbol,
|
||||
)
|
||||
|
||||
|
||||
|
||||
async def notify_admin_promo_activation(bot: Bot, settings: Settings,
|
||||
|
||||
@@ -21,6 +21,14 @@ class PanelApiService:
|
||||
self.api_key = settings.PANEL_API_KEY
|
||||
self._session: Optional[aiohttp.ClientSession] = None
|
||||
self.default_client_ip = "127.0.0.1"
|
||||
|
||||
async def __aenter__(self):
|
||||
"""Context manager entry"""
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||
"""Context manager exit - automatically close session"""
|
||||
await self.close_session()
|
||||
|
||||
async def _get_session(self) -> aiohttp.ClientSession:
|
||||
if self._session is None or self._session.closed:
|
||||
@@ -32,7 +40,7 @@ class PanelApiService:
|
||||
if self._session and not self._session.closed:
|
||||
await self._session.close()
|
||||
self._session = None
|
||||
logging.info("Panel API service HTTP session closed.")
|
||||
logging.debug("Panel API service HTTP session closed.")
|
||||
|
||||
async def close(self):
|
||||
"""Alias for close_session for API consistency."""
|
||||
|
||||
@@ -10,7 +10,7 @@ from db.dal import payment_dal, user_dal
|
||||
from .subscription_service import SubscriptionService
|
||||
from .referral_service import ReferralService
|
||||
from bot.middlewares.i18n import JsonI18n
|
||||
from .notification_service import notify_admin_new_payment
|
||||
from .notification_service import NotificationService
|
||||
from bot.keyboards.inline.user_keyboards import get_connect_and_main_keyboard
|
||||
|
||||
|
||||
@@ -153,13 +153,18 @@ class StarsService:
|
||||
logging.error(
|
||||
f"Failed to send stars payment success message: {e_send}")
|
||||
|
||||
await notify_admin_new_payment(
|
||||
self.bot,
|
||||
self.settings,
|
||||
self.i18n,
|
||||
message.from_user.id,
|
||||
months,
|
||||
float(stars_amount),
|
||||
currency="XTR",
|
||||
)
|
||||
# Send notification about payment
|
||||
try:
|
||||
notification_service = NotificationService(self.bot, self.settings, self.i18n)
|
||||
user = await user_dal.get_user_by_id(session, message.from_user.id)
|
||||
await notification_service.notify_payment_received(
|
||||
user_id=message.from_user.id,
|
||||
amount=float(stars_amount),
|
||||
currency="XTR",
|
||||
months=months,
|
||||
payment_provider="stars",
|
||||
username=user.username if user else None
|
||||
)
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to send stars payment notification: {e}")
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ from bot.middlewares.i18n import JsonI18n
|
||||
from bot.services.subscription_service import SubscriptionService
|
||||
from bot.services.panel_api_service import PanelApiService
|
||||
from bot.services.referral_service import ReferralService
|
||||
from .notification_service import notify_admin_new_payment
|
||||
from .notification_service import NotificationService
|
||||
from bot.keyboards.inline.user_keyboards import get_connect_and_main_keyboard
|
||||
from db.dal import payment_dal, user_dal, subscription_dal
|
||||
|
||||
@@ -187,15 +187,20 @@ class TributeService:
|
||||
logging.error(
|
||||
f"Failed to send Tribute payment success message to user {user_id}: {e}")
|
||||
|
||||
await notify_admin_new_payment(
|
||||
bot,
|
||||
settings,
|
||||
i18n,
|
||||
user_id,
|
||||
months,
|
||||
float(price_rub),
|
||||
currency="RUB",
|
||||
)
|
||||
# Send notification about payment
|
||||
try:
|
||||
notification_service = NotificationService(bot, settings, i18n)
|
||||
user = await user_dal.get_user_by_id(session, user_id)
|
||||
await notification_service.notify_payment_received(
|
||||
user_id=user_id,
|
||||
amount=float(price_rub),
|
||||
currency="RUB",
|
||||
months=months,
|
||||
payment_provider="tribute",
|
||||
username=user.username if user else None
|
||||
)
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to send tribute payment notification: {e}")
|
||||
else:
|
||||
await session.commit()
|
||||
return web.Response(status=200, text="ok")
|
||||
|
||||
+38
-2
@@ -234,7 +234,7 @@
|
||||
"subscription_expired_yesterday_notification": "👋 Hi, {user_name}!\n\n⏳ Your VPN subscription expired yesterday ({end_date}).\n\nPlease renew it using the button below.",
|
||||
|
||||
"admin_new_trial_notification": "\ud83c\udf21 User {user_id} activated a free trial until {end_date}.",
|
||||
"admin_new_payment_notification": "\ud83d\udcb3 Payment received from user {user_id}: {months} mo. for {amount} {currency}.",
|
||||
|
||||
"admin_promo_activation_notification": "\ud83c\udf81 Promo code {code} activated by user {user_id} (+{bonus_days}d).",
|
||||
|
||||
"error_unknown": "An unknown error occurred.",
|
||||
@@ -362,5 +362,41 @@
|
||||
"admin_promo_creation_failed": "❌ Error creating promo code: {error}",
|
||||
|
||||
"admin_panel_back_button": "⬅️ Back",
|
||||
"admin_bulk_promo_creation_failed": "❌ Error creating promo codes: {error}"
|
||||
"admin_bulk_promo_creation_failed": "❌ Error creating promo codes: {error}",
|
||||
|
||||
"admin_user_id_label": "🆔 <b>ID:</b>",
|
||||
"admin_user_name_label": "👤 <b>Name:</b>",
|
||||
"admin_user_username_label": "📱 <b>Username:</b>",
|
||||
"admin_user_language_label": "🌍 <b>Language:</b>",
|
||||
"admin_user_registration_label": "📅 <b>Registration:</b>",
|
||||
"admin_user_status_label": "🛡 <b>Status:</b>",
|
||||
"admin_user_referral_label": "🎁 <b>Referred by:</b>",
|
||||
"admin_user_panel_uuid_label": "🔗 <b>Panel UUID:</b>",
|
||||
"admin_user_panel_status_label": "📊 <b>Panel Status:</b>",
|
||||
"admin_user_traffic_label": "📊 <b>Traffic:</b>",
|
||||
"admin_user_subscription_label": "💼 <b>Subscription:</b>",
|
||||
"admin_user_trial_label": "🏡 <b>Trial:</b>",
|
||||
|
||||
"admin_user_status_banned": "🚫 Banned",
|
||||
"admin_user_status_active": "✅ Active",
|
||||
"admin_user_trial_used": "Used",
|
||||
"admin_user_trial_not_used": "Not used",
|
||||
"admin_user_ban_action_banned": "banned",
|
||||
"admin_user_ban_action_unbanned": "unbanned",
|
||||
"admin_user_na_value": "N/A",
|
||||
|
||||
"admin_user_subscription_active": "Active until {end_date}",
|
||||
"admin_user_subscription_expired": "Expired {end_date}",
|
||||
"admin_user_subscription_none": "No active subscription",
|
||||
|
||||
"admin_user_logs_title": "📋 <b>User logs {user_id}</b>",
|
||||
"admin_user_logs_empty": "No logs available",
|
||||
"admin_user_logs_count": "Total entries: {count}",
|
||||
"admin_user_logs_entry": "📅 {timestamp}\n📝 {event_type}: {content}",
|
||||
|
||||
"admin_user_actions_count_label": "📜 <b>Total actions:</b>",
|
||||
"admin_user_subscription_active_until": "⏰ <b>Active until:</b>",
|
||||
"admin_user_subscription_error": "Loading error",
|
||||
|
||||
"admin_bulk_promo_unique_generation_failed": "Failed to create unique promo code"
|
||||
}
|
||||
|
||||
+38
-2
@@ -234,7 +234,7 @@
|
||||
"subscription_expired_yesterday_notification": "👋 Привет, {user_name}!\n\n⏳ Ваша подписка на VPN истекла сутки назад ({end_date}).\n\nПродлите её по кнопке ниже.",
|
||||
|
||||
"admin_new_trial_notification": "\ud83c\udf21 Пользователь {user_id} активировал пробный период до {end_date}.",
|
||||
"admin_new_payment_notification": "\ud83d\udcb3 Получен платеж от пользователя {user_id}: {months} мес. за {amount} {currency}.",
|
||||
|
||||
"admin_promo_activation_notification": "\ud83c\udf81 Пользователь {user_id} активировал промокод {code} (+{bonus_days} дн.)",
|
||||
|
||||
"error_unknown": "Произошла неизвестная ошибка.",
|
||||
@@ -362,5 +362,41 @@
|
||||
"admin_promo_creation_failed": "❌ Ошибка создания промокода: {error}",
|
||||
|
||||
"admin_panel_back_button": "⬅️ Назад",
|
||||
"admin_bulk_promo_creation_failed": "❌ Ошибка создания промокодов: {error}"
|
||||
"admin_bulk_promo_creation_failed": "❌ Ошибка создания промокодов: {error}",
|
||||
|
||||
"admin_user_id_label": "🆔 <b>ID:</b>",
|
||||
"admin_user_name_label": "👤 <b>Имя:</b>",
|
||||
"admin_user_username_label": "📱 <b>Username:</b>",
|
||||
"admin_user_language_label": "🌍 <b>Язык:</b>",
|
||||
"admin_user_registration_label": "📅 <b>Регистрация:</b>",
|
||||
"admin_user_status_label": "🛡 <b>Статус:</b>",
|
||||
"admin_user_referral_label": "🎁 <b>Привлечен по реферальной программе от:</b>",
|
||||
"admin_user_panel_uuid_label": "🔗 <b>Panel UUID:</b>",
|
||||
"admin_user_panel_status_label": "📊 <b>Статус на панели:</b>",
|
||||
"admin_user_traffic_label": "📊 <b>Трафик:</b>",
|
||||
"admin_user_subscription_label": "💼 <b>Подписка:</b>",
|
||||
"admin_user_trial_label": "🏡 <b>Триал:</b>",
|
||||
|
||||
"admin_user_status_banned": "🚫 Заблокирован",
|
||||
"admin_user_status_active": "✅ Активен",
|
||||
"admin_user_trial_used": "Использовал",
|
||||
"admin_user_trial_not_used": "Не использовал",
|
||||
"admin_user_ban_action_banned": "заблокирован",
|
||||
"admin_user_ban_action_unbanned": "разблокирован",
|
||||
"admin_user_na_value": "N/A",
|
||||
|
||||
"admin_user_subscription_active": "Активна до {end_date}",
|
||||
"admin_user_subscription_expired": "Истекла {end_date}",
|
||||
"admin_user_subscription_none": "Нет активной подписки",
|
||||
|
||||
"admin_user_logs_title": "📋 <b>Логи пользователя {user_id}</b>",
|
||||
"admin_user_logs_empty": "Логи отсутствуют",
|
||||
"admin_user_logs_count": "Всего записей: {count}",
|
||||
"admin_user_logs_entry": "📅 {timestamp}\n📝 {event_type}: {content}",
|
||||
|
||||
"admin_user_actions_count_label": "📜 <b>Всего действий:</b>",
|
||||
"admin_user_subscription_active_until": "⏰ <b>Действует до:</b>",
|
||||
"admin_user_subscription_error": "Ошибка загрузки",
|
||||
|
||||
"admin_bulk_promo_unique_generation_failed": "Не удалось создать уникальный промокод"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user