1use std::ops::Not;
2
3use hyper::Body;
4use hyper_multipart_rfc7578::client::{multipart, multipart::Form};
5use serde::{Serialize, Serializer};
6use serde_json::Value;
7
8pub use answer_callback_query::*;
9pub use delete_chat_photo::*;
10pub use delete_chat_sticker_set::*;
11pub use delete_message::*;
12pub use edit_live_location::*;
13pub use edit_message_caption::*;
14pub use edit_message_media::*;
15pub use edit_message_reply_markup::*;
16pub use edit_message_text::*;
17pub use export_chat_invite_link::*;
18pub use forward_message::*;
19pub use get_chat::*;
20pub use get_chat_administrators::*;
21pub use get_chat_member::*;
22pub use get_chat_members_count::*;
23pub use get_file::*;
24pub use get_me::*;
25pub use get_updates::*;
26pub use get_user_profile_photos::*;
27pub use kick_chat_member::*;
28pub use leave_chat::*;
29pub use pin_chat_message::*;
30pub use promote_chat_member::*;
31pub use restrict_chat_member::*;
32pub use send_animation::*;
33pub use send_audio::*;
34pub use send_chat_action::*;
35pub use send_contact::*;
36pub use send_document::*;
37pub use send_location::*;
38pub use send_media_group::*;
39pub use send_message::*;
40pub use send_photo::*;
41pub use send_poll::*;
42pub use send_venue::*;
43pub use send_video::*;
44pub use send_video_note::*;
45pub use send_voice::*;
46pub use set_chat_description::*;
47pub use set_chat_photo::*;
48pub use set_chat_sticker_set::*;
49pub use set_chat_title::*;
50pub use stop_live_location::*;
51pub use stop_poll::*;
52pub use unban_chat_member::*;
53pub use unpin_chat_message::*;
54
55use crate::error::Error;
56use std::io::Cursor;
57
58mod answer_callback_query;
59mod delete_chat_photo;
60mod delete_chat_sticker_set;
61mod delete_message;
62mod edit_live_location;
63mod edit_message_caption;
64mod edit_message_media;
65mod edit_message_reply_markup;
66mod edit_message_text;
67mod export_chat_invite_link;
68mod forward_message;
69mod get_chat;
70mod get_chat_administrators;
71mod get_chat_member;
72mod get_chat_members_count;
73mod get_file;
74mod get_me;
75mod get_updates;
76mod get_user_profile_photos;
77mod kick_chat_member;
78mod leave_chat;
79mod pin_chat_message;
80mod promote_chat_member;
81mod restrict_chat_member;
82mod send_animation;
83mod send_audio;
84mod send_chat_action;
85mod send_contact;
86mod send_document;
87mod send_location;
88mod send_media_group;
89mod send_message;
90mod send_photo;
91mod send_poll;
92mod send_venue;
93mod send_video;
94mod send_video_note;
95mod send_voice;
96mod set_chat_description;
97mod set_chat_photo;
98mod set_chat_sticker_set;
99mod set_chat_title;
100mod stop_live_location;
101mod stop_poll;
102mod unban_chat_member;
103mod unpin_chat_message;
104
105pub trait Request: Serialize + Sized {
107 type ResponseType;
108
109 fn method(&self) -> &'static str;
110
111 fn set_http_request_body(
112 self,
113 request_builder: hyper::http::request::Builder,
114 ) -> Result<hyper::http::request::Request<Body>, Error> {
115 add_json_body(request_builder, &self)
116 }
117}
118
119pub(crate) fn add_json_body<S: Serialize + Sized>(
120 request_builder: hyper::http::request::Builder,
121 serializable: &S,
122) -> Result<hyper::http::request::Request<Body>, Error> {
123 let json_bytes = serde_json::to_vec(serializable).map_err(Error::Serde)?;
124 request_builder
125 .header("content-type", "application/json")
126 .body(Body::from(json_bytes))
127 .map_err(|x| Error::RequestBuilt(x.to_string()))
128}
129
130pub(crate) fn add_form_body(
131 request_builder: hyper::http::request::Builder,
132 form: Form<'static>,
133) -> Result<hyper::http::request::Request<Body>, Error> {
134 form.set_body_convert::<hyper::Body, multipart::Body>(request_builder)
135 .map_err(|x| Error::RequestBuilt(x.to_string()))
136}
137
138pub(crate) fn add_file_to_form(form: &mut Form, file: FileKind, upload_type: Option<&str>) {
139 if let FileKind::InputFile {
140 name,
141 content,
142 thumb,
143 } = file
144 {
145 form.add_reader_file(upload_type.unwrap_or(name), Cursor::new(content), name);
146 if let Some(thumb) = thumb {
147 let thumb_name = format!("thumb_{}", name);
148 form.add_reader_file(&thumb_name, Cursor::new(thumb), thumb_name.as_str());
149 form.add_text("thumb", format!("attach://{}", &thumb_name));
150 }
151 }
152}
153
154pub(crate) fn add_fields_to_form<S: Serialize + Sized>(
155 form: &mut Form<'static>,
156 serializable: &S,
157) -> Result<(), Error> {
158 let json = serde_json::to_value(serializable).map_err(Error::Serde)?;
159 if let Value::Object(map) = json {
160 for (k, v) in map {
161 match v {
162 Value::String(s) => form.add_text(k, s),
163 other => form.add_text(k, other.to_string()),
164 }
165 }
166 }
167 Ok(())
168}
169
170#[derive(Serialize, Debug, Clone)]
172#[serde(untagged)]
173pub enum FileKind<'a> {
174 FileId(&'a str),
176
177 Url(&'a str),
180
181 #[serde(serialize_with = "FileKind::serialize_attach")]
183 InputFile {
184 name: &'a str,
186
187 content: Vec<u8>,
189
190 thumb: Option<Vec<u8>>,
193 },
194}
195
196impl<'a> FileKind<'a> {
197 pub(crate) fn is_input_file(&self) -> bool {
198 matches!(self, FileKind::InputFile { .. })
199 }
200
201 pub(crate) fn serialize_attach<S: Serializer>(
202 field0: &str,
203 _: &[u8],
204 _: &Option<Vec<u8>>,
205 s: S,
206 ) -> Result<S::Ok, S::Error> {
207 s.serialize_str(&format!("attach://{}", field0))
208 }
209}
210
211#[derive(Serialize, Debug, Clone)]
213#[serde(untagged)]
214pub enum ChatId<'a> {
215 Id(i64),
217 Username(&'a str),
219}
220
221impl<'a> From<i64> for ChatId<'a> {
222 fn from(x: i64) -> Self {
223 ChatId::Id(x)
224 }
225}
226
227impl<'a> From<&'a str> for ChatId<'a> {
228 fn from(x: &'a str) -> Self {
229 ChatId::Username(x)
230 }
231}
232
233#[derive(Serialize, Debug, Clone)]
235pub struct InputMediaPhoto<'a> {
236 pub media: FileKind<'a>,
238
239 #[serde(skip_serializing_if = "Option::is_none")]
241 pub caption: Option<&'a str>,
242
243 #[serde(skip_serializing_if = "Option::is_none")]
247 pub parse_mode: Option<ParseMode>,
248}
249
250impl<'a> InputMediaPhoto<'a> {
251 pub fn new(media: FileKind<'a>) -> Self {
252 Self {
253 media,
254 caption: None,
255 parse_mode: None,
256 }
257 }
258}
259
260#[derive(Serialize, Debug, Clone)]
262pub struct InputMediaVideo<'a> {
263 pub media: FileKind<'a>,
265
266 #[serde(skip_serializing_if = "Option::is_none")]
268 pub caption: Option<&'a str>,
269
270 #[serde(skip_serializing_if = "Option::is_none")]
274 pub parse_mode: Option<ParseMode>,
275
276 #[serde(skip_serializing_if = "Option::is_none")]
278 pub duration: Option<i64>,
279
280 #[serde(skip_serializing_if = "Option::is_none")]
282 pub width: Option<i64>,
283
284 #[serde(skip_serializing_if = "Option::is_none")]
286 pub height: Option<i64>,
287
288 #[serde(skip_serializing_if = "Not::not")]
290 pub supports_streaming: bool,
291}
292
293impl<'a> InputMediaVideo<'a> {
294 pub fn new(media: FileKind<'a>) -> Self {
295 Self {
296 media,
297 caption: None,
298 parse_mode: None,
299 duration: None,
300 width: None,
301 height: None,
302 supports_streaming: false,
303 }
304 }
305}
306
307#[derive(Serialize, Debug, Clone)]
309pub struct InputMediaAnimation<'a> {
310 pub media: FileKind<'a>,
312
313 #[serde(skip_serializing_if = "Option::is_none")]
315 pub caption: Option<&'a str>,
316
317 #[serde(skip_serializing_if = "Option::is_none")]
321 pub parse_mode: Option<ParseMode>,
322
323 #[serde(skip_serializing_if = "Option::is_none")]
325 pub duration: Option<i64>,
326
327 #[serde(skip_serializing_if = "Option::is_none")]
329 pub width: Option<i64>,
330
331 #[serde(skip_serializing_if = "Option::is_none")]
333 pub height: Option<i64>,
334}
335
336impl<'a> InputMediaAnimation<'a> {
337 pub fn new(media: FileKind<'a>) -> Self {
338 Self {
339 media,
340 caption: None,
341 parse_mode: None,
342 duration: None,
343 width: None,
344 height: None,
345 }
346 }
347}
348
349#[derive(Serialize, Debug, Clone)]
351pub struct InputMediaDocument<'a> {
352 pub media: FileKind<'a>,
354
355 #[serde(skip_serializing_if = "Option::is_none")]
357 pub caption: Option<&'a str>,
358
359 #[serde(skip_serializing_if = "Option::is_none")]
363 pub parse_mode: Option<ParseMode>,
364}
365
366impl<'a> InputMediaDocument<'a> {
367 pub fn new(media: FileKind<'a>) -> Self {
368 Self {
369 media,
370 caption: None,
371 parse_mode: None,
372 }
373 }
374}
375
376#[derive(Serialize, Debug, Clone)]
378pub struct InputMediaAudio<'a> {
379 pub media: FileKind<'a>,
381
382 #[serde(skip_serializing)]
385 pub thumb: Option<Vec<u8>>,
386
387 #[serde(skip_serializing_if = "Option::is_none")]
389 pub caption: Option<&'a str>,
390
391 #[serde(skip_serializing_if = "Option::is_none")]
395 pub parse_mode: Option<ParseMode>,
396
397 #[serde(skip_serializing_if = "Option::is_none")]
399 pub duration: Option<i64>,
400
401 #[serde(skip_serializing_if = "Option::is_none")]
403 pub performer: Option<&'a str>,
404
405 #[serde(skip_serializing_if = "Option::is_none")]
407 pub title: Option<&'a str>,
408}
409
410impl<'a> InputMediaAudio<'a> {
411 pub fn new(media: FileKind<'a>) -> Self {
412 Self {
413 media,
414 thumb: None,
415 caption: None,
416 parse_mode: None,
417 duration: None,
418 performer: None,
419 title: None,
420 }
421 }
422}
423
424#[derive(Serialize, Debug, Clone)]
425#[serde(tag = "type")]
426pub enum InputMedia<'a> {
427 #[serde(rename = "video")]
428 Video(InputMediaVideo<'a>),
429
430 #[serde(rename = "photo")]
431 Photo(InputMediaPhoto<'a>),
432
433 #[serde(rename = "animation")]
434 Animation(InputMediaAnimation<'a>),
435
436 #[serde(rename = "document")]
437 Document(InputMediaDocument<'a>),
438
439 #[serde(rename = "audio")]
440 Audio(InputMediaAudio<'a>),
441}
442
443impl<'a> InputMedia<'a> {
444 fn get_file(self) -> FileKind<'a> {
445 match self {
446 InputMedia::Photo(x) => x.media,
447 InputMedia::Video(x) => x.media,
448 InputMedia::Animation(x) => x.media,
449 InputMedia::Document(x) => x.media,
450 InputMedia::Audio(x) => x.media,
451 }
452 }
453
454 fn contains_input_file(&self) -> bool {
455 match &self {
456 InputMedia::Video(x) => x.media.is_input_file(),
457 InputMedia::Photo(x) => x.media.is_input_file(),
458 InputMedia::Animation(x) => x.media.is_input_file(),
459 InputMedia::Document(x) => x.media.is_input_file(),
460 InputMedia::Audio(x) => x.media.is_input_file(),
461 }
462 }
463}
464
465#[derive(Serialize, Debug, Clone)]
467#[serde(untagged)]
468pub enum ReplyMarkup<'a> {
469 InlineKeyboard(InlineKeyboard<'a>),
470 ReplyKeyboardMarkup(ReplyKeyboardMarkup<'a>),
471 ReplyKeyboardRemove(ReplyKeyboardRemove),
472 ForceReply(ForceReply),
473}
474
475#[derive(Serialize, Debug, Clone)]
478pub struct InlineKeyboard<'a> {
479 pub inline_keyboard: &'a [Vec<InlineKeyboardButton<'a>>],
481}
482
483#[derive(Serialize, Debug, Clone)]
485pub struct ReplyKeyboardMarkup<'a> {
486 pub keyboard: &'a [Vec<KeyboardButton<'a>>],
488
489 #[serde(skip_serializing_if = "Not::not")]
493 pub resize_keyboard: bool,
494
495 #[serde(skip_serializing_if = "Not::not")]
500 pub one_time_keyboard: bool,
501 #[serde(skip_serializing_if = "Not::not")]
508 pub selective: bool,
509}
510
511#[derive(Serialize, Debug, Clone)]
516pub struct ReplyKeyboardRemove {
517 #[serde(skip_serializing_if = "Not::not")]
521 pub remove_keyboard: bool,
522
523 #[serde(skip_serializing_if = "Not::not")]
531 pub selective: bool,
532}
533
534#[derive(Serialize, Debug, Clone)]
539pub struct ForceReply {
540 #[serde(skip_serializing_if = "Not::not")]
542 pub force_reply: bool,
543 #[serde(skip_serializing_if = "Not::not")]
547 pub selective: bool,
548}
549
550#[derive(Serialize, Debug, Clone)]
551#[serde(untagged)]
552pub enum InlineKeyboardButton<'a> {
553 Url {
554 text: &'a str,
556 url: &'a str,
558 },
559 CallbackData {
560 text: &'a str,
562 callback_data: &'a str,
565 },
566}
567
568#[derive(Serialize, Debug, Clone)]
570#[serde(untagged)]
571pub enum KeyboardButton<'a> {
572 Text(&'a str),
574}
575
576#[derive(Serialize, Debug, Clone, Copy)]
577pub enum ParseMode {
578 Html,
579 Markdown,
580}
581
582#[derive(Serialize, Debug, Clone)]
583#[serde(untagged)]
584pub enum MessageOrInlineMessageId<'a> {
585 Inline {
586 inline_message_id: &'a str,
587 },
588 Chat {
589 chat_id: ChatId<'a>,
590 message_id: i64,
591 },
592}