atagen

macltok

public
Star0
Watch0
aced11e80b47de7a887296b10eedff310df5360f
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git

Blame: push-gateway/src/lib.rs

2006a11ce142atagen2026-10-26 04:02 UTC1
//! the homeserver side of `POST /_matrix/push/v1/notify`, for a gateway that holds no session
2006a11ce142atagen2026-10-26 04:02 UTC2
2006a11ce142atagen2026-10-26 04:02 UTC3
use ruma_common::{OwnedEventId, OwnedRoomAliasId, OwnedRoomId, OwnedUserId};
2006a11ce142atagen2026-10-26 04:02 UTC4
use serde::{Deserialize, Serialize};
2006a11ce142atagen2026-10-26 04:02 UTC5
use serde_json::{Map, Value, value::RawValue};
2006a11ce142atagen2026-10-26 04:02 UTC6
2006a11ce142atagen2026-10-26 04:02 UTC7
#[derive(Clone, Debug, Deserialize)]
2006a11ce142atagen2026-10-26 04:02 UTC8
#[non_exhaustive]
2006a11ce142atagen2026-10-26 04:02 UTC9
pub struct GatewayNotification {
2006a11ce142atagen2026-10-26 04:02 UTC10
    pub event_id: Option<OwnedEventId>,
2006a11ce142atagen2026-10-26 04:02 UTC11
    pub room_id: Option<OwnedRoomId>,
2006a11ce142atagen2026-10-26 04:02 UTC12
    #[serde(rename = "type")]
2006a11ce142atagen2026-10-26 04:02 UTC13
    pub event_type: Option<String>,
2006a11ce142atagen2026-10-26 04:02 UTC14
    pub sender: Option<OwnedUserId>,
2006a11ce142atagen2026-10-26 04:02 UTC15
    pub sender_display_name: Option<String>,
2006a11ce142atagen2026-10-26 04:02 UTC16
    pub room_name: Option<String>,
2006a11ce142atagen2026-10-26 04:02 UTC17
    pub room_alias: Option<OwnedRoomAliasId>,
2006a11ce142atagen2026-10-26 04:02 UTC18
    #[serde(default)]
2006a11ce142atagen2026-10-26 04:02 UTC19
    pub user_is_target: bool,
2006a11ce142atagen2026-10-26 04:02 UTC20
    #[serde(default)]
2006a11ce142atagen2026-10-26 04:02 UTC21
    pub prio: NotifyPrio,
2006a11ce142atagen2026-10-26 04:02 UTC22
    /// only in the full format, and still encrypted for an encrypted room
2006a11ce142atagen2026-10-26 04:02 UTC23
    pub content: Option<Box<RawValue>>,
2006a11ce142atagen2026-10-26 04:02 UTC24
    #[serde(default)]
2006a11ce142atagen2026-10-26 04:02 UTC25
    pub counts: NotifyCounts,
2006a11ce142atagen2026-10-26 04:02 UTC26
    #[serde(default)]
2006a11ce142atagen2026-10-26 04:02 UTC27
    pub devices: Vec<GatewayDevice>,
2006a11ce142atagen2026-10-26 04:02 UTC28
}
2006a11ce142atagen2026-10-26 04:02 UTC29
2006a11ce142atagen2026-10-26 04:02 UTC30
impl GatewayNotification {
2006a11ce142atagen2026-10-26 04:02 UTC31
    /// a count-only push carries no event, and iOS punishes a web push that shows nothing
2006a11ce142atagen2026-10-26 04:02 UTC32
    pub fn is_event(&self) -> bool {
2006a11ce142atagen2026-10-26 04:02 UTC33
        self.event_id.is_some()
2006a11ce142atagen2026-10-26 04:02 UTC34
    }
2006a11ce142atagen2026-10-26 04:02 UTC35
}
2006a11ce142atagen2026-10-26 04:02 UTC36
2006a11ce142atagen2026-10-26 04:02 UTC37
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
2006a11ce142atagen2026-10-26 04:02 UTC38
#[serde(rename_all = "lowercase")]
2006a11ce142atagen2026-10-26 04:02 UTC39
pub enum NotifyPrio {
2006a11ce142atagen2026-10-26 04:02 UTC40
    #[default]
2006a11ce142atagen2026-10-26 04:02 UTC41
    High,
2006a11ce142atagen2026-10-26 04:02 UTC42
    Low,
2006a11ce142atagen2026-10-26 04:02 UTC43
}
2006a11ce142atagen2026-10-26 04:02 UTC44
2006a11ce142atagen2026-10-26 04:02 UTC45
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
2006a11ce142atagen2026-10-26 04:02 UTC46
#[non_exhaustive]
2006a11ce142atagen2026-10-26 04:02 UTC47
pub struct NotifyCounts {
2006a11ce142atagen2026-10-26 04:02 UTC48
    #[serde(default)]
2006a11ce142atagen2026-10-26 04:02 UTC49
    pub unread: u64,
2006a11ce142atagen2026-10-26 04:02 UTC50
    #[serde(default)]
2006a11ce142atagen2026-10-26 04:02 UTC51
    pub missed_calls: u64,
2006a11ce142atagen2026-10-26 04:02 UTC52
}
2006a11ce142atagen2026-10-26 04:02 UTC53
2006a11ce142atagen2026-10-26 04:02 UTC54
#[derive(Clone, Debug, Deserialize)]
2006a11ce142atagen2026-10-26 04:02 UTC55
#[non_exhaustive]
2006a11ce142atagen2026-10-26 04:02 UTC56
pub struct GatewayDevice {
2006a11ce142atagen2026-10-26 04:02 UTC57
    pub app_id: String,
2006a11ce142atagen2026-10-26 04:02 UTC58
    pub pushkey: String,
2006a11ce142atagen2026-10-26 04:02 UTC59
    pub pushkey_ts: Option<u64>,
2006a11ce142atagen2026-10-26 04:02 UTC60
    /// the pusher's data as it was set, `format` included
2006a11ce142atagen2026-10-26 04:02 UTC61
    #[serde(default)]
2006a11ce142atagen2026-10-26 04:02 UTC62
    pub data: Map<String, Value>,
2006a11ce142atagen2026-10-26 04:02 UTC63
    #[serde(default)]
2006a11ce142atagen2026-10-26 04:02 UTC64
    pub tweaks: Map<String, Value>,
2006a11ce142atagen2026-10-26 04:02 UTC65
}
2006a11ce142atagen2026-10-26 04:02 UTC66
2006a11ce142atagen2026-10-26 04:02 UTC67
#[derive(Deserialize)]
2006a11ce142atagen2026-10-26 04:02 UTC68
struct NotifyBody {
2006a11ce142atagen2026-10-26 04:02 UTC69
    notification: GatewayNotification,
2006a11ce142atagen2026-10-26 04:02 UTC70
}
2006a11ce142atagen2026-10-26 04:02 UTC71
2006a11ce142atagen2026-10-26 04:02 UTC72
pub fn parse_notify(body: &[u8]) -> Result<GatewayNotification, serde_json::Error> {
2006a11ce142atagen2026-10-26 04:02 UTC73
    serde_json::from_slice::<NotifyBody>(body).map(|body| body.notification)
2006a11ce142atagen2026-10-26 04:02 UTC74
}
2006a11ce142atagen2026-10-26 04:02 UTC75
2006a11ce142atagen2026-10-26 04:02 UTC76
/// the pushkeys the homeserver should drop, such as one whose web push endpoint answered 404 or 410
2006a11ce142atagen2026-10-26 04:02 UTC77
pub fn notify_response(rejected: &[String]) -> Vec<u8> {
2006a11ce142atagen2026-10-26 04:02 UTC78
    #[derive(Serialize)]
2006a11ce142atagen2026-10-26 04:02 UTC79
    struct Response<'a> {
2006a11ce142atagen2026-10-26 04:02 UTC80
        rejected: &'a [String],
2006a11ce142atagen2026-10-26 04:02 UTC81
    }
2006a11ce142atagen2026-10-26 04:02 UTC82
    serde_json::to_vec(&Response { rejected }).expect("a list of strings serialises")
2006a11ce142atagen2026-10-26 04:02 UTC83
}
2006a11ce142atagen2026-10-26 04:02 UTC84
2006a11ce142atagen2026-10-26 04:02 UTC85
#[cfg(test)]
2006a11ce142atagen2026-10-26 04:02 UTC86
mod tests {
2006a11ce142atagen2026-10-26 04:02 UTC87
    use super::*;
2006a11ce142atagen2026-10-26 04:02 UTC88
2006a11ce142atagen2026-10-26 04:02 UTC89
    #[test]
2006a11ce142atagen2026-10-26 04:02 UTC90
    fn a_count_only_push_is_no_event_and_devices_keep_their_data() {
2006a11ce142atagen2026-10-26 04:02 UTC91
        let body = br#"{"notification":{"counts":{"unread":2},"devices":[{"app_id":"me.malice","pushkey":"https://push.example/abc","data":{"format":"event_id_only","endpoint":"https://push.example/abc","auth":"secret"},"tweaks":{"sound":"default"}}]}}"#;
2006a11ce142atagen2026-10-26 04:02 UTC92
        let notification = parse_notify(body).unwrap();
2006a11ce142atagen2026-10-26 04:02 UTC93
        assert!(!notification.is_event());
2006a11ce142atagen2026-10-26 04:02 UTC94
        assert_eq!(notification.counts.unread, 2);
2006a11ce142atagen2026-10-26 04:02 UTC95
        assert_eq!(notification.prio, NotifyPrio::High);
2006a11ce142atagen2026-10-26 04:02 UTC96
        assert_eq!(notification.devices[0].data["auth"], "secret");
2006a11ce142atagen2026-10-26 04:02 UTC97
        assert_eq!(notification.devices[0].tweaks["sound"], "default");
2006a11ce142atagen2026-10-26 04:02 UTC98
2006a11ce142atagen2026-10-26 04:02 UTC99
        let body = br#"{"notification":{"event_id":"$e","room_id":"!r:example.org","type":"m.room.message","sender":"@a:example.org","prio":"low","content":{"body":"hi"},"devices":[]}}"#;
2006a11ce142atagen2026-10-26 04:02 UTC100
        let notification = parse_notify(body).unwrap();
2006a11ce142atagen2026-10-26 04:02 UTC101
        assert!(notification.is_event());
2006a11ce142atagen2026-10-26 04:02 UTC102
        assert_eq!(notification.prio, NotifyPrio::Low);
2006a11ce142atagen2026-10-26 04:02 UTC103
        assert_eq!(notification.content.unwrap().get(), r#"{"body":"hi"}"#);
2006a11ce142atagen2026-10-26 04:02 UTC104
        assert_eq!(
2006a11ce142atagen2026-10-26 04:02 UTC105
            notify_response(&["gone".to_owned()]),
2006a11ce142atagen2026-10-26 04:02 UTC106
            br#"{"rejected":["gone"]}"#
2006a11ce142atagen2026-10-26 04:02 UTC107
        );
2006a11ce142atagen2026-10-26 04:02 UTC108
    }
2006a11ce142atagen2026-10-26 04:02 UTC109
}