Files
gitrusprusandCursor 1492576fd9 Add Telegram listener, scheduler, and extended parsers admin UI.
Introduce background scheduling and migrations for analytics ingest, expand parser management and map toolbar UX, and ignore local data/session files from version control.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 20:47:56 +03:00

10 KiB
Raw Permalink Blame History

MapMil Platform (ЦП → ЦА → ПИ)

Единая платформа: ЦА (аналитика и карта) + ЦП (парсинг Telegram).

Архитектура

flowchart LR
  CA[ЦА Analytics Center]
  CP[ЦП Parsing Center]
  PI[ПИ External Consumers]

  CA -->|jobs via Redis| CP
  CP -->|POST /internal/ingest| CA
  CA -->|GET /api/v1/events| PI
Центр Контейнеры Назначение
ЦА ca-db, ca-api, ca-frontend PostgreSQL, ingest API, карта, distribution API
ЦП cp-workers Парсинг Telegram: real-time listener (Telethon) + batch-задания из Redis
Общее redis Очередь заданий ЦА → ЦП

Структура monorepo

MapMil/
├── centers/
│   ├── analytics/
│   │   ├── api/          # CA backend (FastAPI + PostgreSQL)
│   │   └── frontend/     # CA admin UI (Vue + Leaflet)
│   └── parsing/
│       └── workers/      # CP workers (Telethon)
├── contracts/            # Shared schemas (ingest, jobs)
├── data/                 # telegram.session (локально, не в git)
├── docker-compose.yml
└── .env                  # TELEGRAM_API_ID, TELEGRAM_API_HASH, …

Быстрый старт

  1. Создайте .env из примера и укажите ключи Telegram:
cp .env.example .env
# отредактируйте TELEGRAM_API_ID и TELEGRAM_API_HASH
  1. Положите сессию Telegram в data/telegram.session (файл Telethon SQLite). Если мигрируете со старого SocialParser:
cp ../SocialParser/data/telegram.session data/
  1. Запуск:
docker compose up --build
  1. Откройте UI: http://localhost:8080

Admin UI

Веб-интерфейс ЦА доступен по тем же адресу. Навигация в шапке:

Раздел Путь Описание
Карта / Интерактивная карта событий: навигация по датам (flatpickr), пресеты периода, фильтры региона/темы/источника, подложки Яндекс/OSM/Topo/ESRI, линейка, полноэкранный режим, центрирование по координатам и городам, поиск населённых пунктов (Nominatim). CRUD для ручных объектов (ПКМ). Поддерживает ?eventId=
Парсеры /parsers Telegram-парсеры с периодическим запуском, настройка интервала, редактирование и удаление; дубликаты по source_url не записываются
События /events Фильтрация, пагинация, просмотр деталей, ссылка «На карте» для событий с координатами
Аналитика /analytics KPI-карточки, график динамики ingest за 30 дней, топ населённых пунктов и регионов
ПИ /consumers CRUD подписчиков distribution API, ротация ключей, тест среза через /api/v1/events

Сохранение Telegram-сессии

Важно: существующий файл сессии не удаляется и не пересоздаётся.

  • Локально: data/telegram.session (SQLite Telethon, в .gitignore)
  • В Docker cp-workers: каталог ./data монтируется как /data (read-write)
  • Путь в контейнере: /data/telegram.session
  • Переменные из .env: TELEGRAM_API_ID, TELEGRAM_API_HASH, TELEGRAM_SESSION_PATH=/data/telegram.session

Файл сессии и .env не коммитятся.

API

Карта

Метод Путь Описание
GET /api/health Health check
GET /api/map/objects Объекты на карте с данными события; фильтры: date_from, date_to, on_date, region, topic, source_type, search, event_id
GET /api/map/filters Справочники для фильтров: regions, topics, source_types, available_dates, date_bounds
GET /api/objects Объекты (legacy, без фильтров события)
POST /api/objects Создать объект вручную

Инструменты карты (UI)

  • Даты: кнопки ❮❮/❮/❯/❯❯, календарь flatpickr, пресеты периода (1 неделя — 1 год / день)
  • Фильтры: регион, тема, источник (из /api/map/filters)
  • Подложки: Яндекс Карты / Спутник (ru_RU), OpenStreetMap, OpenTopoMap, ESRI Satellite
  • Инструменты: линейка (PolylineMeasure), полноэкранный режим, координаты/города, поиск Nominatim
  • Точки: маркеры из ingest + popup; ручные объекты — CRUD и drag
  • Обновление: polling /api/map/objects каждые 30 с

ЦА Admin

Метод Путь Описание
POST /admin/jobs Создать парсер (сразу в очередь + периодический запуск)
GET /admin/jobs Список парсеров
PATCH /admin/jobs/{id} Изменить канал/лимит, interval_seconds, is_active
DELETE /admin/jobs/{id} Удалить парсер
POST /admin/jobs/{id}/retry Повторить failed-задание
GET /admin/events События с фильтрами ({ items, total })
GET /admin/analytics/summary KPI-сводка
GET /admin/analytics/timeline?days= Динамика ingest по дням
GET /admin/analytics/top-localities?limit= Топ населённых пунктов
GET /admin/analytics/top-regions?limit= Топ регионов
GET /admin/consumers Список подписчиков ПИ
POST /admin/consumers Создать подписчика ПИ
PATCH /admin/consumers/{id} Обновить подписчика
POST /admin/consumers/{id}/rotate-key Сменить API-ключ

Пример задания Telegram:

curl -X POST http://localhost:8080/admin/jobs \
  -H 'Content-Type: application/json' \
  -d '{"source_type":"telegram","source_config":{"channel":"creamy_caprice","limit":50}}'

ЦП → ЦА (internal)

Метод Путь Описание
POST /internal/ingest Приём batch событий (заголовок X-Internal-Token)

ПИ Distribution API

Метод Путь Описание
GET /api/v1/events События с фильтром по API-ключу

Тестовый consumer test-pi создаётся при старте с ключом из TEST_PI_API_KEY (по умолчанию test-pi-api-key-change-me):

curl http://localhost:8080/api/v1/events \
  -H 'Authorization: Bearer test-pi-api-key-change-me'

Парсинг Telegram

cp-workers объединяет два режима в одном процессе (общая сессия telegram.session):

Режим Как работает
Listener (real-time) Telethon NewMessage / Album на активных парсерах (is_active=true); новый пост сразу уходит в ingest
Batch (по расписанию) Планировщик в ca-api ставит задание в Redis; воркер забирает последние N постов (iter_messages)

Дубликаты по source_url при ingest пропускаются. Listener не меняет status парсера (флаг listener: true в ingest).

Переменные cp-workers:

Переменная По умолчанию Описание
TELEGRAM_LISTENER_ENABLED true Включить real-time listener
TELEGRAM_LISTENER_REFRESH_SECONDS 60 Как часто обновлять список каналов из БД

Internal API: GET /internal/listener/subscriptions — список активных каналов для listener.

Поток данных

  1. Аналитик создаёт парсер: POST /admin/jobs (канал, лимит, интервал в секундах)
  2. Listener сразу подписывается на канал и ingest-ит новые посты
  3. Планировщик ca-api периодически ставит batch-задание в Redis (cp:jobs)
  4. cp-workers забирает batch-задание, парсит последние N постов
  5. Результаты → POST /internal/ingest (дубликаты по source_url пропускаются)
  6. Новые события с координатами появляются на карте как MapObject
  7. Внешние ПИ получают срез через /api/v1/events

Миграция EventRecord → Event

SocialParser CA Event
event description / title
date (dd.mm.yy) event_date
geolocation latitude, longitude
locality locality, region
source_url source_url
source_type = "telegram"

Остановка

docker compose down

Данные PostgreSQL сохраняются в volume pgdata.

Legacy

Старые каталоги backend/ и frontend/ в корне оставлены для справки; активная разработка — в centers/.