telbot_types/
sticker.rs

1use std::collections::HashMap;
2
3use crate::{
4    chat::ChatId,
5    file::{File, InputFile, InputFileVariant, PhotoSize},
6    markup::ReplyMarkup,
7    message::Message,
8    FileMethod, JsonMethod, TelegramMethod,
9};
10use serde::{Deserialize, Serialize};
11
12/// This object represents a sticker.
13#[derive(Debug, Deserialize)]
14pub struct Sticker {
15    /// Identifier for this file, which can be used to download or reuse the file
16    pub file_id: String,
17    /// Unique identifier for this file, which is supposed to be the same over time and for different bots.
18    /// Can't be used to download or reuse the file.
19    pub file_unique_id: String,
20    /// Sticker width
21    pub width: u32,
22    /// Sticker height
23    pub height: u32,
24    /// *True*, if the sticker is [animated](https://telegram.org/blog/animated-stickers)
25    pub is_animated: bool,
26    /// Sticker thumbnail in the .WEBP or .JPG format
27    pub thumb: Option<PhotoSize>,
28    /// Emoji associated with the sticker
29    pub emoji: Option<String>,
30    /// Name of the sticker set to which the sticker belongs
31    pub set_name: Option<String>,
32    /// For mask stickers, the position where the mask should be placed
33    pub mask_position: Option<MaskPosition>,
34    /// File size
35    pub file_size: Option<u32>,
36}
37
38/// This object represents a sticker set.
39#[derive(Debug, Deserialize)]
40pub struct StickerSet {
41    /// Sticker set name
42    pub name: String,
43    /// Sticker set title
44    pub title: String,
45    /// *True*, if the sticker set contains [animated stickers](https://telegram.org/blog/animated-stickers)
46    pub is_animated: bool,
47    /// *True*, if the sticker set contains masks
48    pub contains_masks: bool,
49    /// List of all set stickers
50    pub stickers: Vec<Sticker>,
51    /// Sticker set thumbnail in the .WEBP or .TGS format
52    pub thumb: Option<PhotoSize>,
53}
54
55/// This object describes the position on faces where a mask should be placed by default.
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct MaskPosition {
58    /// The part of the face relative to which the mask should be placed.
59    /// One of “forehead”, “eyes”, “mouth”, or “chin”.
60    pub point: MaskPoint,
61    /// Shift by X-axis measured in widths of the mask scaled to the face size, from left to right.
62    /// For example, choosing -1.0 will place mask just to the left of the default mask position.
63    pub x_shift: f32,
64    /// Shift by Y-axis measured in heights of the mask scaled to the face size, from top to bottom.
65    /// For example, 1.0 will place the mask just below the default mask position.
66    pub y_shift: f32,
67    /// Mask scaling coefficient. For example, 2.0 means double size.
68    pub scale: f32,
69}
70
71/// The part of the face used in masked stickers.
72#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
73pub enum MaskPoint {
74    Forehead,
75    Eyes,
76    Mouth,
77    Chin,
78}
79
80/// Use this method to send static .WEBP or [animated](https://telegram.org/blog/animated-stickers) .TGS stickers.
81/// On success, the sent [Message](https://core.telegram.org/bots/api#message) is returned.
82#[derive(Clone, Serialize)]
83pub struct SendSticker {
84    /// Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
85    pub chat_id: ChatId,
86    /// Sticker to send. Pass a file_id as String to send a file that exists on the Telegram servers (recommended),
87    /// pass an HTTP URL as a String for Telegram to get a .WEBP file from the Internet,
88    /// or upload a new one using multipart/form-data.
89    /// [More info on Sending Files »](https://core.telegram.org/bots/api#sending-files)
90    pub sticker: InputFileVariant,
91    /// Sends the message [silently](https://telegram.org/blog/channels-2-0#silent-messages).
92    /// Users will receive a notification with no sound.
93    #[serde(skip_serializing_if = "Option::is_none")]
94    pub disable_notification: Option<bool>,
95    /// If the message is a reply, ID of the original message
96    #[serde(skip_serializing_if = "Option::is_none")]
97    pub reply_to_message_id: Option<i64>,
98    /// Pass *True*, if the message should be sent even if the specified replied-to message is not found
99    #[serde(skip_serializing_if = "Option::is_none")]
100    pub allow_sending_without_reply: Option<bool>,
101    /// Additional interface options.
102    /// A JSON-serialized object for an [inline keyboard](https://core.telegram.org/bots#inline-keyboards-and-on-the-fly-updating),
103    /// [custom reply keyboard](https://core.telegram.org/bots#keyboards),
104    /// instructions to remove reply keyboard or to force a reply from the user.
105    #[serde(skip_serializing_if = "Option::is_none")]
106    pub reply_markup: Option<ReplyMarkup>,
107    /// Protects the contents of the sent message from forwarding and saving
108    #[serde(skip_serializing_if = "Option::is_none")]
109    pub protect_content: Option<bool>,
110}
111
112impl SendSticker {
113    /// Create a new sendSticker request
114    pub fn new(chat_id: impl Into<ChatId>, sticker: impl Into<InputFileVariant>) -> Self {
115        Self {
116            chat_id: chat_id.into(),
117            sticker: sticker.into(),
118            disable_notification: None,
119            reply_to_message_id: None,
120            allow_sending_without_reply: None,
121            reply_markup: None,
122            protect_content: None,
123        }
124    }
125    /// Disable notification
126    pub fn disable_notification(self) -> Self {
127        Self {
128            disable_notification: Some(true),
129            ..self
130        }
131    }
132    /// Reply to message
133    pub fn reply_to(self, message_id: i64) -> Self {
134        Self {
135            reply_to_message_id: Some(message_id),
136            ..self
137        }
138    }
139    /// Allow sending message even if the replying message isn't present
140    pub fn allow_sending_without_reply(self) -> Self {
141        Self {
142            allow_sending_without_reply: Some(true),
143            ..self
144        }
145    }
146    /// Set reply markup
147    pub fn with_reply_markup(self, markup: impl Into<ReplyMarkup>) -> Self {
148        Self {
149            reply_markup: Some(markup.into()),
150            ..self
151        }
152    }
153    /// Protect content
154    pub fn protect_content(self) -> Self {
155        Self {
156            protect_content: Some(true),
157            ..self
158        }
159    }
160}
161
162impl TelegramMethod for SendSticker {
163    type Response = Message;
164
165    fn name() -> &'static str {
166        "sendSticker"
167    }
168}
169
170impl JsonMethod for SendSticker {}
171
172/// Use this method to get a sticker set. On success, a [StickerSet](https://core.telegram.org/bots/api#stickerset) object is returned.
173#[derive(Clone, Serialize)]
174pub struct GetStickerSet {
175    /// Name of the sticker set
176    pub name: String,
177}
178
179impl GetStickerSet {
180    /// Create a new getStickerSet request
181    pub fn new(name: impl Into<String>) -> Self {
182        Self { name: name.into() }
183    }
184}
185
186impl TelegramMethod for GetStickerSet {
187    type Response = StickerSet;
188
189    fn name() -> &'static str {
190        "getStickerSet"
191    }
192}
193
194impl JsonMethod for GetStickerSet {}
195
196/// Use this method to upload a .PNG file with a sticker for later use
197/// in *createNewStickerSet* and *addStickerToSet* methods (can be used multiple times).
198/// Returns the uploaded [`File`] on success.
199#[derive(Clone, Serialize)]
200pub struct UploadStickerFile {
201    /// User identifier of sticker file owner
202    pub user_id: i64,
203    /// **PNG** image with the sticker, must be up to 512 kilobytes in size,
204    /// dimensions must not exceed 512px, and either width or height must be exactly 512px.
205    /// [More info on Sending Files »](https://core.telegram.org/bots/api#sending-files)
206    pub png_sticker: InputFile,
207}
208
209impl UploadStickerFile {
210    /// Create a new uploadStickerFile request
211    pub fn new(user_id: i64, png_sticker: InputFile) -> Self {
212        Self {
213            user_id,
214            png_sticker,
215        }
216    }
217}
218
219impl TelegramMethod for UploadStickerFile {
220    type Response = File;
221
222    fn name() -> &'static str {
223        "uploadStickerFile"
224    }
225}
226
227impl FileMethod for UploadStickerFile {
228    fn files(&self) -> Option<std::collections::HashMap<&str, &InputFile>> {
229        let mut map = HashMap::new();
230        map.insert("png_sticker", &self.png_sticker);
231        Some(map)
232    }
233}
234
235/// Use this method to create a new sticker set owned by a user.
236/// The bot will be able to edit the sticker set thus created.
237/// You must use exactly one of the fields *png_sticker* or *tgs_sticker*.
238/// Returns *True* on success.
239#[derive(Clone, Serialize)]
240pub struct CreateNewStickerSet {
241    /// User identifier of created sticker set owner.
242    pub user_id: i64,
243    /// Short name of sticker set, to be used in `t.me/addstickers/` URLs (e.g., *animals*).
244    /// Can contain only english letters, digits and underscores.
245    /// Must begin with a letter, can't contain consecutive underscores and must end in *“_by_<bot username>”*.
246    /// *<bot_username>* is case insensitive. 1-64 characters.
247    pub name: String,
248    /// Sticker set title, 1-64 characters.
249    pub title: String,
250    /// **PNG** image with the sticker, must be up to 512 kilobytes in size,
251    /// dimensions must not exceed 512px, and either width or height must be exactly 512px.
252    /// Pass a *file_id* as a String to send a file that already exists on the Telegram servers,
253    /// pass an HTTP URL as a String for Telegram to get a file from the Internet,
254    /// or upload a new one using multipart/form-data.
255    /// [More info on Sending Files »](https://core.telegram.org/bots/api#sending-files)
256    #[serde(skip_serializing_if = "Option::is_none")]
257    pub png_sticker: Option<InputFileVariant>,
258    /// **TGS** animation with the sticker, uploaded using multipart/form-data.
259    /// See https://core.telegram.org/animated_stickers#technical-requirements for technical requirements
260    #[serde(skip_serializing_if = "Option::is_none")]
261    pub tgs_sticker: Option<InputFile>,
262    /// One or more emoji corresponding to the sticker.
263    pub emojis: String,
264    /// Pass *True*, if a set of mask stickers should be created.
265    #[serde(skip_serializing_if = "Option::is_none")]
266    pub contains_masks: Option<bool>,
267    /// A JSON-serialized object for position where the mask should be placed on faces.
268    #[serde(skip_serializing_if = "Option::is_none")]
269    pub mask_position: Option<MaskPosition>,
270}
271
272impl CreateNewStickerSet {
273    /// Create a new createNewStickerSet request with png sticker
274    pub fn new_png(
275        user_id: i64,
276        name: impl Into<String>,
277        title: impl Into<String>,
278        emojis: impl Into<String>,
279        png_sticker: impl Into<InputFileVariant>,
280    ) -> Self {
281        Self {
282            user_id,
283            name: name.into(),
284            title: title.into(),
285            png_sticker: Some(png_sticker.into()),
286            tgs_sticker: None,
287            emojis: emojis.into(),
288            contains_masks: None,
289            mask_position: None,
290        }
291    }
292    /// Create a new createNewStickerSet request with tgs sticker
293    pub fn new_tgs(
294        user_id: i64,
295        name: impl Into<String>,
296        title: impl Into<String>,
297        emojis: impl Into<String>,
298        tgs_sticker: InputFile,
299    ) -> Self {
300        Self {
301            user_id,
302            name: name.into(),
303            title: title.into(),
304            png_sticker: None,
305            tgs_sticker: Some(tgs_sticker),
306            emojis: emojis.into(),
307            contains_masks: None,
308            mask_position: None,
309        }
310    }
311    /// Mark as mask sticker
312    pub fn with_masks(self) -> Self {
313        Self {
314            contains_masks: Some(true),
315            ..self
316        }
317    }
318    /// Set mask position
319    pub fn with_mask_position(self, position: MaskPosition) -> Self {
320        Self {
321            mask_position: Some(position),
322            ..self
323        }
324    }
325}
326
327impl TelegramMethod for CreateNewStickerSet {
328    type Response = bool;
329
330    fn name() -> &'static str {
331        "createNewStickerSet"
332    }
333}
334
335impl FileMethod for CreateNewStickerSet {
336    fn files(&self) -> Option<HashMap<&str, &InputFile>> {
337        let mut map = HashMap::new();
338        match (&self.png_sticker, &self.tgs_sticker) {
339            (None, Some(tgs)) => {
340                map.insert("tgs_sticker", tgs);
341            },
342            (Some(InputFileVariant::File(png)), None) => {
343                map.insert("png_sticker", png);
344            }
345            (Some(InputFileVariant::Id(_)), None) => {},
346            _ => panic!("exactly one of CreateNewStickerSet::png_sticker or CreateNewStickerSet::tgs_sticker can be used"),
347        }
348        Some(map)
349    }
350}
351
352/// Use this method to add a new sticker to a set created by the bot.
353/// You **must** use exactly one of the fields _png_sticker_ or _tgs_sticker_.
354/// Animated stickers can be added to animated sticker sets and only to them.
355/// Animated sticker sets can have up to 50 stickers
356/// Static sticker sets can have up to 120 stickers.
357/// Returns _True_ on success.
358#[derive(Clone, Serialize)]
359pub struct AddStickerToSet {
360    /// User identifier of sticker file owner
361    pub user_id: i64,
362    /// Sticker set name
363    pub name: String,
364    /// **PNG** image with the sticker, must be up to 512 kilobytes in size,
365    /// dimensions must not exceed 512px, and either width or height must be exactly 512px.
366    /// Pass a *file_id* as a String to send a file that already exists on the Telegram servers,
367    /// pass an HTTP URL as a String for Telegram to get a file from the Internet,
368    /// or upload a new one using multipart/form-data.
369    /// [More info on Sending Files »](https://core.telegram.org/bots/api#sending-files)
370    #[serde(skip_serializing_if = "Option::is_none")]
371    pub png_sticker: Option<InputFileVariant>,
372    /// **TGS** animation with the sticker, uploaded using multipart/form-data.
373    /// See https://core.telegram.org/animated_stickers#technical-requirements for technical requirements
374    #[serde(skip_serializing_if = "Option::is_none")]
375    pub tgs_sticker: Option<InputFile>,
376    /// One or more emoji corresponding to the sticker
377    pub emojis: String,
378    /// A JSON-serialized object for position where the mask should be placed on faces
379    pub mask_position: Option<MaskPosition>,
380}
381
382impl AddStickerToSet {
383    /// Create a new addStickerToSet request with png sticker
384    pub fn new_png(
385        user_id: i64,
386        name: impl Into<String>,
387        emojis: impl Into<String>,
388        png_sticker: impl Into<InputFileVariant>,
389    ) -> Self {
390        Self {
391            user_id,
392            name: name.into(),
393            png_sticker: Some(png_sticker.into()),
394            tgs_sticker: None,
395            emojis: emojis.into(),
396            mask_position: None,
397        }
398    }
399    /// Create a new addStickerToSet request with tgs sticker
400    pub fn new_tgs(
401        user_id: i64,
402        name: impl Into<String>,
403        emojis: impl Into<String>,
404        tgs_sticker: InputFile,
405    ) -> Self {
406        Self {
407            user_id,
408            name: name.into(),
409            png_sticker: None,
410            tgs_sticker: Some(tgs_sticker),
411            emojis: emojis.into(),
412            mask_position: None,
413        }
414    }
415    /// Set mask position
416    pub fn with_mask_position(self, position: MaskPosition) -> Self {
417        Self {
418            mask_position: Some(position),
419            ..self
420        }
421    }
422}
423
424impl TelegramMethod for AddStickerToSet {
425    type Response = bool;
426
427    fn name() -> &'static str {
428        "addStickerToSet"
429    }
430}
431
432impl FileMethod for AddStickerToSet {
433    fn files(&self) -> Option<HashMap<&str, &InputFile>> {
434        let mut map = HashMap::new();
435        match (&self.png_sticker, &self.tgs_sticker) {
436            (None, Some(tgs)) => {
437                map.insert("tgs_sticker", tgs);
438            },
439            (Some(InputFileVariant::File(png)), None) => {
440                map.insert("png_sticker", png);
441            }
442            (Some(InputFileVariant::Id(_)), None) => {},
443            _ => panic!("exactly one of AddStickerToSet::png_sticker or AddStickerToSet::tgs_sticker can be used"),
444        }
445        Some(map)
446    }
447}
448
449/// Use this method to move a sticker in a set created by the bot to a specific position.
450/// Returns _True_ on success.
451#[derive(Clone, Serialize)]
452pub struct SetStickerPositionInSet {
453    pub sticker: String,
454    pub position: usize,
455}
456
457impl SetStickerPositionInSet {
458    /// Create a new setStickerPositionSet request
459    pub fn new(sticker: impl Into<String>, position: usize) -> Self {
460        Self {
461            sticker: sticker.into(),
462            position,
463        }
464    }
465}
466
467impl TelegramMethod for SetStickerPositionInSet {
468    type Response = bool;
469
470    fn name() -> &'static str {
471        "setStickerPositionInSet"
472    }
473}
474
475impl JsonMethod for SetStickerPositionInSet {}
476
477/// Use this method to delete a sticker from a set created by the bot.
478/// Returns _True_ on success.
479#[derive(Clone, Serialize)]
480pub struct DeleteStickerFromSet {
481    pub sticker: String,
482}
483
484impl DeleteStickerFromSet {
485    /// Create a new deleteStickerFromSet request
486    pub fn new(sticker: impl Into<String>) -> Self {
487        Self {
488            sticker: sticker.into(),
489        }
490    }
491}
492
493impl TelegramMethod for DeleteStickerFromSet {
494    type Response = bool;
495
496    fn name() -> &'static str {
497        "deleteStickerFromSet"
498    }
499}
500
501impl JsonMethod for DeleteStickerFromSet {}
502
503/// Use this method to set the thumbnail of a sticker set.
504/// Animated thumbnails can be set for animated sticker sets only.
505/// Returns _True_ on success.
506#[derive(Clone, Serialize)]
507pub struct SetStickerSetThumb {
508    /// Sticker set name
509    pub name: String,
510    /// User identifier of the sticker set owner
511    pub user_id: i64,
512    /// A **PNG** image with the thumbnail, must be up to 128 kilobytes in size
513    /// and have width and height exactly 100px, or a **TGS** animation with the thumbnailup to 32 kilobytes in size;
514    /// see https://core.telegram.org/animated_stickers#technical-requirements
515    /// for animated sticker technical requirements.
516    /// Pass a _file_id_ as a String to send a file that already exists on the Telegram servers,
517    /// pass an HTTP URL as a String for Telegram to get a file from the Internet,
518    /// or upload a new one using multipart/form-data.
519    /// [More info on Sending Files »](https://core.telegram.org/bots/api#sending-files).
520    /// Animated sticker set thumbnail can't be uploaded via HTTP URL.
521    #[serde(skip_serializing_if = "Option::is_none")]
522    pub thumb: Option<InputFileVariant>,
523}
524
525impl SetStickerSetThumb {
526    /// Create a new setStickerSetThumb request
527    pub fn new(name: impl Into<String>, user_id: i64) -> Self {
528        Self {
529            name: name.into(),
530            user_id,
531            thumb: None,
532        }
533    }
534
535    /// Set thumb
536    pub fn with_thumb(self, thumb: impl Into<InputFileVariant>) -> Self {
537        Self {
538            thumb: Some(thumb.into()),
539            ..self
540        }
541    }
542}
543
544impl TelegramMethod for SetStickerSetThumb {
545    type Response = bool;
546
547    fn name() -> &'static str {
548        "setStickerSetThumb"
549    }
550}
551
552impl FileMethod for SetStickerSetThumb {
553    fn files(&self) -> Option<HashMap<&str, &InputFile>> {
554        if let Some(InputFileVariant::File(thumb)) = &self.thumb {
555            let mut map = HashMap::new();
556            map.insert("thumb", thumb);
557            Some(map)
558        } else {
559            None
560        }
561    }
562}