Первый коммит
This commit is contained in:
434
bot/handlers/commands/users/admins.py
Normal file
434
bot/handlers/commands/users/admins.py
Normal file
@@ -0,0 +1,434 @@
|
||||
"""
|
||||
Обработчики команд управления администраторами
|
||||
"""
|
||||
from aiogram import Router, F
|
||||
from aiogram.filters import Command
|
||||
from aiogram.types import Message, CallbackQuery
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
|
||||
from bot.filters.admin import IsSuperAdmin
|
||||
from configs import settings, COMMANDS
|
||||
from database import get_manager
|
||||
from middleware.loggers import logger
|
||||
from bot.utils.decorators import log_action
|
||||
|
||||
__all__ = ("router",)
|
||||
|
||||
router: Router = Router(name="admin_management_router")
|
||||
|
||||
|
||||
# ================= ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ =================
|
||||
|
||||
def parse_user_id(text: str, command: str) -> tuple[bool, str | int]:
|
||||
"""
|
||||
Парсит ID пользователя из команды.
|
||||
|
||||
Args:
|
||||
text: Полный текст сообщения
|
||||
command: Название команды
|
||||
|
||||
Returns:
|
||||
(success, result): result это либо user_id (int), либо текст ошибки (str)
|
||||
"""
|
||||
parts = text.split(maxsplit=1)
|
||||
|
||||
if len(parts) < 2:
|
||||
return False, f"❌ Использование: <code>/{command} <ID></code>"
|
||||
|
||||
user_id_str = parts[1].strip()
|
||||
|
||||
# Валидация ID
|
||||
try:
|
||||
user_id = int(user_id_str)
|
||||
|
||||
if user_id <= 0:
|
||||
return False, "❌ ID должен быть положительным числом"
|
||||
|
||||
if user_id > 9999999999: # Максимальный Telegram ID
|
||||
return False, "❌ Некорректный ID пользователя"
|
||||
|
||||
return True, user_id
|
||||
|
||||
except ValueError:
|
||||
return False, "❌ 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:
|
||||
"""
|
||||
Добавляет нового администратора бота.
|
||||
|
||||
Доступно только владельцам бота (OWNER_ID).
|
||||
|
||||
Использование: /addadmin <ID>
|
||||
Пример: /addadmin 123456789
|
||||
"""
|
||||
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(
|
||||
"⚠️ <b>Вы уже владелец бота</b>\n\n"
|
||||
"Вам не нужно добавлять себя в администраторы",
|
||||
parse_mode="HTML"
|
||||
)
|
||||
return
|
||||
|
||||
# Проверка: нельзя добавить другого владельца
|
||||
if user_id in settings.OWNER_ID:
|
||||
await message.answer(
|
||||
"⚠️ <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:
|
||||
await message.answer(
|
||||
f"⚠️ Пользователь {format_admin_info(user_id)} уже является администратором",
|
||||
parse_mode="HTML"
|
||||
)
|
||||
return
|
||||
|
||||
# Добавляем администратора
|
||||
added = await manager.add_admin(
|
||||
user_id=user_id,
|
||||
added_by=message.from_user.id
|
||||
)
|
||||
|
||||
if added:
|
||||
text = (
|
||||
f"✅ <b>Администратор добавлен</b>\n\n"
|
||||
f"👤 ID: {format_admin_info(user_id)}\n"
|
||||
f"👑 Добавил: {format_admin_info(message.from_user.id, message.from_user.username)}\n\n"
|
||||
f"📋 Права администратора:\n"
|
||||
f"├─ Управление банвордами\n"
|
||||
f"├─ Просмотр статистики\n"
|
||||
f"├─ Активация режимов модерации\n"
|
||||
f"└─ Все команды бота\n\n"
|
||||
f"⚠️ <i>Не может управлять другими админами</i>\n"
|
||||
f"Список админов: /listadmins"
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"Администратор добавлен: {user_id} (добавил: {message.from_user.id})",
|
||||
log_type="ADMIN_MGMT"
|
||||
)
|
||||
else:
|
||||
text = "❌ <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("❌ <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:
|
||||
"""
|
||||
Удаляет администратора бота.
|
||||
|
||||
Доступно только владельцам бота (OWNER_ID).
|
||||
|
||||
Использование: /remadmin <ID>
|
||||
Пример: /remadmin 123456789
|
||||
"""
|
||||
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(
|
||||
"⚠️ <b>Нельзя удалить владельца</b>\n\n"
|
||||
"Владельцы имеют права постоянно",
|
||||
parse_mode="HTML"
|
||||
)
|
||||
return
|
||||
|
||||
# Проверка: нельзя удалить самого себя (если вы владелец)
|
||||
if user_id == message.from_user.id:
|
||||
await message.answer(
|
||||
"⚠️ <b>Нельзя удалить самого себя</b>",
|
||||
parse_mode="HTML"
|
||||
)
|
||||
return
|
||||
|
||||
manager = get_manager()
|
||||
|
||||
try:
|
||||
# Проверяем, является ли администратором
|
||||
is_admin = await manager.is_admin(user_id)
|
||||
|
||||
if not is_admin:
|
||||
await message.answer(
|
||||
f"⚠️ Пользователь {format_admin_info(user_id)} не является администратором",
|
||||
parse_mode="HTML"
|
||||
)
|
||||
return
|
||||
|
||||
# Удаляем администратора
|
||||
removed = await manager.remove_admin(user_id=user_id)
|
||||
|
||||
if removed:
|
||||
text = (
|
||||
f"🗑 <b>Администратор удалён</b>\n\n"
|
||||
f"👤 ID: {format_admin_info(user_id)}\n"
|
||||
f"👑 Удалил: {format_admin_info(message.from_user.id, message.from_user.username)}\n\n"
|
||||
f"⚠️ <i>Пользователь больше не имеет доступа к командам бота</i>"
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"Администратор удалён: {user_id} (удалил: {message.from_user.id})",
|
||||
log_type="ADMIN_MGMT"
|
||||
)
|
||||
else:
|
||||
text = "❌ <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("❌ <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:
|
||||
"""
|
||||
Показывает список всех администраторов бота.
|
||||
|
||||
Доступно только владельцам бота (OWNER_ID).
|
||||
|
||||
Использование: /listadmins
|
||||
"""
|
||||
# Определяем тип update
|
||||
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()
|
||||
|
||||
# Получаем статистику
|
||||
stats = await manager.get_stats()
|
||||
|
||||
# === ФОРМИРУЕМ ВЫВОД ===
|
||||
|
||||
output = "👥 <b>СПИСОК АДМИНИСТРАТОРОВ</b>\n\n"
|
||||
|
||||
# Владельцы (OWNER_ID)
|
||||
output += "👑 <b>Владельцы бота</b> (полные права):\n"
|
||||
for owner_id in settings.OWNER_ID:
|
||||
output += f"├─ <code>{owner_id}</code>\n"
|
||||
output += "\n"
|
||||
|
||||
# Администраторы из БД
|
||||
if db_admins:
|
||||
output += f"⚙️ <b>Администраторы</b> ({len(db_admins)}):\n"
|
||||
|
||||
for admin_id in sorted(db_admins):
|
||||
output += f"├─ <code>{admin_id}</code>\n"
|
||||
|
||||
output += "\n"
|
||||
output += "📋 <b>Права администраторов:</b>\n"
|
||||
output += "├─ Управление банвордами\n"
|
||||
output += "├─ Просмотр статистики\n"
|
||||
output += "├─ Активация режимов модерации\n"
|
||||
output += "└─ Все команды бота (кроме управления админами)\n\n"
|
||||
else:
|
||||
output += "⚙️ <b>Администраторы:</b>\n"
|
||||
output += "└─ <i>Нет дополнительных администраторов</i>\n\n"
|
||||
|
||||
# Общая статистика
|
||||
total_admins = len(settings.OWNER_ID) + len(db_admins)
|
||||
output += f"📊 <b>Итого:</b> {total_admins} администратор(ов)\n\n"
|
||||
|
||||
# Команды управления
|
||||
output += "🔧 <b>Управление:</b>\n"
|
||||
output += "• /addadmin <code>ID</code> — добавить админа\n"
|
||||
output += "• /remadmin <code>ID</code> — удалить админа\n\n"
|
||||
|
||||
output += "💡 <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("✅ Список обновлён")
|
||||
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 = "❌ <b>Ошибка загрузки списка</b>\n\nПопробуйте позже"
|
||||
|
||||
if is_callback:
|
||||
await update.answer("❌ Ошибка загрузки", 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 = (
|
||||
"➕ <b>Как добавить администратора?</b>\n\n"
|
||||
"1️⃣ Узнайте Telegram ID пользователя\n"
|
||||
" • Используйте бота @userinfobot\n"
|
||||
" • Или попросите пользователя написать /start\n\n"
|
||||
"2️⃣ Выполните команду:\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:
|
||||
"""
|
||||
Показывает подробную справку по управлению администраторами.
|
||||
|
||||
Использование: /adminhelp
|
||||
"""
|
||||
text = (
|
||||
"👥 <b>УПРАВЛЕНИЕ АДМИНИСТРАТОРАМИ</b>\n\n"
|
||||
"🔐 <b>Уровни доступа:</b>\n\n"
|
||||
"👑 <b>Владельцы</b> (OWNER_ID):\n"
|
||||
"├─ Все права администратора\n"
|
||||
"├─ Управление другими админами\n"
|
||||
"└─ Указываются в конфигурации\n\n"
|
||||
"⚙️ <b>Администраторы:</b>\n"
|
||||
"├─ Управление банвордами\n"
|
||||
"├─ Просмотр статистики\n"
|
||||
"├─ Активация режимов модерации\n"
|
||||
"└─ НЕ могут управлять админами\n\n"
|
||||
"📝 <b>Команды:</b>\n"
|
||||
"• /listadmins — список всех админов\n"
|
||||
"• /addadmin <code>ID</code> — добавить админа\n"
|
||||
"• /remadmin <code>ID</code> — удалить админа\n\n"
|
||||
"💡 <b>Как узнать ID пользователя?</b>\n"
|
||||
"• Используйте бота @userinfobot\n"
|
||||
"• Попросите пользователя написать боту\n"
|
||||
"• ID отображается в логах бота\n\n"
|
||||
"⚠️ <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:
|
||||
"""
|
||||
Проверяет, является ли пользователь администратором.
|
||||
|
||||
Использование: /checkadmin <ID>
|
||||
"""
|
||||
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"🔍 <b>Проверка пользователя</b>\n\n"
|
||||
text += f"👤 ID: <code>{user_id}</code>\n\n"
|
||||
|
||||
if is_owner:
|
||||
text += "👑 Статус: <b>Владелец бота</b>\n"
|
||||
text += "✅ Полные права администратора\n"
|
||||
text += "✅ Может управлять админами"
|
||||
elif is_db_admin:
|
||||
text += "⚙️ Статус: <b>Администратор</b>\n"
|
||||
text += "✅ Доступ к командам бота\n"
|
||||
text += "❌ Не может управлять админами"
|
||||
else:
|
||||
text += "👤 Статус: <b>Обычный пользователь</b>\n"
|
||||
text += "❌ Нет прав администратора\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("❌ <b>Ошибка проверки</b>", parse_mode="HTML")
|
||||
Reference in New Issue
Block a user