Enhance ban notification system with delete functionality and improved message formatting

- Added a new handler to delete ban notifications upon user interaction.
- Introduced a delete button in ban notifications for better user experience.
- Updated ban notification messages to include node information more prominently.
- Refactored the BanNotificationService to send messages with the delete button included.
This commit is contained in:
PEDZEO
2026-01-14 14:50:00 +03:00
parent 3aa5e304c3
commit c868ef3b69
4 changed files with 66 additions and 31 deletions

View File

@@ -509,12 +509,12 @@ class Settings(BaseSettings):
BAN_MSG_PUNISHMENT: str = (
"🚫 <b>АККАУНТ ЗАБЛОКИРОВАН</b>\n"
"━━━━━━━━━━━━━━━━━━━━━\n\n"
"❌ <b>Причина:</b> Превышен лимит устройств\n\n"
"❌ <b>Причина:</b> Превышен лимит устройств\n"
"{node_info}\n"
"📊 <b>Детали нарушения:</b>\n"
"├ 📱 Устройств подключено: <b>{ip_count}</b>\n"
"├ 📋 Разрешено по тарифу: <b>{limit}</b>\n"
"└ ⏱ Время блокировки: <b>{ban_minutes} мин</b>\n\n"
"{node_info}"
"━━━━━━━━━━━━━━━━━━━━━\n"
"💡 <b>Что делать:</b>\n"
"1. Отключите лишние устройства от VPN\n"
@@ -541,11 +541,11 @@ class Settings(BaseSettings):
BAN_MSG_WIFI: str = (
"🚫 <b>АККАУНТ ЗАБЛОКИРОВАН</b>\n"
"━━━━━━━━━━━━━━━━━━━━━\n\n"
"❌ <b>Причина:</b> Использование WiFi сети\n\n"
"❌ <b>Причина:</b> Использование WiFi сети\n"
"{node_info}\n"
"📊 <b>Детали:</b>\n"
"├ 📶 Тип подключения: <b>WiFi</b>\n"
"{network_info}"
"{node_info}"
"└ ⏱ Время блокировки: <b>{ban_minutes} мин</b>\n\n"
"━━━━━━━━━━━━━━━━━━━━━\n"
"💡 <b>Что делать:</b>\n"
@@ -560,11 +560,11 @@ class Settings(BaseSettings):
BAN_MSG_MOBILE: str = (
"🚫 <b>АККАУНТ ЗАБЛОКИРОВАН</b>\n"
"━━━━━━━━━━━━━━━━━━━━━\n\n"
"❌ <b>Причина:</b> Использование мобильной сети\n\n"
"❌ <b>Причина:</b> Использование мобильной сети\n"
"{node_info}\n"
"📊 <b>Детали:</b>\n"
"├ 📱 Тип подключения: <b>Мобильная сеть</b>\n"
"{network_info}"
"{node_info}"
"└ ⏱ Время блокировки: <b>{ban_minutes} мин</b>\n\n"
"━━━━━━━━━━━━━━━━━━━━━\n"
"💡 <b>Что делать:</b>\n"

View File

@@ -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"

View File

@@ -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"├ 🖥 Сервер: <b>{node_name}</b>\n" if node_name else ""
# Формируем информацию о ноде (заметно выделяем)
node_info = f"🖥 <b>Нода:</b> <code>{node_name}</code>" 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"├ 🌐 Сеть: <b>{network_type}</b>\n" if network_type else ""
node_info = f"├ 🖥 Сервер: <b>{node_name}</b>\n" if node_name else ""
node_info = f"🖥 <b>Нода:</b> <code>{node_name}</code>" 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"├ 🌐 Сеть: <b>{network_type}</b>\n" if network_type else ""
node_info = f"├ 🖥 Сервер: <b>{node_name}</b>\n" if node_name else ""
node_info = f"🖥 <b>Нода:</b> <code>{node_name}</code>" 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} "

View File

@@ -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: