Перейти к основному содержимому
Версия: 2026.2.4

Компоненты сообщений и модальные окна

Кнопки, выпадающие списки, чекбоксы на сообщении и модальные окна с полями ввода — всё это называется компонентами. На уровне протокола компоненты не описаны отдельными Protobuf-сообщениями на каждый визуальный тип (кнопка, список и т.д.) — вместо этого используется один опаковый JSON, который сервер не разбирает и не проверяет по содержимому, а только хранит и передаёт как есть. Такое решение сознательно: оно позволяет добавлять новые виды компонентов и клиентские возможности рендеринга, не меняя Protobuf-контракт и не требуя синхронного обновления сервера и всех клиентов.

Это единственная часть Bot API, где схема — не Protobuf, а плоский JSON-контракт, описанный прямо в этой документации. Если вы пишете свою библиотеку, стоит реализовать типизированные билдеры/парсеры поверх этого JSON, а не заставлять разработчика бота писать JSON руками (так сделано, например, в официальной библиотеке Voice.Bot.Client.Net).

Где используется​

Поле с этим JSON называется components_json (для компонентов на сообщении) или modal_json (для модального окна) и встречается в нескольких местах API:

  • BotsApi.SendMessage / UpdateMessage / SendInteractionFollowup — поле components_json: бот может прикрепить компоненты к обычному сообщению, отправленному проактивно, не обязательно в ответ на interaction;
  • BotsApi.RespondToInteraction — поля components_json и modal_json: ответ на interaction может нести компоненты (при kind: CHANNEL_MESSAGE/UPDATE_MESSAGE) либо открыть модальное окно (при kind: MODAL, см. «Interactions API»);
  • на прочитанном сообщении (MessageInfo.components_json) — если у сообщения есть компоненты, они возвращаются вместе с сообщением при чтении, в том числе после перезагрузки истории чата.

Схема components_json: массив строк (ActionRow[])​

Компоненты сообщения — это упорядоченный список строк (ActionRow), в каждой из которых один или несколько элементов:

[
{
"type": "actionRow",
"components": [
{ "type": "button", "style": "primary", "customId": "confirm", "label": "Подтвердить" },
{ "type": "button", "style": "danger", "customId": "cancel", "label": "Отмена" },
{ "type": "button", "style": "link", "url": "https://example.com/docs", "label": "Документация" }
]
},
{
"type": "actionRow",
"components": [
{
"type": "select",
"customId": "pick-fruit",
"placeholder": "Выберите вариант",
"options": [
{ "label": "Яблоко", "value": "apple" },
{ "label": "Банан", "value": "banana" }
],
"minValues": 1,
"maxValues": 1
}
]
},
{
"type": "actionRow",
"components": [
{
"type": "combobox",
"customId": "search-user",
"placeholder": "Поиск...",
"searchable": true,
"options": [
{ "label": "Alpha", "value": "alpha-id" },
{ "label": "Beta", "value": "beta-id" }
],
"minValues": 1,
"maxValues": 1
}
]
},
{
"type": "actionRow",
"components": [
{ "type": "checkbox", "customId": "agree", "label": "Я согласен с правилами", "checked": false }
]
}
]

Виды элементов​

typeПоляОписание
buttonstyle, customId (обязателен, кроме style: "link"), url (обязателен для style: "link"), label, emoji, disabledКнопка. style — одно из primary/secondary/success/danger/link. Клик репортится через customId (см. InvokeMessageComponent.custom_id в «Interactions API»).
selectcustomId, placeholder, options[] (label, value), minValues, maxValuesОбычный выпадающий список с фиксированными вариантами. Выбранные значения приходят в InvokeMessageComponent.values.
comboboxТо же, что select, плюс searchable: trueТакой же список вариантов, что и select, но клиенту показывается поле фильтрации/поиска над списком — удобно, когда вариантов много. Репортит выбор точно так же, как select.
checkboxcustomId, label, checked (состояние по умолчанию)Отдельный переключатель; несколько чекбоксов в одной строке образуют чек-лист. Отмеченные customId репортятся так же, как выбор в select.

Схема modal_json​

{
"title": "Обратная связь",
"customId": "feedback-modal",
"components": [
{
"type": "actionRow",
"components": [
{
"type": "textInput",
"customId": "comment",
"label": "Комментарий",
"style": "paragraph",
"required": true,
"placeholder": "Расскажите, что вы думаете",
"maxLength": 1000
}
]
},
{
"type": "actionRow",
"components": [
{ "type": "checkbox", "customId": "subscribe", "label": "Уведомлять об ответах", "checked": true }
]
}
]
}

textInput.style — short (однострочное поле) или paragraph (многострочное) — прямой аналог текстовых полей модальных окон Discord. После отправки формы значение каждого поля приходит в SubmitModal.fields, где ключ — customId этого поля; чекбоксы в модальном окне возвращают значение "true"/"false" тем же способом.

Куда это не проверяется​

Ни в одном месте на сервере это содержимое не парсится и не валидируется по структуре — это осознанно оставлено на усмотрение клиентов (и сервера рендеринга интерфейса Voice). Единственное серверное ограничение — общая длина строки components_json/modal_json (несколько тысяч символов) — как у любого текстового поля API. Ответственность за то, чтобы JSON соответствовал описанной здесь схеме, лежит на клиентской библиотеке бота и на клиенте Voice, который эти компоненты отображает.