Scope monitoring TG by department

This commit is contained in:
Grendgi
2026-06-04 15:31:10 +03:00
parent f9e072774c
commit b78d1eac02
27 changed files with 481 additions and 553 deletions

173
README.md
View File

@@ -1,123 +1,68 @@
# parser-tg-bot
# monitoring-tg
Парсер публичных Telegram-каналов на Telethon (MTProto). Сохраняет сообщения в Postgres,
управляется через REST API. Период опроса настраивается через `.env`. На следующем шаге
легко перевести на realtime через `events.NewMessage`.
Сервис мониторинга Telegram-каналов для портала. Он сохраняет сообщения в
Postgres, раскладывает каналы по вертикалям/подразделам и выполняет AI-анализ
через OpenAI-compatible endpoint, общий с другими сервисами портала.
## Стек
## Доступ
- Python 3.11, Telethon, FastAPI, SQLAlchemy 2 (async) + Alembic, APScheduler, Postgres 16
- Админские операции остаются за админом портала: portal прокидывает
`X-User-Is-Admin=1`.
- Отдел видит только свои подразделы, каналы, сообщения и промпты через
`X-User-Department-Id`.
- Руководитель отдела может создавать и редактировать подразделы своего отдела:
portal прокидывает `X-User-Is-Department-Head=1`.
- Пароли подразделов и IP allowlist удалены.
## Конфигурация
Основные переменные:
```env
TG_API_ID=
TG_API_HASH=
TG_PHONE=
TG_SESSION_STRING=
POSTGRES_HOST=postgres.monitoring-tg.svc.cluster.local
POSTGRES_PORT=5432
POSTGRES_USER=parser
POSTGRES_PASSWORD=parser
POSTGRES_DB=parser
PUBLIC_BASE_PATH=/api/monitoring-tg
LLM_ENABLED=true
LLM_BASE_URL=http://10.2.3.5:8002
LLM_API_KEY=
LLM_MODEL=qwen2.5-14b
```
Для локальной админской отладки можно задать `ADMIN_PASSWORD`, но в проде доступ
должен идти через портал.
## Запуск в k8s
Манифесты лежат в `k8s/`. Перед применением нужно заполнить `k8s/secrets.yaml`
реальными Telegram-кредами и, при необходимости, `LLM_API_KEY`.
```bash
kubectl apply -k k8s
```
Миграции выполняются entrypoint-ом контейнера перед запуском API.
## Структура
```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/ # миграции
├── api/ FastAPI роуты + Pydantic-схемы
├── db/ SQLAlchemy модели + сессии
├── scheduler/ APScheduler-воркер периодического опроса
├── telegram/ Telethon-клиент
├── web/static/ страницы UI без бандлера
├── config.py pydantic-settings
└── main.py FastAPI lifespan + uvicorn
alembic/ миграции
k8s/ манифесты для портала
```
## Первый запуск (локально, через 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.