Begin working on the rewrite.
This commit is contained in:
parent
643950abf8
commit
044b0824e6
@ -26,7 +26,7 @@ DEALINGS IN THE SOFTWARE.
|
||||
import copy
|
||||
from . import utils
|
||||
from .permissions import Permissions, PermissionOverwrite
|
||||
from .enums import ChannelType
|
||||
from .enums import ChannelType, try_enum
|
||||
from collections import namedtuple
|
||||
from .mixins import Hashable
|
||||
from .role import Role
|
||||
@ -54,68 +54,63 @@ class Channel(Hashable):
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
name : str
|
||||
name: str
|
||||
The channel name.
|
||||
server : :class:`Server`
|
||||
server: :class:`Server`
|
||||
The server the channel belongs to.
|
||||
id : str
|
||||
id: str
|
||||
The channel ID.
|
||||
topic : Optional[str]
|
||||
topic: Optional[str]
|
||||
The channel's topic. None if it doesn't exist.
|
||||
is_private : bool
|
||||
is_private: bool
|
||||
``True`` if the channel is a private channel (i.e. PM). ``False`` in this case.
|
||||
position : int
|
||||
position: int
|
||||
The position in the channel list. This is a number that starts at 0. e.g. the
|
||||
top channel is position 0. The position varies depending on being a voice channel
|
||||
or a text channel, so a 0 position voice channel is on top of the voice channel
|
||||
list.
|
||||
type : :class:`ChannelType`
|
||||
type: :class:`ChannelType`
|
||||
The channel type. There is a chance that the type will be ``str`` if
|
||||
the channel type is not within the ones recognised by the enumerator.
|
||||
bitrate : int
|
||||
bitrate: int
|
||||
The channel's preferred audio bitrate in bits per second.
|
||||
voice_members
|
||||
A list of :class:`Members` that are currently inside this voice channel.
|
||||
If :attr:`type` is not :attr:`ChannelType.voice` then this is always an empty array.
|
||||
user_limit : int
|
||||
user_limit: int
|
||||
The channel's limit for number of members that can be in a voice channel.
|
||||
"""
|
||||
|
||||
__slots__ = [ 'voice_members', 'name', 'id', 'server', 'topic', 'position',
|
||||
'is_private', 'type', 'bitrate', 'user_limit',
|
||||
'_permission_overwrites' ]
|
||||
__slots__ = ( 'voice_members', 'name', 'id', 'server', 'topic',
|
||||
'type', 'bitrate', 'user_limit', '_state', 'position',
|
||||
'_permission_overwrites' )
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._update(**kwargs)
|
||||
def __init__(self, *, state, server, data):
|
||||
self._state = state
|
||||
self._update(server, data)
|
||||
self.voice_members = []
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def _update(self, **kwargs):
|
||||
self.name = kwargs.get('name')
|
||||
self.server = kwargs.get('server')
|
||||
self.id = kwargs.get('id')
|
||||
self.topic = kwargs.get('topic')
|
||||
self.is_private = False
|
||||
self.position = kwargs.get('position')
|
||||
self.bitrate = kwargs.get('bitrate')
|
||||
self.type = kwargs.get('type')
|
||||
self.user_limit = kwargs.get('user_limit')
|
||||
try:
|
||||
self.type = ChannelType(self.type)
|
||||
except:
|
||||
pass
|
||||
|
||||
def _update(self, server, data):
|
||||
self.server = server
|
||||
self.name = data['name']
|
||||
self.id = data['id']
|
||||
self.topic = data.get('topic')
|
||||
self.position = data['position']
|
||||
self.bitrate = data.get('bitrate')
|
||||
self.type = data['type']
|
||||
self.user_limit = data.get('user_limit')
|
||||
self._permission_overwrites = []
|
||||
everyone_index = 0
|
||||
everyone_id = self.server.id
|
||||
|
||||
for index, overridden in enumerate(kwargs.get('permission_overwrites', [])):
|
||||
for index, overridden in enumerate(data.get('permission_overwrites', [])):
|
||||
overridden_id = overridden['id']
|
||||
self._permission_overwrites.append(Overwrites(**overridden))
|
||||
|
||||
if overridden.get('type') == 'member':
|
||||
if overridden['type'] == 'member':
|
||||
continue
|
||||
|
||||
if overridden_id == everyone_id:
|
||||
@ -151,6 +146,10 @@ class Channel(Hashable):
|
||||
"""bool : Indicates if this is the default channel for the :class:`Server` it belongs to."""
|
||||
return self.server.id == self.id
|
||||
|
||||
@property
|
||||
def is_private(self):
|
||||
return False
|
||||
|
||||
@property
|
||||
def mention(self):
|
||||
"""str : The string that allows you to mention the channel."""
|
||||
@ -354,19 +353,20 @@ class PrivateChannel(Hashable):
|
||||
:attr:`ChannelType.group` then this is always ``None``.
|
||||
"""
|
||||
|
||||
__slots__ = ['id', 'recipients', 'type', 'owner', 'icon', 'name', 'me']
|
||||
__slots__ = ['id', 'recipients', 'type', 'owner', 'icon', 'name', 'me', '_state']
|
||||
|
||||
def __init__(self, me, **kwargs):
|
||||
self.recipients = [User(**u) for u in kwargs['recipients']]
|
||||
self.id = kwargs['id']
|
||||
def __init__(self, *, me, state, data):
|
||||
self._state = state
|
||||
self.recipients = [state.try_insert_user(u) for u in data['recipients']]
|
||||
self.id = data['id']
|
||||
self.me = me
|
||||
self.type = ChannelType(kwargs['type'])
|
||||
self._update_group(**kwargs)
|
||||
self.type = ChannelType(data['type'])
|
||||
self._update_group(data)
|
||||
|
||||
def _update_group(self, **kwargs):
|
||||
owner_id = kwargs.get('owner_id')
|
||||
self.icon = kwargs.get('icon')
|
||||
self.name = kwargs.get('name')
|
||||
def _update_group(self, data):
|
||||
owner_id = data.get('owner_id')
|
||||
self.icon = data.get('icon')
|
||||
self.name = data.get('name')
|
||||
self.owner = utils.find(lambda u: u.id == owner_id, self.recipients)
|
||||
|
||||
@property
|
||||
|
@ -32,7 +32,6 @@ from .server import Server
|
||||
from .message import Message
|
||||
from .invite import Invite
|
||||
from .object import Object
|
||||
from .reaction import Reaction
|
||||
from .role import Role
|
||||
from .errors import *
|
||||
from .state import ConnectionState
|
||||
@ -145,16 +144,15 @@ class Client:
|
||||
self.shard_id = options.get('shard_id')
|
||||
self.shard_count = options.get('shard_count')
|
||||
|
||||
max_messages = options.get('max_messages')
|
||||
if max_messages is None or max_messages < 100:
|
||||
max_messages = 5000
|
||||
|
||||
self.connection = ConnectionState(self.dispatch, self.request_offline_members,
|
||||
self._syncer, max_messages, loop=self.loop)
|
||||
|
||||
connector = options.pop('connector', None)
|
||||
self.http = HTTPClient(connector, loop=self.loop)
|
||||
|
||||
self.connection = ConnectionState(dispatch=self.dispatch,
|
||||
chunker=self.request_offline_members,
|
||||
syncer=self._syncer,
|
||||
http=self.http, loop=self.loop,
|
||||
**options)
|
||||
|
||||
self._closed = asyncio.Event(loop=self.loop)
|
||||
self._is_logged_in = asyncio.Event(loop=self.loop)
|
||||
self._is_ready = asyncio.Event(loop=self.loop)
|
||||
@ -914,7 +912,7 @@ class Client:
|
||||
raise InvalidArgument('user argument must be a User')
|
||||
|
||||
data = yield from self.http.start_private_message(user.id)
|
||||
channel = PrivateChannel(me=self.user, **data)
|
||||
channel = PrivateChannel(me=self.user, data=data, state=self.connection.ctx)
|
||||
self.connection._add_private_channel(channel)
|
||||
return channel
|
||||
|
||||
@ -1151,7 +1149,7 @@ class Client:
|
||||
|
||||
data = yield from self.http.send_message(channel_id, content, guild_id=guild_id, tts=tts, embed=embed)
|
||||
channel = self.get_channel(data.get('channel_id'))
|
||||
message = self.connection._create_message(channel=channel, **data)
|
||||
message = Message(channel=channel, state=self.connection.ctx, data=data)
|
||||
return message
|
||||
|
||||
@asyncio.coroutine
|
||||
@ -1233,7 +1231,7 @@ class Client:
|
||||
data = yield from self.http.send_file(channel_id, buffer, guild_id=guild_id,
|
||||
filename=filename, content=content, tts=tts)
|
||||
channel = self.get_channel(data.get('channel_id'))
|
||||
message = self.connection._create_message(channel=channel, **data)
|
||||
message = Message(channel=channel, state=self.connection.ctx, data=data)
|
||||
return message
|
||||
|
||||
@asyncio.coroutine
|
||||
@ -1438,7 +1436,7 @@ class Client:
|
||||
embed = embed.to_dict() if embed else None
|
||||
guild_id = channel.server.id if not getattr(channel, 'is_private', True) else None
|
||||
data = yield from self.http.edit_message(message.id, channel.id, content, guild_id=guild_id, embed=embed)
|
||||
return self.connection._create_message(channel=channel, **data)
|
||||
return Message(channel=channel, state=self.connection.ctx, data=data)
|
||||
|
||||
@asyncio.coroutine
|
||||
def get_message(self, channel, id):
|
||||
@ -1471,7 +1469,7 @@ class Client:
|
||||
"""
|
||||
|
||||
data = yield from self.http.get_message(channel.id, id)
|
||||
return self.connection._create_message(channel=channel, **data)
|
||||
return Message(channel=channel, state=self.connection.ctx, data=data)
|
||||
|
||||
@asyncio.coroutine
|
||||
def pin_message(self, message):
|
||||
@ -1541,7 +1539,7 @@ class Client:
|
||||
"""
|
||||
|
||||
data = yield from self.http.pins_from(channel.id)
|
||||
return [self.connection._create_message(channel=channel, **m) for m in data]
|
||||
return [Message(channel=channel, state=self.connection.ctx, data=m) for m in data]
|
||||
|
||||
def _logs_from(self, channel, limit=100, before=None, after=None, around=None):
|
||||
"""|coro|
|
||||
@ -1622,7 +1620,7 @@ class Client:
|
||||
|
||||
def generator(data):
|
||||
for message in data:
|
||||
yield self.connection._create_message(channel=channel, **message)
|
||||
yield Message(channel=channel, state=self.connection.ctx, data=message)
|
||||
|
||||
result = []
|
||||
while limit > 0:
|
||||
@ -2161,7 +2159,7 @@ class Client:
|
||||
perms.append(payload)
|
||||
|
||||
data = yield from self.http.create_channel(server.id, name, str(type), permission_overwrites=perms)
|
||||
channel = Channel(server=server, **data)
|
||||
channel = Channel(server=server, state=self.connection.ctx, data=data)
|
||||
return channel
|
||||
|
||||
@asyncio.coroutine
|
||||
@ -2275,7 +2273,7 @@ class Client:
|
||||
region = region.name
|
||||
|
||||
data = yield from self.http.create_server(name, region, icon)
|
||||
return Server(**data)
|
||||
return Server(data=data, state=self.connection.ctx)
|
||||
|
||||
@asyncio.coroutine
|
||||
def edit_server(self, server, **fields):
|
||||
@ -2397,7 +2395,7 @@ class Client:
|
||||
"""
|
||||
|
||||
data = yield from self.http.get_bans(server.id)
|
||||
return [User(**user['user']) for user in data]
|
||||
return [self.connection.try_insert_user(user) for user in data]
|
||||
|
||||
@asyncio.coroutine
|
||||
def prune_members(self, server, *, days):
|
||||
@ -2514,7 +2512,7 @@ class Client:
|
||||
|
||||
img = utils._bytes_to_base64_data(image)
|
||||
data = yield from self.http.create_custom_emoji(server.id, name, img)
|
||||
return Emoji(server=server, **data)
|
||||
return Emoji(server=server, data=data, state=self.connection.ctx)
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete_custom_emoji(self, emoji):
|
||||
@ -2989,7 +2987,7 @@ class Client:
|
||||
"""
|
||||
|
||||
data = yield from self.http.create_role(server.id)
|
||||
role = Role(server=server, **data)
|
||||
role = Role(server=server, data=data, state=self.connection.ctx)
|
||||
|
||||
# we have to call edit because you can't pass a payload to the
|
||||
# http request currently.
|
||||
@ -3271,7 +3269,7 @@ class Client:
|
||||
data = yield from self.http.application_info()
|
||||
return AppInfo(id=data['id'], name=data['name'],
|
||||
description=data['description'], icon=data['icon'],
|
||||
owner=User(**data['owner']))
|
||||
owner=User(state=self.connection.ctx, data=data['owner']))
|
||||
|
||||
@asyncio.coroutine
|
||||
def get_user_info(self, user_id):
|
||||
@ -3300,4 +3298,4 @@ class Client:
|
||||
Fetching the user failed.
|
||||
"""
|
||||
data = yield from self.http.get_user_info(user_id)
|
||||
return User(**data)
|
||||
return User(state=self.connection.ctx, data=data)
|
||||
|
@ -68,11 +68,12 @@ class Emoji(Hashable):
|
||||
A list of :class:`Role` that is allowed to use this emoji. If roles is empty,
|
||||
the emoji is unrestricted.
|
||||
"""
|
||||
__slots__ = ["require_colons", "managed", "id", "name", "roles", 'server']
|
||||
__slots__ = ('require_colons', 'managed', 'id', 'name', 'roles', 'server', '_state')
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.server = kwargs.pop('server')
|
||||
self._from_data(kwargs)
|
||||
def __init__(self, *, server, state, data):
|
||||
self.server = server
|
||||
self._state = state
|
||||
self._from_data(data)
|
||||
|
||||
def _from_data(self, emoji):
|
||||
self.require_colons = emoji.get('require_colons')
|
||||
@ -86,9 +87,10 @@ class Emoji(Hashable):
|
||||
|
||||
def _iterator(self):
|
||||
for attr in self.__slots__:
|
||||
value = getattr(self, attr, None)
|
||||
if value is not None:
|
||||
yield (attr, value)
|
||||
if attr[0] != '_':
|
||||
value = getattr(self, attr, None)
|
||||
if value is not None:
|
||||
yield (attr, value)
|
||||
|
||||
def __iter__(self):
|
||||
return self._iterator()
|
||||
|
@ -76,23 +76,24 @@ class Invite(Hashable):
|
||||
"""
|
||||
|
||||
|
||||
__slots__ = [ 'max_age', 'code', 'server', 'revoked', 'created_at', 'uses',
|
||||
'temporary', 'max_uses', 'xkcd', 'inviter', 'channel' ]
|
||||
__slots__ = ( 'max_age', 'code', 'server', 'revoked', 'created_at', 'uses',
|
||||
'temporary', 'max_uses', 'xkcd', 'inviter', 'channel', '_state' )
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.max_age = kwargs.get('max_age')
|
||||
self.code = kwargs.get('code')
|
||||
self.server = kwargs.get('server')
|
||||
self.revoked = kwargs.get('revoked')
|
||||
self.created_at = parse_time(kwargs.get('created_at'))
|
||||
self.temporary = kwargs.get('temporary')
|
||||
self.uses = kwargs.get('uses')
|
||||
self.max_uses = kwargs.get('max_uses')
|
||||
self.xkcd = kwargs.get('xkcdpass')
|
||||
def __init__(self, *, state, data):
|
||||
self._state = state
|
||||
self.max_age = data.get('max_age')
|
||||
self.code = data.get('code')
|
||||
self.server = data.get('server')
|
||||
self.revoked = data.get('revoked')
|
||||
self.created_at = parse_time(data.get('created_at'))
|
||||
self.temporary = data.get('temporary')
|
||||
self.uses = data.get('uses')
|
||||
self.max_uses = data.get('max_uses')
|
||||
self.xkcd = data.get('xkcdpass')
|
||||
|
||||
inviter_data = kwargs.get('inviter')
|
||||
self.inviter = None if inviter_data is None else User(**inviter_data)
|
||||
self.channel = kwargs.get('channel')
|
||||
inviter_data = data.get('inviter')
|
||||
self.inviter = None if inviter_data is None else User(state=state, data=data)
|
||||
self.channel = data.get('channel')
|
||||
|
||||
def __str__(self):
|
||||
return self.url
|
||||
|
@ -72,7 +72,6 @@ class LogsFromIterator:
|
||||
def __init__(self, client, channel, limit,
|
||||
before=None, after=None, around=None, reverse=False):
|
||||
self.client = client
|
||||
self.connection = client.connection
|
||||
self.channel = channel
|
||||
self.limit = limit
|
||||
self.before = before
|
||||
@ -81,6 +80,7 @@ class LogsFromIterator:
|
||||
self.reverse = reverse
|
||||
self._filter = None # message dict -> bool
|
||||
self.messages = asyncio.Queue()
|
||||
self.ctx = client.connection.ctx
|
||||
|
||||
if self.around:
|
||||
if self.limit > 101:
|
||||
@ -92,18 +92,18 @@ class LogsFromIterator:
|
||||
|
||||
self._retrieve_messages = self._retrieve_messages_around_strategy
|
||||
if self.before and self.after:
|
||||
self._filter = lambda m: int(self.after.id) < int(m['id']) < int(self.before.id)
|
||||
self._filter = lambda m: self.after.id < m['id'] < self.before.id
|
||||
elif self.before:
|
||||
self._filter = lambda m: int(m['id']) < int(self.before.id)
|
||||
self._filter = lambda m: m['id'] < self.before.id
|
||||
elif self.after:
|
||||
self._filter = lambda m: int(self.after.id) < int(m['id'])
|
||||
self._filter = lambda m: self.after.id < m['id']
|
||||
elif self.before and self.after:
|
||||
if self.reverse:
|
||||
self._retrieve_messages = self._retrieve_messages_after_strategy
|
||||
self._filter = lambda m: int(m['id']) < int(self.before.id)
|
||||
self._filter = lambda m: m['id'] < self.before.id
|
||||
else:
|
||||
self._retrieve_messages = self._retrieve_messages_before_strategy
|
||||
self._filter = lambda m: int(m['id']) > int(self.after.id)
|
||||
self._filter = lambda m: m['id'] > self.after.id
|
||||
elif self.after:
|
||||
self._retrieve_messages = self._retrieve_messages_after_strategy
|
||||
else:
|
||||
@ -126,9 +126,7 @@ class LogsFromIterator:
|
||||
if self._filter:
|
||||
data = filter(self._filter, data)
|
||||
for element in data:
|
||||
yield from self.messages.put(
|
||||
self.connection._create_message(
|
||||
channel=self.channel, **element))
|
||||
yield from self.messages.put(Message(channel=self.channel, state=self.ctx, data=element))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _retrieve_messages(self, retrieve):
|
||||
@ -141,7 +139,7 @@ class LogsFromIterator:
|
||||
data = yield from self.client._logs_from(self.channel, retrieve, before=self.before)
|
||||
if len(data):
|
||||
self.limit -= retrieve
|
||||
self.before = Object(id=data[-1]['id'])
|
||||
self.before = Object(id=int(data[-1]['id']))
|
||||
return data
|
||||
|
||||
@asyncio.coroutine
|
||||
@ -150,7 +148,7 @@ class LogsFromIterator:
|
||||
data = yield from self.client._logs_from(self.channel, retrieve, after=self.after)
|
||||
if len(data):
|
||||
self.limit -= retrieve
|
||||
self.after = Object(id=data[0]['id'])
|
||||
self.after = Object(id=int(data[0]['id']))
|
||||
return data
|
||||
|
||||
@asyncio.coroutine
|
||||
|
@ -28,9 +28,11 @@ from .user import User
|
||||
from .game import Game
|
||||
from .permissions import Permissions
|
||||
from . import utils
|
||||
from .enums import Status, ChannelType
|
||||
from .enums import Status, ChannelType, try_enum
|
||||
from .colour import Colour
|
||||
|
||||
import copy
|
||||
import inspect
|
||||
|
||||
class VoiceState:
|
||||
"""Represents a Discord user's voice state.
|
||||
@ -52,8 +54,8 @@ class VoiceState:
|
||||
is not currently in a voice channel.
|
||||
"""
|
||||
|
||||
__slots__ = [ 'session_id', 'deaf', 'mute', 'self_mute',
|
||||
'self_deaf', 'is_afk', 'voice_channel' ]
|
||||
__slots__ = ( 'session_id', 'deaf', 'mute', 'self_mute',
|
||||
'self_deaf', 'is_afk', 'voice_channel' )
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.session_id = kwargs.get('session_id')
|
||||
@ -74,12 +76,57 @@ def flatten_voice_states(cls):
|
||||
setattr(cls, attr, property(getter))
|
||||
return cls
|
||||
|
||||
def flatten_user(cls):
|
||||
for attr, value in User.__dict__.items():
|
||||
# ignore private/special methods
|
||||
if attr.startswith('_'):
|
||||
continue
|
||||
|
||||
# don't override what we already have
|
||||
if attr in cls.__dict__:
|
||||
continue
|
||||
|
||||
# if it's a slotted attribute or a property, redirect it
|
||||
# slotted members are implemented as member_descriptors in Type.__dict__
|
||||
if hasattr(value, '__get__'):
|
||||
def getter(self, x=attr):
|
||||
return getattr(self._user, x)
|
||||
setattr(cls, attr, property(getter, doc='Equivalent to :attr:`User.%s`' % attr))
|
||||
else:
|
||||
# probably a member function by now
|
||||
def generate_function(x):
|
||||
def general(self, *args, **kwargs):
|
||||
return getattr(self._user, x)(*args, **kwargs)
|
||||
|
||||
general.__name__ = x
|
||||
return general
|
||||
|
||||
func = generate_function(attr)
|
||||
func.__doc__ = value.__doc__
|
||||
setattr(cls, attr, func)
|
||||
|
||||
return cls
|
||||
|
||||
@flatten_voice_states
|
||||
class Member(User):
|
||||
@flatten_user
|
||||
class Member:
|
||||
"""Represents a Discord member to a :class:`Server`.
|
||||
|
||||
This is a subclass of :class:`User` that extends more functionality
|
||||
that server members have such as roles and permissions.
|
||||
This implements a lot of the functionality of :class:`User`.
|
||||
|
||||
Supported Operations:
|
||||
|
||||
+-----------+-----------------------------------------------+
|
||||
| Operation | Description |
|
||||
+===========+===============================================+
|
||||
| x == y | Checks if two members are equal. |
|
||||
+-----------+-----------------------------------------------+
|
||||
| x != y | Checks if two members are not equal. |
|
||||
+-----------+-----------------------------------------------+
|
||||
| hash(x) | Return the member's hash. |
|
||||
+-----------+-----------------------------------------------+
|
||||
| str(x) | Returns the member's name with discriminator. |
|
||||
+-----------+-----------------------------------------------+
|
||||
|
||||
Attributes
|
||||
----------
|
||||
@ -103,18 +150,31 @@ class Member(User):
|
||||
The server specific nickname of the user.
|
||||
"""
|
||||
|
||||
__slots__ = [ 'roles', 'joined_at', 'status', 'game', 'server', 'nick', 'voice' ]
|
||||
__slots__ = ('roles', 'joined_at', 'status', 'game', 'server', 'nick', 'voice', '_user', '_state')
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs.get('user'))
|
||||
self.voice = VoiceState(**kwargs)
|
||||
self.joined_at = utils.parse_time(kwargs.get('joined_at'))
|
||||
self.roles = kwargs.get('roles', [])
|
||||
def __init__(self, *, data, server, state):
|
||||
self._state = state
|
||||
self._user = state.try_insert_user(data['user'])
|
||||
self.voice = VoiceState(**data)
|
||||
self.joined_at = utils.parse_time(data.get('joined_at'))
|
||||
self.roles = data.get('roles', [])
|
||||
self.status = Status.offline
|
||||
game = kwargs.get('game', {})
|
||||
game = data.get('game', {})
|
||||
self.game = Game(**game) if game else None
|
||||
self.server = kwargs.get('server', None)
|
||||
self.nick = kwargs.get('nick', None)
|
||||
self.server = server
|
||||
self.nick = data.get('nick', None)
|
||||
|
||||
def __str__(self):
|
||||
return self._user.__str__()
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, Member) and other._user.id == self._user.id and self.server.id == other.server.id
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self._user.id)
|
||||
|
||||
def _update_voice_state(self, **kwargs):
|
||||
self.voice.self_mute = kwargs.get('self_mute', False)
|
||||
@ -146,6 +206,35 @@ class Member(User):
|
||||
ret.voice = copy.copy(self.voice)
|
||||
return ret
|
||||
|
||||
def _update(self, data, user):
|
||||
self._user.name = user['username']
|
||||
self._user.discriminator = user['discriminator']
|
||||
self._user.avatar = user['avatar']
|
||||
self._user.bot = user.get('bot', False)
|
||||
|
||||
# the nickname change is optional,
|
||||
# if it isn't in the payload then it didn't change
|
||||
if 'nick' in data:
|
||||
self.nick = data['nick']
|
||||
|
||||
# update the roles
|
||||
self.roles = [self.server.default_role]
|
||||
for role in self.server.roles:
|
||||
if role.id in data['roles']:
|
||||
self.roles.append(role)
|
||||
|
||||
# sort the roles by ID since they can be "randomised"
|
||||
self.roles.sort(key=lambda r: int(r.id))
|
||||
|
||||
def _presence_update(self, data, user):
|
||||
self.status = try_enum(Status, data['status'])
|
||||
game = data.get('game', {})
|
||||
self.game = Game(**game) if game else None
|
||||
u = self._user
|
||||
u.name = user.get('username', u.name)
|
||||
u.avatar = user.get('avatar', u.avatar)
|
||||
u.discriminator = user.get('discriminator', u.discriminator)
|
||||
|
||||
@property
|
||||
def colour(self):
|
||||
"""A property that returns a :class:`Colour` denoting the rendered colour
|
||||
@ -173,13 +262,20 @@ class Member(User):
|
||||
|
||||
@property
|
||||
def mention(self):
|
||||
"""Returns a string that mentions the member."""
|
||||
if self.nick:
|
||||
return '<@!{}>'.format(self.id)
|
||||
return '<@{}>'.format(self.id)
|
||||
|
||||
def mentioned_in(self, message):
|
||||
mentioned = super().mentioned_in(message)
|
||||
if mentioned:
|
||||
"""Checks if the member is mentioned in the specified message.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
message: :class:`Message`
|
||||
The message to check if you're mentioned in.
|
||||
"""
|
||||
if self._user.mentioned_in(message):
|
||||
return True
|
||||
|
||||
for role in message.role_mentions:
|
||||
|
@ -107,43 +107,44 @@ class Message:
|
||||
Reactions to a message. Reactions can be either custom emoji or standard unicode emoji.
|
||||
"""
|
||||
|
||||
__slots__ = [ 'edited_timestamp', 'timestamp', 'tts', 'content', 'channel',
|
||||
__slots__ = ( 'edited_timestamp', 'timestamp', 'tts', 'content', 'channel',
|
||||
'mention_everyone', 'embeds', 'id', 'mentions', 'author',
|
||||
'channel_mentions', 'server', '_raw_mentions', 'attachments',
|
||||
'_clean_content', '_raw_channel_mentions', 'nonce', 'pinned',
|
||||
'role_mentions', '_raw_role_mentions', 'type', 'call',
|
||||
'_system_content', 'reactions' ]
|
||||
'channel_mentions', 'server', '_cs_raw_mentions', 'attachments',
|
||||
'_cs_clean_content', '_cs_raw_channel_mentions', 'nonce', 'pinned',
|
||||
'role_mentions', '_cs_raw_role_mentions', 'type', 'call',
|
||||
'_cs_system_content', '_state', 'reactions' )
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
def __init__(self, *, state, channel, data):
|
||||
self._state = state
|
||||
self.reactions = kwargs.pop('reactions')
|
||||
for reaction in self.reactions:
|
||||
reaction.message = self
|
||||
self._update(**kwargs)
|
||||
self._update(channel, data)
|
||||
|
||||
def _update(self, **data):
|
||||
def _update(self, channel, data):
|
||||
# at the moment, the timestamps seem to be naive so they have no time zone and operate on UTC time.
|
||||
# we can use this to our advantage to use strptime instead of a complicated parsing routine.
|
||||
# example timestamp: 2015-08-21T12:03:45.782000+00:00
|
||||
# sometimes the .%f modifier is missing
|
||||
self.edited_timestamp = utils.parse_time(data.get('edited_timestamp'))
|
||||
self.timestamp = utils.parse_time(data.get('timestamp'))
|
||||
self.edited_timestamp = utils.parse_time(data['edited_timestamp'])
|
||||
self.timestamp = utils.parse_time(data['timestamp'])
|
||||
self.tts = data.get('tts', False)
|
||||
self.pinned = data.get('pinned', False)
|
||||
self.content = data.get('content')
|
||||
self.mention_everyone = data.get('mention_everyone')
|
||||
self.embeds = data.get('embeds')
|
||||
self.id = data.get('id')
|
||||
self.channel = data.get('channel')
|
||||
self.author = User(**data.get('author', {}))
|
||||
self.content = data['content']
|
||||
self.mention_everyone = data['mention_everyone']
|
||||
self.embeds = data['embeds']
|
||||
self.id = data['id']
|
||||
self.channel = channel
|
||||
self.author = self._state.try_insert_user(data['author'])
|
||||
self.nonce = data.get('nonce')
|
||||
self.attachments = data.get('attachments')
|
||||
self.attachments = data['attachments']
|
||||
self.type = try_enum(MessageType, data.get('type'))
|
||||
self._handle_upgrades(data.get('channel_id'))
|
||||
self._handle_upgrades(data['channel_id'])
|
||||
self._handle_mentions(data.get('mentions', []), data.get('mention_roles', []))
|
||||
self._handle_call(data.get('call'))
|
||||
|
||||
# clear the cached properties
|
||||
cached = filter(lambda attr: attr[0] == '_', self.__slots__)
|
||||
cached = filter(lambda attr: attr.startswith('_cs_'), self.__slots__)
|
||||
for attr in cached:
|
||||
try:
|
||||
delattr(self, attr)
|
||||
@ -155,7 +156,7 @@ class Message:
|
||||
self.channel_mentions = []
|
||||
self.role_mentions = []
|
||||
if getattr(self.channel, 'is_private', True):
|
||||
self.mentions = [User(**m) for m in mentions]
|
||||
self.mentions = [self._state.try_insert_user(m) for m in mentions]
|
||||
return
|
||||
|
||||
if self.server is not None:
|
||||
@ -193,7 +194,7 @@ class Message:
|
||||
call['participants'] = participants
|
||||
self.call = CallMessage(message=self, **call)
|
||||
|
||||
@utils.cached_slot_property('_raw_mentions')
|
||||
@utils.cached_slot_property('_cs_raw_mentions')
|
||||
def raw_mentions(self):
|
||||
"""A property that returns an array of user IDs matched with
|
||||
the syntax of <@user_id> in the message content.
|
||||
@ -203,21 +204,21 @@ class Message:
|
||||
"""
|
||||
return re.findall(r'<@!?([0-9]+)>', self.content)
|
||||
|
||||
@utils.cached_slot_property('_raw_channel_mentions')
|
||||
@utils.cached_slot_property('_cs_raw_channel_mentions')
|
||||
def raw_channel_mentions(self):
|
||||
"""A property that returns an array of channel IDs matched with
|
||||
the syntax of <#channel_id> in the message content.
|
||||
"""
|
||||
return re.findall(r'<#([0-9]+)>', self.content)
|
||||
|
||||
@utils.cached_slot_property('_raw_role_mentions')
|
||||
@utils.cached_slot_property('_cs_raw_role_mentions')
|
||||
def raw_role_mentions(self):
|
||||
"""A property that returns an array of role IDs matched with
|
||||
the syntax of <@&role_id> in the message content.
|
||||
"""
|
||||
return re.findall(r'<@&([0-9]+)>', self.content)
|
||||
|
||||
@utils.cached_slot_property('_clean_content')
|
||||
@utils.cached_slot_property('_cs_clean_content')
|
||||
def clean_content(self):
|
||||
"""A property that returns the content in a "cleaned up"
|
||||
manner. This basically means that mentions are transformed
|
||||
@ -288,7 +289,7 @@ class Message:
|
||||
if found is not None:
|
||||
self.author = found
|
||||
|
||||
@utils.cached_slot_property('_system_content')
|
||||
@utils.cached_slot_property('_cs_system_content')
|
||||
def system_content(self):
|
||||
"""A property that returns the content that is rendered
|
||||
regardless of the :attr:`Message.type`.
|
||||
|
@ -78,12 +78,13 @@ class Role(Hashable):
|
||||
Indicates if the role can be mentioned by users.
|
||||
"""
|
||||
|
||||
__slots__ = ['id', 'name', 'permissions', 'color', 'colour', 'position',
|
||||
'managed', 'mentionable', 'hoist', 'server' ]
|
||||
__slots__ = ('id', 'name', 'permissions', 'color', 'colour', 'position',
|
||||
'managed', 'mentionable', 'hoist', 'server', '_state' )
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.server = kwargs.pop('server')
|
||||
self._update(**kwargs)
|
||||
def __init__(self, *, server, state, data):
|
||||
self.server = server
|
||||
self._state = state
|
||||
self._update(data)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@ -118,15 +119,15 @@ class Role(Hashable):
|
||||
return NotImplemented
|
||||
return not r
|
||||
|
||||
def _update(self, **kwargs):
|
||||
self.id = kwargs.get('id')
|
||||
self.name = kwargs.get('name')
|
||||
self.permissions = Permissions(kwargs.get('permissions', 0))
|
||||
self.position = kwargs.get('position', 0)
|
||||
self.colour = Colour(kwargs.get('color', 0))
|
||||
self.hoist = kwargs.get('hoist', False)
|
||||
self.managed = kwargs.get('managed', False)
|
||||
self.mentionable = kwargs.get('mentionable', False)
|
||||
def _update(self, data):
|
||||
self.id = data['id']
|
||||
self.name = data['name']
|
||||
self.permissions = Permissions(data.get('permissions', 0))
|
||||
self.position = data.get('position', 0)
|
||||
self.colour = Colour(data.get('color', 0))
|
||||
self.hoist = data.get('hoist', False)
|
||||
self.managed = data.get('managed', False)
|
||||
self.mentionable = data.get('mentionable', False)
|
||||
self.color = self.colour
|
||||
|
||||
@property
|
||||
|
@ -52,39 +52,39 @@ class Server(Hashable):
|
||||
|
||||
Attributes
|
||||
----------
|
||||
name : str
|
||||
name: str
|
||||
The server name.
|
||||
me : :class:`Member`
|
||||
me: :class:`Member`
|
||||
Similar to :attr:`Client.user` except an instance of :class:`Member`.
|
||||
This is essentially used to get the member version of yourself.
|
||||
roles
|
||||
A list of :class:`Role` that the server has available.
|
||||
emojis
|
||||
A list of :class:`Emoji` that the server owns.
|
||||
region : :class:`ServerRegion`
|
||||
region: :class:`ServerRegion`
|
||||
The region the server belongs on. There is a chance that the region
|
||||
will be a ``str`` if the value is not recognised by the enumerator.
|
||||
afk_timeout : int
|
||||
afk_timeout: int
|
||||
The timeout to get sent to the AFK channel.
|
||||
afk_channel : :class:`Channel`
|
||||
afk_channel: :class:`Channel`
|
||||
The channel that denotes the AFK channel. None if it doesn't exist.
|
||||
members
|
||||
An iterable of :class:`Member` that are currently on the server.
|
||||
channels
|
||||
An iterable of :class:`Channel` that are currently on the server.
|
||||
icon : str
|
||||
icon: str
|
||||
The server's icon.
|
||||
id : str
|
||||
id: str
|
||||
The server's ID.
|
||||
owner : :class:`Member`
|
||||
owner: :class:`Member`
|
||||
The member who owns the server.
|
||||
unavailable : bool
|
||||
unavailable: bool
|
||||
Indicates if the server is unavailable. If this is ``True`` then the
|
||||
reliability of other attributes outside of :meth:`Server.id` is slim and they might
|
||||
all be None. It is best to not do anything with the server if it is unavailable.
|
||||
|
||||
Check the :func:`on_server_unavailable` and :func:`on_server_available` events.
|
||||
large : bool
|
||||
large: bool
|
||||
Indicates if the server is a 'large' server. A large server is defined as having
|
||||
more than ``large_threshold`` count members, which for this library is set to
|
||||
the maximum of 250.
|
||||
@ -108,17 +108,18 @@ class Server(Hashable):
|
||||
The server's invite splash.
|
||||
"""
|
||||
|
||||
__slots__ = ['afk_timeout', 'afk_channel', '_members', '_channels', 'icon',
|
||||
__slots__ = ('afk_timeout', 'afk_channel', '_members', '_channels', 'icon',
|
||||
'name', 'id', 'owner', 'unavailable', 'name', 'region',
|
||||
'_default_role', '_default_channel', 'roles', '_member_count',
|
||||
'large', 'owner_id', 'mfa_level', 'emojis', 'features',
|
||||
'verification_level', 'splash' ]
|
||||
'verification_level', 'splash' )
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
def __init__(self, *, data, state):
|
||||
self._channels = {}
|
||||
self.owner = None
|
||||
self._members = {}
|
||||
self._from_data(kwargs)
|
||||
self._state = state
|
||||
self._from_data(data)
|
||||
|
||||
@property
|
||||
def channels(self):
|
||||
@ -197,9 +198,9 @@ class Server(Hashable):
|
||||
self.icon = guild.get('icon')
|
||||
self.unavailable = guild.get('unavailable', False)
|
||||
self.id = guild['id']
|
||||
self.roles = [Role(server=self, **r) for r in guild.get('roles', [])]
|
||||
self.roles = [Role(server=self, data=r, state=self._state) for r in guild.get('roles', [])]
|
||||
self.mfa_level = guild.get('mfa_level')
|
||||
self.emojis = [Emoji(server=self, **r) for r in guild.get('emojis', [])]
|
||||
self.emojis = [Emoji(server=self, data=r, state=self._state) for r in guild.get('emojis', [])]
|
||||
self.features = guild.get('features', [])
|
||||
self.splash = guild.get('splash')
|
||||
|
||||
@ -211,8 +212,7 @@ class Server(Hashable):
|
||||
roles.append(role)
|
||||
|
||||
mdata['roles'] = roles
|
||||
member = Member(**mdata)
|
||||
member.server = self
|
||||
member = Member(data=mdata, server=self, state=self._state)
|
||||
self._add_member(member)
|
||||
|
||||
self._sync(guild)
|
||||
@ -236,18 +236,14 @@ class Server(Hashable):
|
||||
user_id = presence['user']['id']
|
||||
member = self.get_member(user_id)
|
||||
if member is not None:
|
||||
member.status = presence['status']
|
||||
try:
|
||||
member.status = Status(member.status)
|
||||
except:
|
||||
pass
|
||||
member.status = try_enum(Status, presence['status'])
|
||||
game = presence.get('game', {})
|
||||
member.game = Game(**game) if game else None
|
||||
|
||||
if 'channels' in data:
|
||||
channels = data['channels']
|
||||
for c in channels:
|
||||
channel = Channel(server=self, **c)
|
||||
channel = Channel(server=self, data=c, state=self._state)
|
||||
self._add_channel(channel)
|
||||
|
||||
|
||||
@ -311,7 +307,7 @@ class Server(Hashable):
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
name : str
|
||||
name: str
|
||||
The name of the member to lookup with an optional discriminator.
|
||||
|
||||
Returns
|
||||
|
@ -47,18 +47,20 @@ class ListenerType(enum.Enum):
|
||||
chunk = 0
|
||||
|
||||
Listener = namedtuple('Listener', ('type', 'future', 'predicate'))
|
||||
StateContext = namedtuple('StateContext', 'try_insert_user http')
|
||||
log = logging.getLogger(__name__)
|
||||
ReadyState = namedtuple('ReadyState', ('launch', 'servers'))
|
||||
|
||||
class ConnectionState:
|
||||
def __init__(self, dispatch, chunker, syncer, max_messages, *, loop):
|
||||
def __init__(self, *, dispatch, chunker, syncer, http, loop, **options):
|
||||
self.loop = loop
|
||||
self.max_messages = max_messages
|
||||
self.max_messages = max(options.get('max_messages', 5000), 100)
|
||||
self.dispatch = dispatch
|
||||
self.chunker = chunker
|
||||
self.syncer = syncer
|
||||
self.is_bot = None
|
||||
self._listeners = []
|
||||
self.ctx = StateContext(try_insert_user=self.try_insert_user, http=http)
|
||||
self.clear()
|
||||
|
||||
def clear(self):
|
||||
@ -66,6 +68,7 @@ class ConnectionState:
|
||||
self.sequence = None
|
||||
self.session_id = None
|
||||
self._calls = {}
|
||||
self._users = {}
|
||||
self._servers = {}
|
||||
self._voice_clients = {}
|
||||
self._private_channels = {}
|
||||
@ -116,6 +119,15 @@ class ConnectionState:
|
||||
for vc in self.voice_clients:
|
||||
vc.main_ws = ws
|
||||
|
||||
def try_insert_user(self, data):
|
||||
# this way is 300% faster than `dict.setdefault`.
|
||||
user_id = data['id']
|
||||
try:
|
||||
return self._users[user_id]
|
||||
except KeyError:
|
||||
self._users[user_id] = user = User(state=self.ctx, data=data)
|
||||
return user
|
||||
|
||||
@property
|
||||
def servers(self):
|
||||
return self._servers.values()
|
||||
@ -153,7 +165,7 @@ class ConnectionState:
|
||||
return utils.find(lambda m: m.id == msg_id, self.messages)
|
||||
|
||||
def _add_server_from_data(self, guild):
|
||||
server = Server(**guild)
|
||||
server = Server(data=guild, state=self.ctx)
|
||||
Server.me = property(lambda s: s.get_member(self.user.id))
|
||||
Server.voice_client = property(lambda s: self._get_voice_client(s.id))
|
||||
self._add_server(server)
|
||||
@ -207,7 +219,7 @@ class ConnectionState:
|
||||
|
||||
def parse_ready(self, data):
|
||||
self._ready_state = ReadyState(launch=asyncio.Event(), servers=[])
|
||||
self.user = User(**data['user'])
|
||||
self.user = self.try_insert_user(data['user'])
|
||||
guilds = data.get('guilds')
|
||||
|
||||
servers = self._ready_state.servers
|
||||
@ -217,7 +229,7 @@ class ConnectionState:
|
||||
servers.append(server)
|
||||
|
||||
for pm in data.get('private_channels'):
|
||||
self._add_private_channel(PrivateChannel(self.user, **pm))
|
||||
self._add_private_channel(PrivateChannel(me=self.user, data=pm, state=self.ctx))
|
||||
|
||||
compat.create_task(self._delay_ready(), loop=self.loop)
|
||||
|
||||
@ -226,7 +238,7 @@ class ConnectionState:
|
||||
|
||||
def parse_message_create(self, data):
|
||||
channel = self.get_channel(data.get('channel_id'))
|
||||
message = self._create_message(channel=channel, **data)
|
||||
message = Message(channel=channel, data=data, state=self.ctx)
|
||||
self.dispatch('message', message)
|
||||
self.messages.append(message)
|
||||
|
||||
@ -255,7 +267,7 @@ class ConnectionState:
|
||||
# embed only edit
|
||||
message.embeds = data['embeds']
|
||||
else:
|
||||
message._update(channel=message.channel, **data)
|
||||
message._update(channel=message.channel, data=data)
|
||||
|
||||
self.dispatch('message_edit', older_message, message)
|
||||
|
||||
@ -329,22 +341,11 @@ class ConnectionState:
|
||||
server._add_member(member)
|
||||
|
||||
old_member = member._copy()
|
||||
member.status = data.get('status')
|
||||
try:
|
||||
member.status = Status(member.status)
|
||||
except:
|
||||
pass
|
||||
|
||||
game = data.get('game', {})
|
||||
member.game = Game(**game) if game else None
|
||||
member.name = user.get('username', member.name)
|
||||
member.avatar = user.get('avatar', member.avatar)
|
||||
member.discriminator = user.get('discriminator', member.discriminator)
|
||||
|
||||
member._presence_update(data=data, user=user)
|
||||
self.dispatch('member_update', old_member, member)
|
||||
|
||||
def parse_user_update(self, data):
|
||||
self.user = User(**data)
|
||||
self.user = User(state=self.ctx, data=data)
|
||||
|
||||
def parse_channel_delete(self, data):
|
||||
server = self._get_server(data.get('guild_id'))
|
||||
@ -361,7 +362,7 @@ class ConnectionState:
|
||||
if channel_type is ChannelType.group:
|
||||
channel = self._get_private_channel(channel_id)
|
||||
old_channel = copy.copy(channel)
|
||||
channel._update_group(**data)
|
||||
channel._update_group(data)
|
||||
self.dispatch('channel_update', old_channel, channel)
|
||||
return
|
||||
|
||||
@ -370,32 +371,32 @@ class ConnectionState:
|
||||
channel = server.get_channel(channel_id)
|
||||
if channel is not None:
|
||||
old_channel = copy.copy(channel)
|
||||
channel._update(server=server, **data)
|
||||
channel._update(server, data)
|
||||
self.dispatch('channel_update', old_channel, channel)
|
||||
|
||||
def parse_channel_create(self, data):
|
||||
ch_type = try_enum(ChannelType, data.get('type'))
|
||||
channel = None
|
||||
if ch_type in (ChannelType.group, ChannelType.private):
|
||||
channel = PrivateChannel(self.user, **data)
|
||||
channel = PrivateChannel(me=self.user, data=data, state=self.ctx)
|
||||
self._add_private_channel(channel)
|
||||
else:
|
||||
server = self._get_server(data.get('guild_id'))
|
||||
if server is not None:
|
||||
channel = Channel(server=server, **data)
|
||||
channel = Channel(server=server, state=self.ctx, data=data)
|
||||
server._add_channel(channel)
|
||||
|
||||
self.dispatch('channel_create', channel)
|
||||
|
||||
def parse_channel_recipient_add(self, data):
|
||||
channel = self._get_private_channel(data.get('channel_id'))
|
||||
user = User(**data.get('user', {}))
|
||||
user = self.try_insert_user(data['user'])
|
||||
channel.recipients.append(user)
|
||||
self.dispatch('group_join', channel, user)
|
||||
|
||||
def parse_channel_recipient_remove(self, data):
|
||||
channel = self._get_private_channel(data.get('channel_id'))
|
||||
user = User(**data.get('user', {}))
|
||||
user = self.try_insert_user(data['user'])
|
||||
try:
|
||||
channel.recipients.remove(user)
|
||||
except ValueError:
|
||||
@ -411,7 +412,7 @@ class ConnectionState:
|
||||
roles.append(role)
|
||||
|
||||
data['roles'] = sorted(roles, key=lambda r: int(r.id))
|
||||
return Member(server=server, **data)
|
||||
return Member(server=server, data=data, state=self.ctx)
|
||||
|
||||
def parse_guild_member_add(self, data):
|
||||
server = self._get_server(data.get('guild_id'))
|
||||
@ -441,35 +442,18 @@ class ConnectionState:
|
||||
|
||||
def parse_guild_member_update(self, data):
|
||||
server = self._get_server(data.get('guild_id'))
|
||||
user_id = data['user']['id']
|
||||
user = data['user']
|
||||
user_id = user['id']
|
||||
member = server.get_member(user_id)
|
||||
if member is not None:
|
||||
user = data['user']
|
||||
old_member = member._copy()
|
||||
member.name = user['username']
|
||||
member.discriminator = user['discriminator']
|
||||
member.avatar = user['avatar']
|
||||
member.bot = user.get('bot', False)
|
||||
|
||||
# the nickname change is optional,
|
||||
# if it isn't in the payload then it didn't change
|
||||
if 'nick' in data:
|
||||
member.nick = data['nick']
|
||||
|
||||
# update the roles
|
||||
member.roles = [server.default_role]
|
||||
for role in server.roles:
|
||||
if role.id in data['roles']:
|
||||
member.roles.append(role)
|
||||
|
||||
# sort the roles by ID since they can be "randomised"
|
||||
member.roles.sort(key=lambda r: int(r.id))
|
||||
member._update(data, user)
|
||||
self.dispatch('member_update', old_member, member)
|
||||
|
||||
def parse_guild_emojis_update(self, data):
|
||||
server = self._get_server(data.get('guild_id'))
|
||||
before_emojis = server.emojis
|
||||
server.emojis = [Emoji(server=server, **e) for e in data.get('emojis', [])]
|
||||
server.emojis = [Emoji(server=server, data=e, state=self.ctx) for e in data.get('emojis', [])]
|
||||
self.dispatch('server_emojis_update', before_emojis, server.emojis)
|
||||
|
||||
def _get_create_server(self, data):
|
||||
@ -584,13 +568,13 @@ class ConnectionState:
|
||||
server = self._get_server(data.get('guild_id'))
|
||||
if server is not None:
|
||||
if 'user' in data:
|
||||
user = User(**data['user'])
|
||||
user = self.try_insert_user(data['user'])
|
||||
self.dispatch('member_unban', server, user)
|
||||
|
||||
def parse_guild_role_create(self, data):
|
||||
server = self._get_server(data.get('guild_id'))
|
||||
role_data = data.get('role', {})
|
||||
role = Role(server=server, **role_data)
|
||||
server = self._get_server(data['guild_id'])
|
||||
role_data = data['role']
|
||||
role = Role(server=server, data=role_data, state=self.ctx)
|
||||
server._add_role(role)
|
||||
self.dispatch('server_role_create', role)
|
||||
|
||||
@ -609,11 +593,12 @@ class ConnectionState:
|
||||
def parse_guild_role_update(self, data):
|
||||
server = self._get_server(data.get('guild_id'))
|
||||
if server is not None:
|
||||
role_id = data['role']['id']
|
||||
role_data = data['role']
|
||||
role_id = role_data['id']
|
||||
role = utils.find(lambda r: r.id == role_id, server.roles)
|
||||
if role is not None:
|
||||
old_role = copy.copy(role)
|
||||
role._update(**data['role'])
|
||||
role._update(role_data)
|
||||
self.dispatch('server_role_update', old_role, role)
|
||||
|
||||
def parse_guild_members_chunk(self, data):
|
||||
|
@ -58,14 +58,15 @@ class User:
|
||||
Specifies if the user is a bot account.
|
||||
"""
|
||||
|
||||
__slots__ = ['name', 'id', 'discriminator', 'avatar', 'bot']
|
||||
__slots__ = ['name', 'id', 'discriminator', 'avatar', 'bot', '_state']
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.name = kwargs.get('username')
|
||||
self.id = kwargs.get('id')
|
||||
self.discriminator = kwargs.get('discriminator')
|
||||
self.avatar = kwargs.get('avatar')
|
||||
self.bot = kwargs.get('bot', False)
|
||||
def __init__(self, *, state, data):
|
||||
self._state = state
|
||||
self.name = data['username']
|
||||
self.id = data['id']
|
||||
self.discriminator = data['discriminator']
|
||||
self.avatar = data['avatar']
|
||||
self.bot = data.get('bot', False)
|
||||
|
||||
def __str__(self):
|
||||
return '{0.name}#{0.discriminator}'.format(self)
|
||||
|
Loading…
x
Reference in New Issue
Block a user