Canales de voz temporales en Discord: un bot «Crear sala» en Python que los borra al vaciarse
Un servidor con diez canales de voz fijos casi siempre tiene ocho vacíos y dos llenos de gente que no se conoce. Los canales temporales lo arreglan: hay un solo canal «Crear sala», quien entra recibe una sala con su nombre y pasa a ella solo, la gestiona él (nombre, límite de gente, a quién echar) y, cuando sale el último, la sala desaparece. Aquí lo haces tú en discord.py, en un fichero, sin anuncios ni marcas de otros, y sin perder nada al reiniciar.
Qué hace el bot
Eliges un canal de voz como «Crear sala» con /salas-configurar. Cuando alguien entra en él, el bot crea un canal de voz en la misma categoría, con el nombre «Sala de» y su nombre, y le mueve dentro. La sala copia los permisos de «Crear sala»: si ese canal solo lo ven los socios, sus salas también.
Quien la crea es su dueño: el bot le da «Gestionar canales» y «Mover miembros» solo en su sala, así que desde Discord (clic derecho → «Editar canal») le cambia el nombre o le pone un límite de gente (con el límite igual a los que ya estáis, no entra nadie más), y puede desconectar a quien moleste. No hacen falta comandos para eso.
Cuando sale el último, el bot borra la sala. Una persona solo tiene una sala a la vez: si vuelve a entrar en «Crear sala», va a la suya. Y si el bot estaba apagado cuando se vació alguna, la borra al volver: las salas que ha creado van apuntadas en salas.json.
¿Prefieres no montarlo tú? Hosting de bots de Discord desde 1,50 € al mes, sin permanencia y con devolución íntegra en 72 horas. O crea un bot de Discord gratis, sin tarjeta.
1. Crea el bot en Discord
Crea la aplicación y saca el token
En discord.com/developers/applications, «New Application»; en la pestaña «Bot», «Reset Token» y copia el token. Es la contraseña del bot: no lo compartas ni lo subas a GitHub. Si es tu primer bot, el proceso con calma está en tu primer bot en Python.
Invítalo con estos cuatro permisos
En «OAuth2» → «URL Generator», marca «bot» y «applications.commands», y en los permisos «Ver canales», «Gestionar canales», «Mover miembros» y «Conectarse» (en el portal salen en inglés: View Channels, Manage Channels, Move Members y Connect). La calculadora de permisos de bot te da el enlace ya hecho. Los «Privileged Gateway Intents» se quedan apagados: los estados de voz no son privilegiados.
2. Prepara el canal «Crear sala»
Crea una categoría (por ejemplo, «Salas») y dentro un canal de voz llamado «➕ Crear sala». Las salas nacen en esa misma categoría.
Los permisos del bot tienen que valer en esa categoría. Si la categoría está cerrada a @everyone, añade el rol del bot en «Editar categoría» → «Permisos» con los cuatro de arriba. Y no le des a «Crear sala» permisos que el bot no tenga: Discord no deja que un bot reparta en un canal nuevo permisos que él no tiene, y la sala no se crearía.
«Gestionar canales» es de los permisos que piden verificación en dos pasos si tu servidor la exige para moderar: en ese caso, la cuenta dueña del bot tiene que tenerla activada.
3. El código
Tres ficheros en una carpeta. El token va en .env, sin comillas. Hace falta Python 3.10 o más nuevo. Las salas se apuntan en salas.json, que el bot crea solo junto a app.py.
discord.py
python-dotenvDISCORD_TOKEN=pega-aquí-tu-tokenimport json
import os
from pathlib import Path
import discord
from discord import app_commands
from dotenv import load_dotenv
load_dotenv() # lee el token del fichero .env
# Qué canal «Crear sala» tiene cada servidor y qué salas ha creado el bot (y de quién son).
DATOS = Path("salas.json")
# Lo que el bot necesita en la categoría de «Crear sala», con el nombre que enseña Discord.
NECESARIOS = {
"view_channel": "Ver canales",
"manage_channels": "Gestionar canales",
"move_members": "Mover miembros",
"connect": "Conectarse",
}
def leer() -> dict:
return json.loads(DATOS.read_text("utf-8")) if DATOS.exists() else {"crear": {}, "salas": {}}
def guardar(datos: dict) -> None:
DATOS.write_text(json.dumps(datos, ensure_ascii=False, indent=2), "utf-8")
class Bot(discord.Client):
def __init__(self):
# Los estados de voz ya vienen en los intents por defecto: no hay que activar nada en el portal.
super().__init__(intents=discord.Intents.default())
self.tree = app_commands.CommandTree(self)
async def setup_hook(self):
await self.tree.sync() # publica /salas-configurar y /salas-quitar
bot = Bot()
def olvidar(id_sala: int) -> None:
datos = leer()
datos["salas"].pop(str(id_sala), None)
guardar(datos)
async def borrar_si_vacia(sala: discord.VoiceChannel) -> None:
if sala.voice_states: # aún queda alguien dentro
return
try:
await sala.delete(reason="Sala temporal vacía")
except discord.NotFound:
pass # alguien la borró a mano
except discord.Forbidden:
print(f"No puedo borrar {sala.name}: me falta «Gestionar canales».")
return
olvidar(sala.id)
async def crear_sala(miembro: discord.Member, crear: discord.VoiceChannel) -> None:
guild = miembro.guild
datos = leer()
# Una sala por persona: si ya tiene una, va a la suya en vez de crear otra.
for id_sala, dueno in datos["salas"].items():
sala = guild.get_channel(int(id_sala))
if dueno == miembro.id and sala is not None:
try:
await miembro.move_to(sala, reason="Vuelve a su sala")
except discord.HTTPException:
pass # ya no está en «Crear sala»
return
# La sala deja entrar a los mismos que «Crear sala», y su dueño la gestiona desde Discord.
permisos = dict(crear.overwrites)
permisos[miembro] = discord.PermissionOverwrite(connect=True, manage_channels=True, move_members=True)
permisos[guild.me] = discord.PermissionOverwrite(view_channel=True, connect=True, manage_channels=True, move_members=True)
sala = await guild.create_voice_channel(
f"Sala de {miembro.display_name}"[:100], # Discord no deja nombres de más de 100 caracteres
category=crear.category,
overwrites=permisos,
reason=f"Sala temporal de {miembro}",
)
datos["salas"][str(sala.id)] = miembro.id
guardar(datos)
try:
await miembro.move_to(sala, reason="Su sala temporal")
except discord.HTTPException:
# Se fue de «Crear sala» antes de moverlo (error 40032): la sala sobra.
await borrar_si_vacia(sala)
@bot.event
async def on_voice_state_update(miembro: discord.Member, antes: discord.VoiceState, despues: discord.VoiceState):
if antes.channel == despues.channel or miembro.bot:
return # se ha silenciado o ha puesto la cámara: no ha cambiado de canal
datos = leer()
id_crear = datos["crear"].get(str(miembro.guild.id))
if despues.channel is not None and despues.channel.id == id_crear:
try:
await crear_sala(miembro, despues.channel)
except discord.HTTPException as error:
print(f"No he podido crear la sala de {miembro}: {error!r}")
if antes.channel is not None and str(antes.channel.id) in datos["salas"]:
await borrar_si_vacia(antes.channel)
@bot.event
async def on_ready():
# Las salas que se vaciaron mientras el bot estaba apagado se borran al volver.
for id_sala in list(leer()["salas"]):
sala = bot.get_channel(int(id_sala))
if sala is None:
olvidar(int(id_sala)) # ya no existe
else:
await borrar_si_vacia(sala)
print(f"Conectado como {bot.user}")
@bot.tree.command(name="salas-configurar", description="Elige el canal de voz que crea una sala para cada uno")
@app_commands.guild_only()
@app_commands.default_permissions(manage_guild=True)
@app_commands.describe(canal="El canal de voz «Crear sala»")
async def salas_configurar(interaction: discord.Interaction, canal: discord.VoiceChannel):
permisos = canal.permissions_for(interaction.guild.me)
faltan = [nombre for clave, nombre in NECESARIOS.items() if not getattr(permisos, clave)]
if faltan:
await interaction.response.send_message(
f"En {canal.mention} me falta: {', '.join(faltan)}. Dámelo en la categoría y vuelve a probar.", ephemeral=True)
return
datos = leer()
datos["crear"][str(interaction.guild_id)] = canal.id
guardar(datos)
await interaction.response.send_message(f"Hecho: quien entre en {canal.mention} tendrá su propia sala.", ephemeral=True)
@bot.tree.command(name="salas-quitar", description="Deja de crear salas temporales en este servidor")
@app_commands.guild_only()
@app_commands.default_permissions(manage_guild=True)
async def salas_quitar(interaction: discord.Interaction):
datos = leer()
datos["crear"].pop(str(interaction.guild_id), None)
guardar(datos)
await interaction.response.send_message("Hecho: ya no creo salas nuevas. Las que hay se borran al vaciarse.", ephemeral=True)
bot.run(os.environ["DISCORD_TOKEN"])Cómo funciona por dentro
Todo pasa en on_voice_state_update, el aviso que Discord manda cada vez que alguien entra, sale o se cambia de canal de voz (también al silenciarse o poner la cámara: por eso lo primero es mirar si de verdad ha cambiado de canal). Si el canal nuevo es «Crear sala», crea la sala; si el que deja es una sala del bot y ya no queda nadie, la borra.
Para mover a alguien, Discord pide al bot «Mover miembros» y poder entrar en el canal de destino. Por eso el bot se da a sí mismo «Conectarse» en cada sala, aunque la categoría esté cerrada. Si la persona sale de «Crear sala» antes de que la mueva, Discord contesta con el error 40032 («el usuario no está conectado a voz») y el bot borra la sala que acaba de crear.
Las salas se cuentan con voice_states, que no depende de la lista de miembros: no hace falta el intent privilegiado de miembros.
4. Pruébalo
Quién puede usar /salas-configurar y /salas-quitar se cambia sin tocar el código en «Ajustes del servidor» → «Integraciones» → tu bot.
Arranca el bot
En una terminal, en la carpeta:
pip install -r requirements.txtypython app.py. Cuando diga «Conectado como…», ya está. Los comandos pueden tardar unos minutos en salir la primera vez.Elige el canal
Escribe /salas-configurar y elige «➕ Crear sala». Si al bot le falta algún permiso en esa categoría, te dice cuál.
Entra y sal
Entra en «➕ Crear sala»: en un segundo estás en «Sala de» y tu nombre. Haz clic derecho en ella → «Editar canal» y ponle un límite de gente. Sal: la sala desaparece.
Si algo no va
Entras en «Crear sala» y no pasa nada: el bot está apagado o no tiene «Crear sala» configurado en este servidor (vuelve a usar /salas-configurar). Mira la consola: si pone «No he podido crear la sala…» con «Missing Permissions», al bot le falta un permiso en la categoría o «Crear sala» tiene un permiso que el bot no tiene. Los códigos, en Missing Access y Missing Permissions.
La sala se crea pero no te mueve: falta «Mover miembros» o «Conectarse» en la categoría. La sala no se borra al vaciarse: falta «Gestionar canales» (la consola lo dice).
No salen los comandos: qué mirar cuando no aparecen. Si el bot está en línea y no hace nada, mi bot no responde.
5. Déjalo encendido siempre (gratis)
Este bot solo sirve si está encendido cuando alguien entra en «Crear sala»: si vive en tu ordenador y lo apagas, el canal se queda como un canal de voz normal y las salas que estaban en uso no se borran hasta que vuelva. Hay que alojarlo en un servidor.
En NapNodes: crea el bot gratis (nace en Node.js; para este, en «Configuración» → «Lenguaje», «Pasar a Python»), sube los tres ficheros en «Archivos» y pulsa «Encender». Las librerías de requirements.txt se instalan solas al arrancar y app.py es el fichero de inicio que viene puesto. salas.json aparece en «Archivos» junto a app.py.
El bot gratis tiene 256 MB, de sobra para este bot aunque tu servidor tenga muchas salas a la vez, y se renueva con un clic cada semana: si se te pasa, se apaga hasta que lo enciendes. Para no tener que acordarte, el plan Mini (512 MB) está encendido siempre sin renovar nada (precios de los bots). Todo el montaje, en la guía de hosting para tu bot.
Ideas para ampliarlo
Un nombre mejor: en vez de «Sala de», el juego al que está jugando quien la crea (miembro.activity, que pide el intent privilegiado de presencias) o un nombre que el dueño elija con un comando.
Un /sala-reclamar para que, si el dueño se va y la sala sigue llena, otro se quede con los permisos de dueño: cambia el dueño en salas.json y pasa sus permisos con sala.set_permissions. O juntarlo con el bot de moderación y el de bienvenida en uno solo: los tres caben en el gratis.
Preguntas frecuentes
¿Cómo hago canales de voz temporales en Discord?
Con un bot: un canal «Crear sala» que, cuando alguien entra, crea un canal de voz para esa persona, la mueve dentro y lo borra cuando se vacía. El código completo en Python está en esta guía.
¿Discord tiene canales de voz temporales sin bot?
No: los canales de voz de Discord se quedan hasta que alguien los borra. Para que se creen y se borren solos hace falta un bot.
¿Qué permisos necesita el bot?
Ver canales, Gestionar canales, Mover miembros y Conectarse, en la categoría donde está «Crear sala». No necesita ningún intent privilegiado.
¿El dueño de la sala puede cambiarle el nombre o el límite?
Sí: el bot le da «Gestionar canales» y «Mover miembros» solo en su sala, así que lo cambia desde Discord con clic derecho → «Editar canal», sin comandos.
¿Dónde dejo el bot encendido siempre?
En un servidor. En NapNodes hay un bot gratis de 256 MB que se renueva con un clic cada semana, o el plan Mini, encendido siempre.
¿Te ha servido esta guía?
¿Lo montamos?
Hosting de bots de Discord desde 1,50 € al mes, sin sobreventa: tu RAM es solo tuya. Y si quieres empezar sin pagar, tienes un bot de Discord gratis, sin tarjeta.
Herramienta gratis: Permisos de un bot de Discord. Marca los permisos de tu bot en castellano y sal con el número y el enlace de invitación; o pega un número y mira qué permisos da.
Sigue leyendo
- Bot contador de miembros para Discord: canales con miembros, conectados y mejoras (Python y JavaScript)
Haz tu bot contador de miembros de Discord: canales de voz con miembros, conectados y mejoras que se ponen al día solos. Código en discord.py y discord.js.
- Bot de Discord que avisa de tus directos de Twitch y tus vídeos de YouTube
Haz un bot de Discord que avisa en un canal cuando empiezas directo en Twitch o subes un vídeo a YouTube: código en Python, claves y dónde dejarlo encendido.
- Bot de sorteos para Discord en Python: botón para participar, ganadores al azar y repetir
Haz tu bot de sorteos de Discord en Python: /sorteo con botón para participar, cuenta atrás, ganadores al azar y /resortear. Código completo, sin anuncios.