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