How to use components¶
So you’ve seen the new fancy buttons and want to give them a try, but dont know where to start. No problem!
First lets cover the basics. Discord messages can have components, such as buttons, dropdowns etc. These components sit in whats called an “action row”. An action row can hold 5 components at a time. Your message can have 5 action rows, so thats a total of 25 components!
Sending some components¶
Note
This will work in both slash commands, and discord.py commands
First we need to create some buttons, lets put them in a list for now. We’ll use create_button()
to create a green button
from discord_slash.utils.manage_components import create_button, create_actionrow
from discord_slash.model import ButtonStyle
buttons = [
create_button(
style=ButtonStyle.green,
label="A Green Button"
),
]
So we have a button, but where do we use it. Let’s create an action row with create_actionrow()
and put our buttons in it
action_row = create_actionrow(*buttons)
Fantastic, we now have an action row with a green button in it, now lets get it sent in discord
await ctx.send("My Message", components=[action_row])
And to bring it all together, you could use this:
from discord_slash.utils.manage_components import create_button, create_actionrow
from discord_slash.model import ButtonStyle
await ctx.send("My Message", components=[
create_actionrow(
create_button(style=ButtonStyle.green, label="A Green Button"))
])
Now if you’ve followed along, you have a green button in discord! But theres a problem, whenever you click it you see that the interaction failed
. Why is that?
Well, in Discord, clicking buttons and using slash commands are called interactions
, and Discord doesn’t know if we’ve received them or not unless we tell Discord. So how do we do that?
Responding to interactions¶
When responding, you have 3 choices in how you handle interactions. You can either wait for them in the command itself, or listen for them in a global event handler (similar to on_slash_command_error()
), or register async function as component callback.
Wait_for¶
Lets go through the most common method first, responding in the command itself. We simply need to wait_for()
the event, just like you do for reactions. For this we’re going to use wait_for_component()
, and we’re going to only wait for events from the action row we just sent.
This method will return a ComponentContext
object that we can use to respond. For this example, we’ll just edit the original message (edit_origin()
, uses same logic as edit()
)
from discord_slash.utils.manage_components import wait_for_component
await ctx.send("My Message", components=[action_row])
# note: this will only catch one button press, if you want more, put this in a loop
button_ctx: ComponentContext = await wait_for_component(bot, components=action_row)
await button_ctx.edit_origin(content="You pressed a button!")
Note
It’s worth being aware that if you handle the event in the command itself, it will not persist reboots. As such when you restart the bot, the interaction will fail
Global event handler¶
Next we’ll go over the alternative, a global event handler. This works just the same as on_slash_command_error()
or on_ready. But note that this code will be triggered on any components interaction.
@bot.event
async def on_component(ctx: ComponentContext):
# you may want to filter or change behaviour based on custom_id or message
await ctx.edit_origin(content="You pressed a button!")
Component callbacks¶
There is one more method - making a function that’ll be component callback - triggered when components in a specified message or specific custom_id
are used.
Let’s register our callback function via decorator component_callback()
, in similar ways to slash commands.
@slash.component_callback()
async def hello(ctx: ComponentContext):
await ctx.edit_origin(content="You pressed a button!")
In this example, hello()
will be triggered when you receive interaction event from a component with a custom_id set to “hello”. Just like slash commands, the callback’s custom_id defaults to the function name.
You can also register such callbacks in cogs using cog_component()
Additionally, component callbacks can be dynamically added, removed or edited - see SlashCommand
But [writer], I dont want to edit the message¶
Well lucky for you, you don’t have to. You can either respond silently, with a thinking animation, or send a whole new message. Take a look here: ComponentContext
What about selects / Dropdowns?¶
Yep we support those too. You use them much the same as buttons. You can only have 1 select per action row, but each select can have up to 25 options in it!
from discord_slash.utils.manage_components import create_select, create_select_option, create_actionrow
select = create_select(
options=[# the options in your dropdown
create_select_option("Lab Coat", value="coat", emoji="🥼"),
create_select_option("Test Tube", value="tube", emoji="🧪"),
create_select_option("Petri Dish", value="dish", emoji="🧫"),
],
placeholder="Choose your option", # the placeholder text to show when no options have been chosen
min_values=1, # the minimum number of options a user must select
max_values=2, # the maximum number of options a user can select
)
await ctx.send("test", components=[create_actionrow(select)]) # like action row with buttons but without * in front of the variable
@bot.event
async def on_component(ctx: ComponentContext):
# ctx.selected_options is a list of all the values the user selected
await ctx.send(content=f"You selected {ctx.selected_options}")
Additionally, you can pass description
as a keyword-argument for create_select_option()
if you so wish.