tgwatchspam

tgwatchspam

A self-hosted Telegram bot that protects community groups from spam — crypto offers, job scams, and recruitment messages. Rule-based, predictable, zero cloud dependency.

Самостійно розгорнутий Telegram-бот, який захищає спільноти від спаму — крипто-пропозицій, шахрайства та рекрутингових повідомлень. Правила чіткі, без залежностей від хмари.

Go SQLite Long Polling ~15 MB RAM Self-hosted

OverviewОгляд

tgwatchspam is a self-hosted spam filter for Telegram community groups. It blocks unwanted messages about crypto, job scams, and other common spam patterns. Runs on any private VPS, requires no web interface, and is managed entirely through Telegram commands.

tgwatchspam — самостійно розгорнутий фільтр спаму для Telegram-спільнот. Блокує небажані повідомлення про крипту, шахрайство та вакансії. Працює на будь-якому VPS, не потребує веб-інтерфейсу та керується повністю через команди Telegram.

No web panel, no external services Без веб-панелі, без зовнішніх сервісів All management happens inside Telegram. The bot stores data locally in a SQLite file and communicates via long polling — no domain, no SSL certificate, no open ports required. Усе керування відбувається всередині Telegram. Бот зберігає дані локально у файлі SQLite та використовує long polling — не потрібен домен, SSL-сертифікат чи відкриті порти.
FEAT-001 / 002

Word & Regex FiltersФільтри слів та regex

Blocked words and RE2 regex patterns, per chat. Substring match with no word boundaries.

Заблоковані слова та RE2 regex-шаблони, окремо для кожного чату. Пошук підрядка без меж слів.

FEAT-003

Lookalike NormalizationНормалізація схожих символів

Cyrillic/Latin character substitution is detected and normalized before matching.

Заміна кириличних/латинських символів виявляється та нормалізується перед перевіркою.

FEAT-015

Button VerificationВерифікація кнопкою

New members must tap an inline button to prove they're human before posting.

Нові учасники повинні натиснути кнопку, щоб підтвердити, що вони людина, перед першим повідомленням.

FEAT-007

Sandbox ModeРежим пісочниці

Restricts new members to text-only (no media, no link previews) for a configurable period.

Обмежує нових учасників до тексту (без медіа та посилань) на налаштований час.

FEAT-010

Name FilterФільтр імен

Checks new members' display name and username against spam filters on join.

Перевіряє ім'я та нікнейм нового учасника через фільтри спаму при вступі.

FEAT-006

Multi-ChatДекілька чатів

One bot instance manages multiple groups simultaneously. All data is isolated per chat.

Один екземпляр бота керує кількома групами одночасно. Всі дані ізольовані по чату.

SetupНалаштування

RequirementsВимоги

First RunПерший запуск

  1. Clone and buildКлонувати та зібрати

    git clone https://github.com/sqerison/tgwatchspam
    cd tgwatchspam
    go build -o tgwatchspam ./cmd/bot
  2. Create your .env fileСтворити файл .env

    cp .env.example .env
    # Edit .env and fill in BOT_TOKEN and SUPERADMIN_ID
  3. Run the botЗапустити бота

    ./tgwatchspam

    On startup the bot automatically registers all commands with BotFather so users get autocomplete when typing /tgwatch_.

    При запуску бот автоматично реєструє всі команди в BotFather, щоб користувачі отримували автодоповнення при введенні /tgwatch_.

  4. Add the bot to your groupДодати бота до групи

    Grant it admin rights with: Delete messages, Ban users, Restrict members.

    Надайте права адміна: Видаляти повідомлення, Блокувати користувачів, Обмежувати учасників.

  5. Verify it worksПеревірити роботу

    /tgwatch_show settings

    The bot should reply with the default settings for your chat.

    Бот повинен відповісти налаштуваннями за замовчуванням для вашого чату.

DeploymentРозгортання

Build a Linux binary on your local machine, copy it to the server, then create a systemd service so it restarts automatically.

Зберіть Linux-бінарний файл на локальній машині, скопіюйте його на сервер, після чого створіть systemd-сервіс для автоматичного перезапуску.

Cross-compilationКрос-компіляція

No CGO is required — the binary is fully self-contained. Build for any target from any OS:

CGO не потрібен — бінарний файл повністю самодостатній. Збірка для будь-якої платформи з будь-якої ОС:

# Linux AMD64 (most VPS/cloud servers)
# Linux AMD64 (більшість VPS/хмарних серверів)
GOOS=linux GOARCH=amd64 go build -o tgwatchspam-linux ./cmd/bot

# Linux ARM64 (Raspberry Pi, AWS Graviton, etc.)
# Linux ARM64 (Raspberry Pi, AWS Graviton тощо)
GOOS=linux GOARCH=arm64 go build -o tgwatchspam-linux-arm64 ./cmd/bot

# macOS (Apple Silicon)
# macOS (Apple Silicon)
GOOS=darwin GOARCH=arm64 go build -o tgwatchspam-darwin ./cmd/bot

# Windows
# Windows
GOOS=windows GOARCH=amd64 go build -o tgwatchspam.exe ./cmd/bot
Pre-built binaries for all platforms are published automatically on every release via GitHub Actions. Download them from the Releases page. Готові бінарні файли для всіх платформ публікуються автоматично при кожному релізі через GitHub Actions. Завантажити їх можна на сторінці Releases.

Deploy to serverРозгортання на сервері

scp tgwatchspam-linux .env user@yourserver:/opt/tgwatchspam/tgwatchspam

Create /etc/systemd/system/tgwatchspam.service:

Створіть /etc/systemd/system/tgwatchspam.service:

[Unit]
Description=tgwatchspam Telegram bot
After=network.target

[Service]
WorkingDirectory=/opt/tgwatchspam
ExecStart=/opt/tgwatchspam/tgwatchspam
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
systemctl enable --now tgwatchspam
journalctl -u tgwatchspam -f   # view live logs
No open ports needed Відкриті порти не потрібні The bot uses long polling — it connects outbound to Telegram. You do not need to configure any firewall rules, reverse proxy, or SSL certificate. Бот використовує long polling — підключається вихідно до Telegram. Не потрібно налаштовувати правила фаєрволу, зворотний проксі чи SSL-сертифікат.

Spam FilteringФільтрація спаму

Every incoming message passes through a three-stage pipeline:

  1. Normalize — Cyrillic lookalike characters are mapped to their Latin equivalents and the text is lowercased.
  2. Word match — The normalized text is checked against the blocked word list using substring matching (no word boundaries).
  3. Regex match — The normalized text is tested against all RE2 patterns with (?i) prepended automatically.

Кожне вхідне повідомлення проходить триетапний конвейєр:

  1. Нормалізація — Кириличні символи, схожі на латинські, замінюються, текст переводиться в нижній регістр.
  2. Перевірка слів — Нормалізований текст перевіряється по списку заблокованих слів за принципом підрядка (без меж слів).
  3. Перевірка regex — Нормалізований текст тестується по всіх RE2-шаблонах з автоматично доданим (?i).

Word Filtering (FEAT-001)Фільтрація слів (FEAT-001)

Words are stored normalized. Matching is case-insensitive and uses substring logic — доход matches доходності, and binance matches binance/bybit (the slash does not break the match).

Слова зберігаються нормалізованими. Пошук регістронезалежний і використовує логіку підрядка — доход збігається з доходності, а binance — з binance/bybit (коса риска не розриває збіг).

/tgwatch_add word bitcoin
/tgwatch_add word гарна плата

# Bulk import — one word per line:
/tgwatch_add word
bitcoin
usdt
крипта
заробіток
digital nomad

Regex Filtering (FEAT-002)Regex-фільтрація (FEAT-002)

Patterns use Go's RE2 syntax. The (?i) flag is added automatically — you do not need to write it. Example patterns:

Шаблони використовують синтаксис RE2 від Go. Прапор (?i) додається автоматично — писати його не потрібно. Приклади шаблонів:

PatternШаблон MatchesЗбігається з
u[\.\s]?s[\.\s]?d[\.\s]?tusdt, u.s.d.t, u s d t
\+3[4-9]\d{7,}Non-Italian phone numbersНе-італійські номери телефонів
зп.{0,5}\d+\$Salary spam like "ЗП: 200$"Спам про зарплату: "ЗП: 200$"
earn.{0,10}\$\d+"earn $200/day" patternsШаблони "earn $200/day"

Cyrillic/Latin Normalization (FEAT-003)Нормалізація кирилиці/латиниці (FEAT-003)

Spammers bypass filters by mixing scripts — for example writing Rоblox with a Cyrillic о. The bot normalizes these before matching:

Спамери обходять фільтри, змішуючи алфавіти — наприклад, пишуть Rоblox з кириличною о. Бот нормалізує це перед перевіркою:

CyrillicКирилиця Maps toЗамінюється на CyrillicКирилиця Maps toЗамінюється на
а е о р с х у іa e o p c x y iА В Е К М Н О Р С Т Х УA B E K M H O P C T X Y
Testing filters Тестування фільтрів Use /tgwatch_check <text> to test any message against the current filters without taking any action. The bot will show exactly which rule matched and what would have happened. Використайте /tgwatch_check <текст>, щоб перевірити будь-яке повідомлення по поточних фільтрах без будь-якої дії. Бот покаже точно, яке правило спрацювало та що б сталося.

Spam ActionsДії для спаму

When a message matches a filter, the bot takes a configurable action. The message is always deleted first.

Коли повідомлення відповідає фільтру, бот виконує налаштовану дію. Повідомлення завжди видаляється першим.

ActionДія What happensЩо відбувається DefaultЗа замовч.
ban Message deleted + user permanently bannedПовідомлення видалено + користувача заблоковано назавжди YesТак
kick Message deleted + user removed (can rejoin via invite)Повідомлення видалено + користувача виключено (може повернутись за запрошенням)
mute Message deleted + user muted for N hoursПовідомлення видалено + користувача замовчано на N годин
delete Message deleted only, no further actionТільки видалення повідомлення, без подальших дій
/tgwatch_set action ban
/tgwatch_set action mute
/tgwatch_set mute_duration 24    # hours, used when action=mute

After every spam detection the bot posts a notification in the group:

Після кожного виявлення спаму бот публікує сповіщення в групі:

Spam removed User: @spammer
Matched word: bitcoin
Action: permanently banned
Спам видалено Користувач: @spammer
Збіг word: bitcoin
Дія: заблоковано назавжди

New Member ControlsКонтроль нових учасників

Button Verification (FEAT-015)

When enabled, new members must tap an inline button before they can post anything. This stops the vast majority of bot accounts that join and immediately post spam.

Якщо увімкнено, нові учасники повинні натиснути кнопку перед тим, як зможуть щось надіслати. Це зупиняє більшість бот-акаунтів, які вступають і одразу розсилають спам.

Flow:Процес:

  1. User joins → bot fully restricts them and posts a public message with a button: "I'm not a bot — let me in"
  2. User taps the button → bot verifies it's the same person → removes the verification message
  3. If they don't tap within the timeout → bot kicks them automatically
  1. Користувач вступає → бот повністю обмежує його та публікує повідомлення з кнопкою: "Я не бот — впустіть мене"
  2. Натискає кнопку → бот перевіряє, що це та сама людина → видаляє повідомлення верифікації
  3. Якщо не натиснув у відведений час → бот автоматично виключає його
Button security Безпека кнопки Only the user who joined can click their own button. If another user taps it, they receive a private popup: "This button is not for you." Лише той користувач, який вступив, може натиснути свою кнопку. Якщо хтось інший натисне, він отримає спливаюче повідомлення: "Ця кнопка не для вас."
/tgwatch_set verification on
/tgwatch_set verification_timeout 5    # minutes before auto-kick (default: 2)

Sandbox Mode (FEAT-007)

After passing verification (or immediately if verification is off), new members can be placed in sandbox mode. During sandbox they can post regular text messages but cannot send media, stickers, GIFs, or link previews.

Після верифікації (або одразу, якщо верифікація вимкнена) нові учасники можуть бути поміщені в режим пісочниці. У цьому режимі вони можуть надсилати текстові повідомлення, але не можуть надсилати медіа, стікери, GIF або попередній перегляд посилань.

Sandbox and verification work together:

Пісочниця та верифікація працюють разом:

/tgwatch_set sandbox on
/tgwatch_set sandbox_duration 24    # hours (default: 24)
/tgwatch_unrestrict                 # reply to a user's message to lift sandbox early

Name Filter (FEAT-010)

When enabled, the bot runs a new member's display name and username through the same word/regex filters on join. Accounts like crypto_earn_fast or usdt_exchanger are kicked before they post anything.

Якщо увімкнено, бот перевіряє ім'я та нікнейм нового учасника через ті самі фільтри при вступі. Акаунти на кшталт crypto_earn_fast або usdt_exchanger виключаються до того, як вони щось надішлють.

/tgwatch_set name_filter on

Admin AuthorizationАвторизація адмінів

All management commands require the user to be a group admin. Non-admins who send commands are silently ignored — no error reply, no indication that the command was received. This avoids tipping off spammers.

Всі команди управління вимагають, щоб користувач був адміністратором групи. Команди від не-адміністраторів мовчки ігноруються — без відповіді, без жодної індикації. Це не дозволяє спамерам дізнатися про бота.

LevelРівень WhoХто What they can doЩо може робити
Admin Any Telegram group adminБудь-який адмін групи Telegram All filter and settings commands within their chatВсі команди фільтрів та налаштувань у своєму чаті
Superadmin User ID set in SUPERADMIN_IDUser ID вказаний у SUPERADMIN_ID Everything + /tgwatch_copy via DM with the botВсі команди + /tgwatch_copy через DM з ботом

Admin status is verified live via Telegram's getChatMember API on every command call — no caching, no stale permissions.

Статус адміністратора перевіряється в реальному часі через API Telegram getChatMember при кожному виклику команди — без кешування, без застарілих прав.

Spam LogЖурнал спаму

Every filtered message is recorded in SQLite with full context: timestamp, user, matched rule, original message text, and action taken.

Кожне відфільтроване повідомлення записується в SQLite з повним контекстом: мітка часу, користувач, правило збігу, оригінальний текст повідомлення та вжита дія.

/tgwatch_log            # last 10 entries
/tgwatch_log 25         # last 25 entries (max 50)
/tgwatch_log clear      # clear the log for this chat (inline button)

Multi-Chat SupportПідтримка декількох чатів

One bot instance manages multiple groups simultaneously. All data — word lists, regex patterns, settings, spam log — is completely isolated per chat_id. No data is shared between chats unless explicitly copied.

Один екземпляр бота керує кількома групами одночасно. Всі дані — списки слів, regex-шаблони, налаштування, журнал спаму — повністю ізольовані за chat_id. Дані між чатами не спільні, якщо тільки не скопійовані явно.

# Superadmin only, send this in a DM with the bot:
/tgwatch_copy -100123456789 -100987654321

The bot will ask for confirmation before overwriting. Existing data in the target chat is replaced, not merged.

Бот запитає підтвердження перед перезаписом. Існуючі дані в цільовому чаті замінюються, а не об'єднуються.

Command ReferenceДовідник команд

All commands use the /tgwatch_ prefix to avoid conflicts with other bots in the same group.

Усі команди використовують префікс /tgwatch_, щоб уникнути конфліктів з іншими ботами в групі.

Word FiltersФільтри слів

CommandКоманда WhoХто DescriptionОпис
/tgwatch_add word <text>AdminAdd a blocked word or phraseДодати заблоковане слово або фразу
/tgwatch_add word + linesрядкиAdminBulk add — one word per line after the commandМасове додавання — одне слово на рядок після команди
/tgwatch_remove word <text>AdminRemove a wordВидалити слово
/tgwatch_list wordsAdminList all blocked wordsПоказати всі заблоковані слова
/tgwatch_clear wordsAdminRemove all words (inline button confirmation)Видалити всі слова (підтвердження кнопкою)

Regex FiltersRegex-фільтри

CommandКоманда WhoХто DescriptionОпис
/tgwatch_add regex <pattern>AdminAdd a RE2 regex pattern ((?i) applied automatically)Додати RE2 regex-шаблон ((?i) додається автоматично)
/tgwatch_remove regex <pattern>AdminRemove a patternВидалити шаблон
/tgwatch_list regexAdminList all patternsПоказати всі шаблони
/tgwatch_clear regexAdminRemove all patterns (inline button confirmation)Видалити всі шаблони (підтвердження кнопкою)

Spam Action SettingsНалаштування дій для спаму

CommandКоманда WhoХто DescriptionОпис
/tgwatch_set action delete|mute|ban|kickAdminAction when spam is detected (default: ban)Дія при виявленні спаму (за замовч.: ban)
/tgwatch_set mute_duration <hours>AdminMute duration (used when action=mute)Тривалість заглушення (при action=mute)

New Member ControlsКонтроль нових учасників

CommandКоманда WhoХто DescriptionОпис
/tgwatch_set verification on|offAdminRequire new members to tap a button before postingВимагати від нових учасників натиснути кнопку перед постінгом
/tgwatch_set verification_timeout <min>AdminMinutes to verify before auto-kick (default: 2)Хвилин на верифікацію до авто-виключення (за замовч.: 2)
/tgwatch_set sandbox on|offAdminRestrict new members to text-only for sandbox_durationОбмежити нових учасників до тексту на sandbox_duration
/tgwatch_set sandbox_duration <hours>AdminHow long sandbox lasts (default: 24h)Тривалість пісочниці (за замовч.: 24г)
/tgwatch_set name_filter on|offAdminKick new members whose name/username matches spam filtersВиключати нових учасників, чиє ім'я/нікнейм відповідає фільтрам
/tgwatch_unrestrictAdminReply to a message to manually lift sandbox on that userВідповісти на повідомлення, щоб вручну зняти пісочницю з користувача

ManagementУправління

CommandКоманда WhoХто DescriptionОпис
/tgwatch_show settingsAdminShow all current settings for this chatПоказати поточні налаштування чату
/tgwatch_check <text>AdminTest text against filters without taking actionПеревірити текст по фільтрам без дії
/tgwatch_log [n]AdminShow last N spam entries (default 10, max 50)Показати останні N записів спаму (за замовч. 10, макс. 50)
/tgwatch_log clearAdminClear the spam log for this chat (inline button confirmation)Очистити журнал спаму (підтвердження кнопкою)
/tgwatch_cleanAdminDelete all bot replies and admin commands from chatВидалити всі відповіді бота та команди адмінів
/tgwatch_langAdminChoose bot language for this chat (🇬🇧 English / 🇺🇦 Українська)Вибрати мову бота для цього чату (🇬🇧 English / 🇺🇦 Українська)
/tgwatch_helpAdminShow all available commands in chatПоказати всі доступні команди в чаті
/tgwatch_copy <src_id> <dst_id>SuperadminCopy all settings from one chat to another (use in DM)Копіювати налаштування між чатами (у DM)

ConfigurationКонфігурація

Configuration is loaded from a .env file in the working directory.

Конфігурація завантажується з файлу .env у робочій директорії.

VariableЗмінна RequiredОбов'язкова DefaultЗа замовч. DescriptionОпис
BOT_TOKEN YesТак Telegram bot token from @BotFatherТокен Telegram-бота від @BotFather
SUPERADMIN_ID YesТак Numeric Telegram user ID of the owner (from @userinfobot)Числовий Telegram user ID власника (від @userinfobot)
DATABASE_PATH NoНі ./data/bot.db Path to the SQLite database fileШлях до файлу бази даних SQLite
LOG_LEVEL NoНі info debug / info / warn / error

Per-Chat SettingsНалаштування чату

These are stored in SQLite and changed via bot commands. Each chat has independent settings.

Зберігаються в SQLite і змінюються через команди бота. Кожен чат має незалежні налаштування.

SettingНалаштування DefaultЗа замовч. CommandКоманда
Spam actionДія для спамуban/tgwatch_set action
Mute durationТривалість заглушення24h/tgwatch_set mute_duration
VerificationВерифікаціяoff/tgwatch_set verification
Verification timeoutТаймаут верифікації2 min/tgwatch_set verification_timeout
SandboxПісочницяoff/tgwatch_set sandbox
Sandbox durationТривалість пісочниці24h/tgwatch_set sandbox_duration
Name filterФільтр іменon/tgwatch_set name_filter
LanguageМоваen/tgwatch_lang

Feature IndexІндекс функцій

Each feature has a tag in source code comments (e.g. [FEAT-001]). Search with grep -r "FEAT-001" .

Кожна функція має тег у коментарях вихідного коду (наприклад, [FEAT-001]). Пошук: grep -r "FEAT-001" .

ID FeatureФункція Primary FileОсновний файл
FEAT-001Word/phrase filteringФільтрація слів/фразinternal/filter/words.go
FEAT-002Regex filteringRegex-фільтраціяinternal/filter/regex.go
FEAT-003Cyrillic/Latin normalizationНормалізація кирилиці/латиниціinternal/filter/normalize.go
FEAT-004Spam actionsДії для спамуinternal/bot/handlers.go
FEAT-005Admin authorizationАвторизація адмінівinternal/admin/admin.go
FEAT-006Multi-chat supportПідтримка декількох чатівinternal/storage/storage.go
FEAT-007Sandbox modeРежим пісочниціinternal/bot/handlers.go
FEAT-008/tgwatch_check commandКоманда /tgwatch_checkinternal/bot/handlers.go
FEAT-009Spam logЖурнал спамуinternal/storage/storage.go
FEAT-010Name/username filter on joinФільтр імен/нікнеймів при вступіinternal/bot/handlers.go
FEAT-011Bulk word importМасовий імпорт слівinternal/bot/handlers.go
FEAT-012Copy settings between chatsКопіювання налаштувань між чатамиinternal/bot/handlers.go
FEAT-013/tgwatch_clean commandКоманда /tgwatch_cleaninternal/bot/handlers.go
FEAT-014Spam notification in chatСповіщення про спам у чатіinternal/bot/handlers.go
FEAT-015Button verification for new membersВерифікація кнопкою для нових учасниківinternal/bot/handlers.go
FEAT-016Per-chat language switching (🇬🇧 / 🇺🇦)Перемикання мови бота (🇬🇧 / 🇺🇦)internal/i18n/i18n.go, internal/bot/handlers.go


tgwatchspam — self-hosted, open source, written in Go. tgwatchspam — самостійне розгортання, відкритий код, написано на Go.  ·  github.com/sqerison/tgwatchspam

Built byРозроблено AppRecode

Cloud & DevOps consulting company with 14+ years of IT experience. We build infrastructure, automate delivery, and deploy AI systems. Консалтингова компанія з хмарних технологій та DevOps з 14+ роками досвіду в IT. Будуємо інфраструктуру, автоматизуємо доставку та розгортаємо AI-системи.

MLOps & AI Infrastructure AI Security DevOps & Cloud Managed Cloud