tgbot/types/definitions/file/
animation.rs

1use std::{error::Error, fmt};
2
3use serde::{Deserialize, Serialize};
4
5use crate::{
6    api::{Form, Method, Payload},
7    types::{
8        ChatId,
9        InputFile,
10        Integer,
11        Message,
12        ParseMode,
13        PhotoSize,
14        ReplyMarkup,
15        ReplyMarkupError,
16        ReplyParameters,
17        ReplyParametersError,
18        SuggestedPostParameters,
19        SuggestedPostParametersError,
20        TextEntities,
21        TextEntity,
22        TextEntityError,
23    },
24};
25
26/// Represents an animation file (GIF or H.264/MPEG-4 AVC video without sound).
27#[serde_with::skip_serializing_none]
28#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
29pub struct Animation {
30    /// Duration in seconds as defined by sender.
31    pub duration: Integer,
32    /// Identifier of the file.
33    ///
34    /// Can be used to download or reuse the file.
35    pub file_id: String,
36    /// Unique identifier of the file.
37    ///
38    /// It is supposed to be the same over time and for different bots.
39    /// Can't be used to download or reuse the file.
40    pub file_unique_id: String,
41    /// Height as defined by sender.
42    pub height: Integer,
43    /// Width as defined by sender.
44    pub width: Integer,
45    /// Original filename as defined by sender.
46    pub file_name: Option<String>,
47    /// File size in bytes.
48    pub file_size: Option<Integer>,
49    /// MIME type as defined by sender.
50    pub mime_type: Option<String>,
51    /// Thumbnail as defined by sender.
52    pub thumbnail: Option<PhotoSize>,
53}
54
55impl Animation {
56    /// Creates a new `Animation`.
57    ///
58    /// # Arguments
59    ///
60    /// * `duration` - Duration in seconds.
61    /// * `file_id` - Identifier of the file.
62    /// * `file_unique_id` - Unique identifier of the file.
63    /// * `height` - Height.
64    /// * `width` - Width.
65    pub fn new<A, B>(duration: Integer, file_id: A, file_unique_id: B, height: Integer, width: Integer) -> Self
66    where
67        A: Into<String>,
68        B: Into<String>,
69    {
70        Self {
71            duration,
72            file_id: file_id.into(),
73            file_unique_id: file_unique_id.into(),
74            height,
75            width,
76            file_name: None,
77            file_size: None,
78            mime_type: None,
79            thumbnail: None,
80        }
81    }
82
83    /// Sets a new file name.
84    ///
85    /// # Arguments
86    ///
87    /// * `value` - File name.
88    pub fn with_file_name<T>(mut self, value: T) -> Self
89    where
90        T: Into<String>,
91    {
92        self.file_name = Some(value.into());
93        self
94    }
95
96    /// Sets a new file size.
97    ///
98    /// # Arguments
99    ///
100    /// * `value` - The size of the file in bytes.
101    pub fn with_file_size(mut self, value: Integer) -> Self {
102        self.file_size = Some(value);
103        self
104    }
105
106    /// Sets a new MIME type.
107    ///
108    /// # Arguments
109    ///
110    /// * `value` - MIME type.
111    pub fn with_mime_type<T>(mut self, value: T) -> Self
112    where
113        T: Into<String>,
114    {
115        self.mime_type = Some(value.into());
116        self
117    }
118
119    /// Sets a new thumbnail.
120    ///
121    /// # Arguments
122    ///
123    /// * `value` - Thumbnail.
124    pub fn with_thumbnail(mut self, value: PhotoSize) -> Self {
125        self.thumbnail = Some(value);
126        self
127    }
128}
129
130/// Sends an animation file (GIF or H.264/MPEG-4 AVC video without sound).
131///
132/// Bots can currently send animation files of up to 50 MB in size,
133/// this limit may be changed in the future.
134#[derive(Debug)]
135pub struct SendAnimation {
136    form: Form,
137}
138
139impl SendAnimation {
140    /// Creates a new `SendAnimation`.
141    ///
142    /// # Arguments
143    ///
144    /// * `chat_id` - Unique identifier of the target chat.
145    /// * `animation` - Animation to send.
146    pub fn new<A, B>(animation: A, chat_id: B) -> Self
147    where
148        A: Into<InputFile>,
149        B: Into<ChatId>,
150    {
151        Self {
152            form: Form::from([
153                ("animation", animation.into().into()),
154                ("chat_id", chat_id.into().into()),
155            ]),
156        }
157    }
158
159    /// Sets a new value for the `allow_paid_broadcast` flag.
160    ///
161    /// # Arguments
162    ///
163    /// * `value` - Whether to allow up to 1000 messages per second, ignoring broadcasting limits
164    ///   for a fee of 0.1 Telegram Stars per message.
165    ///   The relevant Stars will be withdrawn from the bot's balance.
166    pub fn with_allow_paid_broadcast(mut self, value: bool) -> Self {
167        self.form.insert_field("allow_paid_broadcast", value);
168        self
169    }
170
171    /// Sets a new business connection ID.
172    ///
173    /// # Arguments
174    ///
175    /// * `value` - Unique identifier of the business connection.
176    pub fn with_business_connection_id<T>(mut self, value: T) -> Self
177    where
178        T: Into<String>,
179    {
180        self.form.insert_field("business_connection_id", value.into());
181        self
182    }
183
184    /// Sets a new caption.
185    ///
186    /// # Arguments
187    ///
188    /// * `value` - Caption; 0-1024 characters.
189    ///
190    /// May also be used when resending animation by `file_id`.
191    pub fn with_caption<T>(mut self, value: T) -> Self
192    where
193        T: Into<String>,
194    {
195        self.form.insert_field("caption", value.into());
196        self
197    }
198
199    /// Sets a new list of caption entities.
200    ///
201    /// # Arguments
202    ///
203    /// * `value` - The list of special entities that appear in the caption.
204    ///
205    /// Caption parse mode will be set to [`None`] when this method is called.
206    pub fn with_caption_entities<T>(mut self, value: T) -> Result<Self, TextEntityError>
207    where
208        T: IntoIterator<Item = TextEntity>,
209    {
210        let value: TextEntities = value.into_iter().collect();
211        self.form.insert_field("caption_entities", value.serialize()?);
212        self.form.remove_field("parse_mode");
213        Ok(self)
214    }
215
216    /// Sets a new caption parse mode.
217    ///
218    /// # Arguments
219    ///
220    /// * `value` - Parse mode.
221    ///
222    /// Caption entities will be set to [`None`] when this method is called.
223    pub fn with_caption_parse_mode(mut self, value: ParseMode) -> Self {
224        self.form.insert_field("parse_mode", value);
225        self.form.remove_field("caption_entities");
226        self
227    }
228
229    /// Sets a new direct messages topic ID
230    ///
231    /// * `value` - Identifier of the direct messages topic to which the message will be sent.
232    ///
233    /// Required if the message is sent to a direct messages chat.
234    pub fn with_direct_messages_topic_id(mut self, value: Integer) -> Self {
235        self.form.insert_field("direct_messages_topic_id", value);
236        self
237    }
238
239    /// Sets a new value for the `disable_notification` flag.
240    ///
241    /// # Arguments
242    ///
243    /// * `value` - Indicates whether to send the message silently or not;
244    ///   a user will receive a notification without sound.
245    pub fn with_disable_notification(mut self, value: bool) -> Self {
246        self.form.insert_field("disable_notification", value);
247        self
248    }
249
250    /// Sets a new duration.
251    ///
252    /// # Arguments
253    ///
254    /// * `value` - Duration in seconds.
255    pub fn with_duration(mut self, value: Integer) -> Self {
256        self.form.insert_field("duration", value);
257        self
258    }
259
260    /// Sets a new value for the `has_spoiler` flag.
261    ///
262    /// # Arguments
263    ///
264    /// * `value` - Indicates whether to cover with a spoiler animation.
265    pub fn with_has_spoiler(mut self, value: bool) -> Self {
266        self.form.insert_field("has_spoiler", value);
267        self
268    }
269
270    /// Sets a new height.
271    ///
272    /// # Arguments
273    ///
274    /// * `value` - Height.
275    pub fn with_height(mut self, value: Integer) -> Self {
276        self.form.insert_field("height", value);
277        self
278    }
279
280    /// Sets a new message effect ID.
281    ///
282    /// # Arguments
283    ///
284    /// * `value` - Unique identifier of the message effect to be added to the message; for private chats only.
285    pub fn with_message_effect_id<T>(mut self, value: T) -> Self
286    where
287        T: Into<String>,
288    {
289        self.form.insert_field("message_effect_id", value.into());
290        self
291    }
292
293    /// Sets a new message thread ID.
294    ///
295    /// # Arguments
296    ///
297    /// * `value` - Unique identifier of the target message thread;
298    ///   supergroups only.
299    pub fn with_message_thread_id(mut self, value: Integer) -> Self {
300        self.form.insert_field("message_thread_id", value);
301        self
302    }
303
304    /// Sets a new value for the `protect_content` flag.
305    ///
306    /// # Arguments
307    ///
308    /// * `value` - Indicates whether to protect the contents
309    ///   of the sent message from forwarding and saving.
310    pub fn with_protect_content(mut self, value: bool) -> Self {
311        self.form.insert_field("protect_content", value);
312        self
313    }
314
315    /// Sets a new reply markup.
316    ///
317    /// # Arguments
318    ///
319    /// * `value` - Reply markup.
320    pub fn with_reply_markup<T>(mut self, value: T) -> Result<Self, ReplyMarkupError>
321    where
322        T: Into<ReplyMarkup>,
323    {
324        let value = value.into();
325        self.form.insert_field("reply_markup", value.serialize()?);
326        Ok(self)
327    }
328
329    /// Sets new reply parameters.
330    ///
331    /// # Arguments
332    ///
333    /// * `value` - Description of the message to reply to.
334    pub fn with_reply_parameters(mut self, value: ReplyParameters) -> Result<Self, ReplyParametersError> {
335        self.form.insert_field("reply_parameters", value.serialize()?);
336        Ok(self)
337    }
338
339    /// Sets a new value for the `show_caption_above_media` flag.
340    ///
341    /// # Arguments
342    ///
343    /// `value` - Whether the caption must be shown above the message media.
344    pub fn with_show_caption_above_media(mut self, value: bool) -> Self {
345        self.form.insert_field("show_caption_above_media", value);
346        self
347    }
348
349    /// Sets a new suggested post parameters.
350    ///
351    /// # Arguments
352    ///
353    /// * `value` - An object containing the parameters of the suggested post to send.
354    ///
355    /// For direct messages chats only.
356    ///
357    /// If the message is sent as a reply to another suggested post, then that suggested post is automatically declined.
358    pub fn with_suggested_post_parameters(
359        mut self,
360        value: &SuggestedPostParameters,
361    ) -> Result<Self, SuggestedPostParametersError> {
362        let value = serde_json::to_string(value).map_err(SuggestedPostParametersError::Serialize)?;
363        self.form.insert_field("suggested_post_parameters", value);
364        Ok(self)
365    }
366
367    /// Sets a new thumbnail.
368    ///
369    /// # Arguments
370    ///
371    /// * `value` - Thumbnail.
372    ///
373    /// The thumbnail should be in JPEG format and less than 200 kB in size.
374    /// A thumbnail‘s width and height should not exceed 320.
375    /// Ignored if the file is not uploaded using `multipart/form-data`.
376    /// Thumbnails can’t be reused and can be only uploaded as a new file.
377    pub fn with_thumbnail<T>(mut self, value: T) -> Result<Self, SendAnimationError>
378    where
379        T: Into<InputFile>,
380    {
381        let value = value.into();
382        if matches!(value, InputFile::Id(_)) {
383            return Err(SendAnimationError::InvalidThumbnail);
384        }
385        self.form.insert_field("thumbnail", value);
386        Ok(self)
387    }
388
389    /// Sets a new width.
390    ///
391    /// # Arguments
392    ///
393    /// * `value` - Width.
394    pub fn with_width(mut self, value: Integer) -> Self {
395        self.form.insert_field("width", value);
396        self
397    }
398}
399
400impl Method for SendAnimation {
401    type Response = Message;
402
403    fn into_payload(self) -> Payload {
404        Payload::form("sendAnimation", self.form)
405    }
406}
407
408/// Represents an error when sending an animation.
409#[derive(Debug)]
410pub enum SendAnimationError {
411    /// Thumbnails can not be reused.
412    InvalidThumbnail,
413}
414
415impl fmt::Display for SendAnimationError {
416    fn fmt(&self, out: &mut fmt::Formatter<'_>) -> fmt::Result {
417        match self {
418            Self::InvalidThumbnail => write!(out, "thumbnails can’t be reused and can be only uploaded as a new file"),
419        }
420    }
421}
422
423impl Error for SendAnimationError {}