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
Related
I'm creating a mod-mail feature which members can message the bot and it will respond with instructions. However the event works fine with the exception of bot commands. Here is my code. Strangely no errors were detected.
sent_users = []
modmail_channel = client.get_channel(910023874727542794)
#client.event
async def on_message(message):
if message.guild:
return
if message.author == client.user:
return
if message.author.id in sent_users:
return
embed = discord.Embed(color = color)
embed.set_author(name=f"Misaland Modmail System", icon_url=f'{message.author.avatar_url}')
embed.add_field(name='Report a Member: ', value=f"React with <:Wojak1:917122152078147615> if you would like to report a member.")
embed.add_field(name='Report a Staff Member:', value=f"React with <:grim1:925758467099222066> if you would like to report a staff.")
embed.add_field(name='Warn Appeal:', value=f"React with <:Lovecat:919055184125100102> if you would like to appeal a warning.")
embed.add_field(name='Question:', value=f"React with <:gasm:917112456776679575> if you have a question about the server")
embed.add_field(name='Leave a Review', value=f"React with <:surebuddy1:917122193287163924> to leave a review about the server")
embed.add_field(name='Server Invite', value=f'React with <:smirk:910565317363773511> to get the server invite.')
embed.set_footer(text='Any questions asked that isnt related to the list, you will be severely abused')
msg = await message.author.send(embed=embed)
await msg.add_reaction("<:Wojak1:917122152078147615>")
await msg.add_reaction("<:grim1:925758467099222066>")
await msg.add_reaction("<:Lovecat:919055184125100102>")
await msg.add_reaction("<:gasm:917112456776679575>")
await msg.add_reaction("<:surebuddy1:917122193287163924>")
await msg.add_reaction("<:smirk:910565317363773511>")
sent_users.append(message.author.id)
try:
def check(reaction, user):
return user == message.author and str(reaction.emoji) in ['<:Wojak1:917122152078147615>', '<:grim1:925758467099222066>','<:Lovecat:919055184125100102>','<:gasm:917112456776679575>','<:surebuddy1:917122193287163924>','<:smirk:910565317363773511>']
reaction, user = await client.wait_for("reaction_add", timeout=60, check=check)
if str(reaction.emoji) == "<:Wojak1:917122152078147615>":
embed = discord.Embed(color=color)
embed.set_author(name=f"Misaland Member Report", icon_url=f'{message.author.avatar_url}')
embed.add_field(name="How to Report:", value="Send the ID of the person you are reporting and attach a screenshot breaking the rules")
embed.set_footer(text="Misaland | Member Report")
await message.author.send(embed = embed)
message = await client.wait_for("message", timeout=60, check=lambda m: m.channel == message.channel and m.author == message.author)
embed = discord.Embed(title=f"{message.content}", color=color)
await modmail_channel.send(embed=embed)
except asyncio.TimeoutError:
await message.delete()
Try putting
await bot.process_commands(message)
at the end of your code in
async def on_message(message):
If you're using commands outside of on_message, it's overridden
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)
I am currently working on a kick command that does a double check with the user before carrying out action for my discord bot, and came up with this:
#bot.command()
#commands.has_permissions(manage_guild=True)
async def kick(ctx,
member: discord.Member = None,
*,
reason="No reason provided"):
server_name = ctx.guild.name
user = member
if member == None:
await ctx.send(
f'{x_mark} **{ctx.message.author.name},** please mention somebody to kick.')
return
if member == ctx.message.author:
await ctx.send(
f'{x_mark} **{ctx.message.author.name},** you can\'t kick yourself, silly.')
return
embedcheck = discord.Embed(
title="Kick",
colour=0xFFD166,
description=f'Are you sure you want to kick **{user}?**')
embeddone = discord.Embed(
title="Kicked",
colour=0x06D6A0,
description=f'**{user}** has been kicked from the server.')
embedfail = discord.Embed(
title="Not Kicked",
colour=0xEF476F,
description=f'The kick did not carry out.')
msg = await ctx.send(embed=embedcheck)
await msg.add_reaction(check_mark)
await msg.add_reaction(x_mark)
def check(rctn, user):
return user.id == ctx.author.id and str(rctn) in [check_mark, x_mark]
while True:
try:
reaction, user = await bot.wait_for(
'reaction_add', timeout=60.0, check=check)
if str(reaction.emoji) == check_mark:
await msg.edit(embed=embeddone)
await user.kick(reason=reason)
if reason == None:
await user.send(
f'**{user.name}**, you were kicked from {server_name}. No reason was provided.'
)
else:
await user.send(
f'**{user.name}**, you were kicked from {server_name} for {reason}.'
)
return
elif str(reaction.emoji) == x_mark:
await msg.edit(embed=embedfail)
return
except asyncio.TimeoutError:
await msg.edit(embed=embedfail)
return
However when I do this, I get the error:
raise CommandInvokeError(exc) from exc
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: Forbidden: 403 Forbidden (error code: 50013): Missing Permissions
I have no clue why this is happening, as the bot has every permission checked, as do I, and I am the server owner. Any help would be appriciated, thank you.
Update: I found the error, when running the command it would try to kick the user of the command instead of the specified member. Here is my updated code:
#commands.command()
#commands.has_permissions(kick_members = True)
#commands.bot_has_permissions(kick_members = True)
async def kick(self, ctx, member: discord.Member, *, reason="No reason provided"):
server_name = ctx.guild.name
if member == ctx.message.author:
await ctx.send(
f'{x_mark} **{ctx.message.author.name},** you can\'t kick yourself, silly.')
return
embedcheck = discord.Embed(
title="Kick",
colour=0xFFD166,
description=f'Are you sure you want to kick **{member}?**')
embeddone = discord.Embed(
title="Kicked",
colour=0x06D6A0,
description=f'**{member}** has been kicked from the server.')
embedfail = discord.Embed(
title="Not Kicked",
colour=0xEF476F,
description=f'The kick did not carry out.')
msg = await ctx.send(embed=embedcheck)
await msg.add_reaction(check_mark)
await msg.add_reaction(x_mark)
def check(rctn, user):
return user.id == ctx.author.id and str(rctn) in [check_mark, x_mark]
while True:
try:
reaction, user = await self.bot.wait_for(
'reaction_add', timeout=60.0, check=check)
if str(reaction.emoji) == check_mark:
await msg.edit(embed=embeddone)
await member.kick(reason=reason)
await member.send(f'**{member.name}**, you were kicked from {server_name} for {reason}.')
return
elif str(reaction.emoji) == x_mark:
await msg.edit(embed=embedfail)
return
except asyncio.TimeoutError:
await msg.edit(embed=embedfail)
return
#kick.error
async def kick_error(self, ctx, error):
if isinstance(error, commands.MissingRequiredArgument):
await ctx.send(f'{x_mark} **{ctx.message.author.name}**, you need to mention someone to kick.')
elif isinstance(error, commands.BadArgument):
await ctx.send(f'{x_mark} **{ctx.message.author.name}**, I could not find a user with that name.')
else:
raise error
This issue is that you are checking for manage_guild which is not the correct permission. Also keep in mind you are mixing up bot and commands
#bot.command()
#bot.has_permissions(kick_user = True) # to check the user itself
#bot.bot_has_permissions(kick_user = True) # to check the bot
async def kick(ctx):
Remember to allow all intents like this
intents = discord.Intents().all()
bot = commands.Bot(command_prefix="$", intents=intents)
How can I make the bot choose a right random answer (as a reaction)
And if the user gets it right send a winner message
if its wrong send a loser message.
redcrew = '<:redcrewmates:776867415514153031>'
bluecrew = '<:bluecrewmates:776867439085617153>'
limecrew = '<:limecrewmates:776867489866711041>'
whitecrew = '<:whitecrewmates:776867529900425217>'
await msg1.add_reaction(redcrew)
await msg1.add_reaction(bluecrew)
await msg1.add_reaction(limecrew)
await msg1.add_reaction(whitecrew)
def check(reaction, user):
return user == ctx.author and str(reaction.emoji) ==
try:
reaction, user = await client.wait_for('reaction_add', timeout=60.0, check=check)
except asyncio.TimeoutError:
await ctx.send('Sorry, you took too long to vote!')
else:
await ctx.send('hello')
def setup(bot):
bot.add_cog(games(bot))```
**DONT WORRY ABOUT THE INDENTS**
import random
#bot.command()
async def reaction_game(ctx):
reactions = [
'<:redcrewmates:776867415514153031>',
'<:bluecrewmates:776867439085617153>',
'<:limecrewmates:776867489866711041>',
'<:whitecrewmates:776867529900425217>'
]
winning_reaction = random.choice(reactions)
message = await ctx.send('some message')
for reaction in reactions:
await ctx.add_reaction(reaction)
def check(reaction, user):
return user == ctx.author and str(reaction) == winning_reaction
try:
reaction, user = await bot.wait_for('reaction_add', check=check, timeout=60.0)
await ctx.send('You reacted with the winning emoji')
except asyncio.TimeoutError:
await ctx.send('You took too long to react')
else:
await ctx.send('hello')
def isAdmin(reaction, user):
for i in admins:
if user.id == i: return (str(reaction.emoji) == '👍' or str(reaction.emoji) == '👎')
try: reaction, user = await self.bot.wait_for('reaction_add', timeout=43200, check=isAdmin)
except asyncio.TimeoutError: await msg.edit(embed=embedEnd)
else:
resign_channel = discord.utils.get(ctx.guild.channels, name="resignings")
if str(reaction.emoji) == '👍':
print('{}: {} resigned {} {}.'.format(extCalls.timeNow('datetime'), ctx.channel.category.name, firstName, lastName))
await msg.edit(embed=embedFin)
await resign_channel.send(embed=embedConf)
else: await msg.edit(embed=embedEnd)
This is my code to have the bot wait for a reaction on msg. But, for some reason, when I have two msg (messages) waiting for a reaction at the same time, when I react to one message, both trigger success, even though I didn't react to the second message. This is hugely problematic for an admin-based approval system... does anyone know why this is happening or how to fix it?
This code is really messy, but if you're searching for a way to wait only for a reaction on one message you should add stuff to your check command, for example:
#bot.command(name="some_command")
async def sc(ctx):
msg = await ctx.send("Some interesting message!")
def check(reaction, user):
return reaction.message.id == msg.id
try:
reaction, user = await bot.wait_for("reaction_add", check=check,timeout=30.0)
except asyncio.TimeoutError:
await ctx.send("timeout")
return
await ctx.send(f"You reacted {str(reaction)}")