Skip to content

Commit fb66e93

Browse files
Add is_dm room field to Sliding Sync /sync (#17429)
Based on [MSC3575](matrix-org/matrix-spec-proposals#3575): Sliding Sync
1 parent 5a97bbd commit fb66e93

File tree

3 files changed

+70
-29
lines changed

3 files changed

+70
-29
lines changed

changelog.d/17429.feature

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Populate `is_dm` room field in experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint.

synapse/handlers/sliding_sync.py

+46-29
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ class _RoomMembershipForUser:
291291
sender: The person who sent the membership event
292292
newly_joined: Whether the user newly joined the room during the given token
293293
range
294+
is_dm: Whether this user considers this room as a direct-message (DM) room
294295
"""
295296

296297
room_id: str
@@ -299,6 +300,7 @@ class _RoomMembershipForUser:
299300
membership: str
300301
sender: Optional[str]
301302
newly_joined: bool
303+
is_dm: bool
302304

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

656660
# 1) -----------------------------------------------------
657661

@@ -714,6 +718,7 @@ async def get_sync_room_ids_for_user(
714718
membership=first_membership_change_after_to_token.prev_membership,
715719
sender=first_membership_change_after_to_token.prev_sender,
716720
newly_joined=False,
721+
is_dm=False,
717722
)
718723
else:
719724
# If we can't find the previous membership event, we shouldn't
@@ -809,6 +814,7 @@ async def get_sync_room_ids_for_user(
809814
membership=last_membership_change_in_from_to_range.membership,
810815
sender=last_membership_change_in_from_to_range.sender,
811816
newly_joined=False,
817+
is_dm=False,
812818
)
813819

814820
# 3) Figure out `newly_joined`
@@ -846,6 +852,35 @@ async def get_sync_room_ids_for_user(
846852
room_id
847853
].copy_and_replace(newly_joined=True)
848854

855+
# 4) Figure out which rooms the user considers to be direct-message (DM) rooms
856+
#
857+
# We're using global account data (`m.direct`) instead of checking for
858+
# `is_direct` on membership events because that property only appears for
859+
# the invitee membership event (doesn't show up for the inviter).
860+
#
861+
# We're unable to take `to_token` into account for global account data since
862+
# we only keep track of the latest account data for the user.
863+
dm_map = await self.store.get_global_account_data_by_type_for_user(
864+
user_id, AccountDataTypes.DIRECT
865+
)
866+
867+
# Flatten out the map. Account data is set by the client so it needs to be
868+
# scrutinized.
869+
dm_room_id_set = set()
870+
if isinstance(dm_map, dict):
871+
for room_ids in dm_map.values():
872+
# Account data should be a list of room IDs. Ignore anything else
873+
if isinstance(room_ids, list):
874+
for room_id in room_ids:
875+
if isinstance(room_id, str):
876+
dm_room_id_set.add(room_id)
877+
878+
# 4) Fixup
879+
for room_id in filtered_sync_room_id_set:
880+
filtered_sync_room_id_set[room_id] = filtered_sync_room_id_set[
881+
room_id
882+
].copy_and_replace(is_dm=room_id in dm_room_id_set)
883+
849884
return filtered_sync_room_id_set
850885

851886
async def filter_rooms(
@@ -869,41 +904,24 @@ async def filter_rooms(
869904
A filtered dictionary of room IDs along with membership information in the
870905
room at the time of `to_token`.
871906
"""
872-
user_id = user.to_string()
873-
874-
# TODO: Apply filters
875-
876907
filtered_room_id_set = set(sync_room_map.keys())
877908

878909
# Filter for Direct-Message (DM) rooms
879910
if filters.is_dm is not None:
880-
# We're using global account data (`m.direct`) instead of checking for
881-
# `is_direct` on membership events because that property only appears for
882-
# the invitee membership event (doesn't show up for the inviter). Account
883-
# data is set by the client so it needs to be scrutinized.
884-
#
885-
# We're unable to take `to_token` into account for global account data since
886-
# we only keep track of the latest account data for the user.
887-
dm_map = await self.store.get_global_account_data_by_type_for_user(
888-
user_id, AccountDataTypes.DIRECT
889-
)
890-
891-
# Flatten out the map
892-
dm_room_id_set = set()
893-
if isinstance(dm_map, dict):
894-
for room_ids in dm_map.values():
895-
# Account data should be a list of room IDs. Ignore anything else
896-
if isinstance(room_ids, list):
897-
for room_id in room_ids:
898-
if isinstance(room_id, str):
899-
dm_room_id_set.add(room_id)
900-
901911
if filters.is_dm:
902912
# Only DM rooms please
903-
filtered_room_id_set = filtered_room_id_set.intersection(dm_room_id_set)
913+
filtered_room_id_set = {
914+
room_id
915+
for room_id in filtered_room_id_set
916+
if sync_room_map[room_id].is_dm
917+
}
904918
else:
905919
# Only non-DM rooms please
906-
filtered_room_id_set = filtered_room_id_set.difference(dm_room_id_set)
920+
filtered_room_id_set = {
921+
room_id
922+
for room_id in filtered_room_id_set
923+
if not sync_room_map[room_id].is_dm
924+
}
907925

908926
if filters.spaces:
909927
raise NotImplementedError()
@@ -1538,8 +1556,7 @@ async def get_room_sync_data(
15381556
name=room_name,
15391557
avatar=room_avatar,
15401558
heroes=heroes,
1541-
# TODO: Dummy value
1542-
is_dm=False,
1559+
is_dm=room_membership_for_user_at_to_token.is_dm,
15431560
initial=initial,
15441561
required_state=list(required_room_state.values()),
15451562
timeline_events=timeline_events,

tests/rest/client/test_sync.py

+23
Original file line numberDiff line numberDiff line change
@@ -1662,6 +1662,20 @@ def test_filter_list(self) -> None:
16621662
list(channel.json_body["lists"]["room-invites"]),
16631663
)
16641664

1665+
# Ensure DM's are correctly marked
1666+
self.assertDictEqual(
1667+
{
1668+
room_id: room.get("is_dm")
1669+
for room_id, room in channel.json_body["rooms"].items()
1670+
},
1671+
{
1672+
invite_room_id: None,
1673+
room_id: None,
1674+
invited_dm_room_id: True,
1675+
joined_dm_room_id: True,
1676+
},
1677+
)
1678+
16651679
def test_sort_list(self) -> None:
16661680
"""
16671681
Test that the `lists` are sorted by `stream_ordering`
@@ -1874,6 +1888,9 @@ def test_rooms_meta_when_joined(self) -> None:
18741888
channel.json_body["rooms"][room_id1]["invited_count"],
18751889
0,
18761890
)
1891+
self.assertIsNone(
1892+
channel.json_body["rooms"][room_id1].get("is_dm"),
1893+
)
18771894

18781895
def test_rooms_meta_when_invited(self) -> None:
18791896
"""
@@ -1955,6 +1972,9 @@ def test_rooms_meta_when_invited(self) -> None:
19551972
channel.json_body["rooms"][room_id1]["invited_count"],
19561973
1,
19571974
)
1975+
self.assertIsNone(
1976+
channel.json_body["rooms"][room_id1].get("is_dm"),
1977+
)
19581978

19591979
def test_rooms_meta_when_banned(self) -> None:
19601980
"""
@@ -2037,6 +2057,9 @@ def test_rooms_meta_when_banned(self) -> None:
20372057
channel.json_body["rooms"][room_id1]["invited_count"],
20382058
0,
20392059
)
2060+
self.assertIsNone(
2061+
channel.json_body["rooms"][room_id1].get("is_dm"),
2062+
)
20402063

20412064
def test_rooms_meta_heroes(self) -> None:
20422065
"""

0 commit comments

Comments
 (0)