diff --git a/discord/client.py b/discord/client.py index e2174e069..8cdfd77f4 100644 --- a/discord/client.py +++ b/discord/client.py @@ -1376,7 +1376,8 @@ class Client: payload['status'] = status if custom_activity != getattr(self.user.settings, 'custom_activity', None): # type: ignore # user is always present when logged in payload['custom_activity'] = custom_activity - await self.user.edit_settings(**payload) # type: ignore # user is always present when logged in + if payload: + await self.user.edit_settings(**payload) # type: ignore # user is always present when logged in status_str = str(status) activities_tuple = tuple(a.to_dict() for a in activities) diff --git a/discord/errors.py b/discord/errors.py index 17a719760..ab11f4ec6 100644 --- a/discord/errors.py +++ b/discord/errors.py @@ -47,6 +47,7 @@ __all__ = ( 'AuthFailure', 'LoginFailure', 'ConnectionClosed', + 'CaptchaRequired', ) @@ -169,6 +170,19 @@ class DiscordServerError(HTTPException): pass +class CaptchaRequired(HTTPException): + """Exception that's raised when a captcha is required and no handler exists. + + Subclass of :exc:`HTTPException`. + + .. versionadded:: 2.0 + """ + + def __init__(self, response: _ResponseType, message: Dict[str, Any]): + super().__init__(response, {'code': -1, 'message': 'Captcha required'}) + self.json = message + + class InvalidData(ClientException): """Exception that's raised when the library encounters unknown or invalid data from Discord. diff --git a/discord/http.py b/discord/http.py index 8f43921fb..a3c8a55c4 100644 --- a/discord/http.py +++ b/discord/http.py @@ -52,7 +52,7 @@ import weakref import aiohttp from .enums import RelationshipAction, InviteType -from .errors import HTTPException, Forbidden, NotFound, LoginFailure, DiscordServerError +from .errors import HTTPException, Forbidden, NotFound, LoginFailure, DiscordServerError, CaptchaRequired from .file import File from .tracking import ContextProperties from . import utils @@ -470,7 +470,7 @@ class HTTPClient: if reason: headers['X-Audit-Log-Reason'] = _uriquote(reason) - if payload := kwargs.pop('json', None): + if payload := kwargs.pop('json', None) is not None: headers['Content-Type'] = 'application/json' kwargs['data'] = utils._to_json(payload) @@ -569,6 +569,8 @@ class HTTPClient: elif response.status >= 500: raise DiscordServerError(response, data) else: + if 'captcha_key' in data: + raise CaptchaRequired(response, data) # type: ignore # Should not be text at this point raise HTTPException(response, data) # This is handling exceptions from the request @@ -580,22 +582,17 @@ class HTTPClient: raise # Captcha handling - except HTTPException as e: - try: - captcha_key = data['captcha_key'] # type: ignore # Handled below - except (KeyError, TypeError): + except CaptchaRequired as e: + values = [i for i in e.json['captcha_key'] if any(value in i for value in CAPTCHA_VALUES)] + if captcha_handler is None or tries == 4: + raise + elif not values: raise else: - values = [i for i in captcha_key if any(value in i for value in CAPTCHA_VALUES)] - if captcha_handler is None or tries == 4: - raise HTTPException(e.response, {'code': -1, 'message': 'Captcha required'}) from e - elif not values: - raise - else: - previous = payload or {} - previous['captcha_key'] = await captcha_handler.fetch_token(data, self.proxy, self.proxy_auth) # type: ignore # data is json here - kwargs['headers']['Content-Type'] = 'application/json' - kwargs['data'] = utils._to_json(previous) + previous = payload or {} + previous['captcha_key'] = await captcha_handler.fetch_token(e.json, self.proxy, self.proxy_auth) + kwargs['headers']['Content-Type'] = 'application/json' + kwargs['data'] = utils._to_json(previous) if response is not None: # We've run out of retries, raise