Skip to main content

palpo_core/push/
patterned_push_rule.rs

1//! Common types for the [push notifications module][push].
2//!
3//! [push]: https://spec.matrix.org/latest/client-server-api/#push-notifications
4//!
5//! ## Understanding the types of this module
6//!
7//! Push rules are grouped in `RuleSet`s, and are grouped in five kinds (for
8//! more details about the different kind of rules, see the `Ruleset` documentation,
9//! or the specification). These five kinds are, by order of priority:
10//!
11//! - override rules
12//! - content rules
13//! - room rules
14//! - sender rules
15//! - underride rules
16
17use std::hash::{Hash, Hasher};
18
19use indexmap::Equivalent;
20use salvo::prelude::*;
21use serde::{Deserialize, Serialize};
22
23use crate::push::condition;
24use crate::push::{Action, FlattenedJson, MissingPatternError, PushConditionRoomCtx, PushRule};
25
26/// Like `SimplePushRule`, but with an additional `pattern` field.
27///
28/// Only applicable to content rules.
29///
30/// To create an instance of this type, first create a `PatternedPushRuleInit` and convert it via
31/// `PatternedPushRule::from` / `.into()`.
32#[derive(ToSchema, Deserialize, Serialize, Clone, Debug)]
33pub struct PatternedPushRule {
34    /// Actions to determine if and how a notification is delivered for events matching this rule.
35    pub actions: Vec<Action>,
36
37    /// Whether this is a default rule, or has been set explicitly.
38    pub default: bool,
39
40    /// Whether the push rule is enabled or not.
41    pub enabled: bool,
42
43    /// The ID of this rule.
44    pub rule_id: String,
45
46    /// The glob-style pattern to match against.
47    pub pattern: String,
48}
49
50impl PatternedPushRule {
51    /// Check if the push rule applies to the event.
52    ///
53    /// # Arguments
54    ///
55    /// * `event` - The flattened JSON representation of a room message event.
56    /// * `context` - The context of the room at the time of the event.
57    pub fn applies_to(&self, key: &str, event: &FlattenedJson, context: &PushConditionRoomCtx) -> bool {
58        // The old mention rules are disabled when an m.mentions field is present.
59        if event.contains_mentions() {
60            return false;
61        }
62
63        if event.get_str("sender").is_some_and(|sender| sender == context.user_id) {
64            return false;
65        }
66
67        self.enabled && condition::check_event_match(event, key, &self.pattern, context)
68    }
69}
70
71/// Initial set of fields of `PatterenedPushRule`.
72///
73/// This struct will not be updated even if additional fields are added to `PatterenedPushRule` in a
74/// new (non-breaking) release of the Matrix specification.
75#[derive(Debug)]
76#[allow(clippy::exhaustive_structs)]
77pub struct PatternedPushRuleInit {
78    /// Actions to determine if and how a notification is delivered for events matching this rule.
79    pub actions: Vec<Action>,
80
81    /// Whether this is a default rule, or has been set explicitly.
82    pub default: bool,
83
84    /// Whether the push rule is enabled or not.
85    pub enabled: bool,
86
87    /// The ID of this rule.
88    pub rule_id: String,
89
90    /// The glob-style pattern to match against.
91    pub pattern: String,
92}
93
94impl From<PatternedPushRuleInit> for PatternedPushRule {
95    fn from(init: PatternedPushRuleInit) -> Self {
96        let PatternedPushRuleInit {
97            actions,
98            default,
99            enabled,
100            rule_id,
101            pattern,
102        } = init;
103        Self {
104            actions,
105            default,
106            enabled,
107            rule_id,
108            pattern,
109        }
110    }
111}
112
113// The following trait are needed to be able to make
114// an IndexSet of the type
115
116impl Hash for PatternedPushRule {
117    fn hash<H: Hasher>(&self, state: &mut H) {
118        self.rule_id.hash(state);
119    }
120}
121
122impl PartialEq for PatternedPushRule {
123    fn eq(&self, other: &Self) -> bool {
124        self.rule_id == other.rule_id
125    }
126}
127
128impl Eq for PatternedPushRule {}
129
130impl Equivalent<PatternedPushRule> for str {
131    fn equivalent(&self, key: &PatternedPushRule) -> bool {
132        self == key.rule_id
133    }
134}
135
136/// A patterned push rule to update or create.
137#[derive(ToSchema, Deserialize, Serialize, Clone, Debug)]
138pub struct NewPatternedPushRule {
139    /// The ID of this rule.
140    pub rule_id: String,
141
142    /// The glob-style pattern to match against.
143    pub pattern: String,
144
145    /// Actions to determine if and how a notification is delivered for events matching this
146    /// rule.
147    pub actions: Vec<Action>,
148}
149
150impl NewPatternedPushRule {
151    /// Creates a `NewPatternedPushRule` with the given ID, pattern and actions.
152    pub fn new(rule_id: String, pattern: String, actions: Vec<Action>) -> Self {
153        Self {
154            rule_id,
155            pattern,
156            actions,
157        }
158    }
159}
160
161impl From<NewPatternedPushRule> for PatternedPushRule {
162    fn from(new_rule: NewPatternedPushRule) -> Self {
163        let NewPatternedPushRule {
164            rule_id,
165            pattern,
166            actions,
167        } = new_rule;
168        Self {
169            actions,
170            default: false,
171            enabled: true,
172            rule_id,
173            pattern,
174        }
175    }
176}
177
178impl From<PatternedPushRule> for PushRule {
179    fn from(push_rule: PatternedPushRule) -> Self {
180        let PatternedPushRule {
181            actions,
182            default,
183            enabled,
184            rule_id,
185            pattern,
186            ..
187        } = push_rule;
188        Self {
189            actions,
190            default,
191            enabled,
192            rule_id,
193            conditions: None,
194            pattern: Some(pattern),
195        }
196    }
197}
198
199impl From<PatternedPushRuleInit> for PushRule {
200    fn from(init: PatternedPushRuleInit) -> Self {
201        let PatternedPushRuleInit {
202            actions,
203            default,
204            enabled,
205            rule_id,
206            pattern,
207        } = init;
208        Self {
209            actions,
210            default,
211            enabled,
212            rule_id,
213            pattern: Some(pattern),
214            conditions: None,
215        }
216    }
217}
218
219impl TryFrom<PushRule> for PatternedPushRule {
220    type Error = MissingPatternError;
221
222    fn try_from(push_rule: PushRule) -> Result<Self, Self::Error> {
223        if let PushRule {
224            actions,
225            default,
226            enabled,
227            rule_id,
228            pattern: Some(pattern),
229            ..
230        } = push_rule
231        {
232            Ok(PatternedPushRuleInit {
233                actions,
234                default,
235                enabled,
236                rule_id,
237                pattern,
238            }
239            .into())
240        } else {
241            Err(MissingPatternError)
242        }
243    }
244}