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/extensionsdirectory. 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.ymlfile. - 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.12
- 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: INFOAlternatively, 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=6379When deploying in containerized or high-availability environments where persistent volumes might not be available or when using orchestration platforms like Docker Swarm, it's often preferable to use environment variables instead of configuration files. Botkit provides a convenient script to convert between YAML and environment variable formats:
pdm run convert-config -i config.yml --terminalThis will output your YAML configuration as environment variables that you can then use in your container configuration or deployment platform. For more details about the convert-config script, see the Using scripts section.
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 SSLIf 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 or setup_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.ymlfile with the default values if they aren’t already present. It is required to have AT MINIMAL theenabledkey set toFalseorTrue(you generally want to preferTruefor 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.ymlfile. 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 stringsNote
The top-level strings section (outside of commands) is what gets mapped to
config["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_localefunction:
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.translationsobject:
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 under
strings in the command's translation.
- Provide translations for all supported languages in your
translations.ymlfile. - 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.pyin 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 = PatchedViewThis 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.mdin the root directory of the project, containing your bot's description. - Create a file called
listings.ymlin 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.
This script converts the configuration between YAML and env formats.
By default, if run with no arguments, it converts the config.yaml or config.yml
present in the root to .env format. If a .env file is present and is empty or one is
not present, it converts there. Otherwise, it asks the user if it should overwrite the
existing .env file. If not, it writes to a datetime.converted.env file.
-i,--input: Specify the input file path.--input-format: Specify the input format (yaml,yml,env).--output: Specify the output file path.--output-format: Specify the output format (yaml,yml,env).--terminal: Output to the terminal instead of a file.
Convert config.yaml to .env:
pdm run convert-configConvert a specific file and output to the terminal:
pdm run convert-config -i config.yml --terminalConvert .env to config.yaml:
pdm run convert-config -i .env --output config.yamlWe 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.
This project uses HashiCorp's Copywrite tool to manage copyright headers across all source files. Copywrite automatically ensures consistent copyright headers and license information across the codebase.
The project includes a .copywrite.hcl file that configures how copyright headers are
managed:
To check if any files are missing copyright headers:
copywrite headers --planTo automatically add missing copyright headers:
copywrite headersBotkit 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.