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')