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

98 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Отмены рейсов из @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 в админке. При наличии вручную внесённых локальных времен сначала проверить их и сделать резервную копию базы.
```bash
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](https://my.telegram.org) и получить `api_id` и `api_hash`.
2. На компьютере/сервере оператора создать закрытый каталог **вне checkout**. Пример для локального запуска:
```bash
mkdir -p "$HOME/.config/polotsk-transit-telegram"
chmod 700 "$HOME/.config/polotsk-transit-telegram"
```
3. В `backend/.env` указать:
```dotenv
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` настроить:
```dotenv
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 и не обращаются к рабочей базе:
```bash
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.