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 {}