|
@ -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 = { |
|
|