Skip to main content

ferogram_mtproto/
session.rs

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