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