Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Support for MSC3758: exact_event_match push condition #14964

Merged
merged 4 commits into from Feb 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/14964.feature
@@ -0,0 +1 @@
Implement the experimental `exact_event_match` push rule condition from [MSC3758](https://github.com/matrix-org/matrix-spec-proposals/pull/3758).
65 changes: 53 additions & 12 deletions rust/benches/evaluator.rs
Expand Up @@ -16,6 +16,7 @@
use std::collections::BTreeSet;
use synapse::push::{
evaluator::PushRuleEvaluator, Condition, EventMatchCondition, FilteredPushRules, PushRules,
SimpleJsonValue,
};
use test::Bencher;

Expand All @@ -24,9 +25,18 @@ extern crate test;
#[bench]
fn bench_match_exact(b: &mut Bencher) {
let flattened_keys = [
("type".to_string(), "m.text".to_string()),
("room_id".to_string(), "!room:server".to_string()),
("content.body".to_string(), "test message".to_string()),
(
"type".to_string(),
SimpleJsonValue::Str("m.text".to_string()),
),
(
"room_id".to_string(),
SimpleJsonValue::Str("!room:server".to_string()),
),
(
"content.body".to_string(),
SimpleJsonValue::Str("test message".to_string()),
),
]
.into_iter()
.collect();
Expand All @@ -43,6 +53,7 @@ fn bench_match_exact(b: &mut Bencher) {
true,
vec![],
false,
false,
)
.unwrap();

Expand All @@ -63,9 +74,18 @@ fn bench_match_exact(b: &mut Bencher) {
#[bench]
fn bench_match_word(b: &mut Bencher) {
let flattened_keys = [
("type".to_string(), "m.text".to_string()),
("room_id".to_string(), "!room:server".to_string()),
("content.body".to_string(), "test message".to_string()),
(
"type".to_string(),
SimpleJsonValue::Str("m.text".to_string()),
),
(
"room_id".to_string(),
SimpleJsonValue::Str("!room:server".to_string()),
),
(
"content.body".to_string(),
SimpleJsonValue::Str("test message".to_string()),
),
]
.into_iter()
.collect();
Expand All @@ -82,6 +102,7 @@ fn bench_match_word(b: &mut Bencher) {
true,
vec![],
false,
false,
)
.unwrap();

Expand All @@ -102,9 +123,18 @@ fn bench_match_word(b: &mut Bencher) {
#[bench]
fn bench_match_word_miss(b: &mut Bencher) {
let flattened_keys = [
("type".to_string(), "m.text".to_string()),
("room_id".to_string(), "!room:server".to_string()),
("content.body".to_string(), "test message".to_string()),
(
"type".to_string(),
SimpleJsonValue::Str("m.text".to_string()),
),
(
"room_id".to_string(),
SimpleJsonValue::Str("!room:server".to_string()),
),
(
"content.body".to_string(),
SimpleJsonValue::Str("test message".to_string()),
),
]
.into_iter()
.collect();
Expand All @@ -121,6 +151,7 @@ fn bench_match_word_miss(b: &mut Bencher) {
true,
vec![],
false,
false,
)
.unwrap();

Expand All @@ -141,9 +172,18 @@ fn bench_match_word_miss(b: &mut Bencher) {
#[bench]
fn bench_eval_message(b: &mut Bencher) {
let flattened_keys = [
("type".to_string(), "m.text".to_string()),
("room_id".to_string(), "!room:server".to_string()),
("content.body".to_string(), "test message".to_string()),
(
"type".to_string(),
SimpleJsonValue::Str("m.text".to_string()),
),
(
"room_id".to_string(),
SimpleJsonValue::Str("!room:server".to_string()),
),
(
"content.body".to_string(),
SimpleJsonValue::Str("test message".to_string()),
),
]
.into_iter()
.collect();
Expand All @@ -160,6 +200,7 @@ fn bench_eval_message(b: &mut Bencher) {
true,
vec![],
false,
false,
)
.unwrap();

Expand Down
69 changes: 54 additions & 15 deletions rust/src/push/evaluator.rs
Expand Up @@ -22,8 +22,8 @@ use regex::Regex;

use super::{
utils::{get_glob_matcher, get_localpart_from_id, GlobMatchType},
Action, Condition, EventMatchCondition, FilteredPushRules, KnownCondition,
RelatedEventMatchCondition,
Action, Condition, EventMatchCondition, ExactEventMatchCondition, FilteredPushRules,
KnownCondition, RelatedEventMatchCondition, SimpleJsonValue,
};

lazy_static! {
Expand Down Expand Up @@ -61,9 +61,9 @@ impl RoomVersionFeatures {
/// Allows running a set of push rules against a particular event.
#[pyclass]
pub struct PushRuleEvaluator {
/// A mapping of "flattened" keys to string values in the event, e.g.
/// A mapping of "flattened" keys to simple JSON values in the event, e.g.
/// includes things like "type" and "content.msgtype".
flattened_keys: BTreeMap<String, String>,
flattened_keys: BTreeMap<String, SimpleJsonValue>,

/// The "content.body", if any.
body: String,
Expand All @@ -87,7 +87,7 @@ pub struct PushRuleEvaluator {

/// The related events, indexed by relation type. Flattened in the same manner as
/// `flattened_keys`.
related_events_flattened: BTreeMap<String, BTreeMap<String, String>>,
related_events_flattened: BTreeMap<String, BTreeMap<String, SimpleJsonValue>>,

/// If msc3664, push rules for related events, is enabled.
related_event_match_enabled: bool,
Expand All @@ -98,6 +98,9 @@ pub struct PushRuleEvaluator {
/// If MSC3931 (room version feature flags) is enabled. Usually controlled by the same
/// flag as MSC1767 (extensible events core).
msc3931_enabled: bool,

/// If MSC3758 (exact_event_match push rule condition) is enabled.
msc3758_exact_event_match: bool,
}

#[pymethods]
Expand All @@ -106,22 +109,23 @@ impl PushRuleEvaluator {
#[allow(clippy::too_many_arguments)]
#[new]
pub fn py_new(
flattened_keys: BTreeMap<String, String>,
flattened_keys: BTreeMap<String, SimpleJsonValue>,
has_mentions: bool,
user_mentions: BTreeSet<String>,
room_mention: bool,
room_member_count: u64,
sender_power_level: Option<i64>,
notification_power_levels: BTreeMap<String, i64>,
related_events_flattened: BTreeMap<String, BTreeMap<String, String>>,
related_events_flattened: BTreeMap<String, BTreeMap<String, SimpleJsonValue>>,
related_event_match_enabled: bool,
room_version_feature_flags: Vec<String>,
msc3931_enabled: bool,
msc3758_exact_event_match: bool,
) -> Result<Self, Error> {
let body = flattened_keys
.get("content.body")
.cloned()
.unwrap_or_default();
let body = match flattened_keys.get("content.body") {
Some(SimpleJsonValue::Str(s)) => s.clone(),
_ => String::new(),
};

Ok(PushRuleEvaluator {
flattened_keys,
Expand All @@ -136,6 +140,7 @@ impl PushRuleEvaluator {
related_event_match_enabled,
room_version_feature_flags,
msc3931_enabled,
msc3758_exact_event_match,
})
}

Expand Down Expand Up @@ -252,6 +257,9 @@ impl PushRuleEvaluator {
KnownCondition::EventMatch(event_match) => {
self.match_event_match(event_match, user_id)?
}
KnownCondition::ExactEventMatch(exact_event_match) => {
self.match_exact_event_match(exact_event_match)?
}
KnownCondition::RelatedEventMatch(event_match) => {
self.match_related_event_match(event_match, user_id)?
}
Expand Down Expand Up @@ -337,7 +345,9 @@ impl PushRuleEvaluator {
return Ok(false);
};

let haystack = if let Some(haystack) = self.flattened_keys.get(&*event_match.key) {
let haystack = if let Some(SimpleJsonValue::Str(haystack)) =
self.flattened_keys.get(&*event_match.key)
{
haystack
} else {
return Ok(false);
Expand All @@ -355,6 +365,27 @@ impl PushRuleEvaluator {
compiled_pattern.is_match(haystack)
}

/// Evaluates a `exact_event_match` condition. (MSC3758)
fn match_exact_event_match(
&self,
exact_event_match: &ExactEventMatchCondition,
) -> Result<bool, Error> {
// First check if the feature is enabled.
if !self.msc3758_exact_event_match {
return Ok(false);
}

let value = &exact_event_match.value;

let haystack = if let Some(haystack) = self.flattened_keys.get(&*exact_event_match.key) {
haystack
} else {
return Ok(false);
};

Ok(haystack == &**value)
}

/// Evaluates a `related_event_match` condition. (MSC3664)
fn match_related_event_match(
&self,
Expand Down Expand Up @@ -410,7 +441,7 @@ impl PushRuleEvaluator {
return Ok(false);
};

let haystack = if let Some(haystack) = event.get(&**key) {
let haystack = if let Some(SimpleJsonValue::Str(haystack)) = event.get(&**key) {
haystack
} else {
return Ok(false);
Expand Down Expand Up @@ -455,7 +486,10 @@ impl PushRuleEvaluator {
#[test]
fn push_rule_evaluator() {
let mut flattened_keys = BTreeMap::new();
flattened_keys.insert("content.body".to_string(), "foo bar bob hello".to_string());
flattened_keys.insert(
"content.body".to_string(),
SimpleJsonValue::Str("foo bar bob hello".to_string()),
);
let evaluator = PushRuleEvaluator::py_new(
flattened_keys,
false,
Expand All @@ -468,6 +502,7 @@ fn push_rule_evaluator() {
true,
vec![],
true,
true,
)
.unwrap();

Expand All @@ -482,7 +517,10 @@ fn test_requires_room_version_supports_condition() {
use crate::push::{PushRule, PushRules};

let mut flattened_keys = BTreeMap::new();
flattened_keys.insert("content.body".to_string(), "foo bar bob hello".to_string());
flattened_keys.insert(
"content.body".to_string(),
SimpleJsonValue::Str("foo bar bob hello".to_string()),
);
let flags = vec![RoomVersionFeatures::ExtensibleEvents.as_str().to_string()];
let evaluator = PushRuleEvaluator::py_new(
flattened_keys,
Expand All @@ -496,6 +534,7 @@ fn test_requires_room_version_supports_condition() {
false,
flags,
true,
true,
)
.unwrap();

Expand Down