diff --git a/synapse/config/experimental.py b/synapse/config/experimental.py
index 5e3a889081..6ac2f0c10d 100644
--- a/synapse/config/experimental.py
+++ b/synapse/config/experimental.py
@@ -169,6 +169,11 @@ class ExperimentalConfig(Config):
# MSC3925: do not replace events with their edits
self.msc3925_inhibit_edit = experimental.get("msc3925_inhibit_edit", False)
+ # MSC3758: exact_event_match push rule condition
+ self.msc3758_exact_event_match = experimental.get(
+ "msc3758_exact_event_match", False
+ )
+
# MSC3873: Disambiguate event_match keys.
self.msc3783_escape_event_match_key = experimental.get(
"msc3783_escape_event_match_key", False
diff --git a/synapse/push/bulk_push_rule_evaluator.py b/synapse/push/bulk_push_rule_evaluator.py
index 39d2f88f03..8568aca528 100644
--- a/synapse/push/bulk_push_rule_evaluator.py
+++ b/synapse/push/bulk_push_rule_evaluator.py
@@ -43,6 +43,7 @@ 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 SimpleJsonValue
from synapse.types.state import StateFilter
from synapse.util.caches import register_cache
from synapse.util.metrics import measure_func
@@ -256,13 +257,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, SimpleJsonValue]]:
"""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, SimpleJsonValue]] = {}
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")
@@ -425,6 +428,7 @@ class BulkPushRuleEvaluator:
self._related_event_match_enabled,
event.room_version.msc3931_push_features,
self.hs.config.experimental.msc1767_enabled, # MSC3931 flag
+ self.hs.config.experimental.msc3758_exact_event_match,
)
users = rules_by_user.keys()
@@ -501,15 +505,15 @@ StateGroup = Union[object, int]
def _flatten_dict(
d: Union[EventBase, Mapping[str, Any]],
prefix: Optional[List[str]] = None,
- result: Optional[Dict[str, str]] = None,
+ result: Optional[Dict[str, SimpleJsonValue]] = None,
*,
msc3783_escape_event_match_key: bool = False,
-) -> Dict[str, str]:
+) -> Dict[str, SimpleJsonValue]:
"""
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, and null values are kept. All others are dropped.
Transforms:
@@ -538,8 +542,8 @@ def _flatten_dict(
# nested fields.
key = key.replace("\\", "\\\\").replace(".", "\\.")
- if isinstance(value, str):
- result[".".join(prefix + [key])] = value.lower()
+ if isinstance(value, (bool, str)) or type(value) is int or value is None:
+ result[".".join(prefix + [key])] = value
elif isinstance(value, Mapping):
# do not set `room_version` due to recursion considerations below
_flatten_dict(
diff --git a/synapse/types/__init__.py b/synapse/types/__init__.py
index f82d1cfc29..52e366c8ae 100644
--- a/synapse/types/__init__.py
+++ b/synapse/types/__init__.py
@@ -69,6 +69,8 @@ StateMap = Mapping[StateKey, T]
MutableStateMap = MutableMapping[StateKey, T]
# JSON types. These could be made stronger, but will do for now.
+# A "simple" (canonical) JSON value.
+SimpleJsonValue = Optional[Union[str, int, bool]]
# A JSON-serialisable dict.
JsonDict = Dict[str, Any]
# A JSON-serialisable mapping; roughly speaking an immutable JSONDict.
|