tgbot/types/sticker/
mod.rs

1use serde::{Deserialize, Serialize};
2
3pub use self::{input::*, mask::*, set::*};
4use crate::{
5    api::{Form, Method, Payload},
6    types::{
7        ChatId,
8        File,
9        InputFile,
10        Integer,
11        Message,
12        PhotoSize,
13        ReplyMarkup,
14        ReplyMarkupError,
15        ReplyParameters,
16        ReplyParametersError,
17    },
18};
19
20#[cfg(test)]
21mod tests;
22
23mod input;
24mod mask;
25mod set;
26
27/// Represents a sticker.
28#[serde_with::skip_serializing_none]
29#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
30pub struct Sticker {
31    /// Identifier of the file.
32    ///
33    /// Can be used to download or reuse the file.
34    pub file_id: String,
35    /// Unique identifier of the file.
36    ///
37    /// It is supposed to be the same over time and for different bots.
38    /// Can't be used to download or reuse the file.
39    pub file_unique_id: String,
40    /// Sticker height.
41    pub height: Integer,
42    /// Indicates whether the sticker is animated.
43    pub is_animated: bool,
44    /// Indicates whether the sticker is a video sticker.
45    pub is_video: bool,
46    /// Type of the sticker.
47    ///
48    /// The type of the sticker is independent from its format,
49    /// which is determined by the fields `is_animated` and `is_video`.
50    #[serde(rename = "type")]
51    pub sticker_type: StickerType,
52    /// Sticker width.
53    pub width: Integer,
54    /// For custom emoji stickers, unique identifier of the custom emoji.
55    pub custom_emoji_id: Option<String>,
56    /// Emoji associated with the sticker.
57    pub emoji: Option<String>,
58    /// File size in bytes.
59    pub file_size: Option<Integer>,
60    /// For mask stickers, the position where the mask should be placed.
61    pub mask_position: Option<MaskPosition>,
62    /// Indicates whether the sticker must be repainted to a text color in messages,
63    /// the color of the Telegram Premium badge in emoji status,
64    /// white color on chat photos, or another appropriate color in other places.
65    pub needs_repainting: Option<bool>,
66    /// For premium regular stickers, premium animation for the sticker.
67    pub premium_animation: Option<File>,
68    /// Name of the sticker set to which the sticker belongs.
69    pub set_name: Option<String>,
70    /// Sticker thumbnail in the WEBP or JPEG format.
71    pub thumbnail: Option<PhotoSize>,
72}
73
74impl Sticker {
75    /// Creates a new `Sticker`.
76    ///
77    /// # Arguments
78    ///
79    /// * `file_id` - Identifier for the file.
80    /// * `file_unique_id` - Unique identifier for the file.
81    /// * `sticker_type` - Type of the sticker.
82    /// * `height` - Sticker height.
83    /// * `width` - Sticker width.
84    pub fn new<A, B>(file_id: A, file_unique_id: B, sticker_type: StickerType, height: Integer, width: Integer) -> Self
85    where
86        A: Into<String>,
87        B: Into<String>,
88    {
89        Self {
90            file_id: file_id.into(),
91            file_unique_id: file_unique_id.into(),
92            height,
93            is_animated: false,
94            is_video: false,
95            sticker_type,
96            width,
97            custom_emoji_id: None,
98            emoji: None,
99            file_size: None,
100            mask_position: None,
101            needs_repainting: None,
102            premium_animation: None,
103            set_name: None,
104            thumbnail: None,
105        }
106    }
107
108    /// Sets a new value for the `is_animated` flag.
109    ///
110    /// # Arguments
111    ///
112    /// * `value` - Indicates whether the sticker is animated.
113    pub fn with_is_animated(mut self, value: bool) -> Self {
114        self.is_animated = value;
115        self
116    }
117
118    /// Sets a new value for the `is_video` flag.
119    ///
120    /// # Arguments
121    ///
122    /// * `value` - Indicates whether the sticker is a video sticker.
123    pub fn with_is_video(mut self, value: bool) -> Self {
124        self.is_video = value;
125        self
126    }
127
128    /// Sets a new custom emoji ID.
129    ///
130    /// # Arguments
131    ///
132    /// * `value` - Custom emoji ID.
133    pub fn with_custom_emoji_id<T>(mut self, value: T) -> Self
134    where
135        T: Into<String>,
136    {
137        self.custom_emoji_id = Some(value.into());
138        self
139    }
140
141    /// Sets a new emoji.
142    ///
143    /// # Arguments
144    ///
145    /// * `value` - Emoji.
146    pub fn with_emoji<T>(mut self, value: T) -> Self
147    where
148        T: Into<String>,
149    {
150        self.emoji = Some(value.into());
151        self
152    }
153
154    /// Sets a new file size.
155    ///
156    /// # Arguments
157    ///
158    /// * `value` - File size in bytes.
159    pub fn with_file_size(mut self, value: Integer) -> Self {
160        self.file_size = Some(value);
161        self
162    }
163
164    /// Sets a new mask position.
165    ///
166    /// # Arguments
167    ///
168    /// * `value` - Mask position.
169    pub fn with_mask_position(mut self, value: MaskPosition) -> Self {
170        self.mask_position = Some(value);
171        self
172    }
173    /// Sets a new value for the `needs_repainting` flag.
174    ///
175    /// # Arguments
176    ///
177    /// * `value` - Value of the flag.
178    pub fn with_needs_repainting(mut self, value: bool) -> Self {
179        self.needs_repainting = Some(value);
180        self
181    }
182
183    /// Sets a new premium animation.
184    ///
185    /// # Arguments
186    ///
187    /// * `value` - Premium animation.
188    pub fn with_premium_animation(mut self, value: File) -> Self {
189        self.premium_animation = Some(value);
190        self
191    }
192
193    /// Sets a new sticker set name.
194    ///
195    /// # Arguments
196    ///
197    /// * `value` - Name of a sticker set.
198    pub fn with_set_name<T>(mut self, value: T) -> Self
199    where
200        T: Into<String>,
201    {
202        self.set_name = Some(value.into());
203        self
204    }
205
206    /// Sets a new thumbnail.
207    ///
208    /// # Arguments
209    ///
210    /// * `value` - Thumbnail.
211    pub fn with_thumbnail(mut self, value: PhotoSize) -> Self {
212        self.thumbnail = Some(value);
213        self
214    }
215}
216
217/// Represents a format of stickers in the set.
218#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
219#[serde(rename_all = "snake_case")]
220pub enum StickerFormat {
221    /// PNG or WEBP.
222    Static,
223    /// TGS.
224    Animated,
225    /// WEBM.
226    Video,
227}
228
229impl AsRef<str> for StickerFormat {
230    fn as_ref(&self) -> &str {
231        match self {
232            Self::Static => "static",
233            Self::Animated => "animated",
234            Self::Video => "video",
235        }
236    }
237}
238
239/// Represents a type of stickers in the set.
240#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
241#[serde(rename_all = "snake_case")]
242pub enum StickerType {
243    /// Sticker contains a custom emoji.
244    CustomEmoji,
245    /// Sticker contains a mask.
246    Mask,
247    /// Regular sticker.
248    Regular,
249}
250
251impl AsRef<str> for StickerType {
252    fn as_ref(&self) -> &str {
253        match self {
254            Self::CustomEmoji => "custom_emoji",
255            Self::Mask => "mask",
256            Self::Regular => "regular",
257        }
258    }
259}
260
261/// Returns information about custom emoji stickers by their identifiers.
262#[derive(Clone, Debug, Serialize)]
263pub struct GetCustomEmojiStickers {
264    custom_emoji_ids: Vec<String>,
265}
266
267impl GetCustomEmojiStickers {
268    /// Creates a new `GetCustomEmojiStickers`.
269    ///
270    /// # Arguments
271    ///
272    /// * `custom_emoji_ids` - List of custom emoji identifiers; at most 200 custom emoji identifiers can be specified.
273    pub fn new<A, B>(custom_emoji_ids: A) -> Self
274    where
275        A: IntoIterator<Item = B>,
276        B: Into<String>,
277    {
278        Self {
279            custom_emoji_ids: custom_emoji_ids.into_iter().map(Into::into).collect(),
280        }
281    }
282}
283
284impl Method for GetCustomEmojiStickers {
285    type Response = Vec<Sticker>;
286
287    fn into_payload(self) -> Payload {
288        Payload::json("getCustomEmojiStickers", self)
289    }
290}
291
292/// Sends a static WEBP, animated TGS, or video WEBM sticker.
293#[derive(Debug)]
294pub struct SendSticker {
295    form: Form,
296}
297
298impl SendSticker {
299    /// Creates a new `SendSticker`.
300    ///
301    /// # Arguments
302    ///
303    /// * `chat_id` - Unique identifier of the target chat.
304    /// * `sticker` - Sticker to send.
305    pub fn new<A, B>(chat_id: A, sticker: B) -> Self
306    where
307        A: Into<ChatId>,
308        B: Into<InputFile>,
309    {
310        Self {
311            form: Form::from([("chat_id", chat_id.into().into()), ("sticker", sticker.into().into())]),
312        }
313    }
314
315    /// Sets a new value for the `allow_paid_broadcast` flag.
316    ///
317    /// # Arguments
318    ///
319    /// * `value` - Whether to allow up to 1000 messages per second, ignoring broadcasting limits
320    ///   for a fee of 0.1 Telegram Stars per message.
321    ///   The relevant Stars will be withdrawn from the bot's balance.
322    pub fn with_allow_paid_broadcast(mut self, value: bool) -> Self {
323        self.form.insert_field("allow_paid_broadcast", value);
324        self
325    }
326
327    /// Sets a new business connection ID.
328    ///
329    /// # Arguments
330    ///
331    /// * `value` - Unique identifier of the business connection.
332    pub fn with_business_connection_id<T>(mut self, value: T) -> Self
333    where
334        T: Into<String>,
335    {
336        self.form.insert_field("business_connection_id", value.into());
337        self
338    }
339
340    /// Sets a new value for the `disable_notification` flag.
341    ///
342    /// # Arguments
343    ///
344    /// * `value` - Indicates whether to send the message silently or not;
345    ///   a user will receive a notification without sound.
346    pub fn with_disable_notification(mut self, value: bool) -> Self {
347        self.form.insert_field("disable_notification", value);
348        self
349    }
350
351    /// Sets a new emoji.
352    ///
353    /// # Arguments
354    ///
355    /// * `value` - Emoji associated with the sticker; only for just uploaded stickers.
356    pub fn with_emoji<T>(mut self, value: T) -> Self
357    where
358        T: Into<String>,
359    {
360        self.form.insert_field("emoji", value.into());
361        self
362    }
363
364    /// Sets a new message effect ID.
365    ///
366    /// # Arguments
367    ///
368    /// * `value` - Unique identifier of the message effect to be added to the message; for private chats only.
369    pub fn with_message_effect_id<T>(mut self, value: T) -> Self
370    where
371        T: Into<String>,
372    {
373        self.form.insert_field("message_effect_id", value.into());
374        self
375    }
376
377    /// Sets a new message thread ID.
378    ///
379    /// # Arguments
380    ///
381    /// * `value` - Unique identifier of the target message thread;
382    ///   supergroups only.
383    pub fn with_message_thread_id(mut self, value: Integer) -> Self {
384        self.form.insert_field("message_thread_id", value);
385        self
386    }
387
388    /// Sets a new value for the `protect_content` flag.
389    ///
390    /// # Arguments
391    ///
392    /// * `value` - Indicates whether to protect the contents
393    ///   of the sent message from forwarding and saving.
394    pub fn with_protect_content(mut self, value: bool) -> Self {
395        self.form.insert_field("protect_content", value.to_string());
396        self
397    }
398
399    /// Sets a new reply markup.
400    ///
401    /// # Arguments
402    ///
403    /// * `value` - Reply markup.
404    pub fn with_reply_markup<T>(mut self, value: T) -> Result<Self, ReplyMarkupError>
405    where
406        T: Into<ReplyMarkup>,
407    {
408        let value = value.into();
409        self.form.insert_field("reply_markup", value.serialize()?);
410        Ok(self)
411    }
412
413    /// Sets new reply parameters.
414    ///
415    /// # Arguments
416    ///
417    /// * `value` - Description of the message to reply to.
418    pub fn with_reply_parameters(mut self, value: ReplyParameters) -> Result<Self, ReplyParametersError> {
419        self.form.insert_field("reply_parameters", value.serialize()?);
420        Ok(self)
421    }
422}
423
424impl Method for SendSticker {
425    type Response = Message;
426
427    fn into_payload(self) -> Payload {
428        Payload::form("sendSticker", self.form)
429    }
430}
431
432/// Changes the list of emoji assigned to a regular or custom emoji sticker.
433///
434/// The sticker must belong to a sticker set created by the bot.
435#[derive(Clone, Debug, Serialize)]
436pub struct SetStickerEmojiList {
437    sticker: String,
438    emoji_list: Vec<String>,
439}
440
441impl SetStickerEmojiList {
442    /// Creates a new `SetStickerEmojiList`.
443    ///
444    /// * `sticker` - File identifier of the sticker.
445    /// * `emoji_list` - A list of 1-20 emoji associated with the sticker.
446    pub fn new<A, B, C>(sticker: A, emoji_list: B) -> Self
447    where
448        A: Into<String>,
449        B: IntoIterator<Item = C>,
450        C: Into<String>,
451    {
452        Self {
453            sticker: sticker.into(),
454            emoji_list: emoji_list.into_iter().map(Into::into).collect(),
455        }
456    }
457}
458
459impl Method for SetStickerEmojiList {
460    type Response = bool;
461
462    fn into_payload(self) -> Payload {
463        Payload::json("setStickerEmojiList", self)
464    }
465}
466
467/// Changes search keywords assigned to a regular or custom emoji sticker.
468///
469/// The sticker must belong to a sticker set created by the bot.
470#[derive(Clone, Debug, Serialize)]
471pub struct SetStickerKeywords {
472    sticker: String,
473    keywords: Vec<String>,
474}
475
476impl SetStickerKeywords {
477    /// Creates a new `SetStickerKeywords`.
478    ///
479    /// * `sticker` - File identifier of the sticker.
480    /// * `keywords` - A list of 0-20 search keywords for the sticker
481    ///   with total length of up to 64 characters.
482    pub fn new<A, B, C>(sticker: A, keywords: B) -> Self
483    where
484        A: Into<String>,
485        B: IntoIterator<Item = C>,
486        C: Into<String>,
487    {
488        Self {
489            sticker: sticker.into(),
490            keywords: keywords.into_iter().map(Into::into).collect(),
491        }
492    }
493}
494
495impl Method for SetStickerKeywords {
496    type Response = bool;
497
498    fn into_payload(self) -> Payload {
499        Payload::json("setStickerKeywords", self)
500    }
501}
502
503/// Changes the mask position of a mask sticker.
504///
505/// The sticker must belong to a sticker set created by the bot.
506#[serde_with::skip_serializing_none]
507#[derive(Clone, Debug, Serialize)]
508pub struct SetStickerMaskPosition {
509    sticker: String,
510    mask_position: Option<MaskPosition>,
511}
512
513impl SetStickerMaskPosition {
514    /// Creates a new `SetStickerMaskPosition`.
515    ///
516    /// * `sticker` - File identifier of the sticker.
517    pub fn new<T>(sticker: T) -> Self
518    where
519        T: Into<String>,
520    {
521        Self {
522            sticker: sticker.into(),
523            mask_position: None,
524        }
525    }
526
527    /// Sets a new mask position.
528    ///
529    /// # Arguments
530    ///
531    /// * `value` - Position where the mask should be placed on faces.
532    ///
533    /// Omit the parameter to remove the mask position.
534    pub fn with_mask_position(mut self, value: MaskPosition) -> Self {
535        self.mask_position = Some(value);
536        self
537    }
538}
539
540impl Method for SetStickerMaskPosition {
541    type Response = bool;
542
543    fn into_payload(self) -> Payload {
544        Payload::json("setStickerMaskPosition", self)
545    }
546}
547
548/// Uploads a file with a sticker for later use in
549/// the [`CreateNewStickerSet`] and [`AddStickerToSet`] methods.
550///
551/// The file can be used multiple times.
552#[derive(Debug)]
553pub struct UploadStickerFile {
554    form: Form,
555}
556
557impl UploadStickerFile {
558    /// Creates a new `UploadStickerFile`.
559    ///
560    /// # Arguments
561    ///
562    /// * `user_id` - User identifier of sticker file owner.
563    /// * `sticker` - A file with the sticker in WEBP, PNG, TGS, or WEBM format.
564    /// * `sticker_format` - Format of the sticker.
565    pub fn new<T>(user_id: Integer, sticker: T, sticker_format: StickerFormat) -> Self
566    where
567        T: Into<InputFile>,
568    {
569        Self {
570            form: Form::from([
571                ("user_id", user_id.into()),
572                ("sticker", sticker.into().into()),
573                ("sticker_format", sticker_format.as_ref().into()),
574            ]),
575        }
576    }
577}
578
579impl Method for UploadStickerFile {
580    type Response = File;
581
582    fn into_payload(self) -> Payload {
583        Payload::form("uploadStickerFile", self.form)
584    }
585}