Search code examples
pythondiscorddiscord.py

Why does my Discord.py Bot not recoginize ctx params for commands


I want to run commands on my discord.py bot. I created my own subclass of commands.Bot. The on_ready and on_message events are being triggered as well as my registered command test.

My code:

import discord
from discord.ext import commands
import os
from dotenv import load_dotenv
from typing import Coroutine, Any

os.system("clear")

load_dotenv()
TOKEN = os.environ["TOKEN"]

class Bot(commands.Bot):
    # Bot setup
    def __init__(self, activity: discord.Game, command_prefix: str, intents: discord.Intents, *args, **kwargs) -> None:
        super().__init__(activity=activity, command_prefix=command_prefix, intents=intents, **kwargs)
        self.add_command(self.test)
    
    def set_channel(self, channel_id: int) -> None:
        self.channel_id = channel_id
        self.channel = super().get_channel(channel_id)

    async def on_ready(self) -> None:
        print(f"Logged in as {self.user}!")
    
    async def on_message(self, message: discord.Message) -> None:
        print(f"Message from {message.author}: {message.content}")
        await self.process_commands(message)
    
    async def on_error(self, event_method: str, /, *args: Any, **kwargs: Any) -> Coroutine[Any, Any, None]:
        return await super().on_error(event_method, *args, **kwargs)
    
    @commands.command()
    async def test(self, ctx) -> None:
        await ctx.send("Testing...")

if __name__ == "__main__":
    # Bot configuration setup
    intents: discord.Intents = discord.Intents.default()
    intents.message_content = True
    channel_name: str = "general"
    channel_id: int = 0

    # Setting up bot
    bot = Bot(activity=discord.Game(name="with you"), command_prefix="/", intents=intents)
    for c in bot.get_all_channels():
        if c.name == channel_name:
            channel_id = c.id
            bot.set_channel(channel_id)

    bot.run(TOKEN)

The problem I'm facing is that, through the way I implemented all this, the ctx parameter doesn't seem to be recognized. The error:

discord.ext.commands.errors.CommandInvokeError: Command raised an exception: TypeError: Bot.test() missing 1 required positional argument: 'ctx'


Solution

  • A command needs to be registered to the bot instance. This does not happen if you just add a function to the class.

    To organize commands use Cogs. Events can also be handled by cogs.

    How can I use advanced command handling in discord.py

    class ExtraCommands(commands.Cog):
        # Cog setup
        def __init__(self, bot):
            self.bot = bot
        
        def set_channel(self, channel_id: int) -> None:
            self.bot.channel_id = channel_id
            self.bot.channel = self.bot.get_channel(channel_id) # test if that does what you want
    
        @commands.Cog.listener()
        async def on_ready(self) -> None:
            print(f"Logged in as {self.user}!")
        
        @commands.Cog.listener()
        async def on_message(self, message: discord.Message) -> None:
            print(f"Message from {message.author}: {message.content}")
            await self.process_commands(message) # check if that needs to be rerouted
        
        @commands.Cog.listener()
        async def on_error(self, event_method: str, /, *args: Any, **kwargs: Any) -> Coroutine[Any, Any, None]:
            return await super().on_error(event_method, *args, **kwargs)
        
        @commands.command()
        async def test(self, ctx) -> None:
            await ctx.send("Testing...")
    
    

    The cogs are added asynchronously to the bot via

    await bot.add_cog(ExtraCommands(bot))

    so you need something like the next code snipplet for it to run.
    In short, the main function already needs to be asynchronously defined as well. Easier but not recommended, you could add the cog in on_ready without the need to change your code much.

    async def main():
        await bot.add_cog(ExtraCommands(bot))
        ...
        await bot.start(TOKEN)
    
    asyncio.run(main())