diff --git a/synapse/push/bulk_push_rule_evaluator.py b/synapse/push/bulk_push_rule_evaluator.py
index f73dceb128..199337673f 100644
--- a/synapse/push/bulk_push_rule_evaluator.py
+++ b/synapse/push/bulk_push_rule_evaluator.py
@@ -22,7 +22,7 @@ from typing import (
List,
Mapping,
Optional,
- Set,
+ Sequence,
Tuple,
Union,
)
@@ -36,13 +36,14 @@ from synapse.api.constants import (
Membership,
RelationTypes,
)
-from synapse.api.room_versions import PushRuleRoomFlag, RoomVersion
+from synapse.api.room_versions import PushRuleRoomFlag
from synapse.event_auth import auth_types_for_event, get_user_power_level
from synapse.events import EventBase, relation_from_event
from synapse.events.snapshot import EventContext
from synapse.state import POWER_KEY
from synapse.storage.databases.main.roommember import EventIdMembership
from synapse.synapse_rust.push import FilteredPushRules, PushRuleEvaluator
+from synapse.types import JsonValue
from synapse.types.state import StateFilter
from synapse.util.caches import register_cache
from synapse.util.metrics import measure_func
@@ -148,7 +149,7 @@ class BulkPushRuleEvaluator:
# little, we can skip fetching a huge number of push rules in large rooms.
# This helps make joins and leaves faster.
if event.type == EventTypes.Member:
- local_users = []
+ local_users: Sequence[str] = []
# We never notify a user about their own actions. This is enforced in
# `_action_for_event_by_user` in the loop over `rules_by_user`, but we
# do the same check here to avoid unnecessary DB queries.
@@ -183,7 +184,6 @@ class BulkPushRuleEvaluator:
if event.type == EventTypes.Member and event.membership == Membership.INVITE:
invited = event.state_key
if invited and self.hs.is_mine_id(invited) and invited not in local_users:
- local_users = list(local_users)
local_users.append(invited)
if not local_users:
@@ -256,13 +256,15 @@ class BulkPushRuleEvaluator:
return pl_event.content if pl_event else {}, sender_level
- async def _related_events(self, event: EventBase) -> Dict[str, Dict[str, str]]:
+ async def _related_events(
+ self, event: EventBase
+ ) -> Dict[str, Dict[str, JsonValue]]:
"""Fetches the related events for 'event'. Sets the im.vector.is_falling_back key if the event is from a fallback relation
Returns:
Mapping of relation type to flattened events.
"""
- related_events: Dict[str, Dict[str, str]] = {}
+ related_events: Dict[str, Dict[str, JsonValue]] = {}
if self._related_event_match_enabled:
related_event_id = event.content.get("m.relates_to", {}).get("event_id")
relation_type = event.content.get("m.relates_to", {}).get("rel_type")
@@ -321,7 +323,6 @@ class BulkPushRuleEvaluator:
context: EventContext,
event_id_to_event: Mapping[str, EventBase],
) -> None:
-
if (
not event.internal_metadata.is_notifiable()
or event.internal_metadata.is_historical()
@@ -388,27 +389,14 @@ class BulkPushRuleEvaluator:
del notification_levels[key]
# Pull out any user and room mentions.
- mentions = event.content.get(EventContentFields.MSC3952_MENTIONS)
- has_mentions = self._intentional_mentions_enabled and isinstance(mentions, dict)
- user_mentions: Set[str] = set()
- room_mention = False
- if has_mentions:
- # mypy seems to have lost the type even though it must be a dict here.
- assert isinstance(mentions, dict)
- # Remove out any non-string items and convert to a set.
- user_mentions_raw = mentions.get("user_ids")
- if isinstance(user_mentions_raw, list):
- user_mentions = set(
- filter(lambda item: isinstance(item, str), user_mentions_raw)
- )
- # Room mention is only true if the value is exactly true.
- room_mention = mentions.get("room") is True
+ has_mentions = (
+ self._intentional_mentions_enabled
+ and EventContentFields.MSC3952_MENTIONS in event.content
+ )
evaluator = PushRuleEvaluator(
- _flatten_dict(event, room_version=event.room_version),
+ _flatten_dict(event),
has_mentions,
- user_mentions,
- room_mention,
room_member_count,
sender_power_level,
notification_levels,
@@ -489,17 +477,20 @@ RulesByUser = Dict[str, List[Rule]]
StateGroup = Union[object, int]
+def _is_simple_value(value: Any) -> bool:
+ return isinstance(value, (bool, str)) or type(value) is int or value is None
+
+
def _flatten_dict(
d: Union[EventBase, Mapping[str, Any]],
- room_version: Optional[RoomVersion] = None,
prefix: Optional[List[str]] = None,
- result: Optional[Dict[str, str]] = None,
-) -> Dict[str, str]:
+ result: Optional[Dict[str, JsonValue]] = None,
+) -> Dict[str, JsonValue]:
"""
Given a JSON dictionary (or event) which might contain sub dictionaries,
flatten it into a single layer dictionary by combining the keys & sub-keys.
- Any (non-dictionary), non-string value is dropped.
+ String, integer, boolean, null or lists of those values are kept. All others are dropped.
Transforms:
@@ -511,7 +502,6 @@ def _flatten_dict(
Args:
d: The event or content to continue flattening.
- room_version: The room version object.
prefix: The key prefix (from outer dictionaries).
result: The result to mutate.
@@ -523,22 +513,28 @@ def _flatten_dict(
if result is None:
result = {}
for key, value in d.items():
- if isinstance(value, str):
- result[".".join(prefix + [key])] = value.lower()
+ # Escape periods in the key with a backslash (and backslashes with an
+ # extra backslash). This is since a period is used as a separator between
+ # nested fields.
+ key = key.replace("\\", "\\\\").replace(".", "\\.")
+
+ if _is_simple_value(value):
+ result[".".join(prefix + [key])] = value
+ elif isinstance(value, (list, tuple)):
+ result[".".join(prefix + [key])] = [v for v in value if _is_simple_value(v)]
elif isinstance(value, Mapping):
# do not set `room_version` due to recursion considerations below
_flatten_dict(value, prefix=(prefix + [key]), result=result)
# `room_version` should only ever be set when looking at the top level of an event
if (
- room_version is not None
- and PushRuleRoomFlag.EXTENSIBLE_EVENTS in room_version.msc3931_push_features
- and isinstance(d, EventBase)
+ isinstance(d, EventBase)
+ and PushRuleRoomFlag.EXTENSIBLE_EVENTS in d.room_version.msc3931_push_features
):
# Room supports extensible events: replace `content.body` with the plain text
# representation from `m.markup`, as per MSC1767.
markup = d.get("content").get("m.markup")
- if room_version.identifier.startswith("org.matrix.msc1767."):
+ if d.room_version.identifier.startswith("org.matrix.msc1767."):
markup = d.get("content").get("org.matrix.msc1767.markup")
if markup is not None and isinstance(markup, list):
text = ""
diff --git a/synapse/push/clientformat.py b/synapse/push/clientformat.py
index bb76c169c6..222afbdcc8 100644
--- a/synapse/push/clientformat.py
+++ b/synapse/push/clientformat.py
@@ -41,11 +41,12 @@ def format_push_rules_for_user(
rulearray.append(template_rule)
- pattern_type = template_rule.pop("pattern_type", None)
- if pattern_type == "user_id":
- template_rule["pattern"] = user.to_string()
- elif pattern_type == "user_localpart":
- template_rule["pattern"] = user.localpart
+ for type_key in ("pattern", "value"):
+ type_value = template_rule.pop(f"{type_key}_type", None)
+ if type_value == "user_id":
+ template_rule[type_key] = user.to_string()
+ elif type_value == "user_localpart":
+ template_rule[type_key] = user.localpart
template_rule["enabled"] = enabled
|