2929from prometheus_client import Counter
3030
3131from synapse .api .constants import MAIN_TIMELINE , EventTypes , Membership , RelationTypes
32+ from synapse .api .room_versions import PushRuleRoomFlag , RoomDisposition , RoomVersion
3233from synapse .event_auth import auth_types_for_event , get_user_power_level
3334from synapse .events import EventBase , relation_from_event
3435from synapse .events .snapshot import EventContext
@@ -343,7 +344,7 @@ async def _action_for_event_by_user(
343344 room_version_features = []
344345
345346 evaluator = PushRuleEvaluator (
346- _flatten_dict (event ),
347+ _flatten_dict (event , room_version = event . room_version ),
347348 room_member_count ,
348349 sender_power_level ,
349350 notification_levels ,
@@ -426,6 +427,7 @@ async def _action_for_event_by_user(
426427
427428def _flatten_dict (
428429 d : Union [EventBase , Mapping [str , Any ]],
430+ room_version : Optional [RoomVersion ] = None ,
429431 prefix : Optional [List [str ]] = None ,
430432 result : Optional [Dict [str , str ]] = None ,
431433) -> Dict [str , str ]:
@@ -437,6 +439,31 @@ def _flatten_dict(
437439 if isinstance (value , str ):
438440 result ["." .join (prefix + [key ])] = value .lower ()
439441 elif isinstance (value , Mapping ):
442+ # do not set `room_version` due to recursion considerations below
440443 _flatten_dict (value , prefix = (prefix + [key ]), result = result )
441444
445+ # `room_version` should only ever be set when looking at the top level of an event
446+ if (
447+ room_version is not None
448+ and PushRuleRoomFlag .EXTENSIBLE_EVENTS in room_version .msc3931_push_features
449+ and isinstance (d , EventBase )
450+ ):
451+ # Room supports extensible events: replace `content.body` with the plain text
452+ # representation from `m.markup`, as per MSC1767.
453+ markup = d .get ("content" ).get ("org.matrix.msc1767.markup" )
454+ if room_version .disposition == RoomDisposition .STABLE :
455+ markup = d .get ("content" ).get ("m.markup" )
456+ if markup is not None and isinstance (markup , list ):
457+ text = ""
458+ for rep in markup :
459+ if not isinstance (rep , dict ):
460+ # invalid markup - skip all processing
461+ break
462+ if rep .get ("mimetype" , "text/plain" ) == "text/plain" :
463+ rep_text = rep .get ("body" )
464+ if rep_text is not None and isinstance (rep_text , str ):
465+ text = rep_text .lower ()
466+ break
467+ result ["content.body" ] = text
468+
442469 return result
0 commit comments