Zacky
Zacky

Reputation: 67

Bot.wait_for() doesn't work very properly when I wait it for "reaction_remove"

Expected output and what my code for it is:

My bot is supposed to send a message, and then check if somebody reacted on that message with :tada:, and if someone did, it is supposed to give that user a particular role, that part works fine, but I also want it to check if the user removed their reaction, if yes, then remove the role. I put the role remover and role adder into their own async coroutine functions,

        # Listening for reactions
        await participation_message.add_reaction("🎉")
        reaction_check = lambda reaction, user: str(reaction.emoji) == "🎉" and reaction.message.id == participation_message.id # In case you might be wondering, participation_message is a discord.Message that I send before this code block

        async def remove_participants_loop():
            while True:
                try:
                    reaction, user = await self.client.wait_for('reaction_remove', timeout=60, check=reaction_check)
                    try:
                        await user.remove_roles(participant_role)
                    except Exception as e:
                        console_log("Error in removing participant role from user: {}".format(e), "white", "on_red")
                except TimeoutError:
                    break

        async def add_participants_loop(timeout=delete_after*60):
            while True:
                try:
                    reaction, user = await self.client.wait_for('reaction_add', timeout=60, check=reaction_check)
                    try:
                        await user.add_roles(participant_role)
                    except Exception as e:
                        console_log("Error in adding participant role to user: {}".format(e), "white", "on_red")
                except TimeoutError:
                    break

and I put them into their own coroutines, because I need both of them to run asynchronously, and for that I now do

        asyncio.create_task(add_participants_loop())
        asyncio.create_task(remove_participants_loop())

The problem:

This works for the add_participants_loop() but doesn't work for the remove_participants_loop(), I have tried to debug it using breakpoints, and found that the remove_participants_loop does run properly, but when it waits for "reaction_remove", it doesn't detect it when I remove my reaction, and keeps waiting and eventually raises the asyncio.TimoutError.

I have tried:

Upvotes: 2

Views: 704

Answers (2)

Zacky
Zacky

Reputation: 67

In an issue on the discord.py Github Repository, it's stated that:

this requires the message cache and for the message to be there. It also requires member cache since discord does not provide this event with member data.

and it also states that

If you want to get event without this limitation then use the on_raw_reaction_remove event.

Therefore the reaction_remove event doesn't work because of absent cache. Hence raw_reaction_remove should be used here instead, because it isn't bound to this limitation, and it returns a payload discord.RawReactionActionEvent object, instead of the standard User and Reaction, which can then be used to get the User and the Reaction. Hence the lambda check for that would now be something like:

lambda payload: str(payload.emoji) == "🎉" and payload.message_id == participation_message.id

putting that in the wait_for coroutine,

payload = await self.client.wait_for('raw_reaction_remove', timeout=timeout, check=lambda payload: str(payload.emoji) == "🎉" and payload.message_id == participation_message.id)

# And then the User/Member who removed the Reaction can 
# Be obtained by the user_id in the returned payload
user = self.client.get_guild(payload.guild_id).get_member(payload.user_id)

Summary:

As documented this requires the message cache and for the message to be there. It also requires member cache since discord does not provide this event with member data. If you want to get event without this limitation then use the raw_reaction_remove event.

Upvotes: 1

earningjoker430
earningjoker430

Reputation: 468

I think wait_for can only handle "message" and "reaction_add", not "reaction_remove".

Upvotes: 0

Related Questions