diff --git a/.gigaide/gigaide.properties b/.gigaide/gigaide.properties index e438974..da0696a 100644 --- a/.gigaide/gigaide.properties +++ b/.gigaide/gigaide.properties @@ -1,5 +1,5 @@ -## changed at Sun Oct 12 12:00:13 MSK 2025 -#Sun Oct 12 12:00:13 MSK 2025 +## changed at Sun Oct 12 15:29:33 MSK 2025 +#Sun Oct 12 15:29:33 MSK 2025 com.gigaide.elements.ext.marker.solution.BeanMarkedPsi.shouldMark=true com.gigaide.elements.ext.marker.solution.ConfigMarkedPsi.shouldMark=true com.gigaide.elements.ext.marker.solution.DataMarkedPsi.shouldMark=true diff --git a/README.md b/README.md index 9463717..4ff41e7 100644 --- a/README.md +++ b/README.md @@ -40,4 +40,169 @@ python src/main.py - Проверит наличие обновлений в git-репозитории - Загрузит изменения (`git pull`) - Перезапустит службу бота (`systemctl restart LGBot.service`) -- Покажет статус работы бота \ No newline at end of file +- Покажет статус работы бота + +--- + +## Система автоматического мьюта + +Бот автоматически отслеживает использование нецензурной лексики и применяет прогрессирующие наказания. + +### Как работает + +1. **Обнаружение нарушений:** + - Каждое текстовое сообщение проверяется на наличие бранных слов + - Проверяются только группы и супергруппы (не личные сообщения) + - Команды (начинающиеся с `/`) не проверяются + +2. **При обнаружении мата:** + - Сообщение удаляется автоматически + - Нарушение фиксируется в базе данных + - Пользователь получает мут соответствующего уровня + - Уведомление отправляется в чат и админ-чат + +3. **Прогрессирующие наказания:** + +| № нарушения | Длительность мута | № нарушения | Длительность мута | +|-------------|-------------------|-------------|-------------------| +| 1 | 5 минут | 9 | 1 день | +| 2 | 15 минут | 10 | 2 дня | +| 3 | 30 минут | 11 | 3 дня | +| 4 | 1 час | 12 | 5 дней | +| 5 | 2 часа | 13 | 7 дней | +| 8 | 12 часов | 16+ | **НАВСЕГДА** | + +4. **Накопительный эффект:** + - Нарушения учитываются за последние **30 дней** + - Старые нарушения автоматически удаляются из базы + - Администраторы **освобождены** от автоматических мутов + +### Управление списком бранных слов + +⚠️ **Все команды доступны только администраторам чата** + +#### Основные команды + +```bash +# Показать справку +/badwords help + +# Показать список бранных слов (первые 50) +/badwords list + +# Статистика +/badwords count + +# Добавить слово в список +/badwords add <слово> + +# Удалить слово из списка +/badwords remove <слово> +``` + +#### Исключения + +Исключения — слова, содержащие бранные корни, но не являющиеся матом (например: "республика", "документ"): + +```bash +# Показать исключения +/badwords exceptions + +# Добавить исключение +/badwords add_exception <слово> + +# Удалить исключение +/badwords remove_exception <слово> +``` + +#### Служебные команды + +```bash +# Перезагрузить списки из файла +/badwords reload +``` + +### Рекомендации + +1. **Используйте корни слов**, а не полные формы: + - ✅ Правильно: `ебал` (поймает все формы) + - ❌ Неправильно: `ебала` (пропустит другие формы) + +2. **Избегайте коротких корней** (могут давать ложные срабатывания) + +3. **Тестируйте после добавления** нового слова + +### Хранение данных + +Все списки хранятся в файле `src/data/bad_words.json`: + +```json +{ + "bad_words": ["слово1", "слово2", ...], + "exceptions": ["исключение1", "исключение2", ...] +} +``` + +Изменения через команды применяются **немедленно**, перезапуск бота не требуется. + +### Логирование + +Все действия записываются в: +- **bot.log** - файл логов +- **Админ-чат** - уведомления о мутах + +Примеры логов: +``` +[INFO] Пользователь 123456789 получил автоматический мут на 5 минут за нецензурную лексику (нарушение #1) +[INFO] Нарушение пользователя 123456789 зафиксировано в чате -100123456789 +[INFO] Администратор 987654321 добавил бранное слово: спам +``` + +### База данных + +Таблица `violations` хранит все нарушения: +- `id` - уникальный идентификатор +- `user_id` - ID пользователя +- `chat_id` - ID чата +- `violation_date` - время нарушения (unix timestamp) +- `violation_type` - тип нарушения ('bad_language') + +### Настройка системы + +#### Изменение уровней мутов + +Откройте `src/modules/auto_mute.py` и измените массив `MUTE_LEVELS`: + +```python +MUTE_LEVELS = [ + 300, # 1. 5 минут + 900, # 2. 15 минут + # ... добавьте или измените уровни + None, # Перманентный мут +] +``` + +#### Изменение периода накопления + +Измените `VIOLATIONS_PERIOD` в `src/modules/auto_mute.py`: + +```python +VIOLATIONS_PERIOD = 2592000 # 30 дней в секундах +``` + +### Требования + +- Python 3.7+ +- pyTelegramBotAPI (telebot) +- SQLite3 +- Права бота в чате: + - Удаление сообщений + - Ограничение пользователей + +### Устранение проблем + +При возникновении проблем проверьте: +1. Логи бота в файле `bot.log` +2. Права бота в чате (удаление сообщений, ограничение пользователей) +3. Корректность настроек в `.env` +4. Наличие файла `src/data/bad_words.json` \ No newline at end of file diff --git a/src/bad_words.py b/src/bad_words.py new file mode 100644 index 0000000..6c2f739 --- /dev/null +++ b/src/bad_words.py @@ -0,0 +1,153 @@ +# Система управления бранными словами +# Список слов хранится в JSON файле для возможности управления через команды + +import json +import os +import logging + +logger = logging.getLogger(__name__) + +# Путь к файлу с бранными словами +BAD_WORDS_FILE = os.path.join(os.path.dirname(__file__), 'data', 'bad_words.json') + +# Кэш для загруженных слов +_bad_words_cache = None +_exceptions_cache = None + +def load_bad_words(): + """ + Загружает список бранных слов из JSON файла. + + Returns: + tuple: (список бранных слов, список исключений) + """ + global _bad_words_cache, _exceptions_cache + + try: + with open(BAD_WORDS_FILE, 'r', encoding='utf-8') as f: + data = json.load(f) + _bad_words_cache = data.get('bad_words', []) + _exceptions_cache = data.get('exceptions', []) + logger.info(f"Загружено {len(_bad_words_cache)} бранных слов и {len(_exceptions_cache)} исключений") + return _bad_words_cache, _exceptions_cache + except FileNotFoundError: + logger.error(f"Файл {BAD_WORDS_FILE} не найден") + return [], [] + except json.JSONDecodeError as e: + logger.error(f"Ошибка чтения JSON: {e}") + return [], [] + +def save_bad_words(bad_words: list, exceptions: list): + """ + Сохраняет список бранных слов в JSON файл. + + Args: + bad_words: Список бранных слов + exceptions: Список исключений + + Returns: + bool: True если успешно, иначе False + """ + global _bad_words_cache, _exceptions_cache + + try: + # Создаем директорию, если её нет + os.makedirs(os.path.dirname(BAD_WORDS_FILE), exist_ok=True) + + data = { + 'bad_words': sorted(list(set(bad_words))), # Убираем дубликаты и сортируем + 'exceptions': sorted(list(set(exceptions))) + } + + with open(BAD_WORDS_FILE, 'w', encoding='utf-8') as f: + json.dump(data, f, ensure_ascii=False, indent=2) + + # Обновляем кэш + _bad_words_cache = data['bad_words'] + _exceptions_cache = data['exceptions'] + + logger.info(f"Сохранено {len(_bad_words_cache)} бранных слов и {len(_exceptions_cache)} исключений") + return True + except Exception as e: + logger.error(f"Ошибка сохранения: {e}") + return False + +def get_bad_words(): + """Возвращает список бранных слов (с кэшированием)""" + global _bad_words_cache + if _bad_words_cache is None: + load_bad_words() + return _bad_words_cache or [] + +def get_exceptions(): + """Возвращает список исключений (с кэшированием)""" + global _exceptions_cache + if _exceptions_cache is None: + load_bad_words() + return _exceptions_cache or [] + +def reload_words(): + """Перезагружает списки из файла (сбрасывает кэш)""" + global _bad_words_cache, _exceptions_cache + _bad_words_cache = None + _exceptions_cache = None + return load_bad_words() + +# Загружаем слова при импорте модуля +BAD_WORDS, EXCEPTIONS = load_bad_words() + +def contains_bad_word(text: str) -> bool: + """ + Проверяет, содержит ли текст бранные слова. + + Args: + text: Текст для проверки + + Returns: + True, если найдено бранное слово, иначе False + """ + if not text: + return False + + # Приводим к нижнему регистру для проверки + text_lower = text.lower() + + # Проверяем исключения + for exception in EXCEPTIONS: + if exception in text_lower: + text_lower = text_lower.replace(exception, '') + + # Проверяем бранные слова + for bad_word in BAD_WORDS: + if bad_word in text_lower: + return True + + return False + +def get_bad_words_from_text(text: str) -> list: + """ + Возвращает список найденных бранных слов в тексте. + + Args: + text: Текст для проверки + + Returns: + Список найденных бранных слов + """ + if not text: + return [] + + text_lower = text.lower() + found_words = [] + + # Проверяем исключения + for exception in EXCEPTIONS: + if exception in text_lower: + text_lower = text_lower.replace(exception, '') + + # Ищем бранные слова + for bad_word in BAD_WORDS: + if bad_word in text_lower: + found_words.append(bad_word) + + return found_words \ No newline at end of file diff --git a/src/config.py b/src/config.py index 37ae7e3..5ec5140 100644 --- a/src/config.py +++ b/src/config.py @@ -96,6 +96,33 @@ COMMAND_MESSAGES = { 'banned': '✅ Пользователь успешно забанен.', 'unbanned': '✅ Пользователь успешно разбанен.', 'error': '⚠️ Ошибка: {e}', - 'general_error': '⚠️ Произошла непредвиденная ошибка.' + 'general_error': '⚠️ Произошла непредвиденная ошибка.', + 'auto_mute_warning': ( + '⚠️ Пользователь {user_name} получил мут на {duration} ' + 'за использование нецензурной лексики.\n\n' + '📊 Нарушение #{count}\n' + '💡 При повторных нарушениях время мута будет увеличиваться.' + ), + 'auto_mute_permanent': ( + '⛔️ Пользователь {user_name} получил перманентный мут ' + 'за злостное нарушение правил чата (использование нецензурной лексики).\n\n' + '📊 Количество нарушений: {count}\n' + '🔒 Режим: только чтение (навсегда)' + ), + 'badwords_help': ( + "🔧 Управление списком бранных слов\n\n" + "Основные команды:\n" + "• /badwords list - Показать список слов\n" + "• /badwords count - Статистика\n" + "• /badwords add [слово] - Добавить слово\n" + "• /badwords remove [слово] - Удалить слово\n\n" + "Исключения:\n" + "• /badwords exceptions - Список исключений\n" + "• /badwords add_exception [слово] - Добавить\n" + "• /badwords remove_exception [слово] - Удалить\n\n" + "Прочее:\n" + "• /badwords reload - Перезагрузить из файла\n\n" + "💡 Все изменения применяются немедленно" + ) } \ No newline at end of file diff --git a/src/data/bad_words.json b/src/data/bad_words.json new file mode 100644 index 0000000..1071886 --- /dev/null +++ b/src/data/bad_words.json @@ -0,0 +1,47 @@ +{ + "bad_words": [ + "хуй", "хуе", "хуи", "хую", "хуя", "хер", + "пизд", "пизж", "пезд", + "ебал", "ебан", "ебат", "ебу", "ебош", "ебля", "ебет", + "бля", "блядь", "блять", + "сука", "суки", "сучк", "сучар", + "мудак", "мудил", "муди", + "гандон", + "даун", + "дебил", + "долбоеб", "долбаеб", + "уебан", "уебок", + "хуесос", + "пидор", "пидар", "педик", "педр", + "гей", "гомик", "гомос", + "шлюх", "шалав", + "еблан", + "говн", + "срать", "сраль", "серун", + "дрочи", "дроч", + "жоп", "жёп", + "залуп", + "мразь", "мраз", + "козел", "козл", + "урод", "урода", + "ублюдо", "ублюд", + "тварь", "твар", + "падла", + "сволочь", "сволоч", + "гнида", "гнид", + "выблядо", + "хуета", "хуйн", + "охуе", "охуи", "охуя", + "нахуй", "нахер", + "похуй", "похер", + "захуя", + "ахуе", + "впизду", + "попизд" + ], + "exceptions": [ + "республика", + "документ", + "документы" + ] +} \ No newline at end of file diff --git a/src/database.py b/src/database.py index e10ae5e..9cd2169 100644 --- a/src/database.py +++ b/src/database.py @@ -13,7 +13,7 @@ class Database: # Инициализация класса self.db_name = db_name self._init_db() - # Инициализирует базу данных и создает таблицу, если она не существует + # Инициализирует базу данных и создает таблицы, если они не существуют def _init_db(self): with self._get_connection() as connect: cursor = connect.cursor() @@ -24,6 +24,16 @@ class Database: # Инициализация класса tag TEXT ) ''') + cursor.execute(''' + CREATE TABLE IF NOT EXISTS violations ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + chat_id INTEGER NOT NULL, + violation_date INTEGER NOT NULL, + violation_type TEXT NOT NULL, + FOREIGN KEY (user_id) REFERENCES users (id) + ) + ''') connect.commit() # Возвращает соединение с базой данных @@ -70,15 +80,68 @@ class Database: # Инициализация класса def get_user_by_username(self, username: str) -> Optional[Tuple]: if not username: return None - + with self._get_connection() as connect: cursor = connect.cursor() cursor.execute(''' - SELECT id, nickname, tag - FROM users + SELECT id, nickname, tag + FROM users WHERE LOWER(tag) = LOWER(?) ''', (username,)) return cursor.fetchone() + # Добавляет нарушение в базу данных + def add_violation(self, user_id: int, chat_id: int, violation_type: str = 'bad_language'): + import time + with self._get_connection() as connect: + cursor = connect.cursor() + cursor.execute(''' + INSERT INTO violations (user_id, chat_id, violation_date, violation_type) + VALUES (?, ?, ?, ?) + ''', (user_id, chat_id, int(time.time()), violation_type)) + connect.commit() + logger.info(f"Нарушение пользователя {user_id} зафиксировано в чате {chat_id}") + + # Получает количество нарушений за период (по умолчанию - за последний месяц) + def get_violations_count(self, user_id: int, chat_id: int, period_seconds: int = 2592000) -> int: + import time + with self._get_connection() as connect: + cursor = connect.cursor() + cutoff_time = int(time.time()) - period_seconds + cursor.execute(''' + SELECT COUNT(*) + FROM violations + WHERE user_id = ? AND chat_id = ? AND violation_date > ? + ''', (user_id, chat_id, cutoff_time)) + result = cursor.fetchone() + return result[0] if result else 0 + + # Получает все нарушения пользователя + def get_user_violations(self, user_id: int, chat_id: int): + with self._get_connection() as connect: + cursor = connect.cursor() + cursor.execute(''' + SELECT id, violation_date, violation_type + FROM violations + WHERE user_id = ? AND chat_id = ? + ORDER BY violation_date DESC + ''', (user_id, chat_id)) + return cursor.fetchall() + + # Очищает старые нарушения (старше указанного периода) + def clean_old_violations(self, period_seconds: int = 2592000): + import time + with self._get_connection() as connect: + cursor = connect.cursor() + cutoff_time = int(time.time()) - period_seconds + cursor.execute(''' + DELETE FROM violations + WHERE violation_date < ? + ''', (cutoff_time,)) + deleted_count = cursor.rowcount + connect.commit() + logger.info(f"Удалено старых нарушений: {deleted_count}") + return deleted_count + # Создаем экземпляр базы данных для импорта в других модулях db = Database() \ No newline at end of file diff --git a/src/main.py b/src/main.py index d81471e..7491345 100644 --- a/src/main.py +++ b/src/main.py @@ -121,6 +121,7 @@ async def setup_bot_commands(): BotCommand("unban", "Разбанить пользователя"), BotCommand("mute", "Замутить пользователя"), BotCommand("unmute", "Размутить пользователя"), + BotCommand("badwords", "Управление списком бранных слов"), BotCommand("botdata", "Получить данные бота (только админы)"), ] diff --git a/src/modules/auto_mute.py b/src/modules/auto_mute.py new file mode 100644 index 0000000..c7d49f4 --- /dev/null +++ b/src/modules/auto_mute.py @@ -0,0 +1,199 @@ +from telebot.async_telebot import AsyncTeleBot +from telebot.types import Message, ChatPermissions +import asyncio +import logging +import time + +from database import db +from bad_words import contains_bad_word, get_bad_words_from_text +from action_reporter import action_reporter +from utils import delete_messages, format_mute_time + +# Получаем логгер для текущего модуля +logger = logging.getLogger(__name__) + +# Система прогрессирующих мутов (в секундах) +# Более плавная прогрессия для накопительного эффекта +MUTE_LEVELS = [ + 300, # 1. 5 минут (первое нарушение - символический мут) + 900, # 2. 15 минут + 1800, # 3. 30 минут + 3600, # 4. 1 час + 7200, # 5. 2 часа + 14400, # 6. 4 часа + 28800, # 7. 8 часов + 43200, # 8. 12 часов + 86400, # 9. 1 день + 172800, # 10. 2 дня + 259200, # 11. 3 дня + 432000, # 12. 5 дней + 604800, # 13. 7 дней + None, # 16. Перманентный мут (режим только чтения навсегда) +] + +# Период для подсчета нарушений (30 дней в секундах) +VIOLATIONS_PERIOD = 2592000 + +def get_mute_duration(violations_count: int) -> int: + """ + Определяет длительность мута на основе количества нарушений. + + Args: + violations_count: Количество нарушений пользователя + + Returns: + Длительность мута в секундах (или None для перманентного мута) + """ + if violations_count < 1: + return MUTE_LEVELS[0] + + # Индекс уровня мута (количество нарушений - 1, т.к. начинаем с 0) + level_index = violations_count - 1 + + # Если превысили количество уровней, возвращаем перманентный мут + if level_index >= len(MUTE_LEVELS): + return None + + return MUTE_LEVELS[level_index] + +async def apply_mute(bot: AsyncTeleBot, message: Message, user_id: int, duration: int, violations_count: int): + """ + Применяет мут к пользователю. + + Args: + bot: Экземпляр бота + message: Сообщение, которое вызвало мут + user_id: ID пользователя + duration: Длительность мута в секундах (None для перманентного) + violations_count: Количество нарушений + """ + try: + # Устанавливаем ограничения (только чтение) + 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, + ) + + # Вычисляем время окончания мута + until_date = None if duration is None else int(time.time()) + duration + + # Выполняем мут + await bot.restrict_chat_member( + chat_id=message.chat.id, + user_id=user_id, + permissions=permissions, + until_date=until_date + ) + + # Удаляем сообщение с матом + try: + await bot.delete_message(chat_id=message.chat.id, message_id=message.message_id) + except Exception as e: + logger.warning(f"Не удалось удалить сообщение: {e}") + + # Формируем сообщение о муте + if duration is None: + time_display = "навсегда" + warning_msg = ( + f"⛔️ Пользователь {message.from_user.first_name} получил перманентный мут " + f"за злостное нарушение правил чата (использование нецензурной лексики).\n\n" + f"📊 Количество нарушений: {violations_count}\n" + f"🔒 Режим: только чтение (навсегда)" + ) + else: + time_display = format_mute_time(duration) + warning_msg = ( + f"⚠️ Пользователь {message.from_user.first_name} получил мут на {time_display} " + f"за использование нецензурной лексики.\n\n" + f"📊 Нарушение #{violations_count}\n" + f"💡 При повторных нарушениях время мута будет увеличиваться." + ) + + # Отправляем сообщение в чат + await bot.send_message( + chat_id=message.chat.id, + text=warning_msg, + message_thread_id=message.message_thread_id, + ) + + # Отправляем сообщение-лог в админ-чат + await action_reporter.log_action( + action="АВТОМУТ", + user_id=user_id, + admin_id=None, # Автоматическое действие + reason=f"Использование нецензурной лексики (нарушение #{violations_count})", + duration=time_display, + ) + + # Записываем действие в логи + logger.info( + f"Пользователь {user_id} получил автоматический мут на {time_display} " + f"за нецензурную лексику (нарушение #{violations_count})" + ) + + except Exception as e: + logger.error(f"Ошибка при применении мута: {e}") + +async def check_message_for_profanity(bot: AsyncTeleBot, message: Message): + """ + Проверяет сообщение на наличие бранных слов и применяет мут при необходимости. + + Args: + bot: Экземпляр бота + message: Сообщение для проверки + """ + # Проверяем только текстовые сообщения + if not message.text: + return + + # Не проверяем команды + if message.text.startswith('/'): + return + + # Проверяем, содержит ли сообщение бранные слова + if not contains_bad_word(message.text): + return + + # Получаем ID пользователя и чата + user_id = message.from_user.id + chat_id = message.chat.id + + # Проверяем, является ли отправитель администратором + try: + chat_member = await bot.get_chat_member(chat_id, user_id) + if chat_member.status in ['administrator', 'creator']: + logger.info(f"Администратор {user_id} использовал нецензурную лексику, мут не применен") + return + except Exception as e: + logger.error(f"Ошибка проверки статуса пользователя: {e}") + return + + # Добавляем нарушение в базу данных + db.add_violation(user_id, chat_id, violation_type='bad_language') + + # Получаем количество нарушений за последний месяц + violations_count = db.get_violations_count(user_id, chat_id, VIOLATIONS_PERIOD) + + # Определяем длительность мута + mute_duration = get_mute_duration(violations_count) + + # Применяем мут + await apply_mute(bot, message, user_id, mute_duration, violations_count) + +def register_handlers(bot: AsyncTeleBot): + """ + Регистрирует обработчики для автоматического мута. + """ + + # Обработчик всех текстовых сообщений + @bot.message_handler(func=lambda message: message.text and message.chat.type in ['group', 'supergroup']) + async def handle_text_message(message: Message): + await check_message_for_profanity(bot, message) + + logger.info("Модуль автоматического мута успешно загружен") \ No newline at end of file diff --git a/src/modules/badwords_manager.py b/src/modules/badwords_manager.py new file mode 100644 index 0000000..4c11e74 --- /dev/null +++ b/src/modules/badwords_manager.py @@ -0,0 +1,231 @@ +from telebot.async_telebot import AsyncTeleBot +from telebot.types import Message +import logging + +from bad_words import ( + get_bad_words, + get_exceptions, + save_bad_words, + reload_words +) +from utils import check_admin_status, delete_messages +from config import COMMAND_MESSAGES + +logger = logging.getLogger(__name__) + +def register_handlers(bot: AsyncTeleBot): + """Регистрирует обработчики команд управления бранными словами""" + + @bot.message_handler(commands=['badwords']) + async def badwords_command(message: Message): + """Главная команда управления списком бранных слов""" + + # Проверяем права администратора + if await check_admin_status(bot, message) == 1: + return + + parts = message.text.split(maxsplit=2) + + # /badwords без параметров - показываем help + if len(parts) == 1: + await show_help(bot, message) + return + + subcommand = parts[1].lower() + + # Обработка подкоманд + if subcommand == 'help': + await show_help(bot, message) + elif subcommand == 'list': + await list_bad_words(bot, message) + elif subcommand == 'count': + await count_words(bot, message) + elif subcommand == 'add': + if len(parts) < 3: + await send_temp_message(bot, message, "❌ Укажите слово для добавления: /badwords add <слово>") + else: + await add_bad_word(bot, message, parts[2]) + elif subcommand == 'remove': + if len(parts) < 3: + await send_temp_message(bot, message, "❌ Укажите слово для удаления: /badwords remove <слово>") + else: + await remove_bad_word(bot, message, parts[2]) + elif subcommand == 'exceptions': + await list_exceptions(bot, message) + elif subcommand == 'add_exception': + if len(parts) < 3: + await send_temp_message(bot, message, "❌ Укажите слово для добавления в исключения: /badwords add_exception <слово>") + else: + await add_exception(bot, message, parts[2]) + elif subcommand == 'remove_exception': + if len(parts) < 3: + await send_temp_message(bot, message, "❌ Укажите слово для удаления из исключений: /badwords remove_exception <слово>") + else: + await remove_exception(bot, message, parts[2]) + elif subcommand == 'reload': + await reload_wordlist(bot, message) + else: + await send_temp_message(bot, message, f"❌ Неизвестная команда: {subcommand}\n\nИспользуйте /badwords help") + +async def show_help(bot: AsyncTeleBot, message: Message): + """Показывает справку по командам управления бранными словами""" + help_text = COMMAND_MESSAGES['badwords_help'] + await bot.send_message( + chat_id=message.chat.id, + text=help_text, + message_thread_id=message.message_thread_id, + ) + await delete_messages(bot, message, time_sleep=60, number_message=2) + +async def list_bad_words(bot: AsyncTeleBot, message: Message): + """Показывает список бранных слов (первые 50)""" + words = get_bad_words() + + if not words: + text = "📝 Список бранных слов пуст." + else: + # Показываем только первые 50 слов + display_words = words[:50] + text = f"📝 Бранные слова (всего: {len(words)})\n\n" + text += ", ".join([f"{word}" for word in display_words]) + + if len(words) > 50: + text += f"\n\n...и ещё {len(words) - 50} слов" + + await bot.send_message( + chat_id=message.chat.id, + text=text, + message_thread_id=message.message_thread_id, + ) + await delete_messages(bot, message, time_sleep=30, number_message=2) + +async def count_words(bot: AsyncTeleBot, message: Message): + """Показывает статистику по спискам""" + words = get_bad_words() + exceptions = get_exceptions() + + text = ( + f"📊 Статистика списков\n\n" + f"🚫 Бранных слов: {len(words)}\n" + f"✅ Исключений: {len(exceptions)}" + ) + + await bot.send_message( + chat_id=message.chat.id, + text=text, + message_thread_id=message.message_thread_id, + ) + await delete_messages(bot, message, time_sleep=15, number_message=2) + +async def add_bad_word(bot: AsyncTeleBot, message: Message, word: str): + """Добавляет слово в список бранных""" + word = word.lower().strip() + + words = get_bad_words() + exceptions = get_exceptions() + + if word in words: + await send_temp_message(bot, message, f"⚠️ Слово '{word}' уже есть в списке.") + return + + words.append(word) + if save_bad_words(words, exceptions): + reload_words() # Перезагружаем кэш + await send_temp_message(bot, message, f"✅ Слово '{word}' добавлено в список бранных.") + logger.info(f"Администратор {message.from_user.id} добавил бранное слово: {word}") + else: + await send_temp_message(bot, message, "❌ Ошибка при сохранении файла.") + +async def remove_bad_word(bot: AsyncTeleBot, message: Message, word: str): + """Удаляет слово из списка бранных""" + word = word.lower().strip() + + words = get_bad_words() + exceptions = get_exceptions() + + if word not in words: + await send_temp_message(bot, message, f"⚠️ Слово '{word}' не найдено в списке.") + return + + words.remove(word) + if save_bad_words(words, exceptions): + reload_words() # Перезагружаем кэш + await send_temp_message(bot, message, f"✅ Слово '{word}' удалено из списка бранных.") + logger.info(f"Администратор {message.from_user.id} удалил бранное слово: {word}") + else: + await send_temp_message(bot, message, "❌ Ошибка при сохранении файла.") + +async def list_exceptions(bot: AsyncTeleBot, message: Message): + """Показывает список исключений""" + exceptions = get_exceptions() + + if not exceptions: + text = "📝 Список исключений пуст." + else: + text = f"📝 Исключения (всего: {len(exceptions)})\n\n" + text += ", ".join([f"{word}" for word in exceptions]) + + await bot.send_message( + chat_id=message.chat.id, + text=text, + message_thread_id=message.message_thread_id, + ) + await delete_messages(bot, message, time_sleep=30, number_message=2) + +async def add_exception(bot: AsyncTeleBot, message: Message, word: str): + """Добавляет слово в список исключений""" + word = word.lower().strip() + + words = get_bad_words() + exceptions = get_exceptions() + + if word in exceptions: + await send_temp_message(bot, message, f"⚠️ Слово '{word}' уже есть в исключениях.") + return + + exceptions.append(word) + if save_bad_words(words, exceptions): + reload_words() # Перезагружаем кэш + await send_temp_message(bot, message, f"✅ Слово '{word}' добавлено в исключения.") + logger.info(f"Администратор {message.from_user.id} добавил исключение: {word}") + else: + await send_temp_message(bot, message, "❌ Ошибка при сохранении файла.") + +async def remove_exception(bot: AsyncTeleBot, message: Message, word: str): + """Удаляет слово из списка исключений""" + word = word.lower().strip() + + words = get_bad_words() + exceptions = get_exceptions() + + if word not in exceptions: + await send_temp_message(bot, message, f"⚠️ Слово '{word}' не найдено в исключениях.") + return + + exceptions.remove(word) + if save_bad_words(words, exceptions): + reload_words() # Перезагружаем кэш + await send_temp_message(bot, message, f"✅ Слово '{word}' удалено из исключений.") + logger.info(f"Администратор {message.from_user.id} удалил исключение: {word}") + else: + await send_temp_message(bot, message, "❌ Ошибка при сохранении файла.") + +async def reload_wordlist(bot: AsyncTeleBot, message: Message): + """Перезагружает списки слов из файла""" + words, exceptions = reload_words() + text = ( + f"🔄 Списки перезагружены\n\n" + f"🚫 Бранных слов: {len(words)}\n" + f"✅ Исключений: {len(exceptions)}" + ) + await send_temp_message(bot, message, text) + logger.info(f"Администратор {message.from_user.id} перезагрузил списки слов") + +async def send_temp_message(bot: AsyncTeleBot, message: Message, text: str, time_sleep: int = 10): + """Отправляет временное сообщение, которое удаляется через указанное время""" + await bot.send_message( + chat_id=message.chat.id, + text=text, + message_thread_id=message.message_thread_id, + ) + await delete_messages(bot, message, time_sleep=time_sleep, number_message=2) \ No newline at end of file