Skip to main content

ferogram_mtproto/
session.rs

1// Copyright (c) Ankit Chaubey <ankitchaubey.dev@gmail.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3//
4// ferogram: async Telegram MTProto client in Rust
5// https://github.com/ankit-chaubey/ferogram
6//
7// Based on layer: https://github.com/ankit-chaubey/layer
8// Follows official Telegram client behaviour (tdesktop, TDLib).
9//
10// If you use or modify this code, keep this notice at the top of your file
11// and include the LICENSE-MIT or LICENSE-APACHE file from this repository:
12// https://github.com/ankit-chaubey/ferogram
13
14//! MTProto client session state.
15
16use ferogram_tl_types::RemoteCall;
17
18use crate::message::{Message, MessageId};
19
20/// Tracks per-connection MTProto session state.
21///
22/// A `Session` is cheap to create and can be reset on reconnect.
23///
24/// # Example
25///
26/// ```rust
27/// use ferogram_mtproto::Session;
28/// use ferogram_tl_types::functions;
29///
30/// let mut session = Session::new();
31/// // let msg = session.pack(&my_request);
32/// // send(msg.to_plaintext_bytes()).await?;
33/// ```
34pub struct Session {
35    /// Monotonically increasing counter used to generate unique message IDs.
36    msg_counter: u32,
37    /// The sequence number for the next message.
38    /// Even for content-unrelated messages, odd for content-related (RPC calls).
39    seq_no: i32,
40}
41
42impl Session {
43    /// Create a fresh session.
44    pub fn new() -> Self {
45        Self {
46            msg_counter: 0,
47            seq_no: 0,
48        }
49    }
50
51    /// Allocate a new message ID.
52    pub fn next_msg_id(&mut self) -> MessageId {
53        self.msg_counter = self.msg_counter.wrapping_add(1);
54        MessageId::generate(self.msg_counter)
55    }
56
57    /// Return the next sequence number for a content-related message (RPC call).
58    ///
59    /// Increments by 2 after each call so that even slots remain available
60    /// for content-unrelated messages (acks, pings, etc.).
61    pub fn next_seq_no(&mut self) -> i32 {
62        let n = self.seq_no;
63        self.seq_no += 2;
64        n | 1 // odd = content-related
65    }
66
67    /// Return the next sequence number for a content-*un*related message.
68    pub fn next_seq_no_unrelated(&mut self) -> i32 {
69        let n = self.seq_no;
70        n & !1 // even = content-unrelated (don't increment)
71    }
72
73    /// Serialize an RPC function into a [`Message`] ready to send.
74    ///
75    /// The message body is just the TL-serialized `call`; the surrounding
76    /// transport framing (auth_key_id, etc.) is applied in [`Message::to_plaintext_bytes`].
77    pub fn pack<R: RemoteCall>(&mut self, call: &R) -> Message {
78        let id = self.next_msg_id();
79        let seq_no = self.next_seq_no();
80        let body = call.to_bytes();
81        Message::plaintext(id, seq_no, body)
82    }
83}
84
85impl Default for Session {
86    fn default() -> Self {
87        Self::new()
88    }
89}