From 5ea91a011e1bdc0d398dbb204bcde4ad308d6e39 Mon Sep 17 00:00:00 2001 From: kc1awv Date: Sat, 16 May 2026 23:48:33 +0000 Subject: [PATCH] fix: improve multi-link identity handling to prevent incorrect room state emissions --- CHANGELOG.md | 2 ++ rrcd/router.py | 10 +++++++++- rrcd/session.py | 13 +++++++++++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58a0a0d..e1f0cd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ This project follows the versioning policy in VERSIONING.md. - Added core message type `ACTION` (`T_ACTION = 22`) routing with room-content semantics - Added advisory capability flag `CAP_ACTION` and now include `B_WELCOME_CAPS` in WELCOME payloads - ACTION bodies are forwarded as-is and are not interpreted as slash commands by the hub +- Fixed multi-link identity handling: do not emit room `PARTED` or clear + hash-link index state when a peer still remains in the room via another active link (thanks, neutral for the patch!) ## 0.2.2 - 2026-01-09 diff --git a/rrcd/router.py b/rrcd/router.py index f182aec..16d1f6e 100644 --- a/rrcd/router.py +++ b/rrcd/router.py @@ -602,7 +602,15 @@ class MessageRouter: if st is not None and not st.get("registered"): self.hub.room_manager._room_state.pop(r, None) - if remaining_members and self.hub.identity is not None: + peer_still_in_room = False + if peer_hash: + for member_link in remaining_members: + other = self.hub.session_manager.sessions.get(member_link) + if other and other.get("peer") == peer_hash: + peer_still_in_room = True + break + + if remaining_members and self.hub.identity is not None and not peer_still_in_room: notification_body = ( [peer_hash] if self.hub.config.include_joined_member_list else None ) diff --git a/rrcd/session.py b/rrcd/session.py index 3a9c2c7..0779205 100644 --- a/rrcd/session.py +++ b/rrcd/session.py @@ -119,7 +119,8 @@ class SessionManager: rooms_count = len(sess.get("rooms") or ()) if isinstance(peer, (bytes, bytearray)): - self._index_by_hash.pop(bytes(peer), None) + if self._index_by_hash.get(bytes(peer)) is link: + self._index_by_hash.pop(bytes(peer), None) if nick: self.update_nick_index(link, nick, None) @@ -134,7 +135,15 @@ class SessionManager: self.hub.room_manager.remove_member(room, link) - if remaining_members and peer_hash and self.hub.identity: + peer_still_in_room = False + if peer_hash: + for member_link in remaining_members: + other = self.sessions.get(member_link) + if other and other.get("peer") == peer_hash: + peer_still_in_room = True + break + + if remaining_members and peer_hash and self.hub.identity and not peer_still_in_room: notification_body = ( [peer_hash] if self.hub.config.include_joined_member_list else None )