Яндекс.Еда для Евы: eva-sdk модуль (Rust) + реверс протокола и дизайн Connections/WebView-драйвера
  • Rust 94.2%
  • JavaScript 3.7%
  • Nix 2.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Aleksandr 08b4e65a44 Вход: regex ожидания redirect не матчит authorize-страницу
`verification_code` в url_matches ловил сам authorize_url (там
redirect_uri=…/verification_code), поэтому wait срабатывал мгновенно на
стартовой странице — вход падал с «в URL нет access_token». Сузил до
access_token= / ?code= / якорь ^…/verification_code; источник токена —
сматченный url (фрагмент redirect), eval location.href как фолбэк.

Co-Authored-By: Eva
2026-08-30 21:59:51 +03:00
crates/eda Вход: regex ожидания redirect не матчит authorize-страницу 2026-08-30 21:59:51 +03:00
spec Первый коммит: MCP-сервер Яндекс.Еды 2026-08-30 11:57:52 +03:00
.env.example Переход на per-module KV-store + eva::App + mkServiceFlake 2026-08-30 17:06:16 +03:00
.gitignore Переезд с Python-MCP на Rust eva-sdk модуль 2026-08-30 15:51:50 +03:00
AGENTS.md Переход на per-module KV-store + eva::App + mkServiceFlake 2026-08-30 17:06:16 +03:00
Cargo.lock Переход на per-module KV-store + eva::App + mkServiceFlake 2026-08-30 17:06:16 +03:00
Cargo.toml Переезд с Python-MCP на Rust eva-sdk модуль 2026-08-30 15:51:50 +03:00
CHANGELOG.md Переход на per-module KV-store + eva::App + mkServiceFlake 2026-08-30 17:06:16 +03:00
flake.lock Переход на per-module KV-store + eva::App + mkServiceFlake 2026-08-30 17:06:16 +03:00
flake.nix Переход на per-module KV-store + eva::App + mkServiceFlake 2026-08-30 17:06:16 +03:00
README.md Переход на per-module KV-store + eva::App + mkServiceFlake 2026-08-30 17:06:16 +03:00

eva-eda

Модуль Яндекс.Еды для ядра Евы (на eva-sdk, как telegram-bridge): поиск заведений, меню, сбор корзины, оформление заказа и трекинг статуса — от лица аккаунта Господина. Отдельный процесс: отдаёт инструменты ядру по UDS, а туры ведёт через HTTP API ядра.

Реверс протокола и дизайн auth — в spec/. Артефакты декомпила (jadx-out/, apktool-out/, xapk/, libs/) в git не хранятся.

Auth: KV-стор + module-driven вход

Токен живёт в per-module KV-сторе ядра (Kernel::store("eda"), ключ token) — сериализованным JSON'ом (access_token, опц. token_type, expires_at, refresh_token, scope). На каждый вызов модуль читает его из стора. Если токена нет, он на пороге истечения (expires_at) или пришёл 401, модуль добывает его заново и кладёт обратно в стор:

  1. OAuth-refresh — если у токена есть refresh_token И заданы eda.client_id/client_secret/token_url (POST token_url grant_type=refresh_token). У implicit-флоу Яндекса (response_type=token) refresh_token обычно нет — тогда сразу шаг 2.
  2. Переоткрыть вход через webview машины Господина: открыть eda.authorize_url (ephemeral:false — переиспользует уже залогиненный в Яндексе аккаунт, куки на месте → быстро), дождаться редиректа (url_matches: access_token=|verification_code|[?&]code=), прочитать location.href, распарсить access_token/expires_in/refresh_token из фрагмента/query.

При 401 токен добывается заново, переигрывается /4.0/startup, запрос повторяется один раз. Схему Authorization: OAuth <token> клиент ставит сам. Токен нигде не логируется.

Инструменты

Инструмент Что Эндпоинт
eda_profile профиль + проверка токена GET api/v1/user/profile
eda_addresses сохранённые адреса доставки GET api/v3/user/addresses
eda_restaurants заведения по координатам (+фильтр) POST api/v2/catalog
eda_menu меню заведения по slug GET api/v2/menu/retrieve/{slug}
eda_cart_add положить позиции в корзину POST api/v1/cart/add_bulk
eda_cart текущая корзина: состав, сумма, адрес GET api/v2/cart
eda_cart_clear очистить корзину POST api/v1/cart/change_place
eda_checkout оформить заказ (тратит деньги) POST api/v2/cart/go-checkout
eda_orders статус заказов GET api/v1/orders[/{id}]

Оформление — только с подтверждением

eda_checkout тратит реальные деньги и необратим. Он требует confirm=true; без него возвращает текущую корзину и отказ. Порядок: собрать корзину → показать состав + сумму + адрес → получить явное «да» Господина → eda_checkout(confirm=true).

Конфигурация

Каркас — фреймворк eva: конфиг читается из --config-файлов (YAML/JSON/TOML), секция eda. Env перекрывают файлы: EVA_ + путь через __ (Env::prefixed("EVA_").split("__")), напр. EVA_EDA__CLIENT_SECRETeda.client_secret.

Ключ секции eda Назначение
socket UDS с инструментами (дефолт /run/eva-mcp/eda.sock)
kernel_url база API ядра (дефолт http://127.0.0.1:8090) — KV-стор и webview
authorize_url страница авторизации для webview-входа; пусто — автовход недоступен
lat / lon координаты доставки по умолчанию
lang язык ответов API (дефолт ru)
device_id стабильный device id (иначе выводится из хоста)
client_id / client_secret / token_url опц. — OAuth-refresh (все три)

client_secret — секрет: не клади в несекретный --config/nix-settings, а задавай через env (EVA_EDA__CLIENT_SECRET, systemd environmentFiles) или секретный --config в extraConfigFiles. Хост API переопределяется env YANDEX_EDA_API_HOST (дефолт — прод authproxy). Пример конфига — .env.example (env-форма для systemd EnvironmentFile).

Сборка и запуск

nix develop -c cargo build                 # dev-сборка в devShell
nix build .#eva-eda                         # пакет (crane, без ручных хэшей)
./result/bin/eva-eda --config eda.yaml      # запуск с конфигом

Ядро цепляется к сокету само, если тот лежит в modules.socket_dir ядра или объявлен в modules.servers.

Nix

flake.nix собран на eva.lib.mkServiceFlake (crane): даёт packages.eva-eda, devShells.default и nixosModules.default (services.eva-eda). Git-депы (eva, eva-sdk) crane вендорит из Cargo.lock сам — ручных outputHashes нет; смена rev SDK хэш-танца не требует. Секреты в модуле — через services.eva-eda.environmentFiles (EVA_EDA__CLIENT_SECRET=...) или extraConfigFiles.

Статус протокола

Пути эндпоинтов и заголовки восстановлены из декомпила ru.foodfox.client v26.33.0 (см. spec/20-yandex-eda-api.md). Точные тела запросов (add_bulk, go-checkout, change_place, catalog) спека велит доснять живым трафиком (mitmproxy на один прогон) — в коде они best-effort и помечены комментарием; парсинг ответов терпим к расхождению схемы. При первом реальном заказе тела стоит сверить и поправить client.rs/tools.rs.