Part 6 - Message Components#
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 "Bird": "🐦",
3 "Cat": "🐱",
4 "Dog": "🐶",
5 "Fox": "🦊",
6 "Kangaroo": "🦘",
7 "Koala": "🐨",
8 "Panda": "🐼",
9 "Raccoon": "🦝",
10 "Red Panda": "🐼",
11}
12
13
14@fun_group.child
15@lightbulb.command("animal", "Get a fact & picture of a cute animal :3")
16@lightbulb.implements(lightbulb.SlashSubCommand)
17async def animal_subcommand(ctx: lightbulb.SlashContext) -> None:
18 select_menu = (
19 ctx.bot.rest.build_message_action_row()
20 .add_select_menu(hikari.ComponentType.TEXT_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.TEXT_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.client_session.get(
50 f"https://some-random-api.com/animal/{animal}"
51 ) as res:
52 if not res.ok:
53 await msg.edit(f"API returned a {res.status} status :c", components=[])
54 return
55
56 data = await res.json()
57 embed = hikari.Embed(description=data["fact"], colour=0x3B9DFF)
58 embed.set_image(data["image"])
59
60 animal = animal.replace("_", " ")
61
62 await msg.edit(f"Here's a {animal} for you! :3", embed=embed, components=[])
Line 1-11 - Create a dictionary containing all the possible endpoints of some-random-api.com/animal/
Line 14-16 - Set up the slash subcommand
Line 18-22 - Create a message action row
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 in lowercase with spaces replaced by underscores
and set the emoji for the option
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 that
the interaction is a component interaction
the interaction user is the same user who ran the command
the interaction message is the same as the message we responded with
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 our response 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.com/animal/ with the selected animal as the optionLine 52-54 - If the response doesn’t have an
ok
status,edit our response and remove the message components
return
so no further code will be run
Line 56-62 - If the response was successful,
Line 56 - Get the response’s json
Line 57 - Create an embed, setting its title to the animal fact
Line 58 - Set the embed’s image to the animal image
Line 60 - Replace the underscore in
animal
with a spaceLine 62 - Edit the message to contain the embed, and remove the select menu component
And if the menu times out:
Note
some-random-api.com has a lot of different endpoints, all fun and useful for a Discord bot. If you want to make more API-centred commands, it’s a great API to use!