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