From 52df26e7879ed30e6c37c2a5450a5b827090857d Mon Sep 17 00:00:00 2001 From: Verum Date: Mon, 23 Feb 2026 14:36:11 +0700 Subject: [PATCH] =?UTF-8?q?=D0=A3=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B0=D0=B4=D0=BC=D0=B8=D0=BD=D0=B8=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=B0=D0=BC=D0=B8=20=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot/handlers/commands/users/admins.py | 347 ++++++++++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 bot/handlers/commands/users/admins.py diff --git a/bot/handlers/commands/users/admins.py b/bot/handlers/commands/users/admins.py new file mode 100644 index 0000000..3c2f320 --- /dev/null +++ b/bot/handlers/commands/users/admins.py @@ -0,0 +1,347 @@ +""" +Обработчики команд управления администраторами +""" +from aiogram import Router, F +from aiogram.filters import Command +from aiogram.types import Message, CallbackQuery +from aiogram.utils.keyboard import InlineKeyboardBuilder + +from bot import bot # ← ДОБАВЬ ЭТОТ ИМПОРТ +from bot.filters.admin import IsSuperAdmin +from configs import settings, COMMANDS +from database import get_manager +from middleware.loggers import logger +from bot.utils import log_action, tg_emoji + +__all__ = ("router",) + +router: Router = Router(name="admin_management_router") + + +def parse_user_id(text: str, command: str) -> tuple[bool, str | int]: + parts = text.split(maxsplit=1) + if len(parts) < 2: + return False, f'{tg_emoji("4961187972822074653")} Использование: /{command} ' + + user_id_str = parts[1].strip() + try: + user_id = int(user_id_str) + if user_id <= 0: + return False, f'{tg_emoji("4961187972822074653")} ID должен быть положительным числом' + if user_id > 9999999999: + return False, f'{tg_emoji("4961187972822074653")} Некорректный ID пользователя' + return True, user_id + except ValueError: + return False, f'{tg_emoji("4961187972822074653")} ID должен быть числом' + + +async def get_user_display_name(user_id: int) -> str: + """Получает имя пользователя или username или ID""" + try: + chat = await bot.get_chat(user_id) + name = f"{chat.first_name or ''} {chat.last_name or ''}".strip() + if name: + return name + if chat.username: + return f"@{chat.username}" + return str(user_id) + except: + return str(user_id) + + +def format_admin_info(user_id: int, username: str | None = None) -> str: + if username: + return f'{user_id} (@{username})' + return f'{user_id}' + + +def get_refresh_admins_kb(): + ikb = InlineKeyboardBuilder() + ikb.button(text='🔄 Обновить', callback_data='listadmins:refresh') + ikb.button(text='➕ Добавить', callback_data='admin:help_add') + ikb.adjust(2) + return ikb.as_markup() + + +# ================= ДОБАВЛЕНИЕ АДМИНИСТРАТОРА ================= + +@router.message(Command(*COMMANDS.get('addadmin', ['addadmin']), prefix=settings.PREFIX, ignore_case=True), + IsSuperAdmin()) +@log_action(action_name='ADD_ADMIN', log_args=True) +async def add_admin_cmd(message: Message) -> None: + success, result = parse_user_id(message.text, 'addadmin') + if not success: + await message.answer(result, parse_mode='HTML') + return + + user_id = result + + if user_id == message.from_user.id: + await message.answer( + f'{tg_emoji("4963024861615096794")} Вы уже владелец бота\n\n' + 'Вам не нужно добавлять себя в администраторы', + parse_mode='HTML' + ) + return + + if user_id in settings.OWNER_ID: + await message.answer( + f'{tg_emoji("4963024861615096794")} Этот пользователь уже владелец бота\n\n' + 'Владельцы имеют полные права автоматически', + parse_mode='HTML' + ) + return + + manager = get_manager() + try: + is_already_admin = await manager.is_admin(user_id) + if is_already_admin: + display_name = await get_user_display_name(user_id) + await message.answer( + f'{tg_emoji("4963024861615096794")} Пользователь {display_name} уже является администратором', + parse_mode='HTML' + ) + return + + added = await manager.add_admin(user_id=user_id, added_by=message.from_user.id) + if added: + display_name = await get_user_display_name(user_id) + text = ( + f'{tg_emoji("4963010134172239128")} Администратор добавлен\n\n' + f'{tg_emoji("4961064956368782417")} ID: {format_admin_info(user_id)}\n' + f'{tg_emoji("4963343509533754468")} Имя: {display_name}\n' + f'{tg_emoji("4963343509533754468")} Добавил: {format_admin_info(message.from_user.id, message.from_user.username)}\n\n' + f'{tg_emoji("4961106084975608869")} Права администратора:\n' + f'├─ Управление банвордами\n' + f'├─ Просмотр статистики\n' + f'├─ Активация режимов модерации\n' + f'└─ Все команды бота\n\n' + f'{tg_emoji("4963024861615096794")} Не может управлять другими админами\n' + f'Список админов: /listadmins' + ) + logger.info(f'Администратор добавлен: {user_id} (добавил: {message.from_user.id})', log_type='ADMIN_MGMT') + else: + text = f'{tg_emoji("4961187972822074653")} Ошибка добавления администратора\n\nПопробуйте позже' + + await message.answer(text, parse_mode='HTML') + + except Exception as e: + logger.error(f'Ошибка добавления администратора: {e}', log_type='ADMIN_MGMT') + await message.answer(f'{tg_emoji("4961187972822074653")} Ошибка добавления\n\nПопробуйте позже', parse_mode='HTML') + + +# ================= УДАЛЕНИЕ АДМИНИСТРАТОРА ================= + +@router.message(Command(*COMMANDS.get('remadmin', ['remadmin']), prefix=settings.PREFIX, ignore_case=True), + IsSuperAdmin()) +@log_action(action_name='REMOVE_ADMIN', log_args=True) +async def remove_admin_cmd(message: Message) -> None: + success, result = parse_user_id(message.text, 'remadmin') + if not success: + await message.answer(result, parse_mode='HTML') + return + + user_id = result + + if user_id in settings.OWNER_ID: + await message.answer( + f'{tg_emoji("4963024861615096794")} Нельзя удалить владельца\n\n' + 'Владельцы имеют права постоянно', + parse_mode='HTML' + ) + return + + if user_id == message.from_user.id: + await message.answer( + f'{tg_emoji("4963024861615096794")} Нельзя удалить самого себя', + parse_mode='HTML' + ) + return + + manager = get_manager() + try: + is_admin = await manager.is_admin(user_id) + if not is_admin: + display_name = await get_user_display_name(user_id) + await message.answer( + f'{tg_emoji("4963024861615096794")} Пользователь {display_name} не является администратором', + parse_mode='HTML' + ) + return + + removed = await manager.remove_admin(user_id=user_id) + if removed: + display_name = await get_user_display_name(user_id) + text = ( + f'🗑 Администратор удалён\n\n' + f'{tg_emoji("4961064956368782417")} ID: {format_admin_info(user_id)}\n' + f'{tg_emoji("4961064956368782417")} Имя: {display_name}\n' + f'{tg_emoji("4963343509533754468")} Удалил: {format_admin_info(message.from_user.id, message.from_user.username)}\n\n' + f'{tg_emoji("4963024861615096794")} Пользователь больше не имеет доступа к командам бота' + ) + logger.info(f'Администратор удалён: {user_id} (удалил: {message.from_user.id})', log_type='ADMIN_MGMT') + else: + text = f'{tg_emoji("4961187972822074653")} Ошибка удаления администратора\n\nПопробуйте позже' + + await message.answer(text, parse_mode='HTML') + + except Exception as e: + logger.error(f'Ошибка удаления администратора: {e}', log_type='ADMIN_MGMT') + await message.answer(f'{tg_emoji("4961187972822074653")} Ошибка удаления\n\nПопробуйте позже', parse_mode='HTML') + + +# ================= СПИСОК АДМИНИСТРАТОРОВ ================= + +@router.callback_query(F.data == 'listadmins:refresh') +@router.message(Command(*COMMANDS.get('listadmins', ['listadmins']), prefix=settings.PREFIX, ignore_case=True), + IsSuperAdmin()) +@log_action(action_name='LIST_ADMINS') +async def list_admins_cmd(update: Message | CallbackQuery) -> None: + if isinstance(update, CallbackQuery): + message = update.message + is_callback = True + else: + message = update + is_callback = False + + manager = get_manager() + try: + db_admins = await manager.repo.get_admins() + + output = f'{tg_emoji("4960891456869893259")} СПИСОК АДМИНИСТРАТОРОВ\n\n' + + # ВЛАДЕЛЬЦЫ + output += f'{tg_emoji("4963343509533754468")} Владельцы бота (полные права):\n' + for owner_id in settings.OWNER_ID: + display_name = await get_user_display_name(owner_id) + output += f'├─ {display_name}\n' + output += '\n' + + # АДМИНИСТРАТОРЫ + if db_admins: + output += f'{tg_emoji("4961064956368782417")} Администраторы ({len(db_admins)}):\n' + for admin_id in sorted(db_admins): + display_name = await get_user_display_name(admin_id) + output += f'├─ {display_name}\n' + output += '\n' + output += f'{tg_emoji("4961106084975608869")} Права администраторов:\n' + output += '├─ Управление банвордами\n' + output += '├─ Просмотр статистики\n' + output += '├─ Активация режимов модерации\n' + output += '└─ Все команды бота\n\n' + else: + output += f'{tg_emoji("4961064956368782417")} Администраторы:\n' + output += '└─ Нет дополнительных администраторов\n\n' + + total_admins = len(settings.OWNER_ID) + len(db_admins) + output += f'{tg_emoji("4961061266991875258")} Итого: {total_admins} администратор(ов)\n\n' + + output += f'{tg_emoji("4961027057577362562")} Управление:\n' + output += '• /adminhelp — помощь по командам админов\n' + output += '• /addadmin ID — добавить админа\n' + output += '• /remadmin ID — удалить админа\n\n' + output += f'{tg_emoji("4961186405159011104")} Только владельцы могут управлять администраторами' + + keyboard = get_refresh_admins_kb() + + if is_callback: + await message.edit_text(text=output, parse_mode='HTML', reply_markup=keyboard) + await update.answer(f'{tg_emoji("4963010134172239128")} Список обновлён') + else: + await message.answer(text=output, parse_mode='HTML', reply_markup=keyboard) + + except Exception as e: + logger.error(f'Ошибка получения списка администраторов: {e}', log_type='ADMIN_MGMT') + error_text = f'{tg_emoji("4961187972822074653")} Ошибка загрузки списка\n\nПопробуйте позже' + if is_callback: + await update.answer(f'{tg_emoji("4961187972822074653")} Ошибка загрузки', show_alert=True) + else: + await message.answer(error_text, parse_mode='HTML') + + +# ================= ВСПОМОГАТЕЛЬНЫЕ CALLBACK ================= + +@router.callback_query(F.data == 'admin:help_add') +async def admin_help_add_callback(callback: CallbackQuery) -> None: + text = ( + f'{tg_emoji("4963469772982322370")} Как добавить администратора?\n\n' + f'{tg_emoji("4960889107522782272")} Узнайте Telegram ID пользователя\n' + ' • Используйте команду /id или бота @userinfobot\n' + f'{tg_emoji("4960889107522782272")} Выполните команду:\n' + ' /addadmin ID\n\n' + 'Пример:\n' + '/addadmin 123456789' + ) + await callback.answer() + await callback.message.answer(text, parse_mode='HTML') + + +@router.message(Command(*COMMANDS.get('adminhelp', ['adminhelp']), prefix=settings.PREFIX, ignore_case=True), + IsSuperAdmin()) +async def admin_help_cmd(message: Message) -> None: + text = ( + f'{tg_emoji("4960891456869893259")} УПРАВЛЕНИЕ АДМИНИСТРАТОРАМИ\n\n' + f'{tg_emoji("4963401727815451692")} Уровни доступа:\n\n' + f'{tg_emoji("4963343509533754468")} Владельцы (OWNER_ID):\n' + '├─ Все права администратора\n' + '├─ Управление другими админами\n' + '└─ Указываются в конфигурации\n\n' + f'{tg_emoji("4961064956368782417")} Администраторы:\n' + '├─ Управление банвордами\n' + '├─ Просмотр статистики\n' + '├─ Активация режимов модерации\n' + '└─ Не могут управлять админами\n\n' + f'{tg_emoji("4963241130398319816")} Команды:\n' + '• /adminhelp — помощь по командам админов\n' + '• /listadmins — список всех админов\n' + '• /addadmin ID — добавить админа\n' + '• /remadmin ID — удалить админа\n\n' + f'{tg_emoji("4961186405159011104")} Как узнать ID пользователя?\n' + '• Используйте команду /id или бота @userinfobot\n' + '• Или попросите пользователя написать боту\n' + '• ID отображается в логах бота\n\n' + f'{tg_emoji("4963024861615096794")} Важно:\n' + '├─ Нельзя удалить владельца\n' + '├─ Нельзя удалить самого себя\n' + '└─ Все действия логируются' + ) + await message.answer(text, parse_mode='HTML') + + +@router.message(Command(*COMMANDS.get('checkadmin', ['checkadmin']), prefix=settings.PREFIX, ignore_case=True), + IsSuperAdmin()) +@log_action(action_name='CHECK_ADMIN') +async def check_admin_cmd(message: Message) -> None: + success, result = parse_user_id(message.text, 'checkadmin') + if not success: + await message.answer(result, parse_mode='HTML') + return + + user_id = result + manager = get_manager() + + try: + is_owner = user_id in settings.OWNER_ID + is_db_admin = await manager.is_admin(user_id) + + text = f'{tg_emoji("4961092195051373778")} Проверка пользователя\n\n' + text += f'{tg_emoji("4961064956368782417")} ID: {user_id}\n\n' + + if is_owner: + text += f'{tg_emoji("4963343509533754468")} Статус: Владелец бота\n' + text += f'{tg_emoji("4963010134172239128")} Полные права администратора\n' + text += f'{tg_emoji("4963010134172239128")} Может управлять админами' + elif is_db_admin: + text += f'{tg_emoji("4961064956368782417")} Статус: Администратор\n' + text += f'{tg_emoji("4963010134172239128")} Доступ к командам бота\n' + text += f'{tg_emoji("4961187972822074653")} Не может управлять админами' + else: + text += f'{tg_emoji("4961064956368782417")} Статус: Обычный пользователь\n' + text += f'{tg_emoji("4961187972822074653")} Нет прав администратора\n\n' + text += f'Добавить в админы: /addadmin {user_id}' + + await message.answer(text, parse_mode='HTML') + + except Exception as e: + logger.error(f'Ошибка проверки администратора: {e}', log_type='ADMIN_MGMT') + await message.answer(f'{tg_emoji("4961187972822074653")} Ошибка проверки', parse_mode='HTML')