matrix_sdk_ffi/
notification_settings.rs

1use std::sync::{Arc, RwLock};
2
3use matrix_sdk::{
4    event_handler::EventHandlerHandle,
5    notification_settings::{
6        NotificationSettings as SdkNotificationSettings,
7        RoomNotificationMode as SdkRoomNotificationMode,
8    },
9    ruma::events::push_rules::PushRulesEvent,
10    Client as MatrixClient,
11};
12use ruma::{
13    push::{
14        Action as SdkAction, ComparisonOperator as SdkComparisonOperator, PredefinedOverrideRuleId,
15        PredefinedUnderrideRuleId, PushCondition as SdkPushCondition, RoomMemberCountIs,
16        RuleKind as SdkRuleKind, ScalarJsonValue as SdkJsonValue, Tweak as SdkTweak,
17    },
18    Int, RoomId, UInt,
19};
20use tokio::sync::RwLock as AsyncRwLock;
21
22use crate::error::NotificationSettingsError;
23
24#[derive(Clone, Default, uniffi::Enum)]
25pub enum ComparisonOperator {
26    /// Equals
27    #[default]
28    Eq,
29
30    /// Less than
31    Lt,
32
33    /// Greater than
34    Gt,
35
36    /// Greater or equal
37    Ge,
38
39    /// Less or equal
40    Le,
41}
42
43impl From<SdkComparisonOperator> for ComparisonOperator {
44    fn from(value: SdkComparisonOperator) -> Self {
45        match value {
46            SdkComparisonOperator::Eq => Self::Eq,
47            SdkComparisonOperator::Lt => Self::Lt,
48            SdkComparisonOperator::Gt => Self::Gt,
49            SdkComparisonOperator::Ge => Self::Ge,
50            SdkComparisonOperator::Le => Self::Le,
51        }
52    }
53}
54
55impl From<ComparisonOperator> for SdkComparisonOperator {
56    fn from(value: ComparisonOperator) -> Self {
57        match value {
58            ComparisonOperator::Eq => Self::Eq,
59            ComparisonOperator::Lt => Self::Lt,
60            ComparisonOperator::Gt => Self::Gt,
61            ComparisonOperator::Ge => Self::Ge,
62            ComparisonOperator::Le => Self::Le,
63        }
64    }
65}
66
67#[derive(Debug, Clone, Default, uniffi::Enum)]
68pub enum JsonValue {
69    /// Represents a `null` value.
70    #[default]
71    Null,
72
73    /// Represents a boolean.
74    Bool { value: bool },
75
76    /// Represents an integer.
77    Integer { value: i64 },
78
79    /// Represents a string.
80    String { value: String },
81}
82
83impl From<SdkJsonValue> for JsonValue {
84    fn from(value: SdkJsonValue) -> Self {
85        match value {
86            SdkJsonValue::Null => Self::Null,
87            SdkJsonValue::Bool(b) => Self::Bool { value: b },
88            SdkJsonValue::Integer(i) => Self::Integer { value: i.into() },
89            SdkJsonValue::String(s) => Self::String { value: s },
90        }
91    }
92}
93
94impl From<JsonValue> for SdkJsonValue {
95    fn from(value: JsonValue) -> Self {
96        match value {
97            JsonValue::Null => Self::Null,
98            JsonValue::Bool { value } => Self::Bool(value),
99            JsonValue::Integer { value } => Self::Integer(Int::new(value).unwrap_or_default()),
100            JsonValue::String { value } => Self::String(value),
101        }
102    }
103}
104
105#[derive(Clone, uniffi::Enum)]
106pub enum PushCondition {
107    /// A glob pattern match on a field of the event.
108    EventMatch {
109        /// The [dot-separated path] of the property of the event to match.
110        ///
111        /// [dot-separated path]: https://spec.matrix.org/latest/appendices/#dot-separated-property-paths
112        key: String,
113
114        /// The glob-style pattern to match against.
115        ///
116        /// Patterns with no special glob characters should be treated as having
117        /// asterisks prepended and appended when testing the condition.
118        pattern: String,
119    },
120
121    /// Matches unencrypted messages where `content.body` contains the owner's
122    /// display name in that room.
123    ContainsDisplayName,
124
125    /// Matches the current number of members in the room.
126    RoomMemberCount { prefix: ComparisonOperator, count: u64 },
127
128    /// Takes into account the current power levels in the room, ensuring the
129    /// sender of the event has high enough power to trigger the
130    /// notification.
131    SenderNotificationPermission {
132        /// The field in the power level event the user needs a minimum power
133        /// level for.
134        ///
135        /// Fields must be specified under the `notifications` property in the
136        /// power level event's `content`.
137        key: String,
138    },
139
140    /// Exact value match on a property of the event.
141    EventPropertyIs {
142        /// The [dot-separated path] of the property of the event to match.
143        ///
144        /// [dot-separated path]: https://spec.matrix.org/latest/appendices/#dot-separated-property-paths
145        key: String,
146
147        /// The value to match against.
148        value: JsonValue,
149    },
150
151    /// Exact value match on a value in an array property of the event.
152    EventPropertyContains {
153        /// The [dot-separated path] of the property of the event to match.
154        ///
155        /// [dot-separated path]: https://spec.matrix.org/latest/appendices/#dot-separated-property-paths
156        key: String,
157
158        /// The value to match against.
159        value: JsonValue,
160    },
161}
162
163impl TryFrom<SdkPushCondition> for PushCondition {
164    type Error = ();
165
166    fn try_from(value: SdkPushCondition) -> Result<Self, Self::Error> {
167        Ok(match value {
168            SdkPushCondition::EventMatch { key, pattern } => Self::EventMatch { key, pattern },
169            SdkPushCondition::ContainsDisplayName => Self::ContainsDisplayName,
170            SdkPushCondition::RoomMemberCount { is } => {
171                Self::RoomMemberCount { prefix: is.prefix.into(), count: is.count.into() }
172            }
173            SdkPushCondition::SenderNotificationPermission { key } => {
174                Self::SenderNotificationPermission { key }
175            }
176            SdkPushCondition::EventPropertyIs { key, value } => {
177                Self::EventPropertyIs { key, value: value.into() }
178            }
179            SdkPushCondition::EventPropertyContains { key, value } => {
180                Self::EventPropertyContains { key, value: value.into() }
181            }
182            _ => return Err(()),
183        })
184    }
185}
186
187impl From<PushCondition> for SdkPushCondition {
188    fn from(value: PushCondition) -> Self {
189        match value {
190            PushCondition::EventMatch { key, pattern } => Self::EventMatch { key, pattern },
191            PushCondition::ContainsDisplayName => Self::ContainsDisplayName,
192            PushCondition::RoomMemberCount { prefix, count } => Self::RoomMemberCount {
193                is: RoomMemberCountIs {
194                    prefix: prefix.into(),
195                    count: UInt::new(count).unwrap_or_default(),
196                },
197            },
198            PushCondition::SenderNotificationPermission { key } => {
199                Self::SenderNotificationPermission { key }
200            }
201            PushCondition::EventPropertyIs { key, value } => {
202                Self::EventPropertyIs { key, value: value.into() }
203            }
204            PushCondition::EventPropertyContains { key, value } => {
205                Self::EventPropertyContains { key, value: value.into() }
206            }
207        }
208    }
209}
210
211#[derive(Clone, uniffi::Enum)]
212pub enum RuleKind {
213    /// User-configured rules that override all other kinds.
214    Override,
215
216    /// Lowest priority user-defined rules.
217    Underride,
218
219    /// Sender-specific rules.
220    Sender,
221
222    /// Room-specific rules.
223    Room,
224
225    /// Content-specific rules.
226    Content,
227
228    Custom {
229        value: String,
230    },
231}
232
233impl From<SdkRuleKind> for RuleKind {
234    fn from(value: SdkRuleKind) -> Self {
235        match value {
236            SdkRuleKind::Override => Self::Override,
237            SdkRuleKind::Underride => Self::Underride,
238            SdkRuleKind::Sender => Self::Sender,
239            SdkRuleKind::Room => Self::Room,
240            SdkRuleKind::Content => Self::Content,
241            SdkRuleKind::_Custom(_) => Self::Custom { value: value.as_str().to_owned() },
242            _ => Self::Custom { value: value.to_string() },
243        }
244    }
245}
246
247impl From<RuleKind> for SdkRuleKind {
248    fn from(value: RuleKind) -> Self {
249        match value {
250            RuleKind::Override => Self::Override,
251            RuleKind::Underride => Self::Underride,
252            RuleKind::Sender => Self::Sender,
253            RuleKind::Room => Self::Room,
254            RuleKind::Content => Self::Content,
255            RuleKind::Custom { value } => SdkRuleKind::from(value),
256        }
257    }
258}
259
260#[derive(Clone, uniffi::Enum)]
261/// Enum representing the push notification tweaks for a rule.
262pub enum Tweak {
263    /// A string representing the sound to be played when this notification
264    /// arrives.
265    ///
266    /// A value of "default" means to play a default sound. A device may choose
267    /// to alert the user by some other means if appropriate, eg. vibration.
268    Sound { value: String },
269
270    /// A boolean representing whether or not this message should be highlighted
271    /// in the UI.
272    Highlight { value: bool },
273
274    /// A custom tweak
275    Custom {
276        /// The name of the custom tweak (`set_tweak` field)
277        name: String,
278
279        /// The value of the custom tweak as an encoded JSON string
280        value: String,
281    },
282}
283
284impl TryFrom<SdkTweak> for Tweak {
285    type Error = String;
286
287    fn try_from(value: SdkTweak) -> Result<Self, Self::Error> {
288        Ok(match value {
289            SdkTweak::Sound(sound) => Self::Sound { value: sound },
290            SdkTweak::Highlight(highlight) => Self::Highlight { value: highlight },
291            SdkTweak::Custom { name, value } => {
292                let json_string = serde_json::to_string(&value)
293                    .map_err(|e| format!("Failed to serialize custom tweak value: {}", e))?;
294
295                Self::Custom { name, value: json_string }
296            }
297            _ => return Err("Unsupported tweak type".to_owned()),
298        })
299    }
300}
301
302impl TryFrom<Tweak> for SdkTweak {
303    type Error = String;
304
305    fn try_from(value: Tweak) -> Result<Self, Self::Error> {
306        Ok(match value {
307            Tweak::Sound { value } => Self::Sound(value),
308            Tweak::Highlight { value } => Self::Highlight(value),
309            Tweak::Custom { name, value } => {
310                let json_value: serde_json::Value = serde_json::from_str(&value)
311                    .map_err(|e| format!("Failed to deserialize custom tweak value: {}", e))?;
312                let value = serde_json::from_value(json_value)
313                    .map_err(|e| format!("Failed to convert JSON value: {}", e))?;
314
315                Self::Custom { name, value }
316            }
317        })
318    }
319}
320
321#[derive(Clone, uniffi::Enum)]
322/// Enum representing the push notification actions for a rule.
323pub enum Action {
324    /// Causes matching events to generate a notification.
325    Notify,
326    /// Sets an entry in the 'tweaks' dictionary sent to the push gateway.
327    SetTweak { value: Tweak },
328}
329
330impl TryFrom<SdkAction> for Action {
331    type Error = String;
332
333    fn try_from(value: SdkAction) -> Result<Self, Self::Error> {
334        Ok(match value {
335            SdkAction::Notify => Self::Notify,
336            SdkAction::SetTweak(tweak) => Self::SetTweak {
337                value: tweak.try_into().map_err(|e| format!("Failed to convert tweak: {}", e))?,
338            },
339            _ => return Err("Unsupported action type".to_owned()),
340        })
341    }
342}
343
344impl TryFrom<Action> for SdkAction {
345    type Error = String;
346
347    fn try_from(value: Action) -> Result<Self, Self::Error> {
348        Ok(match value {
349            Action::Notify => Self::Notify,
350            Action::SetTweak { value } => Self::SetTweak(
351                value.try_into().map_err(|e| format!("Failed to convert tweak: {}", e))?,
352            ),
353        })
354    }
355}
356
357/// Enum representing the push notification modes for a room.
358#[derive(Clone, uniffi::Enum)]
359pub enum RoomNotificationMode {
360    /// Receive notifications for all messages.
361    AllMessages,
362    /// Receive notifications for mentions and keywords only.
363    MentionsAndKeywordsOnly,
364    /// Do not receive any notifications.
365    Mute,
366}
367
368impl From<SdkRoomNotificationMode> for RoomNotificationMode {
369    fn from(value: SdkRoomNotificationMode) -> Self {
370        match value {
371            SdkRoomNotificationMode::AllMessages => Self::AllMessages,
372            SdkRoomNotificationMode::MentionsAndKeywordsOnly => Self::MentionsAndKeywordsOnly,
373            SdkRoomNotificationMode::Mute => Self::Mute,
374        }
375    }
376}
377
378impl From<RoomNotificationMode> for SdkRoomNotificationMode {
379    fn from(value: RoomNotificationMode) -> Self {
380        match value {
381            RoomNotificationMode::AllMessages => Self::AllMessages,
382            RoomNotificationMode::MentionsAndKeywordsOnly => Self::MentionsAndKeywordsOnly,
383            RoomNotificationMode::Mute => Self::Mute,
384        }
385    }
386}
387
388/// Delegate to notify of changes in push rules
389#[matrix_sdk_ffi_macros::export(callback_interface)]
390pub trait NotificationSettingsDelegate: Sync + Send {
391    fn settings_did_change(&self);
392}
393
394/// `RoomNotificationSettings` represents the current settings for a `Room`
395#[derive(Clone, uniffi::Record)]
396pub struct RoomNotificationSettings {
397    /// The room notification mode
398    mode: RoomNotificationMode,
399    /// Whether the mode is the default one
400    is_default: bool,
401}
402
403impl RoomNotificationSettings {
404    fn new(mode: RoomNotificationMode, is_default: bool) -> Self {
405        RoomNotificationSettings { mode, is_default }
406    }
407}
408
409#[derive(Clone, uniffi::Object)]
410pub struct NotificationSettings {
411    sdk_client: MatrixClient,
412    sdk_notification_settings: Arc<AsyncRwLock<SdkNotificationSettings>>,
413    pushrules_event_handler: Arc<RwLock<Option<EventHandlerHandle>>>,
414}
415
416impl NotificationSettings {
417    pub(crate) fn new(
418        sdk_client: MatrixClient,
419        sdk_notification_settings: SdkNotificationSettings,
420    ) -> Self {
421        Self {
422            sdk_client,
423            sdk_notification_settings: Arc::new(AsyncRwLock::new(sdk_notification_settings)),
424            pushrules_event_handler: Arc::new(RwLock::new(None)),
425        }
426    }
427}
428
429impl Drop for NotificationSettings {
430    fn drop(&mut self) {
431        // Remove the event handler on the sdk_client.
432        if let Some(event_handler) = self.pushrules_event_handler.read().unwrap().as_ref() {
433            self.sdk_client.remove_event_handler(event_handler.clone());
434        }
435    }
436}
437
438#[matrix_sdk_ffi_macros::export]
439impl NotificationSettings {
440    pub fn set_delegate(&self, delegate: Option<Box<dyn NotificationSettingsDelegate>>) {
441        if let Some(delegate) = delegate {
442            let delegate: Arc<dyn NotificationSettingsDelegate> = Arc::from(delegate);
443
444            // Add an event handler to listen to `PushRulesEvent`
445            let event_handler =
446                self.sdk_client.add_event_handler(move |_: PushRulesEvent| async move {
447                    delegate.settings_did_change();
448                });
449
450            *self.pushrules_event_handler.write().unwrap() = Some(event_handler);
451        } else {
452            // Remove the event handler if there is no delegate
453            let event_handler = &mut *self.pushrules_event_handler.write().unwrap();
454            if let Some(event_handler) = event_handler {
455                self.sdk_client.remove_event_handler(event_handler.clone());
456            }
457            *event_handler = None;
458        }
459    }
460
461    /// Get the notification settings for a room.
462    ///
463    /// # Arguments
464    ///
465    /// * `room_id` - the room ID
466    /// * `is_encrypted` - whether the room is encrypted
467    /// * `is_one_to_one` - whether the room is a direct chat involving two
468    ///   people
469    pub async fn get_room_notification_settings(
470        &self,
471        room_id: String,
472        is_encrypted: bool,
473        is_one_to_one: bool,
474    ) -> Result<RoomNotificationSettings, NotificationSettingsError> {
475        let parsed_room_id = RoomId::parse(&room_id)
476            .map_err(|_e| NotificationSettingsError::InvalidRoomId { room_id })?;
477
478        let notification_settings = self.sdk_notification_settings.read().await;
479
480        // Get the current user defined mode for this room
481        if let Some(mode) =
482            notification_settings.get_user_defined_room_notification_mode(&parsed_room_id).await
483        {
484            return Ok(RoomNotificationSettings::new(mode.into(), false));
485        }
486
487        // If the user has not defined a notification mode, return the default one for
488        // this room
489        let mode = notification_settings
490            .get_default_room_notification_mode(is_encrypted.into(), is_one_to_one.into())
491            .await;
492
493        Ok(RoomNotificationSettings::new(mode.into(), true))
494    }
495
496    /// Set the notification mode for a room.
497    pub async fn set_room_notification_mode(
498        &self,
499        room_id: String,
500        mode: RoomNotificationMode,
501    ) -> Result<(), NotificationSettingsError> {
502        let parsed_room_id = RoomId::parse(&room_id)
503            .map_err(|_e| NotificationSettingsError::InvalidRoomId { room_id })?;
504
505        self.sdk_notification_settings
506            .read()
507            .await
508            .set_room_notification_mode(&parsed_room_id, mode.into())
509            .await?;
510
511        Ok(())
512    }
513
514    /// Get the user defined room notification mode
515    pub async fn get_user_defined_room_notification_mode(
516        &self,
517        room_id: String,
518    ) -> Result<Option<RoomNotificationMode>, NotificationSettingsError> {
519        let notification_settings = self.sdk_notification_settings.read().await;
520        let parsed_room_id = RoomId::parse(&room_id)
521            .map_err(|_e| NotificationSettingsError::InvalidRoomId { room_id })?;
522        // Get the current user defined mode for this room
523        if let Some(mode) =
524            notification_settings.get_user_defined_room_notification_mode(&parsed_room_id).await
525        {
526            Ok(Some(mode.into()))
527        } else {
528            Ok(None)
529        }
530    }
531
532    /// Get the default room notification mode
533    ///
534    /// The mode will depend on the associated `PushRule` based on whether the
535    /// room is encrypted or not, and on the number of members.
536    ///
537    /// # Arguments
538    ///
539    /// * `is_encrypted` - whether the room is encrypted
540    /// * `is_one_to_one` - whether the room is a direct chats involving two
541    ///   people
542    pub async fn get_default_room_notification_mode(
543        &self,
544        is_encrypted: bool,
545        is_one_to_one: bool,
546    ) -> RoomNotificationMode {
547        let notification_settings = self.sdk_notification_settings.read().await;
548        let mode = notification_settings
549            .get_default_room_notification_mode(is_encrypted.into(), is_one_to_one.into())
550            .await;
551        mode.into()
552    }
553
554    /// Set the default room notification mode
555    ///
556    /// # Arguments
557    ///
558    /// * `is_encrypted` - whether the mode is for encrypted rooms
559    /// * `is_one_to_one` - whether the mode is for direct chats involving two
560    ///   people
561    /// * `mode` - the new default mode
562    pub async fn set_default_room_notification_mode(
563        &self,
564        is_encrypted: bool,
565        is_one_to_one: bool,
566        mode: RoomNotificationMode,
567    ) -> Result<(), NotificationSettingsError> {
568        let notification_settings = self.sdk_notification_settings.read().await;
569        notification_settings
570            .set_default_room_notification_mode(
571                is_encrypted.into(),
572                is_one_to_one.into(),
573                mode.into(),
574            )
575            .await?;
576        Ok(())
577    }
578
579    /// Restore the default notification mode for a room
580    pub async fn restore_default_room_notification_mode(
581        &self,
582        room_id: String,
583    ) -> Result<(), NotificationSettingsError> {
584        let notification_settings = self.sdk_notification_settings.read().await;
585        let parsed_room_id = RoomId::parse(&room_id)
586            .map_err(|_e| NotificationSettingsError::InvalidRoomId { room_id })?;
587        notification_settings.delete_user_defined_room_rules(&parsed_room_id).await?;
588        Ok(())
589    }
590
591    /// Get all room IDs for which a user-defined rule exists.
592    pub async fn get_rooms_with_user_defined_rules(&self, enabled: Option<bool>) -> Vec<String> {
593        let notification_settings = self.sdk_notification_settings.read().await;
594        notification_settings.get_rooms_with_user_defined_rules(enabled).await
595    }
596
597    /// Get whether some enabled keyword rules exist.
598    pub async fn contains_keywords_rules(&self) -> bool {
599        let notification_settings = self.sdk_notification_settings.read().await;
600        notification_settings.contains_keyword_rules().await
601    }
602
603    /// Get whether room mentions are enabled.
604    pub async fn is_room_mention_enabled(&self) -> Result<bool, NotificationSettingsError> {
605        let notification_settings = self.sdk_notification_settings.read().await;
606        let enabled = notification_settings
607            .is_push_rule_enabled(SdkRuleKind::Override, PredefinedOverrideRuleId::IsRoomMention)
608            .await?;
609        Ok(enabled)
610    }
611
612    /// Set whether room mentions are enabled.
613    pub async fn set_room_mention_enabled(
614        &self,
615        enabled: bool,
616    ) -> Result<(), NotificationSettingsError> {
617        let notification_settings = self.sdk_notification_settings.read().await;
618        notification_settings
619            .set_push_rule_enabled(
620                SdkRuleKind::Override,
621                PredefinedOverrideRuleId::IsRoomMention,
622                enabled,
623            )
624            .await?;
625        Ok(())
626    }
627
628    /// Get whether user mentions are enabled.
629    pub async fn is_user_mention_enabled(&self) -> Result<bool, NotificationSettingsError> {
630        let notification_settings = self.sdk_notification_settings.read().await;
631        let enabled = notification_settings
632            .is_push_rule_enabled(SdkRuleKind::Override, PredefinedOverrideRuleId::IsUserMention)
633            .await?;
634        Ok(enabled)
635    }
636
637    /// Returns true if [MSC 4028 push rule][rule] is supported and enabled.
638    ///
639    /// [rule]: https://github.com/matrix-org/matrix-spec-proposals/blob/giomfo/push_encrypted_events/proposals/4028-push-all-encrypted-events-except-for-muted-rooms.md
640    pub async fn can_push_encrypted_event_to_device(&self) -> bool {
641        let notification_settings = self.sdk_notification_settings.read().await;
642        // Check stable identifier
643        if let Ok(enabled) = notification_settings
644            .is_push_rule_enabled(SdkRuleKind::Override, ".m.rule.encrypted_event")
645            .await
646        {
647            enabled
648        } else {
649            // Check unstable identifier
650            notification_settings
651                .is_push_rule_enabled(SdkRuleKind::Override, ".org.matrix.msc4028.encrypted_event")
652                .await
653                .unwrap_or(false)
654        }
655    }
656
657    /// Check whether [MSC 4028 push rule][rule] is enabled on the homeserver.
658    ///
659    /// [rule]: https://github.com/matrix-org/matrix-spec-proposals/blob/giomfo/push_encrypted_events/proposals/4028-push-all-encrypted-events-except-for-muted-rooms.md
660    pub async fn can_homeserver_push_encrypted_event_to_device(&self) -> bool {
661        self.sdk_client.can_homeserver_push_encrypted_event_to_device().await.unwrap()
662    }
663
664    /// Set whether user mentions are enabled.
665    pub async fn set_user_mention_enabled(
666        &self,
667        enabled: bool,
668    ) -> Result<(), NotificationSettingsError> {
669        let notification_settings = self.sdk_notification_settings.read().await;
670        notification_settings
671            .set_push_rule_enabled(
672                SdkRuleKind::Override,
673                PredefinedOverrideRuleId::IsUserMention,
674                enabled,
675            )
676            .await?;
677        Ok(())
678    }
679
680    /// Get whether the `.m.rule.call` push rule is enabled
681    pub async fn is_call_enabled(&self) -> Result<bool, NotificationSettingsError> {
682        let notification_settings = self.sdk_notification_settings.read().await;
683        let enabled = notification_settings
684            .is_push_rule_enabled(SdkRuleKind::Underride, PredefinedUnderrideRuleId::Call)
685            .await?;
686        Ok(enabled)
687    }
688
689    /// Set whether the `.m.rule.call` push rule is enabled
690    pub async fn set_call_enabled(&self, enabled: bool) -> Result<(), NotificationSettingsError> {
691        let notification_settings = self.sdk_notification_settings.read().await;
692        notification_settings
693            .set_push_rule_enabled(SdkRuleKind::Underride, PredefinedUnderrideRuleId::Call, enabled)
694            .await?;
695        Ok(())
696    }
697
698    /// Get whether the `.m.rule.invite_for_me` push rule is enabled
699    pub async fn is_invite_for_me_enabled(&self) -> Result<bool, NotificationSettingsError> {
700        let notification_settings = self.sdk_notification_settings.read().await;
701        let enabled = notification_settings
702            .is_push_rule_enabled(
703                SdkRuleKind::Override,
704                PredefinedOverrideRuleId::InviteForMe.as_str(),
705            )
706            .await?;
707        Ok(enabled)
708    }
709
710    /// Set whether the `.m.rule.invite_for_me` push rule is enabled
711    pub async fn set_invite_for_me_enabled(
712        &self,
713        enabled: bool,
714    ) -> Result<(), NotificationSettingsError> {
715        let notification_settings = self.sdk_notification_settings.read().await;
716        notification_settings
717            .set_push_rule_enabled(
718                SdkRuleKind::Override,
719                PredefinedOverrideRuleId::InviteForMe.as_str(),
720                enabled,
721            )
722            .await?;
723        Ok(())
724    }
725
726    /// Sets a custom push rule with the given actions and conditions.
727    pub async fn set_custom_push_rule(
728        &self,
729        rule_id: String,
730        rule_kind: RuleKind,
731        actions: Vec<Action>,
732        conditions: Vec<PushCondition>,
733    ) -> Result<(), NotificationSettingsError> {
734        let notification_settings = self.sdk_notification_settings.read().await;
735        let actions: Result<Vec<_>, _> =
736            actions.into_iter().map(|action| action.try_into()).collect();
737        let actions = actions.map_err(|e| NotificationSettingsError::Generic { msg: e })?;
738
739        notification_settings
740            .create_custom_conditional_push_rule(
741                rule_id,
742                rule_kind.into(),
743                actions,
744                conditions.into_iter().map(|condition| condition.into()).collect(),
745            )
746            .await?;
747        Ok(())
748    }
749
750    /// Unmute a room.
751    ///
752    /// # Arguments
753    ///
754    /// * `room_id` - the room to unmute
755    /// * `is_encrypted` - whether the room is encrypted
756    /// * `is_one_to_one` - whether the room is a direct chat involving two
757    ///   people
758    pub async fn unmute_room(
759        &self,
760        room_id: String,
761        is_encrypted: bool,
762        is_one_to_one: bool,
763    ) -> Result<(), NotificationSettingsError> {
764        let notification_settings = self.sdk_notification_settings.read().await;
765        let parsed_room_id = RoomId::parse(&room_id)
766            .map_err(|_e| NotificationSettingsError::InvalidRoomId { room_id })?;
767        notification_settings
768            .unmute_room(&parsed_room_id, is_encrypted.into(), is_one_to_one.into())
769            .await?;
770        Ok(())
771    }
772}