Object Oriented Discord Bot in Python
• • ☕️☕️ 9 min readIntroduction
The purpose of this post is present an approach to creating and structuring a discord bot in Python using discord.py for larger projects. The intention behind this is to not repeat information that is available in popular tutorials. Instead, the goal is to dig into the details of subclassing components in discord.py and using Cogs to promote modular, scalable, and maintainable code. The examples presented in this post are going to be generic and high level, if you would like to see a real world example see my Broken Jukebox Discord Bot project.
Important notice: The examples in this post have the Discord token as a hard coded value, this is for demonstration purposes. Hard coding secrets is bad, set this value via a dynamically loaded configuration value / environment variable instead.
Background
Discord bots are a great way to enhance the functionality of a server and enrich user experience. There are many tutorials out there that provide a quick and dirty approach to creating a discord bot. For examples, tutorials such as Real Python’s How to Make a Discord Bot in Python and Digital Ocean’s Discord bot tutorial. These tutorials provide a great foundation but skip over more structured and scalable approaches. This is likely because it would have been confusing and counterintuitive to getting up and running as fast as possible. I’ve written this post to dig into some of the missing details after investigating it myself.
Basic Client / Bot Example
In discord.py a Client represents a client connection to Discord. This object can be used to interact with the Discord API / WebSocket. The following code snippet is taken from discord.py Quickstart demonstrates how to create a basic client:
import discord
client = discord.Client()
@client.event
async def on_ready():
print(f'{client.user} bot user is ready to rumble!')
@client.event
async def on_message(message):
if client.user == message.author:
return
if message.content == "hello":
await message.channel.send('hello right back at you!')
client.run('your secret discord token')
As the functionality of a discord bot grows so will the complexity of the code. Logically, at some point it will make sense to extend the client object by subclassing it as seen below:
import discord
class CustomClient(discord.Client):
async def on_ready(self):
print(f'{self.user} bot user is ready to rumble!')
async def on_message(self, message):
if self.user == message.author:
return
if message.content == "hello":
await message.channel.send('hello right back at you!')
client = CustomClient()
client.run('my token goes here')
This same concept can be applied to the Bot Client, a Bot Client is an extended Client object that is exposed by discord.py. Bot clients are particularly useful when handling user input / commands as there are many utility functions built into the Bot Client ontop of the regular Client. The code below demonstrates how to subclass the Bot client like we did above with a regular client.
from discord.ext import commands
class CustomBotClient(commands.Bot):
async def on_ready(self):
print(f'{self.user.name} bot user is ready to rumble!')
client = CustomBotClient()
client.run('my token goes here')
Creating a subclass by itself in this context has no real benefits from a scalability / maintainability perspective. Everything is still in one file and over time it will become bigger and bigger. Instead, we can then break the CustomBotClient
class into a separate file, for demonstration purposes I will use the following file structure:
.
├── clients
│ └── custom_bot_client.py
└── run.py
The custom_bot_client.py
contains just the code relating to the custom client. The contents can be seen below:
from discord.ext import commands
class CustomBotClient(commands.Bot):
async def on_ready(self):
print(f'{self.user.name} has connected to Discord!')
We can then create an entrypoint to the application, I have called this file run.py
. The run.py
file can then reference custom_botclient.py
, the contents of run.py
can be seen below:
#!/usr/bin/env python3
from clients.custom_bot_client import CustomBotClient
def main():
token = "your token"
bot = CustomBotClient(
command_prefix='