From 55c3a0a04f6510a52320d72d534518d9dabd7340 Mon Sep 17 00:00:00 2001 From: Verum Date: Mon, 23 Feb 2026 14:13:10 +0700 Subject: [PATCH] =?UTF-8?q?=D0=9C=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20=D0=BA?= =?UTF-8?q?=D0=B8=D0=BA=D0=B0=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D1=82=D0=B5=D0=BB=D1=8F=20(=D0=B2=20=D0=B1=D0=B5=D1=82?= =?UTF-8?q?=D0=B0!)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot/handlers/commands/admins/kick_cmd.py | 277 +++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 bot/handlers/commands/admins/kick_cmd.py diff --git a/bot/handlers/commands/admins/kick_cmd.py b/bot/handlers/commands/admins/kick_cmd.py new file mode 100644 index 0000000..e0fcadb --- /dev/null +++ b/bot/handlers/commands/admins/kick_cmd.py @@ -0,0 +1,277 @@ +from aiogram import Router +from aiogram.filters import Command +from aiogram.fsm.context import FSMContext +from aiogram.types import Message, User +from html import escape + +from bot import bot +from bot.filters import IsAdmin +from bot.utils import status_clear +from configs import COMMANDS + +# Настройки роутера +__all__ = ("router",) + +from middleware import logger + +CMD: str = "kick" +router: Router = Router(name=f"{CMD}_cmd_router") + + +@router.message(Command(*COMMANDS[CMD], ignore_case=True), IsAdmin()) +async def kick_user_cmd(message: Message, state: FSMContext) -> None: + """ + Команда /kick для кика пользователей из чата. + Использование: /kick или ответ на сообщение пользователя + /kick + """ + await status_clear(update=message, state=state) + + # Проверяем, что команда используется в группе/супергруппе + if message.chat.type not in ["group", "supergroup"]: + await message.answer("❌ Эта команда работает только в группах и супергруппах!") + return + + # Проверяем есть ли ответ на сообщение + if message.reply_to_message: + # Кик по ответу на сообщение + target_user: User | None = message.reply_to_message.from_user + target_user_id: int = target_user.id + target_username: str = target_user.username or target_user.full_name or f"ID{target_user_id}" + + # Кикаем пользователя + success: bool = await _kick_user(target_user_id, target_username, message) + + if success: + safe_username: str = escape(target_username) + await message.answer( + text=f"👢 Пользователь {safe_username} (ID: {target_user_id}) кикнут из чата!", + ) + else: + await message.answer("❌ Не удалось кикнуть пользователя") + + else: + # Кик по ID пользователя + command_parts: list[str] = message.text.split() + if len(command_parts) < 2: + await message.answer( + "ℹ️ Использование команды:\n" + "• Ответьте на сообщение пользователя командой /kick\n" + "• Или укажите ID: /kick " + ) + return + + try: + target_user_id: int = int(command_parts[1]) + success: bool = await _kick_user(target_user_id, f"ID{target_user_id}", message) + + if success: + await message.answer( + text=f"👢 Пользователь (ID: {target_user_id}) кикнут из чата!", + parse_mode=None # Отключаем разметку + ) + else: + await message.answer("❌ Пользователь не найден или не удалось кикнуть") + + except ValueError: + await message.answer("❌ Неверный формат ID пользователя") + + +async def _kick_user(user_id: int, username: str, message: Message) -> bool: + """ + Внутренняя функция для кика пользователя из чата. + + Args: + user_id: ID пользователя для кика + username: Имя пользователя для логов + message: Объект сообщения для контекста + + Returns: + bool: Успешно ли кикнут пользователь + """ + try: + # Проверяем, что бот имеет права администратора в чате + bot_member = await bot.get_chat_member(message.chat.id, bot.id) + if not bot_member.can_restrict_members: + await message.answer("❌ У меня нет прав для кика пользователей!") + return False + + # Проверяем, что целевой пользователь не является администратором/владельцем + target_member = await bot.get_chat_member(message.chat.id, user_id) + if target_member.status in ["creator", "administrator"]: + await message.answer("❌ Нельзя кикнуть администратора или создателя чата!") + return False + + # Проверяем, что отправитель команды имеет права администратора + admin_member = await bot.get_chat_member(message.chat.id, message.from_user.id) + if admin_member.status not in ["creator", "administrator"]: + await message.answer("❌ У вас нет прав для кика пользователей!") + return False + + # Кикаем пользователя из чата + await bot.ban_chat_member( + chat_id=message.chat.id, + user_id=user_id, + revoke_messages=False # Не удаляем сообщения пользователя + ) + + # Сразу разбаниваем, чтобы пользователь мог вернуться по приглашению + await bot.unban_chat_member( + chat_id=message.chat.id, + user_id=user_id + ) + + # Логируем действие + admin_username = message.from_user.username or message.from_user.full_name + logger.info( + f"👢 Админ @{admin_username} кикнул пользователя @{username} (ID: {user_id}) из чата {message.chat.title}") + + return True + + except Exception as e: + logger.error(f"❌ Ошибка при кике пользователя {user_id}: {e}") + await message.answer(f"❌ Ошибка при кике пользователя: {str(e)}") + return False + + +@router.message(Command("kick_ban", ignore_case=True), IsAdmin()) +async def kick_ban_user_cmd(message: Message, state: FSMContext) -> None: + """ + Команда /kick_ban для кика пользователя с удалением сообщений. + Использование: /kick_ban или ответ на сообщение пользователя + /kick_ban + """ + await status_clear(update=message, state=state) + + # Проверяем, что команда используется в группе/супергруппе + if message.chat.type not in ["group", "supergroup"]: + await message.answer("❌ Эта команда работает только в группах и супергруппах!") + return + + # Проверяем есть ли ответ на сообщение + if message.reply_to_message: + # Кик по ответу на сообщение + target_user: User | None = message.reply_to_message.from_user + target_user_id: int = target_user.id + target_username: str = target_user.username or target_user.full_name or f"ID{target_user_id}" + + # Кикаем пользователя с удалением сообщений + success: bool = await _kick_ban_user(target_user_id, target_username, message) + + if success: + safe_username: str = escape(target_username) + await message.answer( + text=f"💥 Пользователь {safe_username} (ID: {target_user_id}) кикнут с удалением сообщений!", + parse_mode=None # Отключаем разметку + ) + else: + await message.answer("❌ Не удалось кикнуть пользователя") + + else: + # Кик по ID пользователя + command_parts: list[str] = message.text.split() + if len(command_parts) < 2: + await message.answer( + "ℹ️ Использование команды:\n" + "• Ответьте на сообщение пользователя командой /kick_ban\n" + "• Или укажите ID: /kick_ban " + ) + return + + try: + target_user_id: int = int(command_parts[1]) + success: bool = await _kick_ban_user(target_user_id, f"ID{target_user_id}", message) + + if success: + await message.answer( + text=f"💥 Пользователь (ID: {target_user_id}) кикнут с удалением сообщений!", + parse_mode=None # Отключаем разметку + ) + else: + await message.answer("❌ Пользователь не найден или не удалось кикнуть") + + except ValueError: + await message.answer("❌ Неверный формат ID пользователя") + + +async def _kick_ban_user(user_id: int, username: str, message: Message) -> bool: + """ + Внутренняя функция для кика пользователя с удалением сообщений. + + Args: + user_id: ID пользователя для кика + username: Имя пользователя для логов + message: Объект сообщения для контекста + + Returns: + bool: Успешно ли кикнут пользователь + """ + try: + # Проверяем, что бот имеет права администратора в чате + bot_member = await bot.get_chat_member(message.chat.id, bot.id) + if not bot_member.can_restrict_members: + await message.answer("❌ У меня нет прав для кика пользователей!") + return False + + # Проверяем, что целевой пользователь не является администратором/владельцем + target_member = await bot.get_chat_member(message.chat.id, user_id) + if target_member.status in ["creator", "administrator"]: + await message.answer("❌ Нельзя кикнуть администратора или создателя чата!") + return False + + # Проверяем, что отправитель команды имеет права администратора + admin_member = await bot.get_chat_member(message.chat.id, message.from_user.id) + if admin_member.status not in ["creator", "administrator"]: + await message.answer("❌ У вас нет прав для кика пользователей!") + return False + + # Кикаем пользователя из чата с удалением сообщений + await bot.ban_chat_member( + chat_id=message.chat.id, + user_id=user_id, + revoke_messages=True # Удаляем сообщения пользователя + ) + + # Сразу разбаниваем, чтобы пользователь мог вернуться по приглашению + await bot.unban_chat_member( + chat_id=message.chat.id, + user_id=user_id + ) + + # Логируем действие + admin_username = message.from_user.username or message.from_user.full_name + logger.info( + f"💥 Админ @{admin_username} кикнул пользователя @{username} (ID: {user_id}) из чата {message.chat.title} с удалением сообщений") + + return True + + except Exception as e: + logger.error(f"❌ Ошибка при кике пользователя {user_id} с удалением сообщений: {e}") + await message.answer(f"❌ Ошибка при кике пользователя: {str(e)}") + return False + + +@router.message(Command("kick_list", ignore_case=True), IsAdmin()) +async def kick_help_cmd(message: Message, state: FSMContext) -> None: + """ + Команда /kick_list для показа справки по командам кика. + """ + await status_clear(update=message, state=state) + + help_text = """ +🤖 **Команды модерации:** + +**👢 /kick** - Кикнуть пользователя (может вернуться по приглашению) +• Ответьте на сообщение пользователя с командой /kick +• Или используйте: /kick + +**💥 /kick_ban** - Кикнуть пользователя с удалением сообщений +• Ответьте на сообщение пользователя с командой /kick_ban +• Или используйте: /kick_ban + +**🚫 /ban** - Полностью забанить пользователя +**🔓 /unban** - Разбанить пользователя +**📋 /banned_list** - Список забаненных + +⚠️ *Команды работают только в группах и требуют прав администратора* + """ + + await message.answer(help_text, parse_mode=None)