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}