Attrs#
An example showcasing how attrs utilities can be used with ext-components.
Say we wish to create a component, but we do not know the number of options beforehand, and we would like the user to be able to select all of them. It can be cumbersome to manually keep updating the max_values
parameter of the select.
Luckily, with the knowledge that ext-components is built upon the attrs
lib, a few options become available to us.
For this example, we will be making use of attrs classes’ __attrs_post_init__ method, which is called immediately after attrs finishes its normal initialisation logic. If you’re familiar with dataclasses, this is essentially the same as a dataclass’ __post_init__
method.
First and foremost, we create a bot as per usual. Since we don’t need any prefix command capabilities, we opt for an InteractionBot
.
import os
import disnake
from disnake.ext import commands, components
bot = commands.InteractionBot()
Next, we make a component manager and register it to the bot.
manager = components.get_manager()
manager.add_to_bot(bot)
Now we create our customizable select.
@manager.register
class CustomisableSelect(components.RichStringSelect):
def __attrs_post_init__(self) -> None:
self.max_values = len(self.options)
async def callback(self, interaction: components.MessageInteraction) -> None:
selection = (
"\n".join(f"- {value}" for value in interaction.values)
if interaction.values
else "nothing :("
)
await interaction.response.send_message(
f"You selected:\n{selection}", ephemeral=True
)
Now, we ensure that max_values adapts to the passed number of options.
Since rich components use attrs under the hood, this can easily be
achieved through the __attrs_post_init__
method.
def __attrs_post_init__(self) -> None:
self.max_values = len(self.options)
Then we create our test command and send the previously created customisable select.
1@bot.slash_command() # pyright: ignore
2async def make_select(interaction: disnake.CommandInteraction, options: str) -> None:
3 """Make your own select menu.
4
5 Parameters
6 ----------
7 options:
8 A comma-separated string with all options. Max 25.
9 """
10 if not options.strip():
11 await interaction.response.send_message("You must specify at least one option!")
12 return
13
14 actual_options = [
15 disnake.SelectOption(label=option.strip())
16 for option in options.split(",")
17 ] # fmt: skip
18
19 if len(actual_options) > 25:
20 await interaction.response.send_message("You must specify at most 25 options!")
21 return
22
23 wrapped = components.wrap_interaction(interaction)
24 await wrapped.response.send_message(
25 components=CustomisableSelect(options=actual_options),
26 )
If the string is empty or whitespace, the user did not provide options (lines 10-12). Next, we make the options by splitting over commas (lines 14-17). Before creating the component, validate that there’s max 25 options (lines 19-21). Finally if everything went correctly, we send the component.
Lastly, we run the bot.
bot.run(os.getenv("EXAMPLE_TOKEN"))
Source Code#
1import os
2
3import disnake
4from disnake.ext import commands, components
5
6bot = commands.InteractionBot()
7
8manager = components.get_manager()
9manager.add_to_bot(bot)
10
11
12@manager.register
13class CustomisableSelect(components.RichStringSelect):
14 def __attrs_post_init__(self) -> None:
15 self.max_values = len(self.options)
16
17 async def callback(self, interaction: components.MessageInteraction) -> None:
18 selection = (
19 "\n".join(f"- {value}" for value in interaction.values)
20 if interaction.values
21 else "nothing :("
22 )
23
24 await interaction.response.send_message(
25 f"You selected:\n{selection}", ephemeral=True
26 )
27
28
29@bot.slash_command() # pyright: ignore
30async def make_select(interaction: disnake.CommandInteraction, options: str) -> None:
31 """Make your own select menu.
32
33 Parameters
34 ----------
35 options:
36 A comma-separated string with all options. Max 25.
37 """
38 if not options.strip():
39 await interaction.response.send_message("You must specify at least one option!")
40 return
41
42 actual_options = [
43 disnake.SelectOption(label=option.strip())
44 for option in options.split(",")
45 ] # fmt: skip
46
47 if len(actual_options) > 25:
48 await interaction.response.send_message("You must specify at most 25 options!")
49 return
50
51 wrapped = components.wrap_interaction(interaction)
52 await wrapped.response.send_message(
53 components=CustomisableSelect(options=actual_options),
54 )
55
56
57bot.run(os.getenv("EXAMPLE_TOKEN"))