""" Обработчик команды /listwords - отображение всех правил модерации """ from aiogram import Router, F from aiogram.filters import Command from aiogram.types import Message, CallbackQuery from aiogram.utils.keyboard import InlineKeyboardBuilder from aiogram.exceptions import TelegramBadRequest from configs import settings, COMMANDS from database import get_manager from middleware.loggers import logger from bot.utils.decorators import log_action __all__ = ("router",) CMD: str = "list" router: Router = Router(name="listwords_cmd_router") def get_refresh_kb(page: int = 0): """Клавиатура с кнопкой обновления""" ikb = InlineKeyboardBuilder() ikb.button(text="🔄 Обновить", callback_data=f"listwords:refresh:{page}") ikb.button(text="📊 Статистика", callback_data="stats") ikb.adjust(2) return ikb.as_markup() async def format_banwords_list() -> str: """ Форматирует список всех банвордов с разбивкой по типам. Args: page: Номер страницы (для будущей пагинации) Returns: Отформатированная строка со всеми правилами """ manager = get_manager() # Получаем все данные из БД try: # Используем существующий метод get_all_words_list() data = await manager.get_all_words_list() stats = await manager.get_stats() # Извлекаем данные из словаря permanent_words = list(data.get('word', set())) permanent_lemmas = list(data.get('lemma', set())) permanent_parts = list(data.get('part', set())) temp_words = list(data.get('temp_word', set())) temp_lemmas = list(data.get('temp_lemma', set())) conflict_words = list(data.get('conflict_word', set())) conflict_lemmas = list(data.get('conflict_lemma', set())) conflict_parts = list(data.get('conflict_part', set())) exceptions = list(data.get('whitelist', set())) except Exception as e: logger.error(f"Ошибка получения данных из БД: {e}", log_type="LISTWORDS") return "❌ Ошибка загрузки данных из базы" # === ФОРМИРУЕМ ВЫВОД === output = "📋 СПИСОК ПРАВИЛ МОДЕРАЦИИ\n\n" # Статистика total_count = ( len(permanent_words) + len(permanent_lemmas) + len(permanent_parts) + len(temp_words) + len(temp_lemmas) + len(conflict_words) + len(conflict_lemmas) + len(conflict_parts) ) output += f"📊 Общая статистика:\n" output += f"├─ Всего правил: {total_count}\n" output += f"├─ Исключений: {len(exceptions)}\n" output += f"├─ Удалений за всё время: {stats.get('total_deletions', 0)}\n" output += f"└─ Администраторов: {stats.get('admins', 0)}\n\n" # === ПОСТОЯННЫЕ ПРАВИЛА === if permanent_words or permanent_lemmas or permanent_parts: output += "🔴 ПОСТОЯННЫЕ ПРАВИЛА:\n\n" if permanent_words: output += f"📝 Слова ({len(permanent_words)}):\n" words_str = ', '.join([f"{w}" for w in sorted(permanent_words)[:20]]) if len(permanent_words) > 20: words_str += f" ... (+{len(permanent_words) - 20} ещё)" output += f"{words_str}\n\n" if permanent_lemmas: output += f"🔤 Леммы (морф.формы) ({len(permanent_lemmas)}):\n" lemmas_str = ', '.join([f"{w}" for w in sorted(permanent_lemmas)[:20]]) if len(permanent_lemmas) > 20: lemmas_str += f" ... (+{len(permanent_lemmas) - 20} ещё)" output += f"{lemmas_str}\n\n" if permanent_parts: output += f"🧩 Части в сообщении ({len(permanent_parts)}):\n" parts_str = ', '.join([f"{w}" for w in sorted(permanent_parts)[:20]]) if len(permanent_parts) > 20: parts_str += f" ... (+{len(permanent_parts) - 20} ещё)" output += f"{parts_str}\n\n" # === ВРЕМЕННЫЕ ПРАВИЛА === if temp_words or temp_lemmas: output += "⏱ ВРЕМЕННЫЕ ПРАВИЛА:\n\n" if temp_words: output += f"📝 Временные слова ({len(temp_words)}):\n" # Для временных слов нужна дополнительная информация о времени истечения # Пока просто выводим список words_str = ', '.join([f"{w}" for w in sorted(temp_words)[:15]]) if len(temp_words) > 15: words_str += f" ... (+{len(temp_words) - 15} ещё)" output += f"{words_str}\n\n" if temp_lemmas: output += f"🔤 Временные леммы ({len(temp_lemmas)}):\n" lemmas_str = ', '.join([f"{w}" for w in sorted(temp_lemmas)[:15]]) if len(temp_lemmas) > 15: lemmas_str += f" ... (+{len(temp_lemmas) - 15} ещё)" output += f"{lemmas_str}\n\n" # === КОНФЛИКТНЫЕ ПРАВИЛА === if conflict_words or conflict_lemmas or conflict_parts: output += "⚔️ КОНФЛИКТНЫЕ ПРАВИЛА:\n" output += "(работают только в режиме /stopconflict время)\n\n" if conflict_words: output += f"📝 Конфликтные слова ({len(conflict_words)}):\n" words_str = ', '.join([f"{w}" for w in sorted(conflict_words)[:15]]) if len(conflict_words) > 15: words_str += f" ... (+{len(conflict_words) - 15} ещё)" output += f"{words_str}\n\n" if conflict_lemmas: output += f"🔤 Конфликтные леммы ({len(conflict_lemmas)}):\n" lemmas_str = ', '.join([f"{w}" for w in sorted(conflict_lemmas)[:15]]) if len(conflict_lemmas) > 15: lemmas_str += f" ... (+{len(conflict_lemmas) - 15} ещё)" output += f"{lemmas_str}\n\n" if conflict_parts: output += f"🧩 Конфликтные части ({len(conflict_parts)}):\n" parts_str = ', '.join([f"{w}" for w in sorted(conflict_parts)[:15]]) if len(conflict_parts) > 15: parts_str += f" ... (+{len(conflict_parts) - 15} ещё)" output += f"{parts_str}\n\n" # === ИСКЛЮЧЕНИЯ (WHITELIST) === if exceptions: output += f"✅ ИСКЛЮЧЕНИЯ ({len(exceptions)}):\n" exc_str = ', '.join([f"{w}" for w in sorted(exceptions)[:15]]) if len(exceptions) > 15: exc_str += f" ... (+{len(exceptions) - 15} ещё)" output += f"{exc_str}\n\n" # === АКТИВНЫЕ РЕЖИМЫ === active_modes = [] if await manager.is_silence_active(): active_modes.append("🔇 Режим тишины") if await manager.is_conflict_active(): active_modes.append("⚔️ Режим антиконфликта") if active_modes: output += "🔴 АКТИВНЫЕ РЕЖИМЫ:\n" for mode in active_modes: output += f"{mode}\n" output += "\n" # === ПУСТОЙ СПИСОК === if total_count == 0: output = ( "📋 СПИСОК ПРАВИЛ МОДЕРАЦИИ\n\n" "⚠️ Правила модерации не настроены\n\n" "Используйте команды добавления:\n" "• /addword — добавить подстроку\n" "• /addlemma — добавить лемму\n" "• /addpart — добавить часть\n\n" "📖 Подробнее: /start" ) # Ограничение длины (Telegram limit 4096) if len(output) > 4000: output = output[:3950] + "\n\n... список обрезан, слишком много правил" return output @router.callback_query(F.data.startswith("listwords:refresh")) @router.message(Command(*COMMANDS[CMD], prefix=settings.PREFIX, ignore_case=True)) @log_action(action_name="LISTWORDS_COMMAND") async def listwords_cmd(update: Message | CallbackQuery) -> None: """ Обработчик команды /listwords. Отображает список всех правил модерации с разбивкой по категориям. Доступно только администраторам. Args: update: Message или CallbackQuery """ # Определяем тип update if isinstance(update, CallbackQuery): message = update.message is_callback = True # Извлекаем номер страницы из callback_data try: page = int(update.data.split(":")[-1]) except: page = 0 else: message = update is_callback = False page = 0 # Формируем список try: text = await format_banwords_list() keyboard = get_refresh_kb(page) if is_callback: try: await message.edit_text( text=text, parse_mode="HTML", reply_markup=keyboard ) await update.answer("✅ Список обновлён") except TelegramBadRequest as e: if 'message is not modified' in str(e).lower(): await update.answer('✅ Список уже актуален') return raise # Другие ошибки пробрасываем else: await message.answer( text=text, parse_mode="HTML", reply_markup=keyboard ) except Exception as e: logger.error(f"Ошибка отправки списка банвордов: {e}", log_type="LISTWORDS") error_text = "❌ Ошибка загрузки списка\n\nПопробуйте позже" if is_callback: await update.answer(f"❌ Ошибка загрузки: {e}", show_alert=True) else: await message.answer(error_text, parse_mode="HTML")