55* https://pypi.org/project/websockets/
66
77"""
8+
89import asyncio
910import logging
1011from asyncio import Future , Lock
1516import websockets
1617from websockets .exceptions import WebSocketException
1718
18- # To keep compatibility with websockets 8.x, we use this import over .legacy.client
19- from websockets import WebSocketClientProtocol
19+ try :
20+ from websockets .asyncio .client import ClientConnection
21+ except ImportError :
22+ # To keep compatibility with websockets <14.x we use WebSocketClientProtocol
23+ # To keep compatibility with websockets 8.x, we use this import over .legacy.client
24+ from websockets import WebSocketClientProtocol as ClientConnection
25+
2026
2127from slack_sdk .socket_mode .async_client import AsyncBaseSocketModeClient
2228from slack_sdk .socket_mode .async_listeners import (
2935from ..logger .messages import debug_redacted_message_string
3036
3137
38+ def _session_closed (session : Optional [ClientConnection ]) -> bool :
39+ if session is None :
40+ return True
41+ if hasattr (session , "closed" ):
42+ # The session is a WebSocketClientProtocol instance
43+ return session .closed
44+ # WebSocket close code, defined in https://datatracker.ietf.org/doc/html/rfc6455.html#section-7.1.5
45+ # None if the connection isn’t closed yet.
46+ return session .close_code is not None
47+
48+
3249class SocketModeClient (AsyncBaseSocketModeClient ):
3350 logger : Logger
3451 web_client : AsyncWebClient
@@ -55,7 +72,7 @@ class SocketModeClient(AsyncBaseSocketModeClient):
5572 ping_interval : float
5673 trace_enabled : bool
5774
58- current_session : Optional [WebSocketClientProtocol ]
75+ current_session : Optional [ClientConnection ]
5976 current_session_monitor : Optional [Future ]
6077
6178 auto_reconnect_enabled : bool
@@ -105,7 +122,7 @@ async def monitor_current_session(self) -> None:
105122 # In the asyncio runtime, accessing a shared object (self.current_session here) from
106123 # multiple tasks can cause race conditions and errors.
107124 # To avoid such, we access only the session that is active when this loop starts.
108- session : WebSocketClientProtocol = self .current_session
125+ session : ClientConnection = self .current_session
109126 session_id : str = await self .session_id ()
110127 if self .logger .level <= logging .DEBUG :
111128 self .logger .debug (f"A new monitor_current_session() execution loop for { session_id } started" )
@@ -117,7 +134,7 @@ async def monitor_current_session(self) -> None:
117134 break
118135 await asyncio .sleep (self .ping_interval )
119136 try :
120- if self .auto_reconnect_enabled and (session is None or session . closed ):
137+ if self .auto_reconnect_enabled and _session_closed (session = session ):
121138 self .logger .info (f"The session ({ session_id } ) seems to be already closed. Reconnecting..." )
122139 await self .connect_to_new_endpoint ()
123140 except Exception as e :
@@ -134,7 +151,7 @@ async def receive_messages(self) -> None:
134151 # In the asyncio runtime, accessing a shared object (self.current_session here) from
135152 # multiple tasks can cause race conditions and errors.
136153 # To avoid such, we access only the session that is active when this loop starts.
137- session : WebSocketClientProtocol = self .current_session
154+ session : ClientConnection = self .current_session
138155 session_id : str = await self .session_id ()
139156 consecutive_error_count = 0
140157 if self .logger .level <= logging .DEBUG :
@@ -171,15 +188,15 @@ async def receive_messages(self) -> None:
171188 raise
172189
173190 async def is_connected (self ) -> bool :
174- return not self .closed and self . current_session is not None and not self .current_session . closed
191+ return not self .closed and not _session_closed ( self .current_session )
175192
176193 async def session_id (self ) -> str :
177194 return self .build_session_id (self .current_session )
178195
179196 async def connect (self ):
180197 if self .wss_uri is None :
181198 self .wss_uri = await self .issue_new_wss_url ()
182- old_session : Optional [WebSocketClientProtocol ] = None if self .current_session is None else self .current_session
199+ old_session : Optional [ClientConnection ] = None if self .current_session is None else self .current_session
183200 # NOTE: websockets does not support proxy settings
184201 self .current_session = await websockets .connect (
185202 uri = self .wss_uri ,
@@ -250,7 +267,7 @@ async def close(self):
250267 self .message_receiver .cancel ()
251268
252269 @classmethod
253- def build_session_id (cls , session : WebSocketClientProtocol ) -> str :
270+ def build_session_id (cls , session : ClientConnection ) -> str :
254271 if session is None :
255272 return ""
256273 return "s_" + str (hash (session ))
0 commit comments