1919
2020from prometheus_client import Counter
2121
22- from synapse .api .constants import EventTypes , Membership
22+ from synapse .api .constants import EventTypes , Membership , RelationTypes
2323from synapse .event_auth import get_user_power_level
24+ from synapse .events import EventBase
25+ from synapse .events .snapshot import EventContext
2426from synapse .state import POWER_KEY
2527from synapse .util .async_helpers import Linearizer
2628from synapse .util .caches import register_cache
5153)
5254
5355
56+ STATE_EVENT_TYPES_TO_MARK_UNREAD = {
57+ EventTypes .Topic ,
58+ EventTypes .Name ,
59+ EventTypes .RoomAvatar ,
60+ EventTypes .Tombstone ,
61+ }
62+
63+
64+ def _should_count_as_unread (event : EventBase , context : EventContext ) -> bool :
65+ # Exclude rejected and soft-failed events.
66+ if context .rejected or event .internal_metadata .is_soft_failed ():
67+ return False
68+
69+ # Exclude notices.
70+ if (
71+ not event .is_state ()
72+ and event .type == EventTypes .Message
73+ and event .content .get ("msgtype" ) == "m.notice"
74+ ):
75+ return False
76+
77+ # Exclude edits.
78+ relates_to = event .content .get ("m.relates_to" , {})
79+ if relates_to .get ("rel_type" ) == RelationTypes .REPLACE :
80+ return False
81+
82+ # Mark events that have a non-empty string body as unread.
83+ body = event .content .get ("body" )
84+ if isinstance (body , str ) and body :
85+ return True
86+
87+ # Mark some state events as unread.
88+ if event .is_state () and event .type in STATE_EVENT_TYPES_TO_MARK_UNREAD :
89+ return True
90+
91+ # Mark encrypted events as unread.
92+ if not event .is_state () and event .type == EventTypes .Encrypted :
93+ return True
94+
95+ return False
96+
97+
5498class BulkPushRuleEvaluator (object ):
5599 """Calculates the outcome of push rules for an event for all users in the
56100 room at once.
@@ -133,9 +177,12 @@ async def _get_power_levels_and_sender_level(self, event, context):
133177 return pl_event .content if pl_event else {}, sender_level
134178
135179 async def action_for_event_by_user (self , event , context ) -> None :
136- """Given an event and context, evaluate the push rules and insert the
137- results into the event_push_actions_staging table.
180+ """Given an event and context, evaluate the push rules, check if the message
181+ should increment the unread count, and insert the results into the
182+ event_push_actions_staging table.
138183 """
184+ count_as_unread = _should_count_as_unread (event , context )
185+
139186 rules_by_user = await self ._get_rules_for_event (event , context )
140187 actions_by_user = {}
141188
@@ -172,6 +219,8 @@ async def action_for_event_by_user(self, event, context) -> None:
172219 if event .type == EventTypes .Member and event .state_key == uid :
173220 display_name = event .content .get ("displayname" , None )
174221
222+ actions_by_user [uid ] = []
223+
175224 for rule in rules :
176225 if "enabled" in rule and not rule ["enabled" ]:
177226 continue
@@ -189,7 +238,9 @@ async def action_for_event_by_user(self, event, context) -> None:
189238 # Mark in the DB staging area the push actions for users who should be
190239 # notified for this event. (This will then get handled when we persist
191240 # the event)
192- await self .store .add_push_actions_to_staging (event .event_id , actions_by_user )
241+ await self .store .add_push_actions_to_staging (
242+ event .event_id , actions_by_user , count_as_unread ,
243+ )
193244
194245
195246def _condition_checker (evaluator , conditions , uid , display_name , cache ):
@@ -369,8 +420,8 @@ async def _update_rules_with_member_event_ids(
369420 Args:
370421 ret_rules_by_user (dict): Partiallly filled dict of push rules. Gets
371422 updated with any new rules.
372- member_event_ids (list ): List of event ids for membership events that
373- have happened since the last time we filled rules_by_user
423+ member_event_ids (dict ): Dict of user id to event id for membership events
424+ that have happened since the last time we filled rules_by_user
374425 state_group: The state group we are currently computing push rules
375426 for. Used when updating the cache.
376427 """
@@ -390,34 +441,19 @@ async def _update_rules_with_member_event_ids(
390441 if logger .isEnabledFor (logging .DEBUG ):
391442 logger .debug ("Found members %r: %r" , self .room_id , members .values ())
392443
393- interested_in_user_ids = {
444+ user_ids = {
394445 user_id
395446 for user_id , membership in members .values ()
396447 if membership == Membership .JOIN
397448 }
398449
399- logger .debug ("Joined: %r" , interested_in_user_ids )
400-
401- if_users_with_pushers = await self .store .get_if_users_have_pushers (
402- interested_in_user_ids , on_invalidate = self .invalidate_all_cb
403- )
404-
405- user_ids = {
406- uid for uid , have_pusher in if_users_with_pushers .items () if have_pusher
407- }
408-
409- logger .debug ("With pushers: %r" , user_ids )
410-
411- users_with_receipts = await self .store .get_users_with_read_receipts_in_room (
412- self .room_id , on_invalidate = self .invalidate_all_cb
413- )
414-
415- logger .debug ("With receipts: %r" , users_with_receipts )
450+ logger .debug ("Joined: %r" , user_ids )
416451
417- # any users with pushers must be ours: they have pushers
418- for uid in users_with_receipts :
419- if uid in interested_in_user_ids :
420- user_ids .add (uid )
452+ # Previously we only considered users with pushers or read receipts in that
453+ # room. We can't do this anymore because we use push actions to calculate unread
454+ # counts, which don't rely on the user having pushers or sent a read receipt into
455+ # the room. Therefore we just need to filter for local users here.
456+ user_ids = list (filter (self .is_mine_id , user_ids ))
421457
422458 rules_by_user = await self .store .bulk_get_push_rules (
423459 user_ids , on_invalidate = self .invalidate_all_cb
0 commit comments