telegram_bot_raw_ars/types/
reply_markup.rs

1use std::ops::Not;
2
3use crate::types::*;
4
5#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
6#[serde(untagged)]
7pub enum ReplyMarkup {
8    InlineKeyboardMarkup(InlineKeyboardMarkup),
9    ReplyKeyboardMarkup(ReplyKeyboardMarkup),
10    ReplyKeyboardRemove(ReplyKeyboardRemove),
11    ForceReply(ForceReply),
12}
13
14impl From<InlineKeyboardMarkup> for ReplyMarkup {
15    fn from(value: InlineKeyboardMarkup) -> ReplyMarkup {
16        ReplyMarkup::InlineKeyboardMarkup(value)
17    }
18}
19
20impl From<Vec<Vec<InlineKeyboardButton>>> for ReplyMarkup {
21    fn from(value: Vec<Vec<InlineKeyboardButton>>) -> ReplyMarkup {
22        ReplyMarkup::InlineKeyboardMarkup(value.into())
23    }
24}
25
26impl From<ReplyKeyboardMarkup> for ReplyMarkup {
27    fn from(value: ReplyKeyboardMarkup) -> ReplyMarkup {
28        ReplyMarkup::ReplyKeyboardMarkup(value)
29    }
30}
31
32impl From<ReplyKeyboardRemove> for ReplyMarkup {
33    fn from(value: ReplyKeyboardRemove) -> ReplyMarkup {
34        ReplyMarkup::ReplyKeyboardRemove(value)
35    }
36}
37
38impl From<ForceReply> for ReplyMarkup {
39    fn from(value: ForceReply) -> ReplyMarkup {
40        ReplyMarkup::ForceReply(value)
41    }
42}
43
44/// This object represents a custom keyboard with reply options.
45#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
46pub struct ReplyKeyboardMarkup {
47    keyboard: Vec<Vec<KeyboardButton>>,
48    #[serde(skip_serializing_if = "Not::not")]
49    resize_keyboard: bool,
50    #[serde(skip_serializing_if = "Not::not")]
51    one_time_keyboard: bool,
52    #[serde(skip_serializing_if = "Not::not")]
53    selective: bool,
54}
55
56impl ReplyKeyboardMarkup {
57    pub fn new() -> Self {
58        ReplyKeyboardMarkup {
59            keyboard: Vec::new(),
60            resize_keyboard: false,
61            one_time_keyboard: false,
62            selective: false,
63        }
64    }
65
66    fn init(rows: Vec<Vec<KeyboardButton>>) -> Self {
67        let mut keyboard = Self::new();
68        keyboard.keyboard = rows;
69        keyboard
70    }
71
72    /// Requests clients to resize the keyboard vertically for
73    /// optimal fit (e.g., make the keyboard smaller if there
74    /// are just two rows of buttons). Defaults to false, in which case
75    /// the custom keyboard is always of the same height as the app's standard keyboard.
76    pub fn resize_keyboard(&mut self) -> &mut Self {
77        self.resize_keyboard = true;
78        self
79    }
80
81    /// Requests clients to hide the keyboard as soon as it's been used.
82    /// The keyboard will still be available, but clients will automatically
83    /// display the usual letter-keyboard in the chat – the user can
84    /// press a special button in the input field to see the custom
85    /// keyboard again. Defaults to false.
86    pub fn one_time_keyboard(&mut self) -> &mut Self {
87        self.one_time_keyboard = true;
88        self
89    }
90
91    /// Use this method if you want to force reply from specific users only.
92    /// Targets: 1) users that are @mentioned in the text of
93    /// the Message object; 2) if the bot's message is a reply (has reply_to_message_id),
94    /// sender of the original message.
95    pub fn selective(&mut self) -> &mut Self {
96        self.selective = true;
97        self
98    }
99
100    pub fn add_row(&mut self, row: Vec<KeyboardButton>) -> &mut Vec<KeyboardButton> {
101        self.keyboard.push(row);
102        self.keyboard.last_mut().unwrap()
103    }
104
105    pub fn add_empty_row(&mut self) -> &mut Vec<KeyboardButton> {
106        self.add_row(Default::default())
107    }
108}
109
110impl From<Vec<Vec<KeyboardButton>>> for ReplyKeyboardMarkup {
111    fn from(value: Vec<Vec<KeyboardButton>>) -> Self {
112        Self::init(value)
113    }
114}
115
116/// This object represents one button of the reply keyboard.
117#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
118pub struct KeyboardButton {
119    text: String,
120    #[serde(skip_serializing_if = "Not::not")]
121    request_contact: bool,
122    #[serde(skip_serializing_if = "Not::not")]
123    request_location: bool,
124}
125
126impl KeyboardButton {
127    pub fn new<S: AsRef<str>>(text: S) -> Self {
128        Self {
129            text: text.as_ref().to_string(),
130            request_contact: false,
131            request_location: false,
132        }
133    }
134
135    /// The user's phone number will be sent as a contact when the
136    /// button is pressed. Available in private chats only
137    pub fn request_contact(&mut self) -> &mut Self {
138        self.request_location = false;
139        self.request_contact = true;
140        self
141    }
142
143    /// The user's current location will be sent when the
144    /// button is pressed. Available in private chats only
145    pub fn request_location(&mut self) -> &mut Self {
146        self.request_contact = false;
147        self.request_location = true;
148        self
149    }
150}
151
152impl<'a> From<&'a str> for KeyboardButton {
153    fn from(value: &'a str) -> KeyboardButton {
154        KeyboardButton::new(value)
155    }
156}
157
158impl From<String> for KeyboardButton {
159    fn from(value: String) -> KeyboardButton {
160        KeyboardButton::new(value)
161    }
162}
163
164/// Upon receiving a message with this object, Telegram clients will remove
165/// the current custom keyboard and display the default letter-keyboard.
166/// By default, custom keyboards are displayed until a new keyboard is sent
167/// by a bot. An exception is made for one-time keyboards that are hidden
168/// immediately after the user presses a button (see ReplyKeyboardMarkup).
169#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
170pub struct ReplyKeyboardRemove {
171    remove_keyboard: True,
172    #[serde(skip_serializing_if = "Not::not")]
173    selective: bool,
174}
175
176impl ReplyKeyboardRemove {
177    pub fn new() -> Self {
178        Self {
179            remove_keyboard: True,
180            selective: false,
181        }
182    }
183
184    /// Use this method if you want to force reply from specific users only.
185    /// Targets: 1) users that are @mentioned in the text of
186    /// the Message object; 2) if the bot's message is a reply (has reply_to_message_id),
187    /// sender of the original message.
188    pub fn selective(&mut self) -> &mut Self {
189        self.selective = true;
190        self
191    }
192}
193
194/// This object represents an inline keyboard that appears right next to the message it belongs to.
195#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
196pub struct InlineKeyboardMarkup {
197    inline_keyboard: Vec<Vec<InlineKeyboardButton>>,
198}
199
200impl InlineKeyboardMarkup {
201    pub fn new() -> Self {
202        Self {
203            inline_keyboard: Default::default(),
204        }
205    }
206
207    fn init(inline_keyboard: Vec<Vec<InlineKeyboardButton>>) -> Self {
208        Self { inline_keyboard }
209    }
210
211    pub fn add_row(&mut self, row: Vec<InlineKeyboardButton>) -> &mut Vec<InlineKeyboardButton> {
212        self.inline_keyboard.push(row);
213        self.inline_keyboard.last_mut().unwrap()
214    }
215
216    pub fn add_empty_row(&mut self) -> &mut Vec<InlineKeyboardButton> {
217        self.add_row(Default::default())
218    }
219}
220
221impl From<Vec<Vec<InlineKeyboardButton>>> for InlineKeyboardMarkup {
222    fn from(value: Vec<Vec<InlineKeyboardButton>>) -> Self {
223        Self::init(value)
224    }
225}
226
227/// This object represents one button of an inline keyboard.
228#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
229pub struct InlineKeyboardButton {
230    text: String,
231    #[serde(flatten)]
232    kind: InlineKeyboardButtonKind,
233}
234
235impl InlineKeyboardButton {
236    /// Data to be sent in a callback query to the bot when button is pressed, 1-64 bytes
237    pub fn callback<T: AsRef<str>, C: AsRef<str>>(text: T, callback: C) -> Self {
238        Self {
239            text: text.as_ref().to_string(),
240            kind: InlineKeyboardButtonKind::CallbackData(callback.as_ref().to_string()),
241        }
242    }
243
244    /// HTTP or tg:// url to be opened when button is pressed
245    pub fn url<T: AsRef<str>, U: AsRef<str>>(text: T, url: U) -> Self {
246        Self {
247            text: text.as_ref().to_string(),
248            kind: InlineKeyboardButtonKind::Url(url.as_ref().to_string()),
249        }
250    }
251
252    /// Pressing the button will prompt the user to select one of their chats, open that chat and
253    /// insert the bot‘s username and the specified inline query in the input field. Can be empty,
254    /// in which case just the bot’s username will be inserted.
255    pub fn switch_inline_query<T: AsRef<str>, Q: AsRef<str>>(text: T, query: Q) -> Self {
256        Self {
257            text: text.as_ref().to_string(),
258            kind: InlineKeyboardButtonKind::SwitchInlineQuery(query.as_ref().to_string()),
259        }
260    }
261
262    /// Pressing the button will insert the bot‘s username and the specified inline query in the
263    /// current chat's input field. Can be empty, in which case just the bot’s username will be
264    /// inserted.
265    pub fn switch_inline_query_current_chat<T: AsRef<str>, Q: AsRef<str>>(
266        text: T,
267        query: Q,
268    ) -> Self {
269        Self {
270            text: text.as_ref().to_string(),
271            kind: InlineKeyboardButtonKind::SwitchInlineQueryCurrentChat(
272                query.as_ref().to_string(),
273            ),
274        }
275    }
276}
277
278#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
279pub enum InlineKeyboardButtonKind {
280    #[serde(rename = "url")]
281    Url(String), // TODO(knsd): Url?
282    #[serde(rename = "callback_data")]
283    CallbackData(String), // TODO(knsd) Validate size?
284    #[serde(rename = "switch_inline_query")]
285    SwitchInlineQuery(String),
286    #[serde(rename = "switch_inline_query_current_chat")]
287    SwitchInlineQueryCurrentChat(String),
288    // #[serde(rename = "callback_game")]
289    //  CallbackGame(CallbackGame),
290    // #[serde(rename = "pay")]
291    //  Pay,
292    // #[serde(rename = "login_url")]
293    //  LoginUrl(LoginUrl),
294}
295
296/// Upon receiving a message with this object, Telegram clients will
297/// display a reply interface to the user (act as if the user has
298/// selected the bot‘s message and tapped ’Reply'). This can be
299/// extremely useful if you want to create user-friendly step-by-step
300/// interfaces without having to sacrifice privacy mod
301#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
302pub struct ForceReply {
303    force_reply: True,
304    #[serde(skip_serializing_if = "Not::not")]
305    selective: bool,
306}
307
308impl ForceReply {
309    pub fn new() -> Self {
310        Self {
311            force_reply: True,
312            selective: false,
313        }
314    }
315
316    /// Use this method if you want to force reply from specific users only.
317    /// Targets: 1) users that are @mentioned in the text of
318    /// the Message object; 2) if the bot's message is a reply (has reply_to_message_id),
319    /// sender of the original message.
320    pub fn selective(&mut self) -> &mut Self {
321        self.selective = true;
322        self
323    }
324}