Re-add support for reactions.
We now store emojis in a global cache and make things like adding and removing reactions part of the stateful Message class.
This commit is contained in:
parent
59a0df5f98
commit
c187d87dae
@ -20,7 +20,7 @@ __version__ = '0.16.0'
|
|||||||
from .client import Client, AppInfo, ChannelPermissions
|
from .client import Client, AppInfo, ChannelPermissions
|
||||||
from .user import User
|
from .user import User
|
||||||
from .game import Game
|
from .game import Game
|
||||||
from .emoji import Emoji
|
from .emoji import Emoji, PartialEmoji
|
||||||
from .channel import *
|
from .channel import *
|
||||||
from .guild import Guild
|
from .guild import Guild
|
||||||
from .member import Member, VoiceState
|
from .member import Member, VoiceState
|
||||||
|
@ -25,10 +25,13 @@ DEALINGS IN THE SOFTWARE.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
from . import utils
|
from . import utils
|
||||||
from .mixins import Hashable
|
from .mixins import Hashable
|
||||||
|
|
||||||
|
PartialEmoji = namedtuple('PartialEmoji', 'id name')
|
||||||
|
|
||||||
class Emoji(Hashable):
|
class Emoji(Hashable):
|
||||||
"""Represents a custom emoji.
|
"""Represents a custom emoji.
|
||||||
|
|
||||||
|
@ -237,7 +237,7 @@ class Guild(Hashable):
|
|||||||
self.id = int(guild['id'])
|
self.id = int(guild['id'])
|
||||||
self.roles = [Role(guild=self, data=r, state=self._state) for r in guild.get('roles', [])]
|
self.roles = [Role(guild=self, data=r, state=self._state) for r in guild.get('roles', [])]
|
||||||
self.mfa_level = guild.get('mfa_level')
|
self.mfa_level = guild.get('mfa_level')
|
||||||
self.emojis = [Emoji(server=self, data=r, state=self._state) for r in guild.get('emojis', [])]
|
self.emojis = tuple(map(lambda d: self._state.store_emoji(self, d), guild.get('emojis', [])))
|
||||||
self.features = guild.get('features', [])
|
self.features = guild.get('features', [])
|
||||||
self.splash = guild.get('splash')
|
self.splash = guild.get('splash')
|
||||||
|
|
||||||
@ -653,7 +653,7 @@ class Guild(Hashable):
|
|||||||
|
|
||||||
img = utils._bytes_to_base64_data(image)
|
img = utils._bytes_to_base64_data(image)
|
||||||
data = yield from self._state.http.create_custom_emoji(self.id, name, img)
|
data = yield from self._state.http.create_custom_emoji(self.id, name, img)
|
||||||
return Emoji(guild=self, data=data, state=self._state)
|
return self._state.store_emoji(self, data)
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def create_role(self, **fields):
|
def create_role(self, **fields):
|
||||||
|
@ -29,10 +29,12 @@ import re
|
|||||||
|
|
||||||
from .user import User
|
from .user import User
|
||||||
from .reaction import Reaction
|
from .reaction import Reaction
|
||||||
|
from .emoji import Emoji
|
||||||
from . import utils, abc
|
from . import utils, abc
|
||||||
from .object import Object
|
from .object import Object
|
||||||
from .calls import CallMessage
|
from .calls import CallMessage
|
||||||
from .enums import MessageType, try_enum
|
from .enums import MessageType, try_enum
|
||||||
|
from .errors import InvalidArgument
|
||||||
|
|
||||||
class Message:
|
class Message:
|
||||||
"""Represents a message from Discord.
|
"""Represents a message from Discord.
|
||||||
@ -66,8 +68,6 @@ class Message:
|
|||||||
In :issue:`very rare cases <21>` this could be a :class:`Object` instead.
|
In :issue:`very rare cases <21>` this could be a :class:`Object` instead.
|
||||||
|
|
||||||
For the sake of convenience, this :class:`Object` instance has an attribute ``is_private`` set to ``True``.
|
For the sake of convenience, this :class:`Object` instance has an attribute ``is_private`` set to ``True``.
|
||||||
guild: Optional[:class:`Guild`]
|
|
||||||
The guild that the message belongs to. If not applicable (i.e. a PM) then it's None instead.
|
|
||||||
call: Optional[:class:`CallMessage`]
|
call: Optional[:class:`CallMessage`]
|
||||||
The call that the message refers to. This is only applicable to messages of type
|
The call that the message refers to. This is only applicable to messages of type
|
||||||
:attr:`MessageType.call`.
|
:attr:`MessageType.call`.
|
||||||
@ -112,16 +112,15 @@ class Message:
|
|||||||
|
|
||||||
__slots__ = ( 'edited_timestamp', 'tts', 'content', 'channel', 'webhook_id',
|
__slots__ = ( 'edited_timestamp', 'tts', 'content', 'channel', 'webhook_id',
|
||||||
'mention_everyone', 'embeds', 'id', 'mentions', 'author',
|
'mention_everyone', 'embeds', 'id', 'mentions', 'author',
|
||||||
'_cs_channel_mentions', 'guild', '_cs_raw_mentions', 'attachments',
|
'_cs_channel_mentions', '_cs_raw_mentions', 'attachments',
|
||||||
'_cs_clean_content', '_cs_raw_channel_mentions', 'nonce', 'pinned',
|
'_cs_clean_content', '_cs_raw_channel_mentions', 'nonce', 'pinned',
|
||||||
'role_mentions', '_cs_raw_role_mentions', 'type', 'call',
|
'role_mentions', '_cs_raw_role_mentions', 'type', 'call',
|
||||||
'_cs_system_content', '_state', 'reactions' )
|
'_cs_system_content', '_state', 'reactions' )
|
||||||
|
|
||||||
def __init__(self, *, state, channel, data):
|
def __init__(self, *, state, channel, data):
|
||||||
self._state = state
|
self._state = state
|
||||||
self.reactions = kwargs.pop('reactions')
|
self.id = int(data['id'])
|
||||||
for reaction in self.reactions:
|
self.reactions = [Reaction(message=self, data=d) for d in data.get('reactions', [])]
|
||||||
reaction.message = self
|
|
||||||
self._update(channel, data)
|
self._update(channel, data)
|
||||||
|
|
||||||
def _try_patch(self, data, key, transform):
|
def _try_patch(self, data, key, transform):
|
||||||
@ -132,6 +131,41 @@ class Message:
|
|||||||
else:
|
else:
|
||||||
setattr(self, key, transform(value))
|
setattr(self, key, transform(value))
|
||||||
|
|
||||||
|
def _add_reaction(self, data):
|
||||||
|
emoji = self._state.reaction_emoji(data['emoji'])
|
||||||
|
reaction = utils.find(lambda r: r.emoji == emoji, self.reactions)
|
||||||
|
is_me = data['me'] = int(data['user_id']) == self._state.self_id
|
||||||
|
|
||||||
|
if reaction is None:
|
||||||
|
reaction = Reaction(message=self, data=data, emoji=emoji)
|
||||||
|
self.reactions.append(reaction)
|
||||||
|
else:
|
||||||
|
reaction.count += 1
|
||||||
|
if is_me:
|
||||||
|
reaction.me = is_me
|
||||||
|
|
||||||
|
return reaction
|
||||||
|
|
||||||
|
def _remove_reaction(self, data):
|
||||||
|
emoji = self._state.reaction_emoji(data['emoji'])
|
||||||
|
reaction = utils.find(lambda r: r.emoji == emoji, self.reactions)
|
||||||
|
|
||||||
|
if reaction is None:
|
||||||
|
# already removed?
|
||||||
|
raise ValueError('Emoji already removed?')
|
||||||
|
|
||||||
|
# if reaction isn't in the list, we crash. This means discord
|
||||||
|
# sent bad data, or we stored improperly
|
||||||
|
reaction.count -= 1
|
||||||
|
|
||||||
|
if int(data['user_id']) == self._state.self_id:
|
||||||
|
reaction.me = False
|
||||||
|
if reaction.count == 0:
|
||||||
|
# this raises ValueError if something went wrong as well.
|
||||||
|
self.reactions.remove(reaction)
|
||||||
|
|
||||||
|
return reaction
|
||||||
|
|
||||||
def _update(self, channel, data):
|
def _update(self, channel, data):
|
||||||
self.channel = channel
|
self.channel = channel
|
||||||
for handler in ('mentions', 'mention_roles', 'call'):
|
for handler in ('mentions', 'mention_roles', 'call'):
|
||||||
@ -198,6 +232,11 @@ class Message:
|
|||||||
call['participants'] = participants
|
call['participants'] = participants
|
||||||
self.call = CallMessage(message=self, **call)
|
self.call = CallMessage(message=self, **call)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def guild(self):
|
||||||
|
"""Optional[:class:`Guild`]: The guild that the message belongs to, if applicable."""
|
||||||
|
return getattr(self.channel, 'guild', None)
|
||||||
|
|
||||||
@utils.cached_slot_property('_cs_raw_mentions')
|
@utils.cached_slot_property('_cs_raw_mentions')
|
||||||
def raw_mentions(self):
|
def raw_mentions(self):
|
||||||
"""A property that returns an array of user IDs matched with
|
"""A property that returns an array of user IDs matched with
|
||||||
@ -428,3 +467,82 @@ class Message:
|
|||||||
|
|
||||||
yield from self._state.http.unpin_message(self.channel.id, self.id)
|
yield from self._state.http.unpin_message(self.channel.id, self.id)
|
||||||
self.pinned = False
|
self.pinned = False
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def add_reaction(self, emoji):
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Add a reaction to the message.
|
||||||
|
|
||||||
|
The emoji may be a unicode emoji or a custom server :class:`Emoji`.
|
||||||
|
|
||||||
|
You must have the :attr:`Permissions.add_reactions` permission to
|
||||||
|
add new reactions to a message.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
------------
|
||||||
|
emoji: :class:`Emoji` or str
|
||||||
|
The emoji to react with.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
--------
|
||||||
|
HTTPException
|
||||||
|
Adding the reaction failed.
|
||||||
|
Forbidden
|
||||||
|
You do not have the proper permissions to react to the message.
|
||||||
|
NotFound
|
||||||
|
The emoji you specified was not found.
|
||||||
|
InvalidArgument
|
||||||
|
The emoji parameter is invalid.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if isinstance(emoji, Emoji):
|
||||||
|
emoji = '%s:%s' % (emoji.name, emoji.id)
|
||||||
|
elif isinstance(emoji, str):
|
||||||
|
pass # this is okay
|
||||||
|
else:
|
||||||
|
raise InvalidArgument('emoji argument must be a string or discord.Emoji')
|
||||||
|
|
||||||
|
yield from self._state.http.add_reaction(self.id, self.channel.id, emoji)
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def remove_reaction(self, emoji, member):
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Remove a reaction by the member from the message.
|
||||||
|
|
||||||
|
The emoji may be a unicode emoji or a custom server :class:`Emoji`.
|
||||||
|
|
||||||
|
If the reaction is not your own (i.e. ``member`` parameter is not you) then
|
||||||
|
the :attr:`Permissions.manage_messages` permission is needed.
|
||||||
|
|
||||||
|
The ``member`` parameter must represent a member and meet
|
||||||
|
the :class:`abc.Snowflake` abc.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
------------
|
||||||
|
emoji: :class:`Emoji` or str
|
||||||
|
The emoji to remove.
|
||||||
|
member: :class:`abc.Snowflake`
|
||||||
|
The member for which to remove the reaction.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
--------
|
||||||
|
HTTPException
|
||||||
|
Removing the reaction failed.
|
||||||
|
Forbidden
|
||||||
|
You do not have the proper permissions to remove the reaction.
|
||||||
|
NotFound
|
||||||
|
The member or emoji you specified was not found.
|
||||||
|
InvalidArgument
|
||||||
|
The emoji parameter is invalid.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if isinstance(emoji, Emoji):
|
||||||
|
emoji = '%s:%s' % (emoji.name, emoji.id)
|
||||||
|
elif isinstance(emoji, str):
|
||||||
|
pass # this is okay
|
||||||
|
else:
|
||||||
|
raise InvalidArgument('emoji argument must be a string or discord.Emoji')
|
||||||
|
|
||||||
|
yield from self._state.http.remove_reaction(self.id, self.channel.id, emoji, member.id)
|
||||||
|
@ -24,7 +24,9 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|||||||
DEALINGS IN THE SOFTWARE.
|
DEALINGS IN THE SOFTWARE.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .emoji import Emoji
|
import asyncio
|
||||||
|
|
||||||
|
from .user import User
|
||||||
|
|
||||||
class Reaction:
|
class Reaction:
|
||||||
"""Represents a reaction to a message.
|
"""Represents a reaction to a message.
|
||||||
@ -48,25 +50,27 @@ class Reaction:
|
|||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
-----------
|
-----------
|
||||||
emoji : :class:`Emoji` or str
|
emoji: :class:`Emoji` or str
|
||||||
The reaction emoji. May be a custom emoji, or a unicode emoji.
|
The reaction emoji. May be a custom emoji, or a unicode emoji.
|
||||||
custom_emoji : bool
|
count: int
|
||||||
If this is a custom emoji.
|
|
||||||
count : int
|
|
||||||
Number of times this reaction was made
|
Number of times this reaction was made
|
||||||
me : bool
|
me: bool
|
||||||
If the user sent this reaction.
|
If the user sent this reaction.
|
||||||
message: :class:`Message`
|
message: :class:`Message`
|
||||||
Message this reaction is for.
|
Message this reaction is for.
|
||||||
"""
|
"""
|
||||||
__slots__ = ['message', 'count', 'emoji', 'me', 'custom_emoji']
|
__slots__ = ('message', 'count', 'emoji', 'me')
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, *, message, data, emoji=None):
|
||||||
self.message = kwargs.get('message')
|
self.message = message
|
||||||
self.emoji = kwargs['emoji']
|
self.emoji = message._state.reaction_emoji(data['emoji']) if emoji is None else emoji
|
||||||
self.count = kwargs.get('count', 1)
|
self.count = data.get('count', 1)
|
||||||
self.me = kwargs.get('me')
|
self.me = data.get('me')
|
||||||
self.custom_emoji = isinstance(self.emoji, Emoji)
|
|
||||||
|
@property
|
||||||
|
def custom_emoji(self):
|
||||||
|
"""bool: If this is a custom emoji."""
|
||||||
|
return not isinstance(self.emoji, str)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return isinstance(other, self.__class__) and other.emoji == self.emoji
|
return isinstance(other, self.__class__) and other.emoji == self.emoji
|
||||||
@ -78,3 +82,45 @@ class Reaction:
|
|||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash(self.emoji)
|
return hash(self.emoji)
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def users(self, limit=100, after=None):
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Get the users that added this reaction.
|
||||||
|
|
||||||
|
The ``after`` parameter must represent a member
|
||||||
|
and meet the :class:`abc.Snowflake` abc.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
------------
|
||||||
|
limit: int
|
||||||
|
The maximum number of results to return.
|
||||||
|
after: :class:`abc.Snowflake`
|
||||||
|
For pagination, reactions are sorted by member.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
--------
|
||||||
|
HTTPException
|
||||||
|
Getting the users for the reaction failed.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
--------
|
||||||
|
List[:class:`User`]
|
||||||
|
A list of users who reacted to the message.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# TODO: Return an iterator a la `MessageChannel.history`?
|
||||||
|
|
||||||
|
if self.custom_emoji:
|
||||||
|
emoji = '{0.name}:{0.id}'.format(self.emoji)
|
||||||
|
else:
|
||||||
|
emoji = self.emoji
|
||||||
|
|
||||||
|
if after:
|
||||||
|
after = after.id
|
||||||
|
|
||||||
|
msg = self.message
|
||||||
|
state = msg._state
|
||||||
|
data = yield from state.http.get_reaction_users(msg.id, msg.channel.id, emoji, limit, after=after)
|
||||||
|
return [User(state=state, data=user) for user in data]
|
||||||
|
115
discord/state.py
115
discord/state.py
@ -27,7 +27,7 @@ DEALINGS IN THE SOFTWARE.
|
|||||||
from .guild import Guild
|
from .guild import Guild
|
||||||
from .user import User
|
from .user import User
|
||||||
from .game import Game
|
from .game import Game
|
||||||
from .emoji import Emoji
|
from .emoji import Emoji, PartialEmoji
|
||||||
from .reaction import Reaction
|
from .reaction import Reaction
|
||||||
from .message import Message
|
from .message import Message
|
||||||
from .channel import *
|
from .channel import *
|
||||||
@ -47,10 +47,16 @@ class ListenerType(enum.Enum):
|
|||||||
chunk = 0
|
chunk = 0
|
||||||
|
|
||||||
Listener = namedtuple('Listener', ('type', 'future', 'predicate'))
|
Listener = namedtuple('Listener', ('type', 'future', 'predicate'))
|
||||||
StateContext = namedtuple('StateContext', 'store_user http self_id')
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
ReadyState = namedtuple('ReadyState', ('launch', 'guilds'))
|
ReadyState = namedtuple('ReadyState', ('launch', 'guilds'))
|
||||||
|
|
||||||
|
class StateContext:
|
||||||
|
__slots__ = ('store_user', 'http', 'self_id', 'store_emoji', 'reaction_emoji')
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
for attr, value in kwargs.items():
|
||||||
|
setattr(self, attr, value)
|
||||||
|
|
||||||
class ConnectionState:
|
class ConnectionState:
|
||||||
def __init__(self, *, dispatch, chunker, syncer, http, loop, **options):
|
def __init__(self, *, dispatch, chunker, syncer, http, loop, **options):
|
||||||
self.loop = loop
|
self.loop = loop
|
||||||
@ -60,7 +66,10 @@ class ConnectionState:
|
|||||||
self.syncer = syncer
|
self.syncer = syncer
|
||||||
self.is_bot = None
|
self.is_bot = None
|
||||||
self._listeners = []
|
self._listeners = []
|
||||||
self.ctx = StateContext(store_user=self.store_user, http=http, self_id=None)
|
self.ctx = StateContext(store_user=self.store_user,
|
||||||
|
store_emoji=self.store_emoji,
|
||||||
|
reaction_emoji=self._get_reaction_emoji,
|
||||||
|
http=http, self_id=None)
|
||||||
self.clear()
|
self.clear()
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
@ -69,6 +78,7 @@ class ConnectionState:
|
|||||||
self.session_id = None
|
self.session_id = None
|
||||||
self._calls = {}
|
self._calls = {}
|
||||||
self._users = {}
|
self._users = {}
|
||||||
|
self._emojis = {}
|
||||||
self._guilds = {}
|
self._guilds = {}
|
||||||
self._voice_clients = {}
|
self._voice_clients = {}
|
||||||
self._private_channels = {}
|
self._private_channels = {}
|
||||||
@ -128,6 +138,14 @@ class ConnectionState:
|
|||||||
self._users[user_id] = user = User(state=self.ctx, data=data)
|
self._users[user_id] = user = User(state=self.ctx, data=data)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
def store_emoji(self, guild, data):
|
||||||
|
emoji_id = int(data['id'])
|
||||||
|
try:
|
||||||
|
return self._emojis[emoji_id]
|
||||||
|
except KeyError:
|
||||||
|
self._emojis[emoji_id] = emoji = Emoji(guild=guild, state=self.ctx, data=data)
|
||||||
|
return emoji
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def guilds(self):
|
def guilds(self):
|
||||||
return self._guilds.values()
|
return self._guilds.values()
|
||||||
@ -274,26 +292,11 @@ class ConnectionState:
|
|||||||
self.dispatch('message_edit', older_message, message)
|
self.dispatch('message_edit', older_message, message)
|
||||||
|
|
||||||
def parse_message_reaction_add(self, data):
|
def parse_message_reaction_add(self, data):
|
||||||
message = self._get_message(data['message_id'])
|
message = self._get_message(int(data['message_id']))
|
||||||
if message is not None:
|
if message is not None:
|
||||||
emoji = self._get_reaction_emoji(**data.pop('emoji'))
|
reaction = message._add_reaction(data)
|
||||||
reaction = utils.get(message.reactions, emoji=emoji)
|
user = self._get_reaction_user(message.channel, int(data['user_id']))
|
||||||
|
self.dispatch('reaction_add', reaction, user)
|
||||||
is_me = data['user_id'] == self.user.id
|
|
||||||
|
|
||||||
if not reaction:
|
|
||||||
reaction = Reaction(
|
|
||||||
message=message, emoji=emoji, me=is_me, **data)
|
|
||||||
message.reactions.append(reaction)
|
|
||||||
else:
|
|
||||||
reaction.count += 1
|
|
||||||
if is_me:
|
|
||||||
reaction.me = True
|
|
||||||
|
|
||||||
channel = self.get_channel(data['channel_id'])
|
|
||||||
member = self._get_member(channel, data['user_id'])
|
|
||||||
|
|
||||||
self.dispatch('reaction_add', reaction, member)
|
|
||||||
|
|
||||||
def parse_message_reaction_remove_all(self, data):
|
def parse_message_reaction_remove_all(self, data):
|
||||||
message = self._get_message(data['message_id'])
|
message = self._get_message(data['message_id'])
|
||||||
@ -303,26 +306,15 @@ class ConnectionState:
|
|||||||
self.dispatch('reaction_clear', message, old_reactions)
|
self.dispatch('reaction_clear', message, old_reactions)
|
||||||
|
|
||||||
def parse_message_reaction_remove(self, data):
|
def parse_message_reaction_remove(self, data):
|
||||||
message = self._get_message(data['message_id'])
|
message = self._get_message(int(data['message_id']))
|
||||||
if message is not None:
|
if message is not None:
|
||||||
emoji = self._get_reaction_emoji(**data['emoji'])
|
try:
|
||||||
reaction = utils.get(message.reactions, emoji=emoji)
|
reaction = message._remove_reaction(data)
|
||||||
|
except (AttributeError, ValueError) as e: # eventual consistency lol
|
||||||
# Eventual consistency means we can get out of order or duplicate removes.
|
pass
|
||||||
if not reaction:
|
else:
|
||||||
log.warning("Unexpected reaction remove {}".format(data))
|
user = self._get_reaction_user(message.channel, int(data['user_id']))
|
||||||
return
|
self.dispatch('reaction_remove', reaction, user)
|
||||||
|
|
||||||
reaction.count -= 1
|
|
||||||
if data['user_id'] == self.user.id:
|
|
||||||
reaction.me = False
|
|
||||||
if reaction.count == 0:
|
|
||||||
message.reactions.remove(reaction)
|
|
||||||
|
|
||||||
channel = self.get_channel(data['channel_id'])
|
|
||||||
member = self._get_member(channel, data['user_id'])
|
|
||||||
|
|
||||||
self.dispatch('reaction_remove', reaction, member)
|
|
||||||
|
|
||||||
def parse_presence_update(self, data):
|
def parse_presence_update(self, data):
|
||||||
guild = self._get_guild(utils._get_as_snowflake(data, 'guild_id'))
|
guild = self._get_guild(utils._get_as_snowflake(data, 'guild_id'))
|
||||||
@ -462,7 +454,7 @@ class ConnectionState:
|
|||||||
def parse_guild_emojis_update(self, data):
|
def parse_guild_emojis_update(self, data):
|
||||||
guild = self._get_guild(int(data['guild_id']))
|
guild = self._get_guild(int(data['guild_id']))
|
||||||
before_emojis = guild.emojis
|
before_emojis = guild.emojis
|
||||||
guild.emojis = [Emoji(guild=guild, data=e, state=self.ctx) for e in data.get('emojis', [])]
|
guild.emojis = tuple(map(lambda d: self.store_emoji(guild, d), data['emojis']))
|
||||||
self.dispatch('guild_emojis_update', before_emojis, guild.emojis)
|
self.dispatch('guild_emojis_update', before_emojis, guild.emojis)
|
||||||
|
|
||||||
def _get_create_guild(self, data):
|
def _get_create_guild(self, data):
|
||||||
@ -675,35 +667,26 @@ class ConnectionState:
|
|||||||
if call is not None:
|
if call is not None:
|
||||||
self.dispatch('call_remove', call)
|
self.dispatch('call_remove', call)
|
||||||
|
|
||||||
def _get_member(self, channel, id):
|
def _get_reaction_user(self, channel, user_id):
|
||||||
if channel.is_private:
|
if isinstance(channel, DMChannel) and user_id == channel.recipient.id:
|
||||||
return utils.get(channel.recipients, id=id)
|
return channel.recipient
|
||||||
|
elif isinstance(channel, TextChannel):
|
||||||
|
return channel.guild.get_member(user_id)
|
||||||
|
elif isinstance(channel, GroupChannel):
|
||||||
|
return utils.find(lambda m: m.id == user_id, channel.recipients)
|
||||||
else:
|
else:
|
||||||
return channel.server.get_member(id)
|
return None
|
||||||
|
|
||||||
def _create_message(self, **message):
|
def _get_reaction_emoji(self, data):
|
||||||
"""Helper mostly for injecting reactions."""
|
emoji_id = utils._get_as_snowflake(data, 'id')
|
||||||
reactions = [
|
|
||||||
self._create_reaction(**r) for r in message.pop('reactions', [])
|
|
||||||
]
|
|
||||||
return Message(channel=message.pop('channel'),
|
|
||||||
reactions=reactions, **message)
|
|
||||||
|
|
||||||
def _create_reaction(self, **reaction):
|
if not emoji_id:
|
||||||
emoji = self._get_reaction_emoji(**reaction.pop('emoji'))
|
|
||||||
return Reaction(emoji=emoji, **reaction)
|
|
||||||
|
|
||||||
def _get_reaction_emoji(self, **data):
|
|
||||||
id = data['id']
|
|
||||||
|
|
||||||
if not id:
|
|
||||||
return data['name']
|
return data['name']
|
||||||
|
|
||||||
for server in self.servers:
|
try:
|
||||||
for emoji in server.emojis:
|
return self._emojis[emoji_id]
|
||||||
if emoji.id == id:
|
except KeyError:
|
||||||
return emoji
|
return PartialEmoji(id=emoji_id, name=data['name'])
|
||||||
return Emoji(server=None, **data)
|
|
||||||
|
|
||||||
def get_channel(self, id):
|
def get_channel(self, id):
|
||||||
if id is None:
|
if id is None:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user