44from contextlib import suppress
55from typing import NamedTuple
66
7- from discord import Colour , Embed , HTTPException , Message , Reaction , User
7+ from discord import ChannelType , Colour , Embed , HTTPException , Message , NotFound , Object , RawReactionActionEvent
88from discord .ext import commands
99from discord .ext .commands import CheckFailure , Cog as DiscordCog , Command , Context
1010from pydis_core .utils .logging import get_logger
@@ -182,33 +182,43 @@ def reset_timeout(self) -> None:
182182 # recreate the timeout task
183183 self ._timeout_task = self ._bot .loop .create_task (self .timeout ())
184184
185- async def on_reaction_add (self , reaction : Reaction , user : User ) -> None :
186- """Event handler for when reactions are added on the help message."""
187- # ensure it was the relevant session message
188- if reaction .message .id != self .message .id :
185+
186+ async def on_raw_reaction_add (self , payload : RawReactionActionEvent ) -> None :
187+ """Handle raw reaction events for DM compatibility."""
188+ # Ignore if not our message
189+ if payload .message_id != self .message .id :
189190 return
190191
191- # ensure it was the session author who reacted
192- if user . id != self .author .id :
192+ # Ignore if not the session author
193+ if payload . user_id != self .author .id :
193194 return
194195
195- emoji = str (reaction .emoji )
196+ # Get the emoji string
197+ emoji = str (payload .emoji )
196198
197- # check if valid action
199+ # Check if valid action
198200 if emoji not in REACTIONS :
199201 return
200202
201203 self .reset_timeout ()
202204
205+ # Remove the reaction to prep for re-use
206+ try :
207+ # We need to get the actual message and user objects for remove_reaction
208+ channel = self ._bot .get_channel (payload .channel_id )
209+ if channel and channel .type != ChannelType .private :
210+ # channel is None in DMs, this condition skips reaction removal in DMs to prevent exceptions
211+ message = channel .get_partial_message (payload .message_id )
212+ await message .remove_reaction (payload .emoji , Object (id = payload .user_id ))
213+ except (HTTPException , NotFound ):
214+ # Ignore errors when removing reactions
215+ pass
216+
203217 # Run relevant action method
204218 action = getattr (self , f"do_{ REACTIONS [emoji ]} " , None )
205219 if action :
206220 await action ()
207221
208- # remove the added reaction to prep for re-use
209- with suppress (HTTPException ):
210- await self .message .remove_reaction (reaction , user )
211-
212222 async def on_message_delete (self , message : Message ) -> None :
213223 """Closes the help session when the help message is deleted."""
214224 if message .id == self .message .id :
@@ -219,7 +229,7 @@ async def prepare(self) -> None:
219229 await self .build_pages ()
220230 await self .update_page ()
221231
222- self ._bot .add_listener (self .on_reaction_add )
232+ self ._bot .add_listener (self .on_raw_reaction_add )
223233 self ._bot .add_listener (self .on_message_delete )
224234
225235 self .add_reactions ()
@@ -459,7 +469,7 @@ async def start(cls, ctx: Context, *command, **options) -> "HelpSession":
459469
460470 async def stop (self ) -> None :
461471 """Stops the help session, removes event listeners and attempts to delete the help message."""
462- self ._bot .remove_listener (self .on_reaction_add )
472+ self ._bot .remove_listener (self .on_raw_reaction_add )
463473 self ._bot .remove_listener (self .on_message_delete )
464474
465475 # ignore if permission issue, or the message doesn't exist
0 commit comments