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-бот, який захищає спільноти від спаму — крипто-пропозицій, шахрайства та рекрутингових повідомлень. Правила чіткі, без залежностей від хмари.
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.
Word & Regex FiltersФільтри слів та regex
Blocked words and RE2 regex patterns, per chat. Substring match with no word boundaries.
Заблоковані слова та RE2 regex-шаблони, окремо для кожного чату. Пошук підрядка без меж слів.
Lookalike NormalizationНормалізація схожих символів
Cyrillic/Latin character substitution is detected and normalized before matching.
Заміна кириличних/латинських символів виявляється та нормалізується перед перевіркою.
Button VerificationВерифікація кнопкою
New members must tap an inline button to prove they're human before posting.
Нові учасники повинні натиснути кнопку, щоб підтвердити, що вони людина, перед першим повідомленням.
Sandbox ModeРежим пісочниці
Restricts new members to text-only (no media, no link previews) for a configurable period.
Обмежує нових учасників до тексту (без медіа та посилань) на налаштований час.
Name FilterФільтр імен
Checks new members' display name and username against spam filters on join.
Перевіряє ім'я та нікнейм нового учасника через фільтри спаму при вступі.
Multi-ChatДекілька чатів
One bot instance manages multiple groups simultaneously. All data is isolated per chat.
Один екземпляр бота керує кількома групами одночасно. Всі дані ізольовані по чату.
SetupНалаштування
RequirementsВимоги
- A Linux VPS (any size — the bot uses ~15 MB RAM)
- A Telegram bot token from @BotFather
- Your numeric Telegram user ID from @userinfobot
- Go 1.21+ (only needed to build; the binary is self-contained)
- Linux VPS (будь-який розмір — бот використовує ~15 МБ RAM)
- Токен Telegram-бота від @BotFather
- Ваш числовий Telegram user ID від @userinfobot
- Go 1.21+ (потрібен лише для збірки; бінарний файл самодостатній)
First RunПерший запуск
-
Clone and buildКлонувати та зібрати
git clone https://github.com/sqerison/tgwatchspam cd tgwatchspam go build -o tgwatchspam ./cmd/bot
-
Create your
.envfileСтворити файл.envcp .env.example .env # Edit .env and fill in BOT_TOKEN and SUPERADMIN_ID
-
Run the botЗапустити бота
./tgwatchspam
On startup the bot automatically registers all commands with BotFather so users get autocomplete when typing
/tgwatch_.При запуску бот автоматично реєструє всі команди в BotFather, щоб користувачі отримували автодоповнення при введенні
/tgwatch_. -
Add the bot to your groupДодати бота до групи
Grant it admin rights with: Delete messages, Ban users, Restrict members.
Надайте права адміна: Видаляти повідомлення, Блокувати користувачів, Обмежувати учасників.
-
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
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
Spam FilteringФільтрація спаму
Every incoming message passes through a three-stage pipeline:
- Normalize — Cyrillic lookalike characters are mapped to their Latin equivalents and the text is lowercased.
- Word match — The normalized text is checked against the blocked word list using substring matching (no word boundaries).
- Regex match — The normalized text is tested against all RE2 patterns with
(?i)prepended automatically.
Кожне вхідне повідомлення проходить триетапний конвейєр:
- Нормалізація — Кириличні символи, схожі на латинські, замінюються, текст переводиться в нижній регістр.
- Перевірка слів — Нормалізований текст перевіряється по списку заблокованих слів за принципом підрядка (без меж слів).
- Перевірка 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]?t | usdt, 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 |
/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:
Після кожного виявлення спаму бот публікує сповіщення в групі:
Matched word:
bitcoinAction: permanently banned
Збіг 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:Процес:
- User joins → bot fully restricts them and posts a public message with a button: "I'm not a bot — let me in"
- User taps the button → bot verifies it's the same person → removes the verification message
- If they don't tap within the timeout → bot kicks them automatically
- Користувач вступає → бот повністю обмежує його та публікує повідомлення з кнопкою: "Я не бот — впустіть мене"
- Натискає кнопку → бот перевіряє, що це та сама людина → видаляє повідомлення верифікації
- Якщо не натиснув у відведений час → бот автоматично виключає його
/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:
Пісочниця та верифікація працюють разом:
- Verification ON + Sandbox ON — Must tap button first, then text-only for sandbox_hours
- Verification ON + Sandbox OFF — Must tap button, then full access
- Verification OFF + Sandbox ON — Text-only immediately, full access after sandbox_hours
- Both OFF — No restrictions on new members
- Верифікація ON + Пісочниця ON — Спочатку натиснути кнопку, потім тільки текст на sandbox_hours
- Верифікація ON + Пісочниця OFF — Натиснути кнопку, потім повний доступ
- Верифікація OFF + Пісочниця ON — Одразу тільки текст, повний доступ після sandbox_hours
- Обидва OFF — Немає обмежень для нових учасників
/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> | Admin | Add a blocked word or phraseДодати заблоковане слово або фразу |
/tgwatch_add word + linesрядки | Admin | Bulk add — one word per line after the commandМасове додавання — одне слово на рядок після команди |
/tgwatch_remove word <text> | Admin | Remove a wordВидалити слово |
/tgwatch_list words | Admin | List all blocked wordsПоказати всі заблоковані слова |
/tgwatch_clear words | Admin | Remove all words (inline button confirmation)Видалити всі слова (підтвердження кнопкою) |
Regex FiltersRegex-фільтри
| CommandКоманда | WhoХто | DescriptionОпис |
|---|---|---|
/tgwatch_add regex <pattern> | Admin | Add a RE2 regex pattern ((?i) applied automatically)Додати RE2 regex-шаблон ((?i) додається автоматично) |
/tgwatch_remove regex <pattern> | Admin | Remove a patternВидалити шаблон |
/tgwatch_list regex | Admin | List all patternsПоказати всі шаблони |
/tgwatch_clear regex | Admin | Remove all patterns (inline button confirmation)Видалити всі шаблони (підтвердження кнопкою) |
Spam Action SettingsНалаштування дій для спаму
| CommandКоманда | WhoХто | DescriptionОпис |
|---|---|---|
/tgwatch_set action delete|mute|ban|kick | Admin | Action when spam is detected (default: ban)Дія при виявленні спаму (за замовч.: ban) |
/tgwatch_set mute_duration <hours> | Admin | Mute duration (used when action=mute)Тривалість заглушення (при action=mute) |
New Member ControlsКонтроль нових учасників
| CommandКоманда | WhoХто | DescriptionОпис |
|---|---|---|
/tgwatch_set verification on|off | Admin | Require new members to tap a button before postingВимагати від нових учасників натиснути кнопку перед постінгом |
/tgwatch_set verification_timeout <min> | Admin | Minutes to verify before auto-kick (default: 2)Хвилин на верифікацію до авто-виключення (за замовч.: 2) |
/tgwatch_set sandbox on|off | Admin | Restrict new members to text-only for sandbox_durationОбмежити нових учасників до тексту на sandbox_duration |
/tgwatch_set sandbox_duration <hours> | Admin | How long sandbox lasts (default: 24h)Тривалість пісочниці (за замовч.: 24г) |
/tgwatch_set name_filter on|off | Admin | Kick new members whose name/username matches spam filtersВиключати нових учасників, чиє ім'я/нікнейм відповідає фільтрам |
/tgwatch_unrestrict | Admin | Reply to a message to manually lift sandbox on that userВідповісти на повідомлення, щоб вручну зняти пісочницю з користувача |
ManagementУправління
| CommandКоманда | WhoХто | DescriptionОпис |
|---|---|---|
/tgwatch_show settings | Admin | Show all current settings for this chatПоказати поточні налаштування чату |
/tgwatch_check <text> | Admin | Test text against filters without taking actionПеревірити текст по фільтрам без дії |
/tgwatch_log [n] | Admin | Show last N spam entries (default 10, max 50)Показати останні N записів спаму (за замовч. 10, макс. 50) |
/tgwatch_log clear | Admin | Clear the spam log for this chat (inline button confirmation)Очистити журнал спаму (підтвердження кнопкою) |
/tgwatch_clean | Admin | Delete all bot replies and admin commands from chatВидалити всі відповіді бота та команди адмінів |
/tgwatch_lang | Admin | Choose bot language for this chat (🇬🇧 English / 🇺🇦 Українська)Вибрати мову бота для цього чату (🇬🇧 English / 🇺🇦 Українська) |
/tgwatch_help | Admin | Show all available commands in chatПоказати всі доступні команди в чаті |
/tgwatch_copy <src_id> <dst_id> | Superadmin | Copy 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-001 | Word/phrase filteringФільтрація слів/фраз | internal/filter/words.go |
FEAT-002 | Regex filteringRegex-фільтрація | internal/filter/regex.go |
FEAT-003 | Cyrillic/Latin normalizationНормалізація кирилиці/латиниці | internal/filter/normalize.go |
FEAT-004 | Spam actionsДії для спаму | internal/bot/handlers.go |
FEAT-005 | Admin authorizationАвторизація адмінів | internal/admin/admin.go |
FEAT-006 | Multi-chat supportПідтримка декількох чатів | internal/storage/storage.go |
FEAT-007 | Sandbox modeРежим пісочниці | internal/bot/handlers.go |
FEAT-008 | /tgwatch_check commandКоманда /tgwatch_check | internal/bot/handlers.go |
FEAT-009 | Spam logЖурнал спаму | internal/storage/storage.go |
FEAT-010 | Name/username filter on joinФільтр імен/нікнеймів при вступі | internal/bot/handlers.go |
FEAT-011 | Bulk word importМасовий імпорт слів | internal/bot/handlers.go |
FEAT-012 | Copy settings between chatsКопіювання налаштувань між чатами | internal/bot/handlers.go |
FEAT-013 | /tgwatch_clean commandКоманда /tgwatch_clean | internal/bot/handlers.go |
FEAT-014 | Spam notification in chatСповіщення про спам у чаті | internal/bot/handlers.go |
FEAT-015 | Button verification for new membersВерифікація кнопкою для нових учасників | internal/bot/handlers.go |
FEAT-016 | Per-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-системи.