diff --git a/discord/http.py b/discord/http.py index 433697c43..e881062d8 100644 --- a/discord/http.py +++ b/discord/http.py @@ -203,12 +203,10 @@ class HTTPClient: async def startup(self) -> None: if self._started: return - self.__session = aiohttp.ClientSession(connector=self.connector) - self.user_agent = ua = await utils._get_user_agent(self.__session) - self.client_build_number = bn = await utils._get_build_number(self.__session) - self.browser_version = bv = await utils._get_browser_version(self.__session) + self.__session = session = aiohttp.ClientSession(connector=self.connector) + self.user_agent, self.browser_version, self.client_build_number = ua, bv, bn = await utils._get_info(session) _log.info('Found user agent %s (%s), build number %s.', ua, bv, bn) - self.super_properties = super_properties = { + self.super_properties = sp = { 'os': 'Windows', 'browser': 'Chrome', 'device': '', @@ -224,13 +222,13 @@ class HTTPClient: 'client_build_number': bn, 'client_event_source': None } - self.encoded_super_properties = b64encode(json.dumps(self.super_properties).encode()).decode('utf-8') + self.encoded_super_properties = b64encode(json.dumps(sp).encode()).decode('utf-8') self._started = True async def ws_connect(self, url: str, *, compress: int = 0, host: Optional[str] = None) -> Any: websocket_key = b64encode(bytes(getrandbits(8) for _ in range(16))).decode() # Thank you Discord-S.C.U.M if not host: - host = url[6:].split('?')[0].rstrip('/') # Removes the 'wss://' and the query params + host = url[6:].split('?')[0].rstrip('/') # Removes 'wss://' and the query params kwargs: Dict[str, Any] = { 'proxy_auth': self.proxy_auth, @@ -239,6 +237,17 @@ class HTTPClient: 'timeout': 30.0, 'autoclose': False, 'headers': { + 'Accept-Encoding': 'gzip, deflate', + 'Accept-Language': 'en-US', + 'Cache-Control': 'no-cache', + 'Connection': 'Upgrade', + 'Host': host, + 'Origin': 'https://discord.com', + 'Pragma': 'no-cache', + 'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits', + 'Sec-WebSocket-Key': websocket_key, + 'Sec-WebSocket-Version': '13', + 'Upgrade': 'websocket', 'User-Agent': self.user_agent, }, 'compress': compress, diff --git a/discord/utils.py b/discord/utils.py index 88f51a404..0bdc1a359 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -1207,14 +1207,19 @@ class Browser: # Inspired from https://github.com/NoahCardoza/CaptchaHarvester pass -async def _get_client_version(session): - try: - request = await session.get('https://discord.com/api/downloads/distributions/app/installers/latest?arch=x86&channel=stable&platform=win', headers={'Accept-Encoding': 'gzip, deflate'}, timeout=7) - url = request.headers['location'] - return url.split('/')[-2] - except (asyncio.TimeoutError, RuntimeError): - _log.warning('Could not fetch client version.') - return '1.0.9003' +async def _get_info(session: ClientSession) -> Tuple[str, str, int]: + for _ in range(3): + try: + async with session.get('https://discord-user-api.cf/api/v1/properties/web', timeout=5) as resp: + json = await resp.json() + return json['chrome_user_agent'], json['chrome_version'], json['client_build_number'] + except Exception: + continue + _log.warning('Info API down. Falling back to manual fetching...') + ua = await _get_user_agent(session) + bn = await _get_build_number(session) + bv = await _get_browser_version(session) + return ua, bv, bn async def _get_build_number(session: ClientSession) -> int: # Thank you Discord-S.C.U.M @@ -1228,8 +1233,8 @@ async def _get_build_number(session: ClientSession) -> int: # Thank you Discord build_index = build_file.find('buildNumber') + 14 return int(build_file[build_index:build_index + 6]) except asyncio.TimeoutError: - _log.warning('Could not fetch client build number.') - return 103016 + _log.critical('Could not fetch client build number. Falling back to hardcoded value...') + return 105304 async def _get_user_agent(session: ClientSession) -> str: @@ -1239,8 +1244,8 @@ async def _get_user_agent(session: ClientSession) -> str: response = json.loads(await request.text()) return response[0] except asyncio.TimeoutError: - _log.warning('Could not fetch user-agent.') - return 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36' + _log.critical('Could not fetch user-agent. Falling back to hardcoded value...') + return 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36' async def _get_browser_version(session: ClientSession) -> str: @@ -1252,5 +1257,5 @@ async def _get_browser_version(session: ClientSession) -> str: return response[0]['versions'][4]['version'] raise RuntimeError except (asyncio.TimeoutError, RuntimeError): - _log.warning('Could not fetch browser version.') - return '91.0.4472.77' + _log.critical('Could not fetch browser version. Falling back to hardcoded value...') + return '96.0.4664.45'