aboutsummaryrefslogtreecommitdiff
path: root/discord/client.py
diff options
context:
space:
mode:
authorRapptz <[email protected]>2016-02-14 19:24:26 -0500
committerRapptz <[email protected]>2016-02-14 19:24:26 -0500
commit4768d950c590ba170ead20aad7ccc797a7d8e737 (patch)
tree7a7ae1164918dcbd7e35fc919e5b4cdd78ed50eb /discord/client.py
parentHandle case where PRESENCE_UPDATE adds members to cache. (diff)
downloaddiscord.py-4768d950c590ba170ead20aad7ccc797a7d8e737.tar.xz
discord.py-4768d950c590ba170ead20aad7ccc797a7d8e737.zip
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.
Diffstat (limited to 'discord/client.py')
-rw-r--r--discord/client.py87
1 files changed, 80 insertions, 7 deletions
diff --git a/discord/client.py b/discord/client.py
index 4e7083f7..fa096c98 100644
--- a/discord/client.py
+++ b/discord/client.py
@@ -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
}
}
@@ -1219,6 +1254,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|