Brief History of discord.py
A few months ago discord.py1 was the king of Discord Bots when it came to Python. Google "build a discord bot python" and chances are the tutorial would be about Dpy. Recently though Rapptz, the owner of Dpy, announced that he was no longer interested in maintaining the library. Unfortunately, no one he trusted was willing to take the reins of the project.
It's a sad event, we had personally build a bot with Dpy and have been extending it for a few years. We initially started it because popular bots limited reaction roles to 20 roles. We also run a 24-32K discord server and needed much more than that. So our idea, build our own. We have spent roughly 4 years learning Dpy, I would not say we're a master or expert by any stretch, but we've done a lot with it. Over the years more and more features were added. A full moderation suite for warns, strikes, kicks and bans that were tracked through postgres, a full xp and level system with customizable goals and values, a customizable escaperoom game, even a small Kanban planning feature and so much more.
discord.py's closing hit us pretty close to home because of this. We spent many hours making this Bot by ourselves and helping teach some kids programming with it. Rapptz detailed his reasoning very well2, it's difficult to maintain a large open source project. Couple that stress with the gentle reminder that discord.py is not Rapptz day job and he made no money off of it. Discords development process is fast, chaotic and not always well communicated. It does not sound like an incredibly fun experience honestly. So while we are sad to see discord.py go we can only wish Rapptz and the discord.py team the absolute best. They did a wonderful job providing a library for many years.
Time moves on, even if discord.py doesn't. Discord mandated that Slash commands are a required. Rapptz last update sounds pretty steadfast that no further development will be happening, so we need to find an alternative. We briefly considered using some discord.py extensions. There are certainly a few that say they can provide the slash command functionality. We watched in anticipation for roughly a month to see the what would happen. A fork of called pycord did show up with promises to add new features and support. Our concern is the developers who have started this fork appear to be a bit inexperienced. We are thrilled new developers are getting into library support. Especially when it is needed and no one else is willing to! Many of the features in our bot ended up becoming fairly complex. The XP system for example mirrors data between Redis and Postgres and manages every user in a 25K guild. Stability is a major concern for us.
We would like to see continued active development and a good community to seek help from.
We spent a few weeks looking around and considering our options. The library we found and liked the most is Hikari3 and Tanjun4. Hikari is the main library, but unlike discord.py, hikari does not provide any build in "bot" functionality. If you're used to discord.pys cogs, Hikari does not provide this. Hikari does have an active community library that fills these gaps. The two biggest are Lightbulb5 and the previously mentioned Tanjun4. Lightbulb will provide the familiar "plugin" style of OOP classes that most discord.py programmers will be familiar with. Tanjun takes a more functional approach. Instead of classes and inheritance Tanjun provides a number of decorators. Neither Tanjun or Lightbulb are "better" or "more powerful" than the other, it's more a choice of how you prefer to implement your bot. If you are new to programming, I would recommend Lightbulb5.
So what's happening here?
After reviewing Hikari and Tanjun we decided to convert our bot! There's not nearly as many resources for Hikari or Tanjun as there was for discord.py, so we want to help fix that! We plan on writing blog posts here, then streaming the programming on our Twitch6 (you should follow us if you haven't!) and finally posting all the code to our Gitlab repo7 for this project! This post we wanted to get some of the quick meta information out of the way and provide a very basic overview of hikari.
Building the Bot with Hikari
Token and Developer Account
Just like discord.py (or most bots really), you need to get a token first. We're not going to review how to do this because we feel it's been covered to death, but discord.js provides an excellent walkthrough8. Just follow this tutorial through and copy down the Bot Token you create. The tl;dr for Bot Tokens:
- Tokens allow your code to connect to and communicate with Discord.
- No Token == Bot won't work.
- Tokens are unique.
- Don't share yours, other people can run your Bot account if you do.
- Tokens can be regenerated at any time! If you do leak yours, you can reset it!
- Don't store it in GIT. We recommend using environment variables. Later we'll provide a setup too!
Developer Server/Guild
You will also need a Discord Guild to test your bot. You're probably used to calling these "servers", but discord's official documentation refers to them as "guilds". You can make a new Server by clicking the "+" button in your servers list. This will walk you through setting up your own. For now you won't need to worry about any of the customization options like a logo or roles. We just need an empty server.
Now we will need to invite your bot! Jump back to the Discord Developers
Panel9, open your bot application, and go to the OAuth2
tab.
To the code
The first thing we will need to do is install hikari itself. We recommend using
PyEnv10 and it's Virtualenv Plugin11 to help manage your dependencies.
Tools like pip, poetry, or pipenv allow you to manage libraries as dependencies.
PyEnv manages installing different versions of Python. The virtualenv plugin
adds a wrapper for virtualenv/builtin pyenv to create self contained Python
projects. While we recommend using these two tools, they are not a hard
requirement. All you really need is Python 3.9.5 and pip
.
Once you you have the right Python, you will still need to install Hikari. You can do this with the following command:
1 |
|
There is also a "speedups" version of Hikari. This version requires some additional software, notably a C Compiler which is not preinstalled on Windows. You can install the speedups version with the command below, just make sure you have gcc or g++ installed.
1 |
|
Next we will create a bot.py
with the following content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Let's go through this script line-by-line.
Lines 1-2 import both the hikari
and os
modules.
Line 4 creates a GatewayBot
12 using the Token you created. The
GatewayBot
is hikari's way to provide basic bot functionality. It does not
provide any way to register commands, set things like prefixes, or join music
channels. It does provide a way to subscribe to events, create listeners,
interactive with users and access the Discord Rest API. If you want to work with
more abstract concepts like commands or groups you need Tanjun or Lightbulb,
which we will be covering next post!
Line 6 might look familiar if you've used discord.py. This does basically the
same thing. We are using our previously created GatewayBot
to define the next
function is a listener
. Listeners allow us to assign a callback function to
events. But unlike discord.py, we
are not passing any information to tell it what "type" of event
it should respond to. Should it reply to Messages? A member joining? A member
being banned? Hikari actually handles this through it's type hinting system on
the next line.
Line 7 defines our new function, ping, and what arguments it should receive. In
our case we are saying the function should receive one argument, event
of the
type GuildMessageCreateEvent
. Defining the function this way will also lock
the function to only receive these types of events. Hikari has a large list
of events12 that you can use in place of GuildMessageCreateEvent
, just make
sure you understand what they return.
Lines 8-10 are just to ensure we do not respond to any bots or empty messages. Remember, this function run and receive an event for every single message from every guild it is in. That includes messages from other bots or messages that only have embeds with no content. We should filter that to not error.
Lines 11 checks if the message starts with !ping
, the command to respond to.
Line 12 uses respond
which is hikari's way of sending messages. send()
supports a number of options for attachments
, embeds
, mentions
and
reply
s. In this article we are just using the most simple version. Providing just
a string defaults to assuming you are sending "content", like so send(content="Pong!")
.
Finally like 14 starts the bot! Save your file and copy that Bot Token from
earlier to your clipboard, you're about to need it. Open your terminal and
navigate to where you saved your bot. Run the command below, with
your-token-here
replaced with your actual bot token.
1 |
|
Adding BOT_TOKEN=your-token
before the python
command is a quick way to set
a temporary EnvironmentVariable. Until your python script finishes executing,
you will be able to access that token via os.environ.get("BOT_TOKEN")
in any
Python file. This is how we are able to access the token without actually
placing it into the file. This also ensure you do not accidentally share your
key or save it to a git repo. There are better ways to do this, and we will
introduce one later, but for now this is fast and easy!
If you open the server you invited your bot to and run !ping
you should get a
response!
Tanjun and the Client
Imagine having to write every bot command as a listener. Yikes. By default
hikari
doesn't provide much more than this to handle our bot commands. That's
where Tanjun comes to the rescue. You can install Tanjun the same way we did
hikari:
1 |
|
With Tanjun installed we will need to change our bot.py
some.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
In lines 3-11 we have completely deleted the previous listener
and moved our GatewayBot
into it's own function. This might look a little odd if you are used to OOP, but
this is the more functional approach Tanjun takes.
Lines 13-22 defines a new make_client
function. The function takes a GatewayBot
, builds and returns a new tanjun.Client
.
Line 18 defines the set_global_commands
which accepts a Discord Guild ID.
You can get the ID by right clicking your Servers Icon/Logo and selecting "
Copy ID". This isn't required immediately, but will be for Slash Commands.
Lastly notice line 20, we are finally adding a bot prefix! By default we set ours to "!", just an exclamation mark. You can change this if you'd like.
Now we will need a way to run the bot again. Let's create a run.py
:
1 2 3 4 5 6 7 8 9 10 11 |
|
Lines 3-8 are to enable hikari's Speedups. If you do not have a working C compiler, or don't want to bother, you can just remove those lines.
Line 11 calls our previously defined build_bot
, which as you remember returns
a GatewayBot
. This allows us to "chain" the functions. Instead of having to
set the GatewayBot
to it's own variable and call run()
off of that, we can
do this shorthand.
Making a Tanjun Plugin
So what's the bot do now? Nothing!
Let's work on fixing that!
Make a new folder called plugins
and add a new file to that folder
__init__.py
. That tells python that the folder should be a Python Module and
we will be able to import
from there. Create another new file in plugins
named utilities.py
and let's add some functionality:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Line 4 defines a new variable of tanjun.Component()
, this is the primary way
to add new "commands" to your bot.
Line 6 uses that newly defined component
and calls with_slash_command()
.
This registers the following function as a new Slash Command in your bot.
Line 7 registers the the name, description and ephemeral status of the command.
@tanjun.as_slash_command()
is how to add metadata to your commands. There are
a few more options as well14 that allow you to limit to specific choices and
more.
Line 8 starts our actual command. By default all commands must accept a
tanjun.abc.Context
in some form, but there are subclasses for Message and Slash
commands15.
Line 9 tells our bot to respond()
16 to the users message with their
mention
and id
.
Finally lines 12-14 are boilerplate. There is no unique code in it, so it can
literally be copy/pasted to any plugin. Just make sure you have at least one
function defined with @tanjun.as_loader
otherwise Tanjun will not be able to
load and run your plugin!
Register the Plugin on your bot
Now update your bot.make_client
function to load the new module:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Run your Slash Command!
Now if you go to your Server and type /whats-my-id
you should get a new popup!
This popup will list all Slash Commands available in the server. This dialogue will also give command options and arguments if the command requires those and even validate them! So if your command requires a Role or User you can lock the popup to only accept those as inputs!
NOTE: If you are not seeing your bot's Slash command show up, make sure your clients
set_global_commands
is correct! Discord bots have to register slash commands with Discord when they start. If your guild id is not correct it can take up to 1 hour to propagate if you do not set this!
If everything is setup right you should get a response like this! Notice the "Only you can see this - Dismiss message" prompt, this indicates that the message is "ephemeral". Only the user who caused the ephemeral message can see it. It's also important to note that unlike old message style commands, other users cannot see your Slash commands when sent. The only indication is the reply shown.
If you would prefer the message be viewable to everyone, simply set
default_to_ephemeral=False
in plugins.utilities
.
Footnotes & Sources
-
Rapptz, discord.py repository, https://github.com/Rapptz/discord.py ↩
-
Rapptz, The Future of discord.py, https://gist.github.com/Rapptz/4a2f62751b9600a31a0d3c78100287f1 ↩
-
Hikari-py, Hikari, https://github.com/hikari-py/hikari ↩
-
FasterSpeeding, hikari-tanjun repository, https://github.com/FasterSpeeding/Tanjun/ ↩↩
-
tandemdude, hikari-lighbulb repository, https://github.com/tandemdude/hikari-lightbulb ↩↩
-
Patchwork Collective, TwitchTV Channel, https://www.twitch.tv/patchworkcollective/ ↩
-
Patchwork Collective, hikari-tanjun-tutorial repository, https://gitlab.com/aster.codes/hikari-tanjun-tutorial ↩
-
discord.js, Setting up a Bot Application https://discordjs.guide/preparations/setting-up-a-bot-application.html#your-token ↩
-
Discord, Discord Developer Account, https://discord.com/developers/applications/889565907200921621/oauth2 ↩
-
PyEnv, pyenv Repository, https://github.com/pyenv/pyenv ↩
-
PyEnv, pyenv-virtualenv Repository, https://github.com/pyenv/pyenv-virtualenv ↩
-
Hikari-py, GatewayBot documentation, https://hikari-py.github.io/hikari/hikari/impl/bot.html#hikari.impl.bot.GatewayBot ↩↩
-
Hikari-py, Hikari Events documentation, https://hikari-py.github.io/hikari/hikari/events/index.html ↩
-
Tanjun, Tanjun as_slash_command documentation, https://fasterspeeding.github.io/Tanjun/master/tanjun.html#as_slash_command ↩
-
Tanjun, Tanjun Context Documentation, https://fasterspeeding.github.io/Tanjun/master/tanjun/abc.html#Context ↩
-
Tanjun, Tanjun Context.respond Documentation, https://fasterspeeding.github.io/Tanjun/master/tanjun/abc.html#Context.respond ↩