Botkit is a comprehensive framework designed for developing feature-rich, production-ready Discord bots. It goes beyond basic bot creation by offering a suite of advanced tools and integrations that empower developers to build sophisticated, scalable, and maintainable bots.
Key features include:
- Robust Internationalization (i18n): Built-in support for multi-language bots, allowing seamless localization across diverse Discord communities.
- Bot Listing Integration: Streamlined processes for managing your bot's presence on popular bot listing websites, enhancing discoverability.
- Advanced Error Handling: Sentry-compatible error tracking, enabling real-time monitoring and debugging of your bot in production environments. (Sentry is an application monitoring platform that helps developers identify and fix crashes in real time.)
- Uptime Status Posting: Automated systems for reporting your bot's operational status, crucial for maintaining user trust and meeting service level agreements.
- Modular Architecture: A flexible, extension-based structure that facilitates easy feature addition and management.
While Botkit provides a rich set of advanced features, it maintains a balance between functionality and efficiency, offering a powerful yet not overly cumbersome development experience.
Botkit is not a pre-built, out-of-the-box Discord bot solution. It's a sophisticated framework and starting point for developers looking to create advanced, custom Discord bots. Botkit is designed for those who need more than basic functionality and are ready to leverage its powerful features to create truly unique and capable bots.
- Modular Design: The bot is designed with a modular architecture, allowing you to easily enable or disable specific extensions or features.
- Extensible: The bot is built around the extensions located in the
src/extensions
directory. There you can find useful and example extensions to get you started. - Configurable: The bot's configuration, including enabled extensions and bot token,
is managed through a
config.yml
file. - Easy Setup: Botkit simplifies the setup process by providing a well-structured project template and configuration management.
- Integrated Backend: Botkit provides an easy way of having a Quart (flask-like) webserver running alongside your bot, with the ability to add routes and endpoints.
- Useful Scripts: Botkit includes useful scripts for managing your bot's listing on top.gg and other bot lists, such as discord's app directory.
- pdm - A modern Python packaging and dependency management tool.
- Python 3.11
- A Discord bot. You can create a new bot and get a token from the Discord Developer Portal.
- Clone the repository and navigate to the project directory.
- Install the required dependencies using
pdm
:
pdm install
When creating your own features, you’re supposed to create a new extension in the
src/extensions
directory for each feature you want to add. To get started, you can
follow the guide available here.
Every extension in the src/extensions
directory will automatically be loaded, and if
its default config is set to enabled: true
, it will be enabled by default. This allows
you to add an extension that you found online simply by adding the file to the
src/extensions
directory. The settings are automatically added to the config.yml
file if you didn't provide them, after running the bot.
You can set up the config.yml
file with your bot token and desired extensions. There,
or trough environment variables, you can enable or disable extensions, set up the bot
token, and configure other options. You’re required to at least provide the bot token.
Here's an example configuration:
extensions:
listings:
enabled: false
topgg_token: "your_top.gg_token"
ping:
enabled: true
bot:
token: "your_bot_token"
logging:
level: INFO
Alternatively, you can set the bot token and other configuration options using
environment variables. You can set any variable as you would in the config.yml
file,
but with the BOTKIT__
prefix, and __
to separate nested keys. To set lists, use
regular json syntax.
BOTKIT__bot__token=your_bot_token
BOTKIT__extensions__listings__enabled=false
BOTKIT__extensions__listings__topgg_token=your_top.gg_token
BOTKIT__extensions__ping__enabled=true
BOTKIT__logging__level=INFO
BOTKIT__cache__type=redis
BOTKIT__cache__redis__host=redis.example.com
BOTKIT__cache__redis__port=6379
Botkit supports two types of caching:
- Memory Cache: Simple in-memory cache (default)
- Redis Cache: Distributed cache using Redis
To configure the cache, use the cache
section under the bot
in your config:
bot:
cache:
type: "redis" # Use "memory" for in-memory cache
redis: # Redis configuration (only needed when type is "redis")
host: "localhost"
port: 6379
db: 0
password: "optional" # Optional Redis password
ssl: false # Whether to use SSL
If Redis cache is requested but no configuration is provided, Botkit will fall back to memory cache with a warning.
Extensions are in truth just python located in the src/extensions
directory. When
creating an extension, it is crucial to follow the following guidelines:
- You should keep only one feature per extension.
- Creating multiple files and submodules is allowed.
- Use the provided
logger
(from src.logging import logger
) for logging messages.- You have five log levels available:
DEBUG
,INFO
,WARNING
,ERROR
, andCRITICAL
, use them accordingly.
- You have five log levels available:
Moreover, each extension is required to export different objects and functions to work properly. These are:
-
setup
: A function that sets up the extension. It CAN accept any of the following arguments, in the order you prefer, and you can safely omit any of them if you don't need them:bot
: The Discord bot instance.config
: The configuration dictionary for the extension. All config keys will always be lowercased for compatibility with environment variables.
-
setup_webserver
: A function for adding webserver routes. It CAN accept any of the following arguments, in the order you prefer, and you can safely omit any of them if you don't need them:app
: The Quart app instance.bot
: The Discord bot instance.config
: The configuration dictionary for the extension.
[!NOTE] Either
setup
orsetup_webserver
is required for the extension to work properly. You can also provide both.
-
on_startup
(optional): An asynchronous function that is called when the bot starts. It CAN accept any of the following arguments, in the order you prefer, and you can safely omit any of them if you don't need them:app
: The Quart app instance.bot
: The Discord bot instance.⚠️ The bot is not yet logged in, so you won't be able to send messages or interact with the Discord API.config
: The configuration dictionary for the extension.
-
default
: A dictionary containing the default configuration for the extension. This is used to populate theconfig.yml
file with the default values if they aren’t already present. It is required to have AT MINIMAL theenabled
key set toFalse
orTrue
(you generally want to preferTrue
for a more intuitive experience to new users, but it is not required, especially if you code just for yourself). -
schema
: A dictionary (or aschema.Schema
, if you want more granular control) containing the schema for the extension's configuration. This is used to validate the configuration in theconfig.yml
file. The schema should be a dictionary where the keys are the configuration keys, and the values are the types of the values. For example:
schema = {
"enabled": bool,
"token": str,
"prefix": str,
"channel": int,
"role": int,
"users": list,
"options": dict,
}
We really encourage you to follow these instructions, even if you’re coding privately, as it will make your code more readable and maintainable in the long run.
Botkit provides robust support for internationalization, allowing you to create multi-language Discord bots with ease. Here's how to implement and use translations in your extensions:
Each extension can have its own translations.yml
file located at
src/extensions/EXT_NAME/translations.yml
. This file follows a specific structure:
commands:
command_name:
name:
en-US: Command Name
fr: Nom de la Commande
# ... other languages
description:
en-US: Command description
fr: Description de la commande
# ... other languages
options:
option_name:
name:
en-US: Option Name
fr: Nom de l'Option
# ... other languages
description:
en-US: Option description
fr: Description de l'option
# ... other languages
strings:
response_key:
en-US: Response text in English
fr: Texte de réponse en français
# ... other languages
strings:
string1:
en-US: General string in English
fr: Chaîne générale en français
# ... other general strings
[!NOTE] The top-level
strings
section (outside ofcommands
) is what gets mapped toconfig["translations"]
. This section is for general strings not directly tied to specific commands.
For command groups and sub-commands, you can nest the structure using the commands
key:
commands:
parent_command:
name:
en-US: Parent Command
# ... other languages
description:
en-US: Parent command description
# ... other languages
commands:
sub_command:
name:
en-US: Sub Command
# ... other languages
description:
en-US: Sub-command description
# ... other languages
# ... options, strings, etc.
This structure can be nested further for sub-sub-commands if needed.
-
For slash commands, options, and their descriptions, Botkit automatically applies the correct translations based on the guild's preferred locale.
-
For other strings, you can access translations using the
apply_locale
function:
from src.i18n import apply_locale
# In your command or event handler:
translations = apply_locale(config["translations"], message.guild.preferred_locale)
response = translations.some_key.response_text
# You can also specify a default locale:
translations = apply_locale(config["translations"], message.guild.preferred_locale, default="en-US")
- In slash commands, translations are available via the
ctx.translations
object:
from src import custom
@discord.slash_command(name="ping")
async def ping(self, ctx: custom.ApplicationContext):
response = ctx.translations.response.format(latency=round(self.bot.latency * 1000))
await ctx.respond(response)
[!NOTE] The translations available under
ctx.translations
are the ones set understrings
in the command's translation.
- Provide translations for all supported languages in your
translations.yml
file. - Use meaningful keys for your strings to make the code more readable.
- Consider using placeholders (e.g.,
{latency}
) in your translated strings for dynamic content. - Always provide at least an English (en-US) translation as a fallback.
By following these guidelines, you can create a bot that seamlessly adapts to different languages, providing a localized experience for users across various Discord servers.
Botkit supports the use of patch files to modify or extend the functionality of the bot or its dependencies before the main extension code runs. This is particularly useful for applying global changes or monkey-patching existing classes.
- Create a file named
patch.py
in your extension's directory. - Define a
patch()
function in this file. This function will be called before the extension is loaded. - The
patch()
function can modify global state, patch classes, or perform any other setup needed.
Here's an example from the nice-errors
extension that demonstrates how to use a patch
file to enhance error handling:
# nice-errors/patch.py
import discord
from discord import Interaction
from discord.ui import Item
from typing_extensions import override
def patch():
class PatchedView(discord.ui.View):
@override
async def on_error(
self,
error: Exception,
item: Item, # pyright: ignore[reportMissingTypeArgument,reportUnknownParameterType]
interaction: Interaction,
) -> None:
await handle_error(error, interaction, use_sentry_sdk=bool(sentry_sdk))
discord.ui.View = PatchedView
This patch modifies the discord.ui.View
class to provide more user-friendly error
messages. It catches exceptions and responds to the user with an appropriate message,
enhancing the overall user experience.
Patch files are powerful but should be used judiciously. They are best suited for:
- Applying global changes that affect multiple parts of your bot.
- Modifying third-party libraries when you can't or don't want to fork them.
- Implementing cross-cutting concerns like logging or error handling.
Remember that patches are applied early in the bot's lifecycle, so they can affect all subsequent code. Use them carefully and document their effects clearly.
This script checks the publishing status of your bot on various bot listing websites, as well as if its description is up-to-date with one provided. To use it:
- Have Google Chrome installed. This is required web scraping.
- Install the development dependencies using
pdm install -d
. - Create a file called
description.md
in the root directory of the project, containing your bot's description. - Create a file called
listings.yml
in the root directory of the project, containing your bot's application id and url for some listing websites, if you want to check them. Here's an example:
application_id: 1234567891011121314
DiscordBotListCom: # add this section if you want to check discordbotlist.com
url: https://discordbotlist.com/bots/my-bot
DisforgeCom: # add this section if you want to check disforge.com
url: https://disforge.com/bot/1234-my-bot
DiscordMe: # add this section if you want to check discord.me
url: https://discord.me/my-bot`
- Run the script using
pdm run check-listings
.
We provide multiple extensions directly within this project to get you started. These are:
ping
: A simple ping command and an http endpoint to test whether the bot is online.listings
: An extension to post server count to various bot listing websites.branding
: An extension to customize the bot's presence and status, and embed aesthetics.add-dm
: An extension to send a direct message to the user who adds the bot to a guild.nice-errors
: An extension to provide user-friendly error messages during command execution.status-post
: An extension to post the bot's status to a specified URL.
Read the provided documentation for each extension to learn more about their features and how to configure them.
We welcome contributions to this project! Please follow the gitmoji.dev convention for commit messages and submit pull requests with descriptive commit messages.
This project follows the PEP 8 style guide
for Python code. We recommend using a linter like black
to ensure your code adheres to the style guidelines. We provide a command to lint the
code using pdm run lint
. For this to work you have to install the development
dependencies using pdm install -d
if you haven't already.
Botkit is designed to be deployed on various platforms, including cloud services like
Heroku or DigitalOcean.
Refer to the documentation of your preferred hosting platform for deployment
instructions. A Dockerfile
is included in the project for containerized deployments,
as well as a GitHub action including tests and linting. You can export the requirements
to a requirements.txt
file using pdm run export
, run the tests using pdm run tests
and lint the code using pdm run lint
. In order for the GitHub tests to pass, you need
to make sure you linted your code, that the tests pass and that you exported the
requirements if you made changes to the dependencies.
If you encounter any issues or have questions about Botkit, feel free to open an issue on the GitHub repository. You can also join the official Discord server for support and discussions.
This project is licensed under the MIT License.