Select#
A simple example on the use of selects with disnake-ext-components.
For this example, we implement a select menu with double functionality. Firstly, the select allows you to select one of three slots. After selecting a slot, the select is modified to instead allow you to select a colour. The selected slot and colour are then combined to colour the corresponding square.
First and foremost, we create a bot as per usual. Since we don’t need any
prefix command capabilities, we opt for an disnake.ext.commands.InteractionBot.
import os
import disnake
from disnake.ext import commands, components
bot = commands.InteractionBot()
Create a manager object and register all components (current and future) to this manager.
manager = components.ComponentManager(bot)
manager.basic_config()
Define possible slots for our select.
LEFT = "\N{BLACK LEFT-POINTING TRIANGLE}\N{VARIATION SELECTOR-16}"
MIDDLE = "\N{BLACK CIRCLE FOR RECORD}\N{VARIATION SELECTOR-16}"
RIGHT = "\N{BLACK RIGHT-POINTING TRIANGLE}\N{VARIATION SELECTOR-16}"
SLOT_OPTIONS = [
disnake.SelectOption(label="Left", value="left", emoji=LEFT),
disnake.SelectOption(label="Middle", value="middle", emoji=MIDDLE),
disnake.SelectOption(label="Right", value="right", emoji=RIGHT),
disnake.SelectOption(label="Finalise", emoji="\N{WHITE HEAVY CHECK MARK}"),
]
Define possible colours for our select.
BLACK_SQUARE = "\N{BLACK LARGE SQUARE}"
BLUE_SQUARE = "\N{LARGE BLUE SQUARE}"
BROWN_SQUARE = "\N{LARGE BROWN SQUARE}"
GREEN_SQUARE = "\N{LARGE GREEN SQUARE}"
PURPLE_SQUARE = "\N{LARGE PURPLE SQUARE}"
RED_SQUARE = "\N{LARGE RED SQUARE}"
WHITE_SQUARE = "\N{WHITE LARGE SQUARE}"
YELLOW_SQUARE = "\N{LARGE YELLOW SQUARE}"
COLOUR_OPTIONS = [
disnake.SelectOption(label="Black", value=BLACK_SQUARE, emoji=BLACK_SQUARE),
disnake.SelectOption(label="Blue", value=BLUE_SQUARE, emoji=BLUE_SQUARE),
disnake.SelectOption(label="Brown", value=BROWN_SQUARE, emoji=BROWN_SQUARE),
disnake.SelectOption(label="Green", value=GREEN_SQUARE, emoji=GREEN_SQUARE),
disnake.SelectOption(label="Purple", value=PURPLE_SQUARE, emoji=PURPLE_SQUARE),
disnake.SelectOption(label="Red", value=RED_SQUARE, emoji=RED_SQUARE),
disnake.SelectOption(label="White", value=WHITE_SQUARE, emoji=WHITE_SQUARE),
disnake.SelectOption(label="Yellow", value=YELLOW_SQUARE, emoji=YELLOW_SQUARE),
]
Then, we make the select.
class MySelect(components.RichStringSelect):
placeholder = "Please select a square." # Set the placeholder text...
options = SLOT_OPTIONS # Set the options...
slot: str = "0" # We store the slot the user is currently working with...
state: str = "slot" # We store whether they're picking a slot or a colour...
colour_left: str = BLACK_SQUARE # And we store the colours for the three slots...
colour_middle: str = BLACK_SQUARE
colour_right: str = BLACK_SQUARE
async def callback(self, inter: components.MessageInteraction) -> None:
# First we get the selected value.
assert inter.values is not None # This should never raise for a select.
selected = inter.values[0]
# If the selection was a slot, run slot selection logic.
# To keep things tidy, we use a separate function for this.
if self.state == "slot":
self.handle_slots(selected)
# Otherwise, run colour selection logic.
else:
self.handle_colours(selected)
# Render the new colours and update the select.
msg = self.render_colours()
await inter.response.edit_message(msg, components=self)
def handle_slots(self, selected: str) -> None:
# In case the user wishes to finalize, disable the select.
if selected == "Finalise":
self.disabled = True
self.placeholder = "Woo!"
return
# Update options and display.
self.options = COLOUR_OPTIONS
self.placeholder = f"Please select a colour for the {selected} square."
# Set the slot to the user's selection and set state to colour.
self.slot = selected
self.state = "colour"
def handle_colours(self, selected: str) -> None:
# Update options.
self.options = SLOT_OPTIONS
# Set the corresponding colour attribute and set state to slot.
setattr(self, f"colour_{self.slot}", selected)
self.state = "slot"
def render_colours(self) -> str:
# Render our three squares.
return f"{self.colour_left}{self.colour_middle}{self.colour_right}\n"
Finally, we make a command that sends the component. In this command, we initialise the timeout for the component.
@bot.slash_command() # pyright: ignore # still some unknowns in disnake
async def test_select(inter: disnake.CommandInteraction) -> None:
# Wrapping the interaction allows you to send the component as-is.
wrapped = components.wrap_interaction(inter)
component = MySelect()
await wrapped.send(component.render_colours(), components=component)
# If we had not wrapped the interaction, we would have needed to do
# `await inter.send(components=await component.as_ui_component())`
# instead.
bot.run(os.getenv("EXAMPLE_TOKEN"))
Source Code#
1import os
2
3import disnake
4from disnake.ext import commands, components
5
6bot = commands.InteractionBot()
7manager = components.ComponentManager(bot)
8manager.basic_config()
9
10
11LEFT = "\N{BLACK LEFT-POINTING TRIANGLE}\N{VARIATION SELECTOR-16}"
12MIDDLE = "\N{BLACK CIRCLE FOR RECORD}\N{VARIATION SELECTOR-16}"
13RIGHT = "\N{BLACK RIGHT-POINTING TRIANGLE}\N{VARIATION SELECTOR-16}"
14
15SLOT_OPTIONS = [
16 disnake.SelectOption(label="Left", value="left", emoji=LEFT),
17 disnake.SelectOption(label="Middle", value="middle", emoji=MIDDLE),
18 disnake.SelectOption(label="Right", value="right", emoji=RIGHT),
19 disnake.SelectOption(label="Finalise", emoji="\N{WHITE HEAVY CHECK MARK}"),
20]
21
22
23BLACK_SQUARE = "\N{BLACK LARGE SQUARE}"
24BLUE_SQUARE = "\N{LARGE BLUE SQUARE}"
25BROWN_SQUARE = "\N{LARGE BROWN SQUARE}"
26GREEN_SQUARE = "\N{LARGE GREEN SQUARE}"
27PURPLE_SQUARE = "\N{LARGE PURPLE SQUARE}"
28RED_SQUARE = "\N{LARGE RED SQUARE}"
29WHITE_SQUARE = "\N{WHITE LARGE SQUARE}"
30YELLOW_SQUARE = "\N{LARGE YELLOW SQUARE}"
31
32COLOUR_OPTIONS = [
33 disnake.SelectOption(label="Black", value=BLACK_SQUARE, emoji=BLACK_SQUARE),
34 disnake.SelectOption(label="Blue", value=BLUE_SQUARE, emoji=BLUE_SQUARE),
35 disnake.SelectOption(label="Brown", value=BROWN_SQUARE, emoji=BROWN_SQUARE),
36 disnake.SelectOption(label="Green", value=GREEN_SQUARE, emoji=GREEN_SQUARE),
37 disnake.SelectOption(label="Purple", value=PURPLE_SQUARE, emoji=PURPLE_SQUARE),
38 disnake.SelectOption(label="Red", value=RED_SQUARE, emoji=RED_SQUARE),
39 disnake.SelectOption(label="White", value=WHITE_SQUARE, emoji=WHITE_SQUARE),
40 disnake.SelectOption(label="Yellow", value=YELLOW_SQUARE, emoji=YELLOW_SQUARE),
41]
42
43
44class MySelect(components.RichStringSelect):
45
46 placeholder = "Please select a square." # Set the placeholder text...
47 options = SLOT_OPTIONS # Set the options...
48
49 slot: str = "0" # We store the slot the user is currently working with...
50 state: str = "slot" # We store whether they're picking a slot or a colour...
51 colour_left: str = BLACK_SQUARE # And we store the colours for the three slots...
52 colour_middle: str = BLACK_SQUARE
53 colour_right: str = BLACK_SQUARE
54
55 async def callback(self, inter: components.MessageInteraction) -> None:
56 assert inter.values is not None # This should never raise for a select.
57 selected = inter.values[0]
58
59 if self.state == "slot":
60 self.handle_slots(selected)
61
62 else:
63 self.handle_colours(selected)
64
65 msg = self.render_colours()
66 await inter.response.edit_message(msg, components=self)
67
68 def handle_slots(self, selected: str) -> None:
69 if selected == "Finalise":
70 self.disabled = True
71 self.placeholder = "Woo!"
72 return
73
74 self.options = COLOUR_OPTIONS
75 self.placeholder = f"Please select a colour for the {selected} square."
76
77 self.slot = selected
78 self.state = "colour"
79
80 def handle_colours(self, selected: str) -> None:
81 self.options = SLOT_OPTIONS
82
83 setattr(self, f"colour_{self.slot}", selected)
84 self.state = "slot"
85
86 def render_colours(self) -> str:
87 return f"{self.colour_left}{self.colour_middle}{self.colour_right}\n"
88
89
90@bot.slash_command() # pyright: ignore # still some unknowns in disnake
91async def test_select(inter: disnake.CommandInteraction) -> None:
92 wrapped = components.wrap_interaction(inter)
93
94 component = MySelect()
95 await wrapped.send(component.render_colours(), components=component)
96
97
98bot.run(os.getenv("EXAMPLE_TOKEN"))