aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRapptz <[email protected]>2020-01-17 19:48:46 -0500
committerRapptz <[email protected]>2020-01-17 19:53:28 -0500
commit87f9dcff9c5af9c4fa6e6b148663320522a3c82f (patch)
tree2a46dc76aafa9e10ad134c681705684bdb4cf29c
parentAdd support for on_invite_create and on_invite_delete (diff)
downloaddiscord.py-87f9dcff9c5af9c4fa6e6b148663320522a3c82f.tar.xz
discord.py-87f9dcff9c5af9c4fa6e6b148663320522a3c82f.zip
Add support for clearing a specific reaction.
Closes #2440
-rw-r--r--discord/http.py7
-rw-r--r--discord/message.py43
-rw-r--r--discord/raw_models.py29
-rw-r--r--discord/reaction.py22
-rw-r--r--discord/state.py17
-rw-r--r--docs/api.rst40
6 files changed, 152 insertions, 6 deletions
diff --git a/discord/http.py b/discord/http.py
index 0c0a5497..3789b3c8 100644
--- a/discord/http.py
+++ b/discord/http.py
@@ -402,6 +402,11 @@ class HTTPClient:
return self.request(r)
+ def clear_single_reaction(self, channel_id, message_id, emoji):
+ r = Route('DELETE', '/channels/{channel_id}/messages/{message_id}/reactions/{emoji}',
+ channel_id=channel_id, message_id=message_id, emoji=emoji)
+ return self.request(r)
+
def get_message(self, channel_id, message_id):
r = Route('GET', '/channels/{channel_id}/messages/{message_id}', channel_id=channel_id, message_id=message_id)
return self.request(r)
@@ -425,7 +430,7 @@ class HTTPClient:
return self.request(Route('GET', '/channels/{channel_id}/messages', channel_id=channel_id), params=params)
def publish_message(self, channel_id, message_id):
- return self.request(Route('POST', '/channels/{channel_id}/messages/{message_id}/crosspost',
+ return self.request(Route('POST', '/channels/{channel_id}/messages/{message_id}/crosspost',
channel_id=channel_id, message_id=message_id))
def pin_message(self, channel_id, message_id):
diff --git a/discord/message.py b/discord/message.py
index e2ae7193..4010a2c3 100644
--- a/discord/message.py
+++ b/discord/message.py
@@ -371,6 +371,18 @@ class Message:
return reaction
+ def _clear_emoji(self, emoji):
+ to_check = str(emoji)
+ for index, reaction in enumerate(self.reactions):
+ if str(reaction.emoji) == to_check:
+ break
+ else:
+ # didn't find anything so just return
+ return
+
+ del self.reactions[index]
+ return reaction
+
def _update(self, data):
handlers = self._HANDLERS
for key, value in data.items():
@@ -945,6 +957,37 @@ class Message:
else:
await self._state.http.remove_reaction(self.channel.id, self.id, emoji, member.id)
+ async def clear_reaction(self, emoji):
+ """|coro|
+
+ Clears a specific reaction from the message.
+
+ The emoji may be a unicode emoji or a custom guild :class:`Emoji`.
+
+ You need the :attr:`~Permissions.manage_messages` permission to use this.
+
+ .. versionadded:: 1.3
+
+ Parameters
+ -----------
+ emoji: Union[:class:`Emoji`, :class:`Reaction`, :class:`PartialEmoji`, :class:`str`]
+ The emoji to clear.
+
+ Raises
+ --------
+ HTTPException
+ Clearing the reaction failed.
+ Forbidden
+ You do not have the proper permissions to clear the reaction.
+ NotFound
+ The emoji you specified was not found.
+ InvalidArgument
+ The emoji parameter is invalid.
+ """
+
+ emoji = self._emoji_reaction(emoji)
+ await self._state.http.clear_single_reaction(self.channel.id, self.id, emoji)
+
@staticmethod
def _emoji_reaction(emoji):
if isinstance(emoji, Reaction):
diff --git a/discord/raw_models.py b/discord/raw_models.py
index 1f318564..dc094e34 100644
--- a/discord/raw_models.py
+++ b/discord/raw_models.py
@@ -177,3 +177,32 @@ class RawReactionClearEvent(_RawReprMixin):
self.guild_id = int(data['guild_id'])
except KeyError:
self.guild_id = None
+
+class RawReactionClearEmojiEvent(_RawReprMixin):
+ """Represents the payload for a :func:`on_raw_reaction_clear_emoji` event.
+
+ .. versionadded:: 1.3.0
+
+ Attributes
+ -----------
+ message_id: :class:`int`
+ The message ID that got its reactions cleared.
+ channel_id: :class:`int`
+ The channel ID where the reactions got cleared.
+ guild_id: Optional[:class:`int`]
+ The guild ID where the reactions got cleared.
+ emoji: :class:`PartialEmoji`
+ The custom or unicode emoji being removed.
+ """
+
+ __slots__ = ('message_id', 'channel_id', 'guild_id', 'emoji')
+
+ def __init__(self, data, emoji):
+ self.emoji = emoji
+ self.message_id = int(data['message_id'])
+ self.channel_id = int(data['channel_id'])
+
+ try:
+ self.guild_id = int(data['guild_id'])
+ except KeyError:
+ self.guild_id = None
diff --git a/discord/reaction.py b/discord/reaction.py
index c7f2ef77..79c8efe2 100644
--- a/discord/reaction.py
+++ b/discord/reaction.py
@@ -121,6 +121,28 @@ class Reaction:
await self.message.remove_reaction(self.emoji, user)
+ async def clear(self):
+ """|coro|
+
+ Clears this reaction from the message.
+
+ You need the :attr:`~Permissions.manage_messages` permission to use this.
+
+ .. versionadded:: 1.3
+
+ Raises
+ --------
+ HTTPException
+ Clearing the reaction failed.
+ Forbidden
+ You do not have the proper permissions to clear the reaction.
+ NotFound
+ The emoji you specified was not found.
+ InvalidArgument
+ The emoji parameter is invalid.
+ """
+ await self.message.clear_reaction(self.emoji)
+
def users(self, limit=None, after=None):
"""Returns an :class:`AsyncIterator` representing the users that have reacted to the message.
diff --git a/discord/state.py b/discord/state.py
index aea0984d..a3a8e243 100644
--- a/discord/state.py
+++ b/discord/state.py
@@ -509,6 +509,23 @@ class ConnectionState:
if user:
self.dispatch('reaction_remove', reaction, user)
+ def parse_message_reaction_remove_emoji(self, data):
+ emoji = data['emoji']
+ emoji_id = utils._get_as_snowflake(emoji, 'id')
+ emoji = PartialEmoji.with_state(self, animated=emoji.get('animated', False), id=emoji_id, name=emoji['name'])
+ raw = RawReactionClearEmojiEvent(data, emoji)
+ self.dispatch('raw_reaction_clear_emoji', raw)
+
+ message = self._get_message(raw.message_id)
+ if message is not None:
+ try:
+ reaction = message._clear_emoji(emoji)
+ except (AttributeError, ValueError): # eventual consistency lol
+ pass
+ else:
+ if reaction:
+ self.dispatch('reaction_clear_emoji', reaction)
+
def parse_presence_update(self, data):
guild_id = utils._get_as_snowflake(data, 'guild_id')
guild = self._get_guild(guild_id)
diff --git a/docs/api.rst b/docs/api.rst
index 8c22e0bc..e4302843 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -250,7 +250,8 @@ to handle it, which defaults to print a traceback and ignoring the exception.
Messages might not be in cache if the message is too old
or the client is participating in high traffic guilds.
- If this occurs increase the :attr:`Client.max_messages` attribute.
+ If this occurs increase the :attr:`Client.max_messages` attribute
+ or use the :func:`on_raw_message_delete` event instead.
:param message: The deleted message.
:type message: :class:`Message`
@@ -264,7 +265,8 @@ to handle it, which defaults to print a traceback and ignoring the exception.
the messages list. Messages might not be in cache if the message is too old
or the client is participating in high traffic guilds.
- If this occurs increase the :attr:`Client.max_messages` attribute.
+ If this occurs increase the :attr:`Client.max_messages` attribute
+ or use the :func:`on_raw_bulk_message_delete` event instead.
:param messages: The messages that have been deleted.
:type messages: List[:class:`Message`]
@@ -298,7 +300,8 @@ to handle it, which defaults to print a traceback and ignoring the exception.
Messages might not be in cache if the message is too old
or the client is participating in high traffic guilds.
- If this occurs increase the :attr:`Client.max_messages` attribute.
+ If this occurs increase the :attr:`Client.max_messages` attribute
+ or use the :func:`on_raw_message_edit` event instead.
The following non-exhaustive cases trigger this event:
@@ -339,7 +342,7 @@ to handle it, which defaults to print a traceback and ignoring the exception.
Called when a message has a reaction added to it. Similar to :func:`on_message_edit`,
if the message is not found in the internal message cache, then this
- event will not be called.
+ event will not be called. Consider using :func:`on_raw_reaction_add` instead.
.. note::
@@ -385,7 +388,7 @@ to handle it, which defaults to print a traceback and ignoring the exception.
Called when a message has all its reactions removed from it. Similar to :func:`on_message_edit`,
if the message is not found in the internal message cache, then this event
- will not be called.
+ will not be called. Consider using :func:`on_raw_reaction_clear` instead.
:param message: The message that had its reactions cleared.
:type message: :class:`Message`
@@ -400,6 +403,27 @@ to handle it, which defaults to print a traceback and ignoring the exception.
:param payload: The raw event payload data.
:type payload: :class:`RawReactionClearEvent`
+.. function:: on_reaction_clear_emoji(reaction)
+
+ Called when a message has a specific reaction removed from it. Similar to :func:`on_message_edit`,
+ if the message is not found in the internal message cache, then this event
+ will not be called. Consider using :func:`on_raw_reaction_clear_emoji` instead.
+
+ .. versionadded:: 1.3.0
+
+ :param reaction: The reaction that got cleared.
+ :type reaction: :class:`Reaction`
+
+.. function:: on_raw_reaction_clear_emoji(payload)
+
+ Called when a message has a specific reaction removed from it. Unlike :func:`on_reaction_clear_emoji` this is called
+ regardless of the state of the internal message cache.
+
+ .. versionadded:: 1.3.0
+
+ :param payload: The raw event payload data.
+ :type payload: :class:`RawReactionClearEmojiEvent`
+
.. function:: on_private_channel_delete(channel)
on_private_channel_create(channel)
@@ -2472,6 +2496,12 @@ RawReactionClearEvent
.. autoclass:: RawReactionClearEvent()
:members:
+RawReactionClearEmojiEvent
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. autoclass:: RawReactionClearEmojiEvent()
+ :members:
+
.. _discord_api_data: