Демонстрация: как заставить Google Apps Script (и любую другую среду, не доверяющую корню Минцифры) работать с MAX Bot API. Разворачивается в вашем облаке за десять минут.
Прокси рабочий и полный: проходит весь MAX Bot API, а не отдельные методы, и обходится нулевой конфигурацией. Когда задача перерастает личный контур, добавляются контроль доступа, свой домен и произвольные апстримы — это Provodox.
platform-api2.max.ru подписан корнем Минцифры. Ряд сред исполнения этому корню не
доверяет и не даёт его добавить — сертификаты там зашиты в платформу. В Google Apps
Script прямой вызов падает на TLS-handshake с SSL Error.
Обойти на стороне клиента нечем: своего CA-бандла в такой среде нет, а отключение
проверки не работает — в Apps Script флаг validateHttpsCertificates: false не помогает.
Код правильный, токен правильный, не работает сам TLS-handshake.
Между клиентом и MAX ставится прокси на Yandex Cloud Functions:
Apps Script ──TLS от GlobalSign──▶ API Gateway ──▶ функция ──▶ platform-api2.max.ru
(Минцифры)
Домен шлюза *.apigw.yandexcloud.net выдан GlobalSign — ему доверяют все. Функция
форвардит запрос на MAX, намеренно не проверяя цепочку сертификата апстрима: именно
её клиент и не может проверить сам. Ответ возвращается как есть — статус, заголовки, тело.
Токен бота прокси не хранит и не видит смысла в нём: заголовок Authorization
пробрасывается насквозь, авторизует по-прежнему MAX.
Нужен аккаунт Yandex Cloud и yc CLI. Пошагово — в deploy.md, кратко:
git clone https://github.com/contributor-pw/provodox-lite.git
cd provodox-lite
PKG=$(mktemp -d); mkdir -p "$PKG/src"
cp src/index.js "$PKG/src/index.js"; cp package.json "$PKG/package.json"
yc serverless function create --name provodox-lite
yc serverless function version create --function-name provodox-lite \
--runtime nodejs18 --entrypoint src/index.handler \
--memory 128MB --execution-timeout 120s --source-path "$PKG"
yc serverless function allow-unauthenticated-invoke --name provodox-lite
# далее — API Gateway, см. deploy.mdНастраивать нечего: апстрим зашит в код, переменных окружения нет.
Проверка, что всё живо:
curl -s "https://<GATEWAY_ID>.apigw.yandexcloud.net/me"
# 401 {"code":"verify.token","message":"No access token"}Этот 401 пришёл от MAX — значит запрос прошёл цепочку целиком. Добавьте токен и
получите 200.
Клиент берёт свой код под MAX Bot API и меняет одну вещь — базовый URL:
https://platform-api2.max.ru → https://<GATEWAY_ID>.apigw.yandexcloud.net
Путь, query-параметры, тело, заголовки — без изменений. Токен передаётся в
Authorization: <токен>; передача через query-параметр access_token в MAX больше не
поддерживается.
Основной сценарий. Через прокси — обычный UrlFetchApp, без плясок с сертификатами:
const props = PropertiesService.getScriptProperties();
const BASE = 'https://<GATEWAY_ID>.apigw.yandexcloud.net';
const TOKEN = props.getProperty('MAX_TOKEN'); // токен бота MAX, проверяет апстрим
function getMe() {
const res = UrlFetchApp.fetch(`${BASE}/me`, {
headers: { Authorization: TOKEN },
muteHttpExceptions: true,
});
return JSON.parse(res.getContentText());
}
function sendMessage(chatId, text) {
const res = UrlFetchApp.fetch(`${BASE}/messages?chat_id=${chatId}`, {
method: 'post',
contentType: 'application/json',
headers: { Authorization: TOKEN },
payload: JSON.stringify({ text }),
muteHttpExceptions: true,
});
return JSON.parse(res.getContentText());
}BASE="https://<GATEWAY_ID>.apigw.yandexcloud.net"
curl "$BASE/me" -H "Authorization: $MAX_TOKEN"
curl -X POST "$BASE/messages?chat_id=$CHAT_ID" \
-H "Authorization: $MAX_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"text":"привет"}'Загрузка файла идёт в три шага, и через прокси нужен только первый и третий:
POST /uploads?type=...— через прокси, вернёт upload-URL;- загрузка на выданный URL (
fu.oneme.ru,iu.oneme.ru,vu.okcdn.ru) — напрямую, эти хосты подписаны публично доверенными CA, проксировать их не нужно; POST /messagesсattachments— через прокси.
Lite решает исходную задачу целиком: клиент, не доверяющий Минцифре, работает с MAX. Для личного бота, прототипа и внутренней автоматизации этого достаточно, и настраивать там нечего — что и есть главное его удобство.
Дальше начинаются задачи не про TLS, а про эксплуатацию. Их закрывает Provodox:
| Provodox Lite | Provodox | |
|---|---|---|
| Доступ | по адресу шлюза | ключ на каждого клиента, отзыв по одному |
| Домен | служебный *.apigw.yandexcloud.net |
свой домен и сертификат |
| Апстрим | MAX, задан в коде | любой, меняется без передеплоя |
| Секреты | не требуются | Lockbox, сервис-аккаунт с минимальной ролью |
| Эксплуатация | ваше облако и ваш контроль | развёрнут и обслуживается |
Первая строка — та, ради которой стоит дочитать. Lite не проверяет вызывающего: адрес шлюза и есть весь доступ. Пока адрес знаете только вы, модель работает. Как только он попадёт к кому-то ещё, запросы к MAX пойдут за ваш счёт и с вашего IP — а свободный лимит API Gateway составляет 100 000 запросов в месяц, дальше тарификация. Поэтому адрес не публикуют, а стенд не оставляют включённым без надобности. Там, где адрес нельзя удержать в секрете, нужен контроль доступа.
Весь прокси — один файл на ~140 строк без зависимостей (src/index.js),
читается за пять минут.
Что стоит знать:
- API Gateway обязателен. Голая функция вызывается по
https://functions.yandexcloud.net/<id>, и такой вызов не передаёт путь:/meв этом URL платформа считает частью идентификатора функции и отвечает400 invalid functionID. Пути маршрутизирует шлюз через{path+}. - Ответ буферизуется целиком и возвращается в base64. Это корректно для JSON, gzip и
вложений. Долгие запросы (long polling
GET /updates) работают: тело приходит один раз, в конце. → Можно усилить: потоковая передача ответа откроет SSE и стриминг. - Таймаут — 120 секунд, с запасом на самый долгий вызов MAX (
/updatesпринимаетtimeoutдо 90 с). → Можно усилить: это не предел платформы, функция допускает до 10 минут — поднимается одним флагом при деплое. - Проверка сертификата апстрима отключена (
rejectUnauthorized: false) — сознательно, это и есть смысл прокси. Апстрим при этом ровно один и задан в коде, так что открытым релеем в интернет прокси не становится. → Можно усилить: закрепить апстрим списком разрешённых хостов, если апстрим станет настраиваемым.
MIT — см. LICENSE.