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