How do you do it, that if someone uses a command, a timer starts and does something if a user answers in a in the given time?
Like someone does "!shoot #user#0000" and the tagged user has to answer with "!dodge" in under 10 seconds or he dies
You could use the wait_for method:
#client.command(name = "shoot")
async def shoot_user(ctx, user: discord.Member):
channel = ctx.channel
def check(m):
return m.content == '!dodge' and m.channel == channel and m.author == user
try:
await client.wait_for('message', check=check, timeout = 10.0)
except asyncio.TimeoutError:
await channel.send(f"{user.mention} did not dodge in time!")
else:
await channel.send(f"{user.mention} dodged!")
Related
I'm quite new to both Python and Discord.py, and I'm trying to find how to make the bot wait for either a message or reaction from user at the same time.
I tried separating each but just resulted in the bot needing a message response before a reaction.
Here's a similar code I'm trying to do:
#bot.event
async def on_message(ctx):
if ctx.author == bot.user:
return
if ctx.content == "$respond":
message = await ctx.send("Waiting for response...")
while True:
try:
response = #Will wait for a message(Yes or No) or reaction(Check or Cross) from ctx.author for 30 secs
except asyncio.TimeoutError:
await message.edit(content=f"{ctx.author} did not respond anymore!")
break
if response.content == "yes":
await message.edit(content=f"{ctx.author} said yes!")
continue
elif response.content == "no":
await message.edit(content=f"{ctx.author} said nyo!")
continue
#These are reactions
elif str(response.emoji) == "✅":
await message.edit(content=f"ctx.author reacted ✅!")
continue
elif str(response.emoji) == "❌":
await messave.edit(content=f"Stopped listening to responses.")
break
bot.wait_for is what you're looking for here.
I would recommend using bot.command() for command handling purposes but this is fine as well.
This is how you wait for specific events (provided as the first argument) with specific conditions (as provided in the check param)
#bot.event
async def on_message(msg):
if msg.author == bot.user:
# if the author is the bot itself we just return to prevent recursive behaviour
return
if msg.content == "$response":
sent_message = await msg.channel.send("Waiting for response...")
res = await bot.wait_for(
"message",
check=lambda x: x.channel.id == msg.channel.id
and msg.author.id == x.author.id
and x.content.lower() == "yes"
or x.content.lower() == "no",
timeout=None,
)
if res.content.lower() == "yes":
await sent_message.edit(content=f"{msg.author} said yes!")
else:
await sent_message.edit(content=f"{msg.author} said no!")
This would result in :
Listening for multiple events is a rather a bit interesting,
Replace the existing wait_for with this snippet :
done, pending = await asyncio.wait([
bot.loop.create_task(bot.wait_for('message')),
bot.loop.create_task(bot.wait_for('reaction_add'))
], return_when=asyncio.FIRST_COMPLETED)
and you can listen for two events simultaneously
Here's how you can handle this using #bot.command()
import discord
import os
from discord.ext import commands
bot = commands.Bot(command_prefix="$", case_insensitive=True)
#bot.event
async def on_ready():
print(f"Logged in as {bot.user}")
#bot.command()
async def response(ctx):
sent_message = await ctx.channel.send("Waiting for response...")
res = await bot.wait_for(
"message",
check=lambda x: x.channel.id == ctx.channel.id
and ctx.author.id == x.author.id
and x.content.lower() == "yes"
or x.content.lower() == "no",
timeout=None,
)
if res.content.lower() == "yes":
await sent_message.edit(content=f"{ctx.author} said yes!")
else:
await sent_message.edit(content=f"{ctx.author} said no!")
bot.run(os.getenv("DISCORD_TOKEN"))
which would get you the same result.
Trying to make it so when someone reacts to the message with the emoji it repeats the function - trying to find something that works similar to goto in batch files
Eg: I type $randomcar
Bot sends: Your chosen car is the bmw! with the 🔄 reaction. When I press on it it repeats the function again:
Bot sends Your chosen car is the audi! with the 🔄 reaction which I can press on to once again to repeat the function
#bot.command()
async def randomcar(ctx):
while True:
msg = await ctx.send("Your chosen car is the {}!".format(random.choice(cars)))
await msg.add_reaction("🔄")
async def on_raw_reaction_add(payload: discord.RawReactionActionEvent):
if msg.reaction=="🔄" and msg.id:
continue
I am getting the error:
Syntax error: continue not properly in loop
Thanks
This can be done using recursion:
#bot.command()
async def randomcar(ctx):
msg = await ctx.send("Your chosen car is the {}!".format(random.choice(cars)))
# you may need to check if your bot has the "add reactions" permission
await msg.add_reaction("🔄")
await bot.wait_for(
"reaction_add",
check=lambda reaction, user: user == ctx.author and reaction.emoji == "🔄" and reaction.message == msg,
)
await randomcar(ctx)
You could also add a timeout after which it will stop repeating:
#bot.command()
async def randomcar(ctx):
msg = await ctx.send("Your chosen car is the {}!".format(random.choice(cars)))
await msg.add_reaction("🔄")
try:
await bot.wait_for(
"reaction_add",
timeout=10.0, # 10 second timeout
check=lambda reaction, user: user == ctx.author and reaction.emoji == "🔄" and reaction.message == msg,
)
except asyncio.TimeoutError:
try:
await msg.remove_reaction("🔄", bot.user)
except discord.NotFound:
pass
else:
await randomcar(ctx)
def check(m):
return m.author == ctx.author and ctx.channel == m.channel
for index, i in enumerate(txtare):
await ctx.send(f'What is the {txtare[index]}')
try:
variable = await client.wait_for('message',timeout=time_out, check=check)
if variable.content == '!end':
print(variable.content)
return
draw.text((x_axis[index], y_axis[index]),codecs.decode(str(variable.content), "unicode_escape"),(0,0,0),font=font2)
except asyncio.TimeoutError:
await ctx.send("Time out!")
return
why isn't it stopping neither recognizing that I wrote !end
So to explain 'variable' is what the discord user sends the bot. then I want to check the variable.content (the content of the message) and if it says !end the command should stop
so right now I'm trying to create a type of application bot and right now the check I used for the bot doesn't allow the user to move onto the next question. There is no traceback so it's something with the check I defined.
#commands.command()
#commands.has_role("Realm OP")
async def block(self, ctx, user: discord.User):
DMchannel = await ctx.author.create_dm()
channel = ctx.message.channel
author = ctx.message.author
mentions = [role.mention for role in ctx.message.author.roles if role.mentionable]
channel2 = str(channel.name)
channel = channel2.split('-')
if len(channel2) == 2: # #real-emoji
realm, emoji = channel
else: # #realm-name-emoji
realm, emoji = channel[0], channel[-1]
realmName = realm.replace("-" , " ")
realmName1 = realmName.lower()
rolelist = []
print(realmName1)
a = solve(realmName1)
print(a)
realmName2 = str(a) + " OP"
print(realmName2)
check_role = discord.utils.get(ctx.guild.roles, name= realmName2)
if check_role not in ctx.author.roles:
return await ctx.send('You do not own this channel')
else:
await ctx.send("You own this channel!")
def check(m):
return m.channel == channel and m.author != self.bot.user
await DMchannel.send("Please fill out the questions in order to block the user!")
await DMchannel.send("User's Gamertag: (If you don't know, try using the >search command to see if they have any previous records!) ")
blocka1 = await self.bot.wait_for('message', check=check)
await DMchannel.send("Reason for block:")
blocka2 = await self.bot.wait_for('message', check=check)
Right now the bot doesn't do anything after the user responds to the first question and there is no traceback.
Any help or suggestions would help greatly!
Your check isn't returning anything. It's supposed to return either True or False. This means your wait_for will never end (as the check never returns True), so it will never ask the next question.
def check(m):
return m.channel == channel and m.author != self.bot.user
EDIT:
Your channel is the ctx.channel, while your bot waits for answers in DM. Assuming you answer your bot in DM as well, this means your check will never pass, as m.channel never equals channel.
def check(m):
return m.guild is None and m.author == ctx.author
This checks if a message was sent in DM (Guild is None), and if it was a DM by the original author.
Assuming a command similar to this:
#bot.command()
async def test(ctx):
def check(r, u):
return u == ctx.message.author and r.message.channel == ctx.message.channel and str(r.emoji) == '✅'
await ctx.send("React to this with ✅")
try:
reaction, user = await bot.wait_for('reaction_add', timeout=300.0, check=check)
except asyncio.TimeoutError:
await ctx.send('Timeout')
else:
await ctx.send('Cool, thanks!')
Is there any way to cancel that wait_for if the user sends the same command multiple times before actually reacting to the message? So the bot stop waiting for reactions on previously sent messages and only waits for the last one.
Would something like this work for you?
pending_tasks = dict()
async def test(ctx):
def check(r, u):
return u == ctx.message.author and r.message.channel == ctx.message.channel and str(r.emoji) == '✅'
await ctx.send("React to this with ✅")
try:
if ctx.message.author in pending_tasks:
pending_tasks[ctx.message.author].close()
pending_tasks[ctx.message.author] = bot.wait_for('reaction_add', timeout=300.0, check=check)
reaction, user = await pending_tasks[ctx.message.author]
except asyncio.TimeoutError:
await ctx.send('Timeout')
else:
await ctx.send('Cool, thanks!')
You store all of the pending requests in a dict, and before creating another request you check if you are already have an existing task for this user, if you do you cancel it and create a new one