Browse Source

Heartbeats bypass the rate limits for gateway

pull/5849/head
Rapptz 5 years ago
parent
commit
5fe998ac19
  1. 17
      discord/gateway.py

17
discord/gateway.py

@ -67,7 +67,8 @@ class WebSocketClosure(Exception):
EventListener = namedtuple('EventListener', 'predicate event result future') EventListener = namedtuple('EventListener', 'predicate event result future')
class GatewayRatelimiter: class GatewayRatelimiter:
def __init__(self, count=120, per=60.0): def __init__(self, count=110, per=60.0):
# The default is 110 to give room for at least 10 heartbeats per minute
self.max = count self.max = count
self.remaining = count self.remaining = count
self.window = 0.0 self.window = 0.0
@ -128,7 +129,7 @@ class KeepAliveHandler(threading.Thread):
data = self.get_payload() data = self.get_payload()
log.debug(self.msg, self.shard_id, data['d']) log.debug(self.msg, self.shard_id, data['d'])
coro = self.ws.send_as_json(data) coro = self.ws.send_heartbeat(data)
f = asyncio.run_coroutine_threadsafe(coro, loop=self.ws.loop) f = asyncio.run_coroutine_threadsafe(coro, loop=self.ws.loop)
try: try:
# block until sending is complete # block until sending is complete
@ -560,7 +561,7 @@ class DiscordWebSocket:
async def send(self, data): async def send(self, data):
delay = self._rate_limiter.get_delay() delay = self._rate_limiter.get_delay()
if delay: if delay:
log.warning('WebSocket is ratelimited, waiting %.2f seconds', delay) log.warning('WebSocket in shard ID %s is ratelimited, waiting %.2f seconds', self.shard_id, delay)
await asyncio.sleep(delay) await asyncio.sleep(delay)
self._dispatch('socket_raw_send', data) self._dispatch('socket_raw_send', data)
@ -573,6 +574,14 @@ class DiscordWebSocket:
if not self._can_handle_close(): if not self._can_handle_close():
raise ConnectionClosed(self.socket, shard_id=self.shard_id) from exc raise ConnectionClosed(self.socket, shard_id=self.shard_id) from exc
async def send_heartbeat(self, data):
# This bypasses the rate limit handling code since it has a higher priority
try:
await self.socket.send_str(utils.to_json(data))
except RuntimeError as exc:
if not self._can_handle_close():
raise ConnectionClosed(self.socket, shard_id=self.shard_id) from exc
async def change_presence(self, *, activity=None, status=None, afk=False, since=0.0): async def change_presence(self, *, activity=None, status=None, afk=False, since=0.0):
if activity is not None: if activity is not None:
if not isinstance(activity, BaseActivity): if not isinstance(activity, BaseActivity):
@ -700,6 +709,8 @@ class DiscordVoiceWebSocket:
log.debug('Sending voice websocket frame: %s.', data) log.debug('Sending voice websocket frame: %s.', data)
await self.ws.send_str(utils.to_json(data)) await self.ws.send_str(utils.to_json(data))
send_heartbeat = send_as_json
async def resume(self): async def resume(self):
state = self._connection state = self._connection
payload = { payload = {

Loading…
Cancel
Save