- Introduced a new handler for canceling the ban process in `admin_handlers.py`, allowing users to easily abort ongoing ban actions. - Enhanced error handling and logging for the cancellation process to improve user experience and debugging. - Removed obsolete comments and cleaned up the code for better readability in the admin handlers. - Updated metrics middleware to streamline event processing and improve logging clarity, ensuring comprehensive metrics collection for all events.
347 lines
14 KiB
Python
347 lines
14 KiB
Python
from aiogram import Router, types, F
|
||
from aiogram.filters import Command, StateFilter, MagicData
|
||
from aiogram.fsm.context import FSMContext
|
||
|
||
from helper_bot.filters.main import ChatTypeFilter
|
||
from helper_bot.keyboards.keyboards import (
|
||
get_reply_keyboard_admin,
|
||
create_keyboard_with_pagination,
|
||
create_keyboard_for_ban_days,
|
||
create_keyboard_for_approve_ban,
|
||
create_keyboard_for_ban_reason
|
||
)
|
||
from helper_bot.handlers.admin.dependencies import AdminAccessMiddleware
|
||
from helper_bot.handlers.admin.services import AdminService
|
||
from helper_bot.handlers.admin.exceptions import (
|
||
UserAlreadyBannedError,
|
||
InvalidInputError
|
||
)
|
||
from helper_bot.handlers.admin.utils import (
|
||
return_to_admin_menu,
|
||
handle_admin_error,
|
||
format_user_info,
|
||
format_ban_confirmation,
|
||
escape_html
|
||
)
|
||
from logs.custom_logger import logger
|
||
|
||
# Создаем роутер с middleware для проверки доступа
|
||
admin_router = Router()
|
||
admin_router.message.middleware(AdminAccessMiddleware())
|
||
|
||
|
||
# ============================================================================
|
||
# ХЕНДЛЕРЫ МЕНЮ
|
||
# ============================================================================
|
||
|
||
@admin_router.message(
|
||
ChatTypeFilter(chat_type=["private"]),
|
||
Command('admin')
|
||
)
|
||
async def admin_panel(
|
||
message: types.Message,
|
||
state: FSMContext,
|
||
**kwargs
|
||
):
|
||
"""Главное меню администратора"""
|
||
try:
|
||
await state.set_state("ADMIN")
|
||
logger.info(f"Запуск админ панели для пользователя: {message.from_user.id}")
|
||
markup = get_reply_keyboard_admin()
|
||
await message.answer("Добро пожаловать в админку. Выбери что хочешь:", reply_markup=markup)
|
||
except Exception as e:
|
||
await handle_admin_error(message, e, state, "admin_panel")
|
||
|
||
|
||
# ============================================================================
|
||
# ХЕНДЛЕР ОТМЕНЫ
|
||
# ============================================================================
|
||
|
||
@admin_router.message(
|
||
ChatTypeFilter(chat_type=["private"]),
|
||
StateFilter("AWAIT_BAN_TARGET", "AWAIT_BAN_DETAILS", "AWAIT_BAN_DURATION", "BAN_CONFIRMATION"),
|
||
F.text == 'Отменить'
|
||
)
|
||
async def cancel_ban_process(
|
||
message: types.Message,
|
||
state: FSMContext,
|
||
**kwargs
|
||
):
|
||
"""Отмена процесса блокировки"""
|
||
try:
|
||
current_state = await state.get_state()
|
||
logger.info(f"Отмена процедуры блокировки из состояния: {current_state}")
|
||
await return_to_admin_menu(message, state)
|
||
except Exception as e:
|
||
await handle_admin_error(message, e, state, "cancel_ban_process")
|
||
|
||
|
||
@admin_router.message(
|
||
ChatTypeFilter(chat_type=["private"]),
|
||
StateFilter("ADMIN"),
|
||
F.text == 'Бан (Список)'
|
||
)
|
||
async def get_last_users(
|
||
message: types.Message,
|
||
state: FSMContext,
|
||
bot_db: MagicData("bot_db")
|
||
):
|
||
"""Получение списка последних пользователей"""
|
||
try:
|
||
logger.info(f"Получение списка последних пользователей. Пользователь: {message.from_user.full_name}")
|
||
admin_service = AdminService(bot_db)
|
||
users = await admin_service.get_last_users()
|
||
|
||
# Преобразуем в формат для клавиатуры (кортежи как ожидает create_keyboard_with_pagination)
|
||
users_data = [
|
||
(user.full_name, user.user_id)
|
||
for user in users
|
||
]
|
||
|
||
keyboard = create_keyboard_with_pagination(1, len(users_data), users_data, 'ban')
|
||
await message.answer(
|
||
text="Список пользователей которые последними обращались к боту",
|
||
reply_markup=keyboard
|
||
)
|
||
except Exception as e:
|
||
await handle_admin_error(message, e, state, "get_last_users")
|
||
|
||
|
||
@admin_router.message(
|
||
ChatTypeFilter(chat_type=["private"]),
|
||
StateFilter("ADMIN"),
|
||
F.text == 'Разбан (список)'
|
||
)
|
||
async def get_banned_users(
|
||
message: types.Message,
|
||
state: FSMContext,
|
||
bot_db: MagicData("bot_db")
|
||
):
|
||
"""Получение списка заблокированных пользователей"""
|
||
try:
|
||
logger.info(f"Получение списка заблокированных пользователей. Пользователь: {message.from_user.full_name}")
|
||
admin_service = AdminService(bot_db)
|
||
message_text, buttons_list = await admin_service.get_banned_users_for_display(0)
|
||
|
||
if buttons_list:
|
||
keyboard = create_keyboard_with_pagination(1, len(buttons_list), buttons_list, 'unlock')
|
||
await message.answer(text=message_text, reply_markup=keyboard)
|
||
else:
|
||
await message.answer(text="В списке заблокированных пользователей никого нет")
|
||
except Exception as e:
|
||
await handle_admin_error(message, e, state, "get_banned_users")
|
||
|
||
|
||
# ============================================================================
|
||
# ХЕНДЛЕРЫ ПРОЦЕССА БАНА
|
||
# ============================================================================
|
||
|
||
@admin_router.message(
|
||
ChatTypeFilter(chat_type=["private"]),
|
||
StateFilter("ADMIN"),
|
||
F.text.in_(['Бан по нику', 'Бан по ID'])
|
||
)
|
||
async def start_ban_process(
|
||
message: types.Message,
|
||
state: FSMContext,
|
||
**kwargs
|
||
):
|
||
"""Начало процесса блокировки пользователя"""
|
||
try:
|
||
ban_type = "username" if message.text == 'Бан по нику' else "id"
|
||
await state.update_data(ban_type=ban_type)
|
||
|
||
prompt_text = "Пришли мне username блокируемого пользователя" if ban_type == "username" else "Пришли мне ID блокируемого пользователя"
|
||
await message.answer(prompt_text)
|
||
await state.set_state('AWAIT_BAN_TARGET')
|
||
except Exception as e:
|
||
await handle_admin_error(message, e, state, "start_ban_process")
|
||
|
||
|
||
@admin_router.message(
|
||
ChatTypeFilter(chat_type=["private"]),
|
||
StateFilter("AWAIT_BAN_TARGET")
|
||
)
|
||
async def process_ban_target(
|
||
message: types.Message,
|
||
state: FSMContext,
|
||
bot_db: MagicData("bot_db")
|
||
):
|
||
"""Обработка введенного username/ID для блокировки"""
|
||
logger.info(f"process_ban_target: === НАЧАЛО ОБРАБОТКИ === Получено сообщение от {message.from_user.id}: {message.text}")
|
||
|
||
try:
|
||
user_data = await state.get_data()
|
||
ban_type = user_data.get('ban_type')
|
||
admin_service = AdminService(bot_db)
|
||
|
||
logger.info(f"process_ban_target: ban_type={ban_type}, user_data={user_data}")
|
||
|
||
# Определяем пользователя
|
||
if ban_type == "username":
|
||
logger.info(f"process_ban_target: Поиск пользователя по username: {message.text}")
|
||
user = await admin_service.get_user_by_username(message.text)
|
||
if not user:
|
||
logger.warning(f"process_ban_target: Пользователь с username '{message.text}' не найден")
|
||
await message.answer(f"Пользователь с username '{escape_html(message.text)}' не найден.")
|
||
await return_to_admin_menu(message, state)
|
||
return
|
||
else: # ban_type == "id"
|
||
try:
|
||
logger.info(f"process_ban_target: Валидация и поиск пользователя по ID: {message.text}")
|
||
user_id = await admin_service.validate_user_input(message.text)
|
||
user = await admin_service.get_user_by_id(user_id)
|
||
if not user:
|
||
logger.warning(f"process_ban_target: Пользователь с ID {user_id} не найден в базе данных")
|
||
await message.answer(f"Пользователь с ID {user_id} не найден в базе данных.")
|
||
await return_to_admin_menu(message, state)
|
||
return
|
||
except InvalidInputError as e:
|
||
logger.error(f"process_ban_target: Ошибка валидации ID: {e}")
|
||
await message.answer(str(e))
|
||
await return_to_admin_menu(message, state)
|
||
return
|
||
|
||
logger.info(f"process_ban_target: Найден пользователь: {user.user_id}, {user.username}, {user.full_name}")
|
||
|
||
# Сохраняем данные пользователя
|
||
await state.update_data(
|
||
target_user_id=user.user_id,
|
||
target_username=user.username,
|
||
target_full_name=user.full_name
|
||
)
|
||
|
||
# Показываем информацию о пользователе и запрашиваем причину
|
||
user_info = format_user_info(user.user_id, user.username, user.full_name)
|
||
markup = create_keyboard_for_ban_reason()
|
||
logger.info(f"process_ban_target: Отправка сообщения с причиной бана, user_info: {user_info}")
|
||
|
||
await message.answer(
|
||
text=f"{user_info}\n\nВыбери причину бана из списка или напиши ее в чат",
|
||
reply_markup=markup
|
||
)
|
||
await state.set_state('AWAIT_BAN_DETAILS')
|
||
logger.info("process_ban_target: Состояние изменено на AWAIT_BAN_DETAILS")
|
||
|
||
except Exception as e:
|
||
logger.error(f"process_ban_target: Неожиданная ошибка: {e}", exc_info=True)
|
||
await handle_admin_error(message, e, state, "process_ban_target")
|
||
|
||
|
||
@admin_router.message(
|
||
ChatTypeFilter(chat_type=["private"]),
|
||
StateFilter("AWAIT_BAN_DETAILS")
|
||
)
|
||
async def process_ban_reason(
|
||
message: types.Message,
|
||
state: FSMContext,
|
||
**kwargs
|
||
):
|
||
"""Обработка причины блокировки"""
|
||
logger.info(f"process_ban_reason: === НАЧАЛО ОБРАБОТКИ === Получено сообщение от {message.from_user.id}: {message.text}")
|
||
|
||
try:
|
||
# Проверяем текущее состояние
|
||
current_state = await state.get_state()
|
||
logger.info(f"process_ban_reason: Текущее состояние: {current_state}")
|
||
|
||
# Проверяем данные состояния
|
||
state_data = await state.get_data()
|
||
logger.info(f"process_ban_reason: Данные состояния: {state_data}")
|
||
|
||
logger.info(f"process_ban_reason: Обновление данных состояния с причиной: {message.text}")
|
||
await state.update_data(ban_reason=message.text)
|
||
|
||
markup = create_keyboard_for_ban_days()
|
||
safe_reason = escape_html(message.text)
|
||
logger.info(f"process_ban_reason: Отправка сообщения с выбором срока бана, причина: {safe_reason}")
|
||
|
||
await message.answer(
|
||
f"Выбрана причина: {safe_reason}. Выбери срок бана в днях или напиши его в чат",
|
||
reply_markup=markup
|
||
)
|
||
await state.set_state('AWAIT_BAN_DURATION')
|
||
logger.info("process_ban_reason: Состояние изменено на AWAIT_BAN_DURATION")
|
||
|
||
except Exception as e:
|
||
logger.error(f"process_ban_reason: Неожиданная ошибка: {e}", exc_info=True)
|
||
await handle_admin_error(message, e, state, "process_ban_reason")
|
||
|
||
|
||
@admin_router.message(
|
||
ChatTypeFilter(chat_type=["private"]),
|
||
StateFilter("AWAIT_BAN_DURATION")
|
||
)
|
||
async def process_ban_duration(
|
||
message: types.Message,
|
||
state: FSMContext,
|
||
**kwargs
|
||
):
|
||
"""Обработка срока блокировки"""
|
||
try:
|
||
user_data = await state.get_data()
|
||
|
||
# Определяем срок блокировки
|
||
if message.text == 'Навсегда':
|
||
ban_days = None
|
||
else:
|
||
try:
|
||
ban_days = int(message.text)
|
||
if ban_days <= 0:
|
||
await message.answer("Срок блокировки должен быть положительным числом.")
|
||
return
|
||
except ValueError:
|
||
await message.answer("Пожалуйста, введите корректное число дней или выберите 'Навсегда'.")
|
||
return
|
||
|
||
await state.update_data(ban_days=ban_days)
|
||
|
||
# Показываем подтверждение
|
||
confirmation_text = format_ban_confirmation(
|
||
user_data['target_user_id'],
|
||
user_data['ban_reason'],
|
||
ban_days
|
||
)
|
||
markup = create_keyboard_for_approve_ban()
|
||
await message.answer(confirmation_text, reply_markup=markup)
|
||
await state.set_state('BAN_CONFIRMATION')
|
||
|
||
except Exception as e:
|
||
await handle_admin_error(message, e, state, "process_ban_duration")
|
||
|
||
|
||
@admin_router.message(
|
||
ChatTypeFilter(chat_type=["private"]),
|
||
StateFilter("BAN_CONFIRMATION"),
|
||
F.text == 'Подтвердить'
|
||
)
|
||
async def confirm_ban(
|
||
message: types.Message,
|
||
state: FSMContext,
|
||
bot_db: MagicData("bot_db"),
|
||
**kwargs
|
||
):
|
||
"""Подтверждение блокировки пользователя"""
|
||
try:
|
||
user_data = await state.get_data()
|
||
admin_service = AdminService(bot_db)
|
||
|
||
|
||
# Выполняем блокировку
|
||
await admin_service.ban_user(
|
||
user_id=user_data['target_user_id'],
|
||
username=user_data['target_username'],
|
||
reason=user_data['ban_reason'],
|
||
ban_days=user_data['ban_days']
|
||
)
|
||
|
||
safe_username = escape_html(user_data['target_username'])
|
||
await message.reply(f"Пользователь {safe_username} успешно заблокирован.")
|
||
await return_to_admin_menu(message, state)
|
||
|
||
except UserAlreadyBannedError as e:
|
||
await message.reply(str(e))
|
||
await return_to_admin_menu(message, state)
|
||
except Exception as e:
|
||
await handle_admin_error(message, e, state, "confirm_ban")
|