Skip to content

Instantly share code, notes, and snippets.

@lykn
Last active September 22, 2024 14:50
Show Gist options
  • Save lykn/a2b68cb790d6dad8ecff75b2aa450f23 to your computer and use it in GitHub Desktop.
Save lykn/a2b68cb790d6dad8ecff75b2aa450f23 to your computer and use it in GitHub Desktop.
A gist explaining the right way to make drop down menus/select menus/selects in discord.py v2(version 2.0.0a)

Note: Before we go any further. Let me make this real clear that the following gist uses the OFFICIAL discord.py library and not forks like discord_components, nextcord, etc... So when your using this code but a different library and fuck up don't comment something mean or go to a help channel in the server and say "this gist is misleading" or "bad gist who wrote this" when your at fault, trust me I'm going to fuck you up😅

Just a reminder^^

Related Links:

DPY's Docs

Discord's Docs

Code(GitHub)

Python

Python(Downloads)

Selects are a new feature added to discord in the brand new 2.0.0a update. Read more about it here(in the discord.py server)

image

How can I install discord.py v2?

It's pretty simple all you have to do it go to your console/terminal and say the following:

pip install -U git+https://github.com/Rapptz/discord.py 

Note: You need to have Git BASH and a python version higher than or equal to 3.8.0. Get it at python.org/downloads

Let's get started shall we? First let me remind you that again please don't use 3rd party libs because there's more for selects that for buttons or anything in v2(2.0.0a)

You may find an example for dropdowns in the main repository with the filename as dropdown.py in the views folder

But I urge you to read this gist as I'm going to explain everything what I'm going to be doing in a linewise manner.

You'll understand what I mean we dive furthere into the gist.

So make to the topic - How do I make dropdowns?

You need classes for such a venture let me show you what I mean.

Code:

import discord 
from discord.ext import commands 

client=commands.Bot(command_prefix=commands.when_mentioned_or("."))

class Select(discord.ui.Select):
    def __init__(self):
        options=[
            discord.SelectOption(label="Option 1",emoji="👌",description="This is option 1!"),
            discord.SelectOption(label="Option 2",emoji="✨",description="This is option 2!"),
            discord.SelectOption(label="Option 3",emoji="🎭",description="This is option 3!")
            ]
        super().__init__(placeholder="Select an option",max_values=1,min_values=1,options=options)


token=""
client.run(token)

Code(ScreenShot): image

Line-wise Explanation:

  1. Imports discord
  2. Imports commands from discord.ext
  3. We declared our client . .
  4. We defined our discord.ui.Select class as Select
  5. Declared our def __init__()
  6. Made a variable called options and made it into a list of options with the method as discord.SelectOption . . .
  7. Added a super().__init__() and declared our placeholder(what's shown when no option is selected),max_values(how many values can be selected at once),min_values(how many values can be selected when you select it for the first time - max_values and min_values are together when the values are 1 it signifies that only one option can be selected at a time) and options(declares the options in the dropdown by calling the options variable declared in Line 8) . .
  8. Declared the token
  9. Ran the code - Started the bot

Response:

None

You guys right now: What the fu-

Let me explain When we added in the token and ran the bot did you guys find something fishy?

Probably yes if your good with discord.py, it's that I didn't add in a command!

So let's do that real quick.

Code:

import discord 
from discord.ext import commands 

client=commands.Bot(command_prefix=commands.when_mentioned_or("."))

class Select(discord.ui.Select):
    def __init__(self):
        options=[
            discord.SelectOption(label="Option 1",emoji="👌",description="This is option 1!"),
            discord.SelectOption(label="Option 2",emoji="✨",description="This is option 2!"),
            discord.SelectOption(label="Option 3",emoji="🎭",description="This is option 3!")
            ]
        super().__init__(placeholder="Select an option",max_values=1,min_values=1,options=options)


@client.command()
async def menu(ctx):
    await ctx.send("Menus!",view=Select())

token=""
client.run(token) 

Response: A very keen programmer who would actually like to learn something from this gist would find out another deliberate mistake. There's an error!

Error:

Ignoring exception in command menu:
Traceback (most recent call last):
  File "C:\Python310\lib\site-packages\discord\ext\commands\core.py", line 167, in wrapped
    ret = await coro(*args, **kwargs)
  File "c:\Users\ADMIN\Desktop\r.lykn\code.py", line 18, in menu
    await ctx.send("Menus!",view=Select())
  File "C:\Python310\lib\site-packages\discord\abc.py", line 1368, in send
    raise InvalidArgument(f'view parameter must be View not {view.__class__!r}')
discord.errors.InvalidArgument: view parameter must be View not <class '__main__.Select'> 

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Python310\lib\site-packages\discord\ext\commands\bot.py", line 994, in invoke
    await ctx.command.invoke(ctx)
  File "C:\Python310\lib\site-packages\discord\ext\commands\core.py", line 894, in invoke
    await injected(*ctx.args, **ctx.kwargs)
  File "C:\Python310\lib\site-packages\discord\ext\commands\core.py", line 176, in wrapped
    raise CommandInvokeError(exc) from exc
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: InvalidArgument: view parameter must be View not <class '__main__.Select'>

What does this mean???

Answer: It means that there isn't a view class where we can actually "view" the selects. The Select class is actually just a class defining the selects and the view! So how do we add the view? Answer: You need to declare another class(in our case SelectView) and add the Select class(the dropdown menu) into the view class and add the view class as our message view.

Code:

import discord 
from discord.ext import commands 

client=commands.Bot(command_prefix=commands.when_mentioned_or("."))

class Select(discord.ui.Select):
    def __init__(self):
        options=[
            discord.SelectOption(label="Option 1",emoji="👌",description="This is option 1!"),
            discord.SelectOption(label="Option 2",emoji="✨",description="This is option 2!"),
            discord.SelectOption(label="Option 3",emoji="🎭",description="This is option 3!")
            ]
        super().__init__(placeholder="Select an option",max_values=1,min_values=1,options=options)

class SelectView(discord.ui.View):
    def __init__(self, *, timeout = 180):
        super().__init__(timeout=timeout)
        self.add_item(Select())

@client.command()
async def menu(ctx):
    await ctx.send("Menus!",view=Select())

token=""
client.run(token) 

Response:

image

Options:

image

If you used the above code and tried to select something, the view wouldn't do anything. To make the options respond to something you see to add in a asynchronus fucntion called callback

How do I do so? It's pretty simple lol. Note: You have to know this before going any further, the value we select is know as self.values[0]. Code:

import discord 
from discord.ext import commands 

client=commands.Bot(command_prefix=commands.when_mentioned_or("."))

class Select(discord.ui.Select):
    def __init__(self):
        options=[
            discord.SelectOption(label="Option 1",emoji="👌",description="This is option 1!"),
            discord.SelectOption(label="Option 2",emoji="✨",description="This is option 2!"),
            discord.SelectOption(label="Option 3",emoji="🎭",description="This is option 3!")
            ]
        super().__init__(placeholder="Select an option",max_values=1,min_values=1,options=options)
    async def callback(self, interaction: discord.Interaction):
        await interaction.response.send_message(content=f"Your choice is {self.values[0]}!",ephemeral=True)

class SelectView(discord.ui.View):
    def __init__(self, *, timeout = 180):
        super().__init__(timeout=timeout)
        self.add_item(Select())

@client.command()
async def menu(ctx):
    await ctx.send("Menus!",view=SelectView())

token=""
client.run(token) 

Response:

image

Now this response is preset and can't be changed, i.e., the option names are what's being displayed in when self.values[0] is being called, How do I make it something custom and make the bot respond different things to the three different values? That's also easy lol. So in the above note I said that the value selected is self.values[0], and, if you are a person with a mind that's a little too much into coding or just with a taste towards logic you'd know that you'll have to add relations statements to this part of the code. New Question: How do I do so? That's even easy lol

Code: The following code will show examples for all the types of messages that can be passed inside the interaction.response.________()

import discord 
from discord.ext import commands 

client=commands.Bot(command_prefix=commands.when_mentioned_or("."))

class Select(discord.ui.Select):
    def __init__(self):
        options=[
            discord.SelectOption(label="Option 1",emoji="👌",description="This is option 1!"),
            discord.SelectOption(label="Option 2",emoji="✨",description="This is option 2!"),
            discord.SelectOption(label="Option 3",emoji="🎭",description="This is option 3!")
            ]
        super().__init__(placeholder="Select an option",max_values=1,min_values=1,options=options)
    async def callback(self, interaction: discord.Interaction):
        if self.values[0] == "Option 1":
            await interaction.response.edit_message(content="This is the first option from the entire list!")
        elif self.values[0] == "Option 2":
            await interaction.response.send_message("This is the second option from the list entire wooo!",ephemeral=False)
        elif self.values[0] == "Option 3":
            await interaction.response.send_message("Third One!",ephemeral=True)

class SelectView(discord.ui.View):
    def __init__(self, *, timeout = 180):
        super().__init__(timeout=timeout)
        self.add_item(Select())

@client.command()
async def menu(ctx):
    await ctx.send("Menus!",view=SelectView())

token=""
client.run(token) 

Response:

image

Everything works perfectly.

Tip: A Small tip when passing positional arguments inside the interaction.response is to always include what type of response it is, i.e., if it is just content or an embed, file, etc...

InteractionResponse.send_message InteractionResponse.edit_message It isn't as stringent for send_message bcause it's a new message, but, for edit_message programming without adding an attribute your bound to get an error.

And as all good things come to an end, I have come to the last few lines of this gist. Firstly I'd like to thank everyone at the discord.py server who have helped me figure out how to make buttons on the whole you guys are amazing(Includes Umbra#9999 if he reads this, that is😆)! A special thanks goes to LeoCx1000#9999, veyron#1741,Jeyy#6639, Hay#7860, Ender2K89#9999, SHERLOCK#7309 and Nyanaka#1224 for helping me with all the patience in this world!

Thanks to everyone reading this! If you feel something is wrong/needs correction or just need some help then drop a comment down below

This is Lykn signing off.

@hirusha-adi
Copy link

how to deal with await interaction.message.edit(embed=embed) inside callback function

@Real-kia
Copy link

Real-kia commented Apr 6, 2023

How can i make it work after i restart my code
for example i made a menu
then i restart my code
the menu wont work anymore

@RuriYS
Copy link

RuriYS commented Jul 22, 2023

how to deal with await interaction.message.edit(embed=embed) inside callback function

just pass and initialize your embed in your __init__() function.

def __init__(self, embed):
    self.embed = embed

then await interaction.message.edit(embed=self.embed)

@tpanati
Copy link

tpanati commented Oct 4, 2023

I keep getting this error when I try to run the above code for select menu. Can someone please help?

2023-10-04 01:21:22 ERROR    discord.ui.view Ignoring exception in view <SelectView timeout=180 children=1> for item <Select placeholder='Select an option' min_values=1 max_values=1 disabled=False options=[<SelectOption label='Option 1' value='Option 1' description='This is option 1!' emoji=<PartialEmoji animated=False name='👌' id=None> default=False>, <SelectOption label='Option 2' value='Option 2' description='This is option 2!' emoji=<PartialEmoji animated=False name='✨' id=None> default=False>, <SelectOption label='Option 3' value='Option 3' description='This is option 3!' emoji=<PartialEmoji animated=False name='🎭' id=None> default=False>]>
Traceback (most recent call last):
  File "C:\Users\panat\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\discord\ui\view.py", line 427, in _scheduled_task
    await item.callback(interaction)
  File "C:\Users\panat\SE\Finbot\src\discordBottest.py", line 72, in callback
    await interaction.response.send_message(content=f"Your choice is {self.values[0]}!",ephemeral=True)
  File "C:\Users\panat\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\discord\interactions.py", line 801, in send_message
    await adapter.create_interaction_response(
  File "C:\Users\panat\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\discord\webhook\async_.py", line 221, in request
    raise HTTPException(response, data)
discord.errors.HTTPException: 400 Bad Request (error code: 50035): Invalid Form Body
In name: Username cannot contain "discord"

@As2Bax
Copy link

As2Bax commented Nov 12, 2023

Nice, it worked! Do you know how to show another option list once an option is selected?

@iRaccoon13
Copy link

iRaccoon13 commented Mar 4, 2024

When allowing multiple selections, is there a way I can get a list of all of them? I'm making a dashboard where I can reload/unload/load cogs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment