1use std::path::PathBuf;
2
3use crate::games::GameHighScore;
4use crate::gifts::{Gifts, OwnedGifts};
5use crate::inline_mode::{PreparedInlineMessage, SentWebAppMessage};
6use crate::input_file::HasInputFile;
7use crate::input_media::{InputMedia, InputProfilePhoto, InputStoryContent, MediaGroupInputMedia};
8use crate::payments::{StarAmount, StarTransactions};
9use crate::response::{MessageOrBool, MethodResponse};
10use crate::stickers::{Sticker, StickerSet};
11use crate::types::{
12 BotCommand, BotDescription, BotName, BotShortDescription, BusinessConnection,
13 ChatAdministratorRights, ChatFullInfo, ChatInviteLink, ChatMember, File, ForumTopic,
14 MenuButton, Message, MessageId, Poll, Story, User, UserChatBoosts, UserProfilePhotos,
15};
16use crate::updates::{Update, WebhookInfo};
17
18macro_rules! request {
19 ($name:ident, $return:ty) => {
20 paste::paste! {
21 #[doc = "Call the `" $name "` method.\n\nSee <https://core.telegram.org/bots/api#" $name:lower ">."]
22 #[inline(always)]
23 fn [<$name:snake>] (
24 &self,
25 params: &crate::methods::[<$name:camel Params>],
26 ) -> Result<MethodResponse<$return>, Self::Error> {
27 self.request(stringify!($name), Some(params))
28 }
29 }
30 }
31}
32
33macro_rules! request_nb {
35 ($name:ident, $return:ty) => {
36 paste::paste! {
37 #[doc = "Call the `" $name "` method.\n\nSee <https://core.telegram.org/bots/api#" $name:lower ">."]
38 #[inline(always)]
39 fn [<$name:snake>] (
40 &self,
41 ) -> Result<MethodResponse<$return>, Self::Error> {
42 let params: Option<()> = None;
43 self.request(stringify!($name), params)
44 }
45 }
46 }
47}
48
49macro_rules! request_f {
51 ($name:ident, $return:ty, $($fileproperty:ident),+) => {
52 paste::paste! {
53 #[doc = "Call the `" $name "` method.\n\nSee <https://core.telegram.org/bots/api#" $name:lower ">."]
54 fn [<$name:snake>] (
55 &self,
56 params: &crate::methods::[<$name:camel Params>],
57 ) -> Result<MethodResponse<$return>, Self::Error> {
58 let mut files = Vec::new();
59 $(
60 if let Some(path) = params.$fileproperty.clone_path() {
61 files.push((stringify!($fileproperty), path));
62 }
63 )+
64 self.request_with_possible_form_data(stringify!($name), params, files)
65 }
66 }
67 }
68}
69
70pub trait TelegramApi {
71 type Error;
72
73 request!(getUpdates, Vec<Update>);
74 request!(sendMessage, Message);
75 request!(setWebhook, bool);
76 request!(deleteWebhook, bool);
77 request_nb!(getWebhookInfo, WebhookInfo);
78 request_nb!(getMe, User);
79 request_nb!(logOut, bool);
80 request_nb!(close, bool);
81 request!(forwardMessage, Message);
82 request!(forwardMessages, Vec<MessageId>);
83 request!(copyMessage, MessageId);
84 request!(copyMessages, Vec<MessageId>);
85 request_f!(sendPhoto, Message, photo);
86 request_f!(sendAudio, Message, audio, thumbnail);
87
88 fn send_media_group(
89 &self,
90 params: &crate::methods::SendMediaGroupParams,
91 ) -> Result<MethodResponse<Vec<Message>>, Self::Error> {
92 let mut files = Vec::new();
93
94 macro_rules! replace_attach {
95 ($base:ident. $property:ident) => {
96 if let Some(file) = $base.$property.replace_attach_dyn(|| files.len()) {
97 files.push(file);
98 }
99 };
100 }
101
102 let mut params = params.clone();
103 for media in &mut params.media {
104 match media {
105 MediaGroupInputMedia::Audio(audio) => {
106 replace_attach!(audio.media);
107 replace_attach!(audio.thumbnail);
108 }
109 MediaGroupInputMedia::Document(document) => {
110 replace_attach!(document.media);
111 }
112 MediaGroupInputMedia::Photo(photo) => {
113 replace_attach!(photo.media);
114 }
115 MediaGroupInputMedia::Video(video) => {
116 replace_attach!(video.media);
117 replace_attach!(video.cover);
118 replace_attach!(video.thumbnail);
119 }
120 }
121 }
122
123 let files_with_str_names = files
124 .iter()
125 .map(|(key, path)| (key.as_str(), path.clone()))
126 .collect();
127
128 self.request_with_possible_form_data("sendMediaGroup", ¶ms, files_with_str_names)
129 }
130
131 request_f!(sendDocument, Message, document, thumbnail);
132 request_f!(sendVideo, Message, video, cover, thumbnail);
133 request_f!(sendAnimation, Message, animation, thumbnail);
134 request_f!(sendVoice, Message, voice);
135 request_f!(sendVideoNote, Message, video_note, thumbnail);
136 request!(sendPaidMedia, Message);
137 request!(sendLocation, Message);
138 request!(editMessageLiveLocation, MessageOrBool);
139 request!(stopMessageLiveLocation, MessageOrBool);
140 request!(sendChecklist, MessageOrBool);
141 request!(editMessageChecklist, MessageOrBool);
142 request!(sendVenue, Message);
143 request!(sendContact, Message);
144 request!(sendPoll, Message);
145 request!(sendDice, Message);
146 request!(sendChatAction, bool);
147 request!(setMessageReaction, bool);
148 request!(getUserProfilePhotos, UserProfilePhotos);
149 request!(setUserEmojiStatus, bool);
150 request!(getFile, File);
151 request!(banChatMember, bool);
152 request!(unbanChatMember, bool);
153 request!(restrictChatMember, bool);
154 request!(promoteChatMember, bool);
155 request!(setChatAdministratorCustomTitle, bool);
156 request!(banChatSenderChat, bool);
157 request!(unbanChatSenderChat, bool);
158 request!(setChatPermissions, bool);
159 request!(exportChatInviteLink, String);
160 request!(createChatInviteLink, ChatInviteLink);
161 request!(editChatInviteLink, ChatInviteLink);
162 request!(createChatSubscriptionInviteLink, ChatInviteLink);
163 request!(editChatSubscriptionInviteLink, ChatInviteLink);
164 request!(revokeChatInviteLink, ChatInviteLink);
165 request!(approveChatJoinRequest, bool);
166 request!(declineChatJoinRequest, bool);
167
168 fn set_chat_photo(
169 &self,
170 params: &crate::methods::SetChatPhotoParams,
171 ) -> Result<MethodResponse<bool>, Self::Error> {
172 let photo = ¶ms.photo;
173 self.request_with_form_data("setChatPhoto", params, vec![("photo", photo.path.clone())])
174 }
175
176 request!(deleteChatPhoto, bool);
177 request!(setChatTitle, bool);
178 request!(setChatDescription, bool);
179 request!(pinChatMessage, bool);
180 request!(unpinChatMessage, bool);
181 request!(unpinAllChatMessages, bool);
182 request!(leaveChat, bool);
183 request!(getChat, ChatFullInfo);
184 request!(getChatAdministrators, Vec<ChatMember>);
185 request!(getChatMemberCount, u32);
186 request!(getChatMember, ChatMember);
187 request!(setChatStickerSet, bool);
188 request!(deleteChatStickerSet, bool);
189 request_nb!(getForumTopicIconStickers, Vec<Sticker>);
190 request!(createForumTopic, ForumTopic);
191 request!(editForumTopic, bool);
192 request!(closeForumTopic, bool);
193 request!(reopenForumTopic, bool);
194 request!(deleteForumTopic, bool);
195 request!(unpinAllForumTopicMessages, bool);
196 request!(editGeneralForumTopic, bool);
197 request!(closeGeneralForumTopic, bool);
198 request!(reopenGeneralForumTopic, bool);
199 request!(hideGeneralForumTopic, bool);
200 request!(unhideGeneralForumTopic, bool);
201 request!(answerCallbackQuery, bool);
202 request!(getUserChatBoosts, UserChatBoosts);
203 request!(getBusinessConnection, BusinessConnection);
204 request!(getMyCommands, Vec<BotCommand>);
205 request!(setMyCommands, bool);
206 request!(deleteMyCommands, bool);
207 request!(setMyName, bool);
208 request!(getMyName, BotName);
209 request!(setMyDescription, bool);
210 request!(getMyDescription, BotDescription);
211 request!(setMyShortDescription, bool);
212 request!(getMyShortDescription, BotShortDescription);
213 request!(answerInlineQuery, bool);
214 request!(editMessageText, MessageOrBool);
215 request!(editMessageCaption, MessageOrBool);
216
217 fn edit_message_media(
218 &self,
219 params: &crate::methods::EditMessageMediaParams,
220 ) -> Result<MethodResponse<MessageOrBool>, Self::Error> {
221 let mut files = Vec::new();
222
223 macro_rules! replace_attach {
224 ($base:ident. $property:ident) => {{
225 const NAME: &str = concat!(stringify!($base), "_", stringify!($property));
226 if let Some(file) = $base.$property.replace_attach(NAME) {
227 files.push((NAME, file));
228 }
229 }};
230 }
231
232 let mut params = params.clone();
233 match &mut params.media {
234 InputMedia::Animation(animation) => {
235 replace_attach!(animation.media);
236 replace_attach!(animation.thumbnail);
237 }
238 InputMedia::Document(document) => {
239 replace_attach!(document.media);
240 replace_attach!(document.thumbnail);
241 }
242 InputMedia::Audio(audio) => {
243 replace_attach!(audio.media);
244 replace_attach!(audio.thumbnail);
245 }
246 InputMedia::Photo(photo) => {
247 replace_attach!(photo.media);
248 }
249 InputMedia::Video(video) => {
250 replace_attach!(video.media);
251 replace_attach!(video.cover);
252 replace_attach!(video.thumbnail);
253 }
254 }
255
256 self.request_with_possible_form_data("editMessageMedia", ¶ms, files)
257 }
258
259 request!(editMessageReplyMarkup, MessageOrBool);
260 request!(stopPoll, Poll);
261 request!(deleteMessage, bool);
262 request!(deleteMessages, bool);
263 request_f!(sendSticker, Message, sticker);
264 request!(getStickerSet, StickerSet);
265
266 fn upload_sticker_file(
267 &self,
268 params: &crate::methods::UploadStickerFileParams,
269 ) -> Result<MethodResponse<File>, Self::Error> {
270 let sticker = ¶ms.sticker;
271 self.request_with_form_data(
272 "uploadStickerFile",
273 params,
274 vec![("sticker", sticker.path.clone())],
275 )
276 }
277
278 fn create_new_sticker_set(
279 &self,
280 params: &crate::methods::CreateNewStickerSetParams,
281 ) -> Result<MethodResponse<bool>, Self::Error> {
282 let mut files = Vec::new();
283
284 let mut params = params.clone();
285 for (index, sticker) in params.stickers.iter_mut().enumerate() {
286 if let Some(file) = sticker.sticker.replace_attach_dyn(|| index) {
287 files.push(file);
288 }
289 }
290
291 let files_with_str_names = files
292 .iter()
293 .map(|(key, path)| (key.as_str(), path.clone()))
294 .collect();
295
296 self.request_with_possible_form_data("createNewStickerSet", ¶ms, files_with_str_names)
297 }
298
299 request!(getCustomEmojiStickers, Vec<Sticker>);
300
301 fn add_sticker_to_set(
302 &self,
303 params: &crate::methods::AddStickerToSetParams,
304 ) -> Result<MethodResponse<bool>, Self::Error> {
305 let mut files = Vec::new();
306 let mut params = params.clone();
307 if let Some(file) = params.sticker.sticker.replace_attach("sticker_upload") {
308 files.push(("sticker_upload", file));
309 }
310 self.request_with_possible_form_data("addStickerToSet", params, files)
311 }
312
313 request!(setStickerPositionInSet, bool);
314 request!(deleteStickerFromSet, bool);
315 request!(replaceStickerInSet, bool);
316 request!(setStickerEmojiList, bool);
317 request!(setStickerKeywords, bool);
318 request!(setStickerMaskPosition, bool);
319 request!(setStickerSetTitle, bool);
320 request_f!(setStickerSetThumbnail, bool, thumbnail);
321 request!(setCustomEmojiStickerSetThumbnail, bool);
322 request!(deleteStickerSet, bool);
323 request_nb!(getAvailableGifts, Gifts);
324 request!(sendGift, bool);
325 request!(giftPremiumSubscription, bool);
326 request!(verifyUser, bool);
327 request!(verifyChat, bool);
328 request!(removeUserVerification, bool);
329 request!(removeChatVerification, bool);
330 request!(readBusinessMessage, bool);
331 request!(deleteBusinessMessages, bool);
332 request!(setBusinessAccountName, bool);
333 request!(setBusinessAccountUsername, bool);
334 request!(setBusinessAccountBio, bool);
335
336 fn set_business_account_profile_photo(
337 &self,
338 params: &crate::methods::SetBusinessAccountProfilePhotoParams,
339 ) -> Result<MethodResponse<bool>, Self::Error> {
340 let mut files = Vec::new();
341
342 let mut params = params.clone();
343 match &mut params.photo {
344 InputProfilePhoto::Static(photo_static) => {
345 if let Some(file) = photo_static.photo.replace_attach("photo_static") {
346 files.push(("photo_static", file));
347 }
348 }
349 InputProfilePhoto::Animated(photo_animated) => {
350 if let Some(file) = photo_animated.animation.replace_attach("photo_animated") {
351 files.push(("photo_animated", file));
352 }
353 }
354 }
355
356 self.request_with_possible_form_data("setBusinessAccountProfilePhoto", params, files)
357 }
358
359 request!(removeBusinessAccountProfilePhoto, bool);
360 request!(setBusinessAccountGiftSettings, bool);
361 request!(getBusinessAccountStarBalance, StarAmount);
362 request!(transferBusinessAccountStars, bool);
363 request!(getBusinessAccountGifts, OwnedGifts);
364 request!(convertGiftToStars, bool);
365 request!(upgradeGift, bool);
366 request!(transferGift, bool);
367
368 fn post_story(
369 &self,
370 params: &crate::methods::PostStoryParams,
371 ) -> Result<MethodResponse<Story>, Self::Error> {
372 let mut files = Vec::new();
373
374 let mut params = params.clone();
375
376 match &mut params.content {
377 InputStoryContent::Photo(photo_content) => {
378 if let Some(file) = photo_content.photo.replace_attach("photo_content") {
379 files.push(("photo_content", file));
380 }
381 }
382 InputStoryContent::Video(video_content) => {
383 if let Some(file) = video_content.video.replace_attach("video_content") {
384 files.push(("video_content", file));
385 }
386 }
387 }
388
389 self.request_with_possible_form_data("postStory", params, files)
390 }
391
392 fn edit_story(
393 &self,
394 params: &crate::methods::EditStoryParams,
395 ) -> Result<MethodResponse<Story>, Self::Error> {
396 let mut files = Vec::new();
397
398 let mut params = params.clone();
399
400 match &mut params.content {
401 InputStoryContent::Photo(photo_content) => {
402 if let Some(file) = photo_content.photo.replace_attach("photo_content") {
403 files.push(("photo_content", file));
404 }
405 }
406 InputStoryContent::Video(video_content) => {
407 if let Some(file) = video_content.video.replace_attach("video_content") {
408 files.push(("video_content", file));
409 }
410 }
411 }
412
413 self.request_with_possible_form_data("editStory", params, files)
414 }
415
416 request!(deleteStory, bool);
417 request!(sendInvoice, Message);
418 request!(createInvoiceLink, String);
419 request!(answerShippingQuery, bool);
420 request!(answerPreCheckoutQuery, bool);
421 request_nb!(getMyStarBalance, u32);
422 request!(getStarTransactions, StarTransactions);
423 request!(refundStarPayment, bool);
424 request!(editUserStarSubscription, bool);
425 request!(sendGame, Message);
426 request!(setGameScore, MessageOrBool);
427 request!(getGameHighScores, Vec<GameHighScore>);
428 request!(setMyDefaultAdministratorRights, bool);
429 request!(getMyDefaultAdministratorRights, ChatAdministratorRights);
430 request!(answerWebAppQuery, SentWebAppMessage);
431 request!(savePreparedInlineMessage, PreparedInlineMessage);
432 request!(setChatMenuButton, bool);
433 request!(getChatMenuButton, MenuButton);
434 request!(unpinAllGeneralForumTopicMessages, bool);
435 request!(setPassportDataErrors, bool);
436
437 fn request_with_possible_form_data<Params, Output>(
438 &self,
439 method_name: &str,
440 params: Params,
441 files: Vec<(&str, PathBuf)>,
442 ) -> Result<Output, Self::Error>
443 where
444 Params: serde::ser::Serialize + std::fmt::Debug,
445 Output: serde::de::DeserializeOwned,
446 {
447 if files.is_empty() {
448 self.request(method_name, Some(params))
449 } else {
450 self.request_with_form_data(method_name, params, files)
451 }
452 }
453
454 fn request<Params, Output>(
455 &self,
456 method: &str,
457 params: Option<Params>,
458 ) -> Result<Output, Self::Error>
459 where
460 Params: serde::ser::Serialize + std::fmt::Debug,
461 Output: serde::de::DeserializeOwned;
462
463 fn request_with_form_data<Params, Output>(
464 &self,
465 method: &str,
466 params: Params,
467 files: Vec<(&str, PathBuf)>,
468 ) -> Result<Output, Self::Error>
469 where
470 Params: serde::ser::Serialize + std::fmt::Debug,
471 Output: serde::de::DeserializeOwned;
472}