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}