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