Support for v5 Gateway.
This commit is contained in:
		| @@ -108,7 +108,7 @@ class Channel(Hashable): | |||||||
|  |  | ||||||
|         self._permission_overwrites = [] |         self._permission_overwrites = [] | ||||||
|         everyone_index = 0 |         everyone_index = 0 | ||||||
|         everyone_id = self.server.default_role.id |         everyone_id = self.server.id | ||||||
|  |  | ||||||
|         for index, overridden in enumerate(kwargs.get('permission_overwrites', [])): |         for index, overridden in enumerate(kwargs.get('permission_overwrites', [])): | ||||||
|             overridden_id = overridden['id'] |             overridden_id = overridden['id'] | ||||||
|   | |||||||
| @@ -138,7 +138,8 @@ class Client: | |||||||
|         if max_messages is None or max_messages < 100: |         if max_messages is None or max_messages < 100: | ||||||
|             max_messages = 5000 |             max_messages = 5000 | ||||||
|  |  | ||||||
|         self.connection = ConnectionState(self.dispatch, self.request_offline_members, max_messages, loop=self.loop) |         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) | ||||||
| @@ -149,6 +150,10 @@ class Client: | |||||||
|  |  | ||||||
|     # internals |     # internals | ||||||
|  |  | ||||||
|  |     @asyncio.coroutine | ||||||
|  |     def _syncer(self, guilds): | ||||||
|  |         yield from self.ws.request_sync(guilds) | ||||||
|  |  | ||||||
|     def _get_cache_filename(self, email): |     def _get_cache_filename(self, email): | ||||||
|         filename = hashlib.md5(email.encode('utf-8')).hexdigest() |         filename = hashlib.md5(email.encode('utf-8')).hexdigest() | ||||||
|         return os.path.join(tempfile.gettempdir(), 'discord_py', filename) |         return os.path.join(tempfile.gettempdir(), 'discord_py', filename) | ||||||
| @@ -295,12 +300,16 @@ class Client: | |||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def _login_1(self, token, **kwargs): |     def _login_1(self, token, **kwargs): | ||||||
|         log.info('logging in using static token') |         log.info('logging in using static token') | ||||||
|         yield from self.http.static_login(token, bot=kwargs.pop('bot', True)) |         is_bot = kwargs.pop('bot', True) | ||||||
|  |         yield from self.http.static_login(token, bot=is_bot) | ||||||
|  |         self.connection.is_bot = is_bot | ||||||
|         self._is_logged_in.set() |         self._is_logged_in.set() | ||||||
|  |  | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def _login_2(self, email, password, **kwargs): |     def _login_2(self, email, password, **kwargs): | ||||||
|         # attempt to read the token from cache |         # attempt to read the token from cache | ||||||
|  |         self.connection.is_bot = False | ||||||
|  |  | ||||||
|         if self.cache_auth: |         if self.cache_auth: | ||||||
|             token = self._get_cache_token(email, password) |             token = self._get_cache_token(email, password) | ||||||
|             try: |             try: | ||||||
|   | |||||||
| @@ -127,6 +127,13 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol): | |||||||
|     INVALIDATE_SESSION |     INVALIDATE_SESSION | ||||||
|         Receive only. Tells the client to invalidate the session and IDENTIFY |         Receive only. Tells the client to invalidate the session and IDENTIFY | ||||||
|         again. |         again. | ||||||
|  |     HELLO | ||||||
|  |         Receive only. Tells the client the heartbeat interval. | ||||||
|  |     HEARTBEAT_ACK | ||||||
|  |         Receive only. Confirms receiving of a heartbeat. Not having it implies | ||||||
|  |         a connection issue. | ||||||
|  |     GUILD_SYNC | ||||||
|  |         Send only. Requests a guild sync. | ||||||
|     gateway |     gateway | ||||||
|         The gateway we are currently connected to. |         The gateway we are currently connected to. | ||||||
|     token |     token | ||||||
| @@ -143,6 +150,9 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol): | |||||||
|     RECONNECT          = 7 |     RECONNECT          = 7 | ||||||
|     REQUEST_MEMBERS    = 8 |     REQUEST_MEMBERS    = 8 | ||||||
|     INVALIDATE_SESSION = 9 |     INVALIDATE_SESSION = 9 | ||||||
|  |     HELLO              = 10 | ||||||
|  |     HEARTBEAT_ACK      = 11 | ||||||
|  |     GUILD_SYNC         = 12 | ||||||
|  |  | ||||||
|     def __init__(self, *args, **kwargs): |     def __init__(self, *args, **kwargs): | ||||||
|         super().__init__(*args, max_size=None, **kwargs) |         super().__init__(*args, max_size=None, **kwargs) | ||||||
| @@ -172,6 +182,10 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol): | |||||||
|         client.connection._update_references(ws) |         client.connection._update_references(ws) | ||||||
|  |  | ||||||
|         log.info('Created websocket connected to {}'.format(gateway)) |         log.info('Created websocket connected to {}'.format(gateway)) | ||||||
|  |  | ||||||
|  |         # poll the event for OP HELLO | ||||||
|  |         yield from ws.poll_event() | ||||||
|  |  | ||||||
|         if not resume: |         if not resume: | ||||||
|             yield from ws.identify() |             yield from ws.identify() | ||||||
|             log.info('sent the identify payload to create the websocket') |             log.info('sent the identify payload to create the websocket') | ||||||
| @@ -232,6 +246,10 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol): | |||||||
|                 'v': 3 |                 'v': 3 | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         if not self._connection.is_bot: | ||||||
|  |             payload['d']['synced_guilds'] = [] | ||||||
|  |  | ||||||
|         yield from self.send_as_json(payload) |         yield from self.send_as_json(payload) | ||||||
|  |  | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
| @@ -277,6 +295,12 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol): | |||||||
|             yield from self.close() |             yield from self.close() | ||||||
|             raise ReconnectWebSocket() |             raise ReconnectWebSocket() | ||||||
|  |  | ||||||
|  |         if op == self.HELLO: | ||||||
|  |             interval = data['heartbeat_interval'] / 1000.0 | ||||||
|  |             self._keep_alive = KeepAliveHandler(ws=self, interval=interval) | ||||||
|  |             self._keep_alive.start() | ||||||
|  |             return | ||||||
|  |  | ||||||
|         if op == self.INVALIDATE_SESSION: |         if op == self.INVALIDATE_SESSION: | ||||||
|             state.sequence = None |             state.sequence = None | ||||||
|             state.session_id = None |             state.session_id = None | ||||||
| @@ -298,11 +322,6 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol): | |||||||
|             state.sequence = msg['s'] |             state.sequence = msg['s'] | ||||||
|             state.session_id = data['session_id'] |             state.session_id = data['session_id'] | ||||||
|  |  | ||||||
|         if is_ready or event == 'RESUMED': |  | ||||||
|             interval = data['heartbeat_interval'] / 1000.0 |  | ||||||
|             self._keep_alive = KeepAliveHandler(ws=self, interval=interval) |  | ||||||
|             self._keep_alive.start() |  | ||||||
|  |  | ||||||
|         parser = 'parse_' + event.lower() |         parser = 'parse_' + event.lower() | ||||||
|  |  | ||||||
|         try: |         try: | ||||||
| @@ -400,6 +419,14 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol): | |||||||
|             status = Status.idle if idle_since else Status.online |             status = Status.idle if idle_since else Status.online | ||||||
|             me.status = status |             me.status = status | ||||||
|  |  | ||||||
|  |     @asyncio.coroutine | ||||||
|  |     def request_sync(self, guild_ids): | ||||||
|  |         payload = { | ||||||
|  |             'op': self.GUILD_SYNC, | ||||||
|  |             'd': list(guild_ids) | ||||||
|  |         } | ||||||
|  |         yield from self.send_as_json(payload) | ||||||
|  |  | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def voice_state(self, guild_id, channel_id, self_mute=False, self_deaf=False): |     def voice_state(self, guild_id, channel_id, self_mute=False, self_deaf=False): | ||||||
|         payload = { |         payload = { | ||||||
|   | |||||||
| @@ -497,4 +497,4 @@ class HTTPClient: | |||||||
|             data = yield from self.get(self.GATEWAY, bucket=_func_()) |             data = yield from self.get(self.GATEWAY, bucket=_func_()) | ||||||
|         except HTTPException as e: |         except HTTPException as e: | ||||||
|             raise GatewayNotFound() from e |             raise GatewayNotFound() from e | ||||||
|         return data.get('url') + '?encoding=json&v=4' |         return data.get('url') + '?encoding=json&v=5' | ||||||
|   | |||||||
| @@ -169,7 +169,6 @@ class Server(Hashable): | |||||||
|             self._member_count = member_count |             self._member_count = member_count | ||||||
|  |  | ||||||
|         self.name = guild.get('name') |         self.name = guild.get('name') | ||||||
|         self.large = guild.get('large', None if member_count is None else self._member_count > 250) |  | ||||||
|         self.region = guild.get('region') |         self.region = guild.get('region') | ||||||
|         try: |         try: | ||||||
|             self.region = ServerRegion(self.region) |             self.region = ServerRegion(self.region) | ||||||
| @@ -181,24 +180,36 @@ class Server(Hashable): | |||||||
|         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, **r) for r in guild.get('roles', [])] | ||||||
|  |         self._sync(guild) | ||||||
|         for data in guild.get('members', []): |         self.large = None if member_count is None else self._member_count > 250 | ||||||
|             roles = [self.default_role] |  | ||||||
|             for role_id in data['roles']: |  | ||||||
|                 role = utils.find(lambda r: r.id == role_id, self.roles) |  | ||||||
|                 if role is not None: |  | ||||||
|                     roles.append(role) |  | ||||||
|  |  | ||||||
|             data['roles'] = roles |  | ||||||
|             member = Member(**data) |  | ||||||
|             member.server = self |  | ||||||
|             self._add_member(member) |  | ||||||
|  |  | ||||||
|         if 'owner_id' in guild: |         if 'owner_id' in guild: | ||||||
|             self.owner_id = guild['owner_id'] |             self.owner_id = guild['owner_id'] | ||||||
|             self.owner = self.get_member(self.owner_id) |             self.owner = self.get_member(self.owner_id) | ||||||
|  |  | ||||||
|         for presence in guild.get('presences', []): |         afk_id = guild.get('afk_channel_id') | ||||||
|  |         self.afk_channel = self.get_channel(afk_id) | ||||||
|  |  | ||||||
|  |         for obj in guild.get('voice_states', []): | ||||||
|  |             self._update_voice_state(obj) | ||||||
|  |  | ||||||
|  |     def _sync(self, data): | ||||||
|  |         if 'large' in data: | ||||||
|  |             self.large = data['large'] | ||||||
|  |  | ||||||
|  |         for mdata in data.get('members', []): | ||||||
|  |             roles = [self.default_role] | ||||||
|  |             for role_id in mdata['roles']: | ||||||
|  |                 role = utils.find(lambda r: r.id == role_id, self.roles) | ||||||
|  |                 if role is not None: | ||||||
|  |                     roles.append(role) | ||||||
|  |  | ||||||
|  |             mdata['roles'] = roles | ||||||
|  |             member = Member(**mdata) | ||||||
|  |             member.server = self | ||||||
|  |             self._add_member(member) | ||||||
|  |  | ||||||
|  |         for presence in data.get('presences', []): | ||||||
|             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: | ||||||
| @@ -210,17 +221,12 @@ class Server(Hashable): | |||||||
|                 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 guild: |         if 'channels' in data: | ||||||
|             channels = guild['channels'] |             channels = data['channels'] | ||||||
|             for c in channels: |             for c in channels: | ||||||
|                 channel = Channel(server=self, **c) |                 channel = Channel(server=self, **c) | ||||||
|                 self._add_channel(channel) |                 self._add_channel(channel) | ||||||
|  |  | ||||||
|         afk_id = guild.get('afk_channel_id') |  | ||||||
|         self.afk_channel = self.get_channel(afk_id) |  | ||||||
|  |  | ||||||
|         for obj in guild.get('voice_states', []): |  | ||||||
|             self._update_voice_state(obj) |  | ||||||
|  |  | ||||||
|     @utils.cached_slot_property('_default_role') |     @utils.cached_slot_property('_default_role') | ||||||
|     def default_role(self): |     def default_role(self): | ||||||
|   | |||||||
| @@ -49,11 +49,13 @@ log = logging.getLogger(__name__) | |||||||
| ReadyState = namedtuple('ReadyState', ('launch', 'servers')) | ReadyState = namedtuple('ReadyState', ('launch', 'servers')) | ||||||
|  |  | ||||||
| class ConnectionState: | class ConnectionState: | ||||||
|     def __init__(self, dispatch, chunker, max_messages, *, loop): |     def __init__(self, dispatch, chunker, syncer, max_messages, *, loop): | ||||||
|         self.loop = loop |         self.loop = loop | ||||||
|         self.max_messages = max_messages |         self.max_messages = max_messages | ||||||
|         self.dispatch = dispatch |         self.dispatch = dispatch | ||||||
|         self.chunker = chunker |         self.chunker = chunker | ||||||
|  |         self.syncer = syncer | ||||||
|  |         self.is_bot = None | ||||||
|         self._listeners = [] |         self._listeners = [] | ||||||
|         self.clear() |         self.clear() | ||||||
|  |  | ||||||
| @@ -165,8 +167,9 @@ class ConnectionState: | |||||||
|             launch.set() |             launch.set() | ||||||
|             yield from asyncio.sleep(2) |             yield from asyncio.sleep(2) | ||||||
|  |  | ||||||
|         # get all the chunks |  | ||||||
|         servers = self._ready_state.servers |         servers = self._ready_state.servers | ||||||
|  |  | ||||||
|  |         # get all the chunks | ||||||
|         chunks = [] |         chunks = [] | ||||||
|         for server in servers: |         for server in servers: | ||||||
|             chunks.extend(self.chunks_needed(server)) |             chunks.extend(self.chunks_needed(server)) | ||||||
| @@ -194,9 +197,12 @@ class ConnectionState: | |||||||
|         servers = self._ready_state.servers |         servers = self._ready_state.servers | ||||||
|         for guild in guilds: |         for guild in guilds: | ||||||
|             server = self._add_server_from_data(guild) |             server = self._add_server_from_data(guild) | ||||||
|             if server.large: |             if server.large or not self.is_bot: | ||||||
|                 servers.append(server) |                 servers.append(server) | ||||||
|  |  | ||||||
|  |         if not self.is_bot: | ||||||
|  |             compat.create_task(self.syncer([s.id for s in self.servers]), loop=self.loop) | ||||||
|  |  | ||||||
|         for pm in data.get('private_channels'): |         for pm in data.get('private_channels'): | ||||||
|             self._add_private_channel(PrivateChannel(id=pm['id'], |             self._add_private_channel(PrivateChannel(id=pm['id'], | ||||||
|                                      user=User(**pm['recipient']))) |                                      user=User(**pm['recipient']))) | ||||||
| @@ -427,6 +433,10 @@ class ConnectionState: | |||||||
|         else: |         else: | ||||||
|             self.dispatch('server_join', server) |             self.dispatch('server_join', server) | ||||||
|  |  | ||||||
|  |     def parse_guild_sync(self, data): | ||||||
|  |         server = self._get_server(data.get('id')) | ||||||
|  |         server._sync(data) | ||||||
|  |  | ||||||
|     def parse_guild_update(self, data): |     def parse_guild_update(self, data): | ||||||
|         server = self._get_server(data.get('id')) |         server = self._get_server(data.get('id')) | ||||||
|         if server is not None: |         if server is not None: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user