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