Skip to content

Сессионные залы (Breakout Rooms)

Сессионные залы позволяют разделить участников звонка на отдельные комнаты для работы в малых группах.

Основные понятия

  • Основной зал (Main Room) — комната с id: 0, в которой находятся все участники по умолчанию
  • Сессионный зал — дополнительная комната с уникальным id
  • Назначенная комната (assignedRoomId) — комната, в которую участник назначен
  • Текущая комната (currentRoomId) — комната, в которой участник находится сейчас

Свойства комнаты

typescript
interface Room {
    id: number;              // Уникальный ID комнаты (0 = основной зал)
    name: string;            // Название комнаты
    active?: boolean;        // Активна ли комната
    muteStates?: MuteStates; // Глобальные мьют-состояния комнаты
    pinnedParticipantId?: ExternalParticipantId; // Закреплённый участник
    countdownSec?: number;   // Обратный отсчёт (секунды)
    timeoutMs?: number;      // Таймаут комнаты (мс)
}

Создание и обновление комнат

javascript
// Создать новую комнату
await SDK.updateRooms([{
    name: 'Группа 1'
}]);

// Создать комнату с определённым ID
await SDK.updateRooms([{
    id: 1,
    name: 'Группа 1'
}]);

// Создать несколько комнат
await SDK.updateRooms([
    { name: 'Группа 1' },
    { name: 'Группа 2' },
    { name: 'Группа 3' }
]);

// Обновить существующую комнату
await SDK.updateRooms([{
    id: 1,
    name: 'Новое название',
    countdownSec: 300,  // 5 минут
    timeoutMs: 600000   // 10 минут
}]);

Назначение участников в комнаты

javascript
import { ExternalIdType } from '@vkontakte/calls-sdk';

// Добавить участника в комнату
await SDK.updateRooms([{
    id: 1,
    addParticipantIds: [
        { id: 'user123', type: ExternalIdType.USER }
    ]
}]);

// Удалить участника из комнаты
await SDK.updateRooms([{
    id: 1,
    removeParticipantIds: [
        { id: 'user123', type: ExternalIdType.USER }
    ]
}]);

// Переместить участника из одной комнаты в другую
await SDK.updateRooms([
    {
        id: 1,  // Из комнаты 1
        removeParticipantIds: [{ id: 'user123', type: ExternalIdType.USER }]
    },
    {
        id: 2,  // В комнату 2
        addParticipantIds: [{ id: 'user123', type: ExternalIdType.USER }]
    }
]);

Случайное распределение участников

javascript
// Распределить участников случайным образом
await SDK.updateRooms([
    { id: 1, participantCount: 3 },  // 3 участника в комнату 1
    { id: 2, participantCount: 3 },  // 3 участника в комнату 2
    { id: 3, participantCount: 2 }   // 2 участника в комнату 3
], true);  // assignRandomly = true

Активация и деактивация комнат

Комнаты нужно активировать, чтобы участники переместились в них:

javascript
// Активировать комнаты
await SDK.activateRooms([1, 2, 3], false);  // deactivate = false

// Деактивировать комнаты (участники вернутся в основной зал)
await SDK.activateRooms([1, 2, 3], true);   // deactivate = true

Переключение между комнатами

Для себя

javascript
// Перейти в комнату
await SDK.switchRoom(1);

// Вернуться в основной зал
await SDK.switchRoom(null);

// или
await SDK.switchRoom(0);

Для другого участника (только админ)

javascript
// Переместить участника в комнату
await SDK.switchRoom(1, { id: 'user123', type: ExternalIdType.USER });

// Вернуть участника в основной зал
await SDK.switchRoom(null, { id: 'user123', type: ExternalIdType.USER });

Удаление комнат

javascript
// Удалить комнату (участники вернутся в основной зал)
await SDK.removeRooms([1]);

// Удалить несколько комнат
await SDK.removeRooms([1, 2, 3]);

Коллбэки

Для администратора

javascript
await SDK.init({
    // ...
    
    // Обновление всех комнат (только для админа)
    onRoomsUpdated: (updates) => {
        // updates содержит изменения по типам событий:
        // - RoomsEventType.UPDATE - обновление комнат
        // - RoomsEventType.ACTIVATE - активация/деактивация
        // - RoomsEventType.REMOVE - удаление
        
        for (const [eventType, update] of Object.entries(updates)) {
            console.log('Событие:', eventType, update);
        }
    }
});

Для всех участников

javascript
import { RoomsEventType } from '@vkontakte/calls-sdk';

await SDK.init({
    // ...
    
    // При старте звонка — узнать начальную комнату
    onRoomStart: (roomId) => {
        console.log('Начальная комната:', roomId);
    },
    
    // Обновление комнаты, доступной участнику
    onRoomUpdated: (eventTypes, roomId, room, deactivate) => {
        if (eventTypes.includes(RoomsEventType.UPDATE) && room) {
            console.log('Комната обновлена:', room.name);
        }
        if (eventTypes.includes(RoomsEventType.ACTIVATE)) {
            console.log('Комната', deactivate ? 'деактивирована' : 'активирована');
        }
    },
    
    // Участник переключился в другую комнату
    onRoomSwitched: (roomId) => {
        console.log('Вы перешли в комнату:', roomId ?? 'основной зал');
    },
    
    // Обновление участников в комнате
    onRoomParticipantsUpdated: (update) => {
        // update.roomId - ID комнаты
        // update.addedParticipantIds - назначенные участники
        // update.addedParticipants - активные участники (с данными)
        
        if (update.addedParticipants) {
            console.log('В комнату вошли:', update.addedParticipants);
        }
    }
});

Полный пример

javascript
import * as SDK from '@vkontakte/calls-sdk';
import { RoomsEventType, ExternalIdType } from '@vkontakte/calls-sdk';

let currentRoomId = 0;
let assignedRoomId = 0;
let rooms = [{ id: 0, name: 'Основной зал' }];

await SDK.init({
    // ...
    
    onRoomStart: (roomId) => {
        currentRoomId = roomId ?? 0;
    },
    
    onRoomsUpdated: (updates) => {
        // Обработка для админа
        const updateEvent = updates[RoomsEventType.UPDATE];
        if (updateEvent?.rooms) {
            updateEvent.rooms.forEach(room => {
                const existing = rooms.find(r => r.id === room.id);
                if (existing) {
                    Object.assign(existing, room);
                } else {
                    rooms.push(room);
                }
            });
        }
        
        const activateEvent = updates[RoomsEventType.ACTIVATE];
        if (activateEvent?.roomIds) {
            activateEvent.roomIds.forEach(id => {
                const room = rooms.find(r => r.id === id);
                if (room) room.active = !activateEvent.deactivated;
            });
        }
        
        const removeEvent = updates[RoomsEventType.REMOVE];
        if (removeEvent?.roomIds) {
            rooms = rooms.filter(r => !removeEvent.roomIds.includes(r.id));
        }
    },
    
    onRoomUpdated: (eventTypes, roomId, room, deactivate) => {
        // Обработка для обычного участника
        if (eventTypes.includes(RoomsEventType.UPDATE) && room) {
            assignedRoomId = roomId;
        }
        if (eventTypes.includes(RoomsEventType.ACTIVATE)) {
            const r = rooms.find(r => r.id === roomId);
            if (r) r.active = !deactivate;
        }
    },
    
    onRoomSwitched: (roomId) => {
        currentRoomId = roomId ?? 0;
        updateUI();
    },
    
    onRoomParticipantsUpdated: (update) => {
        // Обновить список участников в комнатах
        updateParticipantsList(update);
    }
});

// Пример: Админ создаёт комнаты и распределяет участников
async function setupBreakoutRooms() {
    // 1. Создать комнаты
    await SDK.updateRooms([
        { name: 'Группа 1' },
        { name: 'Группа 2' }
    ]);
    
    // 2. Активировать комнаты
    await SDK.activateRooms([1, 2], false);
}

// Пример: Участник переходит в назначенную комнату
async function goToAssignedRoom() {
    if (assignedRoomId && assignedRoomId !== currentRoomId) {
        await SDK.switchRoom(assignedRoomId);
    }
}

// Пример: Вернуться в основной зал
async function goToMainRoom() {
    if (currentRoomId !== 0) {
        await SDK.switchRoom(null);
    }
}

Пример сценария работы

Участники: Админ А, Участник У1, Участник У2

  1. Все находятся в основном зале

  2. Админ создаёт комнату и назначает У1:

    javascript
    await SDK.updateRooms([{
        name: 'Комната N',
        addParticipantIds: [{ id: 'У1', type: ExternalIdType.USER }]
    }]);
    • Админ получает onRoomsUpdated
    • У1 получает onRoomUpdated
  3. Админ активирует комнату:

    javascript
    await SDK.activateRooms([roomId], false);
    • Админ получает:
      • onRoomsUpdated (комната активирована)
      • onRoomParticipantsUpdated (У1 добавлен в N)
      • onRoomParticipantsUpdated (У1 удалён из основного зала)
    • У1 получает:
      • onRoomUpdated (комната активирована)
      • onRoomSwitched (теперь в комнате N)
      • onRoomParticipantsUpdated
    • У2 получает:
      • onRoomParticipantsUpdated (У1 удалён из основного зала)

Особенности

Права доступа

Создавать, редактировать и удалять комнаты могут только пользователи с ролью ADMIN или CREATOR.

Важно

При удалении или деактивации комнаты все её участники автоматически возвращаются в основной зал.

Видимость участников

Участники видят только тех, кто находится в той же комнате. При переключении комнаты меняется список видимых участников.