# Подключение будущего бота MAX Серверная часть готова к подключению. Сам бот ещё не создан, секреты не заполнены, реальная отправка не включена. Изменений интерфейса сайта в этой поставке нет. Интеграция уведомляет **самого пациента**; врачебные оповещения остаются в существующем канале проекта. ## Что реализовано - Одноразовая ссылка для привязки аккаунта MAX к текущему пациенту сайта, срок по умолчанию 10 минут. - Подтверждение подключения, `/help`, `/stop`, отключение при остановке или удалении бота в MAX. - Нейтральные напоминания из существующего планировщика сайта: тесты, дневник, приём лекарств и другие события outbox. - Уведомление о завершении теста: сервер каждые примерно 30 секунд находит новые завершённые `QuestionnaireRecord` и `ScaleResult`, начиная с момента подключения MAX. Незавершённые и старые тесты не отправляются. - Постоянная очередь, восстановление после перезапуска, ограниченные повторы при сетевых ошибках и ответах 429/5xx. - В сообщении только короткий текст и кнопка на сайт. Ответы, оценки, диагнозы, назначения и ключ входа на сайт в MAX не передаются. Переписка с ботом не сохраняется и не отправляется в LLM. ## 1. Создать бота — будущему владельцу 1. Открыть [MAX для партнёров](https://business.max.ru/) и пройти регистрацию/проверку профиля. Условия допуска зависят от типа владельца; актуальный порядок — в [официальной инструкции подключения](https://dev.max.ru/help/platform_connection). 2. Создать чат-бота, оформить описание и пройти требуемую MAX модерацию. 3. В настройках бота получить токен и скопировать имя из его публичной ссылки `https://max.ru/ИМЯ`. 4. Передать администратору сервера имя бота, а токен — через защищённое хранилище секретов. Токен не нужен в переписке, MR или репозитории. ## 2. Заполнить настройки сервера Нужны доступная identity/clinical БД проекта, публичный HTTPS-адрес сайта и HTTPS-адрес API на порту 443. Входящие POST от MAX должны доходить до API без браузерной CAPTCHA или страницы авторизации. Прокси должен передавать заголовок `X-Max-Bot-Api-Secret` и не журналировать тело webhook/Authorization. Дополнительные таблицы identity создаются существующим `init_db()` при запуске приложения. В `.env` сервера (вне Git) заполнить: ```dotenv MAX_ENABLED=false MAX_BOT_TOKEN= MAX_BOT_USERNAME= MAX_WEBHOOK_SECRET= MAX_WEBHOOK_URL=https://api.example.org/api/v1/webhooks/max MAX_SITE_URL=https://patient.example.org/ MAX_LINK_TTL_SECONDS=600 # При необходимости — PEM-файл доверенного CA, доступный внутри контейнера: MAX_CA_BUNDLE= DEMO_CLOCK=false ``` - `MAX_BOT_USERNAME` — только имя, без `@`, домена или параметров ссылки. - `MAX_WEBHOOK_SECRET` — отдельная случайная строка, не токен бота: 32–256 символов из `A–Z a–z 0–9 _ -`. Сгенерировать локально, например `python -c 'import secrets; print(secrets.token_urlsafe(32))'`, сохранить в секреты. - `MAX_SITE_URL` ведёт в обычный личный кабинет; не добавлять в адрес ключи доступа или данные пациента. - Исходящие запросы идут на `https://platform-api2.max.ru`, токен передаётся заголовком `Authorization`. Верификация TLS всегда включена. Если системе нужен корневой сертификат Минцифры, установить его по официальной инструкции MAX и указать PEM-путь в `MAX_CA_BUNDLE`; не отключать проверку TLS. - Переменные должны попасть именно в окружение API-контейнера/процесса, а не только панели сайта. В текущем Compose сервис `app` уже получает `.env` через `env_file`. После изменения окружения пересоздать процесс/контейнер API. Новые зависимости не требуются. ## 3. Проверить и включить В каталоге проекта, в том же окружении, где работает API: ```sh uv run python -m app.channels.max_setup check ``` Команда проверит токен и совпадение имени бота. Сообщений пользователям она не отправляет. Затем установить `MAX_ENABLED=true`, перезапустить API и выполнить: ```sh uv run python -m app.channels.max_setup subscribe uv run python -m app.channels.max_setup status ``` `subscribe` регистрирует только заданный webhook с секретом и событиями `bot_started`, `message_created`, `bot_stopped`, `dialog_removed`. Старые чужие подписки автоматически не удаляются: при переносе сервера владелец должен проверить их отдельно через MAX API. `status` проверяет регистрацию адреса; это ещё не проверка доступности сервера из MAX. Команды не печатают токены, секрет webhook или сырые ответы платформы. При запуске через Compose вместо `uv run python ...` использовать `docker compose exec app python -m app.channels.max_setup check` (также `subscribe` и `status`). После изменения `.env` пересоздать API: `docker compose up -d --build --force-recreate app`. Запускать один процесс API с MAX на один токен бота: его worker отправляет до одного сообщения в секунду. БД защищает очередь от параллельного захвата, но общий лимит скорости нескольких процессов не координируется. Планировщик и MAX worker запускаются вместе с FastAPI, отдельный процесс бота не нужен. При `MAX_ENABLED=false` сетевых вызовов MAX и фонового worker нет; сайт продолжает обычную работу. ## API для разработчика сайта В проекте используется ключ текущего пациента, полученный при входе. Для этих ручек передавать его как `Authorization: Bearer `; это существующая авторизация прототипа, не новая система SSO. Не брать token другого пациента и не публиковать демонстрационные учётные записи для реального использования. | Метод и путь | Запрос | Ответ | |---|---|---| | `GET /api/v1/me/notifications/max` | Authorization | `available`, `connected`, `last_delivery` | | `POST /api/v1/me/notifications/max/link` | Authorization; JSON `{"consent": true}` | `url`, `expires_at` | | `DELETE /api/v1/me/notifications/max` | Authorization | состояние с `connected: false` | | `POST /api/v1/webhooks/max` | MAX JSON + `X-Max-Bot-Api-Secret` | `{"success": true}` после записи события | Поток подключения: пациент явно соглашается на уведомления → сайт получает `url` → открывает ссылку MAX → пользователь запускает бота → webhook подтверждает привязку → повторный GET показывает `connected: true`. Ссылка одноразовая; новая ссылка отменяет предыдущую. Изначальный ключ пациента не входит в URL MAX. Для привязки и доставки дополнительно нужны действующее согласие и подтверждение совершеннолетия в состоянии пациента. Активную привязку нельзя молча переназначить другому аккаунту: сначала отключить её. Коды ошибок: 401 — неизвестный/отсутствующий ключ пациента; 403 — нет согласия или неверный секрет webhook; 422 — неверный запрос; 413 — webhook больше 64 КиБ; 503 — MAX выключен или хранилище события недоступно. Повтор webhook с тем же идентификатором события не создаёт новых отправок. Групповые чаты игнорируются. `last_delivery`: `queued`, `sending`, `sent`, `failed`, `cancelled` или `null`. `sent` означает принятие сообщения API MAX, а не прочтение человеком. ## Пробное подключение после создания бота 1. Войти на сайте синтетическим пациентом и дать необходимые согласия. 2. Получить ссылку через POST выше (Swagger `/docs` или будущий интерфейс сайта), перейти в MAX и запустить бота. 3. Убедиться, что GET показывает `connected: true` и приходит подтверждение. 4. Завершить тест на сайте; примерно через 30 секунд плюс очередь должно прийти сообщение без результатов. 5. Дождаться события планировщика и проверить нейтральное напоминание с кнопкой на сайт. 6. Отправить `/stop`: GET должен показать `connected: false`, ожидающие уведомления отменяются. 7. Повторить подключение новой ссылкой, проверить DELETE через API и остановку бота через интерфейс MAX. При недоставке проверить настройки и подписку, доступность HTTPS, состояние очереди и коды ошибок в логах. `http_401`/`http_403` требуют проверки токена/прав; `http_429` — ожидания; `transport` — сети/TLS; `failed` после шести попыток требует вмешательства администратора. Детали ответов и идентификаторы в логи не выводятся. Отключение отменяет очередь; уже принятый MAX запрос отозвать невозможно. ## Гарантии и ограничения Очередь и подтверждения событий сохраняются в identity БД. Клинические записи остаются в clinical БД. Для событий завершения тестов БД повторно просматривается после перезапуска; ключи уведомлений предотвращают повторную постановку. Напоминания используют существующий механизм outbox/повторов планировщика. При временных ошибках — до 6 попыток с увеличением задержки и учётом `Retry-After`; зависшая отправка получает новую аренду через 60 секунд. Потеря процесса между принятием сообщения MAX и записью успеха в БД может привести к повторному сообщению: MAX не предоставляет ключ идемпотентности отправки. Не удалять строки дедупликации/очереди без согласованной политики хранения. При большом количестве пациентов нужны пакетное сканирование завершённых тестов и общий ограничитель скорости; текущая реализация рассчитана на MVP. Проверки разработчика: `make lint test`; тесты MAX работают с синтетикой и подменённым HTTP-транспортом. Реальную доставку подтвердит только пробное подключение после выдачи токена и настройки публичного сервера. Официальные контракты: [отправка сообщений](https://dev.max.ru/docs-api/methods/POST/messages), [подписка webhook](https://dev.max.ru/docs-api/methods/POST/subscriptions), [события](https://dev.max.ru/docs-api/objects/Update), [ссылки запуска бота](https://dev.max.ru/docs/chatbots/bots-coding/prepare).