mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-04 05:58:11 -04:00
feat(notification): Add support for custom conditional push rules (#4587)
--- Signed-off-by: Jonas Richard Richter <jonas-richard.richter@telekom.de>
This commit is contained in:
committed by
GitHub
parent
1d9d4d3b3a
commit
dbdbfd0b38
@@ -63,6 +63,7 @@ Additions:
|
||||
- Add `Encryption::get_user_identity` which returns `UserIdentity`
|
||||
- Add `ClientBuilder::room_key_recipient_strategy`
|
||||
- Add `Room::send_raw`
|
||||
- Add `NotificationSettings::set_custom_push_rule`
|
||||
- Expose `withdraw_verification` to `UserIdentity`
|
||||
- Expose `report_room` to `Room`
|
||||
- Add `RoomInfo::encryption_state`
|
||||
|
||||
@@ -10,13 +10,350 @@ use matrix_sdk::{
|
||||
Client as MatrixClient,
|
||||
};
|
||||
use ruma::{
|
||||
push::{PredefinedOverrideRuleId, PredefinedUnderrideRuleId, RuleKind},
|
||||
RoomId,
|
||||
push::{
|
||||
Action as SdkAction, ComparisonOperator as SdkComparisonOperator, PredefinedOverrideRuleId,
|
||||
PredefinedUnderrideRuleId, PushCondition as SdkPushCondition, RoomMemberCountIs,
|
||||
RuleKind as SdkRuleKind, ScalarJsonValue as SdkJsonValue, Tweak as SdkTweak,
|
||||
},
|
||||
Int, RoomId, UInt,
|
||||
};
|
||||
use tokio::sync::RwLock as AsyncRwLock;
|
||||
|
||||
use crate::error::NotificationSettingsError;
|
||||
|
||||
#[derive(Clone, Default, uniffi::Enum)]
|
||||
pub enum ComparisonOperator {
|
||||
/// Equals
|
||||
#[default]
|
||||
Eq,
|
||||
|
||||
/// Less than
|
||||
Lt,
|
||||
|
||||
/// Greater than
|
||||
Gt,
|
||||
|
||||
/// Greater or equal
|
||||
Ge,
|
||||
|
||||
/// Less or equal
|
||||
Le,
|
||||
}
|
||||
|
||||
impl From<SdkComparisonOperator> for ComparisonOperator {
|
||||
fn from(value: SdkComparisonOperator) -> Self {
|
||||
match value {
|
||||
SdkComparisonOperator::Eq => Self::Eq,
|
||||
SdkComparisonOperator::Lt => Self::Lt,
|
||||
SdkComparisonOperator::Gt => Self::Gt,
|
||||
SdkComparisonOperator::Ge => Self::Ge,
|
||||
SdkComparisonOperator::Le => Self::Le,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ComparisonOperator> for SdkComparisonOperator {
|
||||
fn from(value: ComparisonOperator) -> Self {
|
||||
match value {
|
||||
ComparisonOperator::Eq => Self::Eq,
|
||||
ComparisonOperator::Lt => Self::Lt,
|
||||
ComparisonOperator::Gt => Self::Gt,
|
||||
ComparisonOperator::Ge => Self::Ge,
|
||||
ComparisonOperator::Le => Self::Le,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, uniffi::Enum)]
|
||||
pub enum JsonValue {
|
||||
/// Represents a `null` value.
|
||||
#[default]
|
||||
Null,
|
||||
|
||||
/// Represents a boolean.
|
||||
Bool { value: bool },
|
||||
|
||||
/// Represents an integer.
|
||||
Integer { value: i64 },
|
||||
|
||||
/// Represents a string.
|
||||
String { value: String },
|
||||
}
|
||||
|
||||
impl From<SdkJsonValue> for JsonValue {
|
||||
fn from(value: SdkJsonValue) -> Self {
|
||||
match value {
|
||||
SdkJsonValue::Null => Self::Null,
|
||||
SdkJsonValue::Bool(b) => Self::Bool { value: b },
|
||||
SdkJsonValue::Integer(i) => Self::Integer { value: i.into() },
|
||||
SdkJsonValue::String(s) => Self::String { value: s },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JsonValue> for SdkJsonValue {
|
||||
fn from(value: JsonValue) -> Self {
|
||||
match value {
|
||||
JsonValue::Null => Self::Null,
|
||||
JsonValue::Bool { value } => Self::Bool(value),
|
||||
JsonValue::Integer { value } => Self::Integer(Int::new(value).unwrap_or_default()),
|
||||
JsonValue::String { value } => Self::String(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, uniffi::Enum)]
|
||||
pub enum PushCondition {
|
||||
/// A glob pattern match on a field of the event.
|
||||
EventMatch {
|
||||
/// The [dot-separated path] of the property of the event to match.
|
||||
///
|
||||
/// [dot-separated path]: https://spec.matrix.org/latest/appendices/#dot-separated-property-paths
|
||||
key: String,
|
||||
|
||||
/// The glob-style pattern to match against.
|
||||
///
|
||||
/// Patterns with no special glob characters should be treated as having
|
||||
/// asterisks prepended and appended when testing the condition.
|
||||
pattern: String,
|
||||
},
|
||||
|
||||
/// Matches unencrypted messages where `content.body` contains the owner's
|
||||
/// display name in that room.
|
||||
ContainsDisplayName,
|
||||
|
||||
/// Matches the current number of members in the room.
|
||||
RoomMemberCount { prefix: ComparisonOperator, count: u64 },
|
||||
|
||||
/// Takes into account the current power levels in the room, ensuring the
|
||||
/// sender of the event has high enough power to trigger the
|
||||
/// notification.
|
||||
SenderNotificationPermission {
|
||||
/// The field in the power level event the user needs a minimum power
|
||||
/// level for.
|
||||
///
|
||||
/// Fields must be specified under the `notifications` property in the
|
||||
/// power level event's `content`.
|
||||
key: String,
|
||||
},
|
||||
|
||||
/// Exact value match on a property of the event.
|
||||
EventPropertyIs {
|
||||
/// The [dot-separated path] of the property of the event to match.
|
||||
///
|
||||
/// [dot-separated path]: https://spec.matrix.org/latest/appendices/#dot-separated-property-paths
|
||||
key: String,
|
||||
|
||||
/// The value to match against.
|
||||
value: JsonValue,
|
||||
},
|
||||
|
||||
/// Exact value match on a value in an array property of the event.
|
||||
EventPropertyContains {
|
||||
/// The [dot-separated path] of the property of the event to match.
|
||||
///
|
||||
/// [dot-separated path]: https://spec.matrix.org/latest/appendices/#dot-separated-property-paths
|
||||
key: String,
|
||||
|
||||
/// The value to match against.
|
||||
value: JsonValue,
|
||||
},
|
||||
}
|
||||
|
||||
impl TryFrom<SdkPushCondition> for PushCondition {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: SdkPushCondition) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
SdkPushCondition::EventMatch { key, pattern } => Self::EventMatch { key, pattern },
|
||||
SdkPushCondition::ContainsDisplayName => Self::ContainsDisplayName,
|
||||
SdkPushCondition::RoomMemberCount { is } => {
|
||||
Self::RoomMemberCount { prefix: is.prefix.into(), count: is.count.into() }
|
||||
}
|
||||
SdkPushCondition::SenderNotificationPermission { key } => {
|
||||
Self::SenderNotificationPermission { key }
|
||||
}
|
||||
SdkPushCondition::EventPropertyIs { key, value } => {
|
||||
Self::EventPropertyIs { key, value: value.into() }
|
||||
}
|
||||
SdkPushCondition::EventPropertyContains { key, value } => {
|
||||
Self::EventPropertyContains { key, value: value.into() }
|
||||
}
|
||||
_ => return Err(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PushCondition> for SdkPushCondition {
|
||||
fn from(value: PushCondition) -> Self {
|
||||
match value {
|
||||
PushCondition::EventMatch { key, pattern } => Self::EventMatch { key, pattern },
|
||||
PushCondition::ContainsDisplayName => Self::ContainsDisplayName,
|
||||
PushCondition::RoomMemberCount { prefix, count } => Self::RoomMemberCount {
|
||||
is: RoomMemberCountIs {
|
||||
prefix: prefix.into(),
|
||||
count: UInt::new(count).unwrap_or_default(),
|
||||
},
|
||||
},
|
||||
PushCondition::SenderNotificationPermission { key } => {
|
||||
Self::SenderNotificationPermission { key }
|
||||
}
|
||||
PushCondition::EventPropertyIs { key, value } => {
|
||||
Self::EventPropertyIs { key, value: value.into() }
|
||||
}
|
||||
PushCondition::EventPropertyContains { key, value } => {
|
||||
Self::EventPropertyContains { key, value: value.into() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, uniffi::Enum)]
|
||||
pub enum RuleKind {
|
||||
/// User-configured rules that override all other kinds.
|
||||
Override,
|
||||
|
||||
/// Lowest priority user-defined rules.
|
||||
Underride,
|
||||
|
||||
/// Sender-specific rules.
|
||||
Sender,
|
||||
|
||||
/// Room-specific rules.
|
||||
Room,
|
||||
|
||||
/// Content-specific rules.
|
||||
Content,
|
||||
|
||||
Custom {
|
||||
value: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<SdkRuleKind> for RuleKind {
|
||||
fn from(value: SdkRuleKind) -> Self {
|
||||
match value {
|
||||
SdkRuleKind::Override => Self::Override,
|
||||
SdkRuleKind::Underride => Self::Underride,
|
||||
SdkRuleKind::Sender => Self::Sender,
|
||||
SdkRuleKind::Room => Self::Room,
|
||||
SdkRuleKind::Content => Self::Content,
|
||||
SdkRuleKind::_Custom(_) => Self::Custom { value: value.as_str().to_owned() },
|
||||
_ => Self::Custom { value: value.to_string() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RuleKind> for SdkRuleKind {
|
||||
fn from(value: RuleKind) -> Self {
|
||||
match value {
|
||||
RuleKind::Override => Self::Override,
|
||||
RuleKind::Underride => Self::Underride,
|
||||
RuleKind::Sender => Self::Sender,
|
||||
RuleKind::Room => Self::Room,
|
||||
RuleKind::Content => Self::Content,
|
||||
RuleKind::Custom { value } => SdkRuleKind::from(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, uniffi::Enum)]
|
||||
/// Enum representing the push notification tweaks for a rule.
|
||||
pub enum Tweak {
|
||||
/// A string representing the sound to be played when this notification
|
||||
/// arrives.
|
||||
///
|
||||
/// A value of "default" means to play a default sound. A device may choose
|
||||
/// to alert the user by some other means if appropriate, eg. vibration.
|
||||
Sound { value: String },
|
||||
|
||||
/// A boolean representing whether or not this message should be highlighted
|
||||
/// in the UI.
|
||||
Highlight { value: bool },
|
||||
|
||||
/// A custom tweak
|
||||
Custom {
|
||||
/// The name of the custom tweak (`set_tweak` field)
|
||||
name: String,
|
||||
|
||||
/// The value of the custom tweak as an encoded JSON string
|
||||
value: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl TryFrom<SdkTweak> for Tweak {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: SdkTweak) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
SdkTweak::Sound(sound) => Self::Sound { value: sound },
|
||||
SdkTweak::Highlight(highlight) => Self::Highlight { value: highlight },
|
||||
SdkTweak::Custom { name, value } => {
|
||||
let json_string = serde_json::to_string(&value)
|
||||
.map_err(|e| format!("Failed to serialize custom tweak value: {}", e))?;
|
||||
|
||||
Self::Custom { name, value: json_string }
|
||||
}
|
||||
_ => return Err("Unsupported tweak type".to_owned()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Tweak> for SdkTweak {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: Tweak) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
Tweak::Sound { value } => Self::Sound(value),
|
||||
Tweak::Highlight { value } => Self::Highlight(value),
|
||||
Tweak::Custom { name, value } => {
|
||||
let json_value: serde_json::Value = serde_json::from_str(&value)
|
||||
.map_err(|e| format!("Failed to deserialize custom tweak value: {}", e))?;
|
||||
let value = serde_json::from_value(json_value)
|
||||
.map_err(|e| format!("Failed to convert JSON value: {}", e))?;
|
||||
|
||||
Self::Custom { name, value }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, uniffi::Enum)]
|
||||
/// Enum representing the push notification actions for a rule.
|
||||
pub enum Action {
|
||||
/// Causes matching events to generate a notification.
|
||||
Notify,
|
||||
/// Sets an entry in the 'tweaks' dictionary sent to the push gateway.
|
||||
SetTweak { value: Tweak },
|
||||
}
|
||||
|
||||
impl TryFrom<SdkAction> for Action {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: SdkAction) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
SdkAction::Notify => Self::Notify,
|
||||
SdkAction::SetTweak(tweak) => Self::SetTweak {
|
||||
value: tweak.try_into().map_err(|e| format!("Failed to convert tweak: {}", e))?,
|
||||
},
|
||||
_ => return Err("Unsupported action type".to_owned()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Action> for SdkAction {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: Action) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
Action::Notify => Self::Notify,
|
||||
Action::SetTweak { value } => Self::SetTweak(
|
||||
value.try_into().map_err(|e| format!("Failed to convert tweak: {}", e))?,
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Enum representing the push notification modes for a room.
|
||||
#[derive(Clone, uniffi::Enum)]
|
||||
pub enum RoomNotificationMode {
|
||||
@@ -267,7 +604,7 @@ impl NotificationSettings {
|
||||
pub async fn is_room_mention_enabled(&self) -> Result<bool, NotificationSettingsError> {
|
||||
let notification_settings = self.sdk_notification_settings.read().await;
|
||||
let enabled = notification_settings
|
||||
.is_push_rule_enabled(RuleKind::Override, PredefinedOverrideRuleId::IsRoomMention)
|
||||
.is_push_rule_enabled(SdkRuleKind::Override, PredefinedOverrideRuleId::IsRoomMention)
|
||||
.await?;
|
||||
Ok(enabled)
|
||||
}
|
||||
@@ -280,7 +617,7 @@ impl NotificationSettings {
|
||||
let notification_settings = self.sdk_notification_settings.read().await;
|
||||
notification_settings
|
||||
.set_push_rule_enabled(
|
||||
RuleKind::Override,
|
||||
SdkRuleKind::Override,
|
||||
PredefinedOverrideRuleId::IsRoomMention,
|
||||
enabled,
|
||||
)
|
||||
@@ -292,7 +629,7 @@ impl NotificationSettings {
|
||||
pub async fn is_user_mention_enabled(&self) -> Result<bool, NotificationSettingsError> {
|
||||
let notification_settings = self.sdk_notification_settings.read().await;
|
||||
let enabled = notification_settings
|
||||
.is_push_rule_enabled(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention)
|
||||
.is_push_rule_enabled(SdkRuleKind::Override, PredefinedOverrideRuleId::IsUserMention)
|
||||
.await?;
|
||||
Ok(enabled)
|
||||
}
|
||||
@@ -304,14 +641,14 @@ impl NotificationSettings {
|
||||
let notification_settings = self.sdk_notification_settings.read().await;
|
||||
// Check stable identifier
|
||||
if let Ok(enabled) = notification_settings
|
||||
.is_push_rule_enabled(RuleKind::Override, ".m.rule.encrypted_event")
|
||||
.is_push_rule_enabled(SdkRuleKind::Override, ".m.rule.encrypted_event")
|
||||
.await
|
||||
{
|
||||
enabled
|
||||
} else {
|
||||
// Check unstable identifier
|
||||
notification_settings
|
||||
.is_push_rule_enabled(RuleKind::Override, ".org.matrix.msc4028.encrypted_event")
|
||||
.is_push_rule_enabled(SdkRuleKind::Override, ".org.matrix.msc4028.encrypted_event")
|
||||
.await
|
||||
.unwrap_or(false)
|
||||
}
|
||||
@@ -332,7 +669,7 @@ impl NotificationSettings {
|
||||
let notification_settings = self.sdk_notification_settings.read().await;
|
||||
notification_settings
|
||||
.set_push_rule_enabled(
|
||||
RuleKind::Override,
|
||||
SdkRuleKind::Override,
|
||||
PredefinedOverrideRuleId::IsUserMention,
|
||||
enabled,
|
||||
)
|
||||
@@ -344,7 +681,7 @@ impl NotificationSettings {
|
||||
pub async fn is_call_enabled(&self) -> Result<bool, NotificationSettingsError> {
|
||||
let notification_settings = self.sdk_notification_settings.read().await;
|
||||
let enabled = notification_settings
|
||||
.is_push_rule_enabled(RuleKind::Underride, PredefinedUnderrideRuleId::Call)
|
||||
.is_push_rule_enabled(SdkRuleKind::Underride, PredefinedUnderrideRuleId::Call)
|
||||
.await?;
|
||||
Ok(enabled)
|
||||
}
|
||||
@@ -353,7 +690,7 @@ impl NotificationSettings {
|
||||
pub async fn set_call_enabled(&self, enabled: bool) -> Result<(), NotificationSettingsError> {
|
||||
let notification_settings = self.sdk_notification_settings.read().await;
|
||||
notification_settings
|
||||
.set_push_rule_enabled(RuleKind::Underride, PredefinedUnderrideRuleId::Call, enabled)
|
||||
.set_push_rule_enabled(SdkRuleKind::Underride, PredefinedUnderrideRuleId::Call, enabled)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -363,7 +700,7 @@ impl NotificationSettings {
|
||||
let notification_settings = self.sdk_notification_settings.read().await;
|
||||
let enabled = notification_settings
|
||||
.is_push_rule_enabled(
|
||||
RuleKind::Override,
|
||||
SdkRuleKind::Override,
|
||||
PredefinedOverrideRuleId::InviteForMe.as_str(),
|
||||
)
|
||||
.await?;
|
||||
@@ -378,7 +715,7 @@ impl NotificationSettings {
|
||||
let notification_settings = self.sdk_notification_settings.read().await;
|
||||
notification_settings
|
||||
.set_push_rule_enabled(
|
||||
RuleKind::Override,
|
||||
SdkRuleKind::Override,
|
||||
PredefinedOverrideRuleId::InviteForMe.as_str(),
|
||||
enabled,
|
||||
)
|
||||
@@ -386,6 +723,30 @@ impl NotificationSettings {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets a custom push rule with the given actions and conditions.
|
||||
pub async fn set_custom_push_rule(
|
||||
&self,
|
||||
rule_id: String,
|
||||
rule_kind: RuleKind,
|
||||
actions: Vec<Action>,
|
||||
conditions: Vec<PushCondition>,
|
||||
) -> Result<(), NotificationSettingsError> {
|
||||
let notification_settings = self.sdk_notification_settings.read().await;
|
||||
let actions: Result<Vec<_>, _> =
|
||||
actions.into_iter().map(|action| action.try_into()).collect();
|
||||
let actions = actions.map_err(|e| NotificationSettingsError::Generic { msg: e })?;
|
||||
|
||||
notification_settings
|
||||
.create_custom_conditional_push_rule(
|
||||
rule_id,
|
||||
rule_kind.into(),
|
||||
actions,
|
||||
conditions.into_iter().map(|condition| condition.into()).collect(),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unmute a room.
|
||||
///
|
||||
/// # Arguments
|
||||
|
||||
@@ -204,11 +204,17 @@ simpler methods:
|
||||
- Enable HTTP/2 support in the HTTP client.
|
||||
([#4566](https://github.com/matrix-org/matrix-rust-sdk/pull/4566))
|
||||
|
||||
- Add support for creating custom conditional push rules in `NotificationSettings::create_custom_conditional_push_rule`.
|
||||
([#4587](https://github.com/matrix-org/matrix-rust-sdk/pull/4587))
|
||||
|
||||
- The media contents stored in the media cache can now be controlled with a
|
||||
`MediaRetentionPolicy` and the new `Media` methods `media_retention_policy()`,
|
||||
`set_media_retention_policy()`, `clean_up_media_cache()`.
|
||||
([#4571](https://github.com/matrix-org/matrix-rust-sdk/pull/4571))
|
||||
|
||||
- Add support for creating custom conditional push rules in `NotificationSettings::create_custom_conditional_push_rule`.
|
||||
([#4587](https://github.com/matrix-org/matrix-rust-sdk/pull/4587))
|
||||
|
||||
### Refactor
|
||||
|
||||
- [**breaking**]: The `RoomEventCacheUpdate::Clear` variant has been removed, as
|
||||
|
||||
@@ -25,6 +25,8 @@ pub(crate) enum Command {
|
||||
DeletePushRule { kind: RuleKind, rule_id: String },
|
||||
/// Set a list of actions
|
||||
SetPushRuleActions { kind: RuleKind, rule_id: String, actions: Vec<Action> },
|
||||
/// Sets a custom push rule
|
||||
SetCustomPushRule { rule: NewPushRule },
|
||||
}
|
||||
|
||||
fn get_notify_actions(notify: bool) -> Vec<Action> {
|
||||
@@ -73,6 +75,8 @@ impl Command {
|
||||
| Self::SetPushRuleActions { .. } => Err(NotificationSettingsError::InvalidParameter(
|
||||
"cannot create a push rule from this command.".to_owned(),
|
||||
)),
|
||||
|
||||
Self::SetCustomPushRule { rule } => Ok(rule.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ use ruma::{
|
||||
delete_pushrule, set_pushrule, set_pushrule_actions, set_pushrule_enabled,
|
||||
},
|
||||
events::push_rules::PushRulesEvent,
|
||||
push::{Action, PredefinedUnderrideRuleId, RuleKind, Ruleset, Tweak},
|
||||
push::{Action, NewPushRule, PredefinedUnderrideRuleId, RuleKind, Ruleset, Tweak},
|
||||
RoomId,
|
||||
};
|
||||
use tokio::sync::{
|
||||
@@ -265,6 +265,44 @@ impl NotificationSettings {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a custom conditional push rule.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `rule_id` - The identifier of the push rule.
|
||||
/// * `rule_kind` - The kind of the push rule.
|
||||
/// * `actions` - The actions to set for the push rule.
|
||||
/// * `conditions` - The conditions for the push rule.
|
||||
///
|
||||
/// See more in the matrix spec: <https://spec.matrix.org/latest/client-server-api/#push-rules>
|
||||
pub async fn create_custom_conditional_push_rule(
|
||||
&self,
|
||||
rule_id: String,
|
||||
rule_kind: RuleKind,
|
||||
actions: Vec<Action>,
|
||||
conditions: Vec<ruma::push::PushCondition>,
|
||||
) -> Result<(), NotificationSettingsError> {
|
||||
let new_conditional_rule =
|
||||
ruma::push::NewConditionalPushRule::new(rule_id, conditions, actions);
|
||||
|
||||
let new_push_rule = match rule_kind {
|
||||
RuleKind::Override => NewPushRule::Override(new_conditional_rule),
|
||||
RuleKind::Underride => NewPushRule::Underride(new_conditional_rule),
|
||||
_ => return Err(NotificationSettingsError::InvalidParameter("rule_kind".to_owned())),
|
||||
};
|
||||
|
||||
let rules = self.rules.read().await.clone();
|
||||
let mut rule_commands = RuleCommands::new(rules.clone().ruleset);
|
||||
rule_commands.insert_custom_rule(new_push_rule)?;
|
||||
|
||||
self.run_server_commands(&rule_commands).await?;
|
||||
|
||||
let rules = &mut *self.rules.write().await;
|
||||
rules.apply(rule_commands);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the notification mode for a room.
|
||||
pub async fn set_room_notification_mode(
|
||||
&self,
|
||||
@@ -515,6 +553,16 @@ impl NotificationSettings {
|
||||
},
|
||||
)?;
|
||||
}
|
||||
Command::SetCustomPushRule { rule } => {
|
||||
let request = set_pushrule::v3::Request::new(rule.clone());
|
||||
|
||||
self.client.send(request).with_request_config(request_config).await.map_err(
|
||||
|error| {
|
||||
error!("Unable to set custom push rule `{rule:#?}`: {error}");
|
||||
NotificationSettingsError::UnableToAddPushRule
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -1574,4 +1622,64 @@ mod tests {
|
||||
RoomNotificationMode::MentionsAndKeywordsOnly
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_create_custom_conditional_push_rule() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
let settings = client.notification_settings().await;
|
||||
|
||||
Mock::given(method("PUT"))
|
||||
.and(path("/_matrix/client/r0/pushrules/global/override/custom_rule"))
|
||||
.respond_with(ResponseTemplate::new(200))
|
||||
.expect(1)
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
let actions = vec![Action::Notify];
|
||||
let conditions = vec![ruma::push::PushCondition::EventMatch {
|
||||
key: "content.body".to_owned(),
|
||||
pattern: "hello".to_owned(),
|
||||
}];
|
||||
|
||||
settings
|
||||
.create_custom_conditional_push_rule(
|
||||
"custom_rule".to_owned(),
|
||||
RuleKind::Override,
|
||||
actions.clone(),
|
||||
conditions.clone(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let rules = settings.rules.read().await;
|
||||
let rule = rules.ruleset.get(RuleKind::Override, "custom_rule").unwrap();
|
||||
|
||||
assert_eq!(rule.rule_id(), "custom_rule");
|
||||
assert!(rule.enabled());
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_create_custom_conditional_push_rule_invalid_kind() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
let settings = client.notification_settings().await;
|
||||
|
||||
let actions = vec![Action::Notify];
|
||||
let conditions = vec![ruma::push::PushCondition::EventMatch {
|
||||
key: "content.body".to_owned(),
|
||||
pattern: "hello".to_owned(),
|
||||
}];
|
||||
|
||||
let result = settings
|
||||
.create_custom_conditional_push_rule(
|
||||
"custom_rule".to_owned(),
|
||||
RuleKind::Room,
|
||||
actions,
|
||||
conditions,
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_matches!(result, Err(NotificationSettingsError::InvalidParameter(_)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use ruma::{
|
||||
push::{
|
||||
Action, PredefinedContentRuleId, PredefinedOverrideRuleId, RemovePushRuleError, RuleKind,
|
||||
Ruleset,
|
||||
Action, NewPushRule, PredefinedContentRuleId, PredefinedOverrideRuleId,
|
||||
RemovePushRuleError, RuleKind, Ruleset,
|
||||
},
|
||||
RoomId,
|
||||
};
|
||||
@@ -62,6 +62,19 @@ impl RuleCommands {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Insert a new custom rule
|
||||
pub(crate) fn insert_custom_rule(
|
||||
&mut self,
|
||||
rule: NewPushRule,
|
||||
) -> Result<(), NotificationSettingsError> {
|
||||
let command = Command::SetCustomPushRule { rule: rule.clone() };
|
||||
|
||||
self.rules.insert(rule, None, None)?;
|
||||
self.commands.push(command);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete a rule
|
||||
pub(crate) fn delete_rule(
|
||||
&mut self,
|
||||
|
||||
@@ -269,6 +269,9 @@ impl Rules {
|
||||
Command::SetPushRuleActions { kind, rule_id, actions } => {
|
||||
_ = self.ruleset.set_actions(kind, rule_id, actions);
|
||||
}
|
||||
Command::SetCustomPushRule { rule } => {
|
||||
_ = self.ruleset.insert(rule, None, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user