Reputation: 3602
I have an event where an embed gets multiple reactions. Here the user has x-seconds to select a reaction. If this is "wrong" an error should be spit out. If the reaction is the desired one, defined by me, another message should appear. I have this code, but I can't get any further:
await embedX.add_reaction("1️⃣")
await embedX.add_reaction("2️⃣")
await embedX.add_reaction("3️⃣")
try:
reaction = await self.bot.wait_for('reaction_add', timeout=10)
if str(reaction.emoji) == "1️⃣":
await ctx.send("Congrats, right answer.")
else:
await ctx.send("Incorrect reaction.")
except asyncio.TimeoutError:
await ctx.send("Took too long.")
Error:
AttributeError: 'tuple' object has no attribute 'emoji'.
I'm still realtively new to Python, so I hope to find help here.
After I got help my code now looks like the following:
e = discord.Embed(color=discord.Color.gold())
e.title = "Woah, look!"
e.description = "**Does this work?**"
e.add_field(name="1️⃣", value="Yes", inline=False)
e.add_field(name="2️⃣", value="No", inline=False)
e.add_field(name="3️⃣", value="Maybe", inline=False)
e.set_footer(text="You have 10 seconds to answer the question", icon_url=self.bot.user.avatar_url)
e.timestamp = datetime.datetime.utcnow()
embedX = await ctx.send(embed=e)
await embedX.add_reaction("1️⃣")
await embedX.add_reaction("2️⃣")
await embedX.add_reaction("3️⃣")
try:
reaction, user = await self.bot.wait_for('reaction_add', timeout=10)
if user == self.bot.user:
return
if str(reaction.emoji) == "1️⃣":
await embedX.edit(embed=er)
else:
await ctx.send(embed=ew)
except asyncio.TimeoutError:
await embedX.edit(embed=etl)
Sometimes the bot counts the reaction, just 1
- to the others he does not react, but what he is not doing is: Giving out/Editing the embed if the reaction is wrong.
(er
and ew
are actually other embeds I defined before.)
Upvotes: 4
Views: 385
Reputation: 528
Your error message should give you a hint: you're receiving a tuple
from self.bot.wait_for
and not a discord.Reaction
.
When using Client.wait_for
, keep in mind what it returns (emphasis mine):
Returns no arguments, a single argument, or a tuple of multiple arguments that mirrors the parameters passed in the event reference.
Looking at the on_reaction_add
event, we can see that this event's payload is a discord.Reaction
and a discord.User
object. When using wait_for(<event>)
, you get the same payload as if you were to extend on_<event>
and handle it that way.
So putting it all together, this line:
await self.bot.wait_for('reaction_add', timeout=10)
Returns a tuple
with a Reaction
and a User
, e.g. (<Reaction ...>, <User ...>)
. So you just need to make a slight modification:
reaction, user = await self.bot.wait_for('reaction_add', timeout=10)
Here we use tuple unpacking (or multiple assignment, if you prefer) to extract the desired values from the returned tuple.
Since you'll receive all events, including those caused by your bot, you'll need to make sure that you ignore the event when it's caused by your bot to avoid a feedback loop. You can trivially guard against this in your event handlers with the following:
# Assuming `self` is an instance of `discord.Client`
if user == self.user: # `user` here comes from the event payload
return
As a concrete example for your situation:
reaction, user = await self.bot.wait_for('reaction_add', timeout=10)
if user == self.bot.user:
return
Upvotes: 2