diff --git a/src/config.py b/src/config.py index 153d051..a6920d0 100644 --- a/src/config.py +++ b/src/config.py @@ -8,6 +8,12 @@ DATABASE_NAME = 'users.db' COMMAND_MESSAGES = { 'start': 'Бот-модератор для чата @linux_gaming_ru', 'help': 'пусто', + 'manual_mute': ( + 'ℹ️ Использование мута:\n' + '1️⃣ Ответьте на сообщение: /mute время\n' + '2️⃣ Укажите тэг: /mute @username время\n' + '3️⃣ Укажите ID: /mute 123456789 время\n\n' + ), 'manual_ban': ( 'ℹ️ Использование бана:\n' '1️⃣ Ответьте на сообщение: /ban\n' @@ -23,7 +29,12 @@ COMMAND_MESSAGES = { 'no_admin_rights': '❌ Только администраторы могут использовать эту команду.', 'no_restrict_rights': '❌ У вас недостаточно прав.', 'user_not_found': '❌ Пользователь не найден.', + 'incorrect_time_format': '❌ Неверный формат времени. Используйте: 10м, 1ч, 2д.', + 'min_mute': '❌ Минимальное время мута - 1 минута.', + 'max_mute': '❌ Максимальное время мута - 30 дней.', + 'cant_mute_admin': '❌ Невозможно замутить администратора.', 'cant_ban_admin': '❌ Невозможно забанить администратора.', + 'muted': '✅ Пользователь замучен на {time_display}.', 'banned': '✅ Пользователь успешно забанен.', 'unbanned': '✅ Пользователь успешно разбанен.', 'general_error': '⚠️ Произошла непредвиденная ошибка.' diff --git a/src/modules/mute.py b/src/modules/mute.py new file mode 100644 index 0000000..9cfdd70 --- /dev/null +++ b/src/modules/mute.py @@ -0,0 +1,343 @@ +from telebot.async_telebot import AsyncTeleBot +from telebot.types import Message, User, ChatPermissions +import asyncio +import logging +import time +from database import db + +from config import COMMAND_MESSAGES + +logger = logging.getLogger(__name__) # Получаем логгер для текущего модуля + +# Возвращает количество секунд +def parse_mute_time(time_str: str) -> int: + + # Парсим строку времени + time_str = time_str.strip().lower() + + # Минуты + if time_str.endswith('m') or time_str.endswith('м'): + try: + minutes = int(time_str[:-1]) + return abs(minutes) * 60 + except: + return None + + # Часы + elif time_str.endswith('h') or time_str.endswith('ч'): + try: + hours = int(time_str[:-1]) + return abs(hours) * 3600 + except: + return None + + # Дни + elif time_str.endswith('d') or time_str.endswith('д'): + try: + days = int(time_str[:-1]) + return abs(days) * 86400 + except: + return None + + # Число без указания единицы (по умолчанию минуты) + elif time_str.isdigit(): + try: + minutes = int(time_str) + return abs(minutes) * 60 + except: + return None + + return None + +# Возвращает причину мута +def extract_reason(words: str) -> str: + + if words == []: + reason = 'отсутствует' + else: + reason = ' '.join(words) + + return reason + +# Форматирует время в нормальный вид +def format_time(seconds: int) -> str: + + # Для минут + minutes = seconds // 60 + if minutes < 60: + if minutes % 10 == 1 and minutes % 100 != 11: + return f"{minutes} минута" + elif 2 <= minutes % 10 <= 4 and minutes % 100 not in (12, 13, 14): + return f"{minutes} минуты" + else: + return f"{minutes} минут" + + # Для часов + hours = minutes // 60 + if hours < 24: + if hours % 10 == 1 and hours % 100 != 11: + return f"{hours} час" + elif 2 <= hours % 10 <= 4 and hours % 100 not in (12, 13, 14): + return f"{hours} часа" + else: + return f"{hours} часов" + + # Для дней + days = hours // 24 + if days % 10 == 1 and days % 100 != 11: + return f"{days} день" + elif 2 <= days % 10 <= 4 and days % 100 not in (12, 13, 14): + return f"{days} дня" + else: + return f"{days} дней" + +# Удаляет два последних сообщения +async def delete_messages(bot: AsyncTeleBot, message: Message, time_sleep: int): + await asyncio.sleep(time_sleep) + await bot.delete_message(message.chat.id, message.message_id) + await bot.delete_message(message.chat.id, message.message_id+1) + +def register_handlers(bot: AsyncTeleBot): # Регистрирует все обработчики команд + + @bot.message_handler(commands=['mute']) # Обработчик команды /mute + async def mute_command(message: Message): + + # Отправка сообщения в тему или обычный чат + send_message = bot.reply_to if message.is_topic_message else bot.send_message + chat_id = message if message.is_topic_message else message.chat.id + + # Определяем целевого пользователя + target_user = None + + # Отпределяем время + time_arg = None + + # Определяем причину + reason = None + + # Разбиваем текст сообщения на части + parts_msg = message.text.split() + + try: + # Проверяем, является ли отправитель администратором + try: + admin_status = await bot.get_chat_member(message.chat.id, message.from_user.id) + + # Проверяем статус администратора (создателя) + if admin_status.status not in ['administrator', 'creator']: + await send_message(chat_id, COMMAND_MESSAGES['no_admin_rights']) + + # Удаляем сообщения через 5 секунд + await delete_messages(bot, message, 5) + + return + + # Проверяем право администратора на мут + if admin_status.status == 'administrator' and not admin_status.can_restrict_members: + await send_message(chat_id, COMMAND_MESSAGES['no_restrict_rights']) + + # Удаляем сообщения через 5 секунд + await delete_messages(bot, message, 5) + + return + + except Exception as e: + await send_message(chat_id, f"⚠️ Ошибка: {str(e)}") + logger.error(f"Ошибка при получении статуса администратора: {str(e)}") + + # Удаляем сообщения через 5 секунд + await delete_messages(bot, message, 5) + + return + + # Выводим помощь + if len(parts_msg) == 2 and parts_msg[1].strip() in ['help', 'помощь']: + await send_message(chat_id, COMMAND_MESSAGES['manual_mute']) + + # Удаляем сообщения через 30 секунд + await delete_messages(bot, message, 30) + + return + + # Случай №1 - Команда используется в ответ на сообщение + if len(parts_msg) >= 2: + + # Если мутят в теме + if message.is_topic_message: + + # Если без ответа на сообщение + if message.message_thread_id == message.reply_to_message.message_id: + await asyncio.sleep(3) + await bot.delete_message(message.chat.id, message.message_id) + return + + # Если с ответом на сообщение + else: + target_user = message.reply_to_message.from_user + time_arg = parts_msg[1] + reason = extract_reason(parts_msg[2:]) + + # Если мутят в обычном чате + elif message.reply_to_message and message.is_topic_message is None: + target_user = message.reply_to_message.from_user + time_arg = parts_msg[1] + reason = extract_reason(parts_msg[2:]) + + # Не выводим сообщение, что команда неправильная + else: + await asyncio.sleep(3) + await bot.delete_message(message.chat.id, message.message_id) + return + + # Случай №2 - Команда через тег или ID + elif len(parts_msg) >= 3: + + identifier = parts_msg[1].strip() + time_arg = parts_msg[2] + reason = extract_reason(parts_msg[3:]) + + # Поиск по ID + if identifier.isdigit(): + + # Делаем в int и ищем + user_info = db.get_user(int(identifier)) + + if user_info: + # Создаем объект пользователя из данных базы + target_user = User( + id=user_info[0], + first_name=user_info[1], + username=user_info[2], + is_bot=False + ) + + # Поиск по тегу + elif identifier.startswith('@'): + + # Убираем @ и ищем + user_info = db.get_user_by_username(identifier[1:]) + + if user_info: + # Создаем объект пользователя из данных базы + target_user = User( + id=user_info[0], + first_name=user_info[1], + username=user_info[2], + is_bot=False + ) + + # Если команда неправильная + else: + await asyncio.sleep(3) + await bot.delete_message(message.chat.id, message.message_id) + return + + print(reason) + + # Если пользователь не найден + if not target_user: + await send_message(chat_id, COMMAND_MESSAGES['user_not_found']) + + # Удаляем сообщения через 5 секунд + await delete_messages(bot, message, 5) + + return + + # Парсинг времени мута + mute_seconds = parse_mute_time(time_arg) + if mute_seconds is None: + await send_message(chat_id, COMMAND_MESSAGES['incorrect_time_format']) + + # Удаляем сообщения через 5 секунд + await delete_messages(bot, message, 5) + + return + + # Минимальный мут 1 минута (60 секунд) + if mute_seconds < 60: + await send_message(chat_id, COMMAND_MESSAGES['min_mute']) + + # Удаляем сообщения через 5 секунд + await delete_messages(bot, message, 5) + + return + + # Максимальный мут 30 дней (2592000 секунд) + if mute_seconds > 2592000: + await send_message(chat_id, COMMAND_MESSAGES['max_mute']) + + # Удаляем сообщения через 5 секунд + await delete_messages(bot, message, 5) + + return + + # Проверяем статус целевого пользователя + try: + target_status = await bot.get_chat_member(message.chat.id, target_user.id) + + # Проверяем, является ли цель администратором или создателем + if target_status.status in ['administrator', 'creator']: + await send_message(chat_id, COMMAND_MESSAGES['cant_mute_admin']) + + # Удаляем сообщения через 5 секунд + await delete_messages(bot, message, 5) + + return + + except Exception as e: + await send_message(chat_id, f"⚠️ Ошибка: {str(e)}") + logger.error(f"Ошибка при получении статуса пользователя: {str(e)}") + + # Удаляем сообщения через 5 секунд + await delete_messages(bot, message, 5) + + return + + # Выполняем мут + try: + + # Вычисляем время окончания мута + until_date = int(time.time()) + mute_seconds + + # Устанавливаем ограничения (только чтение) + permissions = ChatPermissions( + can_send_messages=False, + can_send_media_messages=False, + can_send_polls=False, + can_send_other_messages=False, + can_add_web_page_previews=False, + can_change_info=False, + can_invite_users=False, + can_pin_messages=False, + ) + + await bot.restrict_chat_member( + chat_id=message.chat.id, + user_id=target_user.id, + permissions=permissions, + until_date=until_date + ) + + # Форматирование времени + time_display = format_time(mute_seconds) + + # Отправляем сообщения, что пользователь получил мут + await send_message(chat_id, COMMAND_MESSAGES['muted'].format(time_display=time_display)) + logger.info(f"Пользователь {target_user.id} замучен на {time_display} администратором {message.from_user.id}.") + + # Удаляем сообщения через 5 секунд + await delete_messages(bot, message, 5) + + except Exception as e: + await send_message(chat_id, f"⚠️ Ошибка: {str(e)}") + logger.error(f"Ошибка мута: {str(e)}") + + # Удаляем сообщения через 5 секунд + await delete_messages(bot, message, 5) + + except Exception as e: + await send_message(chat_id, COMMAND_MESSAGES['general_error']) + logger.error(f"Общая ошибка в mute_command: {str(e)}") + + # Удаляем сообщения через 5 секунд + await delete_messages(bot, message, 5) \ No newline at end of file