Skip to main content
Голосовой бот использует два соединения:
  1. основной Gateway wss://miscord.ru/gateway?v=1&encoding=json управляет состоянием;
  2. Voice Gateway wss://miscord.ru/ws/voice-gateway?v=1 согласует UDP-медиасессию.
Аудио передаётся как RTP/Opus через зашифрованный UDP в общий SFU. Offer, answer, ICE и mesh-соединения не поддерживаются.

Требования

  • приложение установлено с областями bot и applications.commands;
  • роль бота имеет VIEW_CHANNEL и CONNECT;
  • для отправки аудио требуется SPEAK, но бот без него может подключиться для прослушивания;
  • включён intent GUILD_VOICE_STATES (1 << 7).

Запрос на подключение

Отправьте opcode 4 в основной Gateway:
Для выхода передайте channel_id: null. После успешной проверки основной Gateway отправит VOICE_STATE_UPDATE и VOICE_SERVER_UPDATE:
Media Ticket одноразовый и живёт 60 секунд. Не сохраняйте и не выводите его в лог.

Voice Gateway handshake

  1. Подключитесь к wss:// + endpoint и получите opcode 8 Hello.
  2. Отправьте opcode 0 Identify.
  3. Получите opcode 2 Ready с ssrc, ip, port и modes.
  4. Выполните UDP discovery.
  5. Отправьте opcode 1 Select Protocol с protocol: "udp".
  6. Получите opcode 4 Session Description с 32-байтовым ключом.

UDP discovery

Отправьте на ip:port из Ready пакет длиной 74 байта:
  • uint16be type = 1;
  • uint16be length = 70;
  • uint32be ssrc;
  • 66 нулевых байт.
Ответ также имеет длину 74 байта: type = 2, length = 70, тот же SSRC, нуль-терминированный IP в байтах 8..71 и uint16be port в байтах 72..73.

RTP и шифрование

  • Opus: 48 кГц, stereo, 20 мс, 960 samples;
  • RTP payload type: 120;
  • SSRC берётся только из Ready и всегда является случайным uint32;
  • обязательный режим: aead_xchacha20_poly1305_rtpsize;
  • aead_aes256_gcm_rtpsize доступен, когда его поддерживает media-node;
  • RTP header используется как AAD, шифруется только payload;
  • к пакету добавляется четырёхбайтовый big-endian counter;
  • сервер проверяет UDP tuple, SSRC и anti-replay окно.
Перед первым RTP отправьте Speaking opcode 5. При остановке передайте пять Opus silence frames F8 FF FE, затем Speaking 0. Это предотвращает обрыв последнего аудиофрейма и зависший статус речи.

Heartbeat и Resume

Heartbeat отправляется opcode 3, подтверждение приходит opcode 6. Для восстановления управляющего WebSocket используйте opcode 7 Resume с server_id, session_id, token и последним seq_ack. При доступном bounded-буфере сервер ответит opcode 9 Resumed, иначе закроет соединение и потребуется новый Identify. Текущий транспорт сообщает dave_protocol_version: 0. DAVE/MLS является отдельным следующим этапом и не меняет SFU-топологию.

Готовый Music Bot

Пример slash-команд, YouTube-резолвера и зашифрованной RTP/Opus-передачи через один UDP socket.