so I'm having an issue while creating a discord music bot and I've been trying to solve it for a while now, but I can't find any solution.
Here's the constructor and three functions to search and play music that are necessary for the command to actually work (I'll only include the skip command since I get the same exception with the rest of the commands)
class music(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.is_playing = False
self.is_paused = False
self.music_queue = []
self.YDL_OPTIONS = {"format": "bestaudio", "noplaylist": "True"}
self.FFMPEG_OPTIONS = {"before_options": "-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5", "options": "-vn"}
self.vc = None
async def play_music(self, ctx):
if len(self.music_queue) > 0:
self.is_playing = True
m_url = self.music_queue[0][0]["source"]
if self.vc == None or not self.vc.is_connected():
self.vc == await self.music_queue[0][1].connect()
if self.vc == None:
await ctx.send("I'm sorry, but I can't join the voice channel")
return
else:
await self.vc.move_to(self.music_queue[0][1])
self.music_queue.pop(0)
self.vc.play(discord.FFmpegPCMAudio(m_url, **self.FFMPEG_OPTIONS), after=lambda e: self.play_next())
else:
self.is_playing = False
def play_next(self):
if len(self.music_queue) > 0:
self.is_playing = True
m_url = self.music_queue[0][0]["source"]
self.music_queue.pop(0)
self.vc.play(discord.FFmpegPCMAudio(m_url, **self.FFMPEG_OPTIONS), after=lambda e: self.play_next())
else:
self.is_playing = False
def search(self, item):
with YoutubeDL(self.YDL_OPTIONS) as ydl:
try:
info = ydl.extract_info("ytsearch:{0}".format(item), download=False)["entries"][0]
except Exception:
return False
return {"source": info["formats"][0]["url"], "title": info["title"]}
async def setup(client):
await client.add_cog(music(client))
bot.py with setup_hook and the command
class MyBot(commands.Bot):
async def setup_hook(self):
await self.load_extension("dustobot")
@commands.command(aliases=["s"], help="Skips the current song that is being played")
async def skip(self, ctx, *args):
if self.vc != None and self.vc:
self.vc.stop()
await self.play_music(ctx) #Calling a function that plays the next song in a queue
And this is the error I get:
2023-01-21 12:28:37 ERROR discord.ext.commands.bot Ignoring exception in command skip
Traceback (most recent call last):
File "C:\Users\private_path\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\discord\ext\commands\core.py", line 229, in wrapped
ret = await coro(*args, **kwargs)
TypeError: music.skip() missing 1 required positional argument: 'ctx'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:\Users\private_path\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\discord\ext\commands\bot.py", line 1349, in invoke
await ctx.command.invoke(ctx)
File "C:\Users\private_path\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\discord\ext\commands\core.py", line 1023, in invoke
await injected(*ctx.args, **ctx.kwargs) # type: ignore
File "C:\Users\private_path\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\discord\ext\commands\core.py", line 238, in wrapped
raise CommandInvokeError(exc) from exc
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: TypeError: music.skip() missing 1 required positional argument: 'ctx'
Is it because I'm using a deprecated discord.py syntax?
Thanks in advance for taking your time to help me, I really appreciate it.
Btw this is my main.py:
import os
import discord
from dustobot import music #This imports the music class
from dotenv import load_dotenv
from discord.ext import commands
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
intents = discord.Intents.default()
intents.message_content = True
client = commands.Bot(command_prefix="!", intents=intents)
client.remove_command("help") #I have my own help command
if __name__ == "__main__":
client.run(TOKEN)
The issue is caused by the way you created the command:
#client refers to the bot
@client.command(aliases=["s"], help="Skips the current song that is being played")
The decorator doesn't expect this to be a method in a class. As such, it's not invoked as a method either, so self
won't be passed automatically and the Context
will be the first argument (self
). As a result, the library won't pass a value for ctx
, which results in your error.
In cogs, commands should be created using @commands.command
instead of @<Bot_instance>.command
, the reason being that you shouldn't have your Bot
instance available to you in that scope.
You could've also gotten this by reading the manual for Cogs...
Every command is marked with the
commands.command()
decorator.