A beginners guide to using hikari.
Before this goes public
- replace all the images/examples with hikari specific ones
- review by hikari developers
Some prerequisites for writing a Discord bot in python.
Required:
- Primitive data types
- Operators
- Control flow
- Function definitions
- String formatting
- Variables, namespace and scope
- Importing
- Classes, objects, attributes and methods
- OOP
- Data structures
- Exception handling
- Console usage, interpreters and environments
- Decorators
Useful to know:
- Databases
asyncio
basics- What is "blocking?"
- Logging
- Reading documentation properly
You should be familiar with Discord's features and naming conventions when writing your bot.
A discord user.
A user in the context of a guild. Discord introduced server-specific customization for users recently (server profiles).
A container for content that a user or bot sends in a channel. This can be text, media, stickers, etc.
A message that only you can see. They can be easily dismissed.
A channel that members can send messages in.
A channel that members connect to and can transmit and receive audio and video.
A channel where a server (server owner or admin) can publish announcements and other servers can "follow" that channel.
An ordered collection of channels in a drop down.
A server. More specifically, a collection of channels that members of the guild can interact with.
Buttons and select menus.
Commands registered with Discord that appear when you type /
in a text
channel.
💡 Slash commands are discord's attempt at a better user experience when
interacting bots. Previously, each bot had it's own prefix, and needed to read
each incoming message to check if it started with the prefix. Remembering the
prefix, viewing all the available commands, and validating user input was not a
simple task, and you better hope that the bot developer lets you change the
prefix so multiple bots don't respond to ?help
. Slash commands solve this by
requiring your bot to register the commands with discord.
Commands regisered with Discord that appear under the "apps" context menu of messages and users.
A bot specific content type that looks better than plain text.
A text channel within a text channel
A modal that a user can input content into. Currently, they only accept text
Hikari is a wrapper for the Discord API, meaning that it abstracts some of the API usage by providing convince classes and methods, and an includes an event manager to handle incoming events. Before we cover what separates Hikari from other libraries, let's cover the basics of the Discord API.
The Discord API is a collection of three smaller APIs: the REST API, the API Gatway, and interaction webhooks.
The REST API is for performing CRUD (create, read, update, delete) operations. For example, you could create a role or update a channel.
The API Gateway is for sending and receiving events.
An event is a notification that something happened. For example, when a channel is updated, your bot will receive a channel update event. Full list of events.
The Gateway is also used for receiving and responding to interactions.
An interaction is a notification that a user interacted with your bot through a slash command or message component. [TODO link]
The difference between events and interactions is that interactions are specific to bots. Interactions are created when a user interacts with a bot, but events are created when a user interacts with Discord in general.
Your bot also sends events to connect to the Gateway, but hikari handles that for us.
Interaction webhooks are for receiving responding to interactions without a bot.
However, a bot is required for interacting with the API Gateway and REST API, so we won't be covering interaction webhooks in this guide.
Hikari has a few amazing features that discord.py and it's forks don't have.
Python classes have quite a bit of runtime overhead regarding memory because
they have a lot of extra methods and attributes that are required for python to
function. Even when adding __slots__
, not using a class
results in a ~90% memory reduction.
Hikari allows you to skip creating intermediary objects. For example, if you wanted to edit a channel, discord.py code might look like
guild = ctx.get_guild()
channel = guild.get_channel(123)
await channel.edit(name="new name")
while the hikari code looks like
await ctx.bot.rest.edit_channel(123, name="new name")
Hikari's cache stores objects so you don't have to fetch them from the REST API. Unlike other caches, hikari's cache always up to date. On startup, it pre-populates the cache in a process called chunking, and on every event your bot receives, it will update accordingly. For example, when a user changes their nickname in a server, your bot will receive a member update event, and hikari will update it's cache to reflect the new changes.
Hikari's cache can be enabled and disabled on an object-by-object basis. For example, you can remove the presence cache because it takes up a lot of memory and gets updated frequently.
Methods that get items from the cache start with a get_
and return either a
CacheView
(which can be treated like a dict
) or T | None
. Because you can
disable parts or all of cache, you are not guarenteed to get anything, so a
common pattern is to use the fetch_
alternative to fetch the resource from the
REST API in a short circiut or using the :=
operator.
member = (
bot.cache.get_member(123) or
await bot.rest.fetch_member(123)
)
Note: fetch_member
will run only if bot.cache.get_member(123)
returns
None
.
if (member := bot.cache.get_member(123)) is not None:
# Do something with the member
await ctx.respond(member.mention)
Hikari is statically typed which makes it easy to work with. It's type system is
very powerful, following similar patterns to traits in Rust's. For example, if
you want to send a message in a channel, that channel class needs to subclass
TextableChannel
which has the send
method. This is very powerful in
isinstance
checks because we don't care what type of channel it is, we just
need to know we can send messages in it.
channel = (
bot.cache.get_guild_channel(123)
or await bot.rest.fetch_channel(123)
)
if isinstance(channel, hikari.TextableChannel):
await channel.send("Hello")
Read more about Rust's traits in the Rust Book.
Hikari has it's own undefined type to explicitly denote an omitted value. We
need a way to signify an empty value to Discord and using None
might bring
unexpected behavior to the developer.
Hikair exposes a lower level API, compared to discord.py, where you can manually create, update, and defer interaction responses. Reasons why you need this fine grained control are: TODO
Because Hikari doesn't come with a built-in command handler (like
discord.ext.commands
), because hikari was developed to be
extendable and reusable, rather than an obstacle for future development.
You either need use another library or write one yourself.
Command handlers help with creating and registering commands with Discord and creating event listeners to handle incoming events from the Gateway.
Command handlers can also help with code organization by grouping commands and event listeners into plugins. A plugin is just a collection of commands and event listeners.
There are currenly three command handlers that each have their own pros and cons, so check them all out before sticking to one.
Some other useful libraries to be aware of
- Miru: A component handler. This is a must have if you are working with components (e.g. buttons).
- Uvloop: A faster version of the asyncio event loop (only available on linux)
Example slash commands with lightbulb https://gist.github.com/AlexanderHOtt/7805843a7120f755938a3b75d680d2e7
TOOD. In the mean time, join the discord and check out the #clusters channel
- intents
- ratelimiting
- sharding