Getting Started#
Installation#
miru can be installed using pip via the following command:
$ python3 -m pip install -U hikari-miru
Note
To check if miru installed correctly, please run the following command:
$ python3 -m miru
This should output version and basic system information.
First steps#
This is what a basic component menu looks like with miru:
import hikari
import miru
# Define a new custom View that contains 3 items
class BasicView(miru.View):
# Define a new TextSelect menu with two options
@miru.text_select(
placeholder="Select me!",
options=[
miru.SelectOption(label="Option 1"),
miru.SelectOption(label="Option 2"),
],
)
async def basic_select(self, select: miru.TextSelect, ctx: miru.ViewContext) -> None:
await ctx.respond(f"You've chosen {select.values[0]}!")
# Define a new Button with the Style of success (Green)
@miru.button(label="Click me!", style=hikari.ButtonStyle.SUCCESS)
async def basic_button(self, button: miru.Button, ctx: miru.ViewContext) -> None:
await ctx.respond("You clicked me!")
# Define a new Button that when pressed will stop the view & invalidate all the buttons in this view
@miru.button(label="Stop me!", style=hikari.ButtonStyle.DANGER)
async def stop_button(self, button: miru.Button, ctx: miru.ViewContext) -> None:
self.stop() # Called to stop the view
# Create an instance of our bot. This doesn't need to be a GatewayBot,
# but needs to implement RESTAware, CacheAware, and EventManagerAware.
bot = hikari.GatewayBot("YOUR_TOKEN_HERE")
miru.install(bot) # Start miru
# This function must be called on startup, otherwise you cannot instantiate views
@bot.listen()
async def buttons(event: hikari.GuildMessageCreateEvent) -> None:
# Ignore bots or webhooks pinging us
if not event.is_human:
return
me = bot.get_me()
# If the bot is mentioned
if me.id in event.message.user_mentions_ids:
view = BasicView(timeout=60) # Create a new view
message = await event.message.respond("Hello miru!", components=view)
await view.start(message) # Start listening for interactions
await view.wait() # Optionally, wait until the view times out or gets stopped
await event.message.respond("Goodbye!")
bot.run()
If you run this code, you should see some basic logging information, and your bot will be online! Mentioning the bot in any channel should make the bot send the component menu defined above!
Subclassing#
A more advanced way to use miru is to create our own custom classes of buttons, select menus, and more. This allows us to customize to a great degree their behaviour, pass variables dynamically, add or remove items on the fly, and more!
Below you can see such an example:
import hikari
import miru
class YesButton(miru.Button):
def __init__(self) -> None:
# Initialize our button with some pre-defined properties
super().__init__(style=hikari.ButtonStyle.SUCCESS, label="Yes")
# The callback is the function that gets called when the button is pressed
# If you are subclassing, you must use the name "callback" when defining it.
async def callback(self, ctx: miru.ViewContext) -> None:
# You can specify the ephemeral message flag to make your response ephemeral
await ctx.respond("I'm sorry but this is unacceptable.", flags=hikari.MessageFlag.EPHEMERAL)
# You can access the view an item is attached to by accessing it's view property
self.view.answer = True
self.view.stop()
class NoButton(miru.Button):
# Let's leave our arguments dynamic this time, instead of hard-coding them
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
async def callback(self, ctx: miru.ViewContext) -> None:
await ctx.respond("This is the only correct answer.", flags=hikari.MessageFlag.EPHEMERAL)
self.view.answer = False
self.view.stop()
bot = hikari.GatewayBot("YOUR_TOKEN_HERE")
miru.install(bot)
@bot.listen()
async def buttons(event: hikari.GuildMessageCreateEvent) -> None:
if not event.is_human:
return
me = bot.get_me()
if me.id in event.message.user_mentions_ids:
view = miru.View() # Create a new view
view.add_item(YesButton()) # Add our custom buttons to it
view.add_item(NoButton(style=hikari.ButtonStyle.DANGER, label="No")) # Pass arguments to NoButton
message = await event.message.respond("Do you put pineapple on your pizza?", components=view)
await view.start(message) # Start listening for interactions
await view.wait() # Wait until the view is stopped or times out
if hasattr(view, "answer"): # Check if there is an answer
print(f"Received an answer! It is: {view.answer}")
else:
print("Did not receive an answer in time!")
bot.run()
Running this code and mentioning the bot in a channel it can see should similarly yield a component menu. The benefits of this approach are that you can define custom methods for your individual components, and create template items for re-use later, reducing the need to paste the same code over and over again.