### Basic GatewayBot Startup Source: https://github.com/hikari-py/hikari/blob/master/README.md A simple example of starting a GatewayBot that listens for guild messages and responds to mentions with 'Pong!'. It avoids responding to bots and webhooks. ```python import hikari bot = hikari.GatewayBot(token="...") @bot.listen() async def ping(event: hikari.GuildMessageCreateEvent) -> None: """If a non-bot user mentions your bot, respond with 'Pong!'.""" # Do not respond to bots nor webhooks pinging us, only user accounts if not event.is_human: return me = bot.get_me() if me.id in event.message.user_mentions_ids: await event.message.respond("Pong!") bot.run() ``` -------------------------------- ### Install Towncrier and Hikari Source: https://github.com/hikari-py/hikari/blob/master/CONTRIBUTING.md Install the necessary tools for contributing, including Towncrier and Hikari from source. ```bash uv sync --group towncrier ``` ```bash pip install towncrier ``` -------------------------------- ### Install Hikari with pip Source: https://context7.com/hikari-py/hikari/llms.txt Install Hikari from PyPI. Use the `[speedups]` extra for C-extension speedups and `[server]` for interaction webhook support. ```bash pip install -U hikari pip install -U "hikari[speedups]" pip install -U "hikari[server]" py -3 -m pip install -U hikari ``` -------------------------------- ### GatewayBot Initialization and Event Handling Source: https://context7.com/hikari-py/hikari/llms.txt Demonstrates how to initialize a GatewayBot with specific intents and set up event listeners for bot events like starting, message creation, and stopping. ```APIDOC ## GatewayBot Initialization and Event Handling ### Description Initialize a `GatewayBot` instance with desired intents and configure event listeners using the `@bot.listen()` decorator to handle various Discord events. ### Method `hikari.GatewayBot()` ### Parameters - **token** (str) - Required - The bot's authentication token. - **intents** (hikari.Intents) - Required - The gateway intents to subscribe to. - **logs** (str, optional) - The logging level for the bot. ### Event Listeners - `@bot.listen()`: Decorator to subscribe an async callback to an event. - `hikari.StartingEvent`: Event dispatched before the bot connects. - `hikari.GuildMessageCreateEvent`: Event dispatched when a message is created in a guild. - `hikari.StoppedEvent`: Event dispatched after the bot disconnects. ### Request Example ```python import os import hikari bot = hikari.GatewayBot( token=os.environ["BOT_TOKEN"], intents=hikari.Intents.GUILDS | hikari.Intents.GUILD_MESSAGES | hikari.Intents.DM_MESSAGES, logs="INFO", ) @bot.listen() async def on_starting(event: hikari.StartingEvent) -> None: print("Bot is starting up...") @bot.listen() async def on_message(event: hikari.GuildMessageCreateEvent) -> None: if not event.is_human: return if event.content == "!ping": await event.message.respond(f"Pong! {bot.heartbeat_latency * 1_000:.0f}ms") elif event.content == "!info": me = bot.get_me() await event.message.respond(f"I am {me.username}#{me.discriminator}") @bot.listen() async def on_stopped(event: hikari.StoppedEvent) -> None: print("Bot has stopped.") ``` ### Running the Bot `bot.run()` starts the bot and blocks until shutdown. Optional arguments can configure the run behavior. ### Request Example ```python bot.run( asyncio_debug=True, coroutine_tracking_depth=20, propagate_interrupts=True, check_for_updates=True, ) ``` ``` -------------------------------- ### Install Nox with uv Source: https://github.com/hikari-py/hikari/blob/master/CONTRIBUTING.md Install the Nox package manager, which is used for running local pipelines. An equivalent pip command is provided. ```bash uv sync --group nox ``` ```bash pip install 'nox[uv]' ``` -------------------------------- ### GatewayBot.run Method Source: https://context7.com/hikari-py/hikari/llms.txt Details on how to use the `GatewayBot.run()` method for starting the bot, including options for multi-shard operation and activity status. ```APIDOC ## GatewayBot.run — Blocking Bot Start ### Description `GatewayBot.run()` starts all gateway shards and blocks until the bot is shut down. It handles signal registration, asyncio loop creation, and shard lifecycle management. For non-blocking startup, use `await bot.start()` instead. ### Method `GatewayBot.run()` ### Parameters - **status** (hikari.Status, optional) - The bot's status (e.g., ONLINE, IDLE). - **activity** (hikari.Activity, optional) - The bot's activity (e.g., PLAYING, LISTENING). - **shard_ids** (list[int], optional) - List of shard IDs to run. - **shard_count** (int, optional) - Total number of shards. - **large_threshold** (int, optional) - Guild size threshold for member chunking. - **ignore_session_start_limit** (bool, optional) - Whether to ignore session start limits. - **enable_signal_handlers** (bool, optional) - Whether to enable OS signal handlers. - **close_loop** (bool, optional) - Whether to close the asyncio loop on shutdown. ### Request Example ```python import hikari bot = hikari.GatewayBot(token="...") # Production multi-shard run — run shards 0 and 1 out of a total of 4 bot.run( status=hikari.Status.ONLINE, activity=hikari.Activity(name="with Python", type=hikari.ActivityType.PLAYING), shard_ids=[0, 1], shard_count=4, large_threshold=250, ignore_session_start_limit=False, enable_signal_handlers=True, close_loop=True, ) ``` ``` -------------------------------- ### GatewayBot with Custom Intents Source: https://github.com/hikari-py/hikari/blob/master/README.md Example of initializing a GatewayBot with custom intents to control which events the bot receives. ```python import hikari # the default is to enable all unprivileged intents (all events that do not target the ``` -------------------------------- ### Install Hikari Source: https://github.com/hikari-py/hikari/blob/master/README.md Install or upgrade Hikari using pip. Windows users may need to use 'py -3'. ```bash python -m pip install -U hikari # Windows users may need to run this instead... py -3 -m pip install -U hikari ``` -------------------------------- ### Preview Changelog with Towncrier Source: https://github.com/hikari-py/hikari/blob/master/changes/README.md Run this command to preview the latest changelog before it is finalized. Ensure towncrier is installed. ```bash towncrier --draft ``` -------------------------------- ### Create and Run a Gateway Bot Source: https://context7.com/hikari-py/hikari/llms.txt Create a `GatewayBot` instance with specified intents and token. Listen for events like `StartingEvent`, `GuildMessageCreateEvent`, and `StoppedEvent`. The `bot.run()` method starts the bot and blocks until shutdown, with options for asyncio debugging and update checks. ```python import os import hikari bot = hikari.GatewayBot( token=os.environ["BOT_TOKEN"], intents=hikari.Intents.GUILDS | hikari.Intents.GUILD_MESSAGES | hikari.Intents.DM_MESSAGES, logs="INFO", ) @bot.listen() async def on_starting(event: hikari.StartingEvent) -> None: """Runs before bot connects — good place to init databases.""" print("Bot is starting up...") @bot.listen() async def on_message(event: hikari.GuildMessageCreateEvent) -> None: """Respond to !ping in guild channels.""" if not event.is_human: return # Ignore bots and webhooks if event.content == "!ping": await event.message.respond(f"Pong! {bot.heartbeat_latency * 1_000:.0f}ms") elif event.content == "!info": me = bot.get_me() await event.message.respond(f"I am {me.username}#{me.discriminator}") @bot.listen() async def on_stopped(event: hikari.StoppedEvent) -> None: """Runs after bot disconnects — clean up resources here.""" print("Bot has stopped.") # Run with development options; use bot.run() with no args for production bot.run( asyncio_debug=True, coroutine_tracking_depth=20, propagate_interrupts=True, check_for_updates=True, ) ``` -------------------------------- ### Use uvloop for Asyncio Event Loop Source: https://github.com/hikari-py/hikari/blob/master/README.md On UNIX-like systems, replace the default asyncio event loop with uvloop for performance benefits. Ensure uvloop is installed. ```python import asyncio import os if os.name != "nt": import uvloop asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) # Your code goes here ``` -------------------------------- ### Initialize RESTApp for REST-only Applications Source: https://github.com/hikari-py/hikari/blob/master/README.md Initialize a RESTApp instance for integrating solely with the Discord REST API, such as for a web dashboard. This setup allows for acquiring clients with specific tokens to interact with the API. ```python import hikari import asyncio rest = hikari.RESTApp() async def print_my_user(token): await rest.start() # We acquire a client with a given token. This allows one REST app instance # with one internal connection pool to be reused. async with rest.acquire(token) as client: my_user = await client.fetch_my_user() print(my_user) await rest.close() asyncio.run(print_my_user("user token acquired through OAuth here")) ``` -------------------------------- ### Hikari Bot Lifetime Events Source: https://context7.com/hikari-py/hikari/llms.txt This example shows how to use `StartingEvent`, `StartedEvent`, `StoppingEvent`, and `StoppedEvent` to hook into the bot's lifecycle for initialization and cleanup tasks. ```python import hikari bot = hikari.GatewayBot(token="...") db = None # Placeholder for a database connection @bot.listen() async def on_starting(event: hikari.StartingEvent) -> None: """Fires before the gateway connects. Bot will wait for all handlers here.""" global db # db = await aiosqlite.connect("bot.db") print("Database connected.") @bot.listen() async def on_started(event: hikari.StartedEvent) -> None: """Fires after all shards are connected and ready.""" me = bot.get_me() print(f"Logged in as {me.username} ({me.id})") await bot.update_presence( status=hikari.Status.ONLINE, activity=hikari.Activity(name="!help", type=hikari.ActivityType.LISTENING), ) @bot.listen() async def on_stopping(event: hikari.StoppingEvent) -> None: """Fires before the gateway disconnects. Bot will wait for all handlers here.""" print("Shutting down...") @bot.listen() async def on_stopped(event: hikari.StoppedEvent) -> None: """Fires after all shards are disconnected.""" if db: # await db.close() print("Database closed.") bot.run() ``` -------------------------------- ### Run Gateway Bot with Production Options Source: https://context7.com/hikari-py/hikari/llms.txt Use `bot.run()` to start gateway shards and block until shutdown. This method handles signal registration and asyncio loop management. Production runs can specify shard IDs, shard count, and other lifecycle options. ```python import hikari bot = hikari.GatewayBot(token="...") # Production multi-shard run — run shards 0 and 1 out of a total of 4 bot.run( status=hikari.Status.ONLINE, activity=hikari.Activity(name="with Python", type=hikari.ActivityType.PLAYING), shard_ids=[0, 1], shard_count=4, large_threshold=250, ignore_session_start_limit=False, enable_signal_handlers=True, close_loop=True, ) ``` -------------------------------- ### hikari.RESTBot - Interaction Webhook Server Source: https://context7.com/hikari-py/hikari/llms.txt Starts an HTTP server to receive and verify Discord interaction payloads. Ideal for serverless or stateless deployments. Requires bot token and public key. ```python import asyncio import hikari bot = hikari.RESTBot( token=os.environ["BOT_TOKEN"], token_type=hikari.TokenType.BOT, public_key=os.environ["PUBLIC_KEY"], # From Discord Developer Portal ) async def handle_slash_command(interaction: hikari.CommandInteraction): """Generator-based deferred response handler.""" if interaction.command_name == "greet": name_option = interaction.options[0].value if interaction.options else "World" # Yield initial deferred response (shows "Bot is thinking...") yield interaction.build_deferred_response() # Do slow work here await asyncio.sleep(1) # Edit the deferred response with actual content await interaction.edit_initial_response(f"Hello, {name_option}!") elif interaction.command_name == "ping": # Immediate response — no deferral needed yield interaction.build_response(hikari.ResponseType.MESSAGE_CREATE).set_content("Pong!") async def register_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("ping", "Check bot latency"), bot.rest.slash_command_builder("greet", "Say hello") .add_option( bot.rest.slash_command_builder("name", "Who to greet") .__class__ # placeholder — use CommandOption ), ], ) bot.add_startup_callback(register_commands) bot.set_listener(hikari.CommandInteraction, handle_slash_command) # Host on 0.0.0.0:8080; expose via ngrok or a reverse proxy for Discord bot.run(host="0.0.0.0", port=8080) ``` -------------------------------- ### OAuth2 Flow with hikari.RESTApp Source: https://context7.com/hikari-py/hikari/llms.txt Implement the OAuth2 code exchange flow to allow users to authorize your bot. This example uses aiohttp to handle web requests and exchanges an authorization code for an access token to fetch user info and post notifications. ```python import os from aiohttp import web import hikari CLIENT_ID = int(os.environ["CLIENT_ID"]) CLIENT_SECRET = os.environ["CLIENT_SECRET"] BOT_TOKEN = os.environ["BOT_TOKEN"] CHANNEL_ID = int(os.environ["CHANNEL_ID"]) REDIRECT_URI = "http://localhost:8080" route_table = web.RouteTableDef() @route_table.get("/") async def oauth_callback(request: web.Request) -> web.Response: code = request.query.get("code") if not code: return web.json_response({"error": "'code' is not provided"}, status=400) discord_rest: hikari.RESTApp = request.app["discord_rest"] # Exchange authorization code for access token async with discord_rest.acquire(None) as r: auth = await r.authorize_access_token(CLIENT_ID, CLIENT_SECRET, code, REDIRECT_URI) # Fetch user info using their Bearer token async with discord_rest.acquire(auth.access_token, hikari.TokenType.BEARER) as client: user = await client.fetch_my_user() # Post notification to a channel using the bot token async with discord_rest.acquire(BOT_TOKEN, hikari.TokenType.BOT) as client: await client.create_message(CHANNEL_ID, f"{user} ({user.id}) just authorized!") return web.Response(text="Successfully authenticated!") async def start_discord_rest(app: web.Application) -> None: discord_rest = hikari.RESTApp() await discord_rest.start() app["discord_rest"] = discord_rest async def stop_discord_rest(app: web.Application) -> None: await app["discord_rest"].close() if __name__ == "__main__": server = web.Application() server.add_routes(route_table) server.on_startup.append(start_discord_rest) server.on_cleanup.append(stop_discord_rest) web.run_app(server, host="localhost", port=8080) ``` -------------------------------- ### hikari.RESTBot — Interaction Webhook Server Source: https://context7.com/hikari-py/hikari/llms.txt `RESTBot` starts an HTTP server that receives and verifies Discord interaction payloads via webhook. It does not maintain a gateway connection and is ideal for serverless or stateless deployments handling only slash commands and component interactions. ```APIDOC ## `hikari.RESTBot` — Interaction Webhook Server `RESTBot` starts an HTTP server that receives and verifies Discord interaction payloads via webhook. It does not maintain a gateway connection and is ideal for serverless or stateless deployments handling only slash commands and component interactions. ```python import asyncio import hikari bot = hikari.RESTBot( token=os.environ["BOT_TOKEN"], token_type=hikari.TokenType.BOT, public_key=os.environ["PUBLIC_KEY"], # From Discord Developer Portal ) async def handle_slash_command(interaction: hikari.CommandInteraction): """Generator-based deferred response handler.""" if interaction.command_name == "greet": name_option = interaction.options[0].value if interaction.options else "World" # Yield initial deferred response (shows "Bot is thinking...") yield interaction.build_deferred_response() # Do slow work here await asyncio.sleep(1) # Edit the deferred response with actual content await interaction.edit_initial_response(f"Hello, {name_option}!") elif interaction.command_name == "ping": # Immediate response — no deferral needed yield interaction.build_response(hikari.ResponseType.MESSAGE_CREATE).set_content("Pong!") async def register_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("ping", "Check bot latency"), bot.rest.slash_command_builder("greet", "Say hello") .add_option( bot.rest.slash_command_builder("name", "Who to greet") .__class__ # placeholder — use CommandOption ), ], ) bot.add_startup_callback(register_commands) bot.set_listener(hikari.CommandInteraction, handle_slash_command) # Host on 0.0.0.0:8080; expose via ngrok or a reverse proxy for Discord bot.run(host="0.0.0.0", port=8080) ``` ``` -------------------------------- ### Initialize and Configure RESTBot Source: https://github.com/hikari-py/hikari/blob/master/README.md Sets up a RESTBot, defines an interaction handler, registers slash commands on startup, and sets up the interaction listener. ```python import asyncio import hikari # This function will handle the interactions received async def handle_command(interaction: hikari.CommandInteraction): # Create an initial response to be able to take longer to respond yield interaction.build_deferred_response() await asyncio.sleep(5) # Edit the initial response await interaction.edit_initial_response("Edit after 5 seconds!") # Register the commands on startup. # # Note that this is not a nice way to manage this, as it is quite spammy # to do it every time the bot is started. You can either use a command handler # or only run this code in a script using `RESTApp` or add checks to not update # the commands if there were no changes async def create_commands(bot: hikari.RESTBot): 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 = hikari.RESTBot( token="...", token_type="...", public_key="...", ) bot.add_startup_callback(create_commands) bot.set_listener(hikari.CommandInteraction, handle_command) bot.run() ``` -------------------------------- ### Initialize GatewayBot with All Intents Source: https://github.com/hikari-py/hikari/blob/master/README.md Use this to initialize a GatewayBot that receives all available events. Note that whitelisting your application may be required. ```python bot = hikari.GatewayBot(intents=hikari.Intents.ALL, token="...") ``` -------------------------------- ### Create Virtual Environment with uv Source: https://github.com/hikari-py/hikari/blob/master/CONTRIBUTING.md Use this command to create a virtual environment for the project. An equivalent pip command is available. ```bash uv venv ``` ```bash python -m venv .venv ``` -------------------------------- ### Basic REST API Operations Source: https://context7.com/hikari-py/hikari/llms.txt Demonstrates common REST API operations such as kicking users, fetching user details, and creating channels using the `bot.rest` client. ```APIDOC ## `hikari.GatewayBot.rest` — REST API Client `bot.rest` exposes the full Discord REST API as async methods for creating messages, managing channels and roles, fetching users, and working with application commands. It handles rate limiting and retries automatically. ### Example Usage: ```python import hikari bot = hikari.GatewayBot(token="...") @bot.listen() async def on_message(event: hikari.GuildMessageCreateEvent) -> None: if not event.is_human: return if event.content == "!kick" and event.message.member: # Check permissions — requires KICK_MEMBERS try: await bot.rest.kick_user( event.guild_id, event.author.id, reason="Requested via !kick command", ) await event.message.respond("User kicked.") except hikari.ForbiddenError: await event.message.respond("I don't have permission to kick members.") except hikari.NotFoundError: await event.message.respond("User not found.") elif event.content == "!fetch_user": user = await bot.rest.fetch_user(event.author.id) await event.message.respond( f"Username: {user.username}\n" f"ID: {user.id}\n" f"Bot: {user.is_bot}\n" f"Avatar: {user.display_avatar_url}" ) elif event.content == "!create_channel": channel = await bot.rest.create_guild_text_channel( event.guild_id, "new-channel", topic="Created via bot command", reason="User requested new channel", ) await event.message.respond(f"Created {channel.mention}") bot.run() ``` ``` -------------------------------- ### Hikari Performance Optimizations Source: https://context7.com/hikari-py/hikari/llms.txt Shows how to apply performance optimizations for production deployments, including C-extension speedups, Python interpreter optimization flags, and the `uvloop` event loop. ```python # 1. Install speedups (requires a C compiler) # pip install -U "hikari[speedups]" # 2. Use uvloop on UNIX for libuv-backed event loop import asyncio import os if os.name != "nt": import uvloop asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) import hikari # 3. Configure cache to reduce memory on large bots cache_settings = hikari.impl.config.CacheSettings( components=hikari.api.config.CacheComponents.GUILDS | hikari.api.config.CacheComponents.MESSAGES, max_messages=500, max_dm_channel_ids=100, ) bot = hikari.GatewayBot( token=os.environ["BOT_TOKEN"], cache_settings=cache_settings, max_rate_limit=300.0, # Max seconds to wait on rate limit before raising max_retries=3, # Number of server-error retries ) bot.run() # 4. Run with CPython optimization flags in production: # python -O bot.py (level 1: disable assertions) # python -OO bot.py (level 2: also strip docstrings) ``` -------------------------------- ### Application Commands (Slash Commands) Source: https://context7.com/hikari-py/hikari/llms.txt Register and handle Discord application commands using `bot.rest.slash_command_builder()` and `set_application_commands()`. This section shows how to set up and respond to slash commands. ```APIDOC ## Application Commands (Slash Commands) Register and handle Discord application commands using `bot.rest.slash_command_builder()` and `set_application_commands()`. Listen for `CommandInteractionCreateEvent` on a `GatewayBot` or use `set_listener` on a `RESTBot`. ### Example Usage: ```python import os import hikari bot = hikari.GatewayBot(token=os.environ["BOT_TOKEN"]) COMMAND_GUILD_ID = hikari.UNDEFINED # Set to guild Snowflake for guild-only, UNDEFINED for global @bot.listen() async def register_commands(_: hikari.StartingEvent) -> None: application = await bot.rest.fetch_application() await bot.rest.set_application_commands( application=application.id, commands=[ bot.rest.slash_command_builder("ping", "Get the bot's latency."), bot.rest.slash_command_builder("info", "Learn something about the bot."), bot.rest.slash_command_builder("ephemeral", "Send a very secret message."), ], guild=COMMAND_GUILD_ID, ) @bot.listen() async def handle_interactions(event: hikari.CommandInteractionCreateEvent) -> None: name = event.interaction.command_name if name == "ping": await event.interaction.create_initial_response( hikari.ResponseType.MESSAGE_CREATE, f"Pong! {bot.heartbeat_latency * 1_000:.0f}ms", ) elif name == "info": await event.interaction.create_initial_response( hikari.ResponseType.MESSAGE_CREATE, "Hello, this is an example bot written in hikari!", ) elif name == "ephemeral": await event.interaction.create_initial_response( hikari.ResponseType.MESSAGE_CREATE, "Only you can see this, keep it a secret :)", flags=hikari.MessageFlag.EPHEMERAL, ) bot.run() ``` ``` -------------------------------- ### Register and Handle Application Commands with GatewayBot Source: https://context7.com/hikari-py/hikari/llms.txt Register slash commands using `bot.rest.slash_command_builder()` and `set_application_commands()`. Listen for `CommandInteractionCreateEvent` to handle command invocations. Commands can be guild-specific or global. ```python import os import hikari bot = hikari.GatewayBot(token=os.environ["BOT_TOKEN"]) COMMAND_GUILD_ID = hikari.UNDEFINED # Set to guild Snowflake for guild-only, UNDEFINED for global @bot.listen() async def register_commands(_: hikari.StartingEvent) -> None: application = await bot.rest.fetch_application() await bot.rest.set_application_commands( application=application.id, commands=[ bot.rest.slash_command_builder("ping", "Get the bot's latency."), bot.rest.slash_command_builder("info", "Learn something about the bot."), bot.rest.slash_command_builder("ephemeral", "Send a very secret message."), ], guild=COMMAND_GUILD_ID, ) @bot.listen() async def handle_interactions(event: hikari.CommandInteractionCreateEvent) -> None: name = event.interaction.command_name if name == "ping": await event.interaction.create_initial_response( hikari.ResponseType.MESSAGE_CREATE, f"Pong! {bot.heartbeat_latency * 1_000:.0f}ms", ) elif name == "info": await event.interaction.create_initial_response( hikari.ResponseType.MESSAGE_CREATE, "Hello, this is an example bot written in hikari!", ) elif name == "ephemeral": await event.interaction.create_initial_response( hikari.ResponseType.MESSAGE_CREATE, "Only you can see this, keep it a secret :)", flags=hikari.MessageFlag.EPHEMERAL, ) bot.run() ``` -------------------------------- ### Listen for MessageCreateEvent with Type Hint Source: https://github.com/hikari-py/hikari/blob/master/README.md Register an asynchronous function to listen for specific events using type hints on the event parameter. ```python import hikari bot = hikari.GatewayBot("...") @bot.listen() async def ping(event: hikari.MessageCreateEvent): ... ``` -------------------------------- ### Programmatic Subscriptions (`subscribe` / `unsubscribe`) Source: https://context7.com/hikari-py/hikari/llms.txt Allows for dynamic registration and de-registration of event listeners at runtime without using decorators. This is particularly useful for plugin systems or when managing listeners programmatically. ```APIDOC ## `hikari.GatewayBot.subscribe` / `unsubscribe` — Programmatic Subscriptions `bot.subscribe()` and `bot.unsubscribe()` attach and detach event callbacks programmatically without decorators. This is useful for dynamic listener management, plugin systems, and testing. ```python import hikari bot = hikari.GatewayBot(token="...") async def on_reaction(event: hikari.GuildReactionAddEvent) -> None: print(f"User {event.user_id} reacted with {event.emoji_name} on {event.message_id}") # Register listener at runtime bot.subscribe(hikari.GuildReactionAddEvent, on_reaction) # Later — unregister it bot.unsubscribe(hikari.GuildReactionAddEvent, on_reaction) ``` ``` -------------------------------- ### Configure Bot Run Options Source: https://github.com/hikari-py/hikari/blob/master/README.md Pass additional options to bot.run() during development for debugging and clearer error reporting. Ensure you consult the respective documentation for GatewayBot.run and RESTBot.run for a full list of available options. ```python import hikari bot = hikari.GatewayBot("...") # or bot = hikari.RESTBot("...", "...") bot.run( asyncio_debug=True, # enable asyncio debug to detect blocking and slow code. coroutine_tracking_depth=20, # enable tracking of coroutines, makes some asyncio # errors clearer. propagate_interrupts=True, # Any OS interrupts get rethrown as errors. ) ``` -------------------------------- ### GatewayBot.listen Decorator Source: https://context7.com/hikari-py/hikari/llms.txt Explains how to use the `@bot.listen()` decorator to subscribe asynchronous callback functions to specific Discord event types. ```APIDOC ## GatewayBot.listen — Event Listener Decorator ### Description `@bot.listen()` subscribes an async callback to one or more event types. The event type can be inferred from the function's type annotation or specified explicitly. ### Method `@bot.listen()` ### Parameters - **event_type** (type, optional) - The specific event type to listen for. If not provided, it's inferred from the callback's type annotation. ### Usage Subscribe an async function to listen for specific Discord events. ### Request Example ```python import hikari bot = hikari.GatewayBot(token="...") # Type inferred from annotation @bot.listen() async def on_guild_join(event: hikari.GuildJoinEvent) -> None: print(f"Joined guild: {event.guild.name} ({event.guild_id})") # Type specified explicitly — no annotation needed @bot.listen(hikari.GuildLeaveEvent) async def on_guild_leave(event) -> None: print(f"Left guild: {event.guild_id}") ``` ``` -------------------------------- ### Create a Changelog Fragment Source: https://github.com/hikari-py/hikari/blob/master/CONTRIBUTING.md Use Towncrier to create a new changelog fragment file for a pull request. The file name should follow the format {pull_request_number}.{type}.md. ```bash towncrier create {pull_request_number}.{type}.md ``` -------------------------------- ### Programmatically Subscribe and Unsubscribe to Events in Hikari Source: https://context7.com/hikari-py/hikari/llms.txt Use bot.subscribe() and bot.unsubscribe() to dynamically manage event listeners at runtime without decorators. This is ideal for plugin systems or testing scenarios where listeners need to be added or removed based on conditions. ```python import hikari bot = hikari.GatewayBot(token="...") async def on_reaction(event: hikari.GuildReactionAddEvent) -> None: print(f"User {event.user_id} reacted with {event.emoji_name} on {event.message_id}") # Register listener at runtime bot.subscribe(hikari.GuildReactionAddEvent, on_reaction) # Later — unregister it bot.unsubscribe(hikari.GuildReactionAddEvent, on_reaction) ``` -------------------------------- ### Listen for MessageCreateEvent without Type Hint Source: https://github.com/hikari-py/hikari/blob/master/README.md Register an asynchronous function to listen for specific events by passing the event type directly to the @bot.listen() decorator. ```python # or @bot.listen(hikari.MessageCreateEvent) async def ping(event): ... ``` -------------------------------- ### Accessing Cached Entities with Hikari Source: https://context7.com/hikari-py/hikari/llms.txt Demonstrates how to use `bot.cache` for synchronous, zero-cost entity lookups. Methods return `None` if the entity is not cached, falling back to REST API calls when necessary. ```python import hikari bot = hikari.GatewayBot(token="...") @bot.listen() async def on_message(event: hikari.GuildMessageCreateEvent) -> None: if not event.is_human or event.content != "!member": return # Try cache first, fall back to REST member = bot.cache.get_member(event.guild_id, event.author.id) if member is None: member = await bot.rest.fetch_member(event.guild_id, event.author.id) # Cache lookups for other entities guild = bot.cache.get_guild(event.guild_id) channel = bot.cache.get_guild_channel(event.channel_id) all_guilds = bot.cache.get_guilds_view() # All cached guilds nickname = member.nickname or member.user.username roles = [ bot.cache.get_role(role_id) for role_id in member.role_ids if bot.cache.get_role(role_id) is not None ] role_names = [r.name for r in roles] await event.message.respond( f"**{nickname}** — Roles: {', '.join(role_names) or 'None'}\n" f"Joined: {member.joined_at.strftime('%Y-%m-%d') if member.joined_at else 'Unknown'}" ) bot.run() ``` -------------------------------- ### Update Bot Presence (`update_presence`) Source: https://context7.com/hikari-py/hikari/llms.txt Updates the bot's status (e.g., Online, Idle) and activity (e.g., Playing, Watching) across all active shards. Any parameter can be set to `hikari.UNDEFINED` to leave that specific field unchanged. ```APIDOC ## `hikari.GatewayBot.update_presence` — Update Bot Presence `bot.update_presence()` updates the bot's status and activity across all running shards. Accepts `hikari.UNDEFINED` for any parameter to leave that field unchanged. ```python import hikari bot = hikari.GatewayBot(token="...") @bot.listen() async def on_started(event: hikari.StartedEvent) -> None: guild_count = len(bot.cache.get_guilds_view()) await bot.update_presence( status=hikari.Status.ONLINE, activity=hikari.Activity( name=f"{guild_count} servers | !help", type=hikari.ActivityType.WATCHING, ), ) bot.run() ``` ``` -------------------------------- ### Interact with Discord REST API using GatewayBot Source: https://context7.com/hikari-py/hikari/llms.txt Use `bot.rest` to perform Discord API actions like kicking users, fetching user data, and creating channels. Handles rate limiting and retries automatically. Requires appropriate permissions for actions like kicking. ```python import hikari bot = hikari.GatewayBot(token="...") @bot.listen() async def on_message(event: hikari.GuildMessageCreateEvent) -> None: if not event.is_human: return if event.content == "!kick" and event.message.member: # Check permissions — requires KICK_MEMBERS try: await bot.rest.kick_user( event.guild_id, event.author.id, reason="Requested via !kick command", ) await event.message.respond("User kicked.") except hikari.ForbiddenError: await event.message.respond("I don't have permission to kick members.") except hikari.NotFoundError: await event.message.respond("User not found.") elif event.content == "!fetch_user": user = await bot.rest.fetch_user(event.author.id) await event.message.respond( f"Username: {user.username}\n" f"ID: {user.id}\n" f"Bot: {user.is_bot}\n" f"Avatar: {user.display_avatar_url}" ) elif event.content == "!create_channel": channel = await bot.rest.create_guild_text_channel( event.guild_id, "new-channel", topic="Created via bot command", reason="User requested new channel", ) await event.message.respond(f"Created {channel.mention}") bot.run() ``` -------------------------------- ### Subscribe to Events with @bot.listen() Source: https://context7.com/hikari-py/hikari/llms.txt The `@bot.listen()` decorator subscribes an async callback to Discord events. The event type can be inferred from the function's type annotation or specified explicitly. A single callback can listen to multiple event types. ```python import hikari bot = hikari.GatewayBot(token="...") # Type inferred from annotation @bot.listen() async def on_guild_join(event: hikari.GuildJoinEvent) -> None: print(f"Joined guild: {event.guild.name} ({event.guild_id})") # Type specified explicitly — no annotation needed @bot.listen(hikari.GuildLeaveEvent) async def on_guild_leave(event) -> None: print(f"Left guild: {event.guild_id}") ``` -------------------------------- ### Listen to Multiple Events with One Handler in Hikari Source: https://context7.com/hikari-py/hikari/llms.txt Use a single listener to handle multiple event types by passing them as arguments to the @bot.listen decorator. This is useful for consolidating logic that applies to several similar events. ```python import hikari @bot.listen(hikari.GuildMessageCreateEvent, hikari.DMMessageCreateEvent) async def on_any_message(event: hikari.MessageCreateEvent) -> None: if event.is_human and event.content: print(f"Message from {event.author.username}: {event.content}") bot.run() ``` -------------------------------- ### Stream Events for Composable Processing in Hikari Source: https://context7.com/hikari-py/hikari/llms.txt Utilize bot.stream() as an async context manager to collect and process events lazily. Streams can be filtered, mapped, and limited, enabling efficient event handling without blocking the event loop, ideal for collecting reactions or other event sequences. ```python import hikari bot = hikari.GatewayBot(token="...") @bot.listen() async def on_message(event: hikari.GuildMessageCreateEvent) -> None: if not event.is_human or event.content != "!poll": return msg = await event.message.respond("React with 👍 or 👎 — collecting votes for 15 seconds!") thumbs_up = 0 thumbs_down = 0 with bot.stream(hikari.GuildReactionAddEvent, timeout=15).filter( ("message_id", msg.id) ) as stream: async for reaction_event in stream: if reaction_event.emoji_name == "👍": thumbs_up += 1 elif reaction_event.emoji_name == "👎": thumbs_down += 1 await event.message.respond(f"Poll results: 👍 {thumbs_up} | 👎 {thumbs_down}") bot.run() ``` -------------------------------- ### hikari.RESTApp - Standalone REST Client Source: https://context7.com/hikari-py/hikari/llms.txt Provides a shared connection pool for making Discord REST API calls without a gateway. Suited for web dashboards, OAuth2 flows, and scripts needing on-demand data access. ```python import asyncio import hikari rest = hikari.RESTApp() async def send_notification(bot_token: str, channel_id: int, message: str) -> None: await rest.start() try: async with rest.acquire(bot_token, hikari.TokenType.BOT) as client: await client.create_message(channel_id, message) finally: await rest.close() async def fetch_user_info(bearer_token: str) -> hikari.OwnUser: await rest.start() try: async with rest.acquire(bearer_token, hikari.TokenType.BEARER) as client: return await client.fetch_my_user() finally: await rest.close() asyncio.run(send_notification(os.environ["BOT_TOKEN"], 123456789, "Hello from REST!")) ``` -------------------------------- ### Send images as attachments in Hikari Source: https://context7.com/hikari-py/hikari/llms.txt This snippet demonstrates how to send user avatars, guild icons, or emoji images as attachments to messages. It handles different input types and uses `Resource` interface objects directly. ```python import re import hikari bot = hikari.GatewayBot(token="...") @bot.listen() async def on_message(event: hikari.GuildMessageCreateEvent) -> None: if not event.is_human or not event.content or not event.content.startswith("!image"): return args = event.content.split() what = args[1] if len(args) > 1 else "" async with bot.rest.trigger_typing(event.channel_id): if user_match := re.match(r"<@!?(\d+)>", what): user_id = hikari.Snowflake(user_match.group(1)) user = bot.cache.get_user(user_id) or await bot.rest.fetch_user(user_id) await event.message.respond("User avatar", attachment=user.display_avatar_url) elif what.casefold() in {"guild", "server", "here"}: guild = event.get_guild() icon_url = guild.make_icon_url() if guild else None if icon_url: await event.message.respond("Guild icon", attachment=icon_url) else: await event.message.respond("No guild icon available.") elif what: emoji = hikari.Emoji.parse(what) await event.message.respond(emoji.name, attachment=emoji) else: await event.message.respond("Your avatar", attachment=event.author.display_avatar_url) bot.run() ``` -------------------------------- ### hikari.Intents - Gateway Event Control Source: https://context7.com/hikari-py/hikari/llms.txt Control which gateway events your bot receives using `hikari.Intents` bitfield flags. Privileged intents require explicit enablement in the Discord Developer Portal. ```python import hikari # Default: all unprivileged intents bot = hikari.GatewayBot(token="...", intents=hikari.Intents.ALL_UNPRIVILEGED) # Minimal bot: only guild structure and messages bot = hikari.GatewayBot( token="...", intents=( hikari.Intents.GUILDS | hikari.Intents.GUILD_MESSAGES | hikari.Intents.MESSAGE_CONTENT # Privileged — enable in dev portal | hikari.Intents.DM_MESSAGES ), ) # Everything including privileged intents bot = hikari.GatewayBot(token="...", intents=hikari.Intents.ALL) # Check if a specific intent is enabled if bot.intents & hikari.Intents.GUILD_MEMBERS: print("Member tracking is enabled") # Runtime intent inspection print(hikari.Intents.GUILD_MESSAGES in bot.intents) # True / False ``` -------------------------------- ### hikari.RESTApp — Standalone REST Client Source: https://context7.com/hikari-py/hikari/llms.txt `RESTApp` provides a shared connection pool for making Discord REST API calls without any gateway or interaction server. It is suited for web dashboards, OAuth2 flows, and scripts that only need to read or write Discord data on demand. ```APIDOC ## `hikari.RESTApp` — Standalone REST Client `RESTApp` provides a shared connection pool for making Discord REST API calls without any gateway or interaction server. It is suited for web dashboards, OAuth2 flows, and scripts that only need to read or write Discord data on demand. ```python import asyncio import hikari rest = hikari.RESTApp() async def send_notification(bot_token: str, channel_id: int, message: str) -> None: await rest.start() try: async with rest.acquire(bot_token, hikari.TokenType.BOT) as client: await client.create_message(channel_id, message) finally: await rest.close() async def fetch_user_info(bearer_token: str) -> hikari.OwnUser: await rest.start() try: async with rest.acquire(bearer_token, hikari.TokenType.BEARER) as client: return await client.fetch_my_user() finally: await rest.close() asyncio.run(send_notification(os.environ["BOT_TOKEN"], 123456789, "Hello from REST!")) ``` ``` -------------------------------- ### Handle Hikari HTTP Errors Source: https://context7.com/hikari-py/hikari/llms.txt This snippet demonstrates how to catch specific `hikari.HTTPResponseError` subclasses to gracefully handle various HTTP errors from Discord, such as permission issues, rate limits, or server errors. ```python import hikari bot = hikari.GatewayBot(token="...") @bot.listen() async def on_message(event: hikari.GuildMessageCreateEvent) -> None: if not event.is_human or event.content != "!dm": return try: dm_channel = await bot.rest.create_dm_channel(event.author.id) await bot.rest.create_message(dm_channel.id, "Hello from the bot!") await event.message.respond("DM sent!") except hikari.ForbiddenError: # 403 — missing permissions or user has DMs disabled await event.message.respond("I can't DM you — check your privacy settings.") except hikari.NotFoundError: # 404 — user or channel not found await event.message.respond("Could not find that user.") except hikari.RateLimitTooLongError as e: # Rate limit exceeds max_rate_limit setting await event.message.respond(f"Rate limited for {e.retry_after:.1f}s — please try later.") except hikari.InternalServerError: # 500 — Discord server error await event.message.respond("Discord is having issues. Try again later.") except hikari.BadRequestError as e: # 400 — malformed request await event.message.respond(f"Bad request: {e.message}") bot.run() ``` -------------------------------- ### Event Stream Iterator (`stream`) Source: https://context7.com/hikari-py/hikari/llms.txt Provides an async context manager that yields events matching specified criteria into a lazy stream. Streams can be further processed using `.filter()`, `.map()`, and `.limit()` for efficient, non-blocking event collection and manipulation. ```APIDOC ## `hikari.GatewayBot.stream` — Event Stream Iterator `bot.stream()` returns an async context manager that collects matching events into a lazy stream. Streams support `.filter()`, `.map()`, and `.limit()` for composable event processing without blocking the event loop. ```python import hikari bot = hikari.GatewayBot(token="...") @bot.listen() async def on_message(event: hikari.GuildMessageCreateEvent) -> None: if not event.is_human or event.content != "!poll": return msg = await event.message.respond("React with 👍 or 👎 — collecting votes for 15 seconds!") thumbs_up = 0 thumbs_down = 0 with bot.stream(hikari.GuildReactionAddEvent, timeout=15).filter( ("message_id", msg.id) ) as stream: async for reaction_event in stream: if reaction_event.emoji_name == "👍": thumbs_up += 1 elif reaction_event.emoji_name == "👎": thumbs_down += 1 await event.message.respond(f"Poll results: 👍 {thumbs_up} | 👎 {thumbs_down}") bot.run() ``` ```