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