Skip to main content

layer_mtproto/
message.rs

1// Copyright (c) Ankit Chaubey <ankitchaubey.dev@gmail.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4// NOTE:
5// The "Layer" project is no longer maintained or supported.
6// Its original purpose for personal SDK/APK experimentation and learning
7// has been fulfilled.
8//
9// Please use Ferogram instead:
10// https://github.com/ankit-chaubey/ferogram
11// Ferogram will receive future updates and development, although progress
12// may be slower.
13//
14// Ferogram is an async Telegram MTProto client library written in Rust.
15// Its implementation follows the behaviour of the official Telegram clients,
16// particularly Telegram Desktop and TDLib, and aims to provide a clean and
17// modern async interface for building Telegram clients and tools.
18
19//! MTProto message framing types.
20
21use std::time::{SystemTime, UNIX_EPOCH};
22
23/// A 64-bit MTProto message identifier.
24///
25/// Per the spec: the lower 32 bits are derived from the current Unix time;
26/// the upper 32 bits are a monotonically increasing counter within the second.
27/// The least significant two bits must be zero for client messages.
28#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
29pub struct MessageId(pub u64);
30
31impl MessageId {
32    /// Generate a new message ID using the system clock.
33    ///
34    /// Call this from the session rather than directly so that the
35    /// counter is properly sequenced.
36    pub(crate) fn generate(counter: u32) -> Self {
37        let unix_secs = SystemTime::now()
38            .duration_since(UNIX_EPOCH)
39            .unwrap_or_default()
40            .as_secs();
41        // Lower 32 bits = seconds, upper 32 bits = intra-second counter × 4
42        // (the two LSBs must be 0b00 for client messages)
43        let id = (unix_secs << 32) | (u64::from(counter) << 2);
44        Self(id)
45    }
46}
47
48/// A framed MTProto message ready to be sent.
49#[derive(Debug)]
50pub struct Message {
51    /// Unique identifier for this message.
52    pub id: MessageId,
53    /// Session-scoped sequence number (even for content-unrelated, odd for content-related).
54    pub seq_no: i32,
55    /// The serialized TL body (constructor ID + fields).
56    pub body: Vec<u8>,
57}
58
59impl Message {
60    /// Construct a new plaintext message (used before key exchange).
61    pub fn plaintext(id: MessageId, seq_no: i32, body: Vec<u8>) -> Self {
62        Self { id, seq_no, body }
63    }
64
65    /// Serialize the message into the plaintext wire format:
66    ///
67    /// ```text
68    /// auth_key_id:long  (0 for plaintext)
69    /// message_id:long
70    /// message_data_length:int
71    /// message_data:bytes
72    /// ```
73    pub fn to_plaintext_bytes(&self) -> Vec<u8> {
74        let mut buf = Vec::with_capacity(8 + 8 + 4 + self.body.len());
75        buf.extend(0i64.to_le_bytes()); // auth_key_id = 0
76        buf.extend(self.id.0.to_le_bytes()); // message_id
77        buf.extend((self.body.len() as u32).to_le_bytes()); // length
78        buf.extend(&self.body);
79        buf
80    }
81}