@@ -136,8 +136,10 @@ def __init__(
136
136
# destination
137
137
self ._pending_presence : Dict [str , UserPresenceState ] = {}
138
138
139
- # room_id -> receipt_type -> user_id -> receipt_dict
140
- self ._pending_rrs : Dict [str , Dict [str , Dict [str , dict ]]] = {}
139
+ # room_id -> receipt_type -> thread_id -> user_id -> receipt_dict
140
+ self ._pending_rrs : Dict [
141
+ str , Dict [str , Dict [Optional [str ], Dict [str , dict ]]]
142
+ ] = {}
141
143
self ._rrs_pending_flush = False
142
144
143
145
# stream_id of last successfully sent to-device message.
@@ -202,12 +204,15 @@ def queue_read_receipt(self, receipt: ReadReceipt) -> None:
202
204
Args:
203
205
receipt: receipt to be queued
204
206
"""
205
- serialized_receipt : JsonDict = {"event_ids" : receipt .event_ids , "data" : receipt .data }
207
+ serialized_receipt : JsonDict = {
208
+ "event_ids" : receipt .event_ids ,
209
+ "data" : receipt .data ,
210
+ }
206
211
if receipt .thread_id is not None :
207
212
serialized_receipt ["data" ]["thread_id" ] = receipt .thread_id
208
213
self ._pending_rrs .setdefault (receipt .room_id , {}).setdefault (
209
214
receipt .receipt_type , {}
210
- )[receipt .user_id ] = serialized_receipt
215
+ ). setdefault ( receipt . thread_id , {}) [receipt .user_id ] = serialized_receipt
211
216
212
217
def flush_read_receipts_for_room (self , room_id : str ) -> None :
213
218
# if we don't have any read-receipts for this room, it may be that we've already
@@ -552,15 +557,44 @@ def _get_rr_edus(self, force_flush: bool) -> Iterable[Edu]:
552
557
# not yet time for this lot
553
558
return
554
559
555
- edu = Edu (
556
- origin = self ._server_name ,
557
- destination = self ._destination ,
558
- edu_type = EduTypes .RECEIPT ,
559
- content = self ._pending_rrs ,
560
- )
560
+ # Build the EDUs needed to send these receipts. This is a bit complicated
561
+ # since we can share one for each unique (room, receipt type, user), but
562
+ # need additional ones for different threads. The result is that we will
563
+ # send N EDUs where N is the maximum number of threads in a room.
564
+ #
565
+ # This could be slightly more efficient by bundling users who have only
566
+ # send receipts for different threads.
567
+ while self ._pending_rrs :
568
+ # The next EDU's content.
569
+ content = {}
570
+
571
+ # Iterate each room's receipt types and threads, adding it to the content.
572
+ for room_id in list (self ._pending_rrs .keys ()):
573
+ for receipt_type in list (self ._pending_rrs [room_id ].keys ()):
574
+ thread_ids = self ._pending_rrs [room_id ][receipt_type ]
575
+ # The thread ID itself doesn't matter at this point.
576
+ content .setdefault (room_id , {})[
577
+ receipt_type
578
+ ] = thread_ids .popitem ()[1 ]
579
+
580
+ # If there are no threads left in this room / receipt type.
581
+ # Clear it out.
582
+ if not thread_ids :
583
+ del self ._pending_rrs [room_id ][receipt_type ]
584
+
585
+ # Again, clear out any blank rooms.
586
+ if not self ._pending_rrs [room_id ]:
587
+ del self ._pending_rrs [room_id ]
588
+
589
+ yield Edu (
590
+ origin = self ._server_name ,
591
+ destination = self ._destination ,
592
+ edu_type = EduTypes .RECEIPT ,
593
+ content = content ,
594
+ )
595
+
561
596
self ._pending_rrs = {}
562
597
self ._rrs_pending_flush = False
563
- yield edu
564
598
565
599
def _pop_pending_edus (self , limit : int ) -> List [Edu ]:
566
600
pending_edus = self ._pending_edus
0 commit comments