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 ID

    • Set 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

  • 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 components

  • Line 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 option

  • Line 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 space

    • Line 62 - Edit the message to contain the embed, and remove the select menu component

animal animal

And if the menu times out:

animal

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!

Read the docs - Components