Files
2026-02-20 03:12:47 +07:00

348 lines
17 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Обработчики команд управления администраторами
"""
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")} Использование: <code>/{command} <ID></code>'
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'<code>{user_id}</code> (@{username})'
return f'<code>{user_id}</code>'
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")} <b>Вы уже владелец бота</b>\n\n'
'Вам не нужно добавлять себя в администраторы',
parse_mode='HTML'
)
return
if user_id in settings.OWNER_ID:
await message.answer(
f'{tg_emoji("4963024861615096794")} <b>Этот пользователь уже владелец бота</b>\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")} Пользователь <b>{display_name}</b> уже является администратором',
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")} <b>Администратор добавлен</b>\n\n'
f'{tg_emoji("4961064956368782417")} ID: {format_admin_info(user_id)}\n'
f'{tg_emoji("4963343509533754468")} Имя: <b>{display_name}</b>\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")} <i>Не может управлять другими админами</i>\n'
f'Список админов: <b>/listadmins</b>'
)
logger.info(f'Администратор добавлен: {user_id} (добавил: {message.from_user.id})', log_type='ADMIN_MGMT')
else:
text = f'{tg_emoji("4961187972822074653")} <b>Ошибка добавления администратора</b>\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")} <b>Ошибка добавления</b>\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")} <b>Нельзя удалить владельца</b>\n\n'
'Владельцы имеют права постоянно',
parse_mode='HTML'
)
return
if user_id == message.from_user.id:
await message.answer(
f'{tg_emoji("4963024861615096794")} <b>Нельзя удалить самого себя</b>',
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")} Пользователь <b>{display_name}</b> не является администратором',
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'🗑 <b>Администратор удалён</b>\n\n'
f'{tg_emoji("4961064956368782417")} ID: {format_admin_info(user_id)}\n'
f'{tg_emoji("4961064956368782417")} Имя: <b>{display_name}</b>\n'
f'{tg_emoji("4963343509533754468")} Удалил: {format_admin_info(message.from_user.id, message.from_user.username)}\n\n'
f'{tg_emoji("4963024861615096794")} <i>Пользователь больше не имеет доступа к командам бота</i>'
)
logger.info(f'Администратор удалён: {user_id} (удалил: {message.from_user.id})', log_type='ADMIN_MGMT')
else:
text = f'{tg_emoji("4961187972822074653")} <b>Ошибка удаления администратора</b>\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")} <b>Ошибка удаления</b>\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")} <b>СПИСОК АДМИНИСТРАТОРОВ</b>\n\n'
# ВЛАДЕЛЬЦЫ
output += f'{tg_emoji("4963343509533754468")} <b>Владельцы бота</b> (полные права):\n'
for owner_id in settings.OWNER_ID:
display_name = await get_user_display_name(owner_id)
output += f'├─ <a href="tg://user?id={owner_id}">{display_name}</a>\n'
output += '\n'
# АДМИНИСТРАТОРЫ
if db_admins:
output += f'{tg_emoji("4961064956368782417")} <b>Администраторы</b> ({len(db_admins)}):\n'
for admin_id in sorted(db_admins):
display_name = await get_user_display_name(admin_id)
output += f'├─ <a href="tg://user?id={admin_id}">{display_name}</a>\n'
output += '\n'
output += f'{tg_emoji("4961106084975608869")} <b>Права администраторов:</b>\n'
output += '├─ Управление банвордами\n'
output += '├─ Просмотр статистики\n'
output += '├─ Активация режимов модерации\n'
output += '└─ Все команды бота\n\n'
else:
output += f'{tg_emoji("4961064956368782417")} <b>Администраторы:</b>\n'
output += '└─ <i>Нет дополнительных администраторов</i>\n\n'
total_admins = len(settings.OWNER_ID) + len(db_admins)
output += f'{tg_emoji("4961061266991875258")} <b>Итого:</b> {total_admins} администратор(ов)\n\n'
output += f'{tg_emoji("4961027057577362562")} <b>Управление:</b>\n'
output += '• <b>/adminhelp</b> — помощь по командам админов\n'
output += '• <code>/addadmin</code> <code>ID</code> — добавить админа\n'
output += '• <code>/remadmin</code> <code>ID</code> — удалить админа\n\n'
output += f'{tg_emoji("4961186405159011104")} <i>Только владельцы могут управлять администраторами</i>'
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")} <b>Ошибка загрузки списка</b>\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")} <b>Как добавить администратора?</b>\n\n'
f'{tg_emoji("4960889107522782272")} Узнайте Telegram ID пользователя\n'
' • Используйте команду <b>/id</b> или бота @userinfobot\n'
f'{tg_emoji("4960889107522782272")} Выполните команду:\n'
' <code>/addadmin ID</code>\n\n'
'Пример:\n'
'<code>/addadmin 123456789</code>'
)
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")} <b>УПРАВЛЕНИЕ АДМИНИСТРАТОРАМИ</b>\n\n'
f'{tg_emoji("4963401727815451692")} <b>Уровни доступа:</b>\n\n'
f'{tg_emoji("4963343509533754468")} <b>Владельцы</b> (OWNER_ID):\n'
'├─ Все права администратора\n'
'├─ Управление другими админами\n'
'└─ Указываются в конфигурации\n\n'
f'{tg_emoji("4961064956368782417")} <b>Администраторы:</b>\n'
'├─ Управление банвордами\n'
'├─ Просмотр статистики\n'
'├─ Активация режимов модерации\n'
'└─ Не могут управлять админами\n\n'
f'{tg_emoji("4963241130398319816")} <b>Команды:</b>\n'
'• <b>/adminhelp</b> — помощь по командам админов\n'
'• <b>/listadmins</b> — список всех админов\n'
'• <code>/addadmin</code> <code>ID</code> — добавить админа\n'
'• <code>/remadmin</code> <code>ID</code> — удалить админа\n\n'
f'{tg_emoji("4961186405159011104")} <b>Как узнать ID пользователя?</b>\n'
'• Используйте команду <b>/id</b> или бота @userinfobot\n'
'• Или попросите пользователя написать боту\n'
'• ID отображается в логах бота\n\n'
f'{tg_emoji("4963024861615096794")} <b>Важно:</b>\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")} <b>Проверка пользователя</b>\n\n'
text += f'{tg_emoji("4961064956368782417")} ID: <code>{user_id}</code>\n\n'
if is_owner:
text += f'{tg_emoji("4963343509533754468")} Статус: <b>Владелец бота</b>\n'
text += f'{tg_emoji("4963010134172239128")} Полные права администратора\n'
text += f'{tg_emoji("4963010134172239128")} Может управлять админами'
elif is_db_admin:
text += f'{tg_emoji("4961064956368782417")} Статус: <b>Администратор</b>\n'
text += f'{tg_emoji("4963010134172239128")} Доступ к командам бота\n'
text += f'{tg_emoji("4961187972822074653")} Не может управлять админами'
else:
text += f'{tg_emoji("4961064956368782417")} Статус: <b>Обычный пользователь</b>\n'
text += f'{tg_emoji("4961187972822074653")} Нет прав администратора\n\n'
text += f'Добавить в админы: <code>/addadmin {user_id}</code>'
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")} <b>Ошибка проверки</b>', parse_mode='HTML')