Skip to content

Custom Context Class⚓︎

There are many uses for context and its properties, however occasionally we wish to add some additional methods or attributes.

Here is how you do it in discord.py:

Creating custom context class⚓︎

  • Subclass context and add your features
import typing

from discord.ext import commands

class MyContext(commands.Context):
    def __init__(self, *args: typing.Any, **kwargs: typing.Any):
        super().__init__(*args, **kwargs)
        self.test = 123

    async def send_test(self):
        await self.send(f"{self.test}")

Note

You can override any library methods of context if you want.

Overriding get_context⚓︎

  • Make discord.py use your context class by overriding the get_context method in the bot subclass.
import discord
from discord.ext import commands

class MyBot(commands.Bot):
    async def get_context(self, message: discord.Message, *, cls=CustomContext):
        return await super().get_context(message, cls=cls)
  • Don't forget to use your custom bot class, not default one
bot = MyBot(".", intents=intents)

Creating example command⚓︎

@bot.command()
async def foo(ctx: MyContext) -> None:
    await ctx.send_test()

Result⚓︎

Showcase

And that's it! You can now use your custom context class.

Note

Custom class context is limited to commands which you commands.Context as a required argument. Meaning that you can't use it with slash commands.

More examples⚓︎

Tick method⚓︎

Here, we're adding a method called tick to the context, which, based on the value provided, adds a cross or a checkmark emoji.

Example from discord.py

Showcase

Storing custom attribute for server⚓︎

Let's create a simple table to hold a guild secret value.

CREATE TABLE `test`.`secret` (
  `guild_id` BIGINT UNSIGNED NOT NULL,
  `value` VARCHAR(64) NOT NULL DEFAULT 'nothing',
  PRIMARY KEY (`guild_id`));

After that, let's add two methods in the context: one retrieves the value from this table, the other modifies it.

import aiomysql
import discord
from discord.ext import commands


class MyContext(commands.Context):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    async def get_secret_value(self):
        async with aiomysql.connect(...) as connection:
            async with connection.cursor() as cursor:
                await cursor.execute("SELECT value FROM secret WHERE guild_id = %s", (self.guild.id,))
                data = await cursor.fetchone()
                if data is None:
                    await cursor.execute("INSERT INTO secret (guild_id) VALUES (%s)", (self.guild.id,))
                    await connection.commit()
                    return "nothing"
                return data[0]

    async def set_secret_value(self, new_value):
        async with aiomysql.connect(...) as connection:
            async with connection.cursor() as cursor:
                await cursor.execute("UPDATE secret SET value = %s WHERE guild_id = %s", (new_value, self.guild.id))
                await connection.commit()


class MyBot(commands.Bot):
    async def get_context(self, message, *, cls=MyContext):
        return await super().get_context(message, cls=cls)


intents = discord.Intents.default()
intents.message_content = True

bot = MyBot('.', intents=intents)


@bot.command()
async def get(ctx: MyContext):
    secret = await ctx.get_secret_value()
    await ctx.send(f"||{secret}||")


@bot.command()
async def change(ctx: MyContext, new_value: str):
    await ctx.set_secret_value(new_value)
    await ctx.send(f"Secret value changed")

bot.run("token")

Showcase

Comments