Перейти к содержанию

HTTP API (служебные эндпоинты)

Этот документ описывает служебные HTTP‑маршруты aiohttp‑приложения Telegram‑бота (telegram-bot/main.py). Эти маршруты вызываются другими сервисами (в первую очередь MCP‑сервером) для уведомлений и доставки документов.

Базовые требования к переменным окружения и запуску описаны в разделе Конфигурация и окружение.

Note

Эти эндпоинты задуманы как внутренние (межсервисные): обычно они доступны только в приватной сети/локально и вызываются MCP‑сервером.

Warning

Авторизации на уровне приложения нет. Если по ошибке опубликовать эти маршруты наружу (например, через публичный ingress/reverse‑proxy), их можно будет вызвать без токена. Убедитесь, что они недоступны извне (firewall/allowlist, private network и т. п.).

Tip

Частый сценарий: webhook Telegram нужно открыть наружу, а служебные эндпоинты — нет.
В таком случае наружу прокидывают только WEBHOOK_SECRET_PATH (и иногда /health), а доступ к /cargo-assigned и /cargo-docx ограничивают по сети (internal ingress / allowlist / отдельный порт).

GET /health и GET /

Назначение: health‑check сервиса.

Ответ (200):

{ "status": "ok", "service": "telegram-bot" }

POST /cargo-assigned (уведомление клиенту)

Кто вызывает: MCP‑сервер, когда по заявке клиента найден/подтверждён перевозчик (см. mcp-server/prime_lardi/notifications.py).

Что делает обработчик: генерирует текст уведомления через OpenAI и отправляет его в Telegram Business‑чат клиенту.

Требования к окружению

  • BUSINESS_CONNECTION_ID должен быть задан (иначе сервис не сможет отправлять проактивные сообщения в бизнес‑чат и вернёт 422).
  • OPENAI_API_KEY должен быть задан и рабочий (иначе генерация текста упадёт и будет 500).

Что важно знать (поможет избежать сюрпризов)

  • Этот эндпоинт вызывает OpenAI для генерации текста → это тратит токены.
  • Успешный вызов не только отправляет сообщение, но и может записать служебный контекст в БД:
  • обновляет user_state.doc_flow (включает режим документов и сохраняет doc_context);
  • добавляет запись в dialogue_history (в том числе content_type="system" с doc_context).
  • Если Telegram вернул 403 при отправке (например, бизнес‑соединение устарело), эндпоинт вернёт 500 с сообщением вида: business connection expired, нужен новый входящий месседж.
  • BUSINESS_CONNECTION_ID берётся не «из воздуха»: это значение из Telegram Business. Его можно увидеть во входящих business_message как поле business_connection_id (удобнее всего — включить DEBUG‑логирование и посмотреть payload обновления).

Запрос

  • Content‑Type: application/json
  • Тело запроса: JSON‑объект
Поле Тип Обязательное Описание
tg_id int да Telegram chat id клиента (положительное число).
request_id int да Идентификатор заявки (положительное число).
cargo object да Контекст заявки (см. ниже).

Минимально обязательные поля внутри cargo:

Поле Тип Обязательное Описание
content_name string да Название груза.
source_towns string[] да Города загрузки (непустой список непустых строк).
target_towns string[] да Города выгрузки (непустой список непустых строк).
payment_value number да Сумма оплаты (число).
currency_name string да Валюта оплаты.

Ответ

200 OK (уведомление отправлено):

{ "ok": true, "chat_id": 123456789, "request_id": 100500 }

Ошибки:

  • 400 — невалидный JSON или тело запроса не объект.
  • 422 — не пройдена валидация полей (tg_id, request_id, обязательные поля внутри cargo), либо отсутствует BUSINESS_CONNECTION_ID.
  • 500 — ошибка генерации текста или отправки сообщения в Telegram.

Формат ошибок (обратите внимание на ключ error):

{ "ok": false, "success": false, "error": "..." }

Пример curl

curl -sS -X POST 'http://localhost:8081/cargo-assigned' \\
  -H 'Content-Type: application/json' \\
  -d '{
    "tg_id": 123456789,
    "request_id": 100500,
    "cargo": {
      "content_name": "Палети",
      "source_towns": ["Київ"],
      "target_towns": ["Львів"],
      "payment_value": 25000,
      "currency_name": "UAH"Ы
    }
  }'

POST /cargo-docx (доставка DOCX пользователю)

Кто вызывает: MCP‑сервер из инструмента send_docx_form (см. mcp-server/prime_lardi/docx_sender.py).

Что делает обработчик: принимает DOCX как файл, отправляет его пользователю и копию ответственному аккаунту через Telethon.

Требования к окружению

  • RESPONSIBLE_TG_ID должен быть задан (иначе 500).
  • Telethon должен быть настроен и успешно инициализирован при старте сервиса:
  • TELETHON_API_ID, TELETHON_API_HASH, TELETHON_SESSION;
  • сессия должна быть авторизована (иначе отправка файлов упадёт).

Что важно знать (поможет избежать сюрпризов)

  • Эндпоинт реально отправляет файл в Telegram через Telethon (это отправка «как пользователь», а не как бот).
  • Telethon может вернуть 422 recipient not found in telethon dialogs, если аккаунт Telethon не видит получателя в своих диалогах. Обычно это значит, что с этим пользователем ещё не было переписки/контакта в аккаунте Telethon.
  • При успешной отправке сервис также обновляет user_state.doc_flow.last_docx_sent_at (если удалось найти пользователя по tg_id).
  • Если RESPONSIBLE_TG_ID задан и отличается от tg_id, копия DOCX отправится ответственному аккаунту. Это тоже может упасть с ошибкой Telethon.

Запрос

  • Content‑Type: multipart/form-data
  • Поля формы:
Поле Тип Обязательное Описание
tg_id string да Telegram chat id клиента (будет приведён к int).
file файл да DOCX файл (любые байты; MIME проверкой не ограничен).
caption string нет Подпись к файлу в Telegram.
request_id string нет Идентификатор заявки (сейчас используется только для логов).

Ответ

200 OK (файл отправлен):

{ "success": true, "message": "Sent", "chat_id": 123456789 }

Ошибки:

  • 400 — некорректный multipart.
  • 422 — невалидный tg_id, отсутствует file или файл пустой, либо Telethon не нашёл получателя в диалогах.
  • 500 — Telethon/отправка упали, либо не настроен RESPONSIBLE_TG_ID.

Формат ошибок (обратите внимание на ключ message):

{ "ok": false, "success": false, "message": "..." }

Пример curl

curl -sS -X POST 'http://localhost:8081/cargo-docx' \\
  -F 'tg_id=123456789' \\
  -F 'caption=DOCX по заявке' \\
  -F 'request_id=100500' \\
  -F 'file=@./example.docx'