From 84c44a2d4ccc82d97727abf038c1ac0e8af394b9 Mon Sep 17 00:00:00 2001 From: Snipy7374 Date: Sat, 14 Jan 2023 20:58:27 +0100 Subject: [PATCH] feat(gateway): add reasons to close event codes (#881) Signed-off-by: Snipy7374 Co-authored-by: shiftinv <8530778+shiftinv@users.noreply.github.com> --- changelog/873.feature.rst | 1 + disnake/errors.py | 40 +++++++++++++++++++++++++++++++++++++-- disnake/gateway.py | 4 ++-- 3 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 changelog/873.feature.rst diff --git a/changelog/873.feature.rst b/changelog/873.feature.rst new file mode 100644 index 0000000000..b3083a4902 --- /dev/null +++ b/changelog/873.feature.rst @@ -0,0 +1 @@ +Adds reasons/descriptions to :exc:`ConnectionClosed` errors. diff --git a/disnake/errors.py b/disnake/errors.py index dce0236dcd..21a1834dff 100644 --- a/disnake/errors.py +++ b/disnake/errors.py @@ -224,20 +224,56 @@ class ConnectionClosed(ClientException): The shard ID that got closed if applicable. """ + # https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes + GATEWAY_CLOSE_EVENT_REASONS: Dict[int, str] = { + 4000: "Unknown error", + 4001: "Unknown opcode", + 4002: "Decode error", + 4003: "Not authenticated", + 4004: "Authentication failed", + 4005: "Already authenticated", + 4007: "Invalid sequence", + 4008: "Rate limited", + 4009: "Session timed out", + 4010: "Invalid Shard", + 4011: "Sharding required - you are required to shard your connection in order to connect.", + 4012: "Invalid API version", + 4013: "Invalid intents", + 4014: "Disallowed intents - you tried to specify an intent that you have not enabled or are not approved for.", + } + + # https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice-voice-close-event-codes + GATEWAY_VOICE_CLOSE_EVENT_REASONS: Dict[int, str] = { + **GATEWAY_CLOSE_EVENT_REASONS, + 4002: "Failed to decode payload", + 4006: "Session no longer valid", + 4011: "Server not found", + 4012: "Unknown protocol", + 4014: "Disconnected, channel was deleted, you were kicked, voice server changed, or the main gateway session was dropped.", + 4015: "Voice server crashed", + 4016: "Unknown encryption mode", + } + def __init__( self, socket: ClientWebSocketResponse, *, shard_id: Optional[int], code: Optional[int] = None, + voice: bool = False, ) -> None: # This exception is just the same exception except # reconfigured to subclass ClientException for users self.code: int = code or socket.close_code or -1 # aiohttp doesn't seem to consistently provide close reason - self.reason: str = "" + self.reason: str = self.GATEWAY_CLOSE_EVENT_REASONS.get(self.code, "Unknown reason") + if voice: + self.reason = self.GATEWAY_VOICE_CLOSE_EVENT_REASONS.get(self.code, "Unknown reason") + self.shard_id: Optional[int] = shard_id - super().__init__(f"Shard ID {self.shard_id} WebSocket closed with {self.code}") + super().__init__( + f"Shard ID {self.shard_id} WebSocket closed with {self.code}: {self.reason}" + ) class PrivilegedIntentsRequired(ClientException): diff --git a/disnake/gateway.py b/disnake/gateway.py index c5f33221e4..ad52f901e6 100644 --- a/disnake/gateway.py +++ b/disnake/gateway.py @@ -1076,14 +1076,14 @@ async def poll_event(self) -> None: await self.received_message(utils._from_json(msg.data)) elif msg.type is aiohttp.WSMsgType.ERROR: _log.debug("Received %s", msg) - raise ConnectionClosed(self.ws, shard_id=None) from msg.data + raise ConnectionClosed(self.ws, shard_id=None, voice=True) from msg.data elif msg.type in ( aiohttp.WSMsgType.CLOSED, aiohttp.WSMsgType.CLOSE, aiohttp.WSMsgType.CLOSING, ): _log.debug("Received %s", msg) - raise ConnectionClosed(self.ws, shard_id=None, code=self._close_code) + raise ConnectionClosed(self.ws, shard_id=None, code=self._close_code, voice=True) async def close(self, code: int = 1000) -> None: if self._keep_alive is not None: