Categoría: Bots de Discord
Crear un bot de Discord#6¡Implementa menús desplegables (Select Menu)!
Esta entrada es una traducción del original en coreano.
Índice8 secciones
¡Hola! Soy Mojangcam 😎
En el episodio #5 vimos cómo interactuar con el bot mediante botones (Button), ¿verdad? Esta vez vamos un paso más allá y crearemos un menú desplegable (Select Menu).
Los botones van bien cuando hay 2 o 3 opciones, pero cuando pasas de 5, la pantalla se llena de botones. En esos casos, con un menú desplegable puedes meter hasta 25 opciones de forma ordenada en un solo menú. ¡Se usa muchísimo para elegir roles, hacer encuestas o seleccionar servidores de juego!

📌 Lo que necesitas
- Python 3.8 o superior
- discord.py 2.x (¡los Select Menu están disponibles desde la 2.0!)
- Token del bot + el bot ya invitado a tu servidor (¡mira el episodio 1!)
pip install -U discord.pyPuedes comprobar la versión con pip show discord.py. ¡Si es 2.x o superior, todo listo! En la versión 1.x no funciona.
📌 Paso 1 - Crear la clase Select (menú desplegable)
La clave del menú desplegable es la clase discord.ui.Select. Heredas de esta clase y defines las opciones (SelectOption) y lo que ocurre cuando se elige una (callback).
import discord
from discord.ext import commands
class GameSelect(discord.ui.Select):
def __init__(self):
options = [
discord.SelectOption(label="Minecraft", value="minecraft", description="Un mundo hecho de bloques", emoji="⛏️"),
discord.SelectOption(label="ARK Survival", value="ark", description="¡Sobrevive entre dinosaurios!", emoji="🦖"),
discord.SelectOption(label="Valorant", value="valorant", description="FPS táctico 5 contra 5", emoji="🔫"),
]
super().__init__(
placeholder="¡Selecciona tu juego favorito!",
min_values=1,
max_values=1,
options=options,
)
async def callback(self, interaction: discord.Interaction):
await interaction.response.send_message(
f"🎮 ¡{interaction.user.mention} eligió **{self.values[0]}**!"
)Vamos a verlo parte por parte 💡
SelectOption: cada una de las opciones que aparecen en el menú.labeles el nombre que se ve en pantalla,valuees el valor que recibes en el código,descriptiones el texto de ayuda en gris yemojies el emoji que va delante.placeholder: el texto de ayuda que se muestra cuando todavía no se ha elegido nada.min_values/max_values: el número mínimo y máximo de opciones que se pueden elegir. ¡Conmax_values=3se convierte en un menú de selección múltiple! (máximo 25)callback: la función que se ejecuta cuando el usuario termina de elegir. Los valores elegidos llegan en la listaself.values.
📌 Paso 2 - Meterlo en una View
Como vimos en el episodio de los botones, todos los componentes de UI se tienen que enviar dentro de un contenedor llamado View.
class GameSelectView(discord.ui.View):
def __init__(self):
super().__init__(timeout=60) # Se desactiva tras 60 segundos
self.add_item(GameSelect())Por cierto, cada menú desplegable ocupa una fila (row) entera de la View. Una View tiene como máximo 5 filas, así que puedes meter hasta 5 menús desplegables en una sola View.
📌 Paso 3 - Enviarlo con un Slash Command
intents = discord.Intents.default()
bot = commands.Bot(command_prefix="!", intents=intents)
@bot.event
async def on_ready():
await bot.tree.sync()
print(f"✅ ¡El bot {bot.user} está listo!")
@bot.tree.command(name="게임선택", description="¡Elige tu juego favorito!")
async def game_select(interaction: discord.Interaction):
await interaction.response.send_message("¡Elige una opción aquí abajo! 👇", view=GameSelectView())
bot.run("Token")📌 Código completo (puedes copiar y pegar)
# discord.py 2.x / Python 3.8+
import discord
from discord.ext import commands
class GameSelect(discord.ui.Select):
def __init__(self):
options = [
discord.SelectOption(label="Minecraft", value="Minecraft", description="Un mundo hecho de bloques", emoji="⛏️"),
discord.SelectOption(label="ARK Survival", value="ARK Survival", description="¡Sobrevive entre dinosaurios!", emoji="🦖"),
discord.SelectOption(label="Valorant", value="Valorant", description="FPS táctico 5 contra 5", emoji="🔫"),
]
super().__init__(placeholder="¡Selecciona tu juego favorito!", min_values=1, max_values=1, options=options)
async def callback(self, interaction: discord.Interaction):
await interaction.response.send_message(
f"🎮 ¡{interaction.user.mention} eligió **{self.values[0]}**!"
)
class GameSelectView(discord.ui.View):
def __init__(self):
super().__init__(timeout=60)
self.add_item(GameSelect())
intents = discord.Intents.default()
bot = commands.Bot(command_prefix="!", intents=intents)
@bot.event
async def on_ready():
await bot.tree.sync()
print(f"✅ ¡El bot {bot.user} está listo!")
@bot.tree.command(name="게임선택", description="¡Elige tu juego favorito!")
async def game_select(interaction: discord.Interaction):
await interaction.response.send_message("¡Elige una opción aquí abajo! 👇", view=GameSelectView())
bot.run("Token")📌 Resultado
Al escribir /게임선택 (elegir juego), el bot envía un mensaje con un menú desplegable, y cuando eliges un juego en el menú responde "🎮 ¡OO eligió Minecraft!".
💡 Extra: ¡también hay menús desplegables especiales para elegir usuarios, roles y canales! Con discord.ui.UserSelect, discord.ui.RoleSelect y discord.ui.ChannelSelect no tienes que crear las opciones a mano: aparece automáticamente la lista de usuarios/roles/canales del servidor. ¡Si surge la ocasión, lo veremos en detalle más adelante!
📌 Errores frecuentes
1. This interaction failed (La interacción ha fallado)
Ocurre si no respondes dentro de 3 segundos en el callback. Si el proceso tarda, llama primero a await interaction.response.defer() y luego responde con interaction.followup.send().
2. El menú desplegable aparece gris y desactivado
Cuando pasa el tiempo de View(timeout=60), el componente caduca. Si el menú tiene que seguir funcionando, pon timeout=None. (Para que se mantenga incluso después de reiniciar el bot, necesitas una persistent view + indicar un custom_id, ¡pero eso lo veremos en un episodio avanzado!)
3. TypeError: __init__() got an unexpected keyword argument 'options'
Este error aparece cuando le pasas options a un Select especial como UserSelect en lugar de discord.ui.Select. Cuando definas tus propias opciones, usa siempre el discord.ui.Select básico.
📌 Para terminar
Hoy vimos cómo meter hasta 25 opciones de forma ordenada con un menú desplegable (Select Menu). Ya hemos pasado de los botones a los menús desplegables, así que en el próximo episodio veremos cómo pedir al usuario que escriba texto con un modal (Modal). ¡No te lo pierdas! 🙌
📚 Serie Crear un bot de Discord
- Domina por completo los Slash Commands
- Añade opciones (argumentos) a tus Slash Commands
- Añade subcomandos a tus Slash Commands
- Envía embeds con Slash Commands
- Implementa interacciones con botones (Button)
- Implementa menús desplegables (Select Menu) ← ¡estás leyendo este!
Serie · Parte 6 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)!Estás aquí
- #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)
- #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)
