|
|
@ -46,6 +46,7 @@ class ListenerType(enum.Enum): |
|
|
|
|
|
|
|
Listener = namedtuple('Listener', ('type', 'future', 'predicate')) |
|
|
|
log = logging.getLogger(__name__) |
|
|
|
ReadyState = namedtuple('ReadyState', ('launch', 'chunks')) |
|
|
|
|
|
|
|
class ConnectionState: |
|
|
|
def __init__(self, dispatch, chunker, max_messages, *, loop): |
|
|
@ -133,33 +134,50 @@ class ConnectionState: |
|
|
|
yield self.receive_chunk(server.id) |
|
|
|
|
|
|
|
@asyncio.coroutine |
|
|
|
def _fill_offline(self): |
|
|
|
# a chunk has a maximum of 1000 members. |
|
|
|
# we need to find out how many futures we're actually waiting for |
|
|
|
large_servers = [s for s in self.servers if s.large] |
|
|
|
yield from self.chunker(large_servers) |
|
|
|
|
|
|
|
chunks = [] |
|
|
|
for server in large_servers: |
|
|
|
chunks.extend(self.chunks_needed(server)) |
|
|
|
|
|
|
|
def _delay_ready(self, large_servers): |
|
|
|
if len(large_servers): |
|
|
|
# for regular accounts with < 100 guilds you will |
|
|
|
# get a regular READY packet without the unavailable |
|
|
|
# streaming so if this is non-empty then it's a regular |
|
|
|
# account and it needs chunking. |
|
|
|
yield from self.chunker(large_servers) |
|
|
|
for server in large_servers: |
|
|
|
self._ready_state.chunks.extend(self.chunks_needed(server)) |
|
|
|
|
|
|
|
launch = self._ready_state.launch |
|
|
|
while not launch.is_set(): |
|
|
|
# this snippet of code is basically waiting 2 seconds |
|
|
|
# until the last GUILD_CREATE was sent |
|
|
|
launch.set() |
|
|
|
yield from asyncio.sleep(2) |
|
|
|
|
|
|
|
# get all the chunks |
|
|
|
chunks = [f for f in self._ready_state.chunks if not f.done()] |
|
|
|
if chunks: |
|
|
|
yield from asyncio.wait(chunks) |
|
|
|
|
|
|
|
# remove the state |
|
|
|
del self._ready_state |
|
|
|
|
|
|
|
# dispatch the event |
|
|
|
self.dispatch('ready') |
|
|
|
|
|
|
|
def parse_ready(self, data): |
|
|
|
self._ready_state = ReadyState(launch=asyncio.Event(), chunks=[]) |
|
|
|
self.user = User(**data['user']) |
|
|
|
guilds = data.get('guilds') |
|
|
|
|
|
|
|
large_servers = [] |
|
|
|
for guild in guilds: |
|
|
|
self._add_server_from_data(guild) |
|
|
|
server = self._add_server_from_data(guild) |
|
|
|
if server.large: |
|
|
|
large_servers.append(server) |
|
|
|
|
|
|
|
for pm in data.get('private_channels'): |
|
|
|
self._add_private_channel(PrivateChannel(id=pm['id'], |
|
|
|
user=User(**pm['recipient']))) |
|
|
|
|
|
|
|
utils.create_task(self._fill_offline(), loop=self.loop) |
|
|
|
utils.create_task(self._delay_ready(large_servers), loop=self.loop) |
|
|
|
|
|
|
|
def parse_message_create(self, data): |
|
|
|
channel = self.get_channel(data.get('channel_id')) |
|
|
@ -295,10 +313,8 @@ class ConnectionState: |
|
|
|
|
|
|
|
self.dispatch('member_update', old_member, member) |
|
|
|
|
|
|
|
@asyncio.coroutine |
|
|
|
def parse_guild_create(self, data): |
|
|
|
unavailable = data.get('unavailable') |
|
|
|
if unavailable == False: |
|
|
|
def _get_create_server(self, data): |
|
|
|
if data.get('unavailable') == False: |
|
|
|
# GUILD_CREATE with unavailable in the response |
|
|
|
# usually means that the server has become available |
|
|
|
# and is therefore in the cache |
|
|
@ -306,27 +322,53 @@ class ConnectionState: |
|
|
|
if server is not None: |
|
|
|
server.unavailable = False |
|
|
|
server._from_data(data) |
|
|
|
self.dispatch('server_available', server) |
|
|
|
return |
|
|
|
return server |
|
|
|
|
|
|
|
return self._add_server_from_data(data) |
|
|
|
|
|
|
|
@asyncio.coroutine |
|
|
|
def parse_guild_create(self, data): |
|
|
|
unavailable = data.get('unavailable') |
|
|
|
if unavailable == True: |
|
|
|
# joined a server with unavailable == True so.. |
|
|
|
return |
|
|
|
|
|
|
|
# if we're at this point then it was probably |
|
|
|
# unavailable during the READY event and is now |
|
|
|
# available, so it isn't in the cache... |
|
|
|
|
|
|
|
server = self._add_server_from_data(data) |
|
|
|
server = self._get_create_server(data) |
|
|
|
|
|
|
|
# check if it requires chunking |
|
|
|
if server.large: |
|
|
|
yield from self.chunker(server) |
|
|
|
chunks = list(self.chunks_needed(server)) |
|
|
|
|
|
|
|
if unavailable == False: |
|
|
|
# check if we're waiting for 'useful' READY |
|
|
|
# and if we are, we don't want to dispatch any |
|
|
|
# event such as server_join or server_available |
|
|
|
# because we're still in the 'READY' phase. Or |
|
|
|
# so we say. |
|
|
|
try: |
|
|
|
state = self._ready_state |
|
|
|
state.launch.clear() |
|
|
|
if chunks: |
|
|
|
state.chunks.extend(chunks) |
|
|
|
except AttributeError: |
|
|
|
# the _ready_state attribute is only there during |
|
|
|
# processing of useful READY. |
|
|
|
pass |
|
|
|
else: |
|
|
|
return |
|
|
|
|
|
|
|
# since we're not waiting for 'useful' READY we'll just |
|
|
|
# do the chunk request here |
|
|
|
if chunks: |
|
|
|
yield from asyncio.wait(chunks) |
|
|
|
|
|
|
|
self.dispatch('server_join', server) |
|
|
|
|
|
|
|
# Dispatch available if newly available |
|
|
|
if unavailable == False: |
|
|
|
self.dispatch('server_available', server) |
|
|
|
else: |
|
|
|
self.dispatch('server_join', server) |
|
|
|
|
|
|
|
def parse_guild_update(self, data): |
|
|
|
server = self._get_server(data.get('id')) |
|
|
|