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}