extend join and part messages

This commit is contained in:
kc1awv
2026-01-08 08:01:05 -05:00
parent 726e3f5a18
commit 23094f6a12
4 changed files with 130 additions and 1 deletions
+8
View File
@@ -2,6 +2,14 @@
This project follows the versioning policy in VERSIONING.md.
## 0.2.1 - 2026-01-08
- **JOINED/PARTED room notifications**: Existing room members now receive real-time notifications when users join or leave
- When a user joins a room, existing members receive a `JOINED` message with the joining user's identity hash
- When a user leaves a room, remaining members receive a `PARTED` message with the parting user's identity hash
- Joining/parting users continue to receive the full member list (when `include_joined_member_list` is enabled)
- See EX1-RRCD.md for detailed protocol documentation
## 0.2.0 - 2026-01-07
- **Major internal refactoring**: Improved code organization and maintainability
+82
View File
@@ -271,6 +271,88 @@ periodically.
room_invite_timeout_s = 900.0
```
## Extension: JOINED and PARTED Room Notifications
The RRC specification defines `JOINED` and `PARTED` messages but doesn't specify
whether room members should be notified when users join or leave. rrcd
implements dual-mode notifications:
### JOIN Behavior
When a user joins a room:
1. **Joining user receives**: A `JOINED` message containing the full list of
room members (if `include_joined_member_list` is enabled in config). This
allows the client to know who is already in the room.
```python
{
0: 1, # protocol version
1: T_JOINED, # message type
2: <msg-id>,
3: <timestamp>,
4: <hub-identity-hash>, # src
5: <room-name>,
6: [<hash1>, <hash2>, ...] # body: list of all member identity hashes
}
```
2. **Existing room members receive**: A `JOINED` message containing **only** the
identity hash of the user who just joined. This allows room members to update
their member lists.
```python
{
0: 1,
1: T_JOINED,
2: <msg-id>,
3: <timestamp>,
4: <hub-identity-hash>,
5: <room-name>,
6: [<new-user-hash>] # body: single-element list
}
```
### PART Behavior
When a user leaves a room:
1. **Parting user receives**: A `PARTED` message containing the list of
remaining room members (if `include_joined_member_list` is enabled).
2. **Remaining room members receive**: A `PARTED` message containing **only**
the identity hash of the user who just left.
```python
{
0: 1,
1: T_PARTED,
2: <msg-id>,
3: <timestamp>,
4: <hub-identity-hash>,
5: <room-name>,
6: [<departed-user-hash>] # body: single-element list
}
```
### Configuration
```toml
include_joined_member_list = true # default: true
```
When disabled, all `JOINED` and `PARTED` messages have `null` or empty bodies.
### Client Implementation Notes
- **JOINED bodies** may contain either a full member list (multiple hashes) or a
single hash. Clients should handle both cases.
- **PARTED bodies** follow the same pattern.
- The message source (`K_SRC`) is always the hub's identity hash, not the
joining/parting user.
- This extension allows clients to maintain accurate room member lists without
polling or issuing `/who` commands after every join/part.
## Extension: Nickname Normalization
The RRC spec says nicknames are "advisory" and may be "ridiculous." rrcd
+1 -1
View File
@@ -1,3 +1,3 @@
__all__ = ["__version__"]
__version__ = "0.2.0"
__version__ = "0.2.1"
+39
View File
@@ -492,6 +492,24 @@ class MessageRouter:
self.hub.room_manager.touch_room(r)
# Notify existing room members about the new joiner
# Send JOINED message with single identity hash to existing members
existing_members = [
member_link
for member_link in self.hub.room_manager.get_room_members(r)
if member_link != link
]
if existing_members:
member_notification = make_envelope(
T_JOINED, src=self.hub.identity.hash, room=r, body=[peer_hash]
)
member_notification_payload = encode(member_notification)
for member_link in existing_members:
self.hub.message_helper.queue_payload(
outgoing, member_link, member_notification_payload
)
# Send JOINED message with full member list to the joining user
joined_body = None
if self.hub.config.include_joined_member_list:
members: list[bytes] = []
@@ -563,6 +581,14 @@ class MessageRouter:
return
sess["rooms"].discard(r)
# Get remaining members before removing the parting user
remaining_members = [
member_link
for member_link in self.hub.room_manager.get_room_members(r)
if member_link != link
]
if self.hub.room_manager.get_room_members(r):
self.hub.room_manager.remove_member(r, link)
if not self.hub.room_manager.get_room_members(r):
@@ -575,6 +601,19 @@ class MessageRouter:
if st is not None and not st.get("registered"):
self.hub.room_manager._room_state.pop(r, None)
# Notify remaining members about the user parting
# Send PARTED message with single identity hash to remaining members
if remaining_members:
member_notification = make_envelope(
T_PARTED, src=self.hub.identity.hash, room=r, body=[peer_hash]
)
member_notification_payload = encode(member_notification)
for member_link in remaining_members:
self.hub.message_helper.queue_payload(
outgoing, member_link, member_notification_payload
)
# Send PARTED message with current member list to the parting user
parted_body = None
if self.hub.config.include_joined_member_list:
members: list[bytes] = []