tgbot/types/game/mod.rs
1use serde::{Deserialize, Deserializer, Serialize, Serializer};
2
3use crate::{
4 api::{Method, Payload},
5 types::{
6 Animation,
7 EditMessageResult,
8 InlineKeyboardMarkup,
9 Integer,
10 Message,
11 PhotoSize,
12 ReplyParameters,
13 Text,
14 TextEntities,
15 User,
16 },
17};
18
19#[cfg(test)]
20mod tests;
21
22/// Represents a Game.
23///
24/// Use BotFather to create and edit games,
25/// their short names will act as unique identifiers.
26#[serde_with::skip_serializing_none]
27#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
28pub struct Game {
29 /// Description of the game.
30 pub description: String,
31 /// Photo that will be displayed in the game message in chats.
32 pub photo: Vec<PhotoSize>,
33 /// Title of the game.
34 pub title: String,
35 /// Animation that will be displayed in the game message in chats.
36 ///
37 /// Upload via BotFather.
38 pub animation: Option<Animation>,
39 /// Brief description or high scores included in the game message; 0-4096 characters.
40 ///
41 /// Can be automatically edited to include current high scores for
42 /// when the bot calls [`SetGameScore`],
43 /// or manually edited using [`crate::types::EditMessageText`].
44 #[serde(
45 flatten,
46 deserialize_with = "GameText::deserialize_value",
47 serialize_with = "GameText::serialize_value"
48 )]
49 pub text: Option<Text>,
50}
51
52impl Game {
53 /// Creates a new `Game`.
54 ///
55 /// # Arguments
56 ///
57 /// * `description` - Description of the game.
58 /// * `photo` - Photo of the game.
59 /// * `title` - Title of the game.
60 pub fn new<A, B, C>(description: A, photo: B, title: C) -> Self
61 where
62 A: Into<String>,
63 B: IntoIterator<Item = PhotoSize>,
64 C: Into<String>,
65 {
66 Self {
67 description: description.into(),
68 photo: photo.into_iter().collect(),
69 title: title.into(),
70 animation: None,
71 text: None,
72 }
73 }
74
75 /// Sets a new animation.
76 ///
77 /// # Arguments
78 ///
79 /// * `value` - Animation that will be displayed in the game message in chats.
80 pub fn with_animation(mut self, value: Animation) -> Self {
81 self.animation = Some(value);
82 self
83 }
84
85 /// Sets a new text.
86 ///
87 /// # Arguments
88 ///
89 /// * `value` - Brief description or high scores included in the game message;
90 /// 0-4096 characters.
91 pub fn with_text<T>(mut self, value: T) -> Self
92 where
93 T: Into<Text>,
94 {
95 self.text = Some(value.into());
96 self
97 }
98}
99
100#[derive(Deserialize, Serialize)]
101struct GameText {
102 text: String,
103 text_entities: Option<TextEntities>,
104}
105
106impl GameText {
107 fn deserialize_value<'de, D>(deserializer: D) -> Result<Option<Text>, D::Error>
108 where
109 D: Deserializer<'de>,
110 {
111 Option::<GameText>::deserialize(deserializer).map(|wrapper| {
112 wrapper.map(
113 |GameText {
114 text: data,
115 text_entities: entities,
116 }| Text { data, entities },
117 )
118 })
119 }
120
121 fn serialize_value<S>(value: &Option<Text>, serializer: S) -> Result<S::Ok, S::Error>
122 where
123 S: Serializer,
124 {
125 let value = value.clone().map(|value| GameText {
126 text: value.data,
127 text_entities: value.entities,
128 });
129 value.serialize(serializer)
130 }
131}
132
133/// Represents a row of the high scores table for a game.
134#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
135pub struct GameHighScore {
136 /// Position in the high score table.
137 pub position: Integer,
138 /// Score achieved by the user.
139 pub score: Integer,
140 /// User associated with the high score.
141 pub user: User,
142}
143
144impl GameHighScore {
145 /// Creates a new `GameHighScore`.
146 ///
147 /// # Arguments
148 ///
149 /// * `position` - Position in the high score table.
150 /// * `score` - Score achieved by the user.
151 /// * `user` - User associated with the high score.
152 pub fn new(position: Integer, score: Integer, user: User) -> Self {
153 Self { position, score, user }
154 }
155}
156
157/// Returns data for high score tables.
158///
159/// Will return the score of the specified user and several of his neighbors in a game.
160/// This method will currently return scores for the target user,
161/// plus two of his closest neighbors on each side.
162/// Will also return the top three users if the user and his neighbors are not among them.
163/// Please note that this behavior is subject to change.
164#[serde_with::skip_serializing_none]
165#[derive(Clone, Debug, Serialize)]
166pub struct GetGameHighScores {
167 user_id: Integer,
168 chat_id: Option<Integer>,
169 inline_message_id: Option<String>,
170 message_id: Option<Integer>,
171}
172
173impl GetGameHighScores {
174 /// Creates a new `GetGameHighScores` for a chat message.
175 ///
176 /// # Arguments
177 ///
178 /// * `user_id` - Target user ID.
179 /// * `chat_id` - Unique identifier for the target chat.
180 /// * `message_id` - Identifier of the sent message.
181 pub fn for_chat_message(user_id: Integer, chat_id: Integer, message_id: Integer) -> Self {
182 Self {
183 user_id,
184 chat_id: Some(chat_id),
185 inline_message_id: None,
186 message_id: Some(message_id),
187 }
188 }
189
190 /// Creates a new `GetGameHighScores` for an inline message.
191 ///
192 /// # Arguments
193 ///
194 /// * `user_id` - Target user ID.
195 /// * `inline_message_id` - Identifier of the inline message.
196 pub fn for_inline_message<T>(user_id: Integer, inline_message_id: T) -> Self
197 where
198 T: Into<String>,
199 {
200 Self {
201 user_id,
202 chat_id: None,
203 inline_message_id: Some(inline_message_id.into()),
204 message_id: None,
205 }
206 }
207}
208
209impl Method for GetGameHighScores {
210 type Response = Vec<GameHighScore>;
211
212 fn into_payload(self) -> Payload {
213 Payload::json("getGameHighScores", self)
214 }
215}
216
217/// Sends a game.
218#[serde_with::skip_serializing_none]
219#[derive(Clone, Debug, Serialize)]
220pub struct SendGame {
221 chat_id: Integer,
222 game_short_name: String,
223 allow_paid_broadcast: Option<bool>,
224 business_connection_id: Option<String>,
225 disable_notification: Option<bool>,
226 message_effect_id: Option<String>,
227 message_thread_id: Option<Integer>,
228 protect_content: Option<bool>,
229 reply_markup: Option<InlineKeyboardMarkup>,
230 reply_parameters: Option<ReplyParameters>,
231}
232
233impl SendGame {
234 /// Creates a new `SendGame`.
235 ///
236 /// # Arguments
237 ///
238 /// * `chat_id` - Unique identifier of the target chat.
239 /// * `game_short_name` - Short name of the game, serves as the unique identifier for the game.
240 pub fn new<T>(chat_id: Integer, game_short_name: T) -> Self
241 where
242 T: Into<String>,
243 {
244 Self {
245 chat_id,
246 game_short_name: game_short_name.into(),
247 allow_paid_broadcast: None,
248 business_connection_id: None,
249 disable_notification: None,
250 message_effect_id: None,
251 message_thread_id: None,
252 protect_content: None,
253 reply_markup: None,
254 reply_parameters: None,
255 }
256 }
257
258 /// Sets a new value for the `allow_paid_broadcast` flag.
259 ///
260 /// # Arguments
261 ///
262 /// * `value` - Whether to allow up to 1000 messages per second, ignoring broadcasting limits
263 /// for a fee of 0.1 Telegram Stars per message.
264 /// The relevant Stars will be withdrawn from the bot's balance.
265 pub fn with_allow_paid_broadcast(mut self, value: bool) -> Self {
266 self.allow_paid_broadcast = Some(value);
267 self
268 }
269
270 /// Sets a new business connection ID.
271 ///
272 /// # Arguments
273 ///
274 /// * `value` - Unique identifier of the business connection.
275 pub fn with_business_connection_id<T>(mut self, value: T) -> Self
276 where
277 T: Into<String>,
278 {
279 self.business_connection_id = Some(value.into());
280 self
281 }
282
283 /// Sets a new value for the `disable_notification` flag.
284 ///
285 /// # Arguments
286 ///
287 /// * `value` - Indicates whether to send the message silently or not;
288 /// a user will receive a notification without sound.
289 pub fn with_disable_notification(mut self, value: bool) -> Self {
290 self.disable_notification = Some(value);
291 self
292 }
293
294 /// Sets a new message effect ID.
295 ///
296 /// # Arguments
297 ///
298 /// * `value` - Unique identifier of the message effect to be added to the message; for private chats only.
299 pub fn with_message_effect_id<T>(mut self, value: T) -> Self
300 where
301 T: Into<String>,
302 {
303 self.message_effect_id = Some(value.into());
304 self
305 }
306
307 /// Sets a new message thread ID.
308 ///
309 /// # Arguments
310 ///
311 /// * `value` - Unique identifier of the target message thread;
312 /// supergroups only.
313 pub fn with_message_thread_id(mut self, value: Integer) -> Self {
314 self.message_thread_id = Some(value);
315 self
316 }
317
318 /// Sets a new value for the `protect_content` flag.
319 ///
320 /// # Arguments
321 ///
322 /// * `value` - Indicates whether to protect the contents
323 /// of the sent message from forwarding and saving.
324 pub fn with_protect_content(mut self, value: bool) -> Self {
325 self.protect_content = Some(value);
326 self
327 }
328
329 /// Sets a new reply markup.
330 ///
331 /// # Arguments
332 ///
333 /// * `value` - Reply markup.
334 pub fn with_reply_markup<T>(mut self, value: T) -> Self
335 where
336 T: Into<InlineKeyboardMarkup>,
337 {
338 self.reply_markup = Some(value.into());
339 self
340 }
341
342 /// Sets new reply parameters.
343 ///
344 /// # Arguments
345 ///
346 /// * `value` - Description of the message to reply to.
347 pub fn with_reply_parameters(mut self, value: ReplyParameters) -> Self {
348 self.reply_parameters = Some(value);
349 self
350 }
351}
352
353impl Method for SendGame {
354 type Response = Message;
355
356 fn into_payload(self) -> Payload {
357 Payload::json("sendGame", self)
358 }
359}
360
361/// Sets the score of the specified user in a game.
362///
363/// Returns an error, if the new score is not greater
364/// than the user's current score in the chat and force is `false`.
365#[serde_with::skip_serializing_none]
366#[derive(Clone, Debug, Serialize)]
367pub struct SetGameScore {
368 user_id: Integer,
369 score: Integer,
370 force: Option<bool>,
371 disable_edit_message: Option<bool>,
372 chat_id: Option<Integer>,
373 message_id: Option<Integer>,
374 inline_message_id: Option<String>,
375}
376
377impl SetGameScore {
378 /// Creates a new `SetGameScore`.
379 ///
380 /// # Arguments
381 ///
382 /// * `chat_id` - Unique identifier of the target chat.
383 /// * `message_id` - Identifier of the sent message.
384 /// * `user_id` - User identifier.
385 /// * `score` - New score, must be non-negative.
386 pub fn for_chat_message(chat_id: Integer, message_id: Integer, user_id: Integer, score: Integer) -> Self {
387 Self {
388 user_id,
389 score,
390 force: None,
391 disable_edit_message: None,
392 chat_id: Some(chat_id),
393 message_id: Some(message_id),
394 inline_message_id: None,
395 }
396 }
397
398 /// Creates a new `SetGameScore`.
399 ///
400 /// # Arguments
401 ///
402 /// * `inline_message_id` - Identifier of the inline message.
403 /// * `user_id` - User identifier.
404 /// * `score` - New score, must be non-negative.
405 pub fn for_inline_message<T>(inline_message_id: T, user_id: Integer, score: Integer) -> Self
406 where
407 T: Into<String>,
408 {
409 Self {
410 user_id,
411 score,
412 force: None,
413 disable_edit_message: None,
414 chat_id: None,
415 message_id: None,
416 inline_message_id: Some(inline_message_id.into()),
417 }
418 }
419
420 /// Sets a new value for the `disable_edit_message` flag.
421 ///
422 /// # Arguments
423 ///
424 /// * `value` - Indicates whether the game message should not be automatically
425 /// edited to include the current scoreboard.
426 pub fn with_disable_edit_message(mut self, value: bool) -> Self {
427 self.disable_edit_message = Some(value);
428 self
429 }
430
431 /// Sets a new value for the `force` flag.
432 ///
433 /// # Arguments
434 ///
435 /// * `value` - Indicates whether the high score is allowed to decrease.
436 ///
437 /// This can be useful when fixing mistakes or banning cheaters.
438 pub fn with_force(mut self, value: bool) -> Self {
439 self.force = Some(value);
440 self
441 }
442}
443
444impl Method for SetGameScore {
445 type Response = EditMessageResult;
446
447 fn into_payload(self) -> Payload {
448 Payload::json("setGameScore", self)
449 }
450}