Part 6 - Message Components#
Message components are a relatively new feature on Discord, allowing you to attach buttons and select menus to messages!
Let’s add some new code to fun.py
.
At the very top of the file, import asyncio:
import asyncio
Then, insert the following after the meme
command, but above the load
function:
1ANIMALS = {
2 "Dog": "🐶",
3 "Cat": "🐱",
4 "Panda": "🐼",
5 "Fox": "🦊",
6 "Red Panda": "🐼",
7 "Koala": "🐨",
8 "Bird": "🐦",
9 "Racoon": "🦝",
10 "Kangaroo": "🦘",
11}
12
13
14@fun_group.child
15@lightbulb.command("animal", "Get a fact + picture of a cute animal :3")
16@lightbulb.implements(lightbulb.PrefixSubCommand, lightbulb.SlashSubCommand)
17async def animal_subcommand(ctx: lightbulb.Context) -> None:
18 select_menu = (
19 ctx.bot.rest.build_action_row()
20 .add_select_menu("animal_select")
21 .set_placeholder("Pick an animal")
22 )
23
24 for name, emoji in ANIMALS.items():
25 select_menu.add_option(
26 name, # the label, which users see
27 name.lower().replace(" ", "_"), # the value, which is used by us later
28 ).set_emoji(emoji).add_to_menu()
29
30 resp = await ctx.respond(
31 "Pick an animal from the dropdown :3",
32 component=select_menu.add_to_container(),
33 )
34 msg = await resp.message()
35
36 try:
37 event = await ctx.bot.wait_for(
38 hikari.InteractionCreateEvent,
39 timeout=60,
40 predicate=lambda e: isinstance(e.interaction, hikari.ComponentInteraction)
41 and e.interaction.user.id == ctx.author.id
42 and e.interaction.message.id == msg.id
43 and e.interaction.component_type == hikari.ComponentType.SELECT_MENU,
44 )
45 except asyncio.TimeoutError:
46 await msg.edit("The menu timed out :c", components=[])
47 else:
48 animal = event.interaction.values[0]
49 async with ctx.bot.d.aio_session.get(
50 f"https://some-random-api.ml/animal/{animal}"
51 ) as res:
52 if res.ok:
53 res = await res.json()
54 embed = hikari.Embed(description=res["fact"], colour=0x3B9DFF)
55 embed.set_image(res["image"])
56
57 animal = animal.replace("_", " ")
58
59 await msg.edit(
60 f"Here's a {animal} for you! :3", embed=embed, components=[]
61 )
62 else:
63 await msg.edit(f"API returned a {res.status} status :c", components=[])
Line 1-11 - Create a dict containing all the possible endpoints of some-random-api.ml/animal/
Line 14-16 - Set up prefix and slash subcommands
- Line 18-22
Create an action row, which returns an ActionRowBuilder
Add a select menu to the action row, with “
animal_select
” as the custom IDSet the placeholder (the text that is seen when no option has been picked) to
Pick an animal
- Line 24-28 - For all the items in the
ANIMALS
dict, add an option to the select menu (Read the docs - SelectMenuBuilder.add_option) with The name
The value, which is the name of the animal but lowercased and with spaces replaced with underscores
Setting the emoji to the value of the animal in the
ANIMALS
dict
- Line 24-28 - For all the items in the
- Line 30-34
Respond to the context with the select menu
Fetch the message from the response (Read the docs - ResponseProxy)
- Line 36-44 - Wait for an interaction to be created and
Check if the interaction is a component interaction
Check that the interaction user is the same who ran the command
Check that the interaction message is the same as the message we sent
Check that the interaction component type is a select menu
Line 45-46 - If the interaction times out, an
asyncio.TimeoutError
will be raised, and so we can use that to handle the timeout by editing the message and removing the componentsLine 48 - Get the value of the interaction (the selected option) - Read the docs - ComponentInteraction.values
Line 49-51 - Make a
GET
request to some-random-api.ml with the selected animal as the option- Line 52 - If the response has an
ok
status, then Line 53 - Get the response’s json
Line 54 - Create an embed, setting its title to the animal fact
Line 55 - Set the embed’s image to the animal image
Line 57 - Replace the underscore in animal with a space
Line 59-61 - Edit the message to contain the embed, and remove the select menu component
- Line 52 - If the response has an
- Line 62 - Otherwise, if the response was not successful, then
Line 63 - Edit the message to say what status code the API responded with, and remove the select menu component
And if the menu times out: