A relatively simple command that takes 3 arguments (reportedTag, reporterTag, and reasons) with the simple intent of deleting the message from #report and sending one to #reported my issue is that it doesn't see the original command and hence does not send the reported message
I've tried using the channel ids, the channel names but to no avail
#---Report Command---#
#bot.command(pass_context=True)
async def report(ctx, reportedTag, reporterTag, *reasons):
if int(ctx.message.channel.id) == 416164062901305345:
reason = ' '.join(reasons)
await bot.delete_message(ctx.message)
mesg = "Report by "+ reporterTag + " for " + reportedTag + "Reason is: " + reason
return await bot.say("Report recieved. Staff have been notified :)\nEnjoy your day and we'll take care of this")
return await bot.send_message(bot.get_channel("534496148149370900"), mesg)
else:
print ("Something went wrong")
Expected result: command line deleted from #report and message sent to #reported
Actual result: "Something went wrong"
Two things wrong:
There's a problem in the return statement you used in the code. return exits a subroutine, so the line bot.send_message(bot.get_channel("534496148149370900"), mesg) is never actually called. So your code should change to something like this:
#---Report Command---#
#bot.command(pass_context=True)
async def report(ctx, reportedTag, reporterTag, *reasons):
if int(ctx.message.channel.id) == 416164062901305345:
reason = ' '.join(reasons)
await bot.delete_message(ctx.message)
mesg = "Report by "+ reporterTag + " for " + reportedTag + "Reason is: " + reason
await bot.send_message(bot.get_channel("534496148149370900"), mesg)
return await bot.say("Report recieved. Staff have been notified :)\nEnjoy your day and we'll take care of this")
else:
print ("Something went wrong")
Apart from that, if "Something went wrong" is actually outputted, that means that int(ctx.message.channel.id) == 416164062901305345 is false. Please check the ID and the channel you're writing in.
Related
This question already has answers here:
Why does on_message() stop commands from working?
(2 answers)
Closed last year.
So I've been writing a bot using discord.py, and I've found an error I can't seem to be able to fix.
First, I have something like this:
#client.command()
async def ping(ctx):
await ctx.send("Pong!")
Then I added an entire section here:
#client.event
async def on_message(message):
# print(message)
if message.content.startswith('$random'):
if len(message.content) == 7:
await message.channel.send("**You need to provide a valid number range. Examples: *$random 7 69 | $random 69 (automatically sets the first number as 1)***")
else:
if message.content.split(" ",1)[1] == "help":
await message.channel.send("**You need to provide a valid number range. Examples: *$random 7 69 | $random 69 (automatically sets the first number as 1)***")
try:
if message.content.split(" ",2)[2] != "":
number1 = int(message.content.split(" ",2)[1])
number2 = int(message.content.split(" ",2)[2])
number2send = str(random.randint(number1,number2))
await message.channel.send("Your random number is: **" + number2send + "**")
return
except:
try:
if message.content.split(" ",1)[1] != "":
number = int(message.content.split(" ",1)[1])
number2send = str(random.randint(1,number))
await message.channel.send("Your random number is: **" + number2send + "**")
return
except:
await message.channel.send("**You need to provide a valid number range. Examples: *$random 7 69 | $random 69 (automatically sets the first number as 1)***")
return
if message.content.startswith('$timein'):
if len(message.content) == 7:
await message.channel.send("**You need to provide a valid country***")
else:
try:
lad = message.split(" ",1)[1]
print("Location address:", lad)
location = geolocator.geocode(lad)
print("Latitude and Longitude of the said address:")
print((location.latitude, location.longitude))
# pass the Latitude and Longitude
# into a timezone_at
# and it return timezone
obj = TimezoneFinder()
# returns 'Europe/Berlin'
result = obj.timezone_at(lng=location.longitude, lat=location.latitude)
print("Time Zone : ", result)
except:
return
And any command made like the first section and $timein command does not work anymore, while both worked on their own, and I have no idea why.
I feel like the errors are caused by the differences in
#client.command
and
#client.event
So this is probably what's causing the issue.
Anyways, to any kind people helping me with these, thanks in advance!
Your commands doesn't work because of on_message. What happens is you are overriding the default on_message and defaut on_message is the one that handles the commands. You can fix it by adding a bot.process_commands(message) line at the end of your on_message.
Discord.py has this issue covered on it's F.A.Q, read more here.
Also, a side note. Please don't handle commands by yourself, Discord.py's commands extension has a lot of features you probably didn't discover. Please learn more about commands, it will save tons of work from your shoulders and will be easier to develop your dream bot.
I'm trying to reply a function from another file called "blockchain.py" but when I tried to await for ctx.reply(blq.blockchain_connection) it gives me this on Discord:
<function blockchain_connection at 0x000001922A5AEEF0>
The code is:
# bot = scrt.bot
bot = commands.Bot(command_prefix = "!")
login = 0
blq.tokens_dict
# Login
#bot.event
async def on_ready():
global login
print('We have logged in as {0.user}'.format(bot))
login = 1
my_background_task.start()
# Coin Price Command
#bot.command()
async def coin(ctx, arg1):
if blq.tokens_dict == blq.tokens_dict:
blq.blockchain_connection
else:
await ctx.reply("The token " + str(arg1) + " is not in the token list, if you want to add " + str(arg1) + " to the list please use the command : " + '\n' + "!add_token and insert key and value with an space on each")
#bot.command()
async def add_token(ctx, key, value):
global tokens_dict
if(key in blq.tokens_dict.keys()):
await ctx.reply('I already have this token on my list, please add another Contract or another Value')
else:
blq.tokens_dict[key] = value
This other file (blockchain) has a dict and this function:
async def blockchain_connection(ctx, arg1):
async with aiohttp.ClientSession() as cs:
async with cs.get('https://api.pancakeswap.info/api/v2/tokens/' + tokens_dict[arg1]) as r:
res = await r.json() # returns dict
await ctx.reply(res['data']['price'])
It looks like the issue is here:
# Coin Price Command
#bot.command()
async def coin(ctx, arg1):
if blq.tokens_dict == blq.tokens_dict:
blq.blockchain_connection
else:
await ctx.reply("The token " + str(arg1) + " is not in the token list, if you want to add " + str(arg1) + " to the list please use the command : " + '\n' + "!add_token and insert key and value with an space on each")
blq.blockchain_connection is a function, receiving two arguments, which are named the same. It looks like you have to call it like so:
# Coin Price Command
#bot.command()
async def coin(ctx, arg1):
if blq.tokens_dict == blq.tokens_dict:
await blq.blockchain_connection(ctx, arg1)
else:
await ctx.reply("The token " + str(arg1) + " is not in the token list, if you want to add " + str(arg1) + " to the list please use the command : " + '\n' + "!add_token and insert key and value with an space on each")
This will actually call the function, which then internally (conditionally) calls ctx.reply(...) with the message you actually want to send.
im trying to add a role when the user react to this emojis but i cant get
Setup Roles
#bot.command()
async def roles(ctx):
global IDMessage
reaction = await ctx.reply("Select your Game" + '\n' + '\n' + "- Valorant 🦸♀️" '\n' + "- World of Warcraft ⚔" + '\n' + "- League of Legends 🧙♀️" + '\n' + "- Cs:Go 🔫")
await reaction.add_reaction('🦸♀️')
await reaction.add_reaction('⚔')
await reaction.add_reaction('🧙♀️')
await reaction.add_reaction('🔫')
IDMessage = reaction.message_id
And this is the part of the code that doesnt work for me
Add role on reaction
#bot.event
async def on_reaction_add(reaction, user):
ChID = '920320584032870424'
if reaction.message.channel.id != ChID:
return
if reaction.emoji == "🦸♀️":
Valorant = discord.utils.get(user.server.roles, name="Valorant")
await bot.add_roles(user, Valorant)
ChID = '920320584032870424' (string) is being compared to an integer reaction.message.channel.id.
ChID = '920320584032870424'
Instead, use
ChID = 920320584032870424
Which is an integer.
There are some issues here. Instead of on_reaction_add, use on_raw_reaction_add. Most bots use the latter instead of the former.
Why you may ask? Because, on_reaction_add only works for messages in bot's cache.
So every message sent after your bot can read messages is stored in a dictionary or list (this is what a cache is). By default the limit of caching is 1000 messages.
So if you ever restarted your bot, the message will never be in cache, and thus the event won't trigger.
Another issue is that you are comparing a string (ChID) variable and an integer (reaction.message.channel.id), they can never be equal so if reaction.message.channel.id != ChID will always return True and thus won't do anything (as you return for that condition being True).
Third issue is that bot.add_roles does not exist. add_roles is a method of discord.Member object.
Fourth issue is, Member.server is not a thing, Member.guild is
So your updated code will be like:
#bot.event
async def on_raw_reaction_add(payload: discord.RawReactionActionEvent):
ChID = 920320584032870424
if payload.channel.id != ChID:
return
if str(payload.emoji) == "🦸♀️":
valorant = discord.utils.get(payload.member.guild.roles, name="Valorant")
await payload.member.add_roles(valorant)
Also my personal advice of not using global
there are very rare few cases where you would actually require this keyword
But it is highly unlikely for you to use this in a bot made with discord.py lib
More on this:
discord.on_reaction_add
discord.RawReactionActionEvent
discord.Member.guild
discord.Member.add_roles
why-are-global-variables-evil
I had the same problem, and this is how I solved it.
You have the reactions being added, now you need to wait for the user to react.
For this, you can use discord.py’s wait_for function.
Check function:
def check(reaction):
if str(reaction.emoji) == “EMOJIHERE”:
return “emoji name”
elif str(reaction.emoji) == “SECONDEMOJI”:
return “emoji name”
elif str(reaction.emoji) == “THIRDEMOJI”:
return “emoji name”
elif str(reaction.emoji) == “FOURTHEMOJI”:
return “emoji name”
else:
return “error”
Code:
async def roles(ctx):
global IDMessage
reaction = await ctx.reply("Select your Game" + '\n' + '\n' + "- Valorant 🦸♀️" '\n' + "- World of Warcraft ⚔" + '\n' + "- League of Legends 🧙♀️" + '\n' + "- Cs:Go 🔫")
await reaction.add_reaction('🦸♀️')
await reaction.add_reaction('⚔')
await reaction.add_reaction('🧙♀️')
await reaction.add_reaction('🔫')
confirm = await bot.wait_for(“reaction_add”, check=check)
if confirm == "emoji name 1":
doSomething()
elif confirm == "emoji name 2":
doSomething()
elif confirm == "emoji name 3":
doSomething()
elif confirm == "emoji name 4":
doSomething()
else:
print("Error")
await ctx.send("An error occurred.")
Replace EMOJIHERE and emoji name with the appropriate values.
i have a weird problem since i'm using an async mysql driver with Python3.9. I often get this error:
Task was destroyed but it is pending!
task: <ClientEventTask state=pending event=on_message coro=<bound method OnMessage.on_message of <cogs.on_message.OnMessage object at 0x7fcb96f50700>>>
and I can't find many information about this error in google, the only things I found couldn't help me. So this error occurs at absolutely random times, so I can't exactly reproduce it. But if it occurs, I can directly see it, because he doesn't answer/count/whatever-he-should to a "message."
So a small explanation of what the "on_message" task does is "do something" if a message gets posted by a user. So depending on the number of users and the activity, it can be triggered very often or very rarely. I saw that this error comes most times on bot stop/start-up, but in my case, it occurs sometimes while the bot is running normally. And I don't know what this error exactly means, how can I fix it or why it occurs. I thought maybe is my code for this task too long, but that would not make any sense to me. So that's my code for this "task":
#commands.Cog.listener("on_message")
async def on_message(self, message):
if message.author.bot:
return
if message.type != discord.MessageType.default:
return
if isinstance(message.channel, discord.channel.DMChannel):
return
prefixes = ["!", "?", "=", ".", ",", "#", "#", "/", '§', "$", "%", "&", ":?", "*", "<", ">", "-", "bday"]
for prefix in prefixes:
if message.content.startswith(str(prefix)):
return
global member_cooldown_list
member_cooldown_list = [i for i in member_cooldown_list if i[1] + cooldown_val > int(time.time())]
member_index = next((i for i, v in enumerate(member_cooldown_list) if v[0] == message.author.id), None)
if member_index is not None:
if member_cooldown_list[member_index][1] + cooldown_val > int(time.time()):
return
member_cooldown_list.append((message.author.id, int(time.time())))
count = 1
mydb = await getConnection()
mycursor = await mydb.cursor()
await mycursor.execute("SELECT ignore_role_id, bonus_role_id FROM guild_role_settings WHERE guild_id = %s", (message.author.guild.id,))
in_database = await mycursor.fetchone()
if in_database:
if in_database[0] is not None:
role_list = in_database[0].split(" ")
for roleid in role_list:
role = message.author.guild.get_role(int(roleid))
if role in message.author.roles:
await mycursor.close()
mydb.close()
return
if in_database[1] is not None:
role_list = in_database[1].split(" ")
for roleid in role_list:
role = message.author.guild.get_role(int(roleid))
if role in message.author.roles:
count += 1
await mycursor.execute("SELECT ignore_channel_id FROM guild_channel_settings WHERE guild_id = %s", (message.author.guild.id,))
in_database1 = await mycursor.fetchone()
if in_database1:
if in_database1[0] is not None:
channel_list = in_database1[0].split(" ")
for channelid in channel_list:
if int(message.channel.id) == int(channelid):
await mycursor.close()
mydb.close()
return
await mycursor.execute("SELECT * FROM guild_message_count WHERE guild_id = %s AND user_id = %s", (message.author.guild.id, message.author.id))
in_database2 = await mycursor.fetchone()
if in_database2:
await mycursor.execute("UPDATE guild_message_count SET user_id = %s, message_count = message_count + %s WHERE guild_id = %s AND user_id = %s", (message.author.id, count, message.author.guild.id, message.author.id))
else:
await mycursor.execute("INSERT INTO guild_message_count (user_id, message_count, guild_id) VALUES (%s, %s, %s)", (message.author.id, count, message.author.guild.id))
await mydb.commit()
await mycursor.close()
mydb.close()
I would be really appreciate it, if anyone explains the error to me, a solution for a fix and why it occurs. I really couldn't find much information about it and its really frustrating. Especially, because it occurs since I use an async MySQL driver and python3.9.
2 days later - Still searching for fix: I don't found a solution since creation and it's really frustrating.
This is because of a race condition between discord client module and the message processing thread as explained in the link.
This is because the discord client module needs control once every
minute or so.
This means that any function that steals control for more than a
certain time causes discord's client to enter an invalid state (which
will manifest itself as an exception some point later, perhaps upon
next method call of client).
In summary, you should offload all the heavy operations to a separate process (not a thread due to the Python GIL). The details are as follows:
Discord listener: dispatches the message to the worker server.
Worker server: parse message and process SQLs.
In your discord listener, global variables should be avoided as follows:
....
#commands.Cog.listener("on_message")
async def on_message(self, message):
import traceback
if message.author.bot or
message.type != discord.MessageType.default or
isinstance(message.channel, discord.channel.DMChannel):
return
prefixes = ["!", "?", "=", ".", ",", "#", "#", "/", '§', "$", "%", "&",
":?", "*", "<", ">", "-", "bday"]
for prefix in prefixes:
if message.content.startswith(prefix):
return
#global member_cooldown_list
self.member_cooldown_list = [i for i in self.member_cooldown_list
if i[1] + cooldown_val > int(time.time())]
member_index = next((i for i, v in enumerate(self.member_cooldown_list)
if v[0] == message.author.id), None)
if member_index is not None:
if self.member_cooldown_list[member_index][1] + cooldown_val > int(time.time()):
return
self.member_cooldown_list.append((message.author.id, int(time.time())))
guild_id = message.author.guild.id
try:
response = await call("process", guild_id)
...
except Exception as e:
print (e)
print (traceback.format_exc())
The following codes have to be moved to the work server script as follows:
class Handler:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
async def process(self, guild_id):
count = 1
mydb = await getConnection()
...
await mycursor.close()
mydb.close()
return 0
Note:
You should adopt the code in the link.
You should run the server worker script using nohup python the_worker_script.py & or something similar.
I am making a command which waits for a user to reply to the bot, but I would like the bot to only accept the author's reply.
#client.command(name='numgame',
brief='Guess a number between 1 and 100',
pass_context=True)
async def numgame(context):
number = random.randint(1,100)
guess = 4
while guess != 0:
await context.send('Pick a number between 1 and 100')
msg = await client.wait_for('message', check=check, timeout=30)
attempt = int(msg.content)
if attempt > number:
await context.send(str(guess) + ' guesses left...')
await asyncio.sleep(1)
await context.send('Try going lower')
await asyncio.sleep(1)
guess -= 1
elif attempt < number:
await context.send(str(guess) + ' guesses left...')
await asyncio.sleep(1)
await context.send('Try going higher')
await asyncio.sleep(1)
guess -=1
elif attempt == number:
await context.send('You guessed it! Good job!')
break
My issue is that anyone can respond to "Pick a number," whereas I would only like the person who started the command to be able to respond.
I am not too sure what to try, but I think it may have something to do with arguments. I have no idea where to begin, though, so a solution would be appreciated! Thanks a ton.
You need rewrite your check so that it knows who the author is. One way of doing this is to use a closure. Let's say you have an existing check
def check(message):
return message.content == "Hello"
You can replace this with a function that generates equivalent check functions with the author you want to check for injected into them
def check(author):
def inner_check(message):
return message.author == author and message.content == "Hello"
return inner_check
Then you would pass the inner check to wait_for by calling the outer check with the appropriate argument:
msg = await client.wait_for('message', check=check(context.author), timeout=30)
For your check this would be
def check(author):
def inner_check(message):
if message.author != author:
return False
try:
int(message.content)
return True
except ValueError:
return False
return inner_check