Проект: Вастрик.Швейцар — антиспам для телеграма  Публичный пост
15 июня 2024  1693
ОХУЕННО ⨯2 Супер-любовь
Вастрик.Швейцар - антиспам для телеграма
https://github.com/TiraelSedai/ClubDoorman

Спам заколебал.

Нет, серьёзно. Эта проблема никак не уникальна для Клубных чатов, просто в телеграме очень плохой антиспам.
Если вы сидите хотя бы одном большом чате, то наверняка видите что-то вроде этого каждый божий день

Sounds like F# propaganda but OK
Sounds like F# propaganda but OK

Зачем?

Не каждый чат из Нетворка закрыт специальной ссылочкой. Некоторые чаты изначально были сделаны открытыми, в них может вступить каждый, например Наука и Книги.

И в них постоянно кто-то предлагал закладки, "удалённый заработок", фотки голых девушек и что ещё там модно нынче у спамеров.

Кажется, что проблема тривиальная, нужно просто поставить капчу на вход и дело в кармане.

Капча так капча

Сначала в качестве капчи я сделал требование #intro.

Так сказать, совместить приятное с полезным, - и спамеров отвадить, и узнать сразу о человеке который только что залетел в чат во что он играет (для Игр) или что он читает (для Книг).

Клубней бот просто вежливо просил написать интро, но не выкидывал тех, кто не написал.

К сожалению, идея не выгорела - люди в основном пишут короткие отписки, действительно полезное интро было у единиц. Капчу заменил на простой "ткни в нужную кнопку".

many such cases
many such cases

Если вы давно в Клубе то видели бота "метаадмин" - и тысячу подобных.

Но не тут-то было

Телеграм иногда просто берёт и не присылает эвент "юзер зашёл в чат". Особенно для больших чатов. Особенно для премиум-юзеров. У меня практически шапочка из фольги, но мне кажется что Дуров так мотивирует спамеров покупать премиум, потому что с премиумом спамится гораздо лучше.

Прикручиваем ML

В подкасте Радио-Т Умпутун рекламировал своего бота, затея мне понравилась, но вот спам у них всё равно регулярно проскакивает, да и фич там больше чем мне нужно (и как бот, и как библиотека, и как веб-сервис - и жнец и жрец и на дуде игрец), поэтому, разумеется, есть один выход - написать своё.

В итоге после вечера экспериментов я остановился на фичеризации текста в триграммы букв и мешок слов.

В качестве статистической важности был выбран старый добрый TF-IDF, в качестве оптимизационной целевой функции - простой советский копеечный Stochastic Dual Coordinate Ascent. Пожалуй, единственным недостатком всего этого является то, что библиотека ML.NET не умеет дообучать модели с такой целевой функцией, и каждый раз, когда мы добавляем новый спам, переобучение идёт с нуля. Как показала практика новое сообщение в датасет добавляется один-два раза в неделю, а обучение занимает секунду (две на дохлой VPSке), так что я вряд ли эти минусы хоть сколько-то важны.

Скучная часть (принцип работы)

  • Пользователи Клуба с привязанным ботом автоматически добавляются в доверенные
  • При эвенте "пользователь вошёл в чат" выдаётся простенькая капча
  • Если пользователь уже в доверенных, то все проверки пропускаются
  • Если пользователь в известных списках спамеров - нафиг
  • Если в сообщении❗❗❗ многовато❗❗❗ 💲💲💲эмодзи💲💲💲 - нафиг
  • Если в сообщении есть слова, которые маскируются под русские но имеют английские буквы внутри - нафиг
  • Если в сообщении есть стоп-слова типа "заработок в сети" - нафиг
  • Дальше сообщение очищается от эмодзи, пунктуации, диакритиков и скармливается ML. Если ML считает что спам - нафиг
  • Если пользователь написал несколько нормальных сообщений, он добавляется в доверенные - спамеры крайне редко "втираются в доверие" и обычно выдают всё первым-вторым сообщением
  • Автоматически бот только удаляет сообщения, банятся пользователи только вручную из админки чтобы снизить урон ложнопозитивных срабатываний
  • Если кого-то в чате забанил не бот, в админку приходит уведомление, возможно бот пропустил спам и его стоит добавить в датасет

так выглядит админка
так выглядит админка

Какой технологический стек вы использовали? Почему?

Разумеется, бот написан на C#, а не на питоне. Кто же пишет ML на питоне?

На самом деле для этого есть две простые причины:

  • Я в разы быстрее пишу на шарпе
  • Моя дешёвая виртуалка на 0.5 CPU и 1Gb RAM которая хостит все мои пет-проекты не вынесет питона и тензорфлоу, разве что с большим скрипом. А тут 40Mb RAM и поехали, перетренировка модели занимает полсекунды.

Первые версии были написаны на Go, ну просто потому что могу, но когда надо было прикручивать ML я перешёл туда, где привычнее и есть готовые средства.

Какие планы на будущее?

Никаких. Надеюсь, и этого хватит надолго.

Аватарка

Разумеется, боту нужно лицо и теперь нейронки прекрасно снабжают нас тысячами картинок на любой цвет и вкус, покажу несколько вариантов которые были:

Первая версия
Первая версия

Добряк
Добряк

Со шляпой
Со шляпой

Разумеется, была выбрана та, что похожа на вышибалу в Клубе. Ведите себя прилично или вас быстро выкинут на мороз!

Вместо послесловия

Сидеть в чате без спама это как пить чистую воду и дышать чистым воздухом - не замечаешь, пока у тебя это не отбирают.

Если вы сидите в Книгах или Науке, то, надеюсь, заметили насколько реже стал пролетать спам (за последние пару недель прорвалось 4 сообщения, и два из них это у меня электричество ночью выключали).

Связанные посты
29 комментариев 👇

  Развернуть 1 комментарий

@captaingim, 🌚🤝🌝

  Развернуть 1 комментарий

Мое почтение, очень круто
Его можно использовать не только в клубных чатах?

Да, насчёт уведомлений о входе в группу - можно подписаться на специальный тип апдейтов, которые по умолчанию не приходят, и там все стабильно приходит

  Развернуть 1 комментарий

@benyamin, насчёт не клубных чатов: лучше захостить свой, потому что во-первых, мы в админке офигеем если будет сообщение "взгляните на (ссылка), чат Котики-матросики", без контекста совсем не понять спам это или нет, ну и в целом это лишняя нагрузка на людей из админки (хотя пока что это с десяток раз кнопку в день нажать, но быстро может стать неприятно, у меня на этот чат включены уведомления).

Насчёт уведомлений для телеги - я не вижу в документации апи типа апдейта на который я не подписан, можешь подсказать какой именно?

  Развернуть 1 комментарий

@TiraelSedai, в методе getUpdates есть параметр allowed_updates
Это список из названий свойств, которые могут быть у объекта Update

Тебе нужно добавить в него chat_member

У меня в метаадмине это выглядит так:

ALLOWED_UPDATES = ['chat_member', 'message', 'callback_query']

  Развернуть 1 комментарий
  Развернуть 1 комментарий

@TiraelSedai, вау, тогда оч странно

  Развернуть 1 комментарий

Отличный проект, отличный подход, отличное решение, отличная статья. Я бы тебя нанял, но я сам простой кодер)

Я вот только не понимаю, почему в клубе плюсов много, а на гитхабе ноль звёзд.
Дорогие плюсующие, поставьте пожалуйста господину каждый по две звезды, страна должна знать своих героев.

  Развернуть 1 комментарий

@grbit, ссылка хорошо спрятана. Мне кажется стоит вынести отдельно в конец статьи.

  Развернуть 1 комментарий

@CoHuK, но ведь тип постов "проект" всегда имеют ссылку там где имеют, это шаблон, я не выбирал куда её вставить :)

  Развернуть 1 комментарий

@CoHuK, а, то есть не я один её долго искал XD

https://github.com/TiraelSedai/ClubDoorman

  Развернуть 1 комментарий
🕵️ Юзер скрыл свои комментарии от публичного просмотра...

Я такой спам в телеграмме не вижу. Вот видел набеги ботов с женскими именами которые ничего не пишут

  Развернуть 1 комментарий
🕵️ Юзер скрыл свои комментарии от публичного просмотра...

@Darel, если чат публичный, то есть ищется в поиске телеграма, то боты набегают туда сразу и постоянно. Сначала отсиживаются какое-то время, а потом начинают свои казино и «стабильный заработок на закладках» рекламировать

У нас в Бусочате недавно капча отвалилась и мы заметили только спустя несколько недель, так туда столько ботов набежало, что они в день по 5-10 сообщений постили, мы заебались вычищать, особенно когда они уже внутри

  Развернуть 1 комментарий
TyVik python-программист 18 июня в 10:23

В группу программистов Краснодара я разработал и подключил вот этого бота https://github.com/krddevdays/bot. Суть в том, что надо поставить определённое эмодзи на приветственное сообщение.

Пока полёт нормальный, количество спама заметно сократилось. Хотя люди тупили и не знали что такое "реакция" :)

  Развернуть 1 комментарий

Когда-то у нас был небольшой канал и мы использовали combot. Канал рос и бот хотел денег, сначала 20$ в год, потом 100$, сейчас уже почти 400$, поэтому пришлось написать своего.
Т.к. питон я трогал когда-то, а go еще нет, то написал его на go.

При входе юзера у него забираются все права и показывается кнопка типа я человек, по нажатию права возвращаются.
Если долго не нажимать, то удаляем сообщение и кикаем юзера.
Из фильтраций примерно такой список получился:

  • пересылки от плохих ботов(типа postbot)
  • регулярки и запрещенные слова
  • сообщения от имени канала
  • сообщения с первыми эмодзи
  • api черного списка combot

Но все равно некоторые сообщения последних дней приходится вручную убирать:

  • Нужно починить Бензопила . Кто умеет ?
  • Для мытья посуды нужны люди
  • Нужны доставщики мягких игрушек
  Развернуть 1 комментарий

@alexkurd, опять всё это вращается вокруг идеи что телега честно присылает эвент что юзер пришёл в чат только что, что по моему скромному опыту не так :( если бы это было так, капча бы резала 99% ботов

  Развернуть 1 комментарий

@TiraelSedai, можно проверить различия между webhook и longpolling. По ощущению самостоятельное получение обновлений стабильнее.

  Развернуть 1 комментарий
metya Applied Deep Learning Researcher 18 июня в 18:30

Кайфовый пет!

  Развернуть 1 комментарий
Vlad Zaitsev Architect & Product Owner 22 июня в 23:20

Ооо, поднял у себя, работает!
Только на старте валится с ошибками "не могу найти файлы", их бы по-умолчанию в сборку докера добавить, может? Или хотя бы в инструкцию.

  Развернуть 1 комментарий

@vvzvlad, завтра как раз планирую переделать на то чтобы удобно было хранить /data и добавить пример композа

  Развернуть 1 комментарий

@vvzvlad, сделал нормальную инициализацию данных в докере, попробуй пожалуйста. ну и если будут проблемы, пиши в телегу, так наверное проще и быстрее отловить, чем через пост в клубе :)

  Развернуть 1 комментарий
Vlad Zaitsev Architect & Product Owner 22 июня в 23:31
version: '3.5'

volumes:
  doorman_data:

services:
  doorman:
    image: ghcr.io/tiraelsedai/clubdoorman:1.0.10
    container_name: doorman
    restart: unless-stopped
    stop_grace_period: 10s
    environment:
      TZ: Europe/Moscow
      DOORMAN_BOT_API: --
      DOORMAN_ADMIN_CHAT: --
    volumes:
      - "doorman_data:/app/data"
    logging:
      driver: "json-file"
      options:
        max-file: 5
        max-size: 10m
    labels:
      com.centurylinklabs.watchtower.enable: "true"

я вот так сделал у себя

  Развернуть 1 комментарий

@vvzvlad, а, черт, надо latest тег сделать, а то обновляться не будет.

  Развернуть 1 комментарий
Павел Петрухин Программист-быдлокодер 7 июля в 19:44

Моё уважение автору!

Я в группе на 8к человек с успехом гоняю умпутуновского бота + на входе стоит @JohnRoebot в качестве капча-привратника (в планах написать своего со специфичной для чата капчой)

Кстати могу поделиться датасетом спама и "хороших" сообщений

  Развернуть 1 комментарий

@twentydraft, спасибо :)

чем меньше датасет -> меньше фич -> быстрее обучается, быстрее инференс, меньше памяти жрёт

так что я стараюсь не раздувать . за месяц добавил с пяток сообщений, одно вот проскользнуло под радаром, остальные были успешно классифицированы как спам, просто конфиденс был не очень большой

  Развернуть 1 комментарий

😎

Автор поста открыл его для большого интернета, но комментирование и движухи доступны только участникам Клуба

Что вообще здесь происходит?


Войти  или  Вступить в Клуб