Hikari
A Discord API wrapper for Python and asyncio built on good intentions.
Install / Use
/learn @hikari-py/HikariREADME
An opinionated, static typed Discord microframework for Python3 and asyncio that supports Discord's v10 REST and Gateway APIs.
Built on good intentions and the hope that it will be extendable and reusable, rather than an obstacle for future development.
Python 3.10, 3.11, 3.12, 3.13, and 3.14 are currently supported.
Installation
Install hikari from PyPI with the following command:
python -m pip install -U hikari
# Windows users may need to run this instead...
py -3 -m pip install -U hikari
Large bots
Hikari is used in the wild to run some large bots! Here are some examples:
<table> <tbody> <tr> <td align="center" valign="top" width="14.28%"><a href="https://invite-tracker.com"><img src="https://invite-tracker.com/og/invitetracker_logo.png" width="100px;" height="100px;" alt="Invite Tracker"/><br /><sub><b>Invite Tracker</b></sub></a> <td align="center" valign="top" width="14.28%"><a href="https://nmarkov.xyz"><img src="https://nmarkov.xyz/logo.png" width="100px;" alt="nMarkov"/><br /><sub><b>nMarkov</b></sub></a> </tr> </tbody> </table>If you want to add your large bot to the list, please contact @davfsa on our Discord server or submit a pull request to add it to the list!
Getting started
Hikari provides two different default bot implementations to suit your needs:
GatewayBot
A GatewayBot
is one which will connect to Discord through the gateway and receive
events through there. A simple startup example could be the following:
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()
This will only respond to messages created in guilds. You can use DMMessageCreateEvent instead to only listen on
DMs, or MessageCreateEvent to listen to both DMs and guild-based messages. A full list of events
can be found in the events docs.
If you wish to customize the intents being used in order to change which events your bot is notified about, then you
can pass the intents kwarg to the GatewayBot constructor:
import hikari
# the default is to enable all unprivileged intents (all events that do not target the
# presence, activity of a specific member nor message content).
bot = hikari.GatewayBot(intents=hikari.Intents.ALL, token="...")
The above example would enable all intents, thus enabling events relating to member presences to be received (you'd need to whitelist your application first to be able to start the bot if you do this).
Events are determined by the type annotation on the event parameter, or alternatively as a type passed to the
@bot.listen() decorator, if you do not want to use type hints.
import hikari
bot = hikari.GatewayBot("...")
@bot.listen()
async def ping(event: hikari.MessageCreateEvent):
...
# or
@bot.listen(hikari.MessageCreateEvent)
async def ping(event):
...
RESTBot
A RESTBot
spawns an interaction server to which Discord will only send interaction events,
which can be handled and responded to.
An example of a simple RESTBot could be the following:
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()
Unlike GatewayBot, registering listeners is done through .set_listener, and it takes in an interaction type
that the handler will take in.
Note that a bit of a setup is required to get the above code to work. You will need to host the project to the World Wide Web (scary!) and then register the URL on the Discord application portal for your application under "Interactions Endpoint URL".
A quick way you can get your bot onto the internet and reachable by Discord (for development environment only) is through a tool like ngrok or localhost.run. More information on how to use them can be found in their respective websites.
Common helpful features
Both implementations take in helpful arguments such as customizing timeouts for requests and enabling a proxy, which are passed directly into the bot during initialization.
Also note that you could pass extra options to bot.run during development, for example:
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.
)
Many other helpful options exist for you to take advantage of if you wish. Links to the respective docs can be seen below:
REST-only applications
You may only want to integrate with the REST API, for example if writing a web dashboard.
This is relatively simple to do:
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"))
Additional resources
You may wish to use a command framework on top of hikari so that you can start writing a bot quickly without implementing your own command handler.
Hikari does not include a command framework by default, so you will want to pick a third party library to do it:
lightbulb- a simple and easy to use command framework for hikari.tanjun- a flexible command framework designed to extend hikari.crescent- a command handler for hikari that keeps your project neat and tidy.arc- a bot framework with a focus on type-safety and correctness.
There are also third party libraries to help you manage components:
- [
yuyo](https://github.com
