""" Обработчики команд режима антиконфликта """ from datetime import datetime from aiogram import Router 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="conflict_mode_router") # ================= ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ================= def parse_conflict_args(text: str, command: str, need_minutes: bool = False) -> tuple[bool, str | list]: """ Парсит аргументы команды для конфликтного режима. Args: text: Полный текст сообщения command: Название команды need_minutes: Требуется ли параметр минут Returns: (success, result): result это либо список аргументов, либо текст ошибки """ parts = text.split(maxsplit=2 if need_minutes else 1) min_args = 1 if need_minutes else 1 if len(parts) < min_args + 1: if need_minutes: return False, f"❌ Использование: /{command} [минуты]" else: return False, f"❌ Использование: /{command} [слово]" args = parts[1:] # Валидация слова if not need_minutes: if len(args[0]) < 2: return False, "❌ Слово должно содержать минимум 2 символа" if len(args[0]) > 100: return False, "❌ Слово слишком длинное (максимум 100 символов)" return True, args def format_time_str(minutes: int) -> str: """Форматирует время в читабельный формат""" if minutes < 60: return f"{minutes} мин" elif minutes < 1440: hours = minutes // 60 mins = minutes % 60 return f"{hours}ч {mins}м" if mins else f"{hours}ч" else: days = minutes // 1440 hours = (minutes % 1440) // 60 return f"{days}д {hours}ч" if hours else f"{days}д" def format_datetime(dt: datetime) -> str: """Форматирует datetime в читабельный формат""" return dt.strftime("%d.%m.%Y %H:%M:%S") # ================= ДОБАВЛЕНИЕ КОНФЛИКТНЫХ СЛОВ ================= @router.message( Command(*COMMANDS.get("addconflictword", ["addconflictword"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="ADD_CONFLICT_WORD", log_args=True) async def add_conflict_word_cmd(message: Message) -> None: """ Добавляет конфликтное слово-подстроку. Конфликтные слова работают только в режиме /stopconflict. Использование: /addconflictword <слово> """ success, result = parse_conflict_args(message.text, "addconflictword", need_minutes=False) 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.CONFLICT_SUBSTRING, added_by=message.from_user.id, reason="Конфликтное слово" ) if added: text = ( f"✅ Конфликтное слово добавлено\n\n" f"📝 Слово: {word}\n" f"🔍 Тип: подстрока\n\n" f"⚔️ Будет работать только в режиме антиконфликта\n" f"Активируйте: /stopconflict [минуты]" ) else: text = f"⚠️ Конфликтное слово {word} уже существует" await message.answer(text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка добавления конфликтного слова: {e}", log_type="CONFLICT") await message.answer("❌ Ошибка добавления\n\nПопробуйте позже", parse_mode="HTML") @router.message( Command(*COMMANDS.get("addconflictlemma", ["addconflictlemma"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="ADD_CONFLICT_LEMMA", log_args=True) async def add_conflict_lemma_cmd(message: Message) -> None: """ Добавляет конфликтную лемму. Конфликтные леммы работают только в режиме /stopconflict. Использование: /addconflictlemma <слово> """ success, result = parse_conflict_args(message.text, "addconflictlemma", need_minutes=False) 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.CONFLICT_LEMMA, added_by=message.from_user.id, reason="Конфликтная лемма" ) if added: text = ( f"✅ Конфликтная лемма добавлена\n\n" f"🔤 Слово: {word}\n" f"🔍 Тип: лемма (все формы слова)\n\n" f"⚔️ Будет работать только в режиме антиконфликта\n" f"Активируйте: /stopconflict [минуты]" ) else: text = f"⚠️ Конфликтная лемма {word} уже существует" await message.answer(text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка добавления конфликтной леммы: {e}", log_type="CONFLICT") await message.answer("❌ Ошибка добавления\n\nПопробуйте позже", parse_mode="HTML") # ================= УДАЛЕНИЕ КОНФЛИКТНЫХ СЛОВ ================= @router.message( Command(*COMMANDS.get("remconflictword", ["remconflictword"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="REMOVE_CONFLICT_WORD", log_args=True) async def remove_conflict_word_cmd(message: Message) -> None: """ Удаляет конфликтное слово-подстроку. Использование: /remconflictword <слово> """ success, result = parse_conflict_args(message.text, "remconflictword", need_minutes=False) 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.CONFLICT_SUBSTRING ) if removed: text = f"🗑 Конфликтное слово удалено\n\n📝 Слово: {word}" else: text = f"⚠️ Конфликтное слово {word} не найдено" await message.answer(text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка удаления конфликтного слова: {e}", log_type="CONFLICT") await message.answer("❌ Ошибка удаления\n\nПопробуйте позже", parse_mode="HTML") @router.message( Command(*COMMANDS.get("remconflictlemma", ["remconflictlemma"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="REMOVE_CONFLICT_LEMMA", log_args=True) async def remove_conflict_lemma_cmd(message: Message) -> None: """ Удаляет конфликтную лемму. Использование: /remconflictlemma <слово> """ success, result = parse_conflict_args(message.text, "remconflictlemma", need_minutes=False) 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.CONFLICT_LEMMA ) if removed: text = f"🗑 Конфликтная лемма удалена\n\n🔤 Слово: {word}" else: text = f"⚠️ Конфликтная лемма {word} не найдена" await message.answer(text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка удаления конфликтной леммы: {e}", log_type="CONFLICT") await message.answer("❌ Ошибка удаления\n\nПопробуйте позже", parse_mode="HTML") # ================= УПРАВЛЕНИЕ РЕЖИМОМ АНТИКОНФЛИКТА ================= @router.message(Command(*COMMANDS.get("stopconflict", ["stopconflict"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="START_CONFLICT_MODE", log_args=True) async def start_conflict_mode_cmd(message: Message) -> None: """ Активирует режим антиконфликта на указанное время. В этом режиме работают только конфликтные слова/леммы. Обычные банворды временно отключаются. Использование: /stopconflict <минуты> Пример: /stopconflict 30 """ success, result = parse_conflict_args(message.text, "stopconflict", need_minutes=True) if not success: await message.answer(result, parse_mode="HTML") return # Валидация минут try: minutes = int(result[0]) 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: # Получаем статистику конфликтных слов data = await manager.get_all_words_list() conflict_words_count = len(data.get('conflict_substring', set())) conflict_lemmas_count = len(data.get('conflict_lemma', set())) total_conflict = conflict_words_count + conflict_lemmas_count if total_conflict == 0: await message.answer( "⚠️ Нет конфликтных слов\n\n" "Сначала добавьте конфликтные слова:\n" "• /addconflictword [слово]\n" "• /addconflictlemma [слово]", parse_mode="HTML" ) return # Активируем режим expires_at = await manager.set_conflict_mode(minutes) time_str = format_time_str(minutes) expires_str = format_datetime(expires_at) text = ( f"⚔️ РЕЖИМ АНТИКОНФЛИКТА АКТИВИРОВАН\n\n" f"⏱ Длительность: {time_str}\n" f"🕐 Окончание: {expires_str}\n\n" f"📊 Активные правила:\n" f"├─ Конфликтные слова: {conflict_words_count}\n" f"└─ Конфликтные леммы: {conflict_lemmas_count}\n\n" f"⚠️ Обычные банворды временно отключены\n" f"Отключить режим: /unstopconflict" ) await message.answer(text, parse_mode="HTML") logger.info( f"Режим антиконфликта активирован на {minutes} мин " f"(конфликтных правил: {total_conflict})", log_type="CONFLICT" ) except Exception as e: logger.error(f"Ошибка активации режима антиконфликта: {e}", log_type="CONFLICT") await message.answer("❌ Ошибка активации режима\n\nПопробуйте позже", parse_mode="HTML") @router.message(Command(*COMMANDS.get("unstopconflict", ["unstopconflict"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="STOP_CONFLICT_MODE") async def stop_conflict_mode_cmd(message: Message) -> None: """ Отключает режим антиконфликта. Использование: /unstopconflict """ manager = get_manager() try: # Проверяем, активен ли режим is_active = await manager.is_conflict_active() if not is_active: await message.answer( "⚠️ Режим антиконфликта не активен\n\n" "Активируйте: /stopconflict [минуты]", parse_mode="HTML" ) return # Отключаем режим await manager.disable_conflict_mode() text = ( f"✅ Режим антиконфликта отключен\n\n" f"🔄 Обычные банворды снова активны\n" f"⚔️ Конфликтные слова деактивированы" ) await message.answer(text, parse_mode="HTML") logger.info("Режим антиконфликта отключён", log_type="CONFLICT") except Exception as e: logger.error(f"Ошибка отключения режима антиконфликта: {e}", log_type="CONFLICT") await message.answer("❌ Ошибка отключения режима\n\nПопробуйте позже", parse_mode="HTML") @router.message(Command(*COMMANDS.get("conflictstatus", ["conflictstatus"]), prefix=settings.PREFIX, ignore_case=True), IsAdmin()) @log_action(action_name="CONFLICT_STATUS") async def conflict_status_cmd(message: Message) -> None: """ Показывает статус режима антиконфликта. Использование: /conflictstatus """ manager = get_manager() try: # Проверяем активность режима is_active = await manager.is_conflict_active() # Получаем статистику data = await manager.get_all_words_list() conflict_words_count = len(data.get('conflict_substring', set())) conflict_lemmas_count = len(data.get('conflict_lemma', set())) total_conflict = conflict_words_count + conflict_lemmas_count if is_active: # Режим активен - показываем детали conflict_until_str = await manager.repo.get_setting("conflict_until") conflict_until = float(conflict_until_str) expires_at = datetime.fromtimestamp(conflict_until) now = datetime.now() time_left_seconds = (expires_at - now).total_seconds() time_left_minutes = int(time_left_seconds / 60) text = ( f"⚔️ РЕЖИМ АНТИКОНФЛИКТА АКТИВЕН\n\n" f"⏱ Осталось: {format_time_str(time_left_minutes)}\n" f"🕐 Окончание: {format_datetime(expires_at)}\n\n" f"📊 Активные правила:\n" f"├─ Конфликтные слова: {conflict_words_count}\n" f"└─ Конфликтные леммы: {conflict_lemmas_count}\n\n" f"⚠️ Обычные банворды отключены\n" f"Отключить: /unstopconflict" ) else: # Режим не активен text = ( f"💤 Режим антиконфликта НЕ активен\n\n" f"📊 Конфликтных правил в базе:\n" f"├─ Слова: {conflict_words_count}\n" f"└─ Леммы: {conflict_lemmas_count}\n\n" ) if total_conflict > 0: text += f"Активировать: /stopconflict [минуты]" else: text += ( f"⚠️ Нет конфликтных слов\n" f"Добавьте:\n" f"• /addconflictword [слово]\n" f"• /addconflictlemma [слово]" ) await message.answer(text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка получения статуса режима: {e}", log_type="CONFLICT") await message.answer("❌ Ошибка получения статуса", parse_mode="HTML")