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}