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