Part 7 - Miru, an optional component handler#

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

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.select(
 7        custom_id="animal_select",
 8        placeholder="Pick an animal",
 9        options=[
10            miru.SelectOption("Dog", "dog", emoji="🐶"),
11            miru.SelectOption("Cat", "cat", emoji="🐱"),
12            miru.SelectOption("Panda", "panda", emoji="🐼"),
13            miru.SelectOption("Fox", "fox", emoji="🦊"),
14            miru.SelectOption("Red Panda", "red_panda", emoji="🐼"),
15            miru.SelectOption("Koala", "koala", emoji="🐨"),
16            miru.SelectOption("Bird", "bird", emoji="🐦"),
17            miru.SelectOption("Racoon", "racoon", emoji="🦝"),
18            miru.SelectOption("Kangaroo", "kangaroo", emoji="🦘"),
19        ],
20    )
21    async def select_menu(self, select: miru.Select, ctx: miru.Context) -> None:
22        animal = select.values[0]
23        async with ctx.app.d.aio_session.get(
24            f"https://some-random-api.ml/animal/{animal}"
25        ) as res:
26            if res.ok:
27                res = await res.json()
28                embed = hikari.Embed(description=res["fact"], colour=0x3B9DFF)
29                embed.set_image(res["image"])
30
31                animal = animal.replace("_", " ")
32
33                await ctx.edit_response(
34                    f"Here's a {animal} for you! :3", embed=embed, components=[]
35                )
36            else:
37                await ctx.edit_response(
38                    f"API returned a {res.status} status :c", components=[]
39                )
40
41    async def on_timeout(self) -> None:
42        await self.message.edit("The menu timed out :c", components=[])
43
44    async def view_check(self, ctx: miru.Context) -> bool:
45        return ctx.user.id == self.author.id
46
47
48@fun_group.child
49@lightbulb.command("animal2", "Get a fact + picture of a cute animal :3")
50@lightbulb.implements(lightbulb.PrefixCommand, lightbulb.SlashSubCommand)
51async def animal_subcommand_2(ctx: lightbulb.Context) -> None:
52    view = AnimalView(ctx.author)
53    resp = await ctx.respond(
54        "Pick an animal from the dropdown :3", components=view.build()
55    )
56    msg = await resp.message()
57
58    view.start(msg)
59    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 other 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-20 - Create our select menu, with the same custom ID, placeholder and options as before

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

  • Line 41-45 - Set our timeout function, and a view check
  • Line 48-50 - Create a second animal command, called “animal2

  • Line 52 - Create an instance of AnimalView

  • Line 53-55 - Respond to the command interaction with our message and components

  • Line 58 - Start the view

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