diff --git a/app/config.py b/app/config.py index b412f4d3..3e735a76 100644 --- a/app/config.py +++ b/app/config.py @@ -509,12 +509,12 @@ class Settings(BaseSettings): BAN_MSG_PUNISHMENT: str = ( "🚫 АККАУНТ ЗАБЛОКИРОВАН\n" "━━━━━━━━━━━━━━━━━━━━━\n\n" - "❌ Причина: Превышен лимит устройств\n\n" + "❌ Причина: Превышен лимит устройств\n" + "{node_info}\n" "📊 Детали нарушения:\n" "├ 📱 Устройств подключено: {ip_count}\n" "├ 📋 Разрешено по тарифу: {limit}\n" "└ ⏱ Время блокировки: {ban_minutes} мин\n\n" - "{node_info}" "━━━━━━━━━━━━━━━━━━━━━\n" "💡 Что делать:\n" "1. Отключите лишние устройства от VPN\n" @@ -541,11 +541,11 @@ class Settings(BaseSettings): BAN_MSG_WIFI: str = ( "🚫 АККАУНТ ЗАБЛОКИРОВАН\n" "━━━━━━━━━━━━━━━━━━━━━\n\n" - "❌ Причина: Использование WiFi сети\n\n" + "❌ Причина: Использование WiFi сети\n" + "{node_info}\n" "📊 Детали:\n" "├ 📶 Тип подключения: WiFi\n" "{network_info}" - "{node_info}" "└ ⏱ Время блокировки: {ban_minutes} мин\n\n" "━━━━━━━━━━━━━━━━━━━━━\n" "💡 Что делать:\n" @@ -560,11 +560,11 @@ class Settings(BaseSettings): BAN_MSG_MOBILE: str = ( "🚫 АККАУНТ ЗАБЛОКИРОВАН\n" "━━━━━━━━━━━━━━━━━━━━━\n\n" - "❌ Причина: Использование мобильной сети\n\n" + "❌ Причина: Использование мобильной сети\n" + "{node_info}\n" "📊 Детали:\n" "├ 📱 Тип подключения: Мобильная сеть\n" "{network_info}" - "{node_info}" "└ ⏱ Время блокировки: {ban_minutes} мин\n\n" "━━━━━━━━━━━━━━━━━━━━━\n" "💡 Что делать:\n" diff --git a/app/handlers/common.py b/app/handlers/common.py index 7d389163..4d9bcc70 100644 --- a/app/handlers/common.py +++ b/app/handlers/common.py @@ -11,13 +11,25 @@ from app.keyboards.inline import get_back_keyboard logger = logging.getLogger(__name__) +async def handle_delete_ban_notification( + callback: types.CallbackQuery, +): + """Удаляет уведомление о бане при нажатии на кнопку""" + try: + await callback.message.delete() + await callback.answer("Уведомление удалено") + except Exception as e: + logger.warning(f"Не удалось удалить уведомление: {e}") + await callback.answer("Не удалось удалить", show_alert=False) + + async def handle_unknown_callback( callback: types.CallbackQuery, db_user: User ): - + texts = get_texts(db_user.language if db_user else "ru") - + await callback.answer( texts.t( "UNKNOWN_CALLBACK_ALERT", @@ -25,7 +37,7 @@ async def handle_unknown_callback( ), show_alert=True, ) - + logger.warning(f"Неизвестный callback: {callback.data} от пользователя {callback.from_user.id}") @@ -99,7 +111,13 @@ async def show_rules( def register_handlers(dp: Dispatcher): - + + # Удаление уведомлений о банах + dp.callback_query.register( + handle_delete_ban_notification, + F.data == "ban_notify:delete" + ) + dp.callback_query.register( show_rules, F.data == "menu_rules" diff --git a/app/services/ban_notification_service.py b/app/services/ban_notification_service.py index a2371b89..c44dbf56 100644 --- a/app/services/ban_notification_service.py +++ b/app/services/ban_notification_service.py @@ -7,6 +7,7 @@ from datetime import datetime from aiogram import Bot from aiogram.exceptions import TelegramAPIError +from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select @@ -18,6 +19,13 @@ from app.config import settings logger = logging.getLogger(__name__) +def get_delete_keyboard() -> InlineKeyboardMarkup: + """Клавиатура с кнопкой удаления уведомления""" + return InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text="🗑 Удалить", callback_data="ban_notify:delete")] + ]) + + class BanNotificationService: """Сервис для отправки уведомлений о банах пользователям""" @@ -102,8 +110,8 @@ class BanNotificationService: logger.warning(f"Пользователь {user_identifier} не найден в базе данных") return False, f"Пользователь не найден: {user_identifier}", None - # Формируем информацию о ноде (в формате дерева) - node_info = f"├ 🖥 Сервер: {node_name}\n" if node_name else "" + # Формируем информацию о ноде (заметно выделяем) + node_info = f"🖥 Нода: {node_name}" if node_name else "" # Формируем сообщение из настроек # Используем безопасное форматирование - если {node_info} отсутствует в шаблоне, не будет ошибки @@ -126,12 +134,13 @@ class BanNotificationService: if node_info: message_text = message_text.rstrip() + f"\n\n{node_info.rstrip()}" - # Отправляем сообщение + # Отправляем сообщение с кнопкой удаления try: await self._bot.send_message( chat_id=user.telegram_id, text=message_text, - parse_mode="HTML" + parse_mode="HTML", + reply_markup=get_delete_keyboard() ) logger.info( f"Уведомление о бане отправлено пользователю {username} " @@ -170,12 +179,13 @@ class BanNotificationService: # Формируем сообщение из настроек message_text = settings.BAN_MSG_ENABLED - # Отправляем сообщение + # Отправляем сообщение с кнопкой удаления try: await self._bot.send_message( chat_id=user.telegram_id, text=message_text, - parse_mode="HTML" + parse_mode="HTML", + reply_markup=get_delete_keyboard() ) logger.info( f"Уведомление о разбане отправлено пользователю {username} " @@ -217,12 +227,13 @@ class BanNotificationService: warning_message=warning_message ) - # Отправляем сообщение + # Отправляем сообщение с кнопкой удаления try: await self._bot.send_message( chat_id=user.telegram_id, text=message_text, - parse_mode="HTML" + parse_mode="HTML", + reply_markup=get_delete_keyboard() ) logger.info( f"Предупреждение отправлено пользователю {username} " @@ -261,9 +272,11 @@ class BanNotificationService: logger.warning(f"Пользователь {user_identifier} не найден в базе данных") return False, f"Пользователь не найден: {user_identifier}", None - # Формируем сообщение из настроек (в формате дерева) + # Формируем сообщение из настроек (заметно выделяем) network_info = f"├ 🌐 Сеть: {network_type}\n" if network_type else "" - node_info = f"├ 🖥 Сервер: {node_name}\n" if node_name else "" + node_info = f"🖥 Нода: {node_name}" if node_name else "" + + logger.info(f"WiFi notification: node_name={node_name!r}, node_info={node_info!r}") # Безопасное форматирование format_vars = { @@ -274,17 +287,19 @@ class BanNotificationService: try: message_text = settings.BAN_MSG_WIFI.format(**format_vars) except KeyError: + logger.warning("BAN_MSG_WIFI template missing placeholders, adding node_info to end") message_text = settings.BAN_MSG_WIFI.format(ban_minutes=ban_minutes) - extra_info = network_info + node_info + extra_info = (network_info + node_info).strip() if extra_info: - message_text = message_text.rstrip() + f"\n\n{extra_info.rstrip()}" + message_text = message_text.rstrip() + f"\n\n{extra_info}" - # Отправляем сообщение + # Отправляем сообщение с кнопкой удаления try: await self._bot.send_message( chat_id=user.telegram_id, text=message_text, - parse_mode="HTML" + parse_mode="HTML", + reply_markup=get_delete_keyboard() ) logger.info( f"Уведомление о WiFi бане отправлено пользователю {username} " @@ -323,9 +338,9 @@ class BanNotificationService: logger.warning(f"Пользователь {user_identifier} не найден в базе данных") return False, f"Пользователь не найден: {user_identifier}", None - # Формируем сообщение из настроек (в формате дерева) + # Формируем сообщение из настроек (заметно выделяем) network_info = f"├ 🌐 Сеть: {network_type}\n" if network_type else "" - node_info = f"├ 🖥 Сервер: {node_name}\n" if node_name else "" + node_info = f"🖥 Нода: {node_name}" if node_name else "" # Безопасное форматирование format_vars = { @@ -337,16 +352,17 @@ class BanNotificationService: message_text = settings.BAN_MSG_MOBILE.format(**format_vars) except KeyError: message_text = settings.BAN_MSG_MOBILE.format(ban_minutes=ban_minutes) - extra_info = network_info + node_info + extra_info = (network_info + node_info).strip() if extra_info: - message_text = message_text.rstrip() + f"\n\n{extra_info.rstrip()}" + message_text = message_text.rstrip() + f"\n\n{extra_info}" - # Отправляем сообщение + # Отправляем сообщение с кнопкой удаления try: await self._bot.send_message( chat_id=user.telegram_id, text=message_text, - parse_mode="HTML" + parse_mode="HTML", + reply_markup=get_delete_keyboard() ) logger.info( f"Уведомление о Mobile бане отправлено пользователю {username} " diff --git a/app/webapi/routes/ban_notifications.py b/app/webapi/routes/ban_notifications.py index 38922f33..20960a58 100644 --- a/app/webapi/routes/ban_notifications.py +++ b/app/webapi/routes/ban_notifications.py @@ -46,7 +46,8 @@ async def send_ban_notification( """ logger.info( f"Получен запрос на отправку уведомления типа '{request.notification_type}' " - f"для пользователя {request.username} ({request.user_identifier})" + f"для пользователя {request.username} ({request.user_identifier}), " + f"node_name={request.node_name!r}" ) try: