Categoría: Bots de Discord
Crear un bot de Discord#13¡Administra tu servidor automatizando roles y canales! (ejemplo en Python)
Esta entrada es una traducción del original en coreano.
Índice9 secciones
¡Hola! Soy Mojangcam 😎
En el #12 aprendimos a proteger el bot con cooldowns y permisos de comandos. En este #13 vamos un paso más allá y le pasamos al bot, de una vez, todo el trabajo repetitivo de quien administra el servidor. Hoy vamos a crear exactamente tres automatizaciones.
- Dar un rol automáticamente a cada miembro nuevo (rol de bienvenida)
- Que cada quien se asigne el rol que quiera con un botón (panel de botones de roles)
- Crear automáticamente una sala personal al entrar a un canal de voz (Join to Create)
Cuanto más crece el servidor, más se repiten sin fin tareas como "darle un rol al que acaba de llegar", "repartir el rol de los que quieren jugar" o "crear una sala para una reunión". Si lo haces a mano, no terminas ni en toda la noche. Si se lo dejas al bot, lo resuelve solo las 24 horas. ¡Empecemos! 🙌
📌 Lo que necesitas
Primero, preparemos el entorno de desarrollo.
- Python 3.9 o superior (
discord.py2.7 requiere 3.9+) discord.py2.x (la última versión estable a julio de 2026 es la 2.7.1)
Instalar la librería es una sola línea en la terminal.
pip install -U discord.pyAdemás, este episodio necesita sí o sí la configuración de Privileged Intents (intents privilegiados). Para detectar la llegada de miembros nuevos tienes que activar el Server Members Intent.
- Entra al Portal para desarrolladores de Discord → elige tu bot
- Pestaña Bot de la izquierda → Privileged Gateway Intents
- Activa Server Members Intent ✅ (si vas a usar comandos de mensaje, activa también Message Content Intent)
Por último, revisa también los permisos del rol del bot en el servidor. Para que la automatización funcione, el bot necesita los permisos de abajo.
- Gestionar roles (Manage Roles) — poner y quitar roles
- Gestionar canales (Manage Channels) — crear y borrar canales de voz
- Mover miembros (Move Members) — mover miembros a la sala creada
💡 ¡El punto más importante! Los roles que asigne el bot tienen que estar siempre por debajo del rol del propio bot. En Ajustes del servidor → Roles, arrastra el rol del bot hacia arriba. Si se te pasa esto, te saldrá el error
Forbiddenque veremos más adelante.
📌 Paso 1 - Da un rol automáticamente a los miembros nuevos
Empecemos por lo más básico: el rol de bienvenida automático. En el momento en que alguien entra al servidor, se ejecuta el evento on_member_join. Solo hay que buscar el rol ahí y ponérselo.
import discord
from discord.ext import commands
intents = discord.Intents.default()
intents.members = True # Detecta la entrada de miembros (intent privilegiado)
intents.message_content = True # Para comandos con prefijo
bot = commands.Bot(command_prefix="!", intents=intents)
AUTO_ROLE_NAME = "Novato" # Nombre del rol que se da automáticamente al entrar
@bot.event
async def on_member_join(member: discord.Member):
role = discord.utils.get(member.guild.roles, name=AUTO_ROLE_NAME)
if role is not None:
await member.add_roles(role)
print(f"👋 ¡Le di a {member.name} el rol '{role.name}'!")Vamos a verlo parte por parte 💡
intents.members = True: es el interruptor que detecta las entradas y salidas de miembros. Si está apagado,on_member_joinno se ejecuta nunca.on_member_join(member): es el evento que se llama automáticamente cada vez que entra un miembro nuevo. Enmemberviene la información de la persona que entró.discord.utils.get(member.guild.roles, name=...): busca en la lista de roles del servidor el rol cuyo nombre coincide. Si no existe devuelveNone, así que compruébalo siempre.await member.add_roles(role): le pone el rol encontrado a ese miembro. ¡Es la línea clave!
Ahora, si creas antes en el servidor un rol llamado "Novato", los miembros nuevos aparecen con el rol Novato puesto nada más entrar. 🌱
📌 Paso 2 - Que cada quien se asigne roles con un solo botón
En el #5 sobre botones (Button) aprendimos a usar botones, ¿verdad? Esta vez, con esos botones vamos a crear un panel en el que los miembros eligen sus propios roles. Así ningún administrador tiene que ir poniendo roles uno por uno.
Aquí hay un concepto que tienes que conocer sí o sí: la View persistente (Persistent View). Normalmente, al reiniciar el bot los botones dejan de responder, pero si cumples las dos condiciones de abajo, siguen funcionando incluso después de reiniciar.
- La View con
timeout=None - Un
custom_iden todos los botones
# Lista de roles con botón (custom_id, nombre del rol, etiqueta del botón, emoji)
BUTTON_ROLES = [
("role_notice", "Avisos", "Avisos", "🔔"),
("role_game", "Jugadores", "Compañeros de juego", "🎮"),
("role_study", "Estudio", "Grupo de estudio", "📚"),
]
class RoleButton(discord.ui.Button):
def __init__(self, custom_id, role_name, label, emoji):
super().__init__(
label=label, emoji=emoji,
style=discord.ButtonStyle.secondary,
custom_id=custom_id, # ¡La View persistente necesita custom_id!
)
self.role_name = role_name
async def callback(self, interaction: discord.Interaction):
role = discord.utils.get(interaction.guild.roles, name=self.role_name)
if role is None:
await interaction.response.send_message(
f"❌ El rol '{self.role_name}' no existe. ¡Créalo primero en el servidor!",
ephemeral=True)
return
if role in interaction.user.roles:
await interaction.user.remove_roles(role)
msg = f"Te quité el rol '{role.name}'. 👋"
else:
await interaction.user.add_roles(role)
msg = f"¡Te di el rol '{role.name}'! 🎉"
await interaction.response.send_message(msg, ephemeral=True)
class RoleView(discord.ui.View):
def __init__(self):
super().__init__(timeout=None) # ¡La View persistente necesita timeout=None!
for custom_id, role_name, label, emoji in BUTTON_ROLES:
self.add_item(RoleButton(custom_id, role_name, label, emoji))Vamos a verlo parte por parte 💡
custom_id: es la etiqueta con nombre único que le pones a cada botón. Hace que, después de reiniciar, el bot lo reconozca: "¡Ah, este botón es aquel botón!". Es una condición obligatoria de la View persistente.callback(self, interaction): es la función que se ejecuta al pulsar el botón.interaction.useres quien pulsó el botón.role in interaction.user.roles: si ya tiene ese rol, se lo quita (remove_roles); si no, se lo pone (add_roles). Así un solo botón funciona como interruptor.ephemeral=True: es un "mensaje que solo ves tú". Avisa en silencio sin llenar la pantalla de los demás.
Ahora añadimos el comando que publica este panel en el chat y el código que mantiene vivos los botones después de reiniciar.
class MyBot(commands.Bot):
def __init__(self):
super().__init__(command_prefix="!", intents=intents)
async def setup_hook(self):
self.add_view(RoleView()) # Revive los botones tras reiniciar
bot = MyBot()
@bot.command(name="역할판")
@commands.has_permissions(administrator=True)
async def role_panel(ctx: commands.Context):
embed = discord.Embed(
title="🎭 Consigue tus roles",
description="¡Pulsa un botón para recibir el rol! Si lo pulsas otra vez, se quita.",
color=0x5865F2,
)
await ctx.send(embed=embed, view=RoleView())setup_hook+add_view: vuelve a registrar la View persistente cuando el bot arranca. Gracias a estas dos líneas, los botones que publicaste antes siguen funcionando aunque reinicies el bot.@commands.has_permissions(administrator=True): hace que solo los administradores puedan usar el comando!역할판(panel de roles).

📌 Paso 3 - Crea automáticamente un canal de voz personal al entrar
Por último, el Join to Create que hoy en día usan casi todos los servidores. Creas un canal de voz tipo "➕ Crear canal" y, cuando alguien entra, el bot le crea al momento una sala solo para esa persona y la mueve ahí. Cuando la sala se queda vacía desaparece sola, así que el servidor también se mantiene ordenado.
Los cambios de canal de voz se detectan con el evento on_voice_state_update.
CREATOR_CHANNEL_ID = 123456789012345678 # ID del canal de voz '➕ Crear canal'
temp_channels = set() # Guarda los ID de los canales temporales que creamos
@bot.event
async def on_voice_state_update(member, before, after):
# (Crear) Si entra al canal 'Crear', crea un canal personal y lo mueve ahí
if after.channel is not None and after.channel.id == CREATOR_CHANNEL_ID:
new_channel = await member.guild.create_voice_channel(
name=f"🔊 Sala de {member.display_name}",
category=after.channel.category,
)
temp_channels.add(new_channel.id)
await member.move_to(new_channel)
# (Borrar) Elimina el canal temporal que creamos cuando se queda vacío
if before.channel is not None and before.channel.id in temp_channels:
if len(before.channel.members) == 0:
await before.channel.delete()
temp_channels.discard(before.channel.id)Vamos a verlo parte por parte 💡
on_voice_state_update(member, before, after): se ejecuta cada vez que alguien cambia de canal de voz.beforees el canal del que salió yafter, el canal al que entró.after.channel.id == CREATOR_CHANNEL_ID: comprueba si el sitio al que acaba de entrar es el canal "Crear". Si lo es, ¡toca crear una sala nueva!create_voice_channel(name, category): crea el canal de voz personal. Si le pasas la mismacategoryque el original, aparece ordenadito dentro de la misma categoría.member.move_to(new_channel): mueve al miembro, ¡zas!, a la sala recién creada.len(before.channel.members) == 0: si no queda nadie en la sala de la que salió, la borra condelete(). Contemp_channelspusimos un seguro para que solo se borren "las salas que creamos nosotros".
💡 El
CREATOR_CHANNEL_IDlo consigues activando el modo desarrollador en Discord (Ajustes de usuario → Avanzado → Modo desarrollador) y luego haciendo clic derecho en el canal de voz para crear salas → "Copiar ID del canal".
📌 Código completo (puedes copiar y pegar)
Copia el código de abajo, cambia solo TOKEN, AUTO_ROLE_NAME, CREATOR_CHANNEL_ID y BUTTON_ROLES para que encajen con tu servidor, y funciona al instante.
# discord.py 2.x (basado en 2.7.1) / Python 3.9+
import discord
from discord.ext import commands
# ── Configuración ─────────────────────
TOKEN = "PON_AQUI_EL_TOKEN_DE_TU_BOT"
AUTO_ROLE_NAME = "Novato" # Nombre del rol que se da automáticamente al entrar
CREATOR_CHANNEL_ID = 123456789012345678 # ID del canal de voz '➕ Crear canal'
# Lista de roles con botón (custom_id, nombre del rol, etiqueta del botón, emoji)
BUTTON_ROLES = [
("role_notice", "Avisos", "Avisos", "🔔"),
("role_game", "Jugadores", "Compañeros de juego", "🎮"),
("role_study", "Estudio", "Grupo de estudio", "📚"),
]
intents = discord.Intents.default()
intents.members = True # Detecta la entrada de miembros (intent privilegiado)
intents.message_content = True # Para comandos con prefijo
# voice_states ya viene incluido en default().
# ── View de roles con botón ───────────
class RoleButton(discord.ui.Button):
def __init__(self, custom_id, role_name, label, emoji):
super().__init__(
label=label, emoji=emoji,
style=discord.ButtonStyle.secondary,
custom_id=custom_id, # ¡La View persistente necesita custom_id!
)
self.role_name = role_name
async def callback(self, interaction: discord.Interaction):
role = discord.utils.get(interaction.guild.roles, name=self.role_name)
if role is None:
await interaction.response.send_message(
f"❌ El rol '{self.role_name}' no existe. ¡Créalo primero en el servidor!",
ephemeral=True)
return
if role in interaction.user.roles:
await interaction.user.remove_roles(role)
msg = f"Te quité el rol '{role.name}'. 👋"
else:
await interaction.user.add_roles(role)
msg = f"¡Te di el rol '{role.name}'! 🎉"
await interaction.response.send_message(msg, ephemeral=True)
class RoleView(discord.ui.View):
def __init__(self):
super().__init__(timeout=None) # ¡La View persistente necesita timeout=None!
for custom_id, role_name, label, emoji in BUTTON_ROLES:
self.add_item(RoleButton(custom_id, role_name, label, emoji))
# ── Cuerpo del bot ────────────────────
class MyBot(commands.Bot):
def __init__(self):
super().__init__(command_prefix="!", intents=intents)
async def setup_hook(self):
# Vuelve a registrar la View persistente para que los botones funcionen tras reiniciar
self.add_view(RoleView())
bot = MyBot()
temp_channels = set() # Guarda los ID de los canales de voz temporales que creamos
@bot.event
async def on_ready():
print(f"✅ ¡Sesión iniciada como {bot.user}!")
# 1) Da un rol automáticamente al entrar
@bot.event
async def on_member_join(member: discord.Member):
role = discord.utils.get(member.guild.roles, name=AUTO_ROLE_NAME)
if role is not None:
await member.add_roles(role)
print(f"👋 {member.name} → rol '{role.name}' asignado")
# 2) Publica el panel de botones de roles (solo administradores)
@bot.command(name="역할판")
@commands.has_permissions(administrator=True)
async def role_panel(ctx: commands.Context):
embed = discord.Embed(
title="🎭 Consigue tus roles",
description="¡Pulsa un botón para recibir el rol! Si lo pulsas otra vez, se quita.",
color=0x5865F2,
)
await ctx.send(embed=embed, view=RoleView())
# 3) Crea un canal de voz personal al entrar al canal indicado + lo borra cuando queda vacío
@bot.event
async def on_voice_state_update(member, before, after):
# (Crear) Si entra al canal 'Crear', crea un canal personal y lo mueve ahí
if after.channel is not None and after.channel.id == CREATOR_CHANNEL_ID:
new_channel = await member.guild.create_voice_channel(
name=f"🔊 Sala de {member.display_name}",
category=after.channel.category,
)
temp_channels.add(new_channel.id)
await member.move_to(new_channel)
# (Borrar) Elimina el canal temporal que creamos cuando se queda vacío
if before.channel is not None and before.channel.id in temp_channels:
if len(before.channel.members) == 0:
await before.channel.delete()
temp_channels.discard(before.channel.id)
bot.run(TOKEN)📌 Resultado
Enciende el bot y compruébalo uno por uno en tu servidor.
- Entra con una cuenta nueva → se le pone automáticamente el rol "Novato". 🌱
- Un administrador escribe
!역할판→ aparece un embed con 3 botones. Al pulsar un botón responde "¡Te di el rol! 🎉" y, si lo pulsas otra vez, "Te quité el rol 👋", con un mensaje que solo ves tú. - Entra al canal de voz "➕ Crear canal" → al instante se crea "🔊 Sala de (tu nombre)" y te mueve ahí. Cuando sales de la sala, desaparece sin más.
📌 Errores frecuentes
1. discord.errors.Forbidden: 403 Missing Permissions
Causa: el bot no tiene permisos, o el rol que intentas dar está por encima del rol del bot.
Solución: en Ajustes del servidor → Roles, sube el rol del bot y actívale los permisos Gestionar roles / Gestionar canales / Mover miembros. Discord nunca deja tocar "un rol más alto que el propio".
2. on_member_join no se ejecuta nunca
Causa: el Server Members Intent está desactivado.
Solución: activa Server Members Intent en Portal para desarrolladores → Bot → Privileged Gateway Intents, y pon también intents.members = True en el código. Tienen que estar activados los dos para que funcione.
3. Al reiniciar el bot, los botones dicen "Esta interacción ha fallado"
Causa: no cumpliste las condiciones de la View persistente.
Solución: comprueba que la View tenga timeout=None, que todos los botones tengan custom_id y que la vuelvas a registrar con add_view en setup_hook. Los tres van en conjunto.
4. Los canales de voz temporales no se borran
Causa: el bot no tiene el permiso Gestionar canales, o el ID no se guardó en temp_channels.
Solución: revisa el permiso de Gestionar canales y comprueba que guardaste el ID con temp_channels.add(...) al crear el canal.
📌 Para terminar
Hoy creamos tres automatizaciones que le pasan al bot la administración del servidor por completo: rol automático al entrar, asignación de roles por uno mismo con botones y creación automática de canales de voz — solo con estas tres, el trabajo repetitivo de los administradores baja muchísimo. En especial, la View persistente (timeout=None + custom_id + add_view) la vas a usar en cualquier botón o menú que hagas a partir de ahora, ¡así que apréndetela bien! 💪
En el próximo, el #14, veremos cómo guardar los datos del bot con JSON y SQLite. Se trata de que los roles, la configuración y los registros no se pierdan aunque apagues y vuelvas a encender el bot. ¡No te lo pierdas! 🙌
📚 Serie Crear un bot de Discord
- #5 - Interacciones con botones (Button)
- #6 - Menús desplegables (Select Menu)
- #7 - Pedir datos con un modal (Modal)
- #8 - Decorar embeds (Embed)
- #9 - Integración con IA (API de Claude y ChatGPT)
- #10 - Soporte multilingüe (i18n)
- #11 - Panel web con Flask
- #12 - Gestión de cooldowns y permisos de comandos
- #13 - Automatización de roles y canales ← ¡estás leyendo este!
Serie · Parte 13 de 32
Crear un bot de Discord
- #1Crear un bot de Discord - ¡Domina por completo los Slash Commands!
- #2¡Añade opciones (argumentos) a tus Slash Commands!
- #3¡Añade subcomandos (Subcommand) a tus Slash Commands!
- #4¡Envía embeds con Slash Commands!
- #5¡Implementa interacciones con botones (Button)!
- #6¡Implementa menús desplegables (Select Menu)!
- #7Pide texto al usuario con un modal (Modal) (ejemplo en Python)
- #8Decora embeds (Embed) con imágenes y campos (ejemplo en Python)
- #9¡Integra la IA (API de Claude y ChatGPT)! (ejemplo en Python)
- #10¡Añade soporte multilingüe (i18n)! (ejemplo en Python)
- #11¡Crea un panel web con Flask! (ejemplo en Python)
- #12¡Gestiona los cooldowns y permisos de tus comandos! (ejemplo en Python)
- #13¡Administra tu servidor automatizando roles y canales! (ejemplo en Python)Estás aquí
- #14¡Guarda tus datos! JSON vs SQLite (ejemplo en Python)
- #15슬래시 명령어 그룹(Group)으로 명령어 정리하기! (파이썬 예제) (en coreano)
- #16리마인더·알림 기능 만들기! 봇이 먼저 말 걸게 하기 (파이썬 예제) (en coreano)
- #17RAG로 문서 기반 답변봇 만들기! 우리 서버 전용 FAQ 봇 (파이썬 예제) (en coreano)
- #18투표(Poll) 기능 만들기! 다수결로 정하는 서버 규칙 (파이썬 예제) (en coreano)
- #19Cog로 코드 쪼개기! 1000줄 봇도 안 더러워지는 구조 (파이썬 예제) (en coreano)
- #20자동 환영 메시지·온보딩 만들기! 규칙 동의 버튼으로 자동 역할 부여 (파이썬 예제) (en coreano)
- #21키워드 자동응답·금지어 필터 만들기! 경고 누적으로 자동 제재까지 (파이썬 예제) (en coreano)
- #22슬래시 명령어 자동완성(Autocomplete) 만들기! 금지어를 골라서 추가·삭제 (파이썬 예제) (en coreano)
- #23aiosqlite로 비동기 DB 제대로 설계하기! JSON 탈출 (파이썬 예제) (en coreano)
- #24채널 난독화(Channel Obfuscation) 대응하기! 11/16부터 안 보이는 채널이 생겨요 (파이썬 예제) (en coreano)
- #25봇이 죽지 않게! 에러 핸들링·로깅 체계 잡기 (파이썬 예제) (en coreano)
- #26티켓(Ticket) 문의 시스템 만들기! 버튼 하나로 1:1 상담 채널 자동 생성 (파이썬 예제) (en coreano)
- #27티켓 담당자 배정·우선순위 만들기! 스태프가 여러 명이어도 안 꼬이게 (파이썬 예제) (en coreano)
- #28레벨·경험치(XP) 시스템 만들기! 채팅할수록 레벨업, 랭킹판까지 (파이썬 예제) (en coreano)
- #29화폐·상점(Economy) 시스템 만들기! 코인 벌어서 아이템 사기 (파이썬 예제) (en coreano)
- #30¡Crea un bot de encuestas y votaciones (Poll)! Encuestas nativas + botones con recuento en tiempo real (ejemplo en Python) 🗳️
- #32¡Crea un canal de registros y auditoría (Audit Log) del servidor! Registra automáticamente mensajes borrados y editados, entradas, salidas y expulsiones (ejemplo en Python) 🕵️
- #33임시 음성방 확장, 파티방 만들기! 방장 위임·강퇴·초대 전용(비공개) 방 + 전용 채팅방 자동 생성 (파이썬 예제) 🎉 (en coreano)
