### Run Python Example Source: https://github.com/hypergonial/hikari-miru/blob/main/examples/README.md To run and test the examples, insert your bot token into the constructor of `hikari.GatewayBot(...)`/`hikari.RESTBot(...)` by replacing the `...`, then run the example using the following command. ```sh python example_name.py ``` -------------------------------- ### Basic Component Menu Example Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/getting_started.md This is a foundational example demonstrating how to set up a basic component menu using hikari-miru. It requires hikari and miru to be imported. ```python import hikari import miru ``` -------------------------------- ### Create Navigator with lightbulb Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/navigators.md Example of setting up a navigator with the lightbulb framework. Requires importing miru.ext.nav. ```python import hikari import lightbulb import miru # Import the navigation module from miru.ext import nav bot = lightbulb.BotApp("TOKEN") client = miru.Client(bot) @bot.command @lightbulb.command("name", "description", auto_defer=False) @lightbulb.implements(lightbulb.SlashCommand) async def my_command(ctx: lightbulb.SlashContext) -> None: embed = hikari.Embed( title="I'm the second page!", description="Also an embed!" ) # A Page object can be used to further customize the page payload page = nav.Page( ``` -------------------------------- ### miru.Client.start_view() Source: https://context7.com/hypergonial/hikari-miru/llms.txt Shows how to register a `View` with the client and start listening for interactions. This method is crucial after sending a message with view components. ```APIDOC ## `miru.Client.start_view()` — Register and Start a View Registers a `View` with the client and starts listening for interactions. Must be called after sending the view's components in a message. The `bind_to` parameter controls whether the view is bound to a specific message. ```python import hikari import miru bot = hikari.GatewayBot("YOUR_TOKEN") client = miru.Client(bot) class MyView(miru.View): @miru.button(label="Click me!", style=hikari.ButtonStyle.SUCCESS) async def click_button(self, ctx: miru.ViewContext, button: miru.Button) -> None: await ctx.respond("You clicked me!") @miru.button(label="Stop", style=hikari.ButtonStyle.DANGER) async def stop_button(self, ctx: miru.ViewContext, button: miru.Button) -> None: self.stop() # Stop listening for interactions @bot.listen() async def on_message(event: hikari.GuildMessageCreateEvent) -> None: if not event.is_human: return me = bot.get_me() if me.id in event.message.user_mentions_ids: view = MyView() await event.message.respond("Hello!", components=view) client.start_view(view) # Start listening for interactions # Optionally wait until view is stopped or times out await view.wait() print("View stopped.") bot.run() ``` ``` -------------------------------- ### Install Development Dependencies Source: https://github.com/hypergonial/hikari-miru/blob/main/CONTRIBUTING.md Installs all necessary tooling for developing the miru project. Ensure you have pip installed. ```sh pip install -r requirements.txt -r dev_requirements.txt ``` -------------------------------- ### Starting Views: v4 vs v3 Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/migrating_from_v3.md The `view.start()` method is removed in v4. Use `Client.start_view()` instead, which does not require awaiting and does not need a message object. ```python view = miru.View(...) await something.respond(..., components=view) client.start_view(view) # This is the client you created earlier ``` ```python view = miru.View(...) msg = await something.respond(..., components=view) await view.start(msg) ``` -------------------------------- ### Start a View with miru.Client Source: https://context7.com/hypergonial/hikari-miru/llms.txt Register a View with the client and start listening for interactions. This should be called after sending the view's components in a message. The view can be optionally waited on until it is stopped or times out. ```python import hikari import miru bot = hikari.GatewayBot("YOUR_TOKEN") client = miru.Client(bot) class MyView(miru.View): @miru.button(label="Click me!", style=hikari.ButtonStyle.SUCCESS) async def click_button(self, ctx: miru.ViewContext, button: miru.Button) -> None: await ctx.respond("You clicked me!") @miru.button(label="Stop", style=hikari.ButtonStyle.DANGER) async def stop_button(self, ctx: miru.ViewContext, button: miru.Button) -> None: self.stop() # Stop listening for interactions @bot.listen() async def on_message(event: hikari.GuildMessageCreateEvent) -> None: if not event.is_human: return me = bot.get_me() if me.id in event.message.user_mentions_ids: view = MyView() await event.message.respond("Hello!", components=view) client.start_view(view) # Start listening for interactions # Optionally wait until view is stopped or times out await view.wait() print("View stopped.") bot.run() ``` -------------------------------- ### Verify Hikari-Miru Installation Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/getting_started.md Run this command to confirm that hikari-miru has been installed correctly. It should display library information. ```sh py -m miru ``` ```sh python3 -m miru ``` -------------------------------- ### Lightbulb DataStore Setup Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/client_state.md Save the miru client to the lightbulb bot's DataStore for global access. This is the recommended way to manage state in lightbulb. ```python bot = lightbulb.BotApp(...) # Save the client to the bot's DataStore bot.d.miru = miru.Client(bot) ``` -------------------------------- ### Create and Send Menu with Crescent Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/menus.md This example demonstrates menu integration with Crescent commands. It creates a menu, builds its response, and sends it to the interaction context. ```python @crescent_client.include @crescent.command(name="name", description="description") class SomeSlashCommand: async def callback(self, ctx: crescent.Context) -> None: # Create a new instance of our view view = BasicView() builder = await my_menu.build_response_async(client, MainScreen(my_menu)) await ctx.respond_with_builder(builder) client.start_view(my_menu) ``` -------------------------------- ### Create and Send Menu with Arc (Gateway) Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/menus.md This example integrates menu creation with Arc's gateway command handling. It creates a menu, builds its initial response, and sends it to the channel. ```python @arc_client.include @arc.slash_command("name", "description") async def some_slash_command(ctx: arc.GatewayContext) -> None: my_menu = menu.Menu() # Create a new Menu # Pass in the initial screen builder = await my_menu.build_response_async(client, MainScreen(my_menu)) await ctx.respond_with_builder(builder) client.start_view(my_menu) ``` -------------------------------- ### Create and Respond with a View Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/getting_started.md This example demonstrates how to create a view, respond with it in a message, and wait for user interaction. It shows the core pattern for using views in Hikari Miru. ```python view = PineappleView() # Create the view await ctx.respond("Do you put pineapple on your pizza?", components=view) client.start_view(view) # You can also wait until the view is stopped or times out await view.wait() if view.answer is not None: print(f"Received an answer! It is: {view.answer}") else: print("Did not receive an answer in time!") ``` ```python @arc_client.include @arc.slash_command("name", "description") async def some_slash_command(ctx: arc.RESTContext) -> None: view = PineappleView() # Create the view await ctx.respond("Do you put pineapple on your pizza?", components=view) client.start_view(view) # You can also wait until the view is stopped or times out await view.wait() if view.answer is not None: print(f"Received an answer! It is: {view.answer}") else: print("Did not receive an answer in time!") ``` ```python @crescent_client.include @crescent.command(name="name", description="description") class SomeSlashCommand: async def callback(self, ctx: crescent.Context) -> None: view = PineappleView() # Create the view await ctx.respond("Do you put pineapple on your pizza?", components=view) client.start_view(view) # You can also wait until the view is stopped or times out await view.wait() if view.answer is not None: print(f"Received an answer! It is: {view.answer}") else: print("Did not receive an answer in time!") ``` ```python @lightbulb_bot.command() @lightbulb.command("name", "description", auto_defer=False) @lightbulb.implements(lightbulb.SlashCommand) async def some_slash_command(ctx: lightbulb.SlashContext) -> None: view = PineappleView() # Create the view await ctx.respond("Do you put pineapple on your pizza?", components=view) client.start_view(view) # You can also wait until the view is stopped or times out await view.wait() if view.answer is not None: print(f"Received an answer! It is: {view.answer}") else: print("Did not receive an answer in time!") ``` -------------------------------- ### Sending a Custom View via Gateway (Hikari) Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/getting_started.md Respond to a message event with a custom `PineappleView`. This example shows how to start the view, wait for user interaction, and process the result. It uses the Gateway connection. ```python @bot.listen() async def some_listener(event: hikari.MessageCreateEvent) -> None: if not event.is_human: return me = bot.get_me() if me.id in event.message.user_mentions_ids: view = PineappleView() # Create the view await event.message.respond( "Do you put pineapple on your pizza?", components=view ) client.start_view(view) # You can also wait until the view is stopped or times out await view.wait() if view.answer is not None: print(f"Received an answer! It is: {view.answer}") else: print("Did not receive an answer in time!") bot.run() ``` -------------------------------- ### Start a Modal with miru.Client Source: https://context7.com/hypergonial/hikari-miru/llms.txt Register a Modal with the client to listen for its submission interaction. This must be called after sending the modal builder as a response to an interaction. ```python import hikari import miru bot = hikari.GatewayBot("YOUR_TOKEN") client = miru.Client(bot) class FeedbackModal(miru.Modal, title="Send Feedback"): subject = miru.TextInput(label="Subject", placeholder="Enter subject...", required=True) body = miru.TextInput( label="Message", style=hikari.TextInputStyle.PARAGRAPH, placeholder="Enter your message...", required=True, ) async def callback(self, ctx: miru.ModalContext) -> None: await ctx.respond( f"**Subject:** {self.subject.value}\n**Message:** {self.body.value}", flags=hikari.MessageFlag.EPHEMERAL, ) class OpenModalView(miru.View): @miru.button(label="Open Feedback Form", style=hikari.ButtonStyle.PRIMARY) async def open_modal(self, ctx: miru.ViewContext, button: miru.Button) -> None: modal = FeedbackModal() await ctx.respond_with_modal(modal) @bot.listen() async def on_message(event: hikari.GuildMessageCreateEvent) -> None: if not event.is_human: return me = bot.get_me() if me.id in event.message.user_mentions_ids: view = OpenModalView() await event.message.respond("Click to open a modal:", components=view) client.start_view(view) bot.run() ``` -------------------------------- ### Build Menu Response Async in v4 Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/migrating_from_v3.md In v4, Menu.send() is removed. Use build_response_async() to get an InteractionMessageBuilder. This builder can be used to create an initial response, send to a channel, or start the view. ```python my_menu = menu.Menu(...) # This returns a custom InteractionMessageBuilder # It can be returned in REST callbacks builder = await my_menu.build_response_async() # Or you can use some of the custom methods it has await builder.create_initial_response(interaction) # Or await builder.send_to_channel(channel_id) client.start_view(my_menu) ``` -------------------------------- ### Install hikari-miru Source: https://github.com/hypergonial/hikari-miru/blob/main/README.md Install the hikari-miru library using pip. This command ensures you have the latest version. ```sh pip install -U hikari-miru ``` -------------------------------- ### Check hikari-miru Installation Source: https://github.com/hypergonial/hikari-miru/blob/main/README.md Verify the installation of hikari-miru by running it as a module. Use 'py -m miru' on Windows. ```sh python3 -m miru ``` ```sh py -m miru ``` -------------------------------- ### Dependency Injection with Alluka Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/client_state.md Example of using a dependency injection library like alluka to manage the miru client when not using a command handler. This allows the client to be injected where needed. ```python # Assuming alluka is set up and the miru client is registered # Example usage within a function that needs the client: # async def some_function(client: miru.Client = alluka.inject()) -> None: # ... # client.start_view(view) ``` -------------------------------- ### Initialize miru.Client for Gateway and REST Bots Source: https://context7.com/hypergonial/hikari-miru/llms.txt Initialize the miru Client by wrapping a hikari bot instance. This must be done before starting any views or modals. Supports both GatewayBot and RESTBot. ```python import hikari import miru # Gateway bot bot = hikari.GatewayBot("YOUR_TOKEN") client = miru.Client(bot) # REST bot # bot = hikari.RESTBot("YOUR_TOKEN") # client = miru.Client(bot) # Integration with arc (shares dependency injector) # import arc # arc_client = arc.GatewayClient(bot) # client = miru.Client.from_arc(arc_client) bot.run() ``` -------------------------------- ### Create and Handle Select Menus with Decorators Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/selects.md Demonstrates how to create and handle different types of select menus using decorators in a Miru View. Includes examples for text, user, role, and channel selects, showcasing placeholder text, options, and channel type filtering. ```python import hikari import miru class SelectView(miru.View): @miru.text_select( placeholder="Select me!", options=[ miru.SelectOption(label="Option 1"), miru.SelectOption(label="Option 2"), ], ) async def get_text(self, ctx: miru.ViewContext, select: miru.TextSelect) -> None: await ctx.respond(f"You've chosen {select.values[0]}!") @miru.user_select(placeholder="Select a user!") async def get_users(self, ctx: miru.ViewContext, select: miru.UserSelect) -> None: await ctx.respond(f"You've chosen {select.values[0].mention}!") # We can control how many options should be selected @miru.role_select(placeholder="Select 3-5 roles!", min_values=3, max_values=5) async def get_roles(self, ctx: miru.ViewContext, select: miru.RoleSelect) -> None: await ctx.respond( f"You've chosen {' '.join([r.mention for r in select.values])}!" ) # A select where the user can only select text and announcement channels @miru.channel_select( placeholder="Select a text channel!", channel_types=[ hikari.ChannelType.GUILD_TEXT, hikari.ChannelType.GUILD_NEWS ], ) async def get_channels(self, ctx: miru.ViewContext, select: miru.ChannelSelect) -> None: await ctx.respond(f"You've chosen {select.values[0].mention}!") ``` -------------------------------- ### Send Modal as Slash Command Response (Lightbulb) Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/modals.md Define a Lightbulb command. Build the modal response and send it. Note that auto_defer is set to False in this example. ```python @lightbulb_bot.command() @lightbulb.command("name", "description", auto_defer=False) ``` -------------------------------- ### Arc Gateway Client Setup Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/client_state.md Set up the miru client as a type dependency for arc's Gateway client. This makes the client available for injection into commands. ```python bot = hikari.GatewayBot(...) arc_client = arc.GatewayClient(bot) # The miru client is automatically set as a type dependency client = miru.Client.from_arc(arc_client) ``` -------------------------------- ### Define a Custom View with TextSelect and Buttons Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/getting_started.md This example demonstrates creating a custom view with a text select menu and two buttons. One button responds with a message, while the other stops the view. ```python class BasicView(miru.View): @miru.text_select( placeholder="Select me!", options=[ miru.SelectOption(label="Option 1"), miru.SelectOption(label="Option 2"), ], ) async def basic_select(self, ctx: miru.ViewContext, select: miru.TextSelect) -> None: await ctx.respond(f"You've chosen {select.values[0]}!") @miru.button(label="Click me!", style=hikari.ButtonStyle.SUCCESS) async def basic_button(self, ctx: miru.ViewContext, button: miru.Button) -> None: await ctx.respond("You clicked me!") @miru.button(label="Stop me!", style=hikari.ButtonStyle.DANGER) async def stop_button(self, ctx: miru.ViewContext, button: miru.Button) -> None: self.stop() ``` -------------------------------- ### Crescent Gateway Model Setup Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/client_state.md Manage the miru client state in crescent Gateway using a dataclass model. The client is initialized and then passed into the crescent client's model. ```python @dataclasses.dataclass class MyModel: miru: miru.Client bot = hikari.GatewayBot(...) client = miru.Client(bot) crescent_client = crescent.Client(bot, MyModel(client)) ``` -------------------------------- ### Arc REST Client Setup Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/client_state.md Set up the miru client as a type dependency for arc's REST client. This allows for easy injection into commands. ```python bot = hikari.RESTBot(...) arc_client = arc.RESTClient(bot) # The miru client is automatically set as a type dependency client = miru.Client.from_arc(arc_client) ``` -------------------------------- ### Create and Send a Navigator Source: https://github.com/hypergonial/hikari-miru/blob/main/miru/ext/nav/README.md Listen for guild message create events, and if the bot is mentioned, create a navigator with multiple pages (text and embeds) and send it to the channel. Ensure the navigator view is started. ```python import hikari import miru as nav @bot.listen() async def navigator(event: hikari.GuildMessageCreateEvent) -> None: if not event.is_human: return me = bot.get_me() if me.id in event.message.user_mentions_ids: embed = hikari.Embed(title="I'm the second page!", description="Also an embed!") pages = ["I'm the first page!", embed, "I'm the last page!"] navigator = nav.NavigatorView(pages=pages) builder = await navigator.build_response_async(client) await builder.send_to_channel(event.channel_id) client.start_view(navigator) ``` -------------------------------- ### Send Modal as Slash Command Response (Crescent) Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/modals.md Define a Crescent command class. Create a modal builder and use ctx.respond_with_builder() to send it. Start the modal with client.start_modal(). ```python @crescent_client.include @crescent.command(name="name", description="description") class SomeSlashCommand: async def callback(self, ctx: crescent.Context) -> None: modal = MyModal() builder = modal.build_response(client) # crescent has a built-in way to respond with a builder await ctx.respond_with_builder(builder) client.start_modal(modal) ``` -------------------------------- ### Crescent REST Model Setup Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/client_state.md Manage the miru client state in crescent REST using a dataclass model. The client is initialized and then passed into the crescent client's model. ```python @dataclasses.dataclass class MyModel: miru: miru.Client bot = hikari.RESTBot(...) client = miru.Client(bot) crescent_client = crescent.Client(bot, MyModel(client)) ``` -------------------------------- ### Initialize Miru Client with Hikari Gateway Bot Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/getting_started.md Instantiate a Hikari Gateway bot and create a Miru client for it. This is the basic setup for using Miru without a specific command handler. ```python bot = hikari.GatewayBot("YOUR_TOKEN_HERE") client = miru.Client(bot) ``` -------------------------------- ### Initialize Miru Client with Hikari REST Bot Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/getting_started.md Instantiate a Hikari REST bot and create a Miru client for it. This setup is for bots that only handle interactions via HTTP POST requests. ```python bot = hikari.RESTBot("YOUR_TOKEN_HERE") client = miru.Client(bot) ``` -------------------------------- ### Create Slash Command with Components using lightbulb Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/getting_started.md Define a slash command using the lightbulb framework. This command responds with a custom view containing components and starts the view with the client. ```Python @lightbulb_bot.command() @lightbulb.command("name", "description", auto_defer=False) @lightbulb.implements(lightbulb.SlashCommand) async def some_slash_command(ctx: lightbulb.SlashContext) -> None: # Create a new instance of our view view = BasicView() await ctx.respond("Hello miru!", components=view) # Assign the view to the client and start it client.start_view(view) ``` -------------------------------- ### Initialize Miru Client from Arc REST Client Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/getting_started.md Create a Miru client by initializing it from an arc REST client. This setup is suitable for bots using arc for RESTful interactions. ```python bot = hikari.RESTBot("YOUR_TOKEN_HERE") arc_client = arc.RESTClient(bot) client = miru.Client.from_arc(arc_client) ``` -------------------------------- ### Dynamically Manage View Items Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/getting_started.md This example shows how to dynamically add items to a view using `add_item()`, including conditional adding of items. It also mentions `remove_item()` and `clear_items()` for managing view components. ```python # add_item() calls can be chained! view = ( PineappleView() .add_item(YesButton()) .add_item(NoButton(style=hikari.ButtonStyle.DANGER, label="No")) ) if some_condition: view.add_item( miru.LinkButton(url="https://en.wikipedia.org/wiki/Hawaiian_pizza", label="Learn More") ) ``` -------------------------------- ### Handle Message Create Event with Components (Gateway) Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/getting_started.md Listen for message creation events and respond with a custom view containing components when the bot is mentioned. Ensure the bot is not mentioned by other bots or webhooks. Assign the view to the client and start it. ```Python import hikari # Assuming 'bot' and 'client' are initialized hikari.GatewayBot and a view client respectively # Assuming BasicView is a defined hikari.ui.View subclass @bot.listen() async def buttons(event: hikari.MessageCreateEvent) -> 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: # Create a new instance of our view view = BasicView() await event.message.respond("Hello miru!", components=view) # Assign the view to the client and start it client.start_view(view) bot.run() ``` -------------------------------- ### Start Bound View with Crescent Gateway Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/persistent_views.md This snippet shows how to restart a bound view listener after application startup with Crescent's Gateway client. The view must be reinstantiated with the same custom IDs. ```python import crescent import hikari @crescent_client.include @crescent.event async def start_views(event: hikari.StartedEvent) -> None: # You must reinstantiate the view with the same custom_ids every time view = Persistence() message_id = ... # Restart the listener for the view after application startup # and explicitly tell it to not bind to a message client.start_view(view, bind_to=message_id) ``` -------------------------------- ### Start Bound View with Arc Gateway Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/persistent_views.md This code demonstrates how to restart a bound view listener after application startup using Arc's Gateway client. The view needs to be reinstantiated with the same custom IDs. ```python import arc @arc_client.set_startup_hook async def start_views(client: arc.GatewayClient) -> None: # You must reinstantiate the view with the same custom_ids every time view = Persistence() message_id = ... # Restart the listener for the view after application startup # and explicitly tell it to not bind to a message client.start_view(view, bind_to=message_id) ``` -------------------------------- ### Integrating Custom Views with Arc Framework (Gateway) Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/getting_started.md Example of how to integrate a custom view with the Arc framework for slash commands. This snippet shows the basic structure for a command that would utilize a view. ```python @arc_client.include @arc.slash_command("name", "description") async def some_slash_command(ctx: arc.GatewayContext) -> None: ``` -------------------------------- ### Send Modal as Slash Command Response (Arc Gateway) Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/modals.md Use arc.slash_command to define a command. Build the modal response and use ctx.respond_with_builder() to send it. Remember to start the modal with client.start_modal(). ```python @arc_client.include @arc.slash_command("name", "description") async def some_slash_command(ctx: arc.GatewayContext) -> None: modal = MyModal() builder = modal.build_response(client) # arc has a built-in way to respond with a builder await ctx.respond_with_builder(builder) client.start_modal(modal) ``` -------------------------------- ### Start Unbound View with Crescent Gateway Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/persistent_views.md Begin an unbound persistent view for Crescent's Gateway client using its event decorator. The view needs to be reinstantiated with identical custom IDs, and bind_to should be None. ```python @crescent_client.include @crescent.event async def start_views(event: hikari.StartedEvent) -> None: # You must reinstantiate the view with the same custom_ids every time view = Persistence() # Restart the listener for the view after application startup # and explicitly tell it to not bind to any message client.start_view(view, bind_to=None) ``` -------------------------------- ### Start Unbound View with Arc Gateway Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/persistent_views.md Initiate an unbound persistent view using Arc's Gateway client startup hook. Recreate the view with consistent custom IDs and specify bind_to=None to prevent message binding. ```python @arc_client.set_startup_hook async def start_views(client: arc.GatewayClient) -> None: # You must reinstantiate the view with the same custom_ids every time view = Persistence() # Restart the listener for the view after application startup # and explicitly tell it to not bind to any message client.start_view(view, bind_to=None) ``` -------------------------------- ### Build and Serve Documentation Locally Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/README.md Run this command to build and serve the documentation locally. Open the provided URL in your browser to view it. ```sh nox -s servedocs ``` -------------------------------- ### Build Navigator Response Async in v4 Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/migrating_from_v3.md In v4, NavigatorView.send() is removed. Use build_response_async() to get an InteractionMessageBuilder. This builder can be used to create an initial response, send to a channel, or start the view. ```python navigator = nav.NavigatorView(...) # This returns a custom InteractionMessageBuilder # It can be returned in REST callbacks builder = await navigator.build_response_async() # Or you can use some of the custom methods it has await builder.create_initial_response(interaction) # Or await builder.send_to_channel(channel_id) client.start_view(navigator) ``` -------------------------------- ### Sending a Custom View via REST (Hikari) Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/getting_started.md Handle a command interaction by sending a custom `PineappleView`. This example demonstrates building a response with components and starting the view for user interaction using REST. ```python async def handle_command(interaction: hikari.CommandInteraction): view = PineappleView() # Create the view builder = interaction.build_response().set_content("Do you put pineapple on your pizza?") for action_row in view.build(): builder.add_component(action_row) yield builder # Assign the view to the client and start it client.start_view(view) # You can also wait until the view is stopped or times out await view.wait() if view.answer is not None: print(f"Received an answer! It is: {view.answer}") else: print("Did not receive an answer in time!") async def create_commands(bot: hikari.RESTBot) -> None: application = await bot.rest.fetch_application() await bot.rest.set_application_commands( application=application.id, commands=[ bot.rest.slash_command_builder("test", "My first test command!"), ], ) bot.add_startup_callback(create_commands) bot.set_listener(hikari.CommandInteraction, handle_command) bot.run() ``` -------------------------------- ### Client Initialization: v4 vs v3 Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/migrating_from_v3.md In v4, `miru.install()` is removed. Create a `miru.Client` instance and pass your bot to it instead of relying on global state. ```python bot = hikari.GatewayBot(...) # or hikari.RESTBot client = miru.Client(bot) ``` ```python bot = hikari.GatewayBot(...) client = miru.install(bot) ``` -------------------------------- ### Start Unbound View with Hikari Gateway Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/persistent_views.md Start an unbound persistent view after the bot has started using Hikari's Gateway. Ensure the view is reinstantiated with the same custom IDs and explicitly set bind_to to None. ```python @bot.listen() async def start_views(event: hikari.StartedEvent) -> None: # You must reinstantiate the view with the same custom_ids every time view = Persistence() # Restart the listener for the view after application startup # and explicitly tell it to not bind to any message client.start_view(view, bind_to=None) ``` -------------------------------- ### miru.Client.start_modal() Source: https://context7.com/hypergonial/hikari-miru/llms.txt Explains how to register a `Modal` with the client to listen for its submission. This should be called after presenting the modal builder in response to an interaction. ```APIDOC ## `miru.Client.start_modal()` — Register and Start a Modal Registers a `Modal` with the client to listen for its submission interaction. Must be called after sending the modal builder as a response to an interaction. ```python import hikari import miru bot = hikari.GatewayBot("YOUR_TOKEN") client = miru.Client(bot) class FeedbackModal(miru.Modal, title="Send Feedback"): subject = miru.TextInput(label="Subject", placeholder="Enter subject...", required=True) body = miru.TextInput( label="Message", style=hikari.TextInputStyle.PARAGRAPH, placeholder="Enter your message...", required=True, ) async def callback(self, ctx: miru.ModalContext) -> None: await ctx.respond( f"**Subject:** {self.subject.value}\n**Message:** {self.body.value}", flags=hikari.MessageFlag.EPHEMERAL, ) class OpenModalView(miru.View): @miru.button(label="Open Feedback Form", style=hikari.ButtonStyle.PRIMARY) async def open_modal(self, ctx: miru.ViewContext, button: miru.Button) -> None: modal = FeedbackModal() await ctx.respond_with_modal(modal) @bot.listen() async def on_message(event: hikari.GuildMessageCreateEvent) -> None: if not event.is_human: return me = bot.get_me() if me.id in event.message.user_mentions_ids: view = OpenModalView() await event.message.respond("Click to open a modal:", components=view) client.start_view(view) bot.run() ``` ``` -------------------------------- ### Create Navigator with Gateway and crescent Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/navigators.md Shows how to set up a navigator using crescent for gateway interactions. Requires miru.ext.nav import. ```python import crescent import hikari import miru bot = hikari.GatewayBot("TOKEN") client = miru.Client(bot) crescent_client = crescent.Client(bot) @crescent_client.include @crescent.command(name="name", description="description") class SomeSlashCommand: async def callback(self, ctx: crescent.Context) -> None: embed = hikari.Embed( title="I'm the second page!", description="Also an embed!" ) # A Page object can be used to further customize the page payload page = nav.Page( content="I'm the last page!", embed=hikari.Embed(title="I also have an embed!") ) # The list of pages this navigator should paginate through # This should be a list that contains # 'str', 'hikari.Embed', or 'nav.Page' objects. pages = ["I'm the first page!", embed, page] # Define our navigator and pass in our list of pages navigator = nav.NavigatorView(pages=pages) builder = await navigator.build_response_async(client) await ctx.respond_with_builder(builder) client.start_view(navigator) bot.run() ``` -------------------------------- ### Create and Send Menu with Lightbulb Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/menus.md This snippet shows how to implement menus with Lightbulb commands. It includes notes on potential deferral if initial page building takes time. ```python @lightbulb_bot.command() @lightbulb.command("name", "description", auto_defer=False) @lightbulb.implements(lightbulb.SlashCommand) async def some_slash_command(ctx: lightbulb.SlashContext) -> None: my_menu = menu.Menu() # Create a new Menu # You may need to defer if building your first page takes more than 3 seconds builder = await my_menu.build_response_async(client, MainScreen(my_menu)) await builder.create_initial_response(ctx.interaction) # Or if using a prefix command: # await builder.send_to_channel(ctx.channel_id) client.start_view(my_menu) ``` -------------------------------- ### Start Unbound View with Hikari REST Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/persistent_views.md Start an unbound persistent view for a REST bot during its startup callback. The view must be reinstantiated with identical custom IDs, and bind_to should be set to None. ```python # Let's assume this is a startup callback of a RESTBot async def start_views(bot: hikari.RESTBot) -> None: # You must reinstantiate the view with the same custom_ids every time view = Persistence() # Restart the listener for the view after application startup # and explicitly tell it to not bind to any message client.start_view(view, bind_to=None) ``` -------------------------------- ### Initialize Miru Client with Lightbulb Bot App Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/getting_started.md Set up a Lightbulb BotApp and create a Miru client. Lightbulb only supports Gateway bots, so this configuration is for that specific use case. ```python bot = lightbulb.BotApp("YOUR_TOKEN_HERE") client = miru.Client(bot) ``` -------------------------------- ### Start Unbound View with Arc REST Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/persistent_views.md Start an unbound persistent view for an Arc REST client via its startup hook. Ensure the view is re-initialized with the same custom IDs and bind_to is set to None. ```python @arc_client.set_startup_hook async def start_views(client: arc.RESTClient) -> None: # You must reinstantiate the view with the same custom_ids every time view = Persistence() # Restart the listener for the view after application startup # and explicitly tell it to not bind to any message client.start_view(view, bind_to=None) ``` -------------------------------- ### miru.Client Initialization Source: https://context7.com/hypergonial/hikari-miru/llms.txt Demonstrates how to initialize the `miru.Client` with either a Gateway or REST hikari bot instance. It also shows integration with the `arc` command handler. ```APIDOC ## `miru.Client` — Core Client `Client` wraps a hikari bot instance and routes all component and modal interactions to the correct running handler. It must be created before any views or modals can be started. Supports both `GatewayBot` and `RESTBot`. ```python import hikari import miru # Gateway bot bot = hikari.GatewayBot("YOUR_TOKEN") client = miru.Client(bot) # REST bot # bot = hikari.RESTBot("YOUR_TOKEN") # client = miru.Client(bot) # Integration with arc (shares dependency injector) # import arc # arc_client = arc.GatewayClient(bot) # client = miru.Client.from_arc(arc_client) bot.run() ``` ``` -------------------------------- ### Build Nested Menus with `miru.ext.menu` Source: https://context7.com/hypergonial/hikari-miru/llms.txt Use `Screen` and `Menu` to create multi-level interactive menus. `build_content` defines screen content, and `Menu.push`/`Menu.pop` manage screen transitions. ```python import hikari import miru from miru.ext import menu class MainScreen(menu.Screen): async def build_content(self) -> menu.ScreenContent: return menu.ScreenContent( embed=hikari.Embed(title="Main Menu", description="Choose a category.", color=0x5865F2) ) @menu.button(label="Settings ⚙️", style=hikari.ButtonStyle.PRIMARY) async def settings(self, ctx: miru.ViewContext, button: miru.ScreenButton) -> None: await self.menu.push(SettingsScreen(self.menu)) class SettingsScreen(menu.Screen): async def build_content(self) -> menu.ScreenContent: return menu.ScreenContent( embed=hikari.Embed(title="Settings", description="Manage your settings.", color=0xE67E22) ) @menu.button(label="← Back", style=hikari.ButtonStyle.SECONDARY) async def back(self, ctx: miru.ViewContext, button: menu.ScreenButton) -> None: await self.menu.pop() @menu.button(label="Reset Settings", style=hikari.ButtonStyle.DANGER) async def reset(self, ctx: miru.ViewContext, button: menu.ScreenButton) -> None: await ctx.respond("Settings reset!", flags=hikari.MessageFlag.EPHEMERAL) ``` -------------------------------- ### Define Main Menu Screen Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/menus.md This is the entry point screen for the menu. It defines buttons to navigate to other screens. Ensure you override the `build_content` method to define the screen's appearance. ```python from miru.ext import menu import hikari class MainScreen(menu.Screen): async def build_content(self) -> menu.ScreenContent: return menu.ScreenContent( embed=hikari.Embed( title="Welcome to the Miru Menu example!", description="This is an example of the Miru Menu extension.", color=0x00FF00, ), ) @menu.button(label="Moderation") async def moderation(self, ctx: miru.ViewContext, button: menu.ScreenButton) -> None: await self.menu.push(ModerationScreen(self.menu)) @menu.button(label="Logging") async def fun(self, ctx: miru.ViewContext, button: menu.ScreenButton) -> None: await self.menu.push(LoggingScreen(self.menu)) ``` -------------------------------- ### Sending Modals: v4 vs v3 Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/migrating_from_v3.md In v4, `Modal.send()` is removed. Use `modal.build_response()` to get a builder, which can then be used to create a modal response or sent via `client.start_modal()`. ```python modal = miru.Modal(...) # This returns a custom InteractionModalBuilder # It can be returned in REST callbacks builder = modal.build_response() # Or you can use some of the custom methods it has await builder.create_modal_response(interaction) client.start_modal(modal) ``` ```python modal = miru.Modal(...) await modal.send(interaction) ``` -------------------------------- ### Create and Send Menu (REST) Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/menus.md This snippet shows how to create a menu and prepare its response for a REST-based interaction, such as a command callback. The builder is yielded for the REST response. ```python # Let's assume this is a RESTBot's CommandInteraction callback async def handle_command(interaction: hikari.CommandInteraction): my_menu = menu.Menu() # Create a new Menu # Pass in the initial screen builder = await my_menu.build_response_async(client, MainScreen(my_menu)) # The builder is a valid REST response builder yield builder # Assign the view to the client and start it client.start_view(my_menu) ``` -------------------------------- ### Create Navigator with REST and arc Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/navigators.md Demonstrates creating a navigator with arc for RESTful interactions. Ensure the miru.ext.nav module is imported. ```python import arc import hikari import miru # Import the navigation module from miru.ext import nav bot = hikari.RESTBot("TOKEN") arc_client = arc.RESTClient(bot) client = miru.Client.from_arc(arc_client) @arc_client.include @arc.slash_command("name", "description") async def my_command(ctx: arc.RESTContext) -> None: embed = hikari.Embed( title="I'm the second page!", description="Also an embed!" ) # A Page object can be used to further customize the page payload page = nav.Page( content="I'm the last page!", embed=hikari.Embed(title="I also have an embed!") ) # The list of pages this navigator should paginate through # This should be a list that contains # 'str', 'hikari.Embed', or 'nav.Page' objects. pages = ["I'm the first page!", embed, page] # Define our navigator and pass in our list of pages navigator = nav.NavigatorView(pages=pages) builder = await navigator.build_response_async(client) await ctx.respond_with_builder(builder) client.start_view(navigator) bot.run() ``` -------------------------------- ### Create Navigator with REST and crescent Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/navigators.md Illustrates navigator creation with crescent for RESTful commands. Ensure miru.ext.nav is imported. ```python import crescent import hikari import miru bot = hikari.RESTBot("TOKEN") client = miru.Client(bot) crescent_client = crescent.Client(bot) @crescent_client.include @crescent.command(name="name", description"description") class SomeSlashCommand: async def callback(self, ctx: crescent.Context) -> None: embed = hikari.Embed( title="I'm the second page!", description="Also an embed!" ) # A Page object can be used to further customize the page payload page = nav.Page( content="I'm the last page!", embed=hikari.Embed(title="I also have an embed!") ) # The list of pages this navigator should paginate through # This should be a list that contains # 'str', 'hikari.Embed', or 'nav.Page' objects. pages = ["I'm the first page!", embed, page] # Define our navigator and pass in our list of pages navigator = nav.NavigatorView(pages=pages) builder = await navigator.build_response_async(client) await ctx.respond_with_builder(builder) client.start_view(navigator) bot.run() ``` -------------------------------- ### Create and Send Menu (Gateway) Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/menus.md This snippet demonstrates how to create a new menu and send its initial response when a bot is mentioned in a message. It's designed for gateway-based interactions. ```python import hikari import menu # Assuming 'bot' and 'client' are already initialized # Assuming 'MainScreen' is a defined screen class @bot.listen() async def buttons(event: hikari.MessageCreateEvent) -> None: # Do not process messages from bots or webhooks if not event.is_human: return me = bot.get_me() # If the bot is mentioned if me.id in event.message.user_mentions_ids: my_menu = menu.Menu() # Create a new Menu # Pass in the initial screen builder = await my_menu.build_response_async(client, MainScreen(my_menu)) await builder.send_to_channel(event.channel_id) client.start_view(my_menu) ``` -------------------------------- ### Edit View Item Properties and Re-send Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/editing_items.md Modify item properties like labels and then use `ctx.edit_response(components=self)` to update the view. This example demonstrates incrementing a counter and disabling buttons. ```python import hikari import miru class EditView(miru.View): def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.counter = 0 @miru.button(label="Counter: 0", style=hikari.ButtonStyle.PRIMARY) async def counter_button(self, ctx: miru.ViewContext, button: miru.Button) -> None: self.counter += 1 # Change the property we want to edit button.label = f"Counter: {self.counter}" # Re-send the view components await ctx.edit_response(components=self) @miru.button(label="Disable Menu", style=hikari.ButtonStyle.DANGER) async def disable_button(self, ctx: miru.ViewContext, button: miru.Button) -> None: # Disable all items attached to the view for item in self.children: item.disabled = True await ctx.edit_response(components=self) self.stop() ``` -------------------------------- ### Create and Send a Navigator View Source: https://github.com/hypergonial/hikari-miru/blob/main/docs/guides/navigators.md Define a list of pages (strings, embeds, or Page objects) and pass them to NavigatorView. Then, build the response and send it to the channel or interaction. ```python from hikari import Embed from miru import nav # Assuming 'client' and 'ctx' are defined elsewhere # Define a Page object page = nav.Page( content="I'm the last page!", embed=Embed(title="I also have an embed!") ) # The list of pages this navigator should paginate through # This should be a list that contains # 'str', 'hikari.Embed', or 'nav.Page' objects. pages = ["I'm the first page!", embed, page] # Define our navigator and pass in our list of pages navigator = nav.NavigatorView(pages=pages) builder = await navigator.build_response_async(client) await builder.create_initial_response(ctx.interaction) # Or in a prefix command: # await builder.send_to_channel(ctx.channel_id) client.start_view(navigator) ```