Commit e9af9e0e0f31
tarball2 files changed+121-51
@@ -4,14 +4,15 @@ use futures_util::FutureExt;
44
use matrix_sdk::encryption::verification::VerificationRequest;55
use matrix_sdk::{66
Client,7
- notification_settings::{8
- IsEncrypted, IsOneToOne, NotificationSettings, RoomNotificationMode as SdkMode,9
- },7
+ notification_settings::{IsEncrypted, IsOneToOne, RoomNotificationMode as SdkMode},108
ruma::{119
OwnedDeviceId, OwnedEventId, OwnedMxcUri, RoomId, TransactionId,12
- api::client::{push::set_pushrule_actions, receipt::create_receipt::v3::ReceiptType},10
+ api::client::{11
+ push::{get_pushrule, set_pushrule_actions},12
+ receipt::create_receipt::v3::ReceiptType,13
+ },1314
events::receipt::ReceiptThread,14
- push::{Action, HighlightTweakValue, RuleKind, SoundTweakValue, Tweak},15
+ push::{Action, HighlightTweakValue, RuleKind, Tweak},1516
},1617
send_queue::{LocalEchoContent, SendHandle},1718
};@@ -140,7 +141,7 @@ async fn notification(client: &Client, command: NotificationCommand) -> Result<(
140141
}141142
NotificationCommand::AddKeyword(keyword) => {142143
match settings.add_keyword(keyword.clone()).await {143
- Ok(()) => return highlight_keyword(client, &settings, &keyword).await,144
+ Ok(()) => return highlight_keyword(client, &keyword).await,144145
Err(error) => Err(error),145146
}146147
}@@ -156,53 +157,46 @@ async fn notification(client: &Client, command: NotificationCommand) -> Result<(
156157
}157158
158159
/// sdk keyword rules carry only notify and a sound, while Element's loud keywords also159
-/// highlight, which is what lets the inbox keep them as mentions160
-async fn highlight_keyword(161
- client: &Client,162
- settings: &NotificationSettings,163
- keyword: &str,164
-) -> Result<(), Error> {165
- let ruleset = settings.ruleset().await;166
- for rule in ruleset167
- .content168
- .iter()169
- .filter(|rule| !rule.default && rule.pattern == keyword)170
- .filter(|rule| !rule.actions.iter().any(Action::is_highlight))160
+/// highlight, which is what lets the inbox keep them as mentions. The sdk names a161
+/// keyword's content rule after the keyword itself, a rule under another id is left162
+/// alone.163
+async fn highlight_keyword(client: &Client, keyword: &str) -> Result<(), Error> {164
+ let failed = |source| Error::from_http("highlighting a notification keyword failed", source);165
+ let rule = match client166
+ .send(get_pushrule::v3::Request::new(167
+ RuleKind::Content,168
+ keyword.to_owned(),169
+ ))170
+ .await171171
{172
- client173
- .send(set_pushrule_actions::v3::Request::new(174
- RuleKind::Content,175
- rule.rule_id.clone(),176
- keyword_actions(&rule.actions),177
- ))178
- .await179
- .map_err(|source| {180
- Error::from_sdk("highlighting a notification keyword failed", source.into())181
- })?;172
+ Ok(response) => response.rule,173
+ Err(error)174
+ if error175
+ .as_client_api_error()176
+ .is_some_and(|error| error.status_code.as_u16() == 404) =>177
+ {178
+ return Ok(());179
+ }180
+ Err(error) => return Err(failed(error)),181
+ };182
+ if rule.actions.iter().any(Action::is_highlight) {183
+ return Ok(());182184
}183
- Ok(())184
-}185
-186
-fn keyword_actions(existing: &[Action]) -> Vec<Action> {187
- let mut actions: Vec<_> = existing188
- .iter()185
+ let mut actions: Vec<_> = rule186
+ .actions187
+ .into_iter()189188
.filter(|action| !matches!(action, Action::SetTweak(Tweak::Highlight(_))))190
- .cloned()191189
.collect();192
- if !actions193
- .iter()194
- .any(|action| matches!(action, Action::Notify))195
- {196
- actions.insert(0, Action::Notify);197
- }198
- if !actions199
- .iter()200
- .any(|action| matches!(action, Action::SetTweak(Tweak::Sound(_))))201
- {202
- actions.push(Action::SetTweak(Tweak::Sound(SoundTweakValue::Default)));203
- }204190
actions.push(Action::SetTweak(Tweak::Highlight(HighlightTweakValue::Yes)));205
- actions191
+ client192
+ .send(set_pushrule_actions::v3::Request::new(193
+ RuleKind::Content,194
+ keyword.to_owned(),195
+ actions,196
+ ))197
+ .await198
+ .map_err(failed)?;199
+ Ok(())206200
}207201
208202
const fn sdk_mode(mode: RoomNotificationMode) -> SdkMode {@@ -28,7 +28,7 @@ use support::receive_until;
2828
use support::shut_down;2929
use wiremock::{3030
Mock, Request, Respond, ResponseTemplate,31
- matchers::{body_string_contains, method, path, path_regex, query_param},31
+ matchers::{method, path, path_regex, query_param},3232
};3333
3434
#[cfg(feature = "serde")]@@ -864,6 +864,26 @@ async fn highlighting_keywords_are_mentions_and_quiet_ones_are_not() {
864864
shut_down(&handle, &mut events, running).await;865865
}866866
867
+fn actions_are(expected: Value) -> impl Fn(&Request) -> bool + Send + Sync + 'static {868
+ move |request: &Request| {869
+ serde_json::from_slice::<Value>(&request.body).is_ok_and(|body| body["actions"] == expected)870
+ }871
+}872
+873
+async fn mount_keyword_rule(server: &MatrixMockServer, actions: Value) {874
+ Mock::given(method("GET"))875
+ .and(path("/_matrix/client/v3/pushrules/global/content/deploy"))876
+ .respond_with(ResponseTemplate::new(200).set_body_json(json!({877
+ "rule_id": "deploy",878
+ "pattern": "deploy",879
+ "default": false,880
+ "enabled": true,881
+ "actions": actions,882
+ })))883
+ .mount(server.server())884
+ .await;885
+}886
+867887
#[tokio::test]868888
async fn adding_a_keyword_gives_its_rule_a_highlight() {869889
let server = MatrixMockServer::new().await;@@ -875,12 +895,68 @@ async fn adding_a_keyword_gives_its_rule_a_highlight() {
875895
.expect(1)876896
.mount(server.server())877897
.await;898
+ mount_keyword_rule(899
+ &server,900
+ json!(["notify", {"set_tweak": "sound", "value": "default"}]),901
+ )902
+ .await;878903
Mock::given(method("PUT"))879904
.and(path(880905
"/_matrix/client/v3/pushrules/global/content/deploy/actions",881906
))882
- .and(body_string_contains("highlight"))883
- .and(body_string_contains("notify"))907
+ .and(actions_are(json!([908
+ "notify",909
+ {"set_tweak": "sound", "value": "default"},910
+ {"set_tweak": "highlight"},911
+ ])))912
+ .respond_with(ResponseTemplate::new(200).set_body_json(json!({})))913
+ .expect(1)914
+ .mount(server.server())915
+ .await;916
+ SlidingFeed::new(room_id.to_owned(), Vec::new())917
+ .mount(&server)918
+ .await;919
+920
+ let (engine, handle, mut events) = Engine::from_client(client, EngineConfig::default())921
+ .await922
+ .unwrap();923
+ let running = tokio::spawn(engine.run());924
+ handle.add_notification_keyword("deploy").await.unwrap();925
+ shut_down(&handle, &mut events, running).await;926
+ server.server().verify().await;927
+}928
+929
+#[tokio::test]930
+async fn a_quiet_keyword_rule_only_gains_the_highlight() {931
+ let server = MatrixMockServer::new().await;932
+ let client = server.client_builder().build().await;933
+ let own_user = client.user_id().unwrap().to_owned();934
+ let room_id = room_id!("!quiet-keyword-rule:localhost");935
+ let mut rules = Ruleset::server_default(&own_user);936
+ rules.content.insert(937
+ NewPatternedPushRule::new("deploy".to_owned(), "deploy".to_owned(), Vec::new()).into(),938
+ );939
+ server940
+ .mock_sync()941
+ .ok_and_run(&client, {942
+ let own_user = own_user.clone();943
+ move |builder| {944
+ let factory = EventFactory::new().room(room_id);945
+ builder.add_global_account_data(factory.push_rules(rules));946
+ builder.add_joined_room(947
+ JoinedRoomBuilder::new(room_id).add_state_event(factory.member(&own_user)),948
+ );949
+ }950
+ })951
+ .await;952
+ mount_keyword_rule(&server, json!([])).await;953
+ Mock::given(method("PUT"))954
+ .and(path(955
+ "/_matrix/client/v3/pushrules/global/content/deploy/actions",956
+ ))957
+ .and(actions_are(958
+ json!([{"set_tweak": "highlight"}]),959
+ ))884960
.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))885961
.expect(1)886962
.mount(server.server())