Skip to main content

xmtp_proto/types/
group_message.rs

1use super::{Cursor, GroupId};
2use crate::ConversionError;
3use chrono::Utc;
4use derive_builder::Builder;
5use openmls::prelude::ContentType;
6
7/// A GroupMessage from the network
8#[derive(Clone, Builder, Debug)]
9#[builder(setter(into), build_fn(error = "ConversionError"), derive(Debug))]
10pub struct GroupMessage {
11    /// Cursor of this message
12    pub cursor: Cursor,
13    /// server timestamp indicating when this message was created
14    pub created_ns: chrono::DateTime<Utc>,
15    /// GroupId of the message
16    pub group_id: GroupId,
17    // MLS Group Message
18    pub message: openmls::framing::ProtocolMessage,
19    /// Sender HMAC key
20    pub sender_hmac: Vec<u8>,
21    /// Whether this message should result in a push notification
22    pub should_push: bool,
23    /// Payload hash of the message
24    /// TODO: make payload hash constant array
25    pub payload_hash: Vec<u8>,
26    #[builder(default)]
27    pub envelope_hash: Option<Vec<u8>>,
28    #[builder(default)]
29    pub expiry_ns: Option<u64>,
30}
31
32impl GroupMessage {
33    pub fn builder() -> GroupMessageBuilder {
34        GroupMessageBuilder::default()
35    }
36
37    pub fn is_commit(&self) -> bool {
38        self.message.content_type() == ContentType::Commit
39    }
40
41    pub fn timestamp(&self) -> i64 {
42        self.created_ns
43            .timestamp_nanos_opt()
44            .expect("timestamp out of range for i64, are we in 2262 A.D?")
45    }
46
47    pub fn sequence_id(&self) -> u64 {
48        self.cursor.0
49    }
50}
51
52#[cfg(any(test, feature = "test-utils"))]
53impl xmtp_common::Generate for GroupMessage {
54    fn generate() -> Self {
55        GroupMessage {
56            cursor: Default::default(),
57            created_ns: chrono::DateTime::from_timestamp_nanos(xmtp_common::rand_i64()),
58            group_id: GroupId::generate(),
59            message: openmls::prelude::PublicMessage::generate().into(),
60            sender_hmac: xmtp_common::rand_vec::<2>(),
61            should_push: true,
62            payload_hash: xmtp_common::rand_vec::<32>(),
63            envelope_hash: None,
64            expiry_ns: None,
65        }
66    }
67}
68
69impl std::fmt::Display for GroupMessage {
70    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71        let s = format!(
72            "GroupMessage {{ cursor {}, created at {:10}, group {:16} }}",
73            self.cursor,
74            self.created_ns.time().format("%H:%M:%S%.6f").to_string(),
75            self.group_id
76        );
77        write!(f, "{}", s)
78    }
79}
80
81#[cfg(test)]
82mod test {
83    use openmls::prelude::ContentType;
84    use xmtp_common::Generate;
85
86    use super::*;
87
88    #[xmtp_common::test]
89    fn test_is_commit() {
90        let group_message = GroupMessage::generate();
91        assert_eq!(
92            group_message.is_commit(),
93            group_message.message.content_type() == ContentType::Commit
94        );
95    }
96
97    #[xmtp_common::test]
98    fn test_timestamp() {
99        let test_time = chrono::Utc::now();
100        let mut group_message = GroupMessage::generate();
101        group_message.created_ns = test_time;
102        assert_eq!(
103            group_message.timestamp(),
104            test_time.timestamp_nanos_opt().unwrap()
105        );
106    }
107}