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