mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-05-13 01:09:50 +00:00
Add first support for gateway redirects and reconnects.
This commit is contained in:
parent
9075d46efd
commit
2c246a0aa7
@ -105,15 +105,15 @@ class Client:
|
|||||||
self.token = None
|
self.token = None
|
||||||
self.gateway = None
|
self.gateway = None
|
||||||
self.voice = None
|
self.voice = None
|
||||||
|
self.session_id = None
|
||||||
|
self.sequence = 0
|
||||||
self.loop = asyncio.get_event_loop() if loop is None else loop
|
self.loop = asyncio.get_event_loop() if loop is None else loop
|
||||||
self._listeners = []
|
self._listeners = []
|
||||||
self.cache_auth = options.get('cache_auth', True)
|
self.cache_auth = options.get('cache_auth', True)
|
||||||
|
|
||||||
max_messages = options.get('max_messages')
|
self.max_messages = options.get('max_messages')
|
||||||
if max_messages is None or max_messages < 100:
|
if self.max_messages is None or self.max_messages < 100:
|
||||||
max_messages = 5000
|
self.max_messages = 5000
|
||||||
|
|
||||||
self.connection = ConnectionState(self.dispatch, max_messages)
|
|
||||||
|
|
||||||
# Blame React for this
|
# Blame React for this
|
||||||
user_agent = 'DiscordBot (https://github.com/Rapptz/discord.py {0}) Python/{1[0]}.{1[1]} aiohttp/{2}'
|
user_agent = 'DiscordBot (https://github.com/Rapptz/discord.py {0}) Python/{1[0]}.{1[1]} aiohttp/{2}'
|
||||||
@ -180,7 +180,6 @@ class Client:
|
|||||||
log.info('a problem occurred while updating the login cache')
|
log.info('a problem occurred while updating the login cache')
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def handle_message(self, message):
|
def handle_message(self, message):
|
||||||
removed = []
|
removed = []
|
||||||
for i, (condition, future) in enumerate(self._listeners):
|
for i, (condition, future) in enumerate(self._listeners):
|
||||||
@ -311,6 +310,7 @@ class Client:
|
|||||||
print('Ignoring exception in {}'.format(event_method), file=sys.stderr)
|
print('Ignoring exception in {}'.format(event_method), file=sys.stderr)
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
def received_message(self, msg):
|
def received_message(self, msg):
|
||||||
log.debug('WebSocket Event: {}'.format(msg))
|
log.debug('WebSocket Event: {}'.format(msg))
|
||||||
self.dispatch('socket_response', msg)
|
self.dispatch('socket_response', msg)
|
||||||
@ -318,6 +318,15 @@ class Client:
|
|||||||
op = msg.get('op')
|
op = msg.get('op')
|
||||||
data = msg.get('d')
|
data = msg.get('d')
|
||||||
|
|
||||||
|
if 's' in msg:
|
||||||
|
self.sequence = msg['s']
|
||||||
|
|
||||||
|
if op == 7:
|
||||||
|
# redirect op code
|
||||||
|
yield from self.ws.close()
|
||||||
|
yield from self.redirect_websocket(data.get('url'))
|
||||||
|
return
|
||||||
|
|
||||||
if op != 0:
|
if op != 0:
|
||||||
log.info('Unhandled op {}'.format(op))
|
log.info('Unhandled op {}'.format(op))
|
||||||
return
|
return
|
||||||
@ -325,6 +334,10 @@ class Client:
|
|||||||
event = msg.get('t')
|
event = msg.get('t')
|
||||||
|
|
||||||
if event == 'READY':
|
if event == 'READY':
|
||||||
|
self.connection = ConnectionState(self.dispatch, self.max_messages)
|
||||||
|
self.session_id = data['session_id']
|
||||||
|
|
||||||
|
if event == 'READY' or event == 'RESUMED':
|
||||||
interval = data['heartbeat_interval'] / 1000.0
|
interval = data['heartbeat_interval'] / 1000.0
|
||||||
self.keep_alive = utils.create_task(self.keep_alive_handler(interval), loop=self.loop)
|
self.keep_alive = utils.create_task(self.keep_alive_handler(interval), loop=self.loop)
|
||||||
|
|
||||||
@ -352,30 +365,60 @@ class Client:
|
|||||||
log.info("Unhandled event {}".format(event))
|
log.info("Unhandled event {}".format(event))
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def _make_websocket(self):
|
def _make_websocket(self, initial=True):
|
||||||
if not self.is_logged_in:
|
if not self.is_logged_in:
|
||||||
raise ClientException('You must be logged in to connect')
|
raise ClientException('You must be logged in to connect')
|
||||||
|
|
||||||
self.ws = yield from websockets.connect(self.gateway, loop=self.loop)
|
self.ws = yield from websockets.connect(self.gateway, loop=self.loop)
|
||||||
self.ws.max_size = None
|
self.ws.max_size = None
|
||||||
log.info('Created websocket connected to {0.gateway}'.format(self))
|
log.info('Created websocket connected to {0.gateway}'.format(self))
|
||||||
|
|
||||||
|
if initial:
|
||||||
|
payload = {
|
||||||
|
'op': 2,
|
||||||
|
'd': {
|
||||||
|
'token': self.token,
|
||||||
|
'properties': {
|
||||||
|
'$os': sys.platform,
|
||||||
|
'$browser': 'discord.py',
|
||||||
|
'$device': 'discord.py',
|
||||||
|
'$referrer': '',
|
||||||
|
'$referring_domain': ''
|
||||||
|
},
|
||||||
|
'v': 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yield from self.ws.send(utils.to_json(payload))
|
||||||
|
log.info('sent the initial payload to create the websocket')
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def redirect_websocket(self, url):
|
||||||
|
# if we get redirected then we need to recreate the websocket
|
||||||
|
# when this recreation happens we have to try to do a reconnection
|
||||||
|
log.info('redirecting websocket from {} to {}'.format(self.gateway, url))
|
||||||
|
self.keep_alive_handler.cancel()
|
||||||
|
|
||||||
|
self.gateway = url
|
||||||
|
yield from self._make_websocket(initial=False)
|
||||||
|
yield from self._reconnect_ws()
|
||||||
|
|
||||||
|
if self.is_voice_connected():
|
||||||
|
# update the websocket reference pointed to by voice
|
||||||
|
self.voice.main_ws = self.ws
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def _reconnect_ws(self):
|
||||||
payload = {
|
payload = {
|
||||||
'op': 2,
|
'op': 6,
|
||||||
'd': {
|
'd': {
|
||||||
'token': self.token,
|
'session_id': self.session_id,
|
||||||
'properties': {
|
'seq': self.sequence
|
||||||
'$os': sys.platform,
|
|
||||||
'$browser': 'discord.py',
|
|
||||||
'$device': 'discord.py',
|
|
||||||
'$referrer': '',
|
|
||||||
'$referring_domain': ''
|
|
||||||
},
|
|
||||||
'v': 3
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info('sending reconnection frame to websocket {}'.format(payload))
|
||||||
yield from self.ws.send(utils.to_json(payload))
|
yield from self.ws.send(utils.to_json(payload))
|
||||||
log.info('sent the initial payload to create the websocket')
|
|
||||||
|
|
||||||
# properties
|
# properties
|
||||||
|
|
||||||
@ -636,10 +679,14 @@ class Client:
|
|||||||
while not self._closed:
|
while not self._closed:
|
||||||
msg = yield from self.ws.recv()
|
msg = yield from self.ws.recv()
|
||||||
if msg is None:
|
if msg is None:
|
||||||
yield from self.close()
|
if self.ws.close_code == 1012:
|
||||||
break
|
yield from self.redirect_websocket(self.gateway)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
yield from self.close()
|
||||||
|
break
|
||||||
|
|
||||||
self.received_message(json.loads(msg))
|
yield from self.received_message(json.loads(msg))
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def close(self):
|
def close(self):
|
||||||
@ -654,11 +701,12 @@ class Client:
|
|||||||
yield from self.voice.disconnect()
|
yield from self.voice.disconnect()
|
||||||
self.voice = None
|
self.voice = None
|
||||||
|
|
||||||
yield from self.ws.close()
|
if self.ws.open:
|
||||||
|
yield from self.ws.close()
|
||||||
|
|
||||||
self.keep_alive.cancel()
|
self.keep_alive.cancel()
|
||||||
self._closed = True
|
self._closed = True
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def start(self, email, password):
|
def start(self, email, password):
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user