Skip to content

Commit

Permalink
Add is_dm room field to Sliding Sync /sync (#17429)
Browse files Browse the repository at this point in the history
  • Loading branch information
MadLittleMods authored Jul 11, 2024
1 parent 5a97bbd commit fb66e93
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 29 deletions.
1 change: 1 addition & 0 deletions changelog.d/17429.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Populate `is_dm` room field in experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint.
75 changes: 46 additions & 29 deletions synapse/handlers/sliding_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ class _RoomMembershipForUser:
sender: The person who sent the membership event
newly_joined: Whether the user newly joined the room during the given token
range
is_dm: Whether this user considers this room as a direct-message (DM) room
"""

room_id: str
Expand All @@ -299,6 +300,7 @@ class _RoomMembershipForUser:
membership: str
sender: Optional[str]
newly_joined: bool
is_dm: bool

def copy_and_replace(self, **kwds: Any) -> "_RoomMembershipForUser":
return attr.evolve(self, **kwds)
Expand Down Expand Up @@ -613,6 +615,7 @@ async def get_sync_room_ids_for_user(
membership=room_for_user.membership,
sender=room_for_user.sender,
newly_joined=False,
is_dm=False,
)
for room_for_user in room_for_user_list
}
Expand Down Expand Up @@ -652,6 +655,7 @@ async def get_sync_room_ids_for_user(
# - 1c) Update room membership events to the point in time of the `to_token`
# - 2) Add back newly_left rooms (> `from_token` and <= `to_token`)
# - 3) Figure out which rooms are `newly_joined`
# - 4) Figure out which rooms are DM's

# 1) -----------------------------------------------------

Expand Down Expand Up @@ -714,6 +718,7 @@ async def get_sync_room_ids_for_user(
membership=first_membership_change_after_to_token.prev_membership,
sender=first_membership_change_after_to_token.prev_sender,
newly_joined=False,
is_dm=False,
)
else:
# If we can't find the previous membership event, we shouldn't
Expand Down Expand Up @@ -809,6 +814,7 @@ async def get_sync_room_ids_for_user(
membership=last_membership_change_in_from_to_range.membership,
sender=last_membership_change_in_from_to_range.sender,
newly_joined=False,
is_dm=False,
)

# 3) Figure out `newly_joined`
Expand Down Expand Up @@ -846,6 +852,35 @@ async def get_sync_room_ids_for_user(
room_id
].copy_and_replace(newly_joined=True)

# 4) Figure out which rooms the user considers to be direct-message (DM) rooms
#
# We're using global account data (`m.direct`) instead of checking for
# `is_direct` on membership events because that property only appears for
# the invitee membership event (doesn't show up for the inviter).
#
# We're unable to take `to_token` into account for global account data since
# we only keep track of the latest account data for the user.
dm_map = await self.store.get_global_account_data_by_type_for_user(
user_id, AccountDataTypes.DIRECT
)

# Flatten out the map. Account data is set by the client so it needs to be
# scrutinized.
dm_room_id_set = set()
if isinstance(dm_map, dict):
for room_ids in dm_map.values():
# Account data should be a list of room IDs. Ignore anything else
if isinstance(room_ids, list):
for room_id in room_ids:
if isinstance(room_id, str):
dm_room_id_set.add(room_id)

# 4) Fixup
for room_id in filtered_sync_room_id_set:
filtered_sync_room_id_set[room_id] = filtered_sync_room_id_set[
room_id
].copy_and_replace(is_dm=room_id in dm_room_id_set)

return filtered_sync_room_id_set

async def filter_rooms(
Expand All @@ -869,41 +904,24 @@ async def filter_rooms(
A filtered dictionary of room IDs along with membership information in the
room at the time of `to_token`.
"""
user_id = user.to_string()

# TODO: Apply filters

filtered_room_id_set = set(sync_room_map.keys())

# Filter for Direct-Message (DM) rooms
if filters.is_dm is not None:
# We're using global account data (`m.direct`) instead of checking for
# `is_direct` on membership events because that property only appears for
# the invitee membership event (doesn't show up for the inviter). Account
# data is set by the client so it needs to be scrutinized.
#
# We're unable to take `to_token` into account for global account data since
# we only keep track of the latest account data for the user.
dm_map = await self.store.get_global_account_data_by_type_for_user(
user_id, AccountDataTypes.DIRECT
)

# Flatten out the map
dm_room_id_set = set()
if isinstance(dm_map, dict):
for room_ids in dm_map.values():
# Account data should be a list of room IDs. Ignore anything else
if isinstance(room_ids, list):
for room_id in room_ids:
if isinstance(room_id, str):
dm_room_id_set.add(room_id)

if filters.is_dm:
# Only DM rooms please
filtered_room_id_set = filtered_room_id_set.intersection(dm_room_id_set)
filtered_room_id_set = {
room_id
for room_id in filtered_room_id_set
if sync_room_map[room_id].is_dm
}
else:
# Only non-DM rooms please
filtered_room_id_set = filtered_room_id_set.difference(dm_room_id_set)
filtered_room_id_set = {
room_id
for room_id in filtered_room_id_set
if not sync_room_map[room_id].is_dm
}

if filters.spaces:
raise NotImplementedError()
Expand Down Expand Up @@ -1538,8 +1556,7 @@ async def get_room_sync_data(
name=room_name,
avatar=room_avatar,
heroes=heroes,
# TODO: Dummy value
is_dm=False,
is_dm=room_membership_for_user_at_to_token.is_dm,
initial=initial,
required_state=list(required_room_state.values()),
timeline_events=timeline_events,
Expand Down
23 changes: 23 additions & 0 deletions tests/rest/client/test_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -1662,6 +1662,20 @@ def test_filter_list(self) -> None:
list(channel.json_body["lists"]["room-invites"]),
)

# Ensure DM's are correctly marked
self.assertDictEqual(
{
room_id: room.get("is_dm")
for room_id, room in channel.json_body["rooms"].items()
},
{
invite_room_id: None,
room_id: None,
invited_dm_room_id: True,
joined_dm_room_id: True,
},
)

def test_sort_list(self) -> None:
"""
Test that the `lists` are sorted by `stream_ordering`
Expand Down Expand Up @@ -1874,6 +1888,9 @@ def test_rooms_meta_when_joined(self) -> None:
channel.json_body["rooms"][room_id1]["invited_count"],
0,
)
self.assertIsNone(
channel.json_body["rooms"][room_id1].get("is_dm"),
)

def test_rooms_meta_when_invited(self) -> None:
"""
Expand Down Expand Up @@ -1955,6 +1972,9 @@ def test_rooms_meta_when_invited(self) -> None:
channel.json_body["rooms"][room_id1]["invited_count"],
1,
)
self.assertIsNone(
channel.json_body["rooms"][room_id1].get("is_dm"),
)

def test_rooms_meta_when_banned(self) -> None:
"""
Expand Down Expand Up @@ -2037,6 +2057,9 @@ def test_rooms_meta_when_banned(self) -> None:
channel.json_body["rooms"][room_id1]["invited_count"],
0,
)
self.assertIsNone(
channel.json_body["rooms"][room_id1].get("is_dm"),
)

def test_rooms_meta_heroes(self) -> None:
"""
Expand Down

0 comments on commit fb66e93

Please sign in to comment.