MAATRIX GAMES / Блог / Голосовые каналы по интересам — авто-создание временных комнат

Голосовые каналы по интересам — авто-создание временных комнат

MAATRIX GAMES

Если у тебя на Discord-сервере комьюнити висит десяток статичных голосовых каналов «Кастомка 1», «Кастомка 2», «Для двоих», «AFK» — и половина из них вечно пустая, а игроки всё равно толпятся в одном — знакомая боль. Статичные каналы либо не хватает, либо их слишком много и список превращается в свалку. Решение, которое реально прижилось в геймерских комьюнити — voice channel hub: один «канал-генератор», зайдя в который бот тут же создаёт тебе персональную временную комнату и переносит туда, а когда все выходят — комната исчезает сама. Ниже — как это поднять на практике, какие боты для этого есть и какие грабли вылезают на реальных серверах.

Как работает hub-канал технически

Идея простая, но механика прячется в правах Discord API. Создаётся один голосовой канал-триггер (обычно называют «➕ Создать канал» или «Join to Create»). Бот слушает событие voiceStateUpdate — когда пользователь заходит именно в этот канал, срабатывает скрипт:

  1. Бот создаёт новый голосовой канал в той же категории через guild.channels.create() с правами, где владельцем назначается зашедший игрок.
  2. Игрока сразу перемещают (member.voice.setChannel()) из хаба в свежесозданную комнату.
  3. Канал живёт, пока в нём есть хоть один участник.
  4. При выходе последнего человека бот слушает voiceStateUpdate на пустоту канала и удаляет его через channel.delete(), обычно с задержкой 5-15 секунд (чтобы не удалить канал, если человек просто переподключается или у него лагает клиент).

Важный нюанс: боту нужны права Manage Channels и Move Members именно в той категории, где живёт хаб — иначе создание/удаление будет падать с ошибкой прав, и в логах бота ты увидишь Missing Permissions (код 50013). Проверить это можно в настройках роли бота: Server Settings → Roles → выбрать роль бота → убедиться, что галочки стоят на уровне категории, а не только сервера в целом (права на категории можно переопределить отдельно, и часто именно это забывают).

Готовые боты: сравнение вариантов

Писать своего бота с нуля ради этой фичи почти всегда избыточно — есть проверенные готовые решения с открытым кодом или бесплатным тарифом.

БотТипЛимит бесплатноКастомизацияГде хостить
VoiceMasterХостится разработчикомДо 2 хабов на сервере (бесплатно), Premium снимает лимитЧерез слэш-команды, без кодаНе нужен свой хостинг
Kokoro (JoinToCreate)Открытый исходник (Node.js)Без лимита, сам хостишьПолный доступ к кодуСвой VPS/сервер
ChannelMasterХостится разработчикомОграниченно на бесплатном тарифеСлэш-команды + веб-панельНе нужен свой хостинг
Самописный на discord.jsСвой кодБез лимитаПолная свободаСвой VPS/сервер

Для 90% серверов достаточно VoiceMaster — он закрывает базовый сценарий за 10 минут настройки командой /voicemaster setup, без единой строчки кода. Если хочешь именных прав, интеграции с игровой статистикой или экономикой сервера — тогда либо форкаешь Kokoro и правишь под себя, либо пишешь своего бота на discord.js/discord.py.

Если у тебя уже крутится RCON-бот или бот статистики онлайна на том же VPS — логично разместить и voice-хаб бота там же, чтобы не плодить процессы. Про выбор такого бота есть отдельный разбор: Discord-бот для управления игровым сервером.

Нужен игровой сервер?

MAATRIX GAMES — 45+ игр, деплой за минуты, DDoS-защита, автобэкапы. Тарифы от 399 ₽/мес.

Создать сервер

Минимальный самописный вариант на discord.js

Если хочется не зависеть от чужого бота и контролировать логику — вот скелет на discord.js v14, который реально можно взять за основу:

const { Events, ChannelType, PermissionsBitField } = require('discord.js');

const HUB_CHANNEL_ID = '123456789012345678';
const CATEGORY_ID = '987654321098765432';
const tempChannels = new Map(); // channelId -> ownerId

client.on(Events.VoiceStateUpdate, async (oldState, newState) => {
  // Игрок зашёл в хаб — создаём временный канал
  if (newState.channelId === HUB_CHANNEL_ID) {
    const guild = newState.guild;
    const member = newState.member;

    const tempChannel = await guild.channels.create({
      name: `🎮 ${member.user.username}`,
      type: ChannelType.GuildVoice,
      parent: CATEGORY_ID,
      userLimit: 5,
      permissionOverwrites: [
        {
          id: member.id,
          allow: [PermissionsBitField.Flags.ManageChannels, PermissionsBitField.Flags.MoveMembers],
        },
      ],
    });

    tempChannels.set(tempChannel.id, member.id);
    await member.voice.setChannel(tempChannel);
  }

  // Проверяем, не опустел ли какой-то временный канал
  const oldChannel = oldState.channel;
  if (oldChannel && tempChannels.has(oldChannel.id) && oldChannel.members.size === 0) {
    setTimeout(async () => {
      const fresh = await oldChannel.guild.channels.fetch(oldChannel.id).catch(() => null);
      if (fresh && fresh.members.size === 0) {
        await fresh.delete().catch(() => {});
        tempChannels.delete(oldChannel.id);
      }
    }, 10000);
  }
});

Задержка в 10 секунд перед проверкой и delete — не декоративная деталь, а обязательная защита от гонки состояний: событие VoiceStateUpdate может прилететь до того, как второй игрок реально успел зайти при быстром переключении между каналами. Без неё бот периодически удаляет канал прямо под ногами у людей.

Лимит участников и автоматическое именование

Дефолтный лимит на временную комнату обычно ставят 4-5 человек — под стандартный отряд в шутере или пати в выживалках. Но если сервер многопрофильный (и шутеры, и выживание, и RP), лучше дать игроку выбор при создании через интерактивное меню: бот присылает эфемерное сообщение с кнопками «Дуо (2)», «Сквад (5)», «Без лимита», и уже по выбору выставляет userLimit.

Имя канала — тоже точка, где стоит подумать заранее. Варианты, которые реально работают:

  • 🎮 {username} — просто ник создателя, самый предсказуемый вариант;
  • Комната {username} — чуть теплее для русскоязычного комьюнити;
  • динамическое имя по игре, если бот интегрирован с Discord Rich Presence и видит, во что играет создатель (сложнее в реализации, но выглядит красиво в списке каналов).

Владелец временного канала должен иметь возможность переименовать его и поднять/снять лимит без прав администратора — иначе фича превращается в обузу для модераторов, которых будут дёргать в личку с просьбой «переименуй». В VoiceMaster это закрыто панелью управления через /voicemaster или кнопки в текстовом канале-логе; в самописном варианте это как раз permissionOverwrites на ManageChannels, которые ты видишь в примере кода выше.

Куда девать текстовые каналы temp-комнат

Отдельный вопрос — нужен ли временный текстовый канал в паре с голосовым. Для казуальных серверов обычно нет: игроки договариваются голосом. Но если у тебя RP-сервер или комьюнити активно шарит скриншоты/ссылки во время созвона — стоит генерировать приватный текстовый канал синхронно с голосовым, с теми же правами доступа (видят только те, кто в голосовом). Технически это тот же паттерн channels.create(), только type: ChannelType.GuildText, и permissionOverwrites синхронизируются с голосовым каналом при каждом входе/выходе участника.

На RP-серверах с FiveM это особенно актуально в связке с прокси-войсом — если у тебя уже настроен PMA-Voice или saltychat для дальности голоса в игре, temp-каналы Discord обычно используют как резервный внеигровой чат, а не основной. Подробности по игровому голосовому чату — в статье FiveM: рации и голосовой чат — PMA-Voice, saltychat.

Организация категорий и структура сервера

Хаб-канал и все временные комнаты держи в отдельной категории «🔊 Голосовые комнаты», физически отделённой от статичных каналов сообщества (общий чат, лобби модераторов, войс для стримов). Это не только для порядка — права на категорию наследуются каналами внутри неё, так что примени Manage Channels и Move Members боту один раз на уровне категории, и они автоматически применятся ко всем создаваемым temp-каналам.

Если структура сервера ещё не продумана целиком — до настройки авто-каналов стоит сначала навести порядок в базовой иерархии категорий и ролей, иначе временные комнаты просто добавят хаос в существующий бардак. Разбор с нуля есть здесь: Discord-сервер сообщества: структура каналов.

Ограничение Discord на количество каналов в одной категории — 50. Для большинства серверов это некритично, но если у тебя очень активное комьюнити с сотнями онлайн одновременно и почти каждый плодит свою комнату, можно упереться в лимит в пиковые часы. Решается либо повышением задержки удаления пустых каналов (чтобы освобождались быстрее), либо второй параллельной категорией-хабом с балансировкой между ними — этот сценарий уже редкий, но стоит держать в голове на вырост.

Нужен игровой сервер?

MAATRIX GAMES — 45+ игр, деплой за минуты, DDoS-защита, автобэкапы. Тарифы от 399 ₽/мес.

Создать сервер

Частые вопросы

Бот удаляет канал, пока я ещё говорю с другом, который просто перезашёл?

Это симптом слишком короткой задержки перед удалением. Увеличь таймаут до 10-15 секунд в коде (или в настройках VoiceMaster/Kokoro) — обычно хватает с запасом на реконнект клиента.

Можно ли ограничить, кто видит временную комнату в списке каналов?

Да, через permissionOverwrites — по умолчанию канал виден всем на сервере (ViewChannel: true для @everyone), но можно сделать приватным, скрыв его от всех кроме создателя и тех, кого он явно пригласит командой бота.

VoiceMaster бесплатный лимит в 2 хаба — это мало для нашего сервера с несколькими играми?

Для мультигейм-комьюнити логичнее один хаб с выбором лимита участников через кнопки при заходе, чем несколько отдельных хабов под каждую игру — так проще для новых участников и не упирается в бесплатный лимит бота.

Что если бот с temp-каналами упадёт, пока люди сидят внутри?

Каналы никуда не денутся — они обычные каналы Discord, просто без активного слушателя событий. При перезапуске бота стоит добавить проверку при старте: пройтись по категории и удалить все пустые каналы, оставшиеся от прошлой сессии (память бота tempChannels в примере кода выше не переживает рестарт, если не сохранять её в файл или базу).

Нужны ли отдельные права модераторам на временные комнаты?

Да, обязательно оставь модераторской роли Move Members и Mute Members на уровне категории, независимо от прав владельца temp-канала — иначе при конфликте между игроками модератор физически не сможет вмешаться.