# parser-tg-bot Парсер публичных Telegram-каналов на Telethon (MTProto). Сохраняет сообщения в Postgres, управляется через REST API. Период опроса настраивается через `.env`. На следующем шаге легко перевести на realtime через `events.NewMessage`. ## Стек - Python 3.11, Telethon, FastAPI, SQLAlchemy 2 (async) + Alembic, APScheduler, Postgres 16 ## Структура ```text src/parser_bot/ ├── api/ # FastAPI роуты + Pydantic-схемы ├── db/ # SQLAlchemy модели + сессии ├── scheduler/ # APScheduler-воркер периодического опроса ├── telegram/ # Telethon-клиент (resolve, fetch) ├── web/static/ # SPA-странички (HTML/CSS/JS, без бандлера) ├── config.py # pydantic-settings └── main.py # FastAPI lifespan + uvicorn alembic/ # миграции ``` ## Первый запуск (локально, через Docker) 1. Получить `api_id` и `api_hash` на [my.telegram.org](https://my.telegram.org) → API development tools. 2. Скопировать `.env.example` в `.env` и заполнить `TG_API_ID`, `TG_API_HASH`, `TG_PHONE`. 3. Поднять Postgres + накатить миграции: ```bash docker compose up -d db docker compose run --rm app alembic upgrade head ``` 4. Запуск: ```bash docker compose up -d docker compose logs app --tail=50 ``` 5. **Авторизация Telegram** — открыть [http://localhost:8000/auth.html](http://localhost:8000/auth.html) и нажать «Отправить код». Telegram пришлёт код на номер из `TG_PHONE` → ввести код (и 2FA-пароль, если включён). Готово, парсер начнёт опрос. Сессия сохраняется в `./data/session/parser.session` — рестарты её переиспользуют, повторно входить не нужно. ### Админ-доступ и коды подразделов - `ADMIN_PASSWORD` — дополнительный пароль для админских функций. Если не задан, остаётся прежний режим: доступ определяется только `ADMIN_ALLOWED_IPS`. - [http://localhost:8000/admin.html](http://localhost:8000/admin.html) — вход по админ-паролю. После входа доступны удаление и редактирование подразделов, просмотр их кодов, управление каналами, ручной опрос, промпты, авторизация Telegram и Swagger. - При создании подраздела обязательно задаётся `Код доступа`. Пользователь вводит этот код при первом открытии данных подраздела; после входа он может добавлять каналы в этот подраздел. Админ видит код в списке подразделов. ### Прод-вариант: без UI и без volume (k8s-friendly) Сделай интерактивный логин **один раз** на dev-машине и получи опаковую строку: ```bash docker compose run --rm -it app python -m parser_bot.auth ``` Скрипт напечатает строку вида `TG_SESSION_STRING=1AbcD...`. Положи её в `.env` или k8s Secret — после этого приложение поднимается без UI и без монтирования сессионного файла: ```ini TG_SESSION_STRING=1AbcDef... # вместо TG_SESSION_PATH/volume ``` > ⚠️ **`ApiIdPublishedFloodError`** — Telegram заблокировал твою пару > `api_id`/`api_hash` (попала в публичный доступ). Создай **новое** приложение > на [my.telegram.org](https://my.telegram.org) и не публикуй креды нигде. > Старый `api_id` восстановить нельзя. ## UI После запуска доступны страницы: - [Дашборд](http://localhost:8000/) — общая статистика, топ каналов, кнопка опросить всех - [Каналы](http://localhost:8000/channels.html) — добавить / удалить / включить-выключить / опросить вручную - [Сообщения](http://localhost:8000/messages.html) — фильтр по каналу, поиск по тексту, пагинация, raw JSON - [Настройки](http://localhost:8000/settings.html) — текущая конфигурация и подсказки - [Авторизация](http://localhost:8000/auth.html) — веб-логин в Telegram (код + 2FA) - [Swagger UI](http://localhost:8000/docs) — интерактивный API Глубокая ссылка `messages.html?channel_id=42` открывает ленту конкретного канала. ## API - `GET /healthz` — health check - `GET /api/v1/auth/status` — авторизован ли клиент - `POST /api/v1/auth/send-code` — отправить код на `TG_PHONE` - `POST /api/v1/auth/submit-code` `{"code": "12345"}` — подтвердить код - `POST /api/v1/auth/submit-password` `{"password": "..."}` — 2FA-пароль - `POST /api/v1/auth/logout` — завершить сессию - `GET /api/v1/stats` — глобальные счётчики - `GET /api/v1/settings` — read-only вид конфигурации - `GET /api/v1/channels` — список каналов - `POST /api/v1/channels` `{"identifier": "@durov"}` — добавить - `GET /api/v1/channels/{id}` — карточка - `PATCH /api/v1/channels/{id}` `{"is_active": false}` — включить/выключить - `DELETE /api/v1/channels/{id}` — удалить - `GET /api/v1/channels/{id}/stats` — счётчики по каналу - `POST /api/v1/channels/{id}/poll` — форсировать опрос одного канала - `POST /api/v1/poll` — форсировать опрос всех активных каналов - `GET /api/v1/messages?channel_id=...&q=...&limit=50&offset=0` — лента - `GET /api/v1/messages/{id}` — одно сообщение (с `raw` JSONB) ## Дальше - **Realtime**: заменить APScheduler на `client.add_event_handler(handler, events.NewMessage)`, оставив periodic poll как фоновый «доводчик» для пропущенных сообщений. - **Go-микросервис**: контракт = таблицы `channels` / `messages` в Postgres. Go-сервис может либо читать ту же БД, либо ходить в `/api/v1/messages`. - **k8s**: добавить Helm-чарт; `data/session/` маппится на PVC, `.env` — в Secret.