Skip to content

Instantly share code, notes, and snippets.

@janovak
Created March 27, 2024 05:47
Show Gist options
  • Select an option

  • Save janovak/06664d9d3c1443d697ca07c602a8edfc to your computer and use it in GitHub Desktop.

Select an option

Save janovak/06664d9d3c1443d697ca07c602a8edfc to your computer and use it in GitHub Desktop.
Add lock to guard writing to connection
diff --git a/twitchAPI/chat/__init__.py b/twitchAPI/chat/__init__.py
index d1b7ce8..b36f089 100644
--- a/twitchAPI/chat/__init__.py
+++ b/twitchAPI/chat/__init__.py
@@ -371,7 +371,7 @@ class HypeChat:
self.currency: str = parsed['tags'].get('pinned-chat-paid-currency')
"""The ISO 4217 alphabetic currency code the user has sent the Hype Chat in."""
self.exponent: int = int(parsed['tags'].get('pinned-chat-paid-exponent'))
"""Indicates how many decimal points this currency represents partial amounts in.
Decimal points start from the right side of the value defined in :const:`~twitchAPI.chat.HypeChat.amount`"""
self.level: str = parsed['tags'].get('pinned-chat-paid-level')
"""The level of the Hype Chat, in English.\n
@@ -589,6 +589,7 @@ class Chat:
self.log_no_registered_command_handler: bool = True
"""Controls if instances of commands being issued in chat where no handler exists should be logged. |default|:code:`True`"""
self.__connection = None
+ self.__connection_lock = threading.Lock()
self._session = None
self.__socket_thread: Optional[threading.Thread] = None
self.__running: bool = False
@@ -1069,7 +1070,8 @@ class Chat:
async def _handle_ping(self, parsed: dict):
self.logger.debug('got PING')
- await self._send_message('PONG ' + parsed['parameters'])
+ with self.__connection_lock:
+ await self._send_message('PONG ' + parsed['parameters'])
# noinspection PyUnusedLocal
async def _handle_ready(self, parsed: dict):
@@ -1122,10 +1124,11 @@ class Chat:
t.add_done_callback(self._task_callback)
async def __task_startup(self):
- await self._send_message('CAP REQ :twitch.tv/membership twitch.tv/tags twitch.tv/commands')
- await self._send_message(f'PASS oauth:{await self.twitch.get_refreshed_user_auth_token()}')
- await self._send_message(f'NICK {self.username}')
- self.__startup_complete = True
+ with self.__connection_lock:
+ await self._send_message('CAP REQ :twitch.tv/membership twitch.tv/tags twitch.tv/commands')
+ await self._send_message(f'PASS oauth:{await self.twitch.get_refreshed_user_auth_token()}')
+ await self._send_message(f'NICK {self.username}')
+ self.__startup_complete = True
def _get_message_bucket(self, channel) -> RateLimitBucket:
bucket = self._send_buckets.get(channel)
@@ -1304,13 +1307,16 @@ class Chat:
if len(target) > self._join_bucket.left():
# we want to join more than the current bucket has left, join slowly one after another
# TODO we could join the current remaining bucket size in blocks
- for r in target:
- await self._join_bucket.put()
- await self._send_message(f'JOIN #{r}')
+
+ with self.__connection_lock:
+ for r in target:
+ await self._join_bucket.put()
+ await self._send_message(f'JOIN #{r}')
else:
# enough space in the current bucket left, join all at once
- await self._join_bucket.put(len(target))
- await self._send_message(f'JOIN {",".join([f"#{x}" for x in target])}')
+ with self.__connection_lock:
+ await self._join_bucket.put(len(target))
+ await self._send_message(f'JOIN {",".join([f"#{x}" for x in target])}')
# wait for us to join all rooms
timeout = datetime.datetime.now() + datetime.timedelta(seconds=self.join_timeout)
while any([r in self._room_join_locks for r in target]) and timeout > datetime.datetime.now():
@@ -1335,7 +1341,8 @@ class Chat:
await asyncio.sleep(0.1)
if message is None or len(message) == 0:
raise ValueError('message must be a non empty string')
- await self._send_message(message)
+ with self.__connection_lock:
+ await self._send_message(message)
async def send_message(self, room: CHATROOM_TYPE, text: str):
"""Send a message to the given channel
@@ -1362,7 +1369,9 @@ class Chat:
room = f'#{room}'.lower()
bucket = self._get_message_bucket(room[1:])
await bucket.put()
- await self._send_message(f'PRIVMSG {room} :{text}')
+
+ with self.__connection_lock:
+ await self._send_message(f'PRIVMSG {room} :{text}')
async def leave_room(self, chat_rooms: Union[List[str], str]):
"""leave one or more chat rooms\n
@@ -1375,7 +1384,8 @@ class Chat:
target = [c[1:].lower() if c[0] == '#' else c.lower() for c in chat_rooms]
for r in target:
self._room_leave_locks.append(r)
- await self._send_message(f'PART {room_str}')
+ with self.__connection_lock:
+ await self._send_message(f'PART {room_str}')
for x in target:
if x in self._join_target:
self._join_target.remove(x)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment