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.
Related
im making a bot for my discord server but i have problem and my problem is this:
There is a command for the warning which is $warn and a command to remove the warnings is $clrw and another command to show the warnings is $warns but there is a problem.
When the user uses the $clrw command, it means that the variable that is accumulated during the warning becomes 0, but for all users, it means when the user use $clrw #moonboyInstead of only the desired person's warren being deleted, all people's warrens are deleted and this is a big problem!
my code:
#app.command()
# #commands.has_permissions(Warning = True)
async def warn(ctx,member: discord.Member,*,reason = None):
global num
if reason == None:
await ctx.send(f"hey {ctx.author.mention} pls enter a reason!")
else:
num += 1
# await member.send(f"hey {ctx.author.mention} you have been warned bequase {reason} from {ctx.guild.name} pls dont do it again!")
await ctx.send(f"{member} has been warned bequase {reason} ! ")
return num
#app.command()
async def clrw(ctx,member:discord.Member):
global num
num = 0
await ctx.send(f"{member} warns has been deleted!")
#app.command()
async def warns(ctx,member: discord.Member):
if num <= 0:
await ctx.send(f"{member} has no warns!")
else:
await ctx.send(f"{member} has {num} warns!")
the global variable num is not related to any particular user. If you wanted to do it per user, you could use a dictionary of their user ids.
warns = dict()
# add a warning
userid = member.id # not sure if this is exactly how to get id, check docs
if userid not in warns:
warns[userid] = 1
else:
warns[userid] += 1
# clear warnings
warns[userid] = 0
# display warnings
if warns[userid] == 0:
await ctx.send(f"{member} has no warnings")
else:
await ctx.send(f"{member} has {warns[userid]} warnings")
(This is all pseudocode to show how dicts work. This will obviously not work when pasted directly into your code)
You're lacking understanding of the variable num.
The Problem:
Atm what you are doing is, just adding and removing to the same variable or number in this case.
The code cannot know if u meant a diff user this way.
The Fix:
You should convert ur variable num to a dictionary. If the bot is gonna be for just one server then a simple dictionary should be enough.
Example:
When doing the current num +=1 replace it with num[member.id] +=1
For reset instead of num = 0 do num[member.id] = 0
For the getting of warnings, instead of num do num[member.id]
In this case u will mention who exactly u want to reset the warns for, get it from or add to.
Also don't forget to convert the current num value where u set it the first time to num = []
Hope this helps!
PS. The amount of warns will reset on bot restart, as this is a variable in the code its not saved anywhere outside. I would recommend saving it in a file or redis server.
Example code for the error in the comment:
Use this for the command where u add the warning.
The reason u got that error is that u cannot add a +1 warning to an user who is not added in the array itself.
if member.id not in num:
num[member.id] = 1
else:
num[member.id] +=1
So u don't get a similar error with the getting of the warn.
Example:
if member.id not in the num or num[member.id] == 0:
await ctx.send(f"{member} has no warns!")
else:
await ctx.send(f"{member} has {num[member.id]} warns!")
Im Trying To Make A Rapid Fire Type Question, Basically "j" is the variable for points and i want it to change/decrease (and to check for messages) every 0.5 seconds ... The Problem Is That No Matter How Late I Try I get 1000 points. Also Im New. (btw sorry for bad english)
The Code:
def check(m):
return m.author == message.author and m.channel
mn = randint(5, 15)
mn1 = randint(5, 10)
mz = mn * mn1
membed = discord.Embed(
title="Here's The Question",
description=str(mn) + " * " + str(mn1) + ''' Type Your Answer Below.. ''',
url=None,
color=discord.Color.blue())
mzz = await message.send(embed=membed)
j = 1100
for i in range(0, 5):
sleep(0.5)
j = j - 100
gt = await bot.wait_for('message', check=check)
if int(gt.content) == int(mz):
await message.send(f'Its Right.. You Got **{j}** Points')
else:
await message.send(f'Its Wrong.. The Answer Is **{j}**')
Please Help...
The issue is because you're misunderstanding how await works.
await foo() causes your program to sleep until foo() returns a value.
When you type await bot.wait_for(), you are saying:
Sleep until bot.wait_for() returns a value
bot.wait_for() only returns a value when it receives a message.
The full flow of your program is thus:
You enter the first loop i = 0. j gets set to 1000.
You sleep until you receive a message. Because you can receive a message any time in the future (say, ten seconds or ten minutes or fifteen hours, etc.), you never enter the second loop i = 1. Basically, your program is frozen at this point until somebody sends you a message.
The solution here is to ask your bot to time out waiting for a message, so it can continue to the next value of i. A timeout tells your bot to sleep only for a specified amount of time before continuing your program. Conveniently, the wait_for command provides a timeout parameter:
j = 1100
for i in range(0, 5):
j = j - 100
try: # A try/except block is needed because this throws an error if bot times out
gt = await bot.wait_for('message', check=check, timeout=0.5)
if int(gt.content) == int(mz):
await message.send(f'Its Right.. You Got **{j}** Points')
break
else:
await message.send(f'Its Wrong.. The Answer Is **{j}**')
except:
continue
else:
await message.send("Whoops, you're out of time! You got zero points.")
I am creating a bot that verifies if a member is in our school or not. for some reason, after entering a student number, the bot asks to enter it again instead of sending an email
#client.event
async def on_member_join(member):
await member.send("You have joined the unofficial (school) discord server! To access the rest of the server you must "
"confirm you are from (our school).")
sent_email = False
confirmation_key = generate_key()
while not sent_email:
await member.send('What is your student number? ')
response = await client.wait_for('message')
student_number = response.content
if student_number.isalpha() or student_number.isalnum():
await member.send("Please Enter Your Student Number!")
elif student_number.isnumeric():
send_mail(student_number, confirmation_key)
sent_email = True
await member.send(f'Sending Confirmation Email...')
Any answers would be appreciated!
student_number.isalnum() will always return True if student_number only contains numbers, so you won't ever reach the elif case.
It may make more sense to reorganize your code a bit:
if student_number.isnumeric():
send_mail(student_number, confirmation_key)
sent_email = True
await member.send(f'Sending Confirmation Email...')
else:
await member.send("Please Enter Your Student Number!")
To my understanding you are only interested in two scenarios:
The student number is formatted correctly (only has numbers)
The student number is formatted incorrectly
This way you handle both cases and get rid of the tricky logic with isalnum().
How do you make a bot respond to a message and ask a question, like
Me:!guess 10
bot: pick a number between 1 and 10
me: 4
bot: incorrect, it was 5!
I can't figure out how to do this,
because once you respond to a command, I don't know how to have something like a global variable to save the answer.
This is some code I used, hopefully this will help, this give you 5 guesses to get it right and it gives you hints with bigger and expands on what you wanted, so you could adapt it if you don't want the bigger and smaller!
#commands.command()
async def game(self, ctx):
number = random.randint(0, 100)
for i in range(0, 5):
await ctx.send('guess')
response = await self.bot.wait_for('message')
guess = int(response.content)
if guess > number:
await ctx.send('bigger')
elif guess < number:
await ctx.send('smaller')
else:
await ctx.send('yes!')
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