Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 0e15dd8

Browse files
committed
Support a thread_id parameter for receipts.
1 parent 03c2bfb commit 0e15dd8

File tree

9 files changed

+59
-7
lines changed

9 files changed

+59
-7
lines changed

changelog.d/13782.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Experimental support for thread-specific receipts ([MSC3771](https://github.com/matrix-org/matrix-spec-proposals/pull/3771)).

synapse/config/experimental.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None:
8383
# MSC3786 (Add a default push rule to ignore m.room.server_acl events)
8484
self.msc3786_enabled: bool = experimental.get("msc3786_enabled", False)
8585

86+
# MSC3771: Thread read receipts
87+
self.msc3771_enabled: bool = experimental.get("msc3771_enabled", False)
8688
# MSC3772: A push rule for mutual relations.
8789
self.msc3772_enabled: bool = experimental.get("msc3772_enabled", False)
8890

synapse/handlers/receipts.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,23 @@ async def _received_remote_receipt(self, origin: str, content: JsonDict) -> None
9191
)
9292
continue
9393

94+
# Check if these receipts apply to a thread.
95+
thread_id = None
96+
data = user_values.get("data", {})
97+
if isinstance(data, dict):
98+
thread_id = data.get("thread_id")
99+
# If the thread ID is invalid, consider it missing.
100+
if not isinstance(thread_id, str):
101+
thread_id = None
102+
94103
receipts.append(
95104
ReadReceipt(
96105
room_id=room_id,
97106
receipt_type=receipt_type,
98107
user_id=user_id,
99108
event_ids=user_values["event_ids"],
100-
data=user_values.get("data", {}),
109+
thread_id=thread_id,
110+
data=data,
101111
)
102112
)
103113

@@ -146,7 +156,12 @@ async def _handle_new_receipts(self, receipts: List[ReadReceipt]) -> bool:
146156
return True
147157

148158
async def received_client_receipt(
149-
self, room_id: str, receipt_type: str, user_id: str, event_id: str
159+
self,
160+
room_id: str,
161+
receipt_type: str,
162+
user_id: str,
163+
event_id: str,
164+
thread_id: Optional[str],
150165
) -> None:
151166
"""Called when a client tells us a local user has read up to the given
152167
event_id in the room.
@@ -156,6 +171,7 @@ async def received_client_receipt(
156171
receipt_type=receipt_type,
157172
user_id=user_id,
158173
event_ids=[event_id],
174+
thread_id=thread_id,
159175
data={"ts": int(self.clock.time_msec())},
160176
)
161177

synapse/replication/tcp/client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,8 @@ async def _on_new_receipts(
427427
receipt.receipt_type,
428428
receipt.user_id,
429429
[receipt.event_id],
430-
receipt.data,
430+
thread_id=receipt.thread_id,
431+
data=receipt.data,
431432
)
432433
await self.federation_sender.send_read_receipt(receipt_info)
433434

synapse/rest/client/read_marker.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ async def on_POST(
8383
receipt_type,
8484
user_id=requester.user.to_string(),
8585
event_id=event_id,
86+
# Setting the thread ID is not possible with the /read_markers endpoint.
87+
thread_id=None,
8688
)
8789

8890
return 200, {}

synapse/rest/client/receipts.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ def __init__(self, hs: "HomeServer"):
4949
ReceiptTypes.READ_PRIVATE,
5050
ReceiptTypes.FULLY_READ,
5151
}
52+
self._msc3771_enabled = hs.config.experimental.msc3771_enabled
5253

5354
async def on_POST(
5455
self, request: SynapseRequest, room_id: str, receipt_type: str, event_id: str
@@ -61,7 +62,17 @@ async def on_POST(
6162
f"Receipt type must be {', '.join(self._known_receipt_types)}",
6263
)
6364

64-
parse_json_object_from_request(request, allow_empty_body=False)
65+
body = parse_json_object_from_request(request)
66+
67+
# Pull the thread ID, if one exists.
68+
thread_id = None
69+
if self._msc3771_enabled:
70+
if "thread_id" in body:
71+
thread_id = body.get("thread_id")
72+
if not thread_id or not isinstance(thread_id, str):
73+
raise SynapseError(
74+
400, "thread_id field must be a non-empty string"
75+
)
6576

6677
await self.presence_handler.bump_presence_active_time(requester.user)
6778

@@ -77,6 +88,7 @@ async def on_POST(
7788
receipt_type,
7889
user_id=requester.user.to_string(),
7990
event_id=event_id,
91+
thread_id=thread_id,
8092
)
8193

8294
return 200, {}

synapse/rest/client/versions.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ def on_GET(self, request: Request) -> Tuple[int, JsonDict]:
103103
"org.matrix.msc3030": self.config.experimental.msc3030_enabled,
104104
# Adds support for thread relations, per MSC3440.
105105
"org.matrix.msc3440.stable": True, # TODO: remove when "v1.3" is added above
106+
# Support for thread read receipts.
107+
"org.matrix.msc3771": self.config.experimental.msc3771_enabled,
106108
# Allows moderators to fetch redacted event content as described in MSC2815
107109
"fi.mau.msc2815": self.config.experimental.msc2815_enabled,
108110
# Adds support for login token requests as per MSC3882

synapse/types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,7 @@ class ReadReceipt:
835835
receipt_type: str
836836
user_id: str
837837
event_ids: List[str]
838+
thread_id: Optional[str]
838839
data: JsonDict
839840

840841

tests/federation/test_federation_sender.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@ def test_send_receipts(self):
4949

5050
sender = self.hs.get_federation_sender()
5151
receipt = ReadReceipt(
52-
"room_id", "m.read", "user_id", ["event_id"], {"ts": 1234}
52+
"room_id",
53+
"m.read",
54+
"user_id",
55+
["event_id"],
56+
thread_id=None,
57+
data={"ts": 1234},
5358
)
5459
self.successResultOf(defer.ensureDeferred(sender.send_read_receipt(receipt)))
5560

@@ -89,7 +94,12 @@ def test_send_receipts_with_backoff(self):
8994

9095
sender = self.hs.get_federation_sender()
9196
receipt = ReadReceipt(
92-
"room_id", "m.read", "user_id", ["event_id"], {"ts": 1234}
97+
"room_id",
98+
"m.read",
99+
"user_id",
100+
["event_id"],
101+
thread_id=None,
102+
data={"ts": 1234},
93103
)
94104
self.successResultOf(defer.ensureDeferred(sender.send_read_receipt(receipt)))
95105

@@ -121,7 +131,12 @@ def test_send_receipts_with_backoff(self):
121131

122132
# send the second RR
123133
receipt = ReadReceipt(
124-
"room_id", "m.read", "user_id", ["other_id"], {"ts": 1234}
134+
"room_id",
135+
"m.read",
136+
"user_id",
137+
["other_id"],
138+
thread_id=None,
139+
data={"ts": 1234},
125140
)
126141
self.successResultOf(defer.ensureDeferred(sender.send_read_receipt(receipt)))
127142
self.pump()

0 commit comments

Comments
 (0)