aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Discord Bots

Discord Temporary Voice Channel (Temp Voice) System

A Discord temporary voice channel (temp voice) system gets rid of the dozens of empty, static voice rooms that pile up on busy servers: a user joins one "create a room" channel, the bot instantly builds a private voice channel for them and moves them in, and the channel deletes itself once it's empty. In this guide we'll build that system from scratch with discord.js v14. The whole thing really hinges on a single event: voiceStateUpdate.

How the system works

The idea is simple, but it helps to make the flow explicit:

  • The server has one "lobby" voice channel (e.g. "➕ Create Room"). Nobody stays here; it's just a trigger.
  • When a user joins that channel, the bot creates a new voice channel for them under the same category and moves the user into it.
  • The room owner is granted management rights over the channel (rename, kick, set a limit).
  • When the last person leaves, the bot deletes the channel, so no junk channels build up behind the scenes.

Requirements and intents

To receive voice state events the bot needs the GuildVoiceStates intent. To create channels and move members, the bot's role must have Manage Channels and Move Members permissions. Let's set up the client first:

// index.js
const {
  Client, GatewayIntentBits, ChannelType, PermissionFlagsBits,
} = require('discord.js');

const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildVoiceStates,
  ],
});

// ID of the "Create Room" lobby channel
const LOBBY_ID = '123456789012345678';

// Temp channels we created: channelId -> ownerId
const tempChannels = new Map();

client.login(process.env.TOKEN);

The tempChannels map is crucial: the bot must only delete channels it created itself. This record is what keeps it from touching any other voice channel on the server.

Creating a room on join

The voiceStateUpdate event fires on every voice state change with two parameters: oldState (the previous state) and newState (the new one). We know a user joined the lobby when newState.channelId equals the lobby ID:

client.on('voiceStateUpdate', async (oldState, newState) => {
  // Did the user join the lobby channel?
  if (newState.channelId !== LOBBY_ID) return;

  const { guild, member } = newState;

  const channel = await guild.channels.create({
    name: `${member.displayName}'s room`,
    type: ChannelType.GuildVoice,
    parent: newState.channel.parentId, // same category as the lobby
    permissionOverwrites: [
      {
        id: member.id,
        allow: [
          PermissionFlagsBits.ManageChannels,
          PermissionFlagsBits.MoveMembers,
        ],
      },
    ],
  });

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

Here we pass the lobby's category as parent, so the new rooms stay neatly grouped under the same folder. With permissionOverwrites we grant the owner the right to manage their own channel. The last line, member.voice.setChannel, pulls the user into the new room.

Auto-deleting the empty room

A second voiceStateUpdate listener catches the moment someone leaves a channel. If oldState.channel exists and nobody is left in it (members.size === 0) and that channel is one of our tracked temp channels, we delete it:

client.on('voiceStateUpdate', async (oldState) => {
  const left = oldState.channel;
  if (!left) return;

  // Is it one of ours and now empty?
  if (tempChannels.has(left.id) && left.members.size === 0) {
    await left.delete().catch(() => {});
    tempChannels.delete(left.id);
  }
});

The .catch(() => {}) matters: if the channel is already gone for another reason (manual delete, a permission change), it prevents the bot from crashing. The tempChannels.delete call keeps the map clean.

Polish and resilience

The core system works; a few additions that make it usable on a real server:

  • User limit: add userLimit: 5 to create to keep rooms small.
  • Spam protection: if a user joins and leaves quickly, many channels can spawn. Add a per-user cooldown.
  • Restarts: when the bot restarts, the tempChannels map resets. It's wise to scan the category on startup and clean up any leftover empty temp channels.
  • Name/limit commands: with slash commands you can let the owner rename or lock their channel.

Frequently asked questions

The bot isn't creating channels, why?

The two most common causes: the bot's role is missing Manage Channels and Move Members, or you forgot to add the GuildVoiceStates intent. Also, the bot's role must be allowed to create channels in that category given its permission overrides.

Will performance be an issue on a server with hundreds of members?

No. Because it's event-driven, it only fires when someone joins the lobby or leaves a channel. The create/delete requests are subject to Discord API rate limits, but normal usage stays well within them.

Do I need a database?

For small servers, no — an in-memory Map is enough. For very large or multi-bot setups, storing the temp channel IDs in a lightweight database like SQLite makes the system survive restarts.

Want a ready-made, resilient temp voice system for your community? I build custom Discord bots with temporary voice channels, a room control panel, and moderation integration — get in touch.

Bu kategorideki tüm yazılar →

Devamı için