Перейти к основному содержимому
Это документация Voice для версии 2026.2.4, которая уже не поддерживается.

Актуальная документация находится на странице последней версии (2026.2.19).

Версия: 2026.2.4

Gateway: события в реальном времени

Чтобы бот реагировал на сообщения и другие события, ему не нужно ничего опрашивать — Voice сам push'ит события боту через постоянное соединение, как только они происходят. Это соединение называется Gateway.

Протокол​

Gateway реализован поверх SignalR — WebSocket-протокол ASP.NET Core с автоматическим фолбэком на Server-Sent Events/long polling, если WebSocket недоступен. Если вы пишете библиотеку не на .NET, ищите клиент протокола SignalR для своего языка (реализации есть для JavaScript/TypeScript, Java, Python, Go, Rust и т.д.) — реализовывать сам протокол SignalR с нуля обычно не требуется.

Подключение​

wss://<ваш gateway-хост>/hubs/bots?access_token=<токен бота>

Токен передаётся параметром строки запроса access_token (см. «Аутентификация ботов») — это единственный способ аутентификации для этого соединения, отдельного «рукопожатия» после подключения не требуется.

Сразу после успешного подключения сервер сам определяет, в какие «комнаты» (группы рассылки) включить это соединение, основываясь на:

  1. группах, в которых состоит бот (сервер запрашивает их на подключении);
  2. Gateway Intents, выданных боту.

Клиенту не нужно ничего «подписывать» вручную — после подключения события просто начинают приходить сами, в соответствии с правами бота.

Задержка при смене intents

Если владелец меняет боту intents уже после того, как бот подключён, соединение продолжит получать события по старому набору intents до переподключения (комнаты рассылки определяются один раз, в момент установки соединения). Переподключитесь, чтобы применить новые intents.

Топики​

После подключения на соединении можно слушать четыре именованных «топика» (в терминах SignalR — это имена методов, которые сервер вызывает на клиенте):

ТопикТребует intentЧто в нём приходит
chat-eventsGuildMessagesНовые/изменённые/удалённые сообщения, реакции, индикаторы набора текста
channel-eventsGuildVoiceStatesПодключение/отключение участников голосовых каналов
group-eventsGuildsВход/выход участников, изменения структуры группы (категории, каналы, роли), смена статуса присутствия
interaction-events— (не гейтится intent'ами)Входящие interactions — слэш-команды, клики по компонентам, сабмиты модальных окон, запросы автодополнения. Это личная, адресная доставка ровно этому боту, а не рассылка по группе.

Формат события (конверт)​

Каждый вызов топика передаёт один аргумент — объект-конверт. Общая форма (набор полей зависит от топика — например, channel_id/message_id есть не везде):

{
"GroupId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"ChatId": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"MessageId": "b3fc2c96-3f66-afa6-9b1d-eb4d3b7d4bad",
"UserId": "2c963f66-afa6-9b1d-eb4d-3b7d4bad9bdd",
"Type": "MessageCreated",
"PayloadJson": "{...сериализованный полезный объект в виде JSON-строки...}",
"Timestamp": 1730000000000
}
Поля конверта — PascalCase

В отличие от gRPC-методов BotsApi/InteractionsApi (там JSON — camelCase, как в примерах «Основные методы»), конверт Gateway-события — это сырой System.Text.Json без camelCase-политики, поэтому его поля — Type, PayloadJson, Timestamp, GroupId, ChatId, UserId, MessageId — именно в PascalCase, как показано выше.

Важно: Payload приходит не как вложенный объект, а как строка, содержащая JSON — поле PayloadJson. Библиотеке нужно распарсить эту строку отдельным шагом после получения конверта. Такая двухуровневая схема — намеренное решение платформы: она позволяет добавлять новые виды событий (Type), не меняя форму самого конверта и не требуя обновления клиентов, которые эти новые события ещё не умеют обрабатывать.

Как это использовать​

on(topic, envelope) => {
payload = json_parse(envelope.PayloadJson)

switch (envelope.Type) {
case "MessageCreated":
handle_new_message(payload) // payload — это объект сообщения
case "MessageReactionAdded":
handle_reaction(payload)
// ... остальные известные типы
default:
// неизвестный тип события — просто игнорируем, а не падаем.
// Сервер может добавлять новые типы событий со временем.
}
}
Всегда игнорируйте неизвестные Type

Список возможных значений Type может со временем расширяться — платформа явно спроектирована так, чтобы добавление нового типа события не требовало обновления уже написанных клиентов. Хорошая библиотека должна тихо пропускать (а не падать с ошибкой на) любой незнакомый Type.

Известные значения Type по топикам​

Список ниже — ориентировочный (актуальный на момент написания документации), а не закрытый перечень. У каждой группы — отдельная страница на каждое значение Type с описанием payload'а и JSON-примером:

  • «Структура группы» (топик group-events) — GroupUpdated, CategoryCreated/CategoryUpdated/CategoryDeleted/CategoryMoved, ChannelCreated/ChannelDeleted/ChannelMoved, RoleCreated/RoleUpdated/RoleMoved/RoleDeleted/RoleAssigned/RoleRemoved — payload у всех один и тот же: полный текущий снимок группы, а не diff;
  • «Участники группы» (топик group-events) — UserJoined, UserLeft, UserKicked;
  • «Статус и профиль» (топик group-events) — UserProfileUpdated, UserStatusChanged;
  • «Голосовые каналы» (топик channel-events) — UserConnected, UserDisconnected;
  • «Сообщения» (топик chat-events) — MessageCreated, MessageUpdated, MessageDeleted, MessageReactionAdded, MessageReactionRemoved, UserTyping, BotTyping;
  • «Interactions» (топик interaction-events) — InteractionCreated, единственный тип на этом топике, личная адресная доставка боту.

Переподключение​

Реализуйте автоматическое переподключение с задержкой (большинство SignalR-клиентов умеют это из коробки) — соединение может обрываться по сетевым причинам, при развёртывании новой версии сервера или при отзыве токена бота (в последнем случае переподключение с тем же токеном будет раз за разом завершаться ошибкой аутентификации — такую ситуацию стоит обрабатывать отдельно, не как временный сбой).