Use X-Ratelimit-Reset-After header by default.

There is now an option to turn it off, of course.
This commit is contained in:
Rapptz 2019-08-27 03:33:44 -04:00
parent 1c6c26ee38
commit ea3d119ca6
3 changed files with 22 additions and 7 deletions

View File

@ -185,6 +185,14 @@ class Client:
In short, this makes it so the only member you can reliably query is the In short, this makes it so the only member you can reliably query is the
message author. Useful for bots that do not require any state. message author. Useful for bots that do not require any state.
assume_unsync_clock: :class:`bool`
Whether to assume the system clock is unsynced. This applies to the ratelimit handling
code. If this is set to ``True``, the default, then the library uses the time to reset
a rate limit bucket given by Discord. If this is ``False`` then your system clock is
used to calculate how long to sleep for. If this is set to ``False`` it is recommended to
sync your system clock to Google's NTP server.
.. versionadded:: 1.3
Attributes Attributes
----------- -----------
@ -203,7 +211,8 @@ class Client:
connector = options.pop('connector', None) connector = options.pop('connector', None)
proxy = options.pop('proxy', None) proxy = options.pop('proxy', None)
proxy_auth = options.pop('proxy_auth', None) proxy_auth = options.pop('proxy_auth', None)
self.http = HTTPClient(connector, proxy=proxy, proxy_auth=proxy_auth, loop=self.loop) unsync_clock = options.pop('assume_unsync_clock', True)
self.http = HTTPClient(connector, proxy=proxy, proxy_auth=proxy_auth, unsync_clock=unsync_clock, loop=self.loop)
self._handlers = { self._handlers = {
'ready': self._handle_ready 'ready': self._handle_ready

View File

@ -86,7 +86,7 @@ class HTTPClient:
SUCCESS_LOG = '{method} {url} has received {text}' SUCCESS_LOG = '{method} {url} has received {text}'
REQUEST_LOG = '{method} {url} with {json} has returned {status}' REQUEST_LOG = '{method} {url} with {json} has returned {status}'
def __init__(self, connector=None, *, proxy=None, proxy_auth=None, loop=None): def __init__(self, connector=None, *, proxy=None, proxy_auth=None, loop=None, unsync_clock=True):
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.connector = connector self.connector = connector
self.__session = None # filled in static_login self.__session = None # filled in static_login
@ -97,6 +97,7 @@ class HTTPClient:
self.bot_token = False self.bot_token = False
self.proxy = proxy self.proxy = proxy
self.proxy_auth = proxy_auth self.proxy_auth = proxy_auth
self.use_clock = not unsync_clock
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}'
self.user_agent = user_agent.format(__version__, sys.version_info, aiohttp.__version__) self.user_agent = user_agent.format(__version__, sys.version_info, aiohttp.__version__)
@ -166,7 +167,7 @@ class HTTPClient:
remaining = r.headers.get('X-Ratelimit-Remaining') remaining = r.headers.get('X-Ratelimit-Remaining')
if remaining == '0' and r.status != 429: if remaining == '0' and r.status != 429:
# we've depleted our current bucket # we've depleted our current bucket
delta = utils._parse_ratelimit_header(r) delta = utils._parse_ratelimit_header(r, use_clock=self.use_clock)
log.debug('A rate limit bucket has been exhausted (bucket: %s, retry: %s).', bucket, delta) log.debug('A rate limit bucket has been exhausted (bucket: %s, retry: %s).', bucket, delta)
maybe_lock.defer() maybe_lock.defer()
self.loop.call_later(delta, lock.release) self.loop.call_later(delta, lock.release)

View File

@ -302,10 +302,15 @@ def _bytes_to_base64_data(data):
def to_json(obj): def to_json(obj):
return json.dumps(obj, separators=(',', ':'), ensure_ascii=True) return json.dumps(obj, separators=(',', ':'), ensure_ascii=True)
def _parse_ratelimit_header(request): def _parse_ratelimit_header(request, *, use_clock=False):
now = parsedate_to_datetime(request.headers['Date']) reset_after = request.headers.get('X-Ratelimit-Reset-After')
reset = datetime.datetime.fromtimestamp(float(request.headers['X-Ratelimit-Reset']), datetime.timezone.utc) if use_clock or not reset_after:
return (reset - now).total_seconds() utc = datetime.timezone.utc
now = datetime.datetime.now(utc)
reset = datetime.datetime.fromtimestamp(float(request.headers['X-Ratelimit-Reset']), utc)
return (reset - now).total_seconds()
else:
return float(reset_after)
async def maybe_coroutine(f, *args, **kwargs): async def maybe_coroutine(f, *args, **kwargs):
value = f(*args, **kwargs) value = f(*args, **kwargs)