Un système de salon vocal temporaire Discord (temp voice) élimine les dizaines de salons vocaux fixes et vides qui s'accumulent sur les serveurs actifs : un utilisateur rejoint un unique salon « créer une salle », le bot lui construit aussitôt un salon vocal privé et l'y déplace, puis le salon se supprime de lui-même dès qu'il est vide. Dans ce guide, nous construisons ce système de zéro avec discord.js v14. Tout repose en réalité sur un seul événement : voiceStateUpdate.
Comment fonctionne le système
L'idée est simple, mais clarifier le flux aide :
- Le serveur possède un salon vocal « lobby » (par ex. « ➕ Créer une salle »). Personne n'y reste ; c'est juste un déclencheur.
- Quand un utilisateur rejoint ce salon, le bot crée un nouveau salon vocal pour lui sous la même catégorie et l'y déplace.
- Le propriétaire de la salle reçoit des droits de gestion sur le salon (renommer, expulser, fixer une limite).
- Quand la dernière personne part, le bot supprime le salon, pour qu'aucun salon fantôme ne s'accumule en coulisses.
Prérequis et intents
Pour recevoir les événements d'état vocal, le bot a besoin de l'intent GuildVoiceStates. Pour créer des salons et déplacer des membres, le rôle du bot doit avoir les permissions Gérer les salons et Déplacer des membres. Configurons d'abord le client :
// index.js
const {
Client, GatewayIntentBits, ChannelType, PermissionFlagsBits,
} = require('discord.js');
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildVoiceStates,
],
});
// ID du salon lobby "Créer une salle"
const LOBBY_ID = '123456789012345678';
// Salons temporaires créés : channelId -> ownerId
const tempChannels = new Map();
client.login(process.env.TOKEN);
La map tempChannels est essentielle : le bot ne doit supprimer que les salons qu'il a lui-même créés. C'est ce registre qui l'empêche de toucher aux autres salons vocaux du serveur.
Créer une salle à l'arrivée
L'événement voiceStateUpdate se déclenche à chaque changement d'état vocal avec deux paramètres : oldState (l'état précédent) et newState (le nouveau). On sait qu'un utilisateur a rejoint le lobby quand newState.channelId est égal à l'ID du lobby :
client.on('voiceStateUpdate', async (oldState, newState) => {
// L'utilisateur a-t-il rejoint le salon lobby ?
if (newState.channelId !== LOBBY_ID) return;
const { guild, member } = newState;
const channel = await guild.channels.create({
name: `Salle de ${member.displayName}`,
type: ChannelType.GuildVoice,
parent: newState.channel.parentId, // même catégorie que le lobby
permissionOverwrites: [
{
id: member.id,
allow: [
PermissionFlagsBits.ManageChannels,
PermissionFlagsBits.MoveMembers,
],
},
],
});
tempChannels.set(channel.id, member.id);
await member.voice.setChannel(channel);
});
Ici, on passe la catégorie du lobby comme parent, pour que les nouvelles salles restent bien regroupées sous le même dossier. Avec permissionOverwrites, on accorde au propriétaire le droit de gérer son propre salon. La dernière ligne, member.voice.setChannel, déplace l'utilisateur dans la nouvelle salle.
Supprimer automatiquement la salle vide
Un second écouteur voiceStateUpdate capture le moment où quelqu'un quitte un salon. Si oldState.channel existe et qu'il n'y reste personne (members.size === 0) et que ce salon est l'un de nos salons temporaires suivis, on le supprime :
client.on('voiceStateUpdate', async (oldState) => {
const left = oldState.channel;
if (!left) return;
// Est-ce l'un des nôtres et désormais vide ?
if (tempChannels.has(left.id) && left.members.size === 0) {
await left.delete().catch(() => {});
tempChannels.delete(left.id);
}
});
Le .catch(() => {}) compte : si le salon a déjà disparu pour une autre raison (suppression manuelle, changement de permission), il évite que le bot ne plante. L'appel tempChannels.delete garde la map propre.
Finitions et robustesse
Le système de base fonctionne ; quelques ajouts pour le rendre utilisable sur un vrai serveur :
- Limite d'utilisateurs : ajoutez
userLimit: 5àcreatepour garder de petites salles. - Protection anti-spam : si un utilisateur entre et sort vite, beaucoup de salons peuvent apparaître. Ajoutez un cooldown par utilisateur.
- Redémarrages : au redémarrage du bot, la map
tempChannelsest réinitialisée. Il est sage de parcourir la catégorie au démarrage pour nettoyer les salons temporaires vides restants. - Commandes nom/limite : avec des commandes slash, vous pouvez laisser le propriétaire renommer ou verrouiller son salon.
Questions fréquentes
Le bot ne crée pas de salons, pourquoi ?
Les deux causes les plus courantes : le rôle du bot n'a pas Gérer les salons et Déplacer des membres, ou vous avez oublié d'ajouter l'intent GuildVoiceStates. De plus, le rôle du bot doit être autorisé à créer des salons dans cette catégorie selon ses surcharges de permissions.
Y aura-t-il un souci de performance sur un serveur de centaines de membres ?
Non. Comme il est piloté par les événements, il ne se déclenche que lorsqu'on rejoint le lobby ou quitte un salon. Les requêtes de création/suppression sont soumises aux limites de débit de l'API Discord, mais un usage normal reste largement en deçà.
Ai-je besoin d'une base de données ?
Pour les petits serveurs, non — une Map en mémoire suffit. Pour de très gros déploiements ou plusieurs bots, stocker les ID des salons temporaires dans une base légère comme SQLite permet de survivre aux redémarrages.
Vous voulez un système temp voice prêt à l'emploi et robuste pour votre communauté ? Je crée des bots Discord sur mesure avec salons vocaux temporaires, panneau de gestion de salle et intégration de modération — contactez-moi.