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