Files
ridebus-admin/docs/telegram-cancellation-import.md
Zakhar Timoshenko f7f0b039dd Add Telegram cancellation import functionality with tests and documentation
- Implemented Telegram cancellation parsing logic in `telegramCancellationParser.js`.
- Created unit tests for cancellation parsing in `telegramCancellationParser.test.js`.
- Developed integration tests for Telegram import service in `telegramImport.integration.test.js`.
- Added API tests for Telegram import endpoints in `telegramImportApi.test.js`.
- Introduced service tests for Telegram import logic in `telegramImportService.test.js`.
- Documented the Telegram cancellation import process in `telegram-cancellation-import.md`.
- Built a React component for managing Telegram imports in `TelegramImportPanel.jsx`.
2026-10-03 00:45:41 +03:00

12 KiB
Raw Blame History

Отмены рейсов из @avp2Polotsk

Первый этап читает публичный канал через отдельный существующий Telegram-аккаунт и MTProto-клиент teleproto. Права управления каналом и Bot API не нужны. Получение выполняется раз в минуту; просматриваются публикации за текущий и два предыдущих дня, включая правки. Вложения не скачиваются. Расписания рейсов не изменяются: импорт создаёт предупреждения существующего типа cancellation.

Распознавание и срок действия

Формат «Не будут выполняться рейсы: ПО МАРШРУТУ № 27 С ОСТАНОВКИ АВ/ВОКЗАЛ НА 15.43,16.43. С ОСТАНОВКИ ЗОДЧЕГО НА 15.58,16.58.» разбирается в четыре конкретных отправления. АВ/ВОКЗАЛ означает Автовокзал, точки в часах заменяются двоеточием. Отмена всего маршрута из такого объявления не выводится.

Если у номера несколько направлений, они сопоставляются по начальной остановке в базе. Неоднозначный номер, неизвестное направление, разные даты, условия, периоды и изображения без текста требуют проверки администратором. Обычные новости не импортируются. Пока нет OCR, распознавания произвольных формулировок и автоматического редактирования расписаний.

Явная дата, «сегодня» или «завтра» определяют день отмены. Если дата отсутствует, используется день оригинальной публикации по Europe/Minsk, а не день импорта или правки. Дата без года выбирается ближайшей к публикации. Конец действия — 00:00 следующего дня по Минску, граница исключительная. Например, для 02.10.2026 это 2026-10-02T21:00:00Z. Будущие отмены активируются в начале указанного дня и получают push тогда же. Просроченные объявления сохраняются как завершённые без push.

Новые однозначные отмены публикуются автоматически. TELEGRAM_AUTO_PUBLISH=false переводит все распознанные отмены на проверку. В админке на странице «Уведомления» → «Отмены из Telegram» доступны состояние подключения, ручная проверка канала, исходные сообщения, ссылки на оригинал и история. При ручной публикации оператор проверяет маршрут, текст и день отмены; срок до полуночи назначается сервером.

Установка

Для существующей базы применить миграцию 013 один раз, до запуска новой версии backend. Она также переводит UTC-значения времени существующих алертов из timestamp в timestamptz. Это соответствует принятому формату хранения UTC в админке. При наличии вручную внесённых локальных времен сначала проверить их и сделать резервную копию базы.

docker compose exec -T postgres psql -U transit_admin -d polotsk_transit \
  -f /docker-entrypoint-initdb.d/013_telegram_alert_import.sql

На чистой базе Docker применяет миграцию автоматически. Не запускайте npm run migrate на существующей базе: старые init-миграции проекта не поддерживают повторный запуск.

Авторизация аккаунта

  1. Создать API-приложение на my.telegram.org и получить api_id и api_hash.

  2. На компьютере/сервере оператора создать закрытый каталог вне checkout. Пример для локального запуска:

    mkdir -p "$HOME/.config/polotsk-transit-telegram"
    chmod 700 "$HOME/.config/polotsk-transit-telegram"
    
  3. В backend/.env указать:

    TELEGRAM_IMPORT_ENABLED=false
    TELEGRAM_API_ID=your_api_id
    TELEGRAM_API_HASH=your_api_hash
    TELEGRAM_SESSION_FILE=/absolute/private/directory/account.session
    TELEGRAM_CHANNEL=avp2Polotsk
    TELEGRAM_CITY_SLUG=polotsk
    TELEGRAM_AUTO_PUBLISH=true
    TELEGRAM_POLL_INTERVAL_MS=60000
    
  4. Из каталога backend выполнить npm ci, затем npm run telegram:login. Номер, код и пароль вводятся оператором локально. Код и пароль скрыты; session string не печатается. Утилита сохраняет файл с правами 600 и работает только с существующим пользовательским аккаунтом. Если Telegram требует регистрацию/дополнительный неподдерживаемый шаг, завершить его в официальном клиенте и повторить авторизацию.

  5. Включить TELEGRAM_IMPORT_ENABLED=true и перезапустить backend. Для push также должны быть настроены Firebase и FCM_ENABLED=true.

Файл сессии даёт доступ к аккаунту. Его нельзя коммитить, передавать frontend или присылать в чат. Backend не выполняет интерактивную авторизацию; отсутствующая/отозванная сессия отображается как ошибка подключения. В статус и логи не попадают API hash, сессия, телефон, коды или сырые ошибки клиента.

Docker / Proxmox

Авторизацию выполнить на доверенном хосте. Поместить созданный account.session в закрытый каталог на Docker-хосте, например /opt/polotsk-transit/telegram-secrets, с правами 600. В корневом .env настроить:

TELEGRAM_SECRETS_DIR=/opt/polotsk-transit/telegram-secrets
TELEGRAM_SESSION_FILE=/run/telegram-secrets/account.session
TELEGRAM_API_ID=your_api_id
TELEGRAM_API_HASH=your_api_hash
TELEGRAM_IMPORT_ENABLED=true
TELEGRAM_AUTO_PUBLISH=true

Compose монтирует каталог только для чтения, отдельно от Firebase. После миграции выполнить docker compose up -d --build backend frontend.

Повторы, правки и доставка

Идентичность публикации: город + числовой ID канала + ID сообщения. Снимок текста, результат распознавания и связи с алертами сохраняются одной транзакцией; повторный импорт и одновременные читатели не создают второй алерт. Более старая правка не заменяет свежую. Администратор публикует только проверенную версию по content_hash; изменившийся текст даёт 409 и требует нового просмотра.

Правка публикации обновляет связанные алерты. Если новая версия неоднозначна или больше не распознаётся как отмена, прежние алерты выключаются и сообщение возвращается на проверку. Отклонение сохраняется при последующих правках. Удалённый администратором алерт не воссоздаётся при правке исходного сообщения. Автоудаление сообщения в Telegram не отменяет предупреждение раньше полуночи; удаление не трактуется как восстановление движения.

Импорт помечает алерты как notification_pending в той же транзакции. Существующий scheduler отправляет их после начала действия, использует блокировки и до пяти попыток; Firebase отключён — ожидание сохраняется. Уже доставленный push при правке того же алерта не отправляется повторно. Остаётся обычное окно между приёмом FCM и записью результата в БД: при аварии в этот момент возможен повтор доставки. alertId позволяет клиенту показывать один экземпляр уведомления.

FCM TTL ограничен остатком срока действия алерта; истёкший алерт не отправляется. Backend передаёт абсолютный endTime. Удаление уже показанного уведомления из системной шторки требует поддержки Android-клиента: проверить endTime при получении, не показывать просроченный push, поставить setTimeoutAfter(endTime-now) и исключать истёкшие алерты из локального списка. Изменения Android-клиента не входят в этот этап backend/админки. Правки и отключение алерта не отзывают уже показанный push.

При FloodWait worker выдерживает паузу, не запускает параллельные циклы и отображает время следующей попытки. За один цикл лимит составляет 1000 публикаций; превышение отображается как ошибка, а не как успешное чтение всей истории. Сообщения, исчезнувшие из Telegram во время длительного простоя сервера, восстановить нельзя.

API и проверка

Все endpoint требуют JWT администратора и учитывают X-City-Id:

  • GET /api/v1/telegram-import — безопасный статус и до 100 сообщений, сначала требующие проверки.
  • POST /api/v1/telegram-import/sync — чтение настроенного канала; запрещено в другом городе.
  • POST /api/v1/telegram-import/:id/review — content_hash, action: publish|dismiss; для публикации также service_date, route_id, title, message.

Обычные unit-тесты: cd backend && npm test. SQL-тесты используют временный PostgreSQL runtime PGlite и не обращаются к рабочей базе:

npm install --prefix /tmp/polotsk-telegram-test-runtime @electric-sql/pglite --ignore-scripts
TELEGRAM_TEST_PGLITE_MODULE=/tmp/polotsk-telegram-test-runtime/node_modules/@electric-sql/pglite \
  node --test test/telegramImport.integration.test.js

Реальное чтение Telegram и доставка на устройство проверяются после локальной авторизации аккаунта, применения миграции и включения Firebase.