push: Make power levels optional in PushConditionRoomCtx
This commit is contained in:
		
							parent
							
								
									4efca6fba5
								
							
						
					
					
						commit
						90d3605b87
					
				@ -1,5 +1,11 @@
 | 
			
		||||
# [unreleased]
 | 
			
		||||
 | 
			
		||||
Breaking changes:
 | 
			
		||||
- The power levels fields in `PushConditionRoomCtx` are grouped in an optional `power_levels` field.
 | 
			
		||||
  If the field is missing, push rules that depend on it will never match. However, this allows to
 | 
			
		||||
  match the `.m.rule.invite_for_me` push rule because usually the `invite_state` doesn't include
 | 
			
		||||
  `m.room.power_levels`.
 | 
			
		||||
 | 
			
		||||
Improvements:
 | 
			
		||||
 | 
			
		||||
- Stabilize support for `.m.rule.suppress_edits` push rule (MSC3958 / Matrix 1.9)
 | 
			
		||||
 | 
			
		||||
@ -38,8 +38,9 @@ pub use self::condition::RoomVersionFeature;
 | 
			
		||||
pub use self::{
 | 
			
		||||
    action::{Action, Tweak},
 | 
			
		||||
    condition::{
 | 
			
		||||
        ComparisonOperator, FlattenedJson, FlattenedJsonValue, PushCondition, PushConditionRoomCtx,
 | 
			
		||||
        RoomMemberCountIs, ScalarJsonValue, _CustomPushCondition,
 | 
			
		||||
        ComparisonOperator, FlattenedJson, FlattenedJsonValue, PushCondition,
 | 
			
		||||
        PushConditionPowerLevelsCtx, PushConditionRoomCtx, RoomMemberCountIs, ScalarJsonValue,
 | 
			
		||||
        _CustomPushCondition,
 | 
			
		||||
    },
 | 
			
		||||
    iter::{AnyPushRule, AnyPushRuleRef, RulesetIntoIter, RulesetIter},
 | 
			
		||||
    predefined::{
 | 
			
		||||
@ -988,7 +989,9 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    use super::{
 | 
			
		||||
        action::{Action, Tweak},
 | 
			
		||||
        condition::{PushCondition, PushConditionRoomCtx, RoomMemberCountIs},
 | 
			
		||||
        condition::{
 | 
			
		||||
            PushCondition, PushConditionPowerLevelsCtx, PushConditionRoomCtx, RoomMemberCountIs,
 | 
			
		||||
        },
 | 
			
		||||
        AnyPushRule, ConditionalPushRule, PatternedPushRule, Ruleset, SimplePushRule,
 | 
			
		||||
    };
 | 
			
		||||
    use crate::{
 | 
			
		||||
@ -1016,6 +1019,14 @@ mod tests {
 | 
			
		||||
        set
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn power_levels() -> PushConditionPowerLevelsCtx {
 | 
			
		||||
        PushConditionPowerLevelsCtx {
 | 
			
		||||
            users: BTreeMap::new(),
 | 
			
		||||
            users_default: int!(50),
 | 
			
		||||
            notifications: NotificationPowerLevels { room: int!(50) },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn iter() {
 | 
			
		||||
        let mut set = example_ruleset();
 | 
			
		||||
@ -1431,9 +1442,7 @@ mod tests {
 | 
			
		||||
            member_count: uint!(2),
 | 
			
		||||
            user_id: owned_user_id!("@jj:server.name"),
 | 
			
		||||
            user_display_name: "Jolly Jumper".into(),
 | 
			
		||||
            users_power_levels: BTreeMap::new(),
 | 
			
		||||
            default_power_level: int!(50),
 | 
			
		||||
            notification_power_levels: NotificationPowerLevels { room: int!(50) },
 | 
			
		||||
            power_levels: Some(power_levels()),
 | 
			
		||||
            #[cfg(feature = "unstable-msc3931")]
 | 
			
		||||
            supported_features: Default::default(),
 | 
			
		||||
        };
 | 
			
		||||
@ -1443,9 +1452,7 @@ mod tests {
 | 
			
		||||
            member_count: uint!(100),
 | 
			
		||||
            user_id: owned_user_id!("@jj:server.name"),
 | 
			
		||||
            user_display_name: "Jolly Jumper".into(),
 | 
			
		||||
            users_power_levels: BTreeMap::new(),
 | 
			
		||||
            default_power_level: int!(50),
 | 
			
		||||
            notification_power_levels: NotificationPowerLevels { room: int!(50) },
 | 
			
		||||
            power_levels: Some(power_levels()),
 | 
			
		||||
            #[cfg(feature = "unstable-msc3931")]
 | 
			
		||||
            supported_features: Default::default(),
 | 
			
		||||
        };
 | 
			
		||||
@ -1536,9 +1543,7 @@ mod tests {
 | 
			
		||||
            member_count: uint!(2),
 | 
			
		||||
            user_id: owned_user_id!("@jj:server.name"),
 | 
			
		||||
            user_display_name: "Jolly Jumper".into(),
 | 
			
		||||
            users_power_levels: BTreeMap::new(),
 | 
			
		||||
            default_power_level: int!(50),
 | 
			
		||||
            notification_power_levels: NotificationPowerLevels { room: int!(50) },
 | 
			
		||||
            power_levels: Some(power_levels()),
 | 
			
		||||
            #[cfg(feature = "unstable-msc3931")]
 | 
			
		||||
            supported_features: Default::default(),
 | 
			
		||||
        };
 | 
			
		||||
@ -1677,9 +1682,7 @@ mod tests {
 | 
			
		||||
            member_count: uint!(100),
 | 
			
		||||
            user_id: owned_user_id!("@jj:server.name"),
 | 
			
		||||
            user_display_name: "Jolly Jumper".into(),
 | 
			
		||||
            users_power_levels: BTreeMap::new(),
 | 
			
		||||
            default_power_level: int!(50),
 | 
			
		||||
            notification_power_levels: NotificationPowerLevels { room: int!(50) },
 | 
			
		||||
            power_levels: Some(power_levels()),
 | 
			
		||||
            #[cfg(feature = "unstable-msc3931")]
 | 
			
		||||
            supported_features: Default::default(),
 | 
			
		||||
        };
 | 
			
		||||
@ -1789,9 +1792,7 @@ mod tests {
 | 
			
		||||
            member_count: uint!(100),
 | 
			
		||||
            user_id: owned_user_id!("@jj:server.name"),
 | 
			
		||||
            user_display_name: "Jolly Jumper".into(),
 | 
			
		||||
            users_power_levels: BTreeMap::new(),
 | 
			
		||||
            default_power_level: int!(50),
 | 
			
		||||
            notification_power_levels: NotificationPowerLevels { room: int!(50) },
 | 
			
		||||
            power_levels: Some(power_levels()),
 | 
			
		||||
            #[cfg(feature = "unstable-msc3931")]
 | 
			
		||||
            supported_features: Default::default(),
 | 
			
		||||
        };
 | 
			
		||||
@ -1834,4 +1835,37 @@ mod tests {
 | 
			
		||||
            PredefinedOverrideRuleId::IsRoomMention.as_ref()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn invite_for_me_applies() {
 | 
			
		||||
        let set = Ruleset::server_default(user_id!("@jolly_jumper:server.name"));
 | 
			
		||||
 | 
			
		||||
        let context = &PushConditionRoomCtx {
 | 
			
		||||
            room_id: owned_room_id!("!far_west:server.name"),
 | 
			
		||||
            member_count: uint!(100),
 | 
			
		||||
            user_id: owned_user_id!("@jj:server.name"),
 | 
			
		||||
            user_display_name: "Jolly Jumper".into(),
 | 
			
		||||
            // `invite_state` usually doesn't include the power levels.
 | 
			
		||||
            power_levels: None,
 | 
			
		||||
            #[cfg(feature = "unstable-msc3931")]
 | 
			
		||||
            supported_features: Default::default(),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let message = serde_json::from_str::<Raw<JsonValue>>(
 | 
			
		||||
            r#"{
 | 
			
		||||
                "content": {
 | 
			
		||||
                    "membership": "invite"
 | 
			
		||||
                },
 | 
			
		||||
                "state_key": "@jolly_jumper:server.name",
 | 
			
		||||
                "sender": "@admin:server.name",
 | 
			
		||||
                "type": "m.room.member"
 | 
			
		||||
            }"#,
 | 
			
		||||
        )
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            set.get_match(&message, context).unwrap().rule_id(),
 | 
			
		||||
            PredefinedOverrideRuleId::InviteForMe.as_ref()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -155,7 +155,8 @@ impl PushCondition {
 | 
			
		||||
    /// # Arguments
 | 
			
		||||
    ///
 | 
			
		||||
    /// * `event` - The flattened JSON representation of a room message event.
 | 
			
		||||
    /// * `context` - The context of the room at the time of the event.
 | 
			
		||||
    /// * `context` - The context of the room at the time of the event. If the power levels context
 | 
			
		||||
    ///   is missing from it, conditions that depend on it will never apply.
 | 
			
		||||
    pub fn applies(&self, event: &FlattenedJson, context: &PushConditionRoomCtx) -> bool {
 | 
			
		||||
        if event.get_str("sender").is_some_and(|sender| sender == context.user_id) {
 | 
			
		||||
            return false;
 | 
			
		||||
@ -173,6 +174,10 @@ impl PushCondition {
 | 
			
		||||
            }
 | 
			
		||||
            Self::RoomMemberCount { is } => is.contains(&context.member_count),
 | 
			
		||||
            Self::SenderNotificationPermission { key } => {
 | 
			
		||||
                let Some(power_levels) = &context.power_levels else {
 | 
			
		||||
                    return false;
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                let sender_id = match event.get_str("sender") {
 | 
			
		||||
                    Some(v) => match <&UserId>::try_from(v) {
 | 
			
		||||
                        Ok(u) => u,
 | 
			
		||||
@ -181,12 +186,10 @@ impl PushCondition {
 | 
			
		||||
                    None => return false,
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                let sender_level = context
 | 
			
		||||
                    .users_power_levels
 | 
			
		||||
                    .get(sender_id)
 | 
			
		||||
                    .unwrap_or(&context.default_power_level);
 | 
			
		||||
                let sender_level =
 | 
			
		||||
                    power_levels.users.get(sender_id).unwrap_or(&power_levels.users_default);
 | 
			
		||||
 | 
			
		||||
                match context.notification_power_levels.get(key) {
 | 
			
		||||
                match power_levels.notifications.get(key) {
 | 
			
		||||
                    Some(l) => sender_level >= l,
 | 
			
		||||
                    None => false,
 | 
			
		||||
                }
 | 
			
		||||
@ -231,24 +234,34 @@ pub struct PushConditionRoomCtx {
 | 
			
		||||
    /// The number of members in the room.
 | 
			
		||||
    pub member_count: UInt,
 | 
			
		||||
 | 
			
		||||
    /// The users matrix ID.
 | 
			
		||||
    /// The user's matrix ID.
 | 
			
		||||
    pub user_id: OwnedUserId,
 | 
			
		||||
 | 
			
		||||
    /// The display name of the current user in the room.
 | 
			
		||||
    pub user_display_name: String,
 | 
			
		||||
 | 
			
		||||
    /// The room power levels context for the room.
 | 
			
		||||
    ///
 | 
			
		||||
    /// If this is missing, push rules that require this will never match.
 | 
			
		||||
    pub power_levels: Option<PushConditionPowerLevelsCtx>,
 | 
			
		||||
 | 
			
		||||
    /// The list of features this room's version or the room itself supports.
 | 
			
		||||
    #[cfg(feature = "unstable-msc3931")]
 | 
			
		||||
    pub supported_features: Vec<RoomVersionFeature>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// The room power levels context to be able to test the corresponding push conditions.
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
#[allow(clippy::exhaustive_structs)]
 | 
			
		||||
pub struct PushConditionPowerLevelsCtx {
 | 
			
		||||
    /// The power levels of the users of the room.
 | 
			
		||||
    pub users_power_levels: BTreeMap<OwnedUserId, Int>,
 | 
			
		||||
    pub users: BTreeMap<OwnedUserId, Int>,
 | 
			
		||||
 | 
			
		||||
    /// The default power level of the users of the room.
 | 
			
		||||
    pub default_power_level: Int,
 | 
			
		||||
    pub users_default: Int,
 | 
			
		||||
 | 
			
		||||
    /// The notification power levels of the room.
 | 
			
		||||
    pub notification_power_levels: NotificationPowerLevels,
 | 
			
		||||
 | 
			
		||||
    #[cfg(feature = "unstable-msc3931")]
 | 
			
		||||
    /// The list of features this room's version or the room itself supports.
 | 
			
		||||
    pub supported_features: Vec<RoomVersionFeature>,
 | 
			
		||||
    pub notifications: NotificationPowerLevels,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Additional functions for character matching.
 | 
			
		||||
@ -451,7 +464,10 @@ mod tests {
 | 
			
		||||
        from_value as from_json_value, json, to_value as to_json_value, Value as JsonValue,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    use super::{FlattenedJson, PushCondition, PushConditionRoomCtx, RoomMemberCountIs, StrExt};
 | 
			
		||||
    use super::{
 | 
			
		||||
        FlattenedJson, PushCondition, PushConditionPowerLevelsCtx, PushConditionRoomCtx,
 | 
			
		||||
        RoomMemberCountIs, StrExt,
 | 
			
		||||
    };
 | 
			
		||||
    use crate::{
 | 
			
		||||
        owned_room_id, owned_user_id, power_levels::NotificationPowerLevels, serde::Raw,
 | 
			
		||||
        OwnedUserId,
 | 
			
		||||
@ -647,17 +663,21 @@ mod tests {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn push_context() -> PushConditionRoomCtx {
 | 
			
		||||
        let mut users_power_levels = BTreeMap::new();
 | 
			
		||||
        users_power_levels.insert(sender(), int!(25));
 | 
			
		||||
        let mut users = BTreeMap::new();
 | 
			
		||||
        users.insert(sender(), int!(25));
 | 
			
		||||
 | 
			
		||||
        let power_levels = PushConditionPowerLevelsCtx {
 | 
			
		||||
            users,
 | 
			
		||||
            users_default: int!(50),
 | 
			
		||||
            notifications: NotificationPowerLevels { room: int!(50) },
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        PushConditionRoomCtx {
 | 
			
		||||
            room_id: owned_room_id!("!room:server.name"),
 | 
			
		||||
            member_count: uint!(3),
 | 
			
		||||
            user_id: owned_user_id!("@gorilla:server.name"),
 | 
			
		||||
            user_display_name: "Groovy Gorilla".into(),
 | 
			
		||||
            users_power_levels,
 | 
			
		||||
            default_power_level: int!(50),
 | 
			
		||||
            notification_power_levels: NotificationPowerLevels { room: int!(50) },
 | 
			
		||||
            power_levels: Some(power_levels),
 | 
			
		||||
            #[cfg(feature = "unstable-msc3931")]
 | 
			
		||||
            supported_features: Default::default(),
 | 
			
		||||
        }
 | 
			
		||||
@ -776,9 +796,7 @@ mod tests {
 | 
			
		||||
            member_count: uint!(3),
 | 
			
		||||
            user_id: owned_user_id!("@gorilla:server.name"),
 | 
			
		||||
            user_display_name: "Groovy Gorilla".into(),
 | 
			
		||||
            users_power_levels: context_not_matching.users_power_levels.clone(),
 | 
			
		||||
            default_power_level: int!(50),
 | 
			
		||||
            notification_power_levels: NotificationPowerLevels { room: int!(50) },
 | 
			
		||||
            power_levels: context_not_matching.power_levels.clone(),
 | 
			
		||||
            supported_features: vec![super::RoomVersionFeature::ExtensibleEvents],
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user