Матеріали до Уроку 4: Флот скрейперів (Telegram · Discord · WhatsApp)
Навігація курсу: Категорія: Довідники
Урок: Урок-04-Соцмережі-та-медіапростір
Курс: Соцмережі та медіапростір: збір, моніторинг, базовий аналіз
Тема: AI для OSINT і розвідки — практичний кейс: власний флот скрейперів
Кейс:telegram_crawler— дашборд + колектори Telegram · Discord · WhatsApp + Waydroid
Цей документ — довідка до лекції. Він підсумовує архітектуру, уроки й
обмеження на прикладі реального проєкту, а також дає орієнтири в коді та
домашнє завдання. Числа — зі стану продакшну на момент лекції.
1. Короткий огляд
Ми будуємо флот колекторів під єдиним дашбордом. Кожен колектор збирає
дані з однієї платформи й складає їх у спільну канонічну модель. Головне в
лекції — не «як витягнути все», а як збирати стійко й не отримати бан:
кеш, бюджети, ротація акаунтів, пейсинг, проксі-вихід.
Три наскрізні ідеї:
- Збирай так, як платформа дозволяє — офіційні шляхи + членство акаунта,
без автоматичного «вступу» з дашборда. - Міряй причину, не здогадуйся — головний баг (FloodWait) був не там, де
здавалося. - Акаунти — витратний матеріал — ротація, кулдауни, облік здоровʼя.
2. Термінологія
| Термін | Значення |
|---|---|
| Crawling | обхід джерела/посилань, щоб знайти, що саме збирати |
| Scraping | витягання конкретних полів з уже знайденої сторінки/повідомлення |
| Monitoring | повторний збір за розкладом + відстеження змін (change tracking) |
| Enrichment | збагачення зібраного (нормалізація, звʼязки, мета) після збору |
| Backfill | одноразовий добір історії джерела «назад у часі» |
| Cursor | знімок «де я зупинився» (останній id / snowflake) для інкрементного збору |
| Канонічна модель | єдиний формат запису: author · url · date · text |
| Rate limit / FloodWait | ліміт платформи; у Telegram — тимчасовий бан за перевищення |
| Self-bot | автоматизація користувацького акаунта (проти ToS платформ) |
| Pool / rotation | набір акаунтів, між якими розподіляється навантаження |
| Egress / proxy | вихідний IP збору; часто через VPN/резидентний проксі |
3. Архітектура системи
Контейнери (прод: Portainer на apollonia):
- Dashboard (FastAPI) — керування акаунтами, лімітами, здоровʼям; вебхуки;
синхронізація членства; панелі статистики. - Crawler container (
main.py) — розклад збору; виконує цикли Telegram і
Discord у тому ж контейнері. - OpenWA bridge — окремий контейнер (
whatsapp-web.js, headless Chrome),
що віддає події WhatsApp у дашборд через вебхук. - Waydroid (Android) — контейнеризований Android для реєстрації/входу
Telegram-акаунтів і витягання сесії. - SOCKS5 egress — виходи IPVanish +
gost-relay (sidecar-контейнери). - MySQL (byteBard) —
sources.*(конфіг і стан) +results.*(спільні
таблиці результатів усіх колекторів).
Схема змін БД їде через ensure_*-функції на старті контейнера; файли
migrations/*.sql — паритетні копії.
4. Канонічна модель даних
Два простори імен:
sources.*— наша конфігурація та стан: акаунти, цілі, курсори,
членство, події здоровʼя, кеш entity.results.*— спільні, повнотекстово-індексовані таблиці результатів, які
читають усі споживачі платформи:results.telegram_channelsresults.discordresults.whatsapp_messages
Спільний «кістяк» запису: author · url · date · text (+ added — час
захоплення; + медіа за потреби). Для Discord сервер і канал відновлюються з
permalink у полі url (discord.com/channels/{guild}/{channel}/{message}).
5. Telegram-колектор
Як працює: Telethon; пул із 6 номерів (round-robin + кулдаун); збір
інкрементний за per-channel курсором; результати → results.telegram_channels.
Головний урок — FloodWait:
- Припущення: «банять за частоту запитів».
- Реальність: банять за обсяг
ResolveUsername(перетворення
username → entity). Сотні нових resolve → аномалія →FloodWait~24 год. - Рішення:
- Кеш entity —
sources.telegram_entity_cacheзберігає
(peer_id, access_hash), тож повторний resolve не потрібен; - Денний бюджет resolve на акаунт (напр. 60/день), рахується до
спроби (мертві цілі не зливають бюджет); - перевищив бюджет → мʼяко відкласти ціль (без помилки, без бана).
- Кеш entity —
- Наслідок: 1 452 цілі обслуговуються 6 акаунтами без банів за обсягом.
Код-орієнтир: crawl/functions/resolve.py → resolve_input_peer().
6. Discord-колектор
Як працює: пул user-token (self-bot, проти ToS). Читає лише сервери, де
акаунт уже є (GET /users/@me/guilds). Нічого не «join-имо» з дашборда.
- Карта членства (
sources.discord_guild_membership): account ↔ guild,
can_read. Кожен член-сервер автоматично стає ціллю. - Ротація: якщо кілька акаунтів у тому самому сервері — вибір за LRU
(last_used), щоб жоден токен не «світився». - Здоровʼя акаунта: помилки класифікуються —
403 «не член»(без штрафу) ·401(мертвий токен →invalid) ·
429 / CAPTCHA(кулдаун саме цього акаунта) — і пишуться в
sources.account_events(панель ризику: зелений/жовтий/червоний).
Код-орієнтири:
auth/discord_account_pool.py → acquire() (ротація), release()
(класифікація сигналів); crawl/functions/discord_crawl.py (цикл збору).
7. WhatsApp-колектор
Як працює: міст OpenWA (whatsapp-web.js); звʼязка акаунта через QR або
pairing-code (без пароля); збір пасивний — події message.received
приходять вебхуком → results.whatsapp_messages.
- Цілі — read-only: синхронізуються зі списку груп/каналів, де номер вже
є. Нову групу додають у застосунку на телефоні. - Проксі: лише через relay — Chromium не вміє авторизацію SOCKS5; сесійний
проксі незмінний після створення. - Backfill (одноразово на чат):
- канал — не E2E: читаються останні пости (вкл. до підписки);
- група/спільнота — E2E: лише те, що номер отримав після вступу;
історію до вступу не віддає жоден клієнт.
Код-орієнтир: crawl/functions/whatsapp_backfill.py →
backfill_next_whatsapp_target().
8. Waydroid — Android у контейнері
Що це: повноцінна система Android (LineageOS) у LXC-контейнері, що
використовує ядро Linux хоста (через binder/ashmem) — це не віртуальна
машина й не емуляція CPU. Екран виводиться через Wayland.
Навіщо нам: деякі акаунти вимагають реєстрації/входу через офіційний
застосунок. У Waydroid ставимо справжній Telegram APK, входимо за номером
(SMS/OTP як на телефоні), витягаємо .session — далі збір іде headless через
Telethon, без Android.
Обмеження, які зустріли: пристрій крешиться, доки на хості не зроблено
modprobe binder_linux; шлях через noVNC/wayvnc виявився глухим кутом.
9. Проксі-вихід і релей
- Вихід із IP дата-центру — сам по собі сигнал для анти-абʼюзу.
- Chromium (WhatsApp/OpenWA) не вміє авторизовувати SOCKS5 → потрібен
gost-relay без пароля в мережі, який фронтить авторизований upstream
(IPVanish). - Проксі сесії WhatsApp незмінний після створення (зміна виходу =
перезвʼязка акаунта). - Без
python-socksTelethon тихо пішов би напряму — «витік IP», що
виглядає як успіх (найгірший тип збою).
10. Обмеження, які ми зустріли (зведення)
| Обмеження | Суть | Помʼякшення |
|---|---|---|
| Флуд-ліміти (Telegram) | бан за обсяг resolve, не частоту | кеш entity + денний бюджет |
| Бани / CAPTCHA (Discord) | self-bot проти ToS; 401/429/CAPTCHA | кулдаун, інвалідація, облік подій |
| Обмеження WhatsApp | TOS_BLOCK, розрив сесії; межа E2E | risk-панель; backfill лише після вступу |
| Ротація акаунтів | один токен не має «світитися» | пули + вибір LRU |
| Проксі-вихід | DC-IP = сигнал; SOCKS-авторизація | gost-relay; стеження за витоком IP |
| Акаунти = витратний матеріал | неофіційні клієнти ризиковані | не зберігаємо пароль — лише токен/сесію |
11. Ключові числа (прод на момент лекції)
| Платформа | Акаунти | Цілі | Рядків результатів | Інше |
|---|---|---|---|---|
| Telegram | 6 | 1 452 | 5 098 538 | entity-кеш: 4 503; бюджет 60/день |
| Discord | 4 | 5 серв. | 25 969 | 115 каналів; 7 зв. членства; 919 подій |
| 2 | 5 | 328 | збір пасивний (вебхук) |
Discord за серверами: Urbexology — 13 873 · Bellingcat — 5 614 ·
AfD Community — 3 796 · Vegan Dreamland — 2 547 · Nour Community — 139.
12. Код-орієнтири (де що лежить)
| Концепт | Файл |
|---|---|
| Кеш + бюджет resolve (Telegram) | crawl/functions/resolve.py |
| Ротація / здоровʼя пулу (Discord) | auth/discord_account_pool.py |
| Цикл збору Discord | crawl/functions/discord_crawl.py |
| Синхронізація членства (Discord) | crawl/functions/discord_membership.py |
| Backfill історії (WhatsApp) | crawl/functions/whatsapp_backfill.py |
| Міст до WhatsApp (OpenWA) | auth/whatsapp_bridge_client.py |
| Панель ризику акаунтів | auth/account_risk.py |
| Канонічні рядки результатів | database/discord_rows.py, database/result_rows.py |
Доступ до БД (ensure_*, запити) | database/functions.py |
| Дашборд / маршрути | ui/app.py, ui/static/* |
13. Інструменти, згадані в лекції
- Telethon — Telegram MTProto-клієнт (Python).
- whatsapp-web.js / OpenWA — міст до WhatsApp web-протоколу (Node).
- Waydroid — Android у контейнері на ядрі хоста.
- gost — легкий проксі/relay (Go).
- FastAPI — дашборд; MySQL — сховище; Docker / Portainer — деплой.
14. Домашнє завдання
Короткий зміст — повний текст у файлі
Домашнє завдання — OSINT-скрейпер.md:
Напишіть Python-проєкт, що збирає дані з однієї соцмережі з лекції
(Telegram / Discord / WhatsApp) або схожої (Reddit, Mastodon, публічний
Telegram-канал, YouTube-коментарі, RSS). Вимоги: інкрементність, канонічна
модельauthor·url·date·text, повага до лімітів (backoff), стійкість + dedup,
конфіг через.env. Здати: git-репо + README, ≥ 200 записів, звіт про
зустрінуті обмеження.
15. Файли цього заняття
Лекція 5 — OSINT-флот (кейс проєкту).pptx— презентація (33 слайди).Домашнє завдання — OSINT-скрейпер.md— домашнє завдання.Заняття 5 — Супровідні матеріали.md— цей документ.