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'
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())