Голосовые каналы

API для подключения к голосовым каналам через WebRTC SFU, управления состоянием и модерации участников.

Голосовые каналы создаются как обычные каналы с типом VOICE (см. Каналы).

Подключение к голосу

POST /channels/{channelId}/voice/join

Требуется членство в канале и право CONNECT_VOICE. Канал должен быть типа VOICE.

Если пользователь уже в другом голосовом канале — он автоматически отключается от предыдущего.

Ответ (200):

{ "sfuUrl": "ws://sfu.example.com/ws/room?roomId=channel-uuid&token=...", "token": "timestamp:roomId:participantId:signature", "turnServers": [ { "urls": ["turn:turn.example.com:3478"], "username": "timestamp:userId@federation", "credential": "base64-hmac-signature" } ] }

Поле

Описание

sfuUrl

WebSocket URL для подключения к SFU

token

HMAC-SHA256 токен для аутентификации в SFU (TTL 24 часа)

turnServers

TURN-серверы для NAT traversal (TTL 24 часа)

При подключении рассылается WebSocket-событие voice.joined.

Ошибки:

Код

Описание

NOT_FOUND

Канал не найден

VALIDATION_ERROR

Канал не является голосовым

FORBIDDEN

Не участник / нет права CONNECT_VOICE

Отключение от голоса

POST /channels/{channelId}/voice/leave

Ответ (200): SuccessResponse.

При отключении рассылается WebSocket-событие voice.left.

Состояние участников

GET /channels/{channelId}/voice/state

Требуется членство в канале. Канал должен быть типа VOICE.

Ответ (200):

{ "channelId": "channel-uuid", "participants": [ { "userId": "user-uuid", "federation": "https://auth.example.com", "muted": false, "deafened": false, "videoEnabled": true, "screenSharing": false, "joinedAt": "2025-01-15T10:30:00Z" } ] }

Модерация

Заглушить участника

POST /channels/{channelId}/voice/mute/{userId}

Требуется право MUTE_VOICE_MEMBERS.

Параметр

Тип

Описание

userId

string

ID пользователя

federation

string (query)

Федерация пользователя (опционально, по умолчанию федерация вызывающего)

Ответ (200): SuccessResponse.

Рассылается WebSocket-событие voice.muted. SFU также уведомляется о мьюте.

Заглушить входящий звук

POST /channels/{channelId}/voice/deafen/{userId}

Требуется право DEAFEN_VOICE_MEMBERS.

Ответ (200): SuccessResponse.

Рассылается WebSocket-событие voice.deafened.

Внутренний SFU Callback

POST /internal/sfu/events

Внутренний эндпоинт для получения событий от SFU. Аутентифицируется заголовком X-SFU-Secret.

Тело запроса:

{ "event": "participant_left", "roomId": "channel-uuid", "participantId": "userId@federation", "timestamp": "2025-01-15T10:30:00Z" }

Событие

Описание

participant_left

Участник отключился от SFU — удаляется его VoiceState

room_empty

Комната пуста — логируется

Ответ (200): пустое тело.

Ошибки: 401 — неверный X-SFU-Secret.