Skip to content

Commit 44f1a05

Browse files
committed
Merge branch 'DES-1255-implement-related-event-match-push-rule' into 'master'
DES-1255 Implement related event match push rule See merge request beeper/synapse!16
2 parents 5547cee + 44a1728 commit 44f1a05

File tree

4 files changed

+103
-16
lines changed

4 files changed

+103
-16
lines changed

synapse/push/baserules.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -527,17 +527,25 @@ def make_base_prepend_rules(
527527
{
528528
"rule_id": "global/override/com.beeper.reaction",
529529
"conditions": [
530+
{
531+
"kind": "event_match",
532+
"key": "type",
533+
"pattern": "m.reaction",
534+
"_id": "_reaction",
535+
},
536+
# Only send reaction notifications for smaller rooms (under 20 members)
530537
{
531538
"kind": "room_member_count",
532539
"is": "<20",
533540
"_id": "_member_count",
534541
},
542+
# Only send notification if the reaction is to your message
535543
{
536-
"kind": "event_match",
537-
"key": "type",
538-
"pattern": "m.reaction",
539-
"_id": "_reaction",
540-
}
544+
"kind": "related_event_match",
545+
"key": "sender",
546+
"pattern_type": "user_id",
547+
"_id": "_sender",
548+
},
541549
],
542550
"actions": ["notify", {"set_tweak": "highlight", "value": False}],
543551
},

synapse/push/bulk_push_rule_evaluator.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737

3838
logger = logging.getLogger(__name__)
3939

40-
4140
push_rules_invalidation_counter = Counter(
4241
"synapse_push_bulk_push_rule_evaluator_push_rules_invalidation_counter", ""
4342
)
@@ -187,8 +186,13 @@ async def action_for_event_by_user(
187186
sender_power_level,
188187
) = await self._get_power_levels_and_sender_level(event, context)
189188

189+
related_event_id = event.content.get("m.relates_to", {}).get("event_id")
190+
related_event = (
191+
(await self.store.get_event(related_event_id)) if related_event_id else None
192+
)
193+
190194
evaluator = PushRuleEvaluatorForEvent(
191-
event, len(room_members), sender_power_level, power_levels
195+
event, len(room_members), sender_power_level, power_levels, related_event
192196
)
193197

194198
condition_cache: Dict[str, bool] = {}

synapse/push/push_rule_evaluator.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ def __init__(
119119
room_member_count: int,
120120
sender_power_level: int,
121121
power_levels: Dict[str, Union[int, Dict[str, int]]],
122+
related_event: Optional[EventBase],
122123
):
123124
self._event = event
124125
self._room_member_count = room_member_count
@@ -128,11 +129,22 @@ def __init__(
128129
# Maps strings of e.g. 'content.body' -> event["content"]["body"]
129130
self._value_cache = _flatten_dict(event)
130131

132+
self._related_event = related_event
133+
self._related_event_value_cache = (
134+
_flatten_dict(related_event) if related_event else None
135+
)
136+
131137
def matches(
132138
self, condition: Dict[str, Any], user_id: str, display_name: Optional[str]
133139
) -> bool:
134140
if condition["kind"] == "event_match":
135-
return self._event_match(condition, user_id)
141+
return self._event_match(condition, user_id, self._event, self._value_cache)
142+
elif condition["kind"] == "related_event_match":
143+
if not self._related_event:
144+
return False
145+
return self._event_match(
146+
condition, user_id, self._related_event, self._related_event_value_cache
147+
)
136148
elif condition["kind"] == "contains_display_name":
137149
return self._contains_display_name(display_name)
138150
elif condition["kind"] == "room_member_count":
@@ -144,7 +156,13 @@ def matches(
144156
else:
145157
return True
146158

147-
def _event_match(self, condition: dict, user_id: str) -> bool:
159+
def _event_match(
160+
self,
161+
condition: dict,
162+
user_id: str,
163+
event: EventBase,
164+
event_value_cache: Dict[str, str],
165+
) -> bool:
148166
pattern = condition.get("pattern", None)
149167

150168
if not pattern:
@@ -160,13 +178,13 @@ def _event_match(self, condition: dict, user_id: str) -> bool:
160178

161179
# XXX: optimisation: cache our pattern regexps
162180
if condition["key"] == "content.body":
163-
body = self._event.content.get("body", None)
181+
body = event.content.get("body", None)
164182
if not body or not isinstance(body, str):
165183
return False
166184

167185
return _glob_matches(pattern, body, word_boundary=True)
168186
else:
169-
haystack = self._get_value(condition["key"])
187+
haystack = event_value_cache.get(condition["key"], None)
170188
if haystack is None:
171189
return False
172190

@@ -190,9 +208,6 @@ def _contains_display_name(self, display_name: Optional[str]) -> bool:
190208

191209
return bool(r.search(body))
192210

193-
def _get_value(self, dotted_key: str) -> Optional[str]:
194-
return self._value_cache.get(dotted_key, None)
195-
196211

197212
# Caches (string, is_glob, word_boundary) -> regex for push. See _glob_matches
198213
regex_cache: LruCache[Tuple[str, bool, bool], Pattern] = LruCache(

tests/push/test_push_rule_evaluator.py

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424

2525
class PushRuleEvaluatorTestCase(unittest.TestCase):
26-
def _get_evaluator(self, content):
26+
def _get_evaluator(self, content, related_event=None):
2727
event = FrozenEvent(
2828
{
2929
"event_id": "$event_id",
@@ -39,7 +39,7 @@ def _get_evaluator(self, content):
3939
sender_power_level = 0
4040
power_levels = {}
4141
return PushRuleEvaluatorForEvent(
42-
event, room_member_count, sender_power_level, power_levels
42+
event, room_member_count, sender_power_level, power_levels, related_event
4343
)
4444

4545
def test_display_name(self):
@@ -266,3 +266,63 @@ def test_tweaks_for_actions(self):
266266
push_rule_evaluator.tweaks_for_actions(actions),
267267
{"sound": "default", "highlight": True},
268268
)
269+
270+
def test_related_event_match(self):
271+
evaluator = self._get_evaluator(
272+
{
273+
"m.relates_to": {
274+
"event_id": "$parent_event_id",
275+
"key": "\ud83d\udc4d\ufe0f",
276+
"rel_type": "m.annotation",
277+
}
278+
},
279+
FrozenEvent(
280+
{
281+
"event_id": "$parent.event_id",
282+
"type": "m.room.message",
283+
"sender": "@other_user:test",
284+
"state_key": "",
285+
"room_id": "#room:test",
286+
"content": {"msgtype": "m.text", "body": "Original message"},
287+
},
288+
RoomVersions.V1,
289+
),
290+
)
291+
self.assertFalse(
292+
evaluator.matches(
293+
{
294+
"kind": "related_event_match",
295+
"key": "sender",
296+
"pattern_type": "user_id",
297+
},
298+
"@user:test",
299+
"display_name",
300+
)
301+
)
302+
self.assertTrue(
303+
evaluator.matches(
304+
{
305+
"kind": "related_event_match",
306+
"key": "sender",
307+
"pattern_type": "user_id",
308+
},
309+
"@other_user:test",
310+
"display_name",
311+
)
312+
)
313+
314+
def test_related_event_match_no_related_event(self):
315+
evaluator = self._get_evaluator(
316+
{"msgtype": "m.text", "body": "Message without related event"}
317+
)
318+
self.assertFalse(
319+
evaluator.matches(
320+
{
321+
"kind": "related_event_match",
322+
"key": "sender",
323+
"pattern_type": "user_id",
324+
},
325+
"@user:test",
326+
"display_name",
327+
)
328+
)

0 commit comments

Comments
 (0)