1use serde::{Deserialize, Serialize};
2
3use crate::message::WebAppInfo;
4
5#[derive(Debug, Clone, Default, Serialize, Deserialize)]
7pub struct InlineKeyboardMarkup {
8 pub inline_keyboard: Vec<Vec<InlineKeyboardButton>>,
11}
12
13impl InlineKeyboardMarkup {
14 #[must_use]
16 pub fn new() -> Self {
17 Self::default()
18 }
19
20 #[must_use]
22 pub fn row(mut self, row: Vec<InlineKeyboardButton>) -> Self {
23 self.inline_keyboard.push(row);
24 self
25 }
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct InlineKeyboardButton {
31 pub text: String,
33
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub url: Option<String>,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
40 pub callback_data: Option<String>,
41
42 #[serde(skip_serializing_if = "Option::is_none")]
44 pub web_app: Option<WebAppInfo>,
45
46 #[serde(skip_serializing_if = "Option::is_none")]
48 pub login_url: Option<LoginUrl>,
49
50 #[serde(skip_serializing_if = "Option::is_none")]
52 pub switch_inline_query: Option<String>,
53
54 #[serde(skip_serializing_if = "Option::is_none")]
56 pub switch_inline_query_current_chat: Option<String>,
57
58 #[serde(skip_serializing_if = "Option::is_none")]
60 pub switch_inline_query_chosen_chat: Option<SwitchInlineQueryChosenChat>,
61
62 #[serde(skip_serializing_if = "Option::is_none")]
64 pub copy_text: Option<CopyTextButton>,
65
66 #[serde(skip_serializing_if = "Option::is_none")]
68 pub callback_game: Option<serde_json::Value>,
69
70 #[serde(skip_serializing_if = "Option::is_none")]
72 pub pay: Option<bool>,
73
74 #[serde(skip_serializing_if = "Option::is_none")]
76 pub icon_custom_emoji_id: Option<String>,
77
78 #[serde(skip_serializing_if = "Option::is_none")]
80 pub style: Option<ButtonStyle>,
81}
82
83impl InlineKeyboardButton {
84 #[must_use]
86 pub fn callback(text: impl Into<String>, data: impl Into<String>) -> Self {
87 Self {
88 text: text.into(),
89 callback_data: Some(data.into()),
90 url: None,
91 web_app: None,
92 login_url: None,
93 switch_inline_query: None,
94 switch_inline_query_current_chat: None,
95 switch_inline_query_chosen_chat: None,
96 copy_text: None,
97 callback_game: None,
98 pay: None,
99 icon_custom_emoji_id: None,
100 style: None,
101 }
102 }
103
104 #[must_use]
106 pub fn url(text: impl Into<String>, url: impl Into<String>) -> Self {
107 Self {
108 text: text.into(),
109 url: Some(url.into()),
110 callback_data: None,
111 web_app: None,
112 login_url: None,
113 switch_inline_query: None,
114 switch_inline_query_current_chat: None,
115 switch_inline_query_chosen_chat: None,
116 copy_text: None,
117 callback_game: None,
118 pay: None,
119 icon_custom_emoji_id: None,
120 style: None,
121 }
122 }
123
124 #[must_use]
126 pub fn web_app(text: impl Into<String>, url: impl Into<String>) -> Self {
127 Self {
128 text: text.into(),
129 web_app: Some(WebAppInfo { url: url.into() }),
130 url: None,
131 callback_data: None,
132 login_url: None,
133 switch_inline_query: None,
134 switch_inline_query_current_chat: None,
135 switch_inline_query_chosen_chat: None,
136 copy_text: None,
137 callback_game: None,
138 pay: None,
139 icon_custom_emoji_id: None,
140 style: None,
141 }
142 }
143}
144
145#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
146#[serde(rename_all = "snake_case")]
148pub enum ButtonStyle {
149 Danger,
151 Success,
153 Primary,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct LoginUrl {
160 pub url: String,
162 #[serde(skip_serializing_if = "Option::is_none")]
164 pub forward_text: Option<String>,
165 #[serde(skip_serializing_if = "Option::is_none")]
167 pub bot_username: Option<String>,
168 #[serde(skip_serializing_if = "Option::is_none")]
170 pub request_write_access: Option<bool>,
171}
172
173#[derive(Debug, Clone, Default, Serialize, Deserialize)]
175pub struct SwitchInlineQueryChosenChat {
176 #[serde(skip_serializing_if = "Option::is_none")]
178 pub query: Option<String>,
179 #[serde(skip_serializing_if = "Option::is_none")]
181 pub allow_user_chats: Option<bool>,
182 #[serde(skip_serializing_if = "Option::is_none")]
184 pub allow_bot_chats: Option<bool>,
185 #[serde(skip_serializing_if = "Option::is_none")]
187 pub allow_group_chats: Option<bool>,
188 #[serde(skip_serializing_if = "Option::is_none")]
190 pub allow_channel_chats: Option<bool>,
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct CopyTextButton {
196 pub text: String,
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize)]
202pub struct ReplyKeyboardMarkup {
203 pub keyboard: Vec<Vec<KeyboardButton>>,
205
206 #[serde(skip_serializing_if = "Option::is_none")]
208 pub is_persistent: Option<bool>,
209
210 #[serde(skip_serializing_if = "Option::is_none")]
212 pub resize_keyboard: Option<bool>,
213
214 #[serde(skip_serializing_if = "Option::is_none")]
216 pub one_time_keyboard: Option<bool>,
217
218 #[serde(skip_serializing_if = "Option::is_none")]
220 pub input_field_placeholder: Option<String>,
221
222 #[serde(skip_serializing_if = "Option::is_none")]
224 pub selective: Option<bool>,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct KeyboardButton {
230 pub text: String,
232
233 #[serde(skip_serializing_if = "Option::is_none")]
235 pub request_users: Option<KeyboardButtonRequestUsers>,
236
237 #[serde(skip_serializing_if = "Option::is_none")]
239 pub request_chat: Option<KeyboardButtonRequestChat>,
240
241 #[serde(skip_serializing_if = "Option::is_none")]
243 pub request_contact: Option<bool>,
244
245 #[serde(skip_serializing_if = "Option::is_none")]
247 pub request_location: Option<bool>,
248
249 #[serde(skip_serializing_if = "Option::is_none")]
251 pub request_poll: Option<KeyboardButtonPollType>,
252
253 #[serde(skip_serializing_if = "Option::is_none")]
255 pub web_app: Option<WebAppInfo>,
256
257 #[serde(skip_serializing_if = "Option::is_none")]
259 pub request_managed_bot: Option<KeyboardButtonRequestManagedBot>,
260
261 #[serde(skip_serializing_if = "Option::is_none")]
263 pub icon_custom_emoji_id: Option<String>,
264
265 #[serde(skip_serializing_if = "Option::is_none")]
267 pub style: Option<ButtonStyle>,
268}
269
270impl KeyboardButton {
271 #[must_use]
273 pub fn text(label: impl Into<String>) -> Self {
274 Self {
275 text: label.into(),
276 request_users: None,
277 request_chat: None,
278 request_contact: None,
279 request_location: None,
280 request_poll: None,
281 web_app: None,
282 request_managed_bot: None,
283 icon_custom_emoji_id: None,
284 style: None,
285 }
286 }
287
288 #[must_use]
290 pub fn request_contact(label: impl Into<String>) -> Self {
291 Self {
292 request_contact: Some(true),
293 ..Self::text(label)
294 }
295 }
296
297 #[must_use]
299 pub fn request_location(label: impl Into<String>) -> Self {
300 Self {
301 request_location: Some(true),
302 ..Self::text(label)
303 }
304 }
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize)]
309pub struct KeyboardButtonRequestUsers {
310 pub request_id: i32,
312 #[serde(skip_serializing_if = "Option::is_none")]
314 pub user_is_bot: Option<bool>,
315 #[serde(skip_serializing_if = "Option::is_none")]
317 pub user_is_premium: Option<bool>,
318 #[serde(skip_serializing_if = "Option::is_none")]
320 pub max_quantity: Option<u8>,
321 #[serde(skip_serializing_if = "Option::is_none")]
323 pub request_name: Option<bool>,
324 #[serde(skip_serializing_if = "Option::is_none")]
326 pub request_username: Option<bool>,
327 #[serde(skip_serializing_if = "Option::is_none")]
329 pub request_photo: Option<bool>,
330}
331
332#[derive(Debug, Clone, Serialize, Deserialize)]
334pub struct KeyboardButtonRequestChat {
335 pub request_id: i32,
337 pub chat_is_channel: bool,
339 #[serde(skip_serializing_if = "Option::is_none")]
341 pub chat_is_forum: Option<bool>,
342 #[serde(skip_serializing_if = "Option::is_none")]
344 pub chat_has_username: Option<bool>,
345 #[serde(skip_serializing_if = "Option::is_none")]
347 pub chat_is_created: Option<bool>,
348 #[serde(skip_serializing_if = "Option::is_none")]
350 pub user_administrator_rights: Option<serde_json::Value>,
351 #[serde(skip_serializing_if = "Option::is_none")]
353 pub bot_administrator_rights: Option<serde_json::Value>,
354 #[serde(skip_serializing_if = "Option::is_none")]
356 pub bot_is_member: Option<bool>,
357 #[serde(skip_serializing_if = "Option::is_none")]
359 pub request_title: Option<bool>,
360 #[serde(skip_serializing_if = "Option::is_none")]
362 pub request_username: Option<bool>,
363 #[serde(skip_serializing_if = "Option::is_none")]
365 pub request_photo: Option<bool>,
366}
367
368#[derive(Debug, Clone, Serialize, Deserialize)]
369pub struct KeyboardButtonPollType {
371 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
373 pub kind: Option<String>,
374}
375
376#[derive(Debug, Clone, Default, Serialize, Deserialize)]
378pub struct KeyboardButtonRequestManagedBot {
379 #[serde(skip_serializing_if = "Option::is_none")]
381 pub bot_is_active: Option<bool>,
382}
383
384#[derive(Debug, Clone, Serialize, Deserialize)]
386pub struct ReplyKeyboardRemove {
387 pub remove_keyboard: bool,
389 #[serde(skip_serializing_if = "Option::is_none")]
391 pub selective: Option<bool>,
392}
393
394#[derive(Debug, Clone, Serialize, Deserialize)]
396pub struct ForceReply {
397 pub force_reply: bool,
399 #[serde(skip_serializing_if = "Option::is_none")]
401 pub input_field_placeholder: Option<String>,
402 #[serde(skip_serializing_if = "Option::is_none")]
404 pub selective: Option<bool>,
405}
406
407#[derive(Debug, Clone, Serialize, Deserialize)]
409#[serde(untagged)]
410pub enum ReplyMarkup {
411 InlineKeyboard(InlineKeyboardMarkup),
413 ReplyKeyboard(ReplyKeyboardMarkup),
415 Remove(ReplyKeyboardRemove),
417 ForceReply(ForceReply),
419}
420
421#[derive(Debug, Clone, Serialize, Deserialize)]
423#[serde(tag = "type", rename_all = "snake_case")]
424pub enum MenuButton {
425 Commands,
427 WebApp {
429 text: String,
431 web_app: WebAppInfo,
433 },
434 Default,
436}