I would like to make it so that once you do the command h!start, it sends you a message every 10 seconds and it stops when the user types h!stop. However, the discord bot never sends a message in the discord chat. I just started to learn how to make discord bots, so excuse me if this is basic. Error is under the code.
import discord
import random
from discord.ext import commands, tasks
from itertools import cycle
client = commands.Bot(command_prefix = 'h!')
#client.event
async def on_ready():
await client.change_presence(status=discord.Status.idle, activity=discord.Game('Work In Progress'))
print('Bot is ready')
#tasks.loop(seconds=10)
async def reminder():
channel = client.get_channel(797915093954199565)
await channel.send('It has been 10 seconds')
#client.command()
async def start():
reminder.start()
print('Reminder Started')
#client.command()
async def stop():
reminder.cancel()
print('Reminder stopped')
Error:
Traceback (most recent call last):
File "C:\Python39\lib\site-packages\discord\client.py", line 343, in _run_event
await coro(*args, **kwargs)
File "C:\Python39\lib\site-packages\discord\ext\commands\bot.py", line 942, in on_message
await self.process_commands(message)
File "C:\Python39\lib\site-packages\discord\ext\commands\bot.py", line 939, in process_commands
await self.invoke(ctx)
File "C:\Python39\lib\site-packages\discord\ext\commands\bot.py", line 902, in invoke
await ctx.command.invoke(ctx)
File "C:\Python39\lib\site-packages\discord\ext\commands\core.py", line 856, in invoke
await self.prepare(ctx)
File "C:\Python39\lib\site-packages\discord\ext\commands\core.py", line 790, in prepare
await self._parse_arguments(ctx)
File "C:\Python39\lib\site-packages\discord\ext\commands\core.py", line 693, in _parse_arguments
raise discord.ClientException(fmt.format(self))
discord.errors.ClientException: Callback for start command is missing "ctx" parameter.
Task exception was never retrieved
future: <Task finished name='Task-13' coro=<Loop._loop() done, defined at C:\Python39\lib\site-packages\discord\ext\tasks\__init__.py:88> exception=NameError("name 'channel' is not defined")>
Traceback (most recent call last):
File "C:\Python39\lib\site-packages\discord\ext\tasks\__init__.py", line 125, in _loop
raise exc
File "C:\Python39\lib\site-packages\discord\ext\tasks\__init__.py", line 101, in _loop
await self.coro(*args, **kwargs)
File "C:\Users\zedga\source\repos\Hydrate\Hydrate\Hydrate.py", line 16, in reminder
await channel.send('It has been 10 seconds')
NameError: name 'channel' is not defined
Unhandled exception in internal background task 'reminder'.
Traceback (most recent call last):
File "C:\Python39\lib\site-packages\discord\ext\tasks\__init__.py", line 101, in _loop
await self.coro(*args, **kwargs)
File "C:\Users\zedga\source\repos\Hydrate\Hydrate\Hydrate.py", line 16, in reminder
await channel.send('It has been 10 seconds')
NameError: name 'channel' is not defined
Yeah sure, just get the id of the channel the command was sent in and global it so it can be used in the task.
#client.command()
async def start(ctx):
global start_channel
start_channel = ctx.channel.id
reminder.start()
print('Reminder Started')
#tasks.loop(seconds=4)
async def reminder():
channel = client.get_channel(int(start_channel))
await channel.send('It has been 10 seconds')
#client.command()
async def stop(ctx):
reminder.cancel()
print('Reminder stopped')
In your error, it includes "channel not defined"
Its because it's channel is defined as a "local" variable, which means it can only be used in the same block of code or within the command. You can simply fix this by adding a global, A global makes a variable accessible throughout the whole code, it can be used like this:
global channel
channel = client.get_channel(797915093954199565)
You are also missing the "ctx" decorator and your task should be below the below the command to trigger it
#client.command()
async def start(ctx):
reminder.start()
print('Reminder Started')
#tasks.loop(seconds=10)
async def reminder():
global channel
channel = client.get_channel(797915093954199565)
await channel.send('It has been 10 seconds')
#client.command()
async def stop(ctx):
reminder.cancel()
print('Reminder stopped')
Related
This question already has answers here:
"TypeError: method() takes 1 positional argument but 2 were given" but I only passed one
(10 answers)
Closed 7 months ago.
I'm new to python but trying to learn some things with it and discord.py ... but I ran in to this error:
Ignoring exception in on_message
Traceback (most recent call last):
File "blabla/.local/lib/python3.8/site-packages/discord/client.py", line 343, in _run_event
await coro(*args, **kwargs)
TypeError: on_message() takes 1 positional argument but 2 were given
And here is my code:
from discord.ext import commands
# Bot responses
class BotResponses(commands.Cog):
def __init__(self, bot):
self.bot = bot
#commands.Cog.listener()
async def on_message(message):
if message.author == bot.user:
return
if message.content.startswith('hello'):
await message.channel.send('Hello')
await bot.process_commands(message)
def setup(bot):
bot.add_cog(BotResponses(bot))
Am I doing something that adds an argument or something? And if so, is there a way to prevent it?
You've created a commands.Cog which is a class. A method (a function inside a class) needs the parameter self.
So, your method should look like that:
#commands.Cog.listener()
async def on_message(self, message):
if message.author == bot.user:
return
if message.content.startswith('hello'):
await message.channel.send('Hello')
await bot.process_commands(message)
Learn about Python classes here: https://www.w3schools.com/python/python_classes.asp
Learn about cogs here: https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html
As a part of learning OOP, I'm trying to create a class that will handle the loading and unloading cogs. However I'm getting the following error
Traceback (most recent call last):
File "c:\Users\mirza\Desktop\Work\Working Directory\Project\Bots\2B\bot.py", line 50, in <module>
main()
File "c:\Users\mirza\Desktop\Work\Working Directory\Project\Bots\2B\bot.py", line 43, in main
bot = CustomBotClient()
File "c:\Users\mirza\Desktop\Work\Working Directory\Project\Bots\2B\bot.py", line 21, in __init__
raise e
File "c:\Users\mirza\Desktop\Work\Working Directory\Project\Bots\2B\bot.py", line 18, in __init__
self.load_extension(f"cogs.{filename[:-3]}")
File "C:\Users\mirza\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\ext\commands\bot.py", line 671, in load_extension
if name in self.__extensions:
AttributeError: 'CustomBotClient' object has no attribute '_BotBase__extensions'
This the code from bot.py that is giving me the error
import discord
from discord.ext import commands
import os
from dotenv import load_dotenv
class CogLoader(commands.Bot):
def __init__(self):
for filename in os.listdir('./cogs'):
if filename.endswith(".py"):
try:
self.load_extension(f"cogs.{filename[:-3]}")
except Exception as e:
print(f"cogs.{filename[:-3]} cannot be loaded")
raise e
#commands.command()
async def load(self, ctx, extension):
self.load_extension(f"cogs.{extension}")
#commands.command()
async def unload(self, ctx, extension):
self.unload_extension(f"cogs.{extension}")
#commands.command()
async def reload(self, ctx, extension):
self.unload_extension(f"cogs.{extension}")
self.load_extension(f"cogs.{extension}")
class CustomBotClient(CogLoader):
async def on_ready(self):
print(f"Bot {self.user} is connected to Discord and ready to roll!")
def main():
bot = CustomBotClient()
load_dotenv()
bot.run(os.getenv("TOKEN"))
if __name__ == "__main__":
main()
This is the cog that I'm trying to load
from discord.ext import commands
class Greetings(commands.Cog):
def __init__(self, bot):
self.bot = bot
#commands.command(name="GreetME")
async def greet_me(self, ctx):
await ctx.send("Hello! {author.user}")
def setup(bot):
bot.add_cog(Greetings(bot))
I'm following a youtube tutorial but he didnot used a class for this. So, I'm a loss here. Any help would be appreciated.
You need to initialize the commands.Bot in your CogLoader.__init__
This can be accomplished through the following code:
class CogLoader(commands.Bot):
def __init__(self, command_prefix, **options):
super().__init__(command_prefix, **options)
for filename in os.listdir('./cogs'):
if filename.endswith(".py"):
try:
self.load_extension(f"cogs.{filename[:-3]}")
except Exception as e:
print(f"cogs.{filename[:-3]} cannot be loaded")
raise e
and
def main():
bot = CustomBotClient(command_prefix="!")
This way, commands.Bot and BotBase, their attributes and their methods are loaded.
The title pretty much explains it self but I'll explain it again.
So basically when I have 2 #client.event or 1 #client.command and 1 #client.event line in the code, the whole thing breaks except for the last block of code.
Like in my code here, the first #client.event works fine. The second one doesn't, but the third one does, now if I switch the second one and the third ones place then the second ones works but the third doesn't.
import discord
from discord.ext.commands import Bot
from discord.ext import commands
import asyncio
import time
import random
from discord import Game
Client = discord.client
client = commands.Bot(command_prefix = " ")
Clientdiscord = discord.Client()
#client.event
async def on_ready():
print('{0.user}'.format(client))
print('Is Online')
#client.command
async def cmds(ctx):
await ctx.send('Prefix: v!')
await client.process_commands(ctx)
#client.event
async def on_message(message):
if message.content == 'v!coin':
variable = [
'flip',
'stay',
]
await message.channel.send((random.choice)(variable))
await client.process_commands(message)
client.run("TOKEN")
If you're using the commands framework, you should add all your commands using the #client.commands decorator instead of having logic in your on_message event handler.
If you need to handle on_message events for another reason (examokeL autoresponse to non commands), you need to call await client.process_commands(message) at the end of your on_message event handler function. It should be outside any if statements but still inside the function. Only call process_commands once in your on_message, and never call it inside your commands unless you know exactly what you are doing. Your on_message function should look something like this:
#client.event
async def on_message(message):
# Do whatever here, but don't handle commands
# If you return early, any commands in the message will be skipped
await client.process_commands(message)
So instead of this:
#client.command
async def cmds(ctx):
await ctx.send('Prefix: v!')
await client.process_commands(ctx)
#client.event
async def on_message(message):
if message.content == 'v!coin':
variable = [
'flip',
'stay',
]
await message.channel.send((random.choice)(variable))
await client.process_commands(message)
You should have something like this:
#client.command()
async def cmds(ctx):
await ctx.send('Prefix: v!')
# Send a list of commands here or do whatever this command is supposed to do
# Don't call client.process_commands here
#client.command()
async def coin(ctx):
# Coinflip logic here
# I've added the original logic you had in your question
variable = [
'flip',
'stay',
]
await ctx.send((random.choice)(variable))
# no #client.event or def on_message unless you need to handle raw messages
Also, you don't need these lines of code:
Client = discord.client
client = commands.Bot(command_prefix = " ")
Clientdiscord = discord.Client()
You can just use this:
client = commands.Bot(command_prefix = "v!")
If you are using commands.Bot already, you don't need another discord.Client.
Instead of placing await client.process_commands(message) within your if statement, place it outside of it. Please review the revised code below.
#client.event
async def on_message(message):
if message.content == 'v!coin':
variable = [
'flip',
'stay',
]
await message.channel.send((random.choice)(variable))
await client.process_commands(message)
Please note that you do not need to include await client.process_commands(message) in a command, only in your on_message event.
I’m trying to write a simple music bot using discord.py to play music using youtube_dl. I’m having problems with my 'join’ command. When I try to use it on discord it gives this error
Ignoring exception in command join:
Traceback (most recent call last):
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/ext/commands/core.py", line 85, in wrapped
ret = await coro(*args, **kwargs)
File "/home/runner/Discord-Bot-2/commands.py", line 44, in join
await ctx.voice_client.connect(ctx.author.voice_channel)
AttributeError: 'NoneType' object has no attribute 'connect'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/ext/commands/bot.py", line 939, in invoke
await ctx.command.invoke(ctx)
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/ext/commands/core.py", line 863, in invoke
await injected(*ctx.args, **ctx.kwargs)
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/ext/commands/core.py", line 94, in wrapped
raise CommandInvokeError(exc) from exc
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: AttributeError: 'NoneType' object has no attribute 'connect'
This error happens when I’m in a voice channel. If I’m not, the bot follows the code that I wrote and sent a message in discord “You are not connected to a voice channel”. But when I am in a voice channel it gives the error.
The code for the join command is
#commands.command()
async def join(self, ctx):
if ctx.author.voice is None:
return await ctx.send("You are not connected to a voice channel")
if ctx.voice_client is not None:
await ctx.voice_client.disconnect()
await ctx.voice_client.connect(ctx.author.voice_channel)
await ctx.send(f"Connected to {ctx.author.voice_channel}")
I’m pretty sure that I set up everything correctly, but here’s all the code above the join command where I set up the cog for my commands
import discord
from discord.ext import commands
import youtube_dl
import pafy
import asyncio
class Commands(commands.Cog):
def __init__(self, client):
self.client = client
self.song_queue = {}
#commands.Cog.listener()
async def on_ready(self):
for guild in self.client.guilds:
self.song_queue[guild.id] = []
print("Cog 'commands' loaded")
async def check_queue(self, ctx):
if len(self.song_queue[ctx.guild.id]) > 0:
ctx.voice_client.stop()
await self.play_song(ctx, self.song_queue[ctx.guild.id][0])
self.song_queue[ctx.guild.id].pop(0)
async def search_song(self, amount, song, get_url=False):
info = await self.client.loop.run_in_executor(None, lambda: youtube_dl.YoutubeDL({"format": "bestaudio", "quiet" : True}).extract_info(f"ytsearch{amount}:{song}", download=False, ie_key="YoutubeSearch"))
if len(info["entries"]) == 0:
return None
return [entry["webpage_url"] for entry in info["entries"]] if get_url else info
async def play_song(self, ctx, song):
url = pafy.new(song).getbestaudio().url
ctx.voice_client.play(discord.PCMVolumeTransformer(discord.FFmpegPCMAudio(url)), after=lambda error: self.client.loop.create_task(self.check_queue(ctx)))
ctx.voice_client.source.volume = 0.5
I was using an outdated method (voice_client)
#commands.command()
async def join(self, ctx):
if ctx.author.voice is None:
return await ctx.send("You are not connected to a voice channel")
else:
channel = ctx.author.voice.channel
await channel.connect()
await ctx.send(f"Connected to voice channel: '{channel}'")
#tasks.loop(minutes=1)
async def FunctionTask():
print("Task running")
#client.event
async def on_ready():
FunctionTask.start()
#client.command()
async def start(ctx):
FunctionTask.restart()
#client.command()
async def start(ctx):
FunctionTask.stop()
I'm trying to stop and restart a task, but I get errors. What I am doing wrong? Thank you in advance
edit: Added error code
Traceback (most recent call last):
File "Test.py", line 39, in <module>
async def start(ctx):
File "C:\Users\Vortex\AppData\Local\Programs\Python\Python38\lib\site-packages\discord\ext\commands\core.py", line 1163, in decorator
self.add_command(result)
File "C:\Users\Vortex\AppData\Local\Programs\Python\Python38\lib\site-packages\discord\ext\commands\core.py", line 1071, in add_command
raise discord.ClientException('Command {0.name} is already registered.'.format(command))
discord.errors.ClientException: Command start is already registered.
you have literally made the start command twice
async def start(ctx):
FunctionTask.restart()
#client.command()
async def start(ctx):
FunctionTask.stop()```
you might wanna change their names like:
#client.command()
async def start(ctx):
FunctionTask.restart()
#client.command()
async def starta(ctx):
FunctionTask.stop()```