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}