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