xmtp_proto/types/
group_message.rs

1use super::{Cursor, GroupId};
2use crate::{ConversionError, types::GlobalCursor};
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 depends_on: GlobalCursor,
28}
29
30impl GroupMessage {
31    pub fn builder() -> GroupMessageBuilder {
32        GroupMessageBuilder::default()
33    }
34
35    pub fn is_commit(&self) -> bool {
36        self.message.content_type() == ContentType::Commit
37    }
38
39    pub fn timestamp(&self) -> i64 {
40        self.created_ns
41            .timestamp_nanos_opt()
42            .expect("timestamp out of range for i64, are we in 2262 A.D?")
43    }
44
45    pub fn originator_id(&self) -> u32 {
46        self.cursor.originator_id
47    }
48
49    pub fn sequence_id(&self) -> u64 {
50        self.cursor.sequence_id
51    }
52}
53
54#[cfg(any(test, feature = "test-utils"))]
55impl xmtp_common::Generate for GroupMessage {
56    fn generate() -> Self {
57        GroupMessage {
58            cursor: Default::default(),
59            created_ns: chrono::DateTime::from_timestamp_nanos(xmtp_common::rand_i64()),
60            group_id: GroupId::generate(),
61            message: openmls::prelude::PublicMessage::generate().into(),
62            sender_hmac: xmtp_common::rand_vec::<2>(),
63            should_push: true,
64            payload_hash: xmtp_common::rand_vec::<32>(),
65            depends_on: GlobalCursor::default(),
66        }
67    }
68}
69
70impl std::fmt::Display for GroupMessage {
71    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72        let s = format!(
73            "GroupMessage {{ cursor {}, depends on {}, created at {:10}, group {:16} }}",
74            self.cursor,
75            self.depends_on,
76            self.created_ns.time().format("%H:%M:%S%.6f").to_string(),
77            self.group_id
78        );
79        write!(f, "{}", s)
80    }
81}
82
83#[cfg(test)]
84mod test {
85    use openmls::prelude::ContentType;
86    use xmtp_common::Generate;
87
88    use super::*;
89
90    #[xmtp_common::test]
91    fn test_is_commit() {
92        let group_message = GroupMessage::generate();
93        assert_eq!(
94            group_message.is_commit(),
95            group_message.message.content_type() == ContentType::Commit
96        );
97    }
98
99    #[xmtp_common::test]
100    fn test_timestamp() {
101        let test_time = chrono::Utc::now();
102        let mut group_message = GroupMessage::generate();
103        group_message.created_ns = test_time;
104        assert_eq!(
105            group_message.timestamp(),
106            test_time.timestamp_nanos_opt().unwrap()
107        );
108    }
109}