Part 7 - Miru, an optional component handler#

Miru is an optional component handler for hikari, making it simpler to add components to messages, and to handle component interactions.

We’ll need to edit bot.py a little bit to get miru working.

At the top of the file, import miru:

import miru

And just above bot.load_extensions_from("./extensions/") add:

miru.load(bot)

Now we need to edit fun.py.

At the top of the file, import miru:

import miru

And now beneath our animal command, add the following:

 1class AnimalView(miru.View):
 2    def __init__(self, author: hikari.User) -> None:
 3        self.author = author
 4        super().__init__(timeout=60)
 5
 6    @miru.text_select(
 7        custom_id="animal_select",
 8        placeholder="Pick an animal",
 9        options=[
10            miru.SelectOption(name, name.lower().replace(" ", "_"), emoji=emoji)
11            for name, emoji in ANIMALS.items()
12        ],
13    )
14    async def select_menu(self, select: miru.TextSelect, ctx: miru.ViewContext) -> None:
15        animal = select.values[0]
16        async with ctx.app.d.client_session.get(
17            f"https://some-random-api.com/animal/{animal}"
18        ) as res:
19            if not res.ok:
20                await ctx.edit_response(
21                    f"API returned a {res.status} status :c", components=[]
22                )
23                return
24
25            data = await res.json()
26            embed = hikari.Embed(description=data["fact"], colour=0x3B9DFF)
27            embed.set_image(data["image"])
28
29            animal = animal.replace("_", " ")
30
31            await ctx.edit_response(
32                f"Here's a {animal} for you! :3", embed=embed, components=[]
33            )
34
35    async def on_timeout(self) -> None:
36        await self.message.edit("The menu timed out :c", components=[])
37
38    async def view_check(self, ctx: miru.ViewContext) -> bool:
39        return ctx.user.id == self.author.id
40
41
42@fun_group.child
43@lightbulb.command("animal2", "Get a fact + picture of a cute animal :3")
44@lightbulb.implements(lightbulb.SlashSubCommand)
45async def animal_subcommand_2(ctx: lightbulb.SlashContext) -> None:
46    view = AnimalView(ctx.author)
47    resp = await ctx.respond(
48        "Pick an animal from the dropdown :3", components=view.build()
49    )
50
51    await view.start(resp)
52    await view.wait()

This new animal2 command produces the exact same result as the first animal command, but it’s much easier to read and understand at a glance, and adding buttons or more select menus would be incredibly easy.

  • Line 1 - Subclass miru.View, to create our custom AnimalView class

  • Line 4 - Initialise our view with a timeout of 60 seconds

  • Line 6-13 - Create our select menu, with the same custom ID, placeholder and options as before

  • Line 14-33 - Perform the same request as before, and respond to the interaction with an embed

  • Line 35-39 - Set our timeout function, and a view check
    Read the docs - View Checks & Timeout Handling

  • Line 42-44 - Create a second animal command, called “animal2

  • Line 46 - Create an instance of AnimalView

  • Line 47-49 - Respond to the command interaction with our message and components

  • Line 51 - Start the view

  • Line 52 - Wait for the view to finish

Note

If you want to learn how to use buttons and more with Miru, check out the Miru guides, written by Miru’s creator: https://hikari-miru.readthedocs.io/en/latest/getting-started.html