Offline members are now added by default automatically.

This commit adds support for GUILD_MEMBERS_CHUNK which had to be done
due to forced large_threshold requirements in the library.
This commit is contained in:
Rapptz
2016-02-14 19:24:26 -05:00
parent 530fbe78b8
commit 4768d950c5
3 changed files with 139 additions and 16 deletions

View File

@ -51,7 +51,7 @@ import logging, traceback
import sys, time, re, json
import tempfile, os, hashlib
import itertools
import zlib
import zlib, math
from random import randint as random_integer
PY35 = sys.version_info >= (3, 5)
@ -81,6 +81,10 @@ class Client:
Indicates if :meth:`login` should cache the authentication tokens. Defaults
to ``True``. The method in which the cache is written is done by writing to
disk to a temporary directory.
request_offline : Optional[bool]
Indicates if the client should request the offline members of every server.
If this is False, then member lists will not store offline members if the
number of members in the server is greater than 250. Defaults to ``True``.
Attributes
-----------
@ -117,12 +121,13 @@ class Client:
self.loop = asyncio.get_event_loop() if loop is None else loop
self._listeners = []
self.cache_auth = options.get('cache_auth', True)
self.request_offline = options.get('request_offline', True)
max_messages = options.get('max_messages')
if max_messages is None or max_messages < 100:
max_messages = 5000
self.connection = ConnectionState(self.dispatch, max_messages)
self.connection = ConnectionState(self.dispatch, max_messages, loop=self.loop)
# Blame React for this
user_agent = 'DiscordBot (https://github.com/Rapptz/discord.py {0}) Python/{1[0]}.{1[1]} aiohttp/{2}'
@ -143,6 +148,25 @@ class Client:
# internals
def _get_all_chunks(self):
# a chunk has a maximum of 1000 members.
# we need to find out how many futures we're actually waiting for
large_servers = filter(lambda s: s.large, self.servers)
futures = []
for server in large_servers:
chunks_needed = math.ceil(server._member_count / 1000)
for chunk in range(chunks_needed):
futures.append(self.connection.receive_chunk(server.id))
return futures
@asyncio.coroutine
def _fill_offline(self):
yield from self.request_offline_members(filter(lambda s: s.large, self.servers))
chunks = self._get_all_chunks()
yield from asyncio.wait(chunks)
self.dispatch('ready')
def _get_cache_filename(self, email):
filename = hashlib.md5(email.encode('utf-8')).hexdigest()
return os.path.join(tempfile.gettempdir(), 'discord_py', filename)
@ -335,12 +359,13 @@ class Client:
return
event = msg.get('t')
is_ready = event == 'READY'
if event == 'READY':
if is_ready:
self.connection.clear()
self.session_id = data['session_id']
if event == 'READY' or event == 'RESUMED':
if is_ready or event == 'RESUMED':
interval = data['heartbeat_interval'] / 1000.0
self.keep_alive = utils.create_task(self.keep_alive_handler(interval), loop=self.loop)
@ -362,10 +387,19 @@ class Client:
return
parser = 'parse_' + event.lower()
if hasattr(self.connection, parser):
getattr(self.connection, parser)(data)
try:
func = getattr(self.connection, parser)
except AttributeError:
log.info('Unhandled event {}'.format(event))
else:
log.info("Unhandled event {}".format(event))
func(data)
if is_ready:
if self.request_offline:
utils.create_task(self._fill_offline(), loop=self.loop)
else:
self.dispatch('ready')
@asyncio.coroutine
def _make_websocket(self, initial=True):
@ -389,6 +423,7 @@ class Client:
'$referring_domain': ''
},
'compress': True,
'large_threshold': 250,
'v': 3
}
}
@ -1218,6 +1253,44 @@ class Client:
# Member management
@asyncio.coroutine
def request_offline_members(self, server):
"""|coro|
Requests previously offline members from the server to be filled up
into the :attr:`Server.members` cache. If the client was initialised
with ``request_offline`` as ``True`` then calling this function would
not do anything.
When the client logs on and connects to the websocket, Discord does
not provide the library with offline members if the number of members
in the server is larger than 250. You can check if a server is large
if :attr:`Server.large` is ``True``.
Parameters
-----------
server : :class:`Server` or iterable
The server to request offline members for. If this parameter is a
iterable then it is interpreted as an iterator of servers to
request offline members for.
"""
if hasattr(server, 'id'):
guild_id = server.id
else:
guild_id = [s.id for s in server]
payload = {
'op': 8,
'd': {
'guild_id': guild_id,
'query': '',
'limit': 0
}
}
yield from self._send_ws(utils.to_json(payload))
@asyncio.coroutine
def kick(self, member):
"""|coro|