Browse Source

Add support for resume_gateway_url

pull/10109/head
Rapptz 3 years ago
committed by dolfies
parent
commit
674845479d
  1. 21
      discord/client.py
  2. 21
      discord/gateway.py
  3. 35
      discord/http.py
  4. 1
      discord/types/gateway.py

21
discord/client.py

@ -766,7 +766,7 @@ class Client:
""" """
backoff = ExponentialBackoff() backoff = ExponentialBackoff()
ws_params = { ws_params: Dict[str, Any] = {
'initial': True, 'initial': True,
} }
while not self.is_closed(): while not self.is_closed():
@ -779,7 +779,9 @@ class Client:
except ReconnectWebSocket as e: except ReconnectWebSocket as e:
_log.info('Got a request to %s the websocket.', e.op) _log.info('Got a request to %s the websocket.', e.op)
self.dispatch('disconnect') self.dispatch('disconnect')
ws_params.update(sequence=self.ws.sequence, resume=e.resume, session=self.ws.session_id) # type: ignore # These are always present at this point ws_params.update(sequence=self.ws.sequence, resume=e.resume, session=self.ws.session_id)
if e.resume:
ws_params['gateway'] = self.ws.gateway
continue continue
except ( except (
OSError, OSError,
@ -803,7 +805,13 @@ class Client:
# If we get connection reset by peer then try to RESUME # If we get connection reset by peer then try to RESUME
if isinstance(exc, OSError) and exc.errno in (54, 10054): if isinstance(exc, OSError) and exc.errno in (54, 10054):
ws_params.update(sequence=self.ws.sequence, initial=False, resume=True, session=self.ws.session_id) # type: ignore # These are always present at this point ws_params.update(
sequence=self.ws.sequence,
gateway=self.ws.gateway,
initial=False,
resume=True,
session=self.ws.session_id,
)
continue continue
# We should only get this when an unhandled close code happens, # We should only get this when an unhandled close code happens,
@ -821,7 +829,12 @@ class Client:
# Always try to RESUME the connection # Always try to RESUME the connection
# If the connection is not RESUME-able then the gateway will invalidate the session # If the connection is not RESUME-able then the gateway will invalidate the session
# This is apparently what the official Discord client does # This is apparently what the official Discord client does
ws_params.update(sequence=self.ws.sequence, resume=True, session=self.ws.session_id) # type: ignore # These are always present at this point ws_params.update(
sequence=self.ws.sequence,
gateway=self.ws.gateway,
resume=True,
session=self.ws.session_id,
)
async def close(self) -> None: async def close(self) -> None:
"""|coro| """|coro|

21
discord/gateway.py

@ -293,6 +293,7 @@ class DiscordWebSocket:
_zlib_enabled: bool _zlib_enabled: bool
# fmt: off # fmt: off
DEFAULT_GATEWAY = 'wss://gateway.discord.gg/'
DISPATCH = 0 DISPATCH = 0
HEARTBEAT = 1 HEARTBEAT = 1
IDENTIFY = 2 IDENTIFY = 2
@ -358,13 +359,24 @@ class DiscordWebSocket:
session: Optional[str] = None, session: Optional[str] = None,
sequence: Optional[int] = None, sequence: Optional[int] = None,
resume: bool = False, resume: bool = False,
encoding: str = 'json',
zlib: bool = True,
) -> Self: ) -> Self:
"""Creates a main websocket for Discord from a :class:`Client`. """Creates a main websocket for Discord from a :class:`Client`.
This is for internal use only. This is for internal use only.
""" """
gateway = gateway or await client.http.get_gateway() # Circular import
socket = await client.http.ws_connect(gateway) from .http import INTERNAL_API_VERSION
gateway = gateway or cls.DEFAULT_GATEWAY
if zlib:
url = f'{gateway}?v={INTERNAL_API_VERSION}&encoding={encoding}&compress=zlib-stream'
else:
url = f'{gateway}?v={INTERNAL_API_VERSION}&encoding={encoding}'
socket = await client.http.ws_connect(url)
ws = cls(socket, loop=client.loop) ws = cls(socket, loop=client.loop)
# Dynamically add attributes needed # Dynamically add attributes needed
@ -380,7 +392,7 @@ class DiscordWebSocket:
ws._max_heartbeat_timeout = client._connection.heartbeat_timeout ws._max_heartbeat_timeout = client._connection.heartbeat_timeout
ws._user_agent = client.http.user_agent ws._user_agent = client.http.user_agent
ws._super_properties = client.http.super_properties ws._super_properties = client.http.super_properties
ws._zlib_enabled = client.http.zlib ws._zlib_enabled = zlib
if client._enable_debug_events: if client._enable_debug_events:
ws.send = ws.debug_send ws.send = ws.debug_send
@ -553,6 +565,7 @@ class DiscordWebSocket:
self.sequence = None self.sequence = None
self.session_id = None self.session_id = None
self.gateway = self.DEFAULT_GATEWAY
_log.info('Gateway session has been invalidated.') _log.info('Gateway session has been invalidated.')
await self.close(code=1000) await self.close(code=1000)
raise ReconnectWebSocket(resume=False) raise ReconnectWebSocket(resume=False)
@ -564,8 +577,8 @@ class DiscordWebSocket:
self._trace = trace = data.get('_trace', []) self._trace = trace = data.get('_trace', [])
self.sequence = msg['s'] self.sequence = msg['s']
self.session_id = data['session_id'] self.session_id = data['session_id']
self.gateway = data['resume_gateway_url']
_log.info('Connected to Gateway: %s (Session ID: %s).', ', '.join(trace), self.session_id) _log.info('Connected to Gateway: %s (Session ID: %s).', ', '.join(trace), self.session_id)
await self.voice_state() # Initial OP 4
elif event == 'RESUMED': elif event == 'RESUMED':
self._trace = trace = data.get('_trace', []) self._trace = trace = data.get('_trace', [])

35
discord/http.py

@ -62,16 +62,6 @@ from . import utils
from .mentions import AllowedMentions from .mentions import AllowedMentions
from .utils import MISSING from .utils import MISSING
CAPTCHA_VALUES = {
'incorrect-captcha',
'response-already-used',
'captcha-required',
'invalid-input-response',
'invalid-response',
'You need to update your app', # Discord moment
}
_log = logging.getLogger(__name__)
if TYPE_CHECKING: if TYPE_CHECKING:
from typing_extensions import Self from typing_extensions import Self
@ -123,6 +113,18 @@ if TYPE_CHECKING:
Response = Coroutine[Any, Any, T] Response = Coroutine[Any, Any, T]
MessageableChannel = Union[TextChannel, Thread, DMChannel, GroupChannel, PartialMessageable, VoiceChannel, ForumChannel] MessageableChannel = Union[TextChannel, Thread, DMChannel, GroupChannel, PartialMessageable, VoiceChannel, ForumChannel]
CAPTCHA_VALUES = {
'incorrect-captcha',
'response-already-used',
'captcha-required',
'invalid-input-response',
'invalid-response',
'You need to update your app', # Discord moment
}
INTERNAL_API_VERSION = 9
_log = logging.getLogger(__name__)
async def json_or_text(response: aiohttp.ClientResponse) -> Union[Dict[str, Any], str]: async def json_or_text(response: aiohttp.ClientResponse) -> Union[Dict[str, Any], str]:
text = await response.text(encoding='utf-8') text = await response.text(encoding='utf-8')
@ -288,7 +290,7 @@ def _gen_accept_encoding_header():
class Route: class Route:
BASE: ClassVar[str] = 'https://discord.com/api/v9' BASE: ClassVar[str] = f'https://discord.com/api/v{INTERNAL_API_VERSION}'
def __init__(self, method: str, path: str, *, metadata: Optional[str] = None, **parameters: Any) -> None: def __init__(self, method: str, path: str, *, metadata: Optional[str] = None, **parameters: Any) -> None:
self.path: str = path self.path: str = path
@ -3989,15 +3991,12 @@ class HTTPClient:
# Misc # Misc
async def get_gateway(self, *, encoding: str = 'json', zlib: bool = True) -> str: async def get_gateway(self, *, encoding: str = 'json', zlib: bool = True) -> str:
# The gateway URL hasn't changed for over 5 years data = await self.request(Route('GET', '/gateway'))
# And, the official clients aren't GETting it anymore, sooooo...
self.zlib = zlib
if zlib: if zlib:
value = 'wss://gateway.discord.gg?encoding={0}&v=9&compress=zlib-stream' value = '{0}?encoding={1}&v={2}&compress=zlib-stream'
else: else:
value = 'wss://gateway.discord.gg?encoding={0}&v=9' value = '{0}?encoding={1}&v={2}'
return value.format(data['url'], encoding, INTERNAL_API_VERSION)
return value.format(encoding)
def get_user(self, user_id: Snowflake) -> Response[user.User]: def get_user(self, user_id: Snowflake) -> Response[user.User]:
return self.request(Route('GET', '/users/{user_id}', user_id=user_id)) return self.request(Route('GET', '/users/{user_id}', user_id=user_id))

1
discord/types/gateway.py

@ -103,6 +103,7 @@ class ReadyEvent(ResumedEvent):
pending_payments: NotRequired[List[Payment]] pending_payments: NotRequired[List[Payment]]
private_channels: List[Union[DMChannel, GroupDMChannel]] private_channels: List[Union[DMChannel, GroupDMChannel]]
relationships: List[Relationship] relationships: List[Relationship]
resume_gateway_url: str
required_action: NotRequired[str] required_action: NotRequired[str]
sessions: List[Session] sessions: List[Session]
session_id: str session_id: str

Loading…
Cancel
Save