tgbot/types/callback/
mod.rs

1use std::{error::Error, fmt};
2
3use serde::{Deserialize, Serialize, de::DeserializeOwned};
4use serde_json::Error as JsonError;
5
6use crate::{
7    api::{Method, Payload},
8    types::{Integer, MaybeInaccessibleMessage, User},
9};
10
11#[cfg(test)]
12mod tests;
13
14/// Represents an incoming callback query from a callback button in an inline keyboard.
15///
16/// If the button that originated the query was attached to a message sent by the bot,
17/// the field message will be present.
18///
19/// If the button was attached to a message sent via the bot (in inline mode),
20/// the field `inline_message_id` will be present.
21///
22/// Exactly one of the fields data or `game_short_name` will be present.
23#[serde_with::skip_serializing_none]
24#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
25pub struct CallbackQuery {
26    /// Sender of the query.
27    pub from: User,
28    /// Unique identifier of the query.
29    pub id: String,
30    /// Global identifier, uniquely corresponding
31    /// to the chat to which the message with the
32    /// callback button was sent.
33    ///
34    /// Useful for high scores in games.
35    pub chat_instance: Option<String>,
36    /// Data associated with the callback button.
37    ///
38    /// Be aware that a bad client can send arbitrary data in this field.
39    pub data: Option<String>,
40    /// Short name of a Game to be returned,
41    /// serves as the unique identifier for the game.
42    pub game_short_name: Option<String>,
43    /// Identifier of the message sent via the bot
44    /// in inline mode, that originated the query.
45    pub inline_message_id: Option<String>,
46    /// Message with the callback button that originated the query.
47    ///
48    /// Note that message content and message date
49    /// will not be available if the message is too old.
50    pub message: Option<MaybeInaccessibleMessage>,
51}
52
53impl CallbackQuery {
54    /// Creates a new `CallbackQuery`.
55    ///
56    /// # Arguments
57    ///
58    /// * `id` - Unique identifier of the query.
59    /// * `from` - Sender of the query.
60    pub fn new<T>(id: T, from: User) -> Self
61    where
62        T: Into<String>,
63    {
64        Self {
65            from,
66            id: id.into(),
67            chat_instance: None,
68            data: None,
69            game_short_name: None,
70            inline_message_id: None,
71            message: None,
72        }
73    }
74
75    /// Parses callback data using [`serde_json`].
76    pub fn parse_data<T: DeserializeOwned>(&self) -> Result<Option<T>, CallbackQueryError> {
77        if let Some(ref data) = self.data {
78            serde_json::from_str(data)
79                .map(Some)
80                .map_err(CallbackQueryError::ParseJsonData)
81        } else {
82            Ok(None)
83        }
84    }
85
86    /// Sets a new chat instance.
87    ///
88    /// # Arguments
89    ///
90    /// * `value` - Global identifier, uniquely corresponding to the chat.
91    pub fn with_chat_instance<T>(mut self, value: T) -> Self
92    where
93        T: Into<String>,
94    {
95        self.chat_instance = Some(value.into());
96        self
97    }
98
99    /// Sets a new data for callback button.
100    ///
101    /// # Arguments
102    ///
103    /// * `value` - Data associated with the callback button.
104    pub fn with_data<T>(mut self, value: T) -> Self
105    where
106        T: Into<String>,
107    {
108        self.data = Some(value.into());
109        self
110    }
111
112    /// Sets a new short name for a game.
113    ///
114    /// # Arguments
115    ///
116    /// * `value` - Short name of the game.
117    pub fn with_game_short_name<T>(mut self, value: T) -> Self
118    where
119        T: Into<String>,
120    {
121        self.game_short_name = Some(value.into());
122        self
123    }
124
125    /// Sets a new inline message ID.
126    ///
127    /// # Arguments
128    ///
129    /// * `value` - Identifier of the message sent via the bot in inline mode.
130    pub fn with_inline_message_id<T>(mut self, value: T) -> Self
131    where
132        T: Into<String>,
133    {
134        self.inline_message_id = Some(value.into());
135        self
136    }
137
138    /// Sets a new message.
139    ///
140    /// # Arguments
141    ///
142    /// * `value` - Message with the callback button that originated the query.
143    pub fn with_message(mut self, value: MaybeInaccessibleMessage) -> Self {
144        self.message = Some(value);
145        self
146    }
147}
148
149/// Represents an error that can occur while parsing data from a callback query.
150#[derive(Debug)]
151pub enum CallbackQueryError {
152    /// Failed to parse JSON data
153    ParseJsonData(JsonError),
154}
155
156impl Error for CallbackQueryError {
157    fn source(&self) -> Option<&(dyn Error + 'static)> {
158        match self {
159            Self::ParseJsonData(err) => Some(err),
160        }
161    }
162}
163
164impl fmt::Display for CallbackQueryError {
165    fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
166        match self {
167            Self::ParseJsonData(err) => write!(out, "failed to parse callback query data: {}", err),
168        }
169    }
170}
171
172/// Sends an answer to a callback query sent from an inline keyboard.
173///
174/// The answer will be displayed to the user as a notification at the top of the chat screen or as an alert.
175///
176/// Alternatively, the user can be redirected to the specified Game URL.
177///
178/// For this option to work, you must first create a game for your bot via Bot Father and accept the terms.
179///
180/// Otherwise, you may use links like `t.me/your_bot?start=XXX` that open your bot with a parameter.
181#[serde_with::skip_serializing_none]
182#[derive(Clone, Debug, Serialize)]
183pub struct AnswerCallbackQuery {
184    callback_query_id: String,
185    cache_time: Option<Integer>,
186    show_alert: Option<bool>,
187    text: Option<String>,
188    url: Option<String>,
189}
190
191impl AnswerCallbackQuery {
192    /// Creates a new `AnswerCallbackQuery`.
193    ///
194    /// # Arguments
195    ///
196    /// * `callback_query_id` - Unique identifier of the query to be answered.
197    pub fn new<T>(callback_query_id: T) -> Self
198    where
199        T: Into<String>,
200    {
201        Self {
202            callback_query_id: callback_query_id.into(),
203            cache_time: None,
204            show_alert: None,
205            text: None,
206            url: None,
207        }
208    }
209
210    /// Sets a new cache time.
211    ///
212    /// # Arguments
213    ///
214    /// * `value` - The maximum amount of time in seconds that the result
215    ///   of the callback query may be cached client-side;
216    ///   telegram apps will support caching starting in version 3.14;
217    ///   default - 0.
218    pub fn with_cache_time(mut self, value: Integer) -> Self {
219        self.cache_time = Some(value);
220        self
221    }
222
223    /// Sets a new value for the `short_alert` flag.
224    ///
225    /// # Arguments
226    ///
227    /// * `value` - An alert will be shown by the client instead
228    ///   of a notification at the top of the chat screen;
229    ///   default - `false`.
230    pub fn with_show_alert(mut self, value: bool) -> Self {
231        self.show_alert = Some(value);
232        self
233    }
234
235    /// Sets a new text.
236    ///
237    /// # Arguments
238    ///
239    /// * `value` - Text of the notification;
240    ///   if not specified, nothing will be shown to the user;
241    ///   0-200 characters.
242    pub fn with_text<T>(mut self, value: T) -> Self
243    where
244        T: Into<String>,
245    {
246        self.text = Some(value.into());
247        self
248    }
249
250    /// Sets a new URL.
251    ///
252    /// # Arguments
253    ///
254    /// * `value` - URL that will be opened by the user's client.
255    ///
256    /// If you have created a game and accepted the conditions via Bot Father,
257    /// specify the URL that opens your game – note that this will only work
258    /// if the query comes from a callback game button.
259    ///
260    /// Otherwise, you may use links like `t.me/your_bot?start=XXX`
261    /// that open your bot with a parameter.
262    pub fn with_url<T>(mut self, value: T) -> Self
263    where
264        T: Into<String>,
265    {
266        self.url = Some(value.into());
267        self
268    }
269}
270
271impl Method for AnswerCallbackQuery {
272    type Response = bool;
273
274    fn into_payload(self) -> Payload {
275        Payload::json("answerCallbackQuery", self)
276    }
277}