- register_max_webhook.py, fetch_schema.py - n8n-code-node-max-normalize.js (max_id, callback из callback.user, contact из vcf_info) - n8n-code-add-menu-buttons.js (меню с callback, request_contact, Главное меню) - docs: max-webhook, max-curl-http-request, max-api (форматы, кнопки, контакт), clpr vs sprf - README, SITUATION, схемы sprf_ и clpr_, .gitignore Co-authored-by: Cursor <cursoragent@cursor.com>
194 lines
8.4 KiB
Markdown
194 lines
8.4 KiB
Markdown
# Форматы текста и кнопки (MAX Bot API)
|
||
|
||
## Форматы текста (поле `format` в теле сообщения)
|
||
|
||
В **NewMessageBody** укажи `"format": "markdown"` или `"format": "html"`. Тогда текст сообщения будет отформатирован.
|
||
|
||
### Markdown
|
||
|
||
| Как написать | Результат |
|
||
|--------------|-----------|
|
||
| `*курсив*` или `_курсив_` | *курсив* |
|
||
| `**жирный**` или `__жирный__` | **жирный** |
|
||
| `~~зачёркнутый~~` | ~~зачёркнутый~~ |
|
||
| `++подчёркнутый++` | подчёркнутый |
|
||
| `` `код` `` | моноширинный (переводы строк внутри — как пробелы) |
|
||
| `[текст ссылки](https://example.com)` | кликабельная ссылка |
|
||
| @упоминание | `"text": "[Имя Фамилия](max://user/user_id)", "format": "markdown"` — полное имя из профиля MAX |
|
||
|
||
### HTML
|
||
|
||
| Теги | Результат |
|
||
|------|-----------|
|
||
| `<i>`, `<em>` | курсив |
|
||
| `<b>`, `<strong>` | жирный |
|
||
| `<del>`, `<s>` | зачёркнутый |
|
||
| `<ins>`, `<u>` | подчёркнутый |
|
||
| `<pre>`, `<code>` | моноширинный |
|
||
| `<a href="https://example.com">Текст</a>` | ссылка |
|
||
| @упоминание | `"text": "<a href=\"max://user/user_id\">Имя Фамилия</a>", "format": "html"` |
|
||
|
||
Пример тела с markdown:
|
||
|
||
```json
|
||
{
|
||
"text": "**Внимание!** Вы отправили *голосовое*. Обрабатываем.",
|
||
"format": "markdown"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Кнопки (inline_keyboard)
|
||
|
||
Кнопки добавляются через **attachments**: один элемент с `type: "inline_keyboard"` и `payload.buttons` — массив **рядов**, каждый ряд — массив **кнопок**.
|
||
|
||
Ограничения:
|
||
- до **210 кнопок** всего;
|
||
- до **30 рядов**;
|
||
- до **7 кнопок в ряду** (для типов `link`, `open_app`, `request_geo_location`, `request_contact` — до **3** в ряду);
|
||
- для кнопки типа `link` ссылка до **2048** символов.
|
||
|
||
### Структура
|
||
|
||
```json
|
||
{
|
||
"text": "Текст сообщения над кнопками",
|
||
"format": "markdown",
|
||
"attachments": [
|
||
{
|
||
"type": "inline_keyboard",
|
||
"payload": {
|
||
"buttons": [
|
||
[
|
||
{ "type": "callback", "text": "Надпись кнопки", "payload": "значение при нажатии" }
|
||
],
|
||
[
|
||
{ "type": "link", "text": "Открыть сайт", "url": "https://example.com" }
|
||
]
|
||
]
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
`buttons` — массив рядов. Каждый ряд — массив кнопок. Одна кнопка — объект с полями в зависимости от типа.
|
||
|
||
### Типы кнопок
|
||
|
||
| type | Описание | Поля кнопки |
|
||
|------|----------|-------------|
|
||
| **callback** | При нажатии в Webhook приходит `message_callback` с `callback_id` и payload. Нужен для ответа через POST /answers. | `text`, `payload` (строка или объект — то, что придёт в бот) |
|
||
| **link** | Открывает ссылку в браузере. | `text`, `url` (до 2048 символов) |
|
||
| **message** | Отправляет боту текстовое сообщение (как будто пользователь написал это). | `text` |
|
||
| **request_contact** | Запрос контакта (номер телефона). Пользователь нажимает → клиент MAX предлагает отправить контакт → в Webhook приходит `message_created` с данными контакта (телефон и т.д.) в теле сообщения. | `text` (подпись на кнопке) |
|
||
| **request_geo_location** | Запрос геолокации. Пользователь нажимает → отправляет геолокацию → в Webhook приходит сообщение с координатами. | `text` |
|
||
| **open_app** | Открывает мини-приложение. | уточнять в [доках](https://dev.max.ru/docs-api) |
|
||
|
||
### Пример: кнопка «Поделиться контактом»
|
||
|
||
У кнопки тип **request_contact**, поле **text** — подпись (например «📱 Отправить номер телефона»). В одном ряду с такими кнопками MAX разрешает до 3 кнопок.
|
||
|
||
```json
|
||
{
|
||
"text": "Чтобы мы могли связаться, поделитесь номером телефона:",
|
||
"format": "markdown",
|
||
"attachments": [
|
||
{
|
||
"type": "inline_keyboard",
|
||
"payload": {
|
||
"buttons": [
|
||
[
|
||
{ "type": "request_contact", "text": "📱 Отправить номер телефона" }
|
||
]
|
||
]
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
После нажатия пользователь подтверждает отправку контакта в клиенте MAX. В Webhook придёт **message_created** с вложением `attachments[0].type === "contact"`. Структура:
|
||
|
||
- **attachments[0].payload.vcf_info** — строка VCARD (например `TEL;TYPE=cell:79262306381`, `FN:Имя Фамилия`). Телефон достаётся из строки `TEL...:номер`.
|
||
- **attachments[0].payload.max_info** — объект пользователя MAX: `user_id`, `first_name`, `last_name`, `name`, `is_bot`, `last_activity_time`.
|
||
|
||
В нормализаторе для такого сообщения: `answer_type: 'contact'`, `answer_text` — извлечённый номер, `contact_payload` — весь payload, `contact_name` — из max_info.name.
|
||
|
||
### Пример: одна callback-кнопка
|
||
|
||
```json
|
||
{
|
||
"text": "Выберите действие:",
|
||
"format": "markdown",
|
||
"attachments": [
|
||
{
|
||
"type": "inline_keyboard",
|
||
"payload": {
|
||
"buttons": [
|
||
[
|
||
{ "type": "callback", "text": "Подтвердить", "payload": "confirm" },
|
||
{ "type": "callback", "text": "Отмена", "payload": "cancel" }
|
||
]
|
||
]
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
При нажатии «Подтвердить» в Webhook придёт `update_type: "message_callback"`, в нормализованном объекте будет `answer_text: "confirm"` и `callback_id` для POST /answers (уведомление или обновление сообщения).
|
||
|
||
### Пример: кнопка-ссылка и callback в одном сообщении
|
||
|
||
```json
|
||
{
|
||
"text": "Официальный сайт и обратная связь:",
|
||
"format": "markdown",
|
||
"attachments": [
|
||
{
|
||
"type": "inline_keyboard",
|
||
"payload": {
|
||
"buttons": [
|
||
[
|
||
{ "type": "link", "text": "Перейти на сайт", "url": "https://example.com" }
|
||
],
|
||
[
|
||
{ "type": "callback", "text": "Написать в поддержку", "payload": "support" }
|
||
]
|
||
]
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### В n8n (HTTP Request — тело с кнопками)
|
||
|
||
В **JSON Body** ноды можно задать статичное тело или собрать через выражение. Пример статичного тела с кнопками:
|
||
|
||
```json
|
||
{
|
||
"text": "Выберите действие:",
|
||
"format": "markdown",
|
||
"attachments": [
|
||
{
|
||
"type": "inline_keyboard",
|
||
"payload": {
|
||
"buttons": [
|
||
[
|
||
{ "type": "callback", "text": "Да", "payload": "yes" },
|
||
{ "type": "callback", "text": "Нет", "payload": "no" }
|
||
]
|
||
]
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
URL и заголовки — как раньше: `POST https://platform-api.max.ru/messages?user_id={{ $json.max_id }}`, `Authorization: <token>`, `Content-Type: application/json`.
|
||
|
||
После нажатия callback-кнопки пользователем обрабатывай событие в воркфлоу (по `answer_type === 'callback'` и `answer_text` / `callback_id`) и при необходимости вызывай **POST /answers?callback_id=...** для уведомления или обновления сообщения (см. `02-methods.md`).
|