""" Обработчики команд добавления и удаления банвордов """ from aiogram import Router, F from aiogram.filters import Command from aiogram.types import Message from bot.filters.admin import IsAdmin from configs import settings, COMMANDS from database import get_manager from database.models import BanWordType from middleware.loggers import logger from bot.utils.decorators import log_action __all__ = ("router",) router: Router = Router(name="manage_words_router") # ================= ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ================= def parse_args(text: str, command: str, min_args: int = 1, max_args: int = 2) -> tuple[bool, str | list]: """ Парсит аргументы команды. Args: text: Полный текст сообщения command: Название команды min_args: Минимальное количество аргументов max_args: Максимальное количество аргументов Returns: (success, result): result это либо список аргументов, либо текст ошибки """ # Убираем команду из текста parts = text.split(maxsplit=max_args) if len(parts) < min_args + 1: return False, f"❌ Использование: /{command} {'<слово>' if min_args == 1 else '<слово> <минуты>'}" args = parts[1:] # Валидация длины слова if args and len(args[0]) < 2: return False, "❌ Слово должно содержать минимум 2 символа" if args and len(args[0]) > 100: return False, "❌ Слово слишком длинное (максимум 100 символов)" return True, args def format_success_message(action: str, word: str, word_type: str, extra: str = "") -> str: """Форматирует сообщение об успехе""" emoji_map = { 'добавлена': '✅', 'добавлен': '✅', 'добавлено': '✅', 'удалена': '🗑', 'удален': '🗑', 'удалено': '🗑' } emoji = emoji_map.get(action, '✅') message = f"{emoji} {word_type.capitalize()} {word} {action}" if extra: message += f"\n{extra}" return message # ================= КОМАНДЫ ДОБАВЛЕНИЯ ================= @router.message(Command(*COMMANDS.get("addword", ["addword"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="ADD_WORD", log_args=True) async def add_word_cmd(message: Message) -> None: """ Добавляет банворд-подстроку (постоянно). Использование: /addword <слово> """ success, result = parse_args(message.text, "addword", min_args=1, max_args=1) if not success: await message.answer(result, parse_mode="HTML") return word = result[0].lower().strip() manager = get_manager() try: added = await manager.add_banword( word=word, word_type=BanWordType.SUBSTRING, added_by=message.from_user.id, reason=f"Добавлено через команду" ) if added: text = format_success_message( "добавлена", word, "подстрока", "🔍 Тип проверки: простой поиск в тексте" ) else: text = f"⚠️ Подстрока {word} уже существует" await message.answer(text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка добавления банворда: {e}", log_type="CMD") await message.answer("❌ Ошибка добавления\n\nПопробуйте позже", parse_mode="HTML") @router.message(Command(*COMMANDS.get("addlemma", ["addlemma"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="ADD_LEMMA", log_args=True) async def add_lemma_cmd(message: Message) -> None: """ Добавляет банворд-лемму (постоянно). Использование: /addlemma <слово> """ success, result = parse_args(message.text, "addlemma", min_args=1, max_args=1) if not success: await message.answer(result, parse_mode="HTML") return word = result[0].lower().strip() manager = get_manager() try: added = await manager.add_banword( word=word, word_type=BanWordType.LEMMA, added_by=message.from_user.id, reason=f"Добавлено через команду" ) if added: text = format_success_message( "добавлена", word, "лемма", "🔤 Тип проверки: все формы слова (купить→куплю, купил, купишь...)" ) else: text = f"⚠️ Лемма {word} уже существует" await message.answer(text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка добавления леммы: {e}", log_type="CMD") await message.answer("❌ Ошибка добавления\n\nПопробуйте позже", parse_mode="HTML") @router.message(Command(*COMMANDS.get("addpart", ["addpart"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="ADD_PART", log_args=True) async def add_part_cmd(message: Message) -> None: """ Добавляет банворд-часть (постоянно). Использование: /addpart <комбинация> """ success, result = parse_args(message.text, "addpart", min_args=1, max_args=1) if not success: await message.answer(result, parse_mode="HTML") return word = result[0].lower().strip() manager = get_manager() try: added = await manager.add_banword( word=word, word_type=BanWordType.PART, added_by=message.from_user.id, reason=f"Добавлено через команду" ) if added: text = format_success_message( "добавлена", word, "часть", "🧩 Тип проверки: поиск без пробелов (обходит \"к у п и т ь\")" ) else: text = f"⚠️ Часть {word} уже существует" await message.answer(text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка добавления части: {e}", log_type="CMD") await message.answer("❌ Ошибка добавления\n\nПопробуйте позже", parse_mode="HTML") @router.message(Command(*COMMANDS.get("addtempword", ["addtempword"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="ADD_TEMP_WORD", log_args=True) async def add_temp_word_cmd(message: Message) -> None: """ Добавляет временную банворд-подстроку. Использование: /addtempword <слово> <минуты> """ success, result = parse_args(message.text, "addtempword", min_args=2, max_args=2) if not success: await message.answer(result, parse_mode="HTML") return word = result[0].lower().strip() # Валидация минут try: minutes = int(result[1]) if minutes < 1 or minutes > 10080: # Максимум неделя await message.answer("❌ Время должно быть от 1 минуты до 10080 минут (7 дней)", parse_mode="HTML") return except ValueError: await message.answer("❌ Неверный формат времени. Укажите число минут", parse_mode="HTML") return manager = get_manager() try: added = await manager.add_temp_banword( word=word, word_type=BanWordType.SUBSTRING, minutes=minutes, added_by=message.from_user.id ) if added: # Форматируем время if minutes < 60: time_str = f"{minutes} мин" elif minutes < 1440: hours = minutes // 60 mins = minutes % 60 time_str = f"{hours}ч {mins}м" if mins else f"{hours}ч" else: days = minutes // 1440 hours = (minutes % 1440) // 60 time_str = f"{days}д {hours}ч" if hours else f"{days}д" text = format_success_message( "добавлена", word, "временная подстрока", f"⏱ Автоматически удалится через {time_str}" ) else: text = f"⚠️ Временная подстрока {word} уже существует" await message.answer(text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка добавления временного банворда: {e}", log_type="CMD") await message.answer("❌ Ошибка добавления\n\nПопробуйте позже", parse_mode="HTML") @router.message(Command(*COMMANDS.get("addtemplemma", ["addtemplemma"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="ADD_TEMP_LEMMA", log_args=True) async def add_temp_lemma_cmd(message: Message) -> None: """ Добавляет временную банворд-лемму. Использование: /addtemplemma <слово> <минуты> """ success, result = parse_args(message.text, "addtemplemma", min_args=2, max_args=2) if not success: await message.answer(result, parse_mode="HTML") return word = result[0].lower().strip() try: minutes = int(result[1]) if minutes < 1 or minutes > 10080: await message.answer("❌ Время должно быть от 1 минуты до 10080 минут (7 дней)", parse_mode="HTML") return except ValueError: await message.answer("❌ Неверный формат времени. Укажите число минут", parse_mode="HTML") return manager = get_manager() try: added = await manager.add_temp_banword( word=word, word_type=BanWordType.LEMMA, minutes=minutes, added_by=message.from_user.id ) if added: if minutes < 60: time_str = f"{minutes} мин" elif minutes < 1440: hours = minutes // 60 mins = minutes % 60 time_str = f"{hours}ч {mins}м" if mins else f"{hours}ч" else: days = minutes // 1440 hours = (minutes % 1440) // 60 time_str = f"{days}д {hours}ч" if hours else f"{days}д" text = format_success_message( "добавлена", word, "временная лемма", f"⏱ Автоматически удалится через {time_str}" ) else: text = f"⚠️ Временная лемма {word} уже существует" await message.answer(text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка добавления временной леммы: {e}", log_type="CMD") await message.answer("❌ Ошибка добавления\n\nПопробуйте позже", parse_mode="HTML") @router.message(Command(*COMMANDS.get("addexcept", ["addexcept"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="ADD_EXCEPTION", log_args=True) async def add_exception_cmd(message: Message) -> None: """ Добавляет исключение в whitelist. Использование: /addexcept <текст> """ success, result = parse_args(message.text, "addexcept", min_args=1, max_args=1) if not success: await message.answer(result, parse_mode="HTML") return word = result[0].lower().strip() manager = get_manager() try: added = await manager.add_whitelist( word=word, added_by=message.from_user.id, reason="Добавлено через команду" ) if added: text = format_success_message( "добавлено", word, "исключение", "✅ Сообщения с этим текстом не будут проверяться" ) else: text = f"⚠️ Исключение {word} уже существует" await message.answer(text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка добавления исключения: {e}", log_type="CMD") await message.answer("❌ Ошибка добавления\n\nПопробуйте позже", parse_mode="HTML") # ================= КОМАНДЫ УДАЛЕНИЯ ================= @router.message(Command(*COMMANDS.get("remword", ["remword"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="REMOVE_WORD", log_args=True) async def remove_word_cmd(message: Message) -> None: """ Удаляет банворд-подстроку. Использование: /remword <слово> """ success, result = parse_args(message.text, "remword", min_args=1, max_args=1) if not success: await message.answer(result, parse_mode="HTML") return word = result[0].lower().strip() manager = get_manager() try: removed = await manager.remove_banword(word=word, word_type=BanWordType.SUBSTRING) if removed: text = format_success_message("удалена", word, "подстрока") else: text = f"⚠️ Подстрока {word} не найдена" await message.answer(text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка удаления банворда: {e}", log_type="CMD") await message.answer("❌ Ошибка удаления\n\nПопробуйте позже", parse_mode="HTML") @router.message(Command(*COMMANDS.get("remlemma", ["remlemma"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="REMOVE_LEMMA", log_args=True) async def remove_lemma_cmd(message: Message) -> None: """Удаляет банворд-лемму""" success, result = parse_args(message.text, "remlemma", min_args=1, max_args=1) if not success: await message.answer(result, parse_mode="HTML") return word = result[0].lower().strip() manager = get_manager() try: removed = await manager.remove_banword(word=word, word_type=BanWordType.LEMMA) if removed: text = format_success_message("удалена", word, "лемма") else: text = f"⚠️ Лемма {word} не найдена" await message.answer(text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка удаления леммы: {e}", log_type="CMD") await message.answer("❌ Ошибка удаления\n\nПопробуйте позже", parse_mode="HTML") @router.message(Command(*COMMANDS.get("rempart", ["rempart"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="REMOVE_PART", log_args=True) async def remove_part_cmd(message: Message) -> None: """Удаляет банворд-часть""" success, result = parse_args(message.text, "rempart", min_args=1, max_args=1) if not success: await message.answer(result, parse_mode="HTML") return word = result[0].lower().strip() manager = get_manager() try: removed = await manager.remove_banword(word=word, word_type=BanWordType.PART) if removed: text = format_success_message("удалена", word, "часть") else: text = f"⚠️ Часть {word} не найдена" await message.answer(text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка удаления части: {e}", log_type="CMD") await message.answer("❌ Ошибка удаления\n\nПопробуйте позже", parse_mode="HTML") @router.message(Command(*COMMANDS.get("remtempword", ["remtempword"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="REMOVE_TEMP_WORD", log_args=True) async def remove_temp_word_cmd(message: Message) -> None: """Удаляет временную подстроку""" success, result = parse_args(message.text, "remtempword", min_args=1, max_args=1) if not success: await message.answer(result, parse_mode="HTML") return word = result[0].lower().strip() manager = get_manager() try: removed = await manager.remove_temp_banword(word=word, word_type=BanWordType.SUBSTRING) if removed: text = format_success_message("удалена", word, "временная подстрока") else: text = f"⚠️ Временная подстрока {word} не найдена" await message.answer(text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка удаления временного банворда: {e}", log_type="CMD") await message.answer("❌ Ошибка удаления\n\nПопробуйте позже", parse_mode="HTML") @router.message(Command(*COMMANDS.get("remtemplemma", ["remtemplemma"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="REMOVE_TEMP_LEMMA", log_args=True) async def remove_temp_lemma_cmd(message: Message) -> None: """Удаляет временную лемму""" success, result = parse_args(message.text, "remtemplemma", min_args=1, max_args=1) if not success: await message.answer(result, parse_mode="HTML") return word = result[0].lower().strip() manager = get_manager() try: removed = await manager.remove_temp_banword(word=word, word_type=BanWordType.LEMMA) if removed: text = format_success_message("удалена", word, "временная лемма") else: text = f"⚠️ Временная лемма {word} не найдена" await message.answer(text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка удаления временной леммы: {e}", log_type="CMD") await message.answer("❌ Ошибка удаления\n\nПопробуйте позже", parse_mode="HTML") @router.message(Command(*COMMANDS.get("remexcept", ["remexcept"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="REMOVE_EXCEPTION", log_args=True) async def remove_exception_cmd(message: Message) -> None: """Удаляет исключение из whitelist""" success, result = parse_args(message.text, "remexcept", min_args=1, max_args=1) if not success: await message.answer(result, parse_mode="HTML") return word = result[0].lower().strip() manager = get_manager() try: removed = await manager.remove_whitelist(word=word) if removed: text = format_success_message("удалено", word, "исключение") else: text = f"⚠️ Исключение {word} не найдено" await message.answer(text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка удаления исключения: {e}", log_type="CMD") await message.answer("❌ Ошибка удаления\n\nПопробуйте позже", parse_mode="HTML")