layer_mtproto/message.rs
1//! MTProto message framing types.
2
3use std::time::{SystemTime, UNIX_EPOCH};
4
5/// A 64-bit MTProto message identifier.
6///
7/// Per the spec: the lower 32 bits are derived from the current Unix time;
8/// the upper 32 bits are a monotonically increasing counter within the second.
9/// The least significant two bits must be zero for client messages.
10#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
11pub struct MessageId(pub u64);
12
13impl MessageId {
14 /// Generate a new message ID using the system clock.
15 ///
16 /// Call this from the session rather than directly so that the
17 /// counter is properly sequenced.
18 pub(crate) fn generate(counter: u32) -> Self {
19 let unix_secs = SystemTime::now()
20 .duration_since(UNIX_EPOCH)
21 .unwrap_or_default()
22 .as_secs();
23 // Lower 32 bits = seconds, upper 32 bits = intra-second counter × 4
24 // (the two LSBs must be 0b00 for client messages)
25 let id = (unix_secs << 32) | (u64::from(counter) << 2);
26 Self(id)
27 }
28}
29
30/// A framed MTProto message ready to be sent.
31#[derive(Debug)]
32pub struct Message {
33 /// Unique identifier for this message.
34 pub id: MessageId,
35 /// Session-scoped sequence number (even for content-unrelated, odd for content-related).
36 pub seq_no: i32,
37 /// The serialized TL body (constructor ID + fields).
38 pub body: Vec<u8>,
39}
40
41impl Message {
42 /// Construct a new plaintext message (used before key exchange).
43 pub fn plaintext(id: MessageId, seq_no: i32, body: Vec<u8>) -> Self {
44 Self { id, seq_no, body }
45 }
46
47 /// Serialize the message into the plaintext wire format:
48 ///
49 /// ```text
50 /// auth_key_id:long (0 for plaintext)
51 /// message_id:long
52 /// message_data_length:int
53 /// message_data:bytes
54 /// ```
55 pub fn to_plaintext_bytes(&self) -> Vec<u8> {
56 let mut buf = Vec::with_capacity(8 + 8 + 4 + self.body.len());
57 buf.extend(0i64.to_le_bytes()); // auth_key_id = 0
58 buf.extend(self.id.0.to_le_bytes()); // message_id
59 buf.extend((self.body.len() as u32).to_le_bytes()); // length
60 buf.extend(&self.body);
61 buf
62 }
63}