Skip to main content

ferogram_mtproto/
session.rs

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