1use std::{ops::Deref, sync::Arc};
18
19use indexmap::IndexSet;
20use ruma::{
21 RoomId,
22 api::client::push::{
23 delete_pushrule, set_pushrule, set_pushrule_actions, set_pushrule_enabled,
24 },
25 events::push_rules::PushRulesEvent,
26 push::{
27 Action, NewPushRule, PredefinedUnderrideRuleId, RuleKind, Ruleset, SoundTweakValue, Tweak,
28 },
29};
30use tokio::sync::{
31 RwLock,
32 broadcast::{self, Receiver},
33};
34use tracing::{debug, error};
35
36use self::{command::Command, rule_commands::RuleCommands, rules::Rules};
37
38mod command;
39mod rule_commands;
40mod rules;
41
42pub use matrix_sdk_base::notification_settings::RoomNotificationMode;
43
44use crate::{
45 Client, Result, config::RequestConfig, error::NotificationSettingsError,
46 event_handler::EventHandlerDropGuard,
47};
48
49#[derive(Debug, Clone, Copy)]
51pub enum IsEncrypted {
52 Yes,
54 No,
56}
57
58impl From<bool> for IsEncrypted {
59 fn from(value: bool) -> Self {
60 if value { Self::Yes } else { Self::No }
61 }
62}
63
64#[derive(Debug, Clone, Copy)]
66pub enum IsOneToOne {
67 Yes,
69 No,
71}
72
73impl From<bool> for IsOneToOne {
74 fn from(value: bool) -> Self {
75 if value { Self::Yes } else { Self::No }
76 }
77}
78
79#[derive(Debug, Clone)]
81pub struct NotificationSettings {
82 client: Client,
84 rules: Arc<RwLock<Rules>>,
86 _push_rules_event_handler_guard: Arc<EventHandlerDropGuard>,
88 changes_sender: broadcast::Sender<()>,
91}
92
93impl NotificationSettings {
94 pub(crate) fn new(client: Client, ruleset: Ruleset) -> Self {
101 let changes_sender = broadcast::Sender::new(100);
102 let rules = Arc::new(RwLock::new(Rules::new(ruleset)));
103
104 let push_rules_event_handler_handle = client.add_event_handler({
106 let changes_sender = changes_sender.clone();
107 let rules = rules.clone();
108 move |ev: PushRulesEvent| async move {
109 *rules.write().await = Rules::new(ev.content.global);
110 let _ = changes_sender.send(());
111 }
112 });
113
114 let _push_rules_event_handler_guard =
115 Arc::new(client.event_handler_drop_guard(push_rules_event_handler_handle));
116
117 Self { client, rules, _push_rules_event_handler_guard, changes_sender }
118 }
119
120 pub fn subscribe_to_changes(&self) -> Receiver<()> {
125 self.changes_sender.subscribe()
126 }
127
128 pub async fn get_user_defined_room_notification_mode(
130 &self,
131 room_id: &RoomId,
132 ) -> Option<RoomNotificationMode> {
133 self.rules.read().await.get_user_defined_room_notification_mode(room_id)
134 }
135
136 pub async fn get_default_room_notification_mode(
144 &self,
145 is_encrypted: IsEncrypted,
146 is_one_to_one: IsOneToOne,
147 ) -> RoomNotificationMode {
148 self.rules.read().await.get_default_room_notification_mode(is_encrypted, is_one_to_one)
149 }
150
151 pub async fn get_rooms_with_user_defined_rules(&self, enabled: Option<bool>) -> Vec<String> {
153 self.rules.read().await.get_rooms_with_user_defined_rules(enabled)
154 }
155
156 pub async fn contains_keyword_rules(&self) -> bool {
158 self.rules.read().await.contains_keyword_rules()
159 }
160
161 pub async fn is_push_rule_enabled(
163 &self,
164 kind: RuleKind,
165 rule_id: impl AsRef<str>,
166 ) -> Result<bool, NotificationSettingsError> {
167 self.rules.read().await.is_enabled(kind, rule_id.as_ref())
168 }
169
170 pub async fn set_push_rule_enabled(
172 &self,
173 kind: RuleKind,
174 rule_id: impl AsRef<str>,
175 enabled: bool,
176 ) -> Result<(), NotificationSettingsError> {
177 let rules = self.rules.read().await.clone();
178
179 let mut rule_commands = RuleCommands::new(rules.ruleset);
180 rule_commands.set_rule_enabled(kind, rule_id.as_ref(), enabled)?;
181
182 self.run_server_commands(&rule_commands).await?;
183
184 let rules = &mut *self.rules.write().await;
185 rules.apply(rule_commands);
186
187 Ok(())
188 }
189
190 pub async fn set_default_room_notification_mode(
199 &self,
200 is_encrypted: IsEncrypted,
201 is_one_to_one: IsOneToOne,
202 mode: RoomNotificationMode,
203 ) -> Result<(), NotificationSettingsError> {
204 let actions = match mode {
205 RoomNotificationMode::AllMessages => {
206 vec![Action::Notify, Action::SetTweak(Tweak::Sound(SoundTweakValue::Default))]
207 }
208 _ => {
209 vec![]
210 }
211 };
212
213 let room_rule_id =
214 rules::get_predefined_underride_room_rule_id(is_encrypted, is_one_to_one);
215 self.set_underride_push_rule_actions(room_rule_id, actions.clone()).await?;
216
217 let poll_start_rule_id = rules::get_predefined_underride_poll_start_rule_id(is_one_to_one);
218 if let Err(error) =
219 self.set_underride_push_rule_actions(poll_start_rule_id, actions.clone()).await
220 {
221 if let NotificationSettingsError::RuleNotFound(rule_id) = &error {
225 debug!("Unable to update poll start push rule: rule `{rule_id}` not found");
226 } else {
227 return Err(error);
228 }
229 }
230
231 Ok(())
232 }
233
234 pub async fn set_underride_push_rule_actions(
245 &self,
246 rule_id: PredefinedUnderrideRuleId,
247 actions: Vec<Action>,
248 ) -> Result<(), NotificationSettingsError> {
249 let rules = self.rules.read().await.clone();
250 let rule_kind = RuleKind::Underride;
251 let mut rule_commands = RuleCommands::new(rules.clone().ruleset);
252
253 rule_commands.set_rule_actions(rule_kind.clone(), rule_id.as_str(), actions)?;
254
255 if !rules.is_enabled(rule_kind.clone(), rule_id.as_str())? {
256 rule_commands.set_rule_enabled(rule_kind, rule_id.as_str(), true)?
257 }
258
259 self.run_server_commands(&rule_commands).await?;
260
261 let rules = &mut *self.rules.write().await;
262 rules.apply(rule_commands);
263
264 Ok(())
265 }
266
267 pub async fn create_custom_conditional_push_rule(
280 &self,
281 rule_id: String,
282 rule_kind: RuleKind,
283 actions: Vec<Action>,
284 conditions: Vec<ruma::push::PushCondition>,
285 ) -> Result<(), NotificationSettingsError> {
286 let new_conditional_rule =
287 ruma::push::NewConditionalPushRule::new(rule_id, conditions, actions);
288
289 let new_push_rule = match rule_kind {
290 RuleKind::Override => NewPushRule::Override(new_conditional_rule),
291 RuleKind::Underride => NewPushRule::Underride(new_conditional_rule),
292 _ => return Err(NotificationSettingsError::InvalidParameter("rule_kind".to_owned())),
293 };
294
295 let rules = self.rules.read().await.clone();
296 let mut rule_commands = RuleCommands::new(rules.clone().ruleset);
297 rule_commands.insert_custom_rule(new_push_rule)?;
298
299 self.run_server_commands(&rule_commands).await?;
300
301 let rules = &mut *self.rules.write().await;
302 rules.apply(rule_commands);
303
304 Ok(())
305 }
306
307 pub async fn set_room_notification_mode(
309 &self,
310 room_id: &RoomId,
311 mode: RoomNotificationMode,
312 ) -> Result<(), NotificationSettingsError> {
313 let rules = self.rules.read().await.clone();
314
315 if rules.get_user_defined_room_notification_mode(room_id) == Some(mode) {
317 return Ok(());
318 }
319
320 let (new_rule_kind, notify) = match mode {
322 RoomNotificationMode::AllMessages => {
323 (RuleKind::Room, true)
325 }
326 RoomNotificationMode::MentionsAndKeywordsOnly => {
327 (RuleKind::Room, false)
329 }
330 RoomNotificationMode::Mute => {
331 (RuleKind::Override, false)
333 }
334 };
335
336 let new_rule_id = room_id.as_str();
338 let custom_rules: Vec<(RuleKind, String)> = rules
339 .get_custom_rules_for_room(room_id)
340 .into_iter()
341 .filter(|(kind, rule_id)| kind != &new_rule_kind || rule_id != new_rule_id)
342 .collect();
343
344 let mut rule_commands = RuleCommands::new(rules.ruleset);
347 rule_commands.insert_rule(new_rule_kind.clone(), room_id, notify)?;
348 for (kind, rule_id) in custom_rules {
349 rule_commands.delete_rule(kind, rule_id)?;
350 }
351
352 self.run_server_commands(&rule_commands).await?;
353
354 let rules = &mut *self.rules.write().await;
355 rules.apply(rule_commands);
356
357 Ok(())
358 }
359
360 pub async fn delete_user_defined_room_rules(
362 &self,
363 room_id: &RoomId,
364 ) -> Result<(), NotificationSettingsError> {
365 let rules = self.rules.read().await.clone();
366
367 let custom_rules = rules.get_custom_rules_for_room(room_id);
368 if custom_rules.is_empty() {
369 return Ok(());
370 }
371
372 let mut rule_commands = RuleCommands::new(rules.ruleset);
373 for (kind, rule_id) in custom_rules {
374 rule_commands.delete_rule(kind, rule_id)?;
375 }
376
377 self.run_server_commands(&rule_commands).await?;
378
379 let rules = &mut *self.rules.write().await;
380 rules.apply(rule_commands);
381
382 Ok(())
383 }
384
385 pub async fn unmute_room(
387 &self,
388 room_id: &RoomId,
389 is_encrypted: IsEncrypted,
390 is_one_to_one: IsOneToOne,
391 ) -> Result<(), NotificationSettingsError> {
392 let rules = self.rules.read().await.clone();
393
394 if let Some(room_mode) = rules.get_user_defined_room_notification_mode(room_id) {
396 if room_mode != RoomNotificationMode::Mute {
397 return Ok(());
399 }
400
401 let default_mode =
403 rules.get_default_room_notification_mode(is_encrypted, is_one_to_one);
404
405 if default_mode == RoomNotificationMode::Mute {
407 self.set_room_notification_mode(room_id, RoomNotificationMode::AllMessages).await
408 } else {
409 self.delete_user_defined_room_rules(room_id).await
411 }
412 } else {
413 self.set_room_notification_mode(room_id, RoomNotificationMode::AllMessages).await
416 }
417 }
418
419 pub async fn enabled_keywords(&self) -> IndexSet<String> {
421 self.rules.read().await.enabled_keywords()
422 }
423
424 pub async fn add_keyword(&self, keyword: String) -> Result<(), NotificationSettingsError> {
430 let rules = self.rules.read().await.clone();
431
432 let mut rule_commands = RuleCommands::new(rules.clone().ruleset);
433
434 let existing_rules = rules.keyword_rules(&keyword);
435
436 if existing_rules.is_empty() {
437 rule_commands.insert_keyword_rule(keyword)?;
439 } else {
440 if existing_rules.iter().any(|r| r.enabled) {
441 return Ok(());
443 }
444
445 rule_commands.set_rule_enabled(RuleKind::Content, &existing_rules[0].rule_id, true)?;
447 }
448
449 self.run_server_commands(&rule_commands).await?;
450
451 let rules = &mut *self.rules.write().await;
452 rules.apply(rule_commands);
453
454 Ok(())
455 }
456
457 pub async fn remove_keyword(&self, keyword: &str) -> Result<(), NotificationSettingsError> {
463 let rules = self.rules.read().await.clone();
464
465 let mut rule_commands = RuleCommands::new(rules.clone().ruleset);
466
467 let existing_rules = rules.keyword_rules(keyword);
468
469 if existing_rules.is_empty() {
470 return Ok(());
471 }
472
473 for rule in existing_rules {
474 rule_commands.delete_rule(RuleKind::Content, rule.rule_id.clone())?;
475 }
476
477 self.run_server_commands(&rule_commands).await?;
478
479 let rules = &mut *self.rules.write().await;
480 rules.apply(rule_commands);
481
482 Ok(())
483 }
484
485 async fn run_server_commands(
487 &self,
488 rule_commands: &RuleCommands,
489 ) -> Result<(), NotificationSettingsError> {
490 let request_config = Some(RequestConfig::short_retry());
491 for command in &rule_commands.commands {
492 match command {
493 Command::DeletePushRule { kind, rule_id } => {
494 let request = delete_pushrule::v3::Request::new(kind.clone(), rule_id.clone());
495 self.client.send(request).with_request_config(request_config).await.map_err(
496 |error| {
497 error!("Unable to delete {kind} push rule `{rule_id}`: {error}");
498 NotificationSettingsError::UnableToRemovePushRule
499 },
500 )?;
501 }
502 Command::SetRoomPushRule { room_id, notify: _ } => {
503 let push_rule = command.to_push_rule()?;
504 let request = set_pushrule::v3::Request::new(push_rule);
505 self.client.send(request).with_request_config(request_config).await.map_err(
506 |error| {
507 error!("Unable to set room push rule `{room_id}`: {error}");
508 NotificationSettingsError::UnableToAddPushRule
509 },
510 )?;
511 }
512 Command::SetOverridePushRule { rule_id, room_id: _, notify: _ } => {
513 let push_rule = command.to_push_rule()?;
514 let request = set_pushrule::v3::Request::new(push_rule);
515 self.client.send(request).with_request_config(request_config).await.map_err(
516 |error| {
517 error!("Unable to set override push rule `{rule_id}`: {error}");
518 NotificationSettingsError::UnableToAddPushRule
519 },
520 )?;
521 }
522 Command::SetKeywordPushRule { keyword: _ } => {
523 let push_rule = command.to_push_rule()?;
524 let request = set_pushrule::v3::Request::new(push_rule);
525 self.client
526 .send(request)
527 .with_request_config(request_config)
528 .await
529 .map_err(|_| NotificationSettingsError::UnableToAddPushRule)?;
530 }
531 Command::SetPushRuleEnabled { kind, rule_id, enabled } => {
532 let request = set_pushrule_enabled::v3::Request::new(
533 kind.clone(),
534 rule_id.clone(),
535 *enabled,
536 );
537 self.client.send(request).with_request_config(request_config).await.map_err(
538 |error| {
539 error!("Unable to set {kind} push rule `{rule_id}` enabled: {error}");
540 NotificationSettingsError::UnableToUpdatePushRule
541 },
542 )?;
543 }
544 Command::SetPushRuleActions { kind, rule_id, actions } => {
545 let request = set_pushrule_actions::v3::Request::new(
546 kind.clone(),
547 rule_id.clone(),
548 actions.clone(),
549 );
550 self.client.send(request).with_request_config(request_config).await.map_err(
551 |error| {
552 error!("Unable to set {kind} push rule `{rule_id}` actions: {error}");
553 NotificationSettingsError::UnableToUpdatePushRule
554 },
555 )?;
556 }
557 Command::SetCustomPushRule { rule } => {
558 let request = set_pushrule::v3::Request::new(rule.clone());
559
560 self.client.send(request).with_request_config(request_config).await.map_err(
561 |error| {
562 error!("Unable to set custom push rule `{rule:#?}`: {error}");
563 NotificationSettingsError::UnableToAddPushRule
564 },
565 )?;
566 }
567 }
568 }
569 Ok(())
570 }
571
572 pub async fn ruleset(&self) -> Ruleset {
575 self.rules.read().await.ruleset.clone()
576 }
577
578 pub(crate) async fn rules(&self) -> impl Deref<Target = Rules> + '_ {
580 self.rules.read().await
581 }
582}
583
584#[cfg(all(test, not(target_family = "wasm")))]
586mod tests {
587 use std::sync::{
588 Arc,
589 atomic::{AtomicBool, Ordering},
590 };
591
592 use assert_matches::assert_matches;
593 use matrix_sdk_test::{
594 TestResult, async_test,
595 event_factory::EventFactory,
596 notification_settings::{build_ruleset, get_server_default_ruleset},
597 };
598 use ruma::{
599 OwnedRoomId, RoomId, owned_room_id,
600 push::{
601 Action, AnyPushRuleRef, EventMatchConditionData, NewPatternedPushRule, NewPushRule,
602 PredefinedContentRuleId, PredefinedOverrideRuleId, PredefinedUnderrideRuleId,
603 PushCondition, RuleKind, Ruleset,
604 },
605 };
606 use stream_assert::{assert_next_eq, assert_pending};
607 use tokio_stream::wrappers::BroadcastStream;
608 use wiremock::{
609 Mock, MockServer, ResponseTemplate,
610 matchers::{method, path, path_regex},
611 };
612
613 use crate::{
614 Client,
615 error::NotificationSettingsError,
616 notification_settings::{
617 IsEncrypted, IsOneToOne, NotificationSettings, RoomNotificationMode,
618 },
619 test_utils::{logged_in_client, mocks::MatrixMockServer},
620 };
621
622 fn get_test_room_id() -> OwnedRoomId {
623 owned_room_id!("!AAAaAAAAAaaAAaaaaa:matrix.org")
624 }
625
626 fn from_insert_rules(
627 client: &Client,
628 rules: Vec<(RuleKind, &RoomId, bool)>,
629 ) -> NotificationSettings {
630 let ruleset = build_ruleset(rules);
631 NotificationSettings::new(client.to_owned(), ruleset)
632 }
633
634 async fn get_custom_rules_for_room(
635 settings: &NotificationSettings,
636 room_id: &RoomId,
637 ) -> Vec<(RuleKind, String)> {
638 settings.rules.read().await.get_custom_rules_for_room(room_id)
639 }
640
641 #[async_test]
642 async fn test_subscribe_to_changes() -> TestResult {
643 let server = MatrixMockServer::new().await;
644 let client = server.client_builder().build().await;
645 let settings = client.notification_settings().await;
646
647 let subscriber = settings.subscribe_to_changes();
648 let mut stream = BroadcastStream::new(subscriber);
649
650 assert_pending!(stream);
651
652 server
653 .mock_sync()
654 .ok_and_run(&client, |sync_response_builder| {
655 let f = EventFactory::new();
656 sync_response_builder.add_global_account_data(
657 f.push_rules(Ruleset::server_default(client.user_id().unwrap())),
658 );
659 })
660 .await;
661
662 assert_next_eq!(stream, Ok(()));
663 assert_pending!(stream);
664
665 Ok(())
666 }
667
668 #[async_test]
669 async fn test_get_custom_rules_for_room() {
670 let server = MockServer::start().await;
671 let client = logged_in_client(Some(server.uri())).await;
672 let room_id = get_test_room_id();
673
674 let settings = from_insert_rules(&client, vec![(RuleKind::Room, &room_id, true)]);
675
676 let custom_rules = get_custom_rules_for_room(&settings, &room_id).await;
677 assert_eq!(custom_rules.len(), 1);
678 assert_eq!(custom_rules[0], (RuleKind::Room, room_id.to_string()));
679
680 let settings = from_insert_rules(
681 &client,
682 vec![(RuleKind::Room, &room_id, true), (RuleKind::Override, &room_id, true)],
683 );
684 let custom_rules = get_custom_rules_for_room(&settings, &room_id).await;
685 assert_eq!(custom_rules.len(), 2);
686 assert_eq!(custom_rules[0], (RuleKind::Override, room_id.to_string()));
687 assert_eq!(custom_rules[1], (RuleKind::Room, room_id.to_string()));
688 }
689
690 #[async_test]
691 async fn test_get_user_defined_room_notification_mode_none() {
692 let server = MockServer::start().await;
693 let client = logged_in_client(Some(server.uri())).await;
694 let room_id = get_test_room_id();
695
696 let settings = client.notification_settings().await;
697 assert!(settings.get_user_defined_room_notification_mode(&room_id).await.is_none());
698 }
699
700 #[async_test]
701 async fn test_get_user_defined_room_notification_mode_all_messages() {
702 let server = MockServer::start().await;
703 let client = logged_in_client(Some(server.uri())).await;
704 let room_id = get_test_room_id();
705
706 let settings = from_insert_rules(&client, vec![(RuleKind::Room, &room_id, true)]);
708
709 assert_eq!(
710 settings.get_user_defined_room_notification_mode(&room_id).await.unwrap(),
711 RoomNotificationMode::AllMessages
712 );
713 }
714
715 #[async_test]
716 async fn test_get_user_defined_room_notification_mode_mentions_and_keywords() {
717 let server = MockServer::start().await;
718 let client = logged_in_client(Some(server.uri())).await;
719 let room_id = get_test_room_id();
720
721 let settings = from_insert_rules(&client, vec![(RuleKind::Room, &room_id, false)]);
724 assert_eq!(
725 settings.get_user_defined_room_notification_mode(&room_id).await.unwrap(),
726 RoomNotificationMode::MentionsAndKeywordsOnly
727 );
728 }
729
730 #[async_test]
731 async fn test_get_user_defined_room_notification_mode_mute() {
732 let server = MockServer::start().await;
733 let client = logged_in_client(Some(server.uri())).await;
734 let room_id = get_test_room_id();
735
736 let settings = from_insert_rules(&client, vec![(RuleKind::Override, &room_id, false)]);
738 assert_eq!(
739 settings.get_user_defined_room_notification_mode(&room_id).await.unwrap(),
740 RoomNotificationMode::Mute
741 );
742 }
743
744 #[async_test]
745 async fn test_get_default_room_notification_mode_all_messages() -> TestResult {
746 let server = MockServer::start().await;
747 let client = logged_in_client(Some(server.uri())).await;
748
749 let mut ruleset = get_server_default_ruleset();
750 ruleset.set_actions(
751 RuleKind::Underride,
752 PredefinedUnderrideRuleId::RoomOneToOne,
753 vec![Action::Notify],
754 )?;
755
756 let settings = NotificationSettings::new(client, ruleset);
757 assert_eq!(
758 settings.get_default_room_notification_mode(IsEncrypted::No, IsOneToOne::Yes).await,
759 RoomNotificationMode::AllMessages
760 );
761
762 Ok(())
763 }
764
765 #[async_test]
766 async fn test_get_default_room_notification_mode_mentions_and_keywords() -> TestResult {
767 let server = MockServer::start().await;
768 let client = logged_in_client(Some(server.uri())).await;
769
770 let mut ruleset = get_server_default_ruleset();
773 ruleset.set_actions(
774 RuleKind::Underride,
775 PredefinedUnderrideRuleId::RoomOneToOne,
776 vec![],
777 )?;
778
779 let settings = NotificationSettings::new(client.to_owned(), ruleset.to_owned());
780 assert_eq!(
781 settings.get_default_room_notification_mode(IsEncrypted::No, IsOneToOne::Yes).await,
782 RoomNotificationMode::MentionsAndKeywordsOnly
783 );
784
785 ruleset.set_enabled(RuleKind::Underride, PredefinedUnderrideRuleId::RoomOneToOne, false)?;
788
789 let settings = NotificationSettings::new(client, ruleset);
790 assert_eq!(
791 settings.get_default_room_notification_mode(IsEncrypted::No, IsOneToOne::Yes).await,
792 RoomNotificationMode::MentionsAndKeywordsOnly
793 );
794
795 Ok(())
796 }
797
798 #[async_test]
799 async fn test_contains_keyword_rules() -> TestResult {
800 let server = MockServer::start().await;
801 let client = logged_in_client(Some(server.uri())).await;
802
803 let mut ruleset = get_server_default_ruleset();
804 let settings = NotificationSettings::new(client.to_owned(), ruleset.to_owned());
805
806 let contains_keywords_rules = settings.contains_keyword_rules().await;
808 assert!(!contains_keywords_rules);
809
810 let rule = NewPatternedPushRule::new(
812 "keyword_rule_id".into(),
813 "keyword".into(),
814 vec![Action::Notify],
815 );
816 ruleset.insert(NewPushRule::Content(rule), None, None)?;
817
818 let settings = NotificationSettings::new(client, ruleset);
819 let contains_keywords_rules = settings.contains_keyword_rules().await;
820 assert!(contains_keywords_rules);
821
822 Ok(())
823 }
824
825 #[async_test]
826 async fn test_is_push_rule_enabled() -> TestResult {
827 let server = MockServer::start().await;
828 let client = logged_in_client(Some(server.uri())).await;
829
830 let mut ruleset = get_server_default_ruleset();
832 ruleset.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction, false)?;
833
834 let settings = NotificationSettings::new(client.clone(), ruleset);
835
836 let enabled = settings
837 .is_push_rule_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction)
838 .await?;
839
840 assert!(!enabled);
841
842 let mut ruleset = get_server_default_ruleset();
844 ruleset.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction, true)?;
845
846 let settings = NotificationSettings::new(client, ruleset);
847
848 let enabled = settings
849 .is_push_rule_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction)
850 .await?;
851
852 assert!(enabled);
853 Ok(())
854 }
855
856 #[async_test]
857 async fn test_set_push_rule_enabled() -> TestResult {
858 let server = MockServer::start().await;
859 let client = logged_in_client(Some(server.uri())).await;
860 let mut ruleset = client.account().push_rules().await?;
861 ruleset.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction, false)?;
863
864 let settings = NotificationSettings::new(client, ruleset);
865
866 Mock::given(method("PUT"))
867 .and(path("/_matrix/client/r0/pushrules/global/override/.m.rule.reaction/enabled"))
868 .respond_with(ResponseTemplate::new(200))
869 .expect(1)
870 .mount(&server)
871 .await;
872
873 settings
874 .set_push_rule_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction, true)
875 .await?;
876
877 let rules = settings.rules.read().await;
879 let rule =
880 rules.ruleset.get(RuleKind::Override, PredefinedOverrideRuleId::Reaction).unwrap();
881 assert!(rule.enabled());
882
883 server.verify().await;
884 Ok(())
885 }
886
887 #[async_test]
888 async fn test_set_push_rule_enabled_api_error() -> TestResult {
889 let server = MockServer::start().await;
890 let client = logged_in_client(Some(server.uri())).await;
891 let mut ruleset = client.account().push_rules().await?;
892 ruleset.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention, false)?;
894
895 let settings = NotificationSettings::new(client, ruleset);
896
897 Mock::given(method("PUT")).respond_with(ResponseTemplate::new(500)).mount(&server).await;
899
900 assert_eq!(
902 settings
903 .set_push_rule_enabled(
904 RuleKind::Override,
905 PredefinedOverrideRuleId::IsUserMention,
906 true,
907 )
908 .await,
909 Err(NotificationSettingsError::UnableToUpdatePushRule)
910 );
911
912 let rules = settings.rules.read().await;
914 let rule =
915 rules.ruleset.get(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention).unwrap();
916 assert!(!rule.enabled());
917
918 Ok(())
919 }
920
921 #[async_test]
922 async fn test_set_room_notification_mode() -> TestResult {
923 let server = MockServer::start().await;
924 let client = logged_in_client(Some(server.uri())).await;
925
926 Mock::given(method("PUT")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
927 Mock::given(method("DELETE")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
928
929 let settings = client.notification_settings().await;
930 let room_id = get_test_room_id();
931
932 let mode = settings.get_user_defined_room_notification_mode(&room_id).await;
933 assert!(mode.is_none());
934
935 let new_modes = [
936 RoomNotificationMode::AllMessages,
937 RoomNotificationMode::MentionsAndKeywordsOnly,
938 RoomNotificationMode::Mute,
939 ];
940 for new_mode in new_modes {
941 settings.set_room_notification_mode(&room_id, new_mode).await?;
942 assert_eq!(
943 new_mode,
944 settings.get_user_defined_room_notification_mode(&room_id).await.unwrap()
945 );
946 }
947
948 Ok(())
949 }
950
951 #[async_test]
952 async fn test_set_room_notification_mode_requests_order() -> TestResult {
953 let server = MockServer::start().await;
954 let client = logged_in_client(Some(server.uri())).await;
955
956 let put_was_called = Arc::new(AtomicBool::default());
957
958 Mock::given(method("PUT"))
959 .and(path_regex(r"_matrix/client/r0/pushrules/global/override/.*"))
960 .and({
961 let put_was_called = put_was_called.clone();
962 move |_: &wiremock::Request| {
963 put_was_called.store(true, Ordering::SeqCst);
964
965 true
966 }
967 })
968 .respond_with(ResponseTemplate::new(200))
969 .expect(1)
970 .mount(&server)
971 .await;
972
973 Mock::given(method("DELETE"))
974 .and(path_regex(r"_matrix/client/r0/pushrules/global/room/.*"))
975 .and(move |_: &wiremock::Request| {
976 let put_was_called = put_was_called.load(Ordering::SeqCst);
983 assert!(
984 put_was_called,
985 "The PUT /pushrules/global/override/ method should have been called before the \
986 DELETE method"
987 );
988
989 true
990 })
991 .respond_with(ResponseTemplate::new(200))
992 .expect(1)
993 .mount(&server)
994 .await;
995
996 let room_id = get_test_room_id();
997
998 let settings = from_insert_rules(&client, vec![(RuleKind::Room, &room_id, true)]);
1001
1002 settings.set_room_notification_mode(&room_id, RoomNotificationMode::Mute).await?;
1005
1006 assert_eq!(
1007 RoomNotificationMode::Mute,
1008 settings.get_user_defined_room_notification_mode(&room_id).await.unwrap()
1009 );
1010
1011 server.verify().await;
1012 Ok(())
1013 }
1014
1015 #[async_test]
1016 async fn test_set_room_notification_mode_put_api_error() {
1017 let server = MockServer::start().await;
1018 let client = logged_in_client(Some(server.uri())).await;
1019
1020 Mock::given(method("PUT")).respond_with(ResponseTemplate::new(500)).mount(&server).await;
1022 Mock::given(method("DELETE")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
1023
1024 let room_id = get_test_room_id();
1025
1026 let settings = from_insert_rules(&client, vec![(RuleKind::Room, &room_id, true)]);
1029
1030 assert_eq!(
1031 settings.get_user_defined_room_notification_mode(&room_id).await.unwrap(),
1032 RoomNotificationMode::AllMessages
1033 );
1034
1035 assert_eq!(
1037 settings.set_room_notification_mode(&room_id, RoomNotificationMode::Mute).await,
1038 Err(NotificationSettingsError::UnableToAddPushRule)
1039 );
1040
1041 assert_eq!(
1043 settings.get_user_defined_room_notification_mode(&room_id).await.unwrap(),
1044 RoomNotificationMode::AllMessages
1045 );
1046 }
1047
1048 #[async_test]
1049 async fn test_set_room_notification_mode_delete_api_error() {
1050 let server = MockServer::start().await;
1051 let client = logged_in_client(Some(server.uri())).await;
1052
1053 Mock::given(method("PUT")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
1055 Mock::given(method("DELETE")).respond_with(ResponseTemplate::new(500)).mount(&server).await;
1056
1057 let room_id = get_test_room_id();
1058
1059 let settings = from_insert_rules(&client, vec![(RuleKind::Room, &room_id, true)]);
1062
1063 assert_eq!(
1064 settings.get_user_defined_room_notification_mode(&room_id).await.unwrap(),
1065 RoomNotificationMode::AllMessages
1066 );
1067
1068 assert_eq!(
1070 settings.set_room_notification_mode(&room_id, RoomNotificationMode::Mute).await,
1071 Err(NotificationSettingsError::UnableToRemovePushRule)
1072 );
1073
1074 assert_eq!(
1076 settings.get_user_defined_room_notification_mode(&room_id).await.unwrap(),
1077 RoomNotificationMode::AllMessages
1078 );
1079 }
1080
1081 #[async_test]
1082 async fn test_delete_user_defined_room_rules() -> TestResult {
1083 let server = MockServer::start().await;
1084 let client = logged_in_client(Some(server.uri())).await;
1085 let room_id_a = owned_room_id!("!AAAaAAAAAaaAAaaaaa:matrix.org");
1086 let room_id_b = owned_room_id!("!BBBbBBBBBbbBBbbbbb:matrix.org");
1087
1088 Mock::given(method("DELETE")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
1089
1090 let settings = from_insert_rules(
1092 &client,
1093 vec![
1094 (RuleKind::Room, &room_id_a, true),
1095 (RuleKind::Room, &room_id_b, true),
1096 (RuleKind::Override, &room_id_b, true),
1097 ],
1098 );
1099
1100 settings.delete_user_defined_room_rules(&room_id_a).await?;
1102
1103 let updated_rules = settings.rules.read().await;
1105 assert_eq!(updated_rules.get_custom_rules_for_room(&room_id_b).len(), 2);
1106 assert!(updated_rules.get_custom_rules_for_room(&room_id_a).is_empty());
1107 Ok(())
1108 }
1109
1110 #[async_test]
1111 async fn test_unmute_room_not_muted() -> TestResult {
1112 let server = MockServer::start().await;
1113 let client = logged_in_client(Some(server.uri())).await;
1114 let room_id = get_test_room_id();
1115
1116 let settings = from_insert_rules(&client, vec![(RuleKind::Room, &room_id, false)]);
1118 assert_eq!(
1119 settings.get_user_defined_room_notification_mode(&room_id).await.unwrap(),
1120 RoomNotificationMode::MentionsAndKeywordsOnly
1121 );
1122
1123 settings.unmute_room(&room_id, IsEncrypted::Yes, IsOneToOne::Yes).await?;
1125
1126 assert_eq!(
1128 settings.get_user_defined_room_notification_mode(&room_id).await.unwrap(),
1129 RoomNotificationMode::MentionsAndKeywordsOnly
1130 );
1131
1132 let room_rules = get_custom_rules_for_room(&settings, &room_id).await;
1133 assert_eq!(room_rules.len(), 1);
1134 assert_matches!(settings.rules.read().await.ruleset.get(RuleKind::Room, &room_id),
1135 Some(AnyPushRuleRef::Room(rule)) => {
1136 assert_eq!(rule.rule_id, room_id);
1137 assert!(rule.actions.is_empty());
1138 }
1139 );
1140
1141 Ok(())
1142 }
1143
1144 #[async_test]
1145 async fn test_unmute_room() -> TestResult {
1146 let server = MockServer::start().await;
1147 Mock::given(method("PUT")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
1148 Mock::given(method("DELETE")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
1149 let client = logged_in_client(Some(server.uri())).await;
1150 let room_id = get_test_room_id();
1151
1152 let settings = from_insert_rules(&client, vec![(RuleKind::Override, &room_id, false)]);
1154 assert_eq!(
1155 settings.get_user_defined_room_notification_mode(&room_id).await,
1156 Some(RoomNotificationMode::Mute)
1157 );
1158
1159 settings.unmute_room(&room_id, IsEncrypted::No, IsOneToOne::Yes).await?;
1161
1162 assert!(settings.get_user_defined_room_notification_mode(&room_id).await.is_none());
1164
1165 Ok(())
1166 }
1167
1168 #[async_test]
1169 async fn test_unmute_room_default_mode() -> TestResult {
1170 let server = MockServer::start().await;
1171 Mock::given(method("PUT")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
1172 let client = logged_in_client(Some(server.uri())).await;
1173 let room_id = get_test_room_id();
1174 let settings = client.notification_settings().await;
1175
1176 settings.unmute_room(&room_id, IsEncrypted::No, IsOneToOne::Yes).await?;
1178
1179 assert_eq!(
1181 Some(RoomNotificationMode::AllMessages),
1182 settings.get_user_defined_room_notification_mode(&room_id).await
1183 );
1184
1185 let room_rules = get_custom_rules_for_room(&settings, &room_id).await;
1186 assert_eq!(room_rules.len(), 1);
1187 assert_matches!(settings.rules.read().await.ruleset.get(RuleKind::Room, &room_id),
1188 Some(AnyPushRuleRef::Room(rule)) => {
1189 assert_eq!(rule.rule_id, room_id);
1190 assert!(!rule.actions.is_empty());
1191 }
1192 );
1193
1194 Ok(())
1195 }
1196
1197 #[async_test]
1198 async fn test_set_default_room_notification_mode() -> TestResult {
1199 let server = MockServer::start().await;
1200 Mock::given(method("PUT")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
1201 let client = logged_in_client(Some(server.uri())).await;
1202
1203 let mut ruleset = get_server_default_ruleset();
1205 ruleset.set_actions(
1206 RuleKind::Underride,
1207 PredefinedUnderrideRuleId::Message,
1208 vec![Action::Notify],
1209 )?;
1210
1211 ruleset.set_actions(
1212 RuleKind::Underride,
1213 PredefinedUnderrideRuleId::PollStart,
1214 vec![Action::Notify],
1215 )?;
1216
1217 let settings = NotificationSettings::new(client, ruleset);
1218 assert_eq!(
1219 settings.get_default_room_notification_mode(IsEncrypted::No, IsOneToOne::No).await,
1220 RoomNotificationMode::AllMessages
1221 );
1222
1223 settings
1225 .set_default_room_notification_mode(
1226 IsEncrypted::No,
1227 IsOneToOne::No,
1228 RoomNotificationMode::MentionsAndKeywordsOnly,
1229 )
1230 .await?;
1231
1232 assert_matches!(settings.rules.read().await.ruleset.get(RuleKind::Underride, PredefinedUnderrideRuleId::Message),
1234 Some(AnyPushRuleRef::Underride(rule)) => {
1235 assert!(rule.actions.is_empty());
1236 }
1237 );
1238
1239 assert_matches!(settings.rules.read().await.ruleset.get(RuleKind::Underride, PredefinedUnderrideRuleId::PollStart),
1240 Some(AnyPushRuleRef::Underride(rule)) => {
1241 assert!(rule.actions.is_empty());
1242 }
1243 );
1244
1245 assert_matches!(
1248 settings.get_default_room_notification_mode(IsEncrypted::No, IsOneToOne::No).await,
1249 RoomNotificationMode::MentionsAndKeywordsOnly
1250 );
1251
1252 Ok(())
1253 }
1254
1255 #[async_test]
1256 async fn test_set_default_room_notification_mode_one_to_one() -> TestResult {
1257 let server = MockServer::start().await;
1258 Mock::given(method("PUT")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
1259 let client = logged_in_client(Some(server.uri())).await;
1260
1261 let mut ruleset = get_server_default_ruleset();
1263 ruleset.set_actions(
1264 RuleKind::Underride,
1265 PredefinedUnderrideRuleId::RoomOneToOne,
1266 vec![Action::Notify],
1267 )?;
1268
1269 ruleset.set_actions(
1270 RuleKind::Underride,
1271 PredefinedUnderrideRuleId::PollStartOneToOne,
1272 vec![Action::Notify],
1273 )?;
1274
1275 let settings = NotificationSettings::new(client, ruleset);
1276 assert_eq!(
1277 settings.get_default_room_notification_mode(IsEncrypted::No, IsOneToOne::Yes).await,
1278 RoomNotificationMode::AllMessages
1279 );
1280
1281 settings
1283 .set_default_room_notification_mode(
1284 IsEncrypted::No,
1285 IsOneToOne::Yes,
1286 RoomNotificationMode::MentionsAndKeywordsOnly,
1287 )
1288 .await?;
1289
1290 assert_matches!(settings.rules.read().await.ruleset.get(RuleKind::Underride, PredefinedUnderrideRuleId::RoomOneToOne),
1292 Some(AnyPushRuleRef::Underride(rule)) => {
1293 assert!(rule.actions.is_empty());
1294 }
1295 );
1296
1297 assert_matches!(settings.rules.read().await.ruleset.get(RuleKind::Underride, PredefinedUnderrideRuleId::PollStartOneToOne),
1298 Some(AnyPushRuleRef::Underride(rule)) => {
1299 assert!(rule.actions.is_empty());
1300 }
1301 );
1302
1303 assert_matches!(
1306 settings.get_default_room_notification_mode(IsEncrypted::No, IsOneToOne::Yes).await,
1307 RoomNotificationMode::MentionsAndKeywordsOnly
1308 );
1309
1310 Ok(())
1311 }
1312
1313 #[async_test]
1314 async fn test_set_default_room_notification_mode_enables_rules() -> TestResult {
1315 let server = MockServer::start().await;
1316 Mock::given(method("PUT")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
1317 let client = logged_in_client(Some(server.uri())).await;
1318
1319 let mut ruleset = get_server_default_ruleset();
1321 ruleset.set_actions(
1322 RuleKind::Underride,
1323 PredefinedUnderrideRuleId::RoomOneToOne,
1324 vec![],
1325 )?;
1326
1327 ruleset.set_actions(
1328 RuleKind::Underride,
1329 PredefinedUnderrideRuleId::PollStartOneToOne,
1330 vec![],
1331 )?;
1332
1333 ruleset.set_enabled(RuleKind::Underride, PredefinedUnderrideRuleId::RoomOneToOne, false)?;
1335
1336 let settings = NotificationSettings::new(client, ruleset);
1337
1338 settings
1340 .set_default_room_notification_mode(
1341 IsEncrypted::No,
1342 IsOneToOne::Yes,
1343 RoomNotificationMode::AllMessages,
1344 )
1345 .await?;
1346
1347 assert_matches!(
1350 settings.get_default_room_notification_mode(IsEncrypted::No, IsOneToOne::Yes).await,
1351 RoomNotificationMode::AllMessages
1352 );
1353
1354 Ok(())
1355 }
1356
1357 #[async_test]
1358 async fn test_list_keywords() -> TestResult {
1359 let server = MockServer::start().await;
1360 let client = logged_in_client(Some(server.uri())).await;
1361
1362 let ruleset = get_server_default_ruleset();
1364 let settings = NotificationSettings::new(client.clone(), ruleset);
1365
1366 let keywords = settings.enabled_keywords().await;
1367
1368 assert!(keywords.is_empty());
1369
1370 let mut ruleset = get_server_default_ruleset();
1372 ruleset.insert(
1373 NewPushRule::Content(NewPatternedPushRule::new("a".to_owned(), "a".to_owned(), vec![])),
1374 None,
1375 None,
1376 )?;
1377 ruleset.insert(
1379 NewPushRule::Content(NewPatternedPushRule::new(
1380 "a_bis".to_owned(),
1381 "a".to_owned(),
1382 vec![],
1383 )),
1384 None,
1385 None,
1386 )?;
1387 ruleset.insert(
1388 NewPushRule::Content(NewPatternedPushRule::new("b".to_owned(), "b".to_owned(), vec![])),
1389 None,
1390 None,
1391 )?;
1392
1393 let settings = NotificationSettings::new(client, ruleset);
1394
1395 let keywords = settings.enabled_keywords().await;
1396 assert_eq!(keywords.len(), 2);
1397 assert!(keywords.get("a").is_some());
1398 assert!(keywords.get("b").is_some());
1399
1400 Ok(())
1401 }
1402
1403 #[async_test]
1404 async fn test_add_keyword_missing() -> TestResult {
1405 let server = MockServer::start().await;
1406 let client = logged_in_client(Some(server.uri())).await;
1407 let settings = client.notification_settings().await;
1408
1409 Mock::given(method("PUT"))
1410 .and(path("/_matrix/client/r0/pushrules/global/content/banana"))
1411 .respond_with(ResponseTemplate::new(200))
1412 .expect(1)
1413 .mount(&server)
1414 .await;
1415
1416 settings.add_keyword("banana".to_owned()).await?;
1417
1418 let keywords = settings.enabled_keywords().await;
1420 assert_eq!(keywords.len(), 1);
1421 assert!(keywords.get("banana").is_some());
1422
1423 let rule_enabled = settings.is_push_rule_enabled(RuleKind::Content, "banana").await?;
1425 assert!(rule_enabled);
1426
1427 Ok(())
1428 }
1429
1430 #[async_test]
1431 async fn test_add_keyword_disabled() -> TestResult {
1432 let server = MockServer::start().await;
1433 let client = logged_in_client(Some(server.uri())).await;
1434
1435 let mut ruleset = get_server_default_ruleset();
1436 ruleset.insert(
1437 NewPushRule::Content(NewPatternedPushRule::new(
1438 "banana_two".to_owned(),
1439 "banana".to_owned(),
1440 vec![],
1441 )),
1442 None,
1443 None,
1444 )?;
1445 ruleset.set_enabled(RuleKind::Content, "banana_two", false)?;
1446 ruleset.insert(
1447 NewPushRule::Content(NewPatternedPushRule::new(
1448 "banana_one".to_owned(),
1449 "banana".to_owned(),
1450 vec![],
1451 )),
1452 None,
1453 None,
1454 )?;
1455 ruleset.set_enabled(RuleKind::Content, "banana_one", false)?;
1456
1457 let settings = NotificationSettings::new(client, ruleset);
1458 Mock::given(method("PUT"))
1459 .and(path("/_matrix/client/r0/pushrules/global/content/banana_one/enabled"))
1460 .respond_with(ResponseTemplate::new(200))
1461 .expect(1)
1462 .mount(&server)
1463 .await;
1464
1465 settings.add_keyword("banana".to_owned()).await?;
1466
1467 let keywords = settings.enabled_keywords().await;
1469
1470 assert_eq!(keywords.len(), 1);
1471 assert!(keywords.get("banana").is_some());
1472
1473 let first_rule_enabled =
1475 settings.is_push_rule_enabled(RuleKind::Content, "banana_one").await?;
1476 assert!(first_rule_enabled);
1477 let second_rule_enabled =
1478 settings.is_push_rule_enabled(RuleKind::Content, "banana_two").await?;
1479 assert!(!second_rule_enabled);
1480
1481 Ok(())
1482 }
1483
1484 #[async_test]
1485 async fn test_add_keyword_noop() -> TestResult {
1486 let server = MockServer::start().await;
1487 let client = logged_in_client(Some(server.uri())).await;
1488
1489 let mut ruleset = get_server_default_ruleset();
1490 ruleset.insert(
1491 NewPushRule::Content(NewPatternedPushRule::new(
1492 "banana_two".to_owned(),
1493 "banana".to_owned(),
1494 vec![],
1495 )),
1496 None,
1497 None,
1498 )?;
1499 ruleset.insert(
1500 NewPushRule::Content(NewPatternedPushRule::new(
1501 "banana_one".to_owned(),
1502 "banana".to_owned(),
1503 vec![],
1504 )),
1505 None,
1506 None,
1507 )?;
1508 ruleset.set_enabled(RuleKind::Content, "banana_one", false)?;
1509
1510 let settings = NotificationSettings::new(client, ruleset);
1511 settings.add_keyword("banana".to_owned()).await?;
1512
1513 let keywords = settings.enabled_keywords().await;
1515
1516 assert_eq!(keywords.len(), 1);
1517 assert!(keywords.get("banana").is_some());
1518
1519 let first_rule_enabled =
1520 settings.is_push_rule_enabled(RuleKind::Content, "banana_one").await?;
1521 assert!(!first_rule_enabled);
1522 let second_rule_enabled =
1523 settings.is_push_rule_enabled(RuleKind::Content, "banana_two").await?;
1524 assert!(second_rule_enabled);
1525
1526 Ok(())
1527 }
1528
1529 #[async_test]
1530 async fn test_remove_keyword_all() -> TestResult {
1531 let server = MockServer::start().await;
1532 let client = logged_in_client(Some(server.uri())).await;
1533
1534 let mut ruleset = get_server_default_ruleset();
1535 ruleset.insert(
1536 NewPushRule::Content(NewPatternedPushRule::new(
1537 "banana_two".to_owned(),
1538 "banana".to_owned(),
1539 vec![],
1540 )),
1541 None,
1542 None,
1543 )?;
1544 ruleset.insert(
1545 NewPushRule::Content(NewPatternedPushRule::new(
1546 "banana_one".to_owned(),
1547 "banana".to_owned(),
1548 vec![],
1549 )),
1550 None,
1551 None,
1552 )?;
1553 ruleset.set_enabled(RuleKind::Content, "banana_one", false)?;
1554
1555 let settings = NotificationSettings::new(client, ruleset);
1556
1557 Mock::given(method("DELETE"))
1558 .and(path("/_matrix/client/r0/pushrules/global/content/banana_one"))
1559 .respond_with(ResponseTemplate::new(200))
1560 .expect(1)
1561 .mount(&server)
1562 .await;
1563 Mock::given(method("DELETE"))
1564 .and(path("/_matrix/client/r0/pushrules/global/content/banana_two"))
1565 .respond_with(ResponseTemplate::new(200))
1566 .expect(1)
1567 .mount(&server)
1568 .await;
1569
1570 settings.remove_keyword("banana").await?;
1571
1572 let keywords = settings.enabled_keywords().await;
1574 assert!(keywords.is_empty());
1575
1576 let first_rule_error =
1578 settings.is_push_rule_enabled(RuleKind::Content, "banana_one").await.unwrap_err();
1579 assert_matches!(first_rule_error, NotificationSettingsError::RuleNotFound(_));
1580 let second_rule_error =
1581 settings.is_push_rule_enabled(RuleKind::Content, "banana_two").await.unwrap_err();
1582 assert_matches!(second_rule_error, NotificationSettingsError::RuleNotFound(_));
1583
1584 Ok(())
1585 }
1586
1587 #[async_test]
1588 async fn test_remove_keyword_noop() -> TestResult {
1589 let server = MockServer::start().await;
1590 let client = logged_in_client(Some(server.uri())).await;
1591 let settings = client.notification_settings().await;
1592
1593 settings.remove_keyword("banana").await?;
1594 Ok(())
1595 }
1596
1597 #[async_test]
1598 async fn test_set_default_room_notification_mode_missing_poll_start() -> TestResult {
1599 let server = MockServer::start().await;
1600 Mock::given(method("PUT")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
1601 let client = logged_in_client(Some(server.uri())).await;
1602
1603 let mut ruleset = get_server_default_ruleset();
1605 ruleset.underride.swap_remove(PredefinedUnderrideRuleId::PollStart.as_str());
1606
1607 let settings = NotificationSettings::new(client, ruleset);
1608 assert_eq!(
1609 settings.get_default_room_notification_mode(IsEncrypted::No, IsOneToOne::No).await,
1610 RoomNotificationMode::AllMessages
1611 );
1612
1613 settings
1615 .set_default_room_notification_mode(
1616 IsEncrypted::No,
1617 IsOneToOne::No,
1618 RoomNotificationMode::MentionsAndKeywordsOnly,
1619 )
1620 .await?;
1621
1622 assert_matches!(
1625 settings.get_default_room_notification_mode(IsEncrypted::No, IsOneToOne::No).await,
1626 RoomNotificationMode::MentionsAndKeywordsOnly
1627 );
1628 Ok(())
1629 }
1630
1631 #[async_test]
1632 async fn test_create_custom_conditional_push_rule() -> TestResult {
1633 let server = MockServer::start().await;
1634 let client = logged_in_client(Some(server.uri())).await;
1635 let settings = client.notification_settings().await;
1636
1637 Mock::given(method("PUT"))
1638 .and(path("/_matrix/client/r0/pushrules/global/override/custom_rule"))
1639 .respond_with(ResponseTemplate::new(200))
1640 .expect(1)
1641 .mount(&server)
1642 .await;
1643
1644 let actions = vec![Action::Notify];
1645 let conditions = vec![PushCondition::EventMatch(EventMatchConditionData::new(
1646 "content.body".to_owned(),
1647 "hello".to_owned(),
1648 ))];
1649
1650 settings
1651 .create_custom_conditional_push_rule(
1652 "custom_rule".to_owned(),
1653 RuleKind::Override,
1654 actions.clone(),
1655 conditions.clone(),
1656 )
1657 .await?;
1658
1659 let rules = settings.rules.read().await;
1660 let rule = rules.ruleset.get(RuleKind::Override, "custom_rule").unwrap();
1661
1662 assert_eq!(rule.rule_id(), "custom_rule");
1663 assert!(rule.enabled());
1664
1665 Ok(())
1666 }
1667
1668 #[async_test]
1669 async fn test_create_custom_conditional_push_rule_invalid_kind() {
1670 let server = MockServer::start().await;
1671 let client = logged_in_client(Some(server.uri())).await;
1672 let settings = client.notification_settings().await;
1673
1674 let actions = vec![Action::Notify];
1675 let conditions = vec![PushCondition::EventMatch(EventMatchConditionData::new(
1676 "content.body".to_owned(),
1677 "hello".to_owned(),
1678 ))];
1679
1680 let result = settings
1681 .create_custom_conditional_push_rule(
1682 "custom_rule".to_owned(),
1683 RuleKind::Room,
1684 actions,
1685 conditions,
1686 )
1687 .await;
1688
1689 assert_matches!(result, Err(NotificationSettingsError::InvalidParameter(_)));
1690 }
1691
1692 #[async_test]
1693 #[allow(deprecated)]
1694 async fn test_enable_mention_ignore_missing_legacy_push_rules() -> TestResult {
1695 let server = MatrixMockServer::new().await;
1696 let client = server.client_builder().build().await;
1697 let mut ruleset = get_server_default_ruleset();
1698
1699 if let Some(idx) = ruleset
1701 .override_
1702 .iter()
1703 .position(|rule| rule.rule_id == PredefinedOverrideRuleId::ContainsDisplayName.as_ref())
1704 {
1705 ruleset.override_.shift_remove_index(idx);
1706 }
1707
1708 if let Some(idx) = ruleset
1709 .override_
1710 .iter()
1711 .position(|rule| rule.rule_id == PredefinedOverrideRuleId::RoomNotif.as_ref())
1712 {
1713 ruleset.override_.shift_remove_index(idx);
1714 }
1715
1716 if let Some(idx) = ruleset
1717 .content
1718 .iter()
1719 .position(|rule| rule.rule_id == PredefinedContentRuleId::ContainsUserName.as_ref())
1720 {
1721 ruleset.content.shift_remove_index(idx);
1722 }
1723
1724 assert_matches!(
1725 ruleset.iter().find(|rule| {
1726 rule.rule_id() == PredefinedOverrideRuleId::ContainsDisplayName.as_ref()
1727 || rule.rule_id() == PredefinedOverrideRuleId::RoomNotif.as_ref()
1728 || rule.rule_id() == PredefinedContentRuleId::ContainsUserName.as_ref()
1729 }),
1730 None,
1731 "ruleset must not have legacy mention push rules"
1732 );
1733
1734 let settings = NotificationSettings::new(client, ruleset);
1735
1736 server
1737 .mock_enable_push_rule(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention)
1738 .ok()
1739 .mock_once()
1740 .named("is_user_mention")
1741 .mount()
1742 .await;
1743 settings
1744 .set_push_rule_enabled(
1745 RuleKind::Override,
1746 PredefinedOverrideRuleId::IsUserMention,
1747 false,
1748 )
1749 .await?;
1750
1751 server
1752 .mock_enable_push_rule(RuleKind::Override, PredefinedOverrideRuleId::IsRoomMention)
1753 .ok()
1754 .mock_once()
1755 .named("is_room_mention")
1756 .mount()
1757 .await;
1758 settings
1759 .set_push_rule_enabled(
1760 RuleKind::Override,
1761 PredefinedOverrideRuleId::IsRoomMention,
1762 false,
1763 )
1764 .await?;
1765
1766 Ok(())
1767 }
1768}