I'm planning to make a bot that got Name and Birthday from User input, and before storing the user's answer to database (in my case, Google Spreadsheet via gspread), Ask the user that input is correct.
So I decided to show an react emojis bottom the Checking embed, and If user reacts with ⭕, It will append the User's input to Google Spreadsheet. in the other hand, if user reacts with ❌ one, It discard the user input and Showing Error Message.
The bot works.... only the Checking Embed. how nice.
and yes, bot deployed with no error, and If I use slash command and input the Data, the Bot prints Checking Embed and React Emojis well. (Look at the Picture for result.)
But It didn't react when I responded emoji. both of all not work.
here's my code about that command.
#slash.slash(
name="테스트쓰기",
description="샘플 데이터를 구글 시트에 작성합니다.",
guild_ids=[865433__________],
options=[
create_option(
name="이름",
description="스프레드시트에 표시될 이름",
option_type=3,
required=True
),
create_option(
name="생일",
description="MM-DD 형식으로 입력",
option_type=3,
required=True
)
],
connector={
'이름': 'uname',
'생일': 'btday'
}
)
async def ap_sheet(ctx:SlashContext, uname:str, btday:str):
checker = await ctx.send(embed=check_emb(ctx, uname=uname, btday=btday))
await checker.add_reaction("⭕")
await checker.add_reaction("❌")
async def check(reaction, user):
return user == ctx.user and reaction.message.id == checker.id and str(reaction.emoji) in ['⭕','❌']
while True :
try:
reaction, user = await bot.wait_for("reaction_add", check=check, wait_for=20.0) #Wait 20sec for user input
if str(reaction.emoji) == '⭕': #User Accepts and wants to append data to sheet
await checker.delete()
await ctx.send(discord.Embed(title="완료", description="입력한 정보를 내보냈습니다.", color=discord.Color.blue()), delete_after=5)
gc1.append_row([uname, ctx.author.id, btday])
elif str(reaction.emoji) == '❌': #User Cancels command
await checker.delete()
await ctx.send(discord.Embed(title="취소됨!", description="사용자의 조작에 의해 취소되었어요.", color=discord.Color.red()), delete_after=5)
pass
except asyncio.TimeoutError: #Time is Over
await ctx.send("시간 초과되었습니다.")
This is the proper way to call a check, it doesnt look like yours is ever called
answers = ["⭕","❌"]
for reaction in answers:
await ctx.message.add_reaction(emoji=reaction)
def check(reaction, user):
if str(reaction.emoji) in answers and user.id == ctx.author.id:
return user == ctx.author and str(reaction.emoji) == str(reaction.emoji)
try:
reaction, user = await bot.wait_for('reaction_add', timeout=20.0, check=check)
if(str(reaction)=="❌"):
#Deny code here, I would recommend deleting the prompt
if(str(reaction)=="⭕"):
#Confirm code here
except:
#Code will go here if there is a timeout. I would recommend deleting the prompt
Oh, you have an indentation mistake in your while loop. Your while loop is inside your reaction check function.
Related
I'm writing a simple script that exec if a simplified string is written in a channel, after that the user message gets deleted.
Or if the user send a message that is not one of the strings it to be deleted too.
However, even the bot message gets deleted too which are theses
await message.channel.send(badr.mention)
await message.channel.send(expired)
and
await message.channel.send(author.mention)
await message.channel.send(WL_message)
which is not what I'm aiming for I want only the member's message to get deleted not the bots.
if (message.channel.id == 897508930744381491):
author = message.author
content = message.content
expire = '||!WL xdxdxd||', '||!WL 432-234-4312-fas2||'
Valied= '||!WL Furball2244||', '||!WL 432-234-4www312-32242||', 'Furball2244', '!WL Furball2244'
if message.content.startswith(expire):
await message.delete()
badr = author
expired= "This code is expired, contact an admin in <#899289942897860659>, there is a chance they can add you manually"
await message.channel.send(badr.mention)
await message.channel.send(expired)
if message.content.startswith(Valied):
ROLE = "Role55555"
WL_message='Congratulations, now you have access to <#930074601436905522>, you enter your information there.'
await message.channel.send(author.mention)
await message.channel.send(WL_message)
role = get(message.guild.roles, name=ROLE)
await author.add_roles(role)
else: await message.delete()
Is there a trick around it?
To my understanding, you want to delete the message the user sends, in which you would use
await ctx.message.delete()
You need to wait for author to send message
def check(m):
return message.author == oldmsg.author #To make sure it is the only message author is getting
msg = await self.bot.wait_for('message', timeout=60.0, check=check)
if message.content == "your codes":
# do stuff
await message.delete()
This way you can delete the next message from the user right away. make sure you save the command author in a global dictionary or something!
elif message.author.id == 930374951935021096:
return
else:
await message.delete()
seems like just adding an elif solves the problem, regardless thank you for your reply.
I started reworking my mod-mail bot and wanted to give the user a choice on what server they want to contact. Since there are 2 server the bot reactions with 2 reactions and based on what the users reacts to, it contacts that server.
But I ran into a problem. When trying to do this. I couldn't find a way to have like an if else function, where if the users reacts to one of the reactions added, it contacts that server.
How can I make it so where if the user reactions to the green reaction the bot does something else than if the user reacted to the red reaction.
Here is my code:
def green_check(reaction, user):
return str(reaction.emoji) == '🟢' and user != bot.user
try:
reaction, user = await bot.wait_for('reaction_add', timeout=3600.0, check=green_check)
except asyncio.TimeoutError:
await concept_msg.delete()
await ctx.author.send("No moderator responded, wait some time and try again.")
return
else:
concept_embed_dict = concept_embed.to_dict()
In your check you can see if the emoji is either one of the two things you want, and then later check which one it is and execute the code that you want.
Something like this maybe?
def green_check(reaction, user):
return str(reaction.emoji) in ['🟢', '🔴'] and user != bot.user
try:
reaction, user = await bot.wait_for('reaction_add', timeout=3600.0, check=green_check)
except asyncio.TimeoutError:
await concept_msg.delete()
await ctx.author.send("No moderator responded, wait some time and try again.")
return
else:
if str(reaction.emoji) == '🟢':
#do something
elif str(reaction.emoji) == '🔴':
#do something else
else:
pass
I have an event waiting for a reaction under a message. But if the bot should fail once this event is dropped and if you then click on the reaction there is no reaction from the bot, because he can not capture this reaction. Is there an alternative how to get the bot to continue anyway?
I don't like to work with a timeout event or asyncio.sleep, because the input should come from the user. Is this possible?
My code so far without workaround:
try:
reaction, user = await self.bot.wait_for('reaction_add', timeout=43200)
while user == self.bot.user:
reaction, user = await self.bot.wait_for('reaction_add', timeout=43200)
if str(reaction.emoji) == "⏯":
await ctx.send("**Your next question will appear in `6` seconds.**", delete_after=5)
await intense.delete()
except asyncio.TimeoutError:
await ctx.send("**Really?! I though `12 hours` would be enough..."
"Your next question will come in about `6` seconds.**", delete_after=5)
await asyncio.sleep(6)
Below I've added two examples and tweaked up existing code to make it a little more cleaner. It cannot fail unless the message that the bot was adding to was deleted, otherwise it would work. I also added a check function just to help, but you can change that to whatever you want. Included a true loop, so it would keep waiting for an input.
message = await ctx.send("Test reactions")
back = '⬅️'
forward= '⏯'
await message.add_reaction(back)
await message.add_reaction(forward)
def check(reaction, user):
return user == ctx.author and str(reaction.emoji) in [back, forward]
member = ctx.author
while True:
try:
reaction, user = await client.wait_for("reaction_add", check=check)
if str(reaction.emoji) == back:
await ctx.send('Going back')
if str(reaction.emoji) == forward:
await ctx.send('Going forward')
Your other solution is just to use on_reaction_add event, but I wouldn't recommend, unless your bot is small and no one minds if a message always gets send if they sent an emoji you put in your code.
It's by far a much easier approach but may not expect the best results compared to the wait_for But hope this helps too.
#client.event
async def on_reaction_add(reaction, user):
if reaction.emoji == '⬅️':
await ctx.send('Going back')
if reaction.emoji == '⏯':
await ctx.send('Going back')
The problem with this, is it's always active and waiting for those reactions to be added, it can cause intermittent slowdown of the bot, or people could react to the emojis by accident and mess up the command because they shouldn't be able to use these commands unless the original command has been invoked.
I believe raw_reaction_add should work in this context. Eveytime a user will interact with the message reaction it causes an event
message=await ctx.send(f"{ctx.author.mention}, after reacting to an emoji below the message will be edited to your choosen category)
await message.add_reaction('📜')
await message.add_reaction('🔧')
await message.add_reaction('🔨')
await message.add_reaction('🔥')
message = await client.wait_for('on_reaction_add', timeout= 10, check=lambda message: message.author == ctx.author)
if message.reaction_add == '📜':
await ctx.send("You reacted!")
Im making a command to use the help page and it edits the pre-existing help message to the category the user chooses that corresponds to the emoji they reacted for.
I have used a way that has no checks which annoys other people if they happen to react with those emojis to another message, is there are way to make a check similar to what I have already done or close to, where the message of help can be edited to the page the user wants and only works if they used the command specifically for the author that can use the emojis?
To condense this a little bit, I will use only two of the provided reactions.
#client.command()
async def test(ctx):
message = await ctx.send("Message that's getting reactions")
paper = '📜' # for the sake of "I'd rather not copy and paste these emojis a lot"
hammer = '🔨'
await message.add_reaction(paper)
await message.add_reaction(hammer)
def check(reaction, user):#checking for reactions and user
return user == ctx.author and str(reaction.emoji) in [paper, hammer]
# in this case, the message author and the reaction emojis paper and hammer
try:
reaction, user = await client.wait_for("reaction_add", timeout = 3, check=check)
# waiting for a reaction to be added
if str(reaction.emoji) == paper:
await ctx.send("You reacted!")
if str(reaction.emoji) == hammer:
await message.edit(content="Another reaction!")# this is for editing the message sent
except: # Timeout error-handling
await message.edit(content="Run command to use again!")
If you'd like, you could also include while True so the user doesn't have to constantly execute the command.
Working test as seen in the image below:
References:
discord.Client.wait_for
discord.Message.edit
How to loop with a reaction embed menu (SO)
reaction, user = await client.wait_for('reaction_add', timeout=10.0, check...)
If you read the docs about on_reaction_add, you'll see that it takes reaction and user arguments.
To edit the message:
message = reaction.message
await message.edit(content='new content!')
So I am trying to make my personal "splashbot" for my discord server. This is one of the commands that it should do. The problem is that I don't know how to check if the player choose one of the reactions. I tried some things out and here is the code:
#client.command(aliases=['paidsplash'])
async def splash(ctx):
message = await ctx.send('Are you sure you want to do a paid splash?')
emoji1 = '✅'
await message.add_reaction(emoji1)
emoji2 = '❌'
await message.add_reaction(emoji2)
await ctx.message.delete()
client.wait_for("reaction.users()=='✅'", timeout=10.0) #reaction.users()?
await ctx.send('yes')
Basically the player types '*splash', the bots removes that and asks the question + adds those 2 reactions on it. Then the player must pick a reaction. They will be added to a queue if ✅ but I haven't come to that part...
Preferable just copy and edit to code in your answer, so I can see what is wrong, I learn the fastest that way
The client.wait_for code is incorrect. You're supposed to define a check function first, then pass that through to the wait_for function so that it can check for a reaction.
This is some sample code from the discord.py documentation (https://discordpy.readthedocs.io/en/latest/api.html#discord.Client.wait_for) edited to work for your situation. Essentially, it's just the same thing as the example code but waits for a reaction not done by the bot (or the author of the message in the code)
def check(reaction, user):
return user != message.author and str(reaction.emoji) == '👍'
try:
reaction, user = await client.wait_for('reaction_add', timeout=60.0, check=check)
except asyncio.TimeoutError:
await channel.send('👎')
else:
await channel.send('👍')