Files
2026-02-20 03:12:47 +07:00

244 lines
10 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Обработчик команды /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 bot.filters.admin import IsAdmin
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(page: int = 0) -> 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('substring', set()))
permanent_lemmas = list(data.get('lemma', set()))
permanent_parts = list(data.get('part', set()))
temp_words = list(data.get('temp_substring', set()))
temp_lemmas = list(data.get('temp_lemma', set()))
conflict_words = list(data.get('conflict_substring', set()))
conflict_lemmas = list(data.get('conflict_lemma', set()))
exceptions = list(data.get('whitelist', set()))
except Exception as e:
logger.error(f"Ошибка получения данных из БД: {e}", log_type="LISTWORDS")
return "❌ <b>Ошибка загрузки данных из базы</b>"
# === ФОРМИРУЕМ ВЫВОД ===
output = "📋 <b>СПИСОК ПРАВИЛ МОДЕРАЦИИ</b>\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)
)
output += f"📊 <b>Общая статистика:</b>\n"
output += f"├─ Всего правил: <code>{total_count}</code>\n"
output += f"├─ Исключений: <code>{len(exceptions)}</code>\n"
output += f"├─ Удалений за всё время: <code>{stats.get('total_deletions', 0)}</code>\n"
output += f"└─ Администраторов: <code>{stats.get('admins', 0)}</code>\n\n"
# === ПОСТОЯННЫЕ ПРАВИЛА ===
if permanent_words or permanent_lemmas or permanent_parts:
output += "🔴 <b>ПОСТОЯННЫЕ ПРАВИЛА:</b>\n\n"
if permanent_words:
output += f"📝 <b>Подстроки</b> ({len(permanent_words)}):\n"
words_str = ', '.join([f"<code>{w}</code>" for w in sorted(permanent_words)[:20]])
if len(permanent_words) > 20:
words_str += f" ... <i>(+{len(permanent_words) - 20} ещё)</i>"
output += f"{words_str}\n\n"
if permanent_lemmas:
output += f"🔤 <b>Леммы</b> ({len(permanent_lemmas)}):\n"
lemmas_str = ', '.join([f"<code>{w}</code>" for w in sorted(permanent_lemmas)[:20]])
if len(permanent_lemmas) > 20:
lemmas_str += f" ... <i>(+{len(permanent_lemmas) - 20} ещё)</i>"
output += f"{lemmas_str}\n\n"
if permanent_parts:
output += f"🧩 <b>Части</b> ({len(permanent_parts)}):\n"
parts_str = ', '.join([f"<code>{w}</code>" for w in sorted(permanent_parts)[:20]])
if len(permanent_parts) > 20:
parts_str += f" ... <i>(+{len(permanent_parts) - 20} ещё)</i>"
output += f"{parts_str}\n\n"
# === ВРЕМЕННЫЕ ПРАВИЛА ===
if temp_words or temp_lemmas:
output += "⏱ <b>ВРЕМЕННЫЕ ПРАВИЛА:</b>\n\n"
if temp_words:
output += f"📝 <b>Временные подстроки</b> ({len(temp_words)}):\n"
# Для временных слов нужна дополнительная информация о времени истечения
# Пока просто выводим список
words_str = ', '.join([f"<code>{w}</code>" for w in sorted(temp_words)[:15]])
if len(temp_words) > 15:
words_str += f" ... <i>(+{len(temp_words) - 15} ещё)</i>"
output += f"{words_str}\n\n"
if temp_lemmas:
output += f"🔤 <b>Временные леммы</b> ({len(temp_lemmas)}):\n"
lemmas_str = ', '.join([f"<code>{w}</code>" for w in sorted(temp_lemmas)[:15]])
if len(temp_lemmas) > 15:
lemmas_str += f" ... <i>(+{len(temp_lemmas) - 15} ещё)</i>"
output += f"{lemmas_str}\n\n"
# === КОНФЛИКТНЫЕ ПРАВИЛА ===
if conflict_words or conflict_lemmas:
output += "⚔️ <b>КОНФЛИКТНЫЕ ПРАВИЛА:</b>\n"
output += "<i>(работают только в режиме <code>/stopconflict</code> <code>время</code>)</i>\n\n"
if conflict_words:
output += f"📝 <b>Конфликтные слова</b> ({len(conflict_words)}):\n"
words_str = ', '.join([f"<code>{w}</code>" for w in sorted(conflict_words)[:15]])
if len(conflict_words) > 15:
words_str += f" ... <i>(+{len(conflict_words) - 15} ещё)</i>"
output += f"{words_str}\n\n"
if conflict_lemmas:
output += f"🔤 <b>Конфликтные леммы</b> ({len(conflict_lemmas)}):\n"
lemmas_str = ', '.join([f"<code>{w}</code>" for w in sorted(conflict_lemmas)[:15]])
if len(conflict_lemmas) > 15:
lemmas_str += f" ... <i>(+{len(conflict_lemmas) - 15} ещё)</i>"
output += f"{lemmas_str}\n\n"
# === ИСКЛЮЧЕНИЯ (WHITELIST) ===
if exceptions:
output += f"✅ <b>ИСКЛЮЧЕНИЯ</b> ({len(exceptions)}):\n"
exc_str = ', '.join([f"<code>{exceptions}</code>" for w in sorted(exceptions)[:15]])
if len(exceptions) > 15:
exc_str += f" ... <i>(+{len(exceptions) - 15} ещё)</i>"
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 += "🔴 <b>АКТИВНЫЕ РЕЖИМЫ:</b>\n"
for mode in active_modes:
output += f"{mode}\n"
output += "\n"
# === ПУСТОЙ СПИСОК ===
if total_count == 0:
output = (
"📋 <b>СПИСОК ПРАВИЛ МОДЕРАЦИИ</b>\n\n"
"⚠️ <i>Правила модерации не настроены</i>\n\n"
"Используйте команды добавления:\n"
"• /addword — добавить подстроку\n"
"• /addlemma — добавить лемму\n"
"• /addpart — добавить часть\n\n"
"📖 Подробнее: /start"
)
# Ограничение длины (Telegram limit 4096)
if len(output) > 4000:
output = output[:3950] + "\n\n<i>... список обрезан, слишком много правил</i>"
return output
@router.callback_query(F.data.startswith("listwords:refresh"))
@router.message(Command(*COMMANDS[CMD], prefix=settings.PREFIX, ignore_case=True), IsAdmin())
@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(page)
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 = "❌ <b>Ошибка загрузки списка</b>\n\nПопробуйте позже"
if is_callback:
await update.answer(f"❌ Ошибка загрузки: {e}", show_alert=True)
else:
await message.answer(error_text, parse_mode="HTML")