miyabi_telegram/
types.rs

1//! Telegram Bot API types
2//!
3//! Type definitions for Telegram Bot API objects and requests.
4//! Based on: https://core.telegram.org/bots/api
5
6use serde::{Deserialize, Serialize};
7
8/// Telegram Update object
9/// https://core.telegram.org/bots/api#update
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct Update {
12    /// Unique update identifier
13    pub update_id: i64,
14
15    /// Optional. New incoming message
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub message: Option<Message>,
18
19    /// Optional. Callback query from inline keyboard
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub callback_query: Option<CallbackQuery>,
22}
23
24/// Telegram Message object
25/// https://core.telegram.org/bots/api#message
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct Message {
28    /// Unique message identifier
29    pub message_id: i64,
30
31    /// Sender (can be empty for channels)
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub from: Option<User>,
34
35    /// Chat the message belongs to
36    pub chat: Chat,
37
38    /// Date the message was sent (Unix time)
39    pub date: i64,
40
41    /// Optional. Text of the message
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub text: Option<String>,
44}
45
46/// Telegram User object
47/// https://core.telegram.org/bots/api#user
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct User {
50    /// Unique identifier for this user or bot
51    pub id: i64,
52
53    /// True if this user is a bot
54    pub is_bot: bool,
55
56    /// User's or bot's first name
57    pub first_name: String,
58
59    /// Optional. User's or bot's last name
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub last_name: Option<String>,
62
63    /// Optional. User's or bot's username
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub username: Option<String>,
66
67    /// Optional. IETF language tag of the user's language
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub language_code: Option<String>,
70}
71
72/// Telegram Chat object
73/// https://core.telegram.org/bots/api#chat
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct Chat {
76    /// Unique identifier for this chat
77    pub id: i64,
78
79    /// Type of chat: "private", "group", "supergroup", "channel"
80    #[serde(rename = "type")]
81    pub chat_type: String,
82
83    /// Optional. Title for supergroups, channels and group chats
84    #[serde(skip_serializing_if = "Option::is_none")]
85    pub title: Option<String>,
86
87    /// Optional. Username for private chats, supergroups and channels
88    #[serde(skip_serializing_if = "Option::is_none")]
89    pub username: Option<String>,
90
91    /// Optional. First name of the other party in a private chat
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub first_name: Option<String>,
94}
95
96/// Telegram CallbackQuery object
97/// https://core.telegram.org/bots/api#callbackquery
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct CallbackQuery {
100    /// Unique identifier for this query
101    pub id: String,
102
103    /// Sender
104    pub from: User,
105
106    /// Optional. Message with the callback button
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub message: Option<Message>,
109
110    /// Optional. Data associated with the callback button
111    #[serde(skip_serializing_if = "Option::is_none")]
112    pub data: Option<String>,
113}
114
115/// Request to send a message
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct SendMessageRequest {
118    /// Unique identifier for the target chat
119    pub chat_id: i64,
120
121    /// Text of the message to be sent
122    pub text: String,
123
124    /// Optional. Parse mode: "Markdown" or "HTML"
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub parse_mode: Option<String>,
127
128    /// Optional. Inline keyboard attached to the message
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub reply_markup: Option<InlineKeyboard>,
131}
132
133/// Inline keyboard markup
134/// https://core.telegram.org/bots/api#inlinekeyboardmarkup
135#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct InlineKeyboard {
137    /// Array of button rows
138    pub inline_keyboard: Vec<Vec<InlineKeyboardButton>>,
139}
140
141/// Inline keyboard button
142/// https://core.telegram.org/bots/api#inlinekeyboardbutton
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct InlineKeyboardButton {
145    /// Label text on the button
146    pub text: String,
147
148    /// Optional. Data to be sent in a callback query
149    #[serde(skip_serializing_if = "Option::is_none")]
150    pub callback_data: Option<String>,
151
152    /// Optional. HTTP or tg:// URL to be opened
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub url: Option<String>,
155}
156
157/// Response wrapper for API calls
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct ApiResponse<T> {
160    /// True if request was successful
161    pub ok: bool,
162
163    /// Result object (present if ok == true)
164    #[serde(skip_serializing_if = "Option::is_none")]
165    pub result: Option<T>,
166
167    /// Error description (present if ok == false)
168    #[serde(skip_serializing_if = "Option::is_none")]
169    pub description: Option<String>,
170}
171
172impl InlineKeyboard {
173    /// Create a new inline keyboard with rows
174    pub fn new(rows: Vec<Vec<InlineKeyboardButton>>) -> Self {
175        Self {
176            inline_keyboard: rows,
177        }
178    }
179
180    /// Create a simple keyboard with one row
181    pub fn single_row(buttons: Vec<InlineKeyboardButton>) -> Self {
182        Self::new(vec![buttons])
183    }
184}
185
186impl InlineKeyboardButton {
187    /// Create a button with callback data
188    pub fn callback(text: impl Into<String>, data: impl Into<String>) -> Self {
189        Self {
190            text: text.into(),
191            callback_data: Some(data.into()),
192            url: None,
193        }
194    }
195
196    /// Create a button with URL
197    pub fn url(text: impl Into<String>, url: impl Into<String>) -> Self {
198        Self {
199            text: text.into(),
200            callback_data: None,
201            url: Some(url.into()),
202        }
203    }
204}