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

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/.

S
Description
MapMil monorepo
Readme
141 KiB
Languages
Vue 46.5%
Python 38.2%
TypeScript 10.6%
CSS 3.7%
HTML 0.5%
Other 0.5%