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