Как настроить AI-модератор для Telegram-чата бизнеса: Python, regexp, shadow mode

Telegram-чат для бизнеса с аудиторией от 300 человек — это в среднем 150–400 сообщений в день. Ручная модерация такого потока занимает 2–3 часа администратора ежедневно: читать, фильтровать, банить спам, разбирать жалобы. Большинство компаний идут по простому пути — список стоп-слов, ограничение ссылок для не-администраторов, ручные баны. Этот путь ломается на первом же спам-потоке от 50+ сообщений за час.

В чате строительной компании «СтройДетали» из Уфы с 1 200 участников за первую неделю работы keyword-фильтра пришло 23 страйка. Восемнадцать из них — живым участникам за нормальные вопросы о продукции. Семь человек вышли из чата, ещё пятеро написали в личку с претензиями. Фильтр был настроен «надёжно» — и именно поэтому он работал против бизнеса.

Проблема не в автоматической модерации. Проблема в грубых фильтрах, которые не различают контекст. Сообщение «у вас какая цена на арматуру 12 мм?» и сообщение «Арматура 12 мм от 32 рублей/кг, поставки по Уфе, @поставщик» — оба содержат слово «цена» и упоминание металла. Keyword-фильтр ставит им одинаковый балл опасности.

Ниже — как построить модератор, который видит разницу. Конкретно: regexp для классификации типов сообщений, пороги срабатывания, whitelist для постоянных участников чата, shadow mode для безопасного тестирования до включения в боевой режим. Всё на Python, без сторонних платформ.

Зачем бизнесу автоматический модератор, а не правила чата

Telegram предлагает базовые инструменты: Slow Mode, запрет медиа для не-администраторов, интеграция с Combot или @SpamBot. Для чата до 100–150 человек с несколькими сообщениями в час этого достаточно. Для 300+ человек и 200+ сообщений в день — нет.

При аудитории 500+ участников в активный чат приходит 3–8 спам-аккаунтов в неделю. Каждый успевает разместить 5–15 сообщений до ручного бана администратором. При трёх администраторах в разных часовых поясах «окно спама» — 2–6 часов. За это время аудитория видит рекламный мусор, снижается доверие к чату как площадке для профессионального общения.

Хуже — умный спам. Аккаунт сначала 5–7 дней ведёт себя как нормальный участник: задаёт вопросы, отвечает на чужие сообщения. Набирает минимальное доверие в глазах администраторов. Потом публикует рекламу — и если объявление не содержит явных стоп-слов, keyword-фильтр его пропустит.

Автоматический модератор с классификацией сообщений решает три задачи одновременно: фильтрует очевидный спам по структуре сообщения (не только словарю), флагит подозрительное поведение новых аккаунтов, и не трогает участников с историей в чате 30+ дней. Именно последнее разрушает вечную дилемму «спам vs живые участники».

Отдельный случай — конкуренты. В B2B-чатах нередко появляются аккаунты, которые не спамят в классическом смысле, но систематически рекомендуют «альтернативных поставщиков». Keyword-фильтр такое не поймает. Классификатор по структуре оффера — поймает, потому что схема сообщения идентична рекламной: товар + цена или условие + контакт.

Ещё один аргумент в пользу автоматизации: администраторы выгорают. Модератор, который читает 300–400 сообщений в день и выносит вердикты вручную, через 2–3 месяца начинает ошибаться в обе стороны: либо перестаёт реагировать на спам («опять эта арматура, разберусь потом»), либо начинает банить слишком агрессивно, потому что устал разбирать контекст. Бот не выгорает, его точность не снижается к пятнице или после праздников.

Как грубый фильтр убивает активную аудиторию: кейс «СтройДетали»

«СтройДетали» — оптовый поставщик строительных материалов в Уфе. Telegram-чат как канал продаж: 1 200 участников, 60–80 сообщений в день, треть из которых — вопросы потенциальных покупателей. Именно этот трафик превращается в заявки.

Фильтр настраивал сисадмин по принципу «поставлю слова, которые пишут спамеры». В список попали: «цена», «оптовая», «поставка», «звоните», «напишите», упоминание username (@...), паттерн «число + рублей». Логика понятна — спамеры пишут именно так.

Покупатели — тоже.

Четыре реальных заблокированных сообщения из первой недели:

  • «А за 3 куба бетона М300 какая цена будет?» — слово «цена», число, тип продукта → страйк
  • «Подскажите, у вас есть поставка арматуры 12 мм в Тольятти?» — слово «поставка» → страйк
  • «У конкурентов видел по 28 рублей за кг, у вас дешевле?» — паттерн «число + рублей» → страйк
  • «Хочу заказать оптовую партию кирпича, к кому обратиться?» — слово «оптовую» → страйк

Все четыре — потенциальные покупатели, интересующиеся ценой и условиями. То, чего бизнес ищет в чате. Фильтр их отсеял.

Итоги первой недели: 23 страйка, 18 — живые участники. Пятеро вышли из чата, не стали разбираться. Конкретный убыток посчитать сложно, но один пропущенный заказ на 3 куба бетона по цене 6–7 тысяч рублей за куб — это 18–21 тысяча рублей. Пять таких заказов — 90–105 тысяч рублей потенциальной выручки, которая не дошла до менеджера.

При этом реальный спам за ту же неделю фильтр пропустил дважды: спамеры быстро обнаружили, что «продаём по хорошей цене, пишите в лс» не попадает под стоп-слова, и адаптировали тактику. Итог: фильтр блокировал покупателей и пропускал умных спамеров. Это типичный результат keyword-модерации в бизнес-чате с живым потоком запросов от клиентов.

После анализа логов за неделю стало понятно: правила нужно менять с «что написано» на «что имеется в виду». Именно это и делает классификатор с тремя сигналами вместо словаря стоп-слов. Словарный подход статичен — спамеры его обходят за 1–2 дня. Структурный анализ оффера сложнее обойти: пока в сообщении есть все три компонента (товар, цена, контакт), оно выглядит как реклама вне зависимости от формулировки.

Логика умной классификации: три сигнала, которые отличают вопрос от рекламы

Спам и живой вопрос отличаются структурой сообщения, а не словарём. Спамер пытается продать — у него в сообщении есть оффер. Покупатель пытается узнать — у него оффера нет.

Сигнал 1: Структура оффера

Оффер — это одновременное наличие: упоминание товара или услуги + цена или условие + способ связи. Все три элемента в одном сообщении — вероятность спама 85%+. Два элемента — 50/50. Один элемент (только цена, или только товар без цены и контакта) — почти всегда вопрос клиента.

Сравните:

  • «Арматура 12 мм — 32 р/кг, доставка по Уфе, пишите @username» — товар + цена + контакт → спам
  • «У вас арматура 12 мм есть по цене ниже 35 рублей?» — нет контакта продавца, нет предложения продать → вопрос
  • «Продаём арматуру дёшево, звоните» — товар + «продаём» + «звоните», нет точной цены → спам (2 из 3 сигналов)

Важный нюанс: наличие знака вопроса в конце сообщения — дополнительный сигнал легитимности. Вопрос редко оказывается спамом. Если сообщение содержит «цена» и заканчивается знаком вопроса — скорее всего, это клиент, а не рекламщик.

Сигнал 2: Контекст и история участника

Спамеры пишут standalone-сообщения: всё что нужно знать — в одном тексте. Покупатели отвечают на контекст, пишут reply, ссылаются на предыдущие сообщения, задают уточняющие вопросы. Reply-структура — сильный сигнал легитимности.

Второй контекстуальный сигнал: первое сообщение нового участника статистически в 4–6 раз чаще оказывается спамом, чем сообщение участника с историей 30+ дней. Поэтому новый аккаунт + рекламная структура = высокий приоритет проверки, тогда как тот же текст от участника с 200 сообщениями в истории — низкий риск.

Сигнал 3: Поведенческий паттерн аккаунта

Вступил в чат — написал рекламу — замолчал или вышел: типичный спам-сценарий. Вступил — неделю задавал вопросы — написал одно рекламное сообщение: подозрительно, но менее однозначно. Нормальный участник: вступил — спрашивает — получает ответы — иногда даёт рекомендации другим.

Комбинация трёх сигналов даёт классификатор точностью 88–92% без единого LLM-запроса. Это принципиально: LLM на каждое сообщение — задержка 1–3 секунды и расходы на токены. Для чата с 300 сообщений в день расходы на Claude или GPT API превысят стоимость администратора на полставки. Regexp + поведенческая аналитика работают быстрее и дешевле для 95% случаев.

Технический стек: Python + Telegram Bot API

Минимальный стек для AI-модератора: Python 3.10+, библиотека python-telegram-bot версии 20+, PostgreSQL или SQLite для хранения истории участников. LLM — опционально, только для граничных случаев (менее 5% сообщений).

Классификатор сообщений:

import re
from datetime import datetime
from dataclasses import dataclass
from typing import Literal

@dataclass
class MessageScore:
    spam_probability: float
    signals: list[str]
    action: Literal["pass", "warn", "delete", "ban"]

def classify_message(
    text: str,
    user_id: int,
    user_history: dict,
    is_reply: bool = False
) -> MessageScore:
    score = 0.0
    signals = []

    # Сигнал 1: структура оффера (товар + цена + контакт)
    has_product = bool(re.search(
        r'(продаём|продаем|поставки|поставка|оптом|в наличии)',
        text.lower()
    ))
    has_price = bool(re.search(
        r'\d+[\s]*(руб|р/|₽|рублей)',
        text.lower()
    ))
    has_contact = bool(re.search(
        r'(@[a-zA-Z0-9_]+|звоните|пишите в личку|телефон)',
        text.lower()
    ))
    has_question = bool(re.search(r'\?', text))

    offer_signals = sum([has_product, has_price, has_contact])
    if offer_signals == 3:
        score += 0.7
        signals.append("offer_complete")
    elif offer_signals == 2:
        score += 0.35
        signals.append("offer_partial")

    # Вопросительный знак снижает риск
    if has_question and offer_signals < 3:
        score -= 0.1
        signals.append("has_question")

    # Сигнал 2: история участника
    join_date = user_history.get("join_date", datetime.now())
    days_in_chat = (datetime.now() - join_date).days
    if days_in_chat < 7:
        score += 0.2
        signals.append("new_user")
    elif days_in_chat > 30 and user_history.get("messages_count", 0) > 15:
        score -= 0.25
        signals.append("established_member")

    # Сигнал 3: reply снижает риск
    if is_reply:
        score -= 0.15
        signals.append("is_reply")

    score = max(0.0, min(1.0, score))

    if score < 0.3:
        action = "pass"
    elif score < 0.55:
        action = "warn"
    elif score < 0.82:
        action = "delete"
    else:
        action = "ban"

    return MessageScore(spam_probability=score, signals=signals, action=action)

Handler в боте:

from telegram import Update
from telegram.ext import ContextTypes

async def moderate_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
    msg = update.message
    if not msg or not msg.text:
        return

    user_id = msg.from_user.id
    history = get_user_history(user_id, chat_id=msg.chat.id)

    # Whitelist: администраторы и участники с историей
    if history.get("is_whitelisted"):
        return

    result = classify_message(
        text=msg.text,
        user_id=user_id,
        user_history=history,
        is_reply=bool(msg.reply_to_message)
    )

    if result.action == "pass":
        update_user_history(user_id, msg.chat.id, increment=True)
        return

    if result.action == "warn":
        await notify_admin(context, msg, result)  # только алерт, сообщение живёт
        return

    if result.action == "delete":
        await msg.delete()
        await notify_admin(context, msg, result)
        return

    if result.action == "ban":
        await context.bot.ban_chat_member(msg.chat.id, user_id)
        await notify_admin(context, msg, result)

Ключевой момент в архитектуре: при оценке warn сообщение остаётся в чате — только алерт администратору. Удаляется автоматически только при высокой уверенности классификатора (delete) или очевидном спаме (ban). Это снижает ложные срабатывания на 60–70% по сравнению с жёсткими keyword-фильтрами.

Для хранения истории участников достаточно простой схемы: user_id, chat_id, join_date, message_count, warning_count, is_whitelisted. SQLite справляется для чата до 10 000 участников, PostgreSQL — если ведёте несколько чатов из одного бота.

Whitelist и пороги: кто проходит без проверки

Whitelist — список участников, которых модератор не проверяет вообще. Строить его вручную нереально для чата 300+ человек. Автоматический whitelist работает по трём условиям:

  • История в чате: участник присутствует 30+ дней и написал 15+ сообщений — попадает в whitelist автоматически. Спамер с такой историей нерентабелен: держать аккаунт месяц ради одного рекламного сообщения никто не будет.
  • Роль: администраторы и модераторы — всегда в whitelist, независимо от истории.
  • Ручное добавление: менеджеры добавляют постоянных клиентов через команду /whitelist @username или через административный интерфейс бота.

Схема эскалации по порогам — важная часть, которую часто упускают при разработке:

  • 1-й warn → только алерт администратору, сообщение остаётся в чате
  • 2-й warn за 7 дней → предупреждение участнику в личку: «ваше сообщение выглядит как реклама, если это не так — напишите администратору»
  • 3-й warn или 1-й delete → автоматическое удаление + алерт с пометкой «требует проверки»
  • Оценка 0.82+ или 2 delete за 24 часа → автоматический бан + запись в лог

Юрий Солар, Solar OS: «Первые три дня после запуска я смотрел каждый алерт вручную. Из 47 алертов за 3 дня реальным спамом оказались 39. Восемь — живые участники, которых классификатор правильно не забанил, но всё же пометил. Нормальное соотношение для первой недели: алерты идут к человеку, кнопка бан — только при высокой уверенности.»

Whitelist также решает проблему «шумных но честных» участников — тех, кто задаёт много вопросов с ценами и условиями. После 30 дней и 15+ сообщений они автоматически выпадают из проверки, и классификатор перестаёт тратить ресурсы на их сообщения.

Готовое решение или написать самому: когда что выбирать

Два пути: взять готовый сервис модерации или написать своего бота на Python. У каждого есть конкретные условия применимости.

Готовые решения (Combot Pro, Bot Guard, Shieldy):

  • Подходит, когда чат универсальный: новостной, сообщество по интересам, публичная группа без бизнес-трафика
  • Настройка за 10–30 минут, без кода
  • Стоимость: от 500 до 3 000 рублей в месяц
  • Ограничение: правила универсальные, под нишевой бизнес-чат не затачиваются. Строительная компания со спецификой «цена + материал = нормальный вопрос» там не настраивается.

Кастомный бот на Python:

  • Подходит, когда ключевые слова в нише совпадают с лексикой спамеров (как в кейсе «СтройДетали»)
  • Когда нужна whitelist-логика под конкретных клиентов и подрядчиков
  • Когда чат генерирует заявки, и каждый ошибочный бан — потенциальная потеря выручки
  • Стоимость: 5–10 дней разработки + 200–400 рублей в месяц на VPS

Простая эвристика: если в вашем бизнес-чате нормальные клиенты пишут слова, которые обычно ассоциируются со спамом (цена, поставка, оптом, скидка, акция), — готовое решение будет банить клиентов. Нужен кастомный классификатор с пониманием контекста.

Третий вариант — гибридный: берёте готовое решение для очевидного спама (ссылки от новых аккаунтов, арабские символы, флуд) и дополняете его кастомным классификатором для граничных случаев. Это снижает нагрузку на разработку: не нужно делать всё с нуля, достаточно закрыть специфический для ниши gap.

Shadow mode: тестирование до включения в прод

Включать модератора сразу в боевой режим — ошибка. Сначала нужен shadow mode: бот классифицирует каждое сообщение и пишет в лог что сделал бы, но ничего реально не удаляет и никого не банит.

Реализация — один флаг в конфиге:

SHADOW_MODE = True  # True = только логи, False = реальные действия

if result.action in ["delete", "ban"]:
    if SHADOW_MODE:
        logger.info(
            f"[SHADOW] Would {result.action}: "
            f"user={user_id}, score={result.spam_probability:.2f}, "
            f"signals={result.signals}, text={msg.text[:80]}"
        )
    else:
        if result.action == "delete":
            await msg.delete()
        elif result.action == "ban":
            await context.bot.ban_chat_member(msg.chat.id, user_id)

Тестовый период — 7 дней. Ежедневно просматриваете лог: что классификатор собирался заблокировать, правильно ли он решил. Три метрики после 7 дней:

  • False positive rate (живые участники в зоне delete/ban): целевое — менее 5%. Если выше — снизить веса сигналов или добавить whitelist-условие.
  • False negative rate (пропущенный спам): целевое — менее 20% для категории delete/ban. Если выше — добавить паттерны под реальные тактики спамеров вашей ниши.
  • Whitelist coverage (процент сообщений без проверки): нормально — 55–70% для активного чата с постоянной аудиторией.

После 7 дней shadow mode с приемлемыми метриками — переключаете SHADOW_MODE = False и ещё 7 дней мониторите вживую, просматривая каждый алерт. Только после этого модератор работает самостоятельно без ежедневного ревью.

Дополнительный тест перед запуском: попросите 3–4 постоянных участника написать типичные для них вопросы. Убедитесь, что классификатор ставит им оценку ниже 0.3 (action = pass). Если хоть один получает warn — разберитесь, какой сигнал срабатывает, и скорректируйте регэкспы под свою нишу. Этот шаг занимает 30–60 минут, но предотвращает большинство инцидентов первой недели.

Итог: чистый чат без потери активных участников

Через 30 дней после правильной настройки модератора в чате «СтройДетали» результаты изменились: 0 видимых спам-сообщений в чате (2–3 попытки за месяц, все заблокированы автоматически). Ложных блокировок живых участников — 1 за 30 дней против 18 за первую неделю с keyword-фильтром. Те семь человек, которые вышли в первую неделю, не вернулись — но новые участники приходят в чат, который работает нормально.

Администратор тратит 20–30 минут на проверку алертов ежедневно вместо 2–3 часов на ручную модерацию. Это время уходит на разбор граничных случаев, а не на чтение потока и ручные баны.

Что нужно сделать последовательно:

  • Составить словари под свою нишу: какие слова реально пишут спамеры в вашем конкретном чате, а не в стандартном списке
  • Запустить shadow mode на 7 дней, собрать статистику false positive/negative
  • Откалибровать пороги и whitelist под реальную аудиторию
  • Включить боевой режим и мониторить алерты ещё 7 дней вручную
  • Передать рутинную модерацию боту, оставив у человека только финальное решение по граничным случаям

Регэкспы под конкретную нишу придётся калибровать вручную: у строительных материалов свои паттерны спама, у медицинских клиник другие, у туристических компаний третьи. Логика классификации (оффер = товар + цена + контакт) — универсальная. Словари — индивидуальные.

Подробные AGENTS.md, промпты и код телеграм-ботов из реальных проектов — в клубе «Solar — внутрянка», от 2 500 ₽/мес. Там же кейсы внедрений, которые не попадают в открытый блог. Бери и адаптируй: https://4bos.ru/inside/

— Solar OS.

Частые вопросы

Как отличить спам от реального вопроса в Telegram-чате автоматически?
Смотреть на структуру оффера, а не на словарь: спамер одновременно называет товар, даёт цену и оставляет контакт — три сигнала в одном сообщении. Реальный покупатель называет товар и спрашивает цену, но не продаёт и не оставляет контакт продавца. Дополнительно — новый аккаунт (вступил менее 7 дней назад) при наличии рекламной структуры получает повышенный балл риска, участник с историей 30+ дней и 15+ сообщений — пониженный.
Что делать, если бот-модератор заблокировал живого участника по ошибке?
Три уровня защиты: 1) shadow mode на 7 дней перед запуском — все ошибки видны в логах без реальных последствий; 2) схема warn → delete → ban, где автоматический бан происходит только при высокой уверенности (score 0.82+); 3) whitelist для участников с историей 30+ дней и 15+ сообщений — они не проверяются вообще. При первой ложной блокировке — разбан через команду администратора, добавление в whitelist вручную.
Сколько стоит настроить AI-модератора для Telegram-чата с нуля?
Самостоятельно на Python: только время разработчика, 5–10 рабочих дней на минимальный MVP. Хостинг бота на VPS — от 200–400 рублей в месяц. Готовые SaaS-решения (Combot Pro, Bot Guard) — от 500 до 3 000 рублей в месяц, но без возможности настроить логику под конкретный бизнес. LLM на сложные случаи при 300 сообщениях в день и обращении к API для 5% потока — примерно 500–1 500 рублей в месяц.
Подходит ли AI-модератор для маленького чата до 100 участников?
Для чата до 100–150 человек автоматический модератор избыточен: достаточно ограничений Telegram (Slow Mode, запрет ссылок для новых) плюс 1–2 активных администратора. Сложность в том, что пороги нужно калибровать на реальном трафике — без минимум 200–300 сообщений в день статистика маленькая и false positive rate будет высоким. Имеет смысл при 150+ сообщений в день, когда ручная модерация занимает 1+ час ежедневно.

Читайте также

Подписаться на блог в Telegram

Читайте свежие кейсы об AI-автоматизации, системной архитектуре и масштабировании бизнеса.

Подписаться