Customize Python Discord bot
Tags: python, discord, bot, extensions
Published: 2022-04-03 21:08:14

[Cogs](#cogs)
[Loading Cogs](#loading-cogs)

## Some Event Listeners

Event listeners can be used in many different ways like keeping track of the servers that the bot gets added to.

To keep track of what server your bot has joined, the `on_guild_join()` method can be called via the `@client.event` decorator.

- `mkdir guilds.json`
- `import json` Python module

```Python
import json

...

@client.event
async def on_guild_join(guild):
    with open('guilds.json', 'r') as f:
        guilds = json.load(f)
    # Set default prefix variable and name of server
    guilds[str(guild.id)] = {
        "guild Name": guild.name,
        "Prefix": "$"
    }
    # Push dictionary to JSON file
    with open('guilds.json', 'w') as f:
        json.dump(guilds, f, indent=4)

...
```

Likewise for when the bot is removed from the server to clean up the JSON file with `on_guild_remove()`.

```Python
...

@client.event
async def on_guild_remove(guild):
    with open('guilds.json', 'r') as f:
        guilds = json.load(f)
    # Remove guild dictionary from JSON file
    guilds.pop(str(guild.id))
    # Apply changes to file
    with open('guilds.json', 'w') as f:
        json.dump(guilds, f, indent=4)

...
```

If you would like to create some interaction with the bot and have a "conversation" the `on_message()` method has some things it can do.

This can be customized to fit your needs, but here's a basic example.

```Python
...

@client.event
async def on_message(message):
    # Stop the bot from replying to itself or any other bot
    if message.author.bot: return

    # Send a reply to a human
    if message.content == "ping":
        await message.channel.send("pong")

...
```

## Cogs

Adding commands and event listeners to the main file will create unnecessary clutter. Using extensions will create a space to fill in more methods to be used in more concise ways. To do this, the `Cog` class will be required.

### Cog Decorator

To create a cog file, each will need to an instance of a new class.

Cog template

```Python
from nextcord.ext import commands

class <ClassName>(commands.Cog):
    # Initialize with `self` and `bot: commands.Bot`
    def __init__(self, bot: commands.Bot):
        self.bot = bot
    # All commands/listeners always take a parameter of "self"
    @commands.Cog.listener()
    async def <commandName>(self):

        # Do some stuff

def setup(bot: commands.Bot):
    bot.add_cog(<ClassName>(bot))
```

### Error Handler

Having a custom error handler will come in handy to provide client feedback about their issued command.

```Python
...

async def on_command_error(self, ctx: commands.Content, error: commands.CommandError):
    if isinstance(error, commands.CommandNotFound):
        message = f'{error}'
    elif isinstance(error, commands.UserInputError):
        message = 'Your input was incorrect'
    else:
        message = 'Something went wrong while running a command'

    await ctx.send(message, delete_after=5)
    await ctx.message.delete(delay=5)

...
```

The `isinstance()` method takes in two parameters and returns a boolean:

- an `object`
- `class or tuple`
- returns `bool` 

## Loading Cogs

After creating some cogs, go back into the main `bot.py` file and a `for loop` will be needed to load the cogs upon bot startup.

Along with this if not already done, you'll need to import the `os` module at the top of the file.

```Python
import os
...

# Load extensions on bot start
for filename in os.listdir('./cogs'):
    if filename.endswith('.py'):
        client.load_extension(f'cogs.{filename[:-3]}')

...
```

If now errors found within the extensions the bot will start up and the cogs will now be ready for use.