├── .editorconfig ├── .gitignore ├── .gitlab-ci.yml ├── Cargo.toml ├── LICENSE ├── clippy.toml ├── examples ├── assets │ ├── gif.mp4 │ ├── photo.jpg │ └── stickers │ │ ├── 1.png │ │ └── 2.png ├── attachments.rs ├── commands.rs ├── download.rs ├── echo.rs ├── game.rs ├── get_me.rs ├── keyboard.rs ├── location.rs ├── messenger.rs ├── payment.rs ├── polls.rs ├── predicates.rs ├── proxy.rs ├── questionary.rs ├── sticker_packs.rs ├── tutorial.rs └── webhook.rs ├── logo.svg ├── readme.md ├── rustfmt.toml └── src ├── bot.rs ├── bot ├── builder.rs └── inner_bot.rs ├── compositors.rs ├── compositors └── state.rs ├── connectors.rs ├── contexts.rs ├── contexts ├── animation.rs ├── any_update.rs ├── audio.rs ├── changed_auto_delete_timer.rs ├── chat_member.rs ├── chosen_inline.rs ├── command.rs ├── connected_website.rs ├── contact.rs ├── created_group.rs ├── data_callback.rs ├── deleted_chat_photo.rs ├── dice.rs ├── document.rs ├── edited_animation.rs ├── edited_audio.rs ├── edited_command.rs ├── edited_document.rs ├── edited_location.rs ├── edited_photo.rs ├── edited_text.rs ├── edited_video.rs ├── ended_voice_chat.rs ├── fields.rs ├── fields │ ├── album.rs │ ├── attachments.rs │ ├── callback.rs │ ├── context.rs │ ├── messages.rs │ └── texts.rs ├── game.rs ├── game_callback.rs ├── inline.rs ├── invited_voice_chat_participants.rs ├── invoice.rs ├── left_member.rs ├── location.rs ├── macros.rs ├── macros │ ├── callback.rs │ ├── common.rs │ ├── edited_message.rs │ ├── media_message.rs │ └── message_base.rs ├── methods.rs ├── methods │ ├── callback.rs │ ├── copyable.rs │ ├── forwardable.rs │ ├── message.rs │ └── pinnable.rs ├── migration.rs ├── my_chat_member.rs ├── new_chat_photo.rs ├── new_chat_title.rs ├── new_members.rs ├── passport.rs ├── payment.rs ├── photo.rs ├── pinned_message.rs ├── poll.rs ├── poll_answer.rs ├── pre_checkout.rs ├── proximity_alert.rs ├── scheduled_voice_chat.rs ├── shipping.rs ├── started_voice_chat.rs ├── sticker.rs ├── text.rs ├── unhandled.rs ├── updated_poll.rs ├── venue.rs ├── video.rs ├── video_note.rs └── voice.rs ├── download_file.rs ├── errors.rs ├── errors ├── download.rs ├── http_webhook.rs ├── https_webhook.rs ├── method_call.rs ├── polling.rs └── polling_setup.rs ├── event_loop.rs ├── event_loop ├── handlers_macros.rs ├── polling.rs ├── webhook.rs └── webhook │ ├── http.rs │ └── https.rs ├── internal.rs ├── lib.rs ├── markup.rs ├── markup ├── bold.rs ├── code_block.rs ├── html.rs ├── inline_code.rs ├── italic.rs ├── link.rs ├── markdown_v2.rs ├── raw.rs ├── strikethrough.rs └── underline.rs ├── methods.rs ├── methods ├── add_sticker_to_set.rs ├── answer_callback_query.rs ├── answer_inline_query.rs ├── answer_pre_checkout_query.rs ├── answer_shipping_query.rs ├── ban_chat_member.rs ├── call_method.rs ├── close.rs ├── copy_message.rs ├── create_chat_invite_link.rs ├── create_new_sticker_set.rs ├── delete_chat_photo.rs ├── delete_chat_sticker_set.rs ├── delete_message.rs ├── delete_sticker_from_set.rs ├── delete_webhook.rs ├── edit_chat_invite_link.rs ├── edit_inline_caption.rs ├── edit_inline_location.rs ├── edit_inline_media.rs ├── edit_inline_reply_markup.rs ├── edit_inline_text.rs ├── edit_message_caption.rs ├── edit_message_location.rs ├── edit_message_media.rs ├── edit_message_reply_markup.rs ├── edit_message_text.rs ├── export_chat_invite_link.rs ├── forward_message.rs ├── get_chat.rs ├── get_chat_administrators.rs ├── get_chat_member.rs ├── get_chat_member_count.rs ├── get_file.rs ├── get_inline_game_high_scores.rs ├── get_me.rs ├── get_message_game_high_scores.rs ├── get_my_commands.rs ├── get_sticker_set.rs ├── get_updates.rs ├── get_user_profile_photos.rs ├── get_webhook_info.rs ├── leave_chat.rs ├── log_out.rs ├── pin_chat_message.rs ├── promote_chat_member.rs ├── restrict_chat_member.rs ├── revoke_chat_invite_link.rs ├── send_animation.rs ├── send_audio.rs ├── send_chat_action.rs ├── send_contact.rs ├── send_dice.rs ├── send_document.rs ├── send_game.rs ├── send_invoice.rs ├── send_location.rs ├── send_media_group.rs ├── send_message.rs ├── send_photo.rs ├── send_poll.rs ├── send_sticker.rs ├── send_venue.rs ├── send_video.rs ├── send_video_note.rs ├── send_voice.rs ├── set_chat_administrator_custom_title.rs ├── set_chat_description.rs ├── set_chat_permissions.rs ├── set_chat_photo.rs ├── set_chat_sticker_set.rs ├── set_chat_title.rs ├── set_inline_game_score.rs ├── set_message_game_score.rs ├── set_my_commands.rs ├── set_passport_data_errors.rs ├── set_sticker_position_in_set.rs ├── set_sticker_set_thumb.rs ├── set_webhook.rs ├── stop_inline_location.rs ├── stop_message_location.rs ├── stop_poll.rs ├── unban_chat_member.rs ├── unpin_all_chat_messages.rs ├── unpin_chat_message.rs └── upload_sticker_file.rs ├── multipart.rs ├── predicates.rs ├── predicates ├── chat.rs ├── media.rs ├── message.rs └── traits.rs ├── proxy.rs ├── state.rs ├── state ├── chats.rs ├── event_loop.rs ├── messages.rs └── polling.rs ├── token.rs ├── types.rs ├── types ├── animation.rs ├── audio.rs ├── bot_command.rs ├── callback.rs ├── callback │ ├── game.rs │ ├── query.rs │ └── query │ │ └── id.rs ├── chat.rs ├── chat │ ├── action.rs │ ├── id.rs │ ├── invite_link.rs │ ├── kind.rs │ ├── location.rs │ ├── member.rs │ ├── permissions.rs │ └── photo.rs ├── chosen_inline_result.rs ├── contact.rs ├── dice.rs ├── document.rs ├── file.rs ├── file │ └── id.rs ├── game.rs ├── game │ └── high_score.rs ├── inline_message_id.rs ├── inline_query.rs ├── inline_query │ ├── id.rs │ ├── result.rs │ └── result │ │ ├── article.rs │ │ ├── audio.rs │ │ ├── contact.rs │ │ ├── document.rs │ │ ├── game.rs │ │ ├── gifs.rs │ │ ├── location.rs │ │ ├── photo.rs │ │ ├── sticker.rs │ │ ├── thumb.rs │ │ ├── venue.rs │ │ ├── video.rs │ │ └── voice.rs ├── input_file.rs ├── input_file │ ├── animation.rs │ ├── audio.rs │ ├── chat_photo.rs │ ├── document.rs │ ├── editable_media.rs │ ├── media_group.rs │ ├── photo.rs │ ├── png_sticker.rs │ ├── sticker.rs │ ├── sticker_for_sticker_set.rs │ ├── sticker_set_thumb.rs │ ├── tgs_sticker.rs │ ├── thumb.rs │ ├── video.rs │ ├── video_note.rs │ └── voice.rs ├── input_message_content.rs ├── input_message_content │ ├── contact.rs │ ├── invoice.rs │ ├── location.rs │ ├── text.rs │ └── venue.rs ├── invoice.rs ├── keyboard.rs ├── keyboard │ ├── any.rs │ ├── force_reply.rs │ ├── inline.rs │ ├── inline │ │ └── button.rs │ └── reply.rs ├── labeled_price.rs ├── location.rs ├── login_url.rs ├── message.rs ├── message │ ├── forward.rs │ ├── from.rs │ ├── id.rs │ ├── kind.rs │ ├── text.rs │ └── timer.rs ├── order_info.rs ├── parameters.rs ├── parameters │ ├── allowed_updates.rs │ ├── callback_action.rs │ ├── chat_id.rs │ ├── invoice.rs │ ├── photo.rs │ ├── poll.rs │ ├── text.rs │ └── tip.rs ├── passport.rs ├── passport │ ├── credentials.rs │ ├── data.rs │ ├── element.rs │ ├── element │ │ ├── error.rs │ │ ├── error │ │ │ ├── source.rs │ │ │ └── source │ │ │ │ ├── data.rs │ │ │ │ ├── file.rs │ │ │ │ ├── front_side.rs │ │ │ │ ├── reverse_side.rs │ │ │ │ ├── selfie.rs │ │ │ │ ├── translation_file.rs │ │ │ │ └── unspecified.rs │ │ └── kind.rs │ └── file.rs ├── photo_size.rs ├── poll.rs ├── pre_checkout_query.rs ├── pre_checkout_query │ └── id.rs ├── proximity_alert.rs ├── shipping.rs ├── shipping │ ├── address.rs │ ├── option.rs │ ├── query.rs │ └── query │ │ └── id.rs ├── sticker.rs ├── sticker │ ├── mask_position.rs │ └── set.rs ├── successful_payment.rs ├── update.rs ├── user.rs ├── user │ ├── id.rs │ ├── me.rs │ └── profile_photos.rs ├── venue.rs ├── video.rs ├── video_note.rs ├── voice.rs ├── voice_chat.rs └── webhook_info.rs ├── util.rs └── util ├── chat_action_loop.rs ├── entities.rs └── entities ├── entity.rs └── tests.rs /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | 4 | end_of_line = lf 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | 8 | indent_style = space 9 | indent_size = 4 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | 5 | # IDE 6 | .idea/ 7 | .vscode/ 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 SnejUgal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /clippy.toml: -------------------------------------------------------------------------------- 1 | doc-valid-idents = ["MarkdownV2", "GitLab", "GitHub", "VCard", "JavaScript"] 2 | msrv = "1.50" 3 | -------------------------------------------------------------------------------- /examples/assets/gif.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbot-rs/tbot/3b1fbfef72a4638afff0636a1d887001f47e23dd/examples/assets/gif.mp4 -------------------------------------------------------------------------------- /examples/assets/photo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbot-rs/tbot/3b1fbfef72a4638afff0636a1d887001f47e23dd/examples/assets/photo.jpg -------------------------------------------------------------------------------- /examples/assets/stickers/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbot-rs/tbot/3b1fbfef72a4638afff0636a1d887001f47e23dd/examples/assets/stickers/1.png -------------------------------------------------------------------------------- /examples/assets/stickers/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbot-rs/tbot/3b1fbfef72a4638afff0636a1d887001f47e23dd/examples/assets/stickers/2.png -------------------------------------------------------------------------------- /examples/commands.rs: -------------------------------------------------------------------------------- 1 | use tbot::{prelude::*, Bot}; 2 | 3 | #[tokio::main] 4 | async fn main() { 5 | let mut bot = Bot::from_env("BOT_TOKEN").event_loop(); 6 | 7 | bot.command_with_description( 8 | "hello", 9 | "Sends hello", 10 | |context| async move { 11 | let call_result = 12 | context.send_message_in_reply("Hello!").call().await; 13 | 14 | if let Err(error) = call_result { 15 | dbg!(error); 16 | } 17 | }, 18 | ); 19 | 20 | bot.help_with_description("Shows help", |context| async move { 21 | let call_result = context 22 | .send_message_in_reply("Just send me a /hello") 23 | .call() 24 | .await; 25 | 26 | if let Err(error) = call_result { 27 | dbg!(error); 28 | } 29 | }); 30 | 31 | bot.polling().start().await.unwrap(); 32 | } 33 | -------------------------------------------------------------------------------- /examples/download.rs: -------------------------------------------------------------------------------- 1 | use tbot::Bot; 2 | 3 | #[tokio::main] 4 | async fn main() { 5 | let mut bot = Bot::from_env("BOT_TOKEN").event_loop(); 6 | 7 | bot.document(|context| async move { 8 | let call_result = context 9 | .bot 10 | .get_file(context.document.file_id.clone()) 11 | .call() 12 | .await; 13 | let file = match call_result { 14 | Ok(file) => file, 15 | Err(err) => { 16 | dbg!(err); 17 | return; 18 | } 19 | }; 20 | 21 | let call_result = context.bot.download_file(&file).await; 22 | let bytes = match call_result { 23 | Ok(bytes) => bytes, 24 | Err(err) => { 25 | dbg!(err); 26 | return; 27 | } 28 | }; 29 | 30 | match String::from_utf8(bytes) { 31 | Ok(document) => println!("{}", document), 32 | Err(err) => { 33 | dbg!(err); 34 | } 35 | } 36 | }); 37 | 38 | bot.polling().start().await.unwrap(); 39 | } 40 | -------------------------------------------------------------------------------- /examples/echo.rs: -------------------------------------------------------------------------------- 1 | use tbot::{markup::markdown_v2, prelude::*, util::entities, Bot}; 2 | 3 | #[tokio::main] 4 | async fn main() { 5 | let mut bot = Bot::from_env("BOT_TOKEN").event_loop(); 6 | 7 | bot.text(|context| async move { 8 | let entities = entities(&context.text); 9 | let echo = markdown_v2(entities); 10 | 11 | let call_result = context.send_message(echo).call().await; 12 | if let Err(error) = call_result { 13 | dbg!(error); 14 | } 15 | }); 16 | 17 | bot.polling().start().await.unwrap(); 18 | } 19 | -------------------------------------------------------------------------------- /examples/get_me.rs: -------------------------------------------------------------------------------- 1 | use tbot::Bot; 2 | 3 | #[tokio::main] 4 | async fn main() -> Result<(), tbot::errors::MethodCall> { 5 | let bot = Bot::from_env("BOT_TOKEN"); 6 | 7 | let me = bot.get_me().call().await?; 8 | dbg!(me); 9 | 10 | Ok(()) 11 | } 12 | -------------------------------------------------------------------------------- /examples/keyboard.rs: -------------------------------------------------------------------------------- 1 | use tbot::{ 2 | prelude::*, 3 | types::keyboard::inline::button::{self, Button}, 4 | Bot, 5 | }; 6 | 7 | const TUTORIAL: &str = "https://gitlab.com/SnejUgal/tbot/wikis/Tutorial"; 8 | 9 | #[tokio::main] 10 | async fn main() { 11 | let mut bot = Bot::from_env("BOT_TOKEN").event_loop(); 12 | 13 | bot.command("keyboard", |context| async move { 14 | let keyboard = vec![ 15 | vec![ 16 | Button::new("Cool!", button::Kind::with_callback_data("cool")), 17 | Button::new( 18 | "Amazing!", 19 | button::Kind::with_callback_data("amazing"), 20 | ), 21 | ], 22 | vec![Button::new( 23 | "I wanna get started with it!", 24 | button::Kind::with_url(TUTORIAL), 25 | )], 26 | ]; 27 | let call_result = context 28 | .send_message("This is a keyboard done with tbot!") 29 | .reply_markup(keyboard) 30 | .call() 31 | .await; 32 | 33 | if let Err(err) = call_result { 34 | dbg!(err); 35 | } 36 | }); 37 | 38 | bot.message_data_callback(|context| async move { 39 | let message = match context.data.as_str() { 40 | "cool" => "You're cool too!", 41 | "amazing" => "Thanks, I'm trying!", 42 | _ => "Are you trying to hack me?", 43 | }; 44 | 45 | let call_result = context.notify(message).call().await; 46 | if let Err(err) = call_result { 47 | dbg!(err); 48 | } 49 | }); 50 | 51 | bot.polling().start().await.unwrap(); 52 | } 53 | -------------------------------------------------------------------------------- /examples/location.rs: -------------------------------------------------------------------------------- 1 | use std::{sync::Arc, time::Duration}; 2 | use tbot::{contexts::Command, prelude::*, types::location, Bot}; 3 | use tokio::time::sleep; 4 | 5 | const INTERVAL: u64 = 15; 6 | const PLACES: [(f64, f64); 7] = [ 7 | (38.904_722, -77.016_389), // Washington 8 | (51.507_222, -0.1275), // London 9 | (41.9, 12.5), // Rome 10 | (59.329_444, 18.068_611), // Stockholm 11 | (41.709_539_2, 44.802_765_4), // Tbilisi 12 | (55.796_389, 49.108_889), // Kazan 13 | (56.5, 84.966_667), // Tomsk 14 | ]; 15 | const UPDATE_PERIOD: u32 = 3600 * 24; 16 | 17 | #[tokio::main] 18 | async fn main() { 19 | let mut bot = Bot::from_env("BOT_TOKEN").event_loop(); 20 | 21 | bot.command("location", handle_location); 22 | 23 | bot.polling().start().await.unwrap(); 24 | } 25 | 26 | async fn handle_location(context: Arc) { 27 | let mut places = PLACES.iter().cycle(); 28 | 29 | let first_place = *places.next().unwrap(); 30 | let call_result = context 31 | .send_location(first_place) 32 | .live_location(location::Live::new(UPDATE_PERIOD)) 33 | .call() 34 | .await; 35 | let location = match call_result { 36 | Ok(location) => location, 37 | Err(err) => { 38 | dbg!(err); 39 | return; 40 | } 41 | }; 42 | 43 | for &place in places { 44 | sleep(Duration::from_secs(INTERVAL)).await; 45 | 46 | let call_result = context 47 | .edit_message_location(location.id, place) 48 | .call() 49 | .await; 50 | 51 | if call_result.is_err() { 52 | break; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/predicates.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | use tbot::{ 3 | compositors::filter, 4 | contexts::{Document, Text}, 5 | predicates::{ 6 | chat::{is_group, is_supergroup}, 7 | media::match_extension, 8 | PredicateBooleanOperations, 9 | }, 10 | prelude::*, 11 | Bot, 12 | }; 13 | 14 | async fn is_message_short(context: Arc) -> bool { 15 | context.text.value.chars().count() < 5 16 | } 17 | 18 | #[tokio::main] 19 | async fn main() { 20 | let mut bot = Bot::from_env("BOT_TOKEN").event_loop(); 21 | 22 | bot.text(filter( 23 | is_supergroup.or(is_group).and(is_message_short.not()), 24 | |context| async move { 25 | let call_result = 26 | context.send_message_in_reply("Hello group!").call().await; 27 | 28 | if let Err(error) = call_result { 29 | dbg!(error); 30 | } 31 | }, 32 | )); 33 | 34 | bot.text(filter(is_message_short, |context| async move { 35 | let call_result = context 36 | .send_message_in_reply("The message is too short!") 37 | .call() 38 | .await; 39 | 40 | if let Err(error) = call_result { 41 | dbg!(error); 42 | } 43 | })); 44 | 45 | bot.document(filter( 46 | match_extension(["rs", "toml"]), 47 | |context: Arc| async move { 48 | let call_result = context 49 | .send_message_in_reply( 50 | "I see you're a man of the culture as well!", 51 | ) 52 | .call() 53 | .await; 54 | 55 | if let Err(error) = call_result { 56 | dbg!(error); 57 | } 58 | }, 59 | )); 60 | 61 | bot.polling().start().await.unwrap(); 62 | } 63 | -------------------------------------------------------------------------------- /examples/proxy.rs: -------------------------------------------------------------------------------- 1 | use tbot::{ 2 | bot, 3 | prelude::*, 4 | proxy::https::{Intercept, Proxy}, 5 | }; 6 | 7 | const PROXY: &str = "http://127.0.0.1:8080"; 8 | 9 | #[tokio::main] 10 | async fn main() { 11 | let proxy = Proxy::new(Intercept::All, PROXY.parse().unwrap()); 12 | // or, for SOCKS: 13 | // let proxy = tbot::proxy::Proxy::socks(SOCKS_PROXY, AUTH); 14 | 15 | let mut bot = bot::Builder::with_env_token("BOT_TOKEN") 16 | .proxy(proxy) 17 | .build() 18 | .event_loop(); 19 | 20 | bot.text(|context| async move { 21 | let call_result = context 22 | .send_message_in_reply(&context.text.value) 23 | .call() 24 | .await; 25 | 26 | if let Err(error) = call_result { 27 | dbg!(error); 28 | } 29 | }); 30 | 31 | bot.polling().start().await.unwrap(); 32 | } 33 | -------------------------------------------------------------------------------- /examples/sticker_packs.rs: -------------------------------------------------------------------------------- 1 | use tbot::{types::input_file::PngSticker, Bot}; 2 | 3 | const USER: i64 = 0; 4 | // Must end with `_by_` 5 | const NAME: &str = "tbot"; 6 | const TITLE: &str = "tbot"; 7 | const STICKERS: [(&[u8], &str); 2] = [ 8 | (include_bytes!("./assets/stickers/1.png"), "⌨️"), 9 | (include_bytes!("./assets/stickers/2.png"), "🐱"), 10 | ]; 11 | 12 | #[tokio::main] 13 | async fn main() -> Result<(), tbot::errors::MethodCall> { 14 | let bot = Bot::from_env("BOT_TOKEN"); 15 | 16 | let user_id = USER.into(); 17 | let mut stickers = STICKERS.iter(); 18 | let &(bytes, emoji) = stickers.next().unwrap(); 19 | 20 | let sticker = PngSticker::with_bytes(bytes); 21 | bot.create_new_sticker_set(user_id, NAME, TITLE, sticker, emoji) 22 | .call() 23 | .await?; 24 | 25 | for &(bytes, emoji) in stickers { 26 | let sticker = PngSticker::with_bytes(bytes); 27 | bot.add_sticker_to_set(user_id, NAME, sticker, emoji) 28 | .call() 29 | .await?; 30 | } 31 | 32 | println!( 33 | "Go check out this amazing sticker pack: https://t.me/addstickers/{}", 34 | NAME, 35 | ); 36 | 37 | Ok(()) 38 | } 39 | -------------------------------------------------------------------------------- /examples/webhook.rs: -------------------------------------------------------------------------------- 1 | use tbot::{prelude::*, Bot}; 2 | 3 | const URL: &str = "https://example.com"; 4 | const PORT: u16 = 2000; 5 | 6 | #[tokio::main] 7 | async fn main() { 8 | let mut bot = Bot::from_env("BOT_TOKEN").event_loop(); 9 | 10 | bot.text(|context| async move { 11 | let echo = &context.text.value; 12 | let call_result = context.send_message_in_reply(echo).call().await; 13 | 14 | if let Err(err) = call_result { 15 | dbg!(err); 16 | } 17 | }); 18 | 19 | // For HTTPS, see this wiki: 20 | // https://gitlab.com/SnejUgal/tbot/wikis/How-to/How-to-use-webhooks 21 | bot.webhook(URL, PORT).http().start().await.unwrap(); 22 | } 23 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | newline_style = "Unix" 3 | use_field_init_shorthand = true 4 | use_try_shorthand = true 5 | -------------------------------------------------------------------------------- /src/bot/inner_bot.rs: -------------------------------------------------------------------------------- 1 | use crate::{connectors::Client, token::Token}; 2 | use hyper::Uri; 3 | 4 | const CLOUD_BOT_API: &str = "https://api.telegram.org/"; 5 | 6 | #[derive(Debug)] 7 | pub struct InnerBot { 8 | token: Token, 9 | client: Client, 10 | uri: Uri, 11 | } 12 | 13 | impl InnerBot { 14 | pub fn new(token: Token, client: Client) -> Self { 15 | Self { 16 | token, 17 | client, 18 | uri: Uri::from_static(CLOUD_BOT_API), 19 | } 20 | } 21 | 22 | pub fn set_client(&mut self, client: Client) { 23 | self.client = client; 24 | } 25 | 26 | pub fn set_uri(&mut self, uri: Uri) { 27 | self.uri = uri; 28 | } 29 | 30 | pub fn token(&self) -> &str { 31 | &self.token.0 32 | } 33 | 34 | pub const fn client(&self) -> &Client { 35 | &self.client 36 | } 37 | 38 | pub fn uri(&self) -> Uri { 39 | self.uri.clone() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/contexts/animation.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | contexts::fields::{self, AnyText, Caption}, 3 | types::{self, message::Text}, 4 | }; 5 | 6 | media_message! { 7 | struct Animation { 8 | /// The animation. 9 | animation: types::Animation, 10 | /// The caption of the animation. 11 | caption: Text, 12 | } -> EventLoop::animation 13 | 14 | fn new(caption: Text,) -> Self { 15 | Self { 16 | caption: caption, 17 | } 18 | } 19 | } 20 | 21 | impl fields::Animation for Animation { 22 | #[must_use] 23 | fn animation(&self) -> &types::Animation { 24 | &self.animation 25 | } 26 | } 27 | 28 | impl Caption for Animation { 29 | #[must_use] 30 | fn caption(&self) -> &Text { 31 | &self.caption 32 | } 33 | } 34 | 35 | impl AnyText for Animation { 36 | #[must_use] 37 | fn text(&self) -> &Text { 38 | &self.caption 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/contexts/any_update.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | types::update::{self, Update}, 3 | Bot, 4 | }; 5 | 6 | common! { 7 | /// The context for [`any_update`] handlers. 8 | /// 9 | /// [`any_update`]: crate::EventLoop::any_update 10 | struct AnyUpdate { 11 | /// The update's ID. 12 | id: update::Id, 13 | /// The update's kind. 14 | kind: update::Kind, 15 | } 16 | } 17 | 18 | impl AnyUpdate { 19 | #[allow(clippy::missing_const_for_fn)] 20 | pub(crate) fn new(bot: Bot, update: Update) -> Self { 21 | Self { 22 | bot, 23 | id: update.id, 24 | kind: update.kind, 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/contexts/audio.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | contexts::fields::{self, AnyText, Caption}, 3 | types::{self, message::Text}, 4 | }; 5 | 6 | use super::fields::Album; 7 | 8 | media_message! { 9 | struct Audio { 10 | /// The audio. 11 | audio: types::Audio, 12 | /// The caption of the audio. 13 | caption: Text, 14 | /// The media group's ID. 15 | media_group_id: Option, 16 | } -> EventLoop::audio 17 | 18 | fn new(caption: Text, media_group_id: Option,) -> Self { 19 | Self { 20 | caption: caption, 21 | media_group_id: media_group_id, 22 | } 23 | } 24 | } 25 | 26 | impl fields::Audio for Audio { 27 | #[must_use] 28 | fn audio(&self) -> &types::Audio { 29 | &self.audio 30 | } 31 | } 32 | 33 | impl Caption for Audio { 34 | #[must_use] 35 | fn caption(&self) -> &Text { 36 | &self.caption 37 | } 38 | } 39 | 40 | impl AnyText for Audio { 41 | #[must_use] 42 | fn text(&self) -> &Text { 43 | &self.caption 44 | } 45 | } 46 | 47 | impl Album for Audio { 48 | #[must_use] 49 | fn media_group_id(&self) -> Option<&str> { 50 | self.media_group_id.as_ref().map(String::as_ref) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/contexts/changed_auto_delete_timer.rs: -------------------------------------------------------------------------------- 1 | use crate::types::message; 2 | 3 | message_base! { 4 | struct ChangedAutoDeleteTimer { 5 | /// The new auto-delete timer value. 6 | auto_delete_time: u64, 7 | } -> EventLoop::changed_auto_delete_timer 8 | 9 | fn new(change: message::AutoDeleteTimerChanged,) -> Self { 10 | Self { 11 | auto_delete_time: change.auto_delete_time, 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/contexts/chat_member.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | types::{chat, Chat, User}, 3 | Bot, 4 | }; 5 | 6 | common! { 7 | /// The context for [`chat_member`] handlers. 8 | /// 9 | /// [`chat_member`]: crate::EventLoop::chat_member 10 | struct ChatMember { 11 | /// The chat in which the change occured. 12 | chat: Chat, 13 | /// The user who caused the change. 14 | from: User, 15 | /// Timestamp when this change occured. 16 | date: i64, 17 | /// Previous information about the member. 18 | before: chat::Member, 19 | /// New information about the member. 20 | after: chat::Member, 21 | /// The invite link which the user used to join the chat. 22 | invite_link: Option, 23 | } 24 | } 25 | 26 | impl ChatMember { 27 | #[allow(clippy::missing_const_for_fn)] 28 | pub(crate) fn new(bot: Bot, update: chat::member::Updated) -> Self { 29 | Self { 30 | bot, 31 | chat: update.chat, 32 | from: update.from, 33 | date: update.date, 34 | before: update.before, 35 | after: update.after, 36 | invite_link: update.invite_link, 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/contexts/chosen_inline.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | types::{ChosenInlineResult, InlineMessageId, Location, User}, 3 | Bot, 4 | }; 5 | 6 | common! { 7 | /// The context for [`chosen_inline`] handlers. 8 | /// 9 | /// [`chosen_inline`]: crate::EventLoop::chosen_inline 10 | struct ChosenInline { 11 | /// ID of the chosen result. 12 | result_id: String, 13 | /// The user who chose the result. 14 | from: User, 15 | /// The location of the user, if enabled and allowed. 16 | location: Option, 17 | /// The ID of the sent message. 18 | inline_message_id: Option, 19 | /// The query used to obtain the result. 20 | query: String, 21 | } 22 | } 23 | 24 | impl ChosenInline { 25 | #[allow(clippy::missing_const_for_fn)] 26 | pub(crate) fn new(bot: Bot, chosen_result: ChosenInlineResult) -> Self { 27 | Self { 28 | bot, 29 | result_id: chosen_result.result_id, 30 | from: chosen_result.from, 31 | location: chosen_result.location, 32 | inline_message_id: chosen_result.inline_message_id, 33 | query: chosen_result.query, 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/contexts/command.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | contexts::fields::{self, AnyText}, 3 | types::message, 4 | }; 5 | 6 | media_message! { 7 | struct Command { 8 | /// The text of the message. 9 | text: message::Text, 10 | /// The command which triggered the handler. 11 | command: String, 12 | } -> EventLoop::text 13 | 14 | fn new(command: String,) -> Self { 15 | Self { 16 | command: command, 17 | } 18 | } 19 | } 20 | 21 | impl fields::Text for Command { 22 | #[must_use] 23 | fn text(&self) -> &message::Text { 24 | &self.text 25 | } 26 | } 27 | 28 | impl AnyText for Command { 29 | #[must_use] 30 | fn text(&self) -> &message::Text { 31 | &self.text 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/contexts/connected_website.rs: -------------------------------------------------------------------------------- 1 | media_message! { 2 | struct ConnectedWebsite { 3 | /// The connected website. 4 | website: String, 5 | } -> EventLoop::connected_website 6 | 7 | fn new() -> Self { 8 | Self { } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/contexts/contact.rs: -------------------------------------------------------------------------------- 1 | use crate::types; 2 | 3 | media_message! { 4 | struct Contact { 5 | /// The contact. 6 | contact: types::Contact, 7 | } -> EventLoop::contact 8 | 9 | fn new() -> Self { 10 | Self { } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/contexts/created_group.rs: -------------------------------------------------------------------------------- 1 | message_base! { 2 | struct CreatedGroup { } -> EventLoop::created_group 3 | 4 | fn new() -> Self { 5 | Self {} 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/contexts/data_callback.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | contexts::{ 3 | fields, 4 | methods::{Copyable, Forwardable, Pinnable}, 5 | }, 6 | types::{message, Chat}, 7 | }; 8 | 9 | callback! { 10 | struct MessageDataCallback { 11 | /// Data from the callback. 12 | data: String, 13 | message: crate::types::Message, 14 | } -> EventLoop::message_data_callback 15 | } 16 | 17 | impl fields::Message for MessageDataCallback { 18 | fn message_id(&self) -> message::Id { 19 | self.message.id 20 | } 21 | 22 | fn from(&self) -> Option<&message::From> { 23 | self.message.from.as_ref() 24 | } 25 | 26 | fn date(&self) -> i64 { 27 | self.message.date 28 | } 29 | 30 | fn chat(&self) -> &Chat { 31 | &self.message.chat 32 | } 33 | } 34 | 35 | impl Copyable for MessageDataCallback {} 36 | impl Forwardable for MessageDataCallback {} 37 | impl Pinnable for MessageDataCallback {} 38 | 39 | callback! { 40 | struct InlineDataCallback { 41 | /// Data from the callback. 42 | data: String, 43 | inline_message_id: String, 44 | } -> EventLoop::inline_data_callback 45 | } 46 | -------------------------------------------------------------------------------- /src/contexts/deleted_chat_photo.rs: -------------------------------------------------------------------------------- 1 | message_base! { 2 | struct DeletedChatPhoto { } -> EventLoop::deleted_chat_photo 3 | 4 | fn new() -> Self { 5 | Self {} 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/contexts/dice.rs: -------------------------------------------------------------------------------- 1 | use crate::types; 2 | 3 | media_message! { 4 | struct Dice { 5 | /// The dice. 6 | dice: types::Dice, 7 | } -> EventLoop::dice 8 | 9 | fn new() -> Self { 10 | Self { } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/contexts/document.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | contexts::fields::{self, AnyText, Caption}, 3 | types::{self, message::Text}, 4 | }; 5 | 6 | use super::fields::Album; 7 | 8 | media_message! { 9 | struct Document { 10 | /// The document. 11 | document: types::Document, 12 | /// The caption of the document. 13 | caption: Text, 14 | /// The media group's ID. 15 | media_group_id: Option, 16 | } -> EventLoop::document 17 | 18 | fn new(caption: Text, media_group_id: Option,) -> Self { 19 | Self { 20 | caption: caption, 21 | media_group_id: media_group_id, 22 | } 23 | } 24 | } 25 | 26 | impl fields::Document for Document { 27 | #[must_use] 28 | fn document(&self) -> &types::Document { 29 | &self.document 30 | } 31 | } 32 | 33 | impl Caption for Document { 34 | #[must_use] 35 | fn caption(&self) -> &Text { 36 | &self.caption 37 | } 38 | } 39 | 40 | impl AnyText for Document { 41 | #[must_use] 42 | fn text(&self) -> &Text { 43 | &self.caption 44 | } 45 | } 46 | 47 | impl Album for Document { 48 | #[must_use] 49 | fn media_group_id(&self) -> Option<&str> { 50 | self.media_group_id.as_ref().map(String::as_ref) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/contexts/edited_animation.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | contexts::fields::{self, AnyText, Caption}, 3 | types::{message::Text, Animation}, 4 | }; 5 | 6 | edited_message! { 7 | struct EditedAnimation { 8 | /// The animation. 9 | animation: Animation, 10 | /// The caption of the animation. 11 | caption: Text, 12 | } -> EventLoop::edited_animation 13 | 14 | fn new(caption: Text,) -> Self { 15 | Self { 16 | caption: caption, 17 | } 18 | } 19 | } 20 | 21 | impl fields::Animation for EditedAnimation { 22 | #[must_use] 23 | fn animation(&self) -> &Animation { 24 | &self.animation 25 | } 26 | } 27 | 28 | impl Caption for EditedAnimation { 29 | #[must_use] 30 | fn caption(&self) -> &Text { 31 | &self.caption 32 | } 33 | } 34 | 35 | impl AnyText for EditedAnimation { 36 | #[must_use] 37 | fn text(&self) -> &Text { 38 | &self.caption 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/contexts/edited_audio.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | contexts::fields::{self, AnyText, Caption}, 3 | types::{message::Text, Audio}, 4 | }; 5 | 6 | use super::fields::Album; 7 | 8 | edited_message! { 9 | struct EditedAudio { 10 | /// The audio. 11 | audio: Audio, 12 | /// The caption of the audio. 13 | caption: Text, 14 | /// The media group's ID. 15 | media_group_id: Option, 16 | } -> EventLoop::edited_audio 17 | 18 | fn new(caption: Text, media_group_id: Option,) -> Self { 19 | Self { 20 | caption: caption, 21 | media_group_id: media_group_id, 22 | } 23 | } 24 | } 25 | 26 | impl fields::Audio for EditedAudio { 27 | #[must_use] 28 | fn audio(&self) -> &Audio { 29 | &self.audio 30 | } 31 | } 32 | 33 | impl Caption for EditedAudio { 34 | #[must_use] 35 | fn caption(&self) -> &Text { 36 | &self.caption 37 | } 38 | } 39 | 40 | impl AnyText for EditedAudio { 41 | #[must_use] 42 | fn text(&self) -> &Text { 43 | &self.caption 44 | } 45 | } 46 | 47 | impl Album for EditedAudio { 48 | #[must_use] 49 | fn media_group_id(&self) -> Option<&str> { 50 | self.media_group_id.as_ref().map(String::as_ref) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/contexts/edited_command.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | contexts::fields::{self, AnyText}, 3 | types::message, 4 | }; 5 | 6 | edited_message! { 7 | struct EditedCommand { 8 | /// The text of the message. 9 | text: message::Text, 10 | /// The command which triggered the handler. 11 | command: String, 12 | } -> EventLoop::text 13 | 14 | fn new(command: String,) -> Self { 15 | Self { 16 | command: command, 17 | } 18 | } 19 | } 20 | 21 | impl fields::Text for EditedCommand { 22 | #[must_use] 23 | fn text(&self) -> &message::Text { 24 | &self.text 25 | } 26 | } 27 | 28 | impl AnyText for EditedCommand { 29 | #[must_use] 30 | fn text(&self) -> &message::Text { 31 | &self.text 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/contexts/edited_document.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | contexts::fields::{self, AnyText, Caption}, 3 | types::{message::Text, Document}, 4 | }; 5 | 6 | use super::fields::Album; 7 | 8 | edited_message! { 9 | struct EditedDocument { 10 | /// The document. 11 | document: Document, 12 | /// The caption of the document. 13 | caption: Text, 14 | /// The media group's ID. 15 | media_group_id: Option, 16 | } -> EventLoop::edited_document 17 | 18 | fn new(caption: Text, media_group_id: Option,) -> Self { 19 | Self { 20 | caption: caption, 21 | media_group_id: media_group_id, 22 | } 23 | } 24 | } 25 | 26 | impl fields::Document for EditedDocument { 27 | #[must_use] 28 | fn document(&self) -> &Document { 29 | &self.document 30 | } 31 | } 32 | 33 | impl Caption for EditedDocument { 34 | #[must_use] 35 | fn caption(&self) -> &Text { 36 | &self.caption 37 | } 38 | } 39 | 40 | impl AnyText for EditedDocument { 41 | #[must_use] 42 | fn text(&self) -> &Text { 43 | &self.caption 44 | } 45 | } 46 | 47 | impl Album for EditedDocument { 48 | #[must_use] 49 | fn media_group_id(&self) -> Option<&str> { 50 | self.media_group_id.as_ref().map(String::as_ref) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/contexts/edited_location.rs: -------------------------------------------------------------------------------- 1 | use crate::{contexts::fields, types::Location}; 2 | 3 | edited_message! { 4 | struct EditedLocation { 5 | /// The location. 6 | location: Location, 7 | } -> EventLoop::edited_location 8 | 9 | fn new() -> Self { 10 | Self { } 11 | } 12 | } 13 | 14 | impl fields::Location for EditedLocation { 15 | #[must_use] 16 | fn location(&self) -> &Location { 17 | &self.location 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/contexts/edited_photo.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | contexts::fields::{self, Album, AnyText, Caption}, 3 | types::{message::Text, PhotoSize}, 4 | }; 5 | 6 | edited_message! { 7 | struct EditedPhoto { 8 | /// The photo. 9 | photo: Vec, 10 | /// The caption of the photo. 11 | caption: Text, 12 | /// The media group's ID. 13 | media_group_id: Option, 14 | } -> EventLoop::edited_photo 15 | 16 | fn new(caption: Text, media_group_id: Option,) -> Self { 17 | Self { 18 | caption: caption, 19 | media_group_id: media_group_id, 20 | } 21 | } 22 | } 23 | 24 | impl fields::Photo for EditedPhoto { 25 | #[must_use] 26 | fn photo(&self) -> &[PhotoSize] { 27 | &self.photo[..] 28 | } 29 | } 30 | 31 | impl Caption for EditedPhoto { 32 | #[must_use] 33 | fn caption(&self) -> &Text { 34 | &self.caption 35 | } 36 | } 37 | 38 | impl AnyText for EditedPhoto { 39 | #[must_use] 40 | fn text(&self) -> &Text { 41 | &self.caption 42 | } 43 | } 44 | 45 | impl Album for EditedPhoto { 46 | #[must_use] 47 | fn media_group_id(&self) -> Option<&str> { 48 | self.media_group_id.as_ref().map(String::as_ref) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/contexts/edited_text.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | contexts::fields::{self, AnyText}, 3 | types::message::Text, 4 | }; 5 | 6 | edited_message! { 7 | struct EditedText { 8 | /// The text of the message. 9 | text: Text, 10 | } -> EventLoop::edited_text 11 | 12 | fn new() -> Self { 13 | Self { } 14 | } 15 | } 16 | 17 | impl fields::Text for EditedText { 18 | #[must_use] 19 | fn text(&self) -> &Text { 20 | &self.text 21 | } 22 | } 23 | 24 | impl AnyText for EditedText { 25 | #[must_use] 26 | fn text(&self) -> &Text { 27 | &self.text 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/contexts/edited_video.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | contexts::fields::{self, Album, AnyText, Caption}, 3 | types::{message::Text, Video}, 4 | }; 5 | 6 | edited_message! { 7 | struct EditedVideo { 8 | /// The video. 9 | video: Video, 10 | /// The caption of the video. 11 | caption: Text, 12 | /// The media group's ID. 13 | media_group_id: Option, 14 | } -> EventLoop::edited_video 15 | 16 | fn new(caption: Text, media_group_id: Option,) -> Self { 17 | Self { 18 | caption: caption, 19 | media_group_id: media_group_id, 20 | } 21 | } 22 | } 23 | 24 | impl fields::Video for EditedVideo { 25 | #[must_use] 26 | fn video(&self) -> &Video { 27 | &self.video 28 | } 29 | } 30 | 31 | impl Caption for EditedVideo { 32 | #[must_use] 33 | fn caption(&self) -> &Text { 34 | &self.caption 35 | } 36 | } 37 | 38 | impl AnyText for EditedVideo { 39 | #[must_use] 40 | fn text(&self) -> &Text { 41 | &self.caption 42 | } 43 | } 44 | 45 | impl Album for EditedVideo { 46 | #[must_use] 47 | fn media_group_id(&self) -> Option<&str> { 48 | self.media_group_id.as_ref().map(String::as_ref) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/contexts/ended_voice_chat.rs: -------------------------------------------------------------------------------- 1 | use crate::types::voice_chat; 2 | 3 | message_base! { 4 | struct EndedVoiceChat { 5 | /// The duration of the voice chat in seconds. 6 | duration: u64, 7 | } -> EventLoop::ended_voice_chat 8 | 9 | fn new(ended: voice_chat::Ended,) -> Self { 10 | Self { 11 | duration: ended.duration, 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/contexts/fields.rs: -------------------------------------------------------------------------------- 1 | //! Traits for common context fields. 2 | //! 3 | //! Suppose that you want to process users' photos whenever they send or edit 4 | //! one. You would like to abstract this as much as possible, like this: 5 | //! 6 | //! ``` 7 | //! # async fn process_photo(_: std::sync::Arc) { } 8 | //! let mut bot = tbot::from_env!("BOT_TOKEN").event_loop(); 9 | //! bot.photo(process_photo); 10 | //! bot.edited_photo(process_photo); 11 | //! ``` 12 | //! 13 | //! However, in the first case we have [`contexts::Photo`], but in the second 14 | //! one we have [`contexts::EditedPhoto`]. Luckily, they both implement 15 | //! [`fields::Photo`], which allows accessing the `photo` field without caring 16 | //! about the exact update type. So, if you already have this handler: 17 | //! 18 | //! ``` 19 | //! use std::sync::Arc; 20 | //! use tbot::{contexts}; 21 | //! async fn process_photo(context: Arc) { 22 | //! let photo = &context.photo; 23 | //! // .. 24 | //! } 25 | //! ``` 26 | //! 27 | //! You can generalize it to this one in order to also support 28 | //! [`contexts::EditedPhoto`]: 29 | //! 30 | //! ``` 31 | //! use std::sync::Arc; 32 | //! use tbot::{contexts::fields::Photo}; 33 | //! async fn process_photo(context: Arc) { 34 | //! let photo = context.photo(); 35 | //! // .. 36 | //! } 37 | //! ``` 38 | //! 39 | //! [`contexts::Photo`]: super::Photo 40 | //! [`contexts::EditedPhoto`]: super::EditedPhoto 41 | //! [`fields::Photo`]: Photo 42 | 43 | mod album; 44 | mod attachments; 45 | mod callback; 46 | mod context; 47 | mod messages; 48 | mod texts; 49 | 50 | pub use { 51 | album::Album, 52 | attachments::{Animation, Audio, Document, Location, Photo, Video}, 53 | callback::Callback, 54 | context::Context, 55 | messages::{EditedMessage, Forward, MediaMessage, Message}, 56 | texts::{AnyText, Caption, Text}, 57 | }; 58 | -------------------------------------------------------------------------------- /src/contexts/fields/album.rs: -------------------------------------------------------------------------------- 1 | use super::MediaMessage; 2 | 3 | /// A general trait for album items. 4 | pub trait Album: MediaMessage { 5 | /// The ID of the album. 6 | fn media_group_id(&self) -> Option<&str>; 7 | } 8 | -------------------------------------------------------------------------------- /src/contexts/fields/attachments.rs: -------------------------------------------------------------------------------- 1 | use super::MediaMessage; 2 | use crate::types::{self, PhotoSize}; 3 | 4 | /// A general trait for animation messages. 5 | pub trait Animation: MediaMessage { 6 | /// The animation of the message. 7 | fn animation(&self) -> &types::Animation; 8 | } 9 | 10 | /// A general trait for audio messages. 11 | pub trait Audio: MediaMessage { 12 | /// The audio of the message. 13 | fn audio(&self) -> &types::Audio; 14 | } 15 | 16 | /// A general trait for document messages. 17 | pub trait Document: MediaMessage { 18 | /// The document of the message. 19 | fn document(&self) -> &types::Document; 20 | } 21 | 22 | /// A general trait for location messages. 23 | pub trait Location: MediaMessage { 24 | /// The location of the message. 25 | fn location(&self) -> &types::Location; 26 | } 27 | 28 | /// A general trait for photo messages. 29 | pub trait Photo: MediaMessage { 30 | /// The photo of the message. 31 | fn photo(&self) -> &[PhotoSize]; 32 | } 33 | 34 | /// A general trait for video messages. 35 | pub trait Video: MediaMessage { 36 | /// The video of the message. 37 | fn video(&self) -> &types::Video; 38 | } 39 | -------------------------------------------------------------------------------- /src/contexts/fields/callback.rs: -------------------------------------------------------------------------------- 1 | use super::Context; 2 | use crate::types::{callback, User}; 3 | 4 | /// A general trait for callback updates. 5 | pub trait Callback: Context { 6 | /// The ID of the callback. 7 | fn id(&self) -> &callback::query::Id; 8 | /// The user who initiated the callback. 9 | fn from(&self) -> &User; 10 | /// The identifier of the chat. 11 | fn chat_instance(&self) -> &str; 12 | // todo: origin 13 | } 14 | -------------------------------------------------------------------------------- /src/contexts/fields/context.rs: -------------------------------------------------------------------------------- 1 | use crate::{internal::Sealed, Bot}; 2 | 3 | /// A general trait for all contexts. 4 | pub trait Context: Send + Sync + Sealed + 'static { 5 | /// A bot for calling API without information inference. 6 | fn bot(&self) -> &Bot; 7 | } 8 | -------------------------------------------------------------------------------- /src/contexts/fields/messages.rs: -------------------------------------------------------------------------------- 1 | use super::Context; 2 | use crate::types::{self, keyboard::inline, message, Chat, User}; 3 | 4 | /// A general trait for all message contexts. 5 | pub trait Message: Context { 6 | /// ID of the message. 7 | fn message_id(&self) -> message::Id; 8 | /// The author of the message. 9 | fn from(&self) -> Option<&message::From>; 10 | /// The timestamp of the message. 11 | fn date(&self) -> i64; 12 | /// The chat to which the message was sent. 13 | fn chat(&self) -> &Chat; 14 | } 15 | 16 | /// A general trait for all non-service messages. 17 | pub trait MediaMessage: Message { 18 | /// The replied message. 19 | fn reply_to(&self) -> Option<&types::Message>; 20 | /// The author's signature, if enabled for the channel. 21 | fn author_signature(&self) -> Option<&str>; 22 | /// The inline keyboard attached to the message. 23 | fn reply_markup(&self) -> Option<&inline::Keyboard>; 24 | /// The bot via which the message was sent. 25 | fn via_bot(&self) -> Option<&User>; 26 | } 27 | 28 | /// A general trait for messages that _can_ be a forward. 29 | pub trait Forward: MediaMessage { 30 | /// The origin of the message if it's a forward. 31 | fn forward(&self) -> Option<&message::Forward>; 32 | } 33 | 34 | /// A general trait for edited messages. 35 | pub trait EditedMessage: MediaMessage { 36 | /// The last time when the message was edited. 37 | fn edit_date(&self) -> i64; 38 | } 39 | -------------------------------------------------------------------------------- /src/contexts/fields/texts.rs: -------------------------------------------------------------------------------- 1 | use super::MediaMessage; 2 | use crate::types::message; 3 | 4 | /// A general trait for text messages. 5 | pub trait Text: MediaMessage { 6 | /// The text of the message. 7 | fn text(&self) -> &message::Text; 8 | } 9 | 10 | /// A general trait for messages with a caption. 11 | pub trait Caption: MediaMessage { 12 | /// The caption of the message. 13 | fn caption(&self) -> &message::Text; 14 | } 15 | 16 | /// Unites [`Text`] and [`Caption`]. 17 | pub trait AnyText: MediaMessage { 18 | /// The text or the caption of the message. 19 | fn text(&self) -> &message::Text; 20 | } 21 | -------------------------------------------------------------------------------- /src/contexts/game.rs: -------------------------------------------------------------------------------- 1 | use crate::types; 2 | 3 | media_message! { 4 | struct Game { 5 | /// The game. 6 | game: types::Game, 7 | } -> EventLoop::game 8 | 9 | fn new() -> Self { 10 | Self { } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/contexts/game_callback.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | contexts::{ 3 | fields, 4 | methods::{Copyable, Forwardable, Pinnable}, 5 | }, 6 | types::{message, Chat}, 7 | }; 8 | 9 | callback! { 10 | struct MessageGameCallback { 11 | /// The requested game. 12 | game: String, 13 | message: crate::types::Message, 14 | } -> EventLoop::message_game_callback 15 | } 16 | 17 | impl fields::Message for MessageGameCallback { 18 | fn message_id(&self) -> message::Id { 19 | self.message.id 20 | } 21 | 22 | fn from(&self) -> Option<&message::From> { 23 | self.message.from.as_ref() 24 | } 25 | 26 | fn date(&self) -> i64 { 27 | self.message.date 28 | } 29 | 30 | fn chat(&self) -> &Chat { 31 | &self.message.chat 32 | } 33 | } 34 | 35 | impl Copyable for MessageGameCallback {} 36 | impl Forwardable for MessageGameCallback {} 37 | impl Pinnable for MessageGameCallback {} 38 | 39 | callback! { 40 | struct InlineGameCallback { 41 | /// The requested game. 42 | game: String, 43 | inline_message_id: String, 44 | } -> EventLoop::inline_game_callback 45 | } 46 | -------------------------------------------------------------------------------- /src/contexts/inline.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | methods::AnswerInlineQuery, 3 | types::{inline_query, InlineQuery, Location, User}, 4 | Bot, 5 | }; 6 | 7 | common! { 8 | /// The context for [`inline`] handlers. 9 | /// 10 | /// [`inline`]: crate::EventLoop::inline 11 | struct Inline { 12 | /// The ID of the query. 13 | id: inline_query::Id, 14 | /// The user who sent the query. 15 | from: User, 16 | /// The location of the user, if enabled and allowed. 17 | location: Option, 18 | /// The query itself. 19 | query: String, 20 | /// The offset of the result to be returned. 21 | offset: String, 22 | /// The type of chat inline query was sent from. 23 | chat_kind: Option, 24 | } 25 | } 26 | 27 | impl Inline { 28 | #[allow(clippy::missing_const_for_fn)] 29 | pub(crate) fn new(bot: Bot, inline_query: InlineQuery) -> Self { 30 | Self { 31 | bot, 32 | id: inline_query.id, 33 | from: inline_query.from, 34 | location: inline_query.location, 35 | query: inline_query.query, 36 | offset: inline_query.offset, 37 | chat_kind: inline_query.chat_kind, 38 | } 39 | } 40 | 41 | /// Answers the query. 42 | pub fn answer( 43 | &self, 44 | results: impl Into>, 45 | ) -> AnswerInlineQuery<'_> { 46 | self.bot.answer_inline_query(self.id.clone(), results) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/contexts/invited_voice_chat_participants.rs: -------------------------------------------------------------------------------- 1 | use crate::types::{voice_chat, User}; 2 | 3 | message_base! { 4 | struct InvitedVoiceChatParticipants { 5 | /// Users who were invited to the voice chat. 6 | users: Vec, 7 | } -> EventLoop::invited_voice_chat_participants 8 | 9 | fn new(invited: voice_chat::ParticipantsInvited,) -> Self { 10 | Self { 11 | users: invited.users, 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/contexts/invoice.rs: -------------------------------------------------------------------------------- 1 | use crate::types; 2 | 3 | media_message! { 4 | struct Invoice { 5 | /// The invoice. 6 | invoice: types::Invoice, 7 | } -> EventLoop::invoice 8 | 9 | fn new() -> Self { 10 | Self { } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/contexts/left_member.rs: -------------------------------------------------------------------------------- 1 | use crate::types::User; 2 | 3 | message_base! { 4 | struct LeftMember { 5 | /// The left member. 6 | member: User, 7 | } -> EventLoop::left_member 8 | 9 | fn new(member: User,) -> Self { 10 | Self { 11 | member: member, 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/contexts/location.rs: -------------------------------------------------------------------------------- 1 | use crate::{contexts::fields, types}; 2 | 3 | media_message! { 4 | struct Location { 5 | /// The location. 6 | location: types::Location, 7 | } -> EventLoop::location 8 | 9 | fn new() -> Self { 10 | Self { } 11 | } 12 | } 13 | 14 | impl fields::Location for Location { 15 | #[must_use] 16 | fn location(&self) -> &types::Location { 17 | &self.location 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/contexts/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod common; 3 | #[macro_use] 4 | mod message_base; 5 | #[macro_use] 6 | mod media_message; 7 | #[macro_use] 8 | mod edited_message; 9 | #[macro_use] 10 | mod callback; 11 | -------------------------------------------------------------------------------- /src/contexts/macros/common.rs: -------------------------------------------------------------------------------- 1 | macro_rules! common { 2 | ( 3 | $(#[doc = $doc:expr])+ 4 | struct $name:ident { 5 | $(#[doc = $field_doc:literal] $field:ident: $type:ty,)+ 6 | } 7 | ) => { 8 | $(#[doc = $doc])+ 9 | #[derive(Debug, Clone)] 10 | #[non_exhaustive] 11 | pub struct $name { 12 | /// A bot for calling API without information inference. 13 | pub bot: crate::Bot, 14 | $(#[doc = $field_doc] pub $field: $type,)+ 15 | } 16 | 17 | impl crate::internal::Sealed for $name { } 18 | 19 | impl crate::contexts::fields::Context for $name { 20 | fn bot(&self) -> &crate::Bot { 21 | &self.bot 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/contexts/methods.rs: -------------------------------------------------------------------------------- 1 | //! Traits for calling methods inferring as much data as possible from 2 | //! the context. 3 | 4 | mod callback; 5 | mod copyable; 6 | mod forwardable; 7 | mod message; 8 | mod pinnable; 9 | 10 | pub use { 11 | callback::Callback, copyable::Copyable, forwardable::Forwardable, 12 | message::Message, pinnable::Pinnable, 13 | }; 14 | -------------------------------------------------------------------------------- /src/contexts/methods/callback.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | contexts::fields, methods::AnswerCallbackQuery, 3 | types::parameters::CallbackAction, 4 | }; 5 | 6 | /// Provides methods appliable to callback queries. 7 | pub trait Callback: fields::Callback { 8 | /// Answers the callback query. 9 | /// 10 | /// If you don't need to choose the action dynamically, using dedicated 11 | /// methods will be more convenient: [`ignore`], [`open_url`], [`notify`] 12 | /// and [`alert`]. 13 | /// 14 | /// [`ignore`]: Self::ignore 15 | /// [`open_url`]: Self::open_url 16 | /// [`notify`]: Self::notify 17 | /// [`alert`]: Self::alert 18 | fn answer( 19 | &self, 20 | action: Option, 21 | ) -> AnswerCallbackQuery<'_> { 22 | self.bot().answer_callback_query(self.id().clone(), action) 23 | } 24 | 25 | /// Answers the query without any action. 26 | fn ignore(&self) -> AnswerCallbackQuery<'_> { 27 | self.answer(None) 28 | } 29 | 30 | /// Opens a URL. 31 | fn open_url(&self, url: impl Into) -> AnswerCallbackQuery<'_> { 32 | self.answer(Some(CallbackAction::with_url(url))) 33 | } 34 | 35 | /// Shows a notification to the user. 36 | fn notify(&self, text: impl Into) -> AnswerCallbackQuery<'_> { 37 | self.answer(Some(CallbackAction::with_notification(text))) 38 | } 39 | 40 | /// Shows an alert to the user. 41 | fn alert(&self, text: impl Into) -> AnswerCallbackQuery<'_> { 42 | self.answer(Some(CallbackAction::with_alert(text))) 43 | } 44 | } 45 | 46 | impl Callback for T {} 47 | -------------------------------------------------------------------------------- /src/contexts/methods/copyable.rs: -------------------------------------------------------------------------------- 1 | use super::Message; 2 | use crate::{methods::CopyMessage, types::parameters::ImplicitChatId}; 3 | 4 | /// Provides methods for copyable messages. 5 | pub trait Copyable: Message { 6 | /// Copies this message to another chat. 7 | fn copy_to(&self, chat_id: impl ImplicitChatId) -> CopyMessage<'_> { 8 | self.bot() 9 | .copy_message(chat_id, self.chat().id, self.message_id()) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/contexts/methods/forwardable.rs: -------------------------------------------------------------------------------- 1 | use super::Message; 2 | use crate::{methods::ForwardMessage, types::parameters::ImplicitChatId}; 3 | 4 | /// Provides methods for forwardable messages. 5 | pub trait Forwardable: Message { 6 | /// Forwards this message to another chat. 7 | fn forward_to(&self, chat_id: impl ImplicitChatId) -> ForwardMessage<'_> { 8 | self.bot() 9 | .forward_message(chat_id, self.chat().id, self.message_id()) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/contexts/methods/pinnable.rs: -------------------------------------------------------------------------------- 1 | use super::Message; 2 | use crate::methods::PinChatMessage; 3 | 4 | /// Provides methods for pinnable messages. 5 | pub trait Pinnable: Message { 6 | /// Pins this message. 7 | fn pin_this_message(&self) -> PinChatMessage<'_> { 8 | self.bot() 9 | .pin_chat_message(self.chat().id, self.message_id()) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/contexts/migration.rs: -------------------------------------------------------------------------------- 1 | use crate::types::chat; 2 | 3 | message_base! { 4 | struct Migration { 5 | /// The old ID of the group. 6 | old_id: chat::Id, 7 | } -> EventLoop::migration 8 | 9 | fn new(old_id: chat::Id,) -> Self { 10 | Self { 11 | old_id: old_id, 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/contexts/my_chat_member.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | types::{chat, Chat, User}, 3 | Bot, 4 | }; 5 | 6 | common! { 7 | /// The context for [`my_chat_member`] handlers. 8 | /// 9 | /// [`my_chat_member`]: crate::EventLoop::my_chat_member 10 | struct MyChatMember { 11 | /// The chat in which the change occured. 12 | chat: Chat, 13 | /// The user who caused the change. 14 | from: User, 15 | /// Timestamp when this change occured. 16 | date: i64, 17 | /// Previous information about the bot's member status. 18 | before: chat::Member, 19 | /// New information about the bot's member status. 20 | after: chat::Member, 21 | } 22 | } 23 | 24 | impl MyChatMember { 25 | #[allow(clippy::missing_const_for_fn)] 26 | pub(crate) fn new(bot: Bot, update: chat::member::Updated) -> Self { 27 | Self { 28 | bot, 29 | chat: update.chat, 30 | from: update.from, 31 | date: update.date, 32 | before: update.before, 33 | after: update.after, 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/contexts/new_chat_photo.rs: -------------------------------------------------------------------------------- 1 | use crate::types::PhotoSize; 2 | 3 | message_base! { 4 | struct NewChatPhoto { 5 | /// The photo. 6 | photo: Vec, 7 | } -> EventLoop::new_chat_photo 8 | 9 | fn new(photo: Vec,) -> Self { 10 | Self { 11 | photo: photo, 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/contexts/new_chat_title.rs: -------------------------------------------------------------------------------- 1 | message_base! { 2 | struct NewChatTitle { 3 | /// The title. 4 | title: String, 5 | } -> EventLoop::new_chat_title 6 | 7 | fn new(title: String,) -> Self { 8 | Self { 9 | title: title, 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/contexts/new_members.rs: -------------------------------------------------------------------------------- 1 | use crate::types::User; 2 | 3 | message_base! { 4 | struct NewMembers { 5 | /// The new members. 6 | members: Vec, 7 | } -> EventLoop::new_members 8 | 9 | fn new(members: Vec,) -> Self { 10 | Self { 11 | members: members, 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/contexts/passport.rs: -------------------------------------------------------------------------------- 1 | use crate::types::passport; 2 | 3 | media_message! { 4 | struct Passport { 5 | /// The passport data. 6 | passport_data: passport::Data, 7 | } -> EventLoop::passport 8 | 9 | fn new() -> Self { 10 | Self { } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/contexts/payment.rs: -------------------------------------------------------------------------------- 1 | use crate::types::SuccessfulPayment; 2 | 3 | media_message! { 4 | struct Payment { 5 | /// Information about the payment. 6 | invoice: SuccessfulPayment, 7 | } -> EventLoop::payment 8 | 9 | fn new() -> Self { 10 | Self { } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/contexts/photo.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | contexts::fields::{self, Album, AnyText, Caption}, 3 | types::{message::Text, PhotoSize}, 4 | }; 5 | 6 | media_message! { 7 | struct Photo { 8 | /// The photo. 9 | photo: Vec, 10 | /// The caption of the photo. 11 | caption: Text, 12 | /// The media group's ID. 13 | media_group_id: Option, 14 | } -> EventLoop::photo 15 | 16 | fn new(caption: Text, media_group_id: Option,) -> Self { 17 | Self { 18 | caption: caption, 19 | media_group_id: media_group_id, 20 | } 21 | } 22 | } 23 | 24 | impl fields::Photo for Photo { 25 | #[must_use] 26 | fn photo(&self) -> &[PhotoSize] { 27 | &self.photo[..] 28 | } 29 | } 30 | 31 | impl Caption for Photo { 32 | #[must_use] 33 | fn caption(&self) -> &Text { 34 | &self.caption 35 | } 36 | } 37 | 38 | impl AnyText for Photo { 39 | #[must_use] 40 | fn text(&self) -> &Text { 41 | &self.caption 42 | } 43 | } 44 | 45 | impl Album for Photo { 46 | #[must_use] 47 | fn media_group_id(&self) -> Option<&str> { 48 | self.media_group_id.as_ref().map(String::as_ref) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/contexts/pinned_message.rs: -------------------------------------------------------------------------------- 1 | use crate::types::Message; 2 | 3 | message_base! { 4 | struct PinnedMessage { 5 | /// The pinned message. 6 | message: Message, 7 | } -> EventLoop::pinned_message 8 | 9 | fn new (message: Message,) -> Self { 10 | Self { 11 | message: message, 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/contexts/poll.rs: -------------------------------------------------------------------------------- 1 | use crate::types; 2 | 3 | media_message! { 4 | struct Poll { 5 | /// The poll. 6 | poll: types::Poll, 7 | } -> EventLoop::poll 8 | 9 | fn new() -> Self { 10 | Self { } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/contexts/poll_answer.rs: -------------------------------------------------------------------------------- 1 | use crate::{types::poll::Answer, Bot}; 2 | 3 | common! { 4 | /// The context for [`poll_answer`] handlers. 5 | /// 6 | /// [`poll_answer`]: crate::EventLoop::poll_answer 7 | struct PollAnswer { 8 | /// The new answer in the poll. 9 | answer: Answer, 10 | } 11 | } 12 | 13 | impl PollAnswer { 14 | pub(crate) const fn new(bot: Bot, answer: Answer) -> Self { 15 | Self { bot, answer } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/contexts/proximity_alert.rs: -------------------------------------------------------------------------------- 1 | use crate::types; 2 | 3 | message_base! { 4 | struct ProximityAlert { 5 | /// The proximity alert 6 | alert: types::ProximityAlert, 7 | } -> EventLoop::proximity_alert 8 | 9 | fn new(alert: types::ProximityAlert,) -> Self { 10 | Self { 11 | alert: alert, 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/contexts/scheduled_voice_chat.rs: -------------------------------------------------------------------------------- 1 | use crate::types::voice_chat; 2 | 3 | message_base! { 4 | struct ScheduledVoiceChat { 5 | /// Timestamp when the voice chat will be started. 6 | start_date: i64, 7 | } -> EventLoop::scheduled_voice_chat 8 | 9 | fn new(scheduled: voice_chat::Scheduled,) -> Self { 10 | Self { 11 | start_date: scheduled.start_date, 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/contexts/started_voice_chat.rs: -------------------------------------------------------------------------------- 1 | message_base! { 2 | struct StartedVoiceChat {} -> EventLoop::started_voice_chat 3 | 4 | fn new() -> Self { 5 | Self {} 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/contexts/sticker.rs: -------------------------------------------------------------------------------- 1 | use crate::types; 2 | 3 | media_message! { 4 | struct Sticker { 5 | /// The sticker. 6 | sticker: types::Sticker, 7 | } -> EventLoop::sticker 8 | 9 | fn new() -> Self { 10 | Self { } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/contexts/text.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | contexts::fields::{self, AnyText}, 3 | types::message, 4 | }; 5 | 6 | media_message! { 7 | struct Text { 8 | /// The text of the message. 9 | text: message::Text, 10 | } -> EventLoop::text 11 | 12 | fn new() -> Self { 13 | Self { } 14 | } 15 | } 16 | 17 | impl fields::Text for Text { 18 | #[must_use] 19 | fn text(&self) -> &message::Text { 20 | &self.text 21 | } 22 | } 23 | 24 | impl AnyText for Text { 25 | #[must_use] 26 | fn text(&self) -> &message::Text { 27 | &self.text 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/contexts/unhandled.rs: -------------------------------------------------------------------------------- 1 | use crate::{types::update, Bot}; 2 | 3 | common! { 4 | /// The context for [`unhandled`] handlers. 5 | /// 6 | /// [`unhandled`]: crate::EventLoop::unhandled 7 | struct Unhandled { 8 | /// The unhandled update. 9 | update: update::Kind, 10 | } 11 | } 12 | 13 | impl Unhandled { 14 | pub(crate) const fn new(bot: Bot, update: update::Kind) -> Self { 15 | Self { bot, update } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/contexts/updated_poll.rs: -------------------------------------------------------------------------------- 1 | use crate::{types::Poll, Bot}; 2 | 3 | common! { 4 | /// The context for [`updated_poll`] handlers. 5 | /// 6 | /// [`updated_poll`]: crate::EventLoop::updated_poll 7 | struct UpdatedPoll { 8 | /// The new state of the poll. 9 | poll: Poll, 10 | } 11 | } 12 | 13 | impl UpdatedPoll { 14 | pub(crate) const fn new(bot: Bot, poll: Poll) -> Self { 15 | Self { bot, poll } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/contexts/venue.rs: -------------------------------------------------------------------------------- 1 | use crate::types; 2 | 3 | media_message! { 4 | struct Venue { 5 | /// The venue. 6 | venue: types::Venue, 7 | } -> EventLoop::venue 8 | 9 | fn new() -> Self { 10 | Self { } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/contexts/video.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | contexts::fields::{self, Album, AnyText, Caption}, 3 | types::{self, message::Text}, 4 | }; 5 | 6 | media_message! { 7 | struct Video { 8 | /// The video. 9 | video: types::Video, 10 | /// The caption of the video. 11 | caption: Text, 12 | /// The media group's ID. 13 | media_group_id: Option, 14 | } -> EventLoop::video 15 | 16 | fn new(caption: Text, media_group_id: Option,) -> Self { 17 | Self { 18 | caption: caption, 19 | media_group_id: media_group_id, 20 | } 21 | } 22 | } 23 | 24 | impl fields::Video for Video { 25 | #[must_use] 26 | fn video(&self) -> &types::Video { 27 | &self.video 28 | } 29 | } 30 | 31 | impl Caption for Video { 32 | #[must_use] 33 | fn caption(&self) -> &Text { 34 | &self.caption 35 | } 36 | } 37 | 38 | impl AnyText for Video { 39 | #[must_use] 40 | fn text(&self) -> &Text { 41 | &self.caption 42 | } 43 | } 44 | 45 | impl Album for Video { 46 | #[must_use] 47 | fn media_group_id(&self) -> Option<&str> { 48 | self.media_group_id.as_ref().map(String::as_ref) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/contexts/video_note.rs: -------------------------------------------------------------------------------- 1 | use crate::types; 2 | 3 | media_message! { 4 | struct VideoNote { 5 | /// The video note. 6 | video_note: types::VideoNote, 7 | } -> EventLoop::video_note 8 | 9 | fn new() -> Self { 10 | Self { } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/contexts/voice.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | contexts::fields::{AnyText, Caption}, 3 | types::{self, message::Text}, 4 | }; 5 | 6 | media_message! { 7 | struct Voice { 8 | /// The voice. 9 | voice: types::Voice, 10 | /// The caption of the voice. 11 | caption: Text, 12 | } -> EventLoop::voice 13 | 14 | fn new(caption: Text,) -> Self { 15 | Self { 16 | caption: caption, 17 | } 18 | } 19 | } 20 | 21 | impl Caption for Voice { 22 | #[must_use] 23 | fn caption(&self) -> &Text { 24 | &self.caption 25 | } 26 | } 27 | 28 | impl AnyText for Voice { 29 | #[must_use] 30 | fn text(&self) -> &Text { 31 | &self.caption 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | //! Types representing errors. 2 | 3 | mod download; 4 | mod http_webhook; 5 | mod https_webhook; 6 | mod method_call; 7 | mod polling; 8 | mod polling_setup; 9 | 10 | pub use { 11 | download::Download, http_webhook::HttpWebhook, https_webhook::HttpsWebhook, 12 | method_call::MethodCall, polling::Polling, polling_setup::PollingSetup, 13 | }; 14 | -------------------------------------------------------------------------------- /src/errors/polling.rs: -------------------------------------------------------------------------------- 1 | use super::MethodCall; 2 | use is_macro::Is; 3 | use tokio::time::error::Elapsed; 4 | 5 | /// Represents possible errors that may happen during the polling event loop. 6 | #[derive(Debug, Is)] 7 | pub enum Polling { 8 | /// Calling `GetUpdates` resulted in an error. 9 | Fetching(MethodCall), 10 | /// Calling `GetUpdates` timed out. 11 | Timeout(Elapsed), 12 | } 13 | 14 | impl From for Polling { 15 | #[must_use] 16 | fn from(error: MethodCall) -> Self { 17 | Self::Fetching(error) 18 | } 19 | } 20 | 21 | impl From for Polling { 22 | #[must_use] 23 | fn from(error: Elapsed) -> Self { 24 | Self::Timeout(error) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/errors/polling_setup.rs: -------------------------------------------------------------------------------- 1 | use super::MethodCall; 2 | use is_macro::Is; 3 | use tokio::time::error::Elapsed; 4 | 5 | /// Represents possible errors that may happen during preparation of the 6 | /// polling event loop. 7 | #[derive(Debug, Is)] 8 | pub enum PollingSetup { 9 | /// Calling the `deleteWebhook` method resulted in an error. 10 | DeleteWebhook(MethodCall), 11 | /// Calling the `deleteWebhook` method timed out. 12 | DeleteWebhookTimeout(Elapsed), 13 | /// Calling the `setMyCommands` method resulted in an error. 14 | SetMyCommands(MethodCall), 15 | /// Calling the `setMyCommands` method timed out. 16 | SetMyCommandsTimeout(Elapsed), 17 | } 18 | 19 | impl From for PollingSetup { 20 | #[must_use] 21 | fn from(error: MethodCall) -> Self { 22 | Self::DeleteWebhook(error) 23 | } 24 | } 25 | 26 | impl From for PollingSetup { 27 | #[must_use] 28 | fn from(error: Elapsed) -> Self { 29 | Self::DeleteWebhookTimeout(error) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/event_loop/handlers_macros.rs: -------------------------------------------------------------------------------- 1 | macro_rules! handlers { 2 | ( 3 | $( 4 | $(#[doc = $doc:literal])+ 5 | $name:ident: $context:path, 6 | )+ 7 | ) => { 8 | $( 9 | $(#[doc = $doc])+ 10 | pub fn $name(&mut self, handler: H) 11 | where 12 | H: (Fn(std::sync::Arc<$context>) -> F) + Send + Sync + 'static, 13 | F: std::future::Future + Send + 'static, 14 | { 15 | self.add_handler(handler); 16 | } 17 | )+ 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/internal.rs: -------------------------------------------------------------------------------- 1 | pub trait Sealed {} // used for sealing traits 2 | -------------------------------------------------------------------------------- /src/markup/bold.rs: -------------------------------------------------------------------------------- 1 | use super::{html, markdown_v2, Formattable, Nesting}; 2 | use std::fmt::{self, Formatter, Write}; 3 | 4 | /// Formats text in bold. Can be created with [`bold`]. 5 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] 6 | #[must_use = "formatters need to be formatted with `markdown_v2` or `html`"] 7 | pub struct Bold(T); 8 | 9 | /// Formats text in bold. 10 | pub fn bold(text: T) -> Bold { 11 | Bold(text) 12 | } 13 | 14 | impl markdown_v2::Formattable for Bold { 15 | fn format( 16 | &self, 17 | formatter: &mut Formatter, 18 | nesting: Nesting, 19 | ) -> fmt::Result { 20 | if !nesting.bold { 21 | formatter.write_char('*')?; 22 | } 23 | markdown_v2::Formattable::format( 24 | &self.0, 25 | formatter, 26 | Nesting { 27 | bold: true, 28 | ..nesting 29 | }, 30 | )?; 31 | if !nesting.bold { 32 | formatter.write_char('*')?; 33 | } 34 | Ok(()) 35 | } 36 | } 37 | 38 | impl html::Formattable for Bold { 39 | fn format( 40 | &self, 41 | formatter: &mut Formatter, 42 | nesting: Nesting, 43 | ) -> fmt::Result { 44 | formatter.write_str("")?; 45 | html::Formattable::format(&self.0, formatter, nesting)?; 46 | formatter.write_str("") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/markup/inline_code.rs: -------------------------------------------------------------------------------- 1 | use super::{html, markdown_v2, Nesting}; 2 | use std::{ 3 | fmt::{self, Formatter, Write}, 4 | ops::Deref, 5 | }; 6 | 7 | /// Formats an inline piece of code. Can be created with [`inline_code`]. 8 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] 9 | #[must_use = "formatters need to be formatted with `markdown_v2` or `html`"] 10 | pub struct InlineCode(T); 11 | 12 | /// Formats an inline piece of code. 13 | pub fn inline_code(code: T) -> InlineCode 14 | where 15 | T: Deref, 16 | { 17 | InlineCode(code) 18 | } 19 | 20 | impl markdown_v2::Formattable for InlineCode 21 | where 22 | T: Deref, 23 | { 24 | fn format(&self, formatter: &mut Formatter, _: Nesting) -> fmt::Result { 25 | formatter.write_char('`')?; 26 | self.0.chars().try_for_each(|x| { 27 | if markdown_v2::ESCAPED_CODE_CHARACTERS.contains(&x) { 28 | formatter.write_char('\\')?; 29 | } 30 | formatter.write_char(x) 31 | })?; 32 | formatter.write_char('`') 33 | } 34 | } 35 | 36 | impl html::Formattable for InlineCode 37 | where 38 | T: Deref, 39 | { 40 | fn format( 41 | &self, 42 | formatter: &mut Formatter, 43 | nesting: Nesting, 44 | ) -> fmt::Result { 45 | formatter.write_str("")?; 46 | html::Formattable::format(&&*self.0, formatter, nesting)?; 47 | formatter.write_str("") 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/markup/italic.rs: -------------------------------------------------------------------------------- 1 | use super::{html, markdown_v2, Formattable, Nesting}; 2 | use std::fmt::{self, Formatter}; 3 | 4 | /// Formats text in italic. Can be created with [`italic`]. 5 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] 6 | #[must_use = "formatters need to be formatted with `markdown_v2` or `html`"] 7 | pub struct Italic(T); 8 | 9 | /// Formats text in italic. 10 | pub fn italic(text: T) -> Italic { 11 | Italic(text) 12 | } 13 | 14 | impl markdown_v2::Formattable for Italic { 15 | fn format( 16 | &self, 17 | formatter: &mut Formatter, 18 | nesting: Nesting, 19 | ) -> fmt::Result { 20 | if !nesting.italic { 21 | formatter.write_str("\r_")?; 22 | } 23 | markdown_v2::Formattable::format( 24 | &self.0, 25 | formatter, 26 | Nesting { 27 | italic: true, 28 | ..nesting 29 | }, 30 | )?; 31 | if !nesting.italic { 32 | formatter.write_str("\r_")?; 33 | } 34 | Ok(()) 35 | } 36 | } 37 | 38 | impl html::Formattable for Italic { 39 | fn format( 40 | &self, 41 | formatter: &mut Formatter, 42 | nesting: Nesting, 43 | ) -> fmt::Result { 44 | formatter.write_str("")?; 45 | html::Formattable::format(&self.0, formatter, nesting)?; 46 | formatter.write_str("") 47 | } 48 | } 49 | 50 | #[cfg(tests)] 51 | mod tests { 52 | use super::{italic, markdown_v2}; 53 | #[test] 54 | fn sibling_italics_are_displayed_correctly() { 55 | assert_eq!( 56 | markdown_v2((italic("a"), italic("b"))).to_string(), 57 | "\r_a\r_\r_b\r_" 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/markup/raw.rs: -------------------------------------------------------------------------------- 1 | use super::{html, markdown_v2, Nesting}; 2 | use std::{ 3 | fmt::{self, Formatter}, 4 | ops::Deref, 5 | }; 6 | 7 | /// Represents a raw string for formatting. Can be created with [`raw`]. 8 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] 9 | #[must_use = "formatters need to be formatted with `markdown_v2` or `html`"] 10 | pub struct Raw(T); 11 | 12 | /// Creates a raw string for formatting. 13 | /// 14 | /// **Use this utility with extreme care**: it inserts the provided string into 15 | /// the resulting string as-is. As a result, if it contains malformed 16 | /// formatting, the resulting string won't be parsed by Telegram. Also, 17 | /// unchecked user-provided input may insert its own formatting, which may be 18 | /// undesrirable. Note that all other utilities automatically escape provided 19 | /// strings as needed. 20 | pub fn raw(iterator: I) -> Raw 21 | where 22 | for<'a> &'a I: IntoIterator, 23 | T: Deref, 24 | { 25 | Raw(iterator) 26 | } 27 | 28 | impl markdown_v2::Formattable for Raw 29 | where 30 | for<'a> &'a I: IntoIterator, 31 | T: Deref, 32 | { 33 | fn format(&self, formatter: &mut Formatter, _: Nesting) -> fmt::Result { 34 | (&self.0) 35 | .into_iter() 36 | .try_for_each(|x| formatter.write_str(&*x)) 37 | } 38 | } 39 | 40 | impl html::Formattable for Raw 41 | where 42 | for<'a> &'a I: IntoIterator, 43 | T: Deref, 44 | { 45 | fn format( 46 | &self, 47 | formatter: &mut Formatter, 48 | nesting: Nesting, 49 | ) -> fmt::Result { 50 | markdown_v2::Formattable::format(self, formatter, nesting) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/markup/strikethrough.rs: -------------------------------------------------------------------------------- 1 | use super::{html, markdown_v2, Formattable, Nesting}; 2 | use std::fmt::{self, Formatter, Write}; 3 | 4 | /// Formats text with strikethrough. Can be created with [`strikethrough`]. 5 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] 6 | #[must_use = "formatters need to be formatted with `markdown_v2` or `html`"] 7 | pub struct Strikethrough(T); 8 | 9 | /// Formats text with strikethrough. 10 | pub fn strikethrough(text: T) -> Strikethrough { 11 | Strikethrough(text) 12 | } 13 | 14 | impl markdown_v2::Formattable for Strikethrough { 15 | fn format( 16 | &self, 17 | formatter: &mut Formatter, 18 | nesting: Nesting, 19 | ) -> fmt::Result { 20 | if !nesting.strikethrough { 21 | formatter.write_char('~')?; 22 | } 23 | markdown_v2::Formattable::format( 24 | &self.0, 25 | formatter, 26 | Nesting { 27 | strikethrough: true, 28 | ..nesting 29 | }, 30 | )?; 31 | if !nesting.strikethrough { 32 | formatter.write_char('~')?; 33 | } 34 | Ok(()) 35 | } 36 | } 37 | 38 | impl html::Formattable for Strikethrough { 39 | fn format( 40 | &self, 41 | formatter: &mut Formatter, 42 | nesting: Nesting, 43 | ) -> fmt::Result { 44 | formatter.write_str("")?; 45 | html::Formattable::format(&self.0, formatter, nesting)?; 46 | formatter.write_str("") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/markup/underline.rs: -------------------------------------------------------------------------------- 1 | use super::{html, markdown_v2, Formattable, Nesting}; 2 | use std::fmt::{self, Formatter}; 3 | 4 | /// Formats text underlined. Can be created with [`underline`]. 5 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] 6 | #[must_use = "formatters need to be formatted with `markdown_v2` or `html`"] 7 | pub struct Underline(T); 8 | 9 | /// Formats text underlined. 10 | pub fn underline(text: T) -> Underline { 11 | Underline(text) 12 | } 13 | 14 | impl markdown_v2::Formattable for Underline { 15 | fn format( 16 | &self, 17 | formatter: &mut Formatter, 18 | nesting: Nesting, 19 | ) -> fmt::Result { 20 | if !nesting.underline { 21 | formatter.write_str("\r__")?; 22 | } 23 | markdown_v2::Formattable::format( 24 | &self.0, 25 | formatter, 26 | Nesting { 27 | underline: true, 28 | ..nesting 29 | }, 30 | )?; 31 | if !nesting.underline { 32 | formatter.write_str("\r__")?; 33 | } 34 | Ok(()) 35 | } 36 | } 37 | 38 | impl html::Formattable for Underline { 39 | fn format( 40 | &self, 41 | formatter: &mut Formatter, 42 | nesting: Nesting, 43 | ) -> fmt::Result { 44 | formatter.write_str("")?; 45 | html::Formattable::format(&self.0, formatter, nesting)?; 46 | formatter.write_str("") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/methods/answer_pre_checkout_query.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{bot::InnerBot, errors, types::pre_checkout_query}; 3 | use serde::Serialize; 4 | 5 | /// Answers a pre-checkout query. 6 | /// 7 | /// Reflects the [`answerPreCheckoutQuery`][docs] method. 8 | /// 9 | /// [docs]: https://core.telegram.org/bots/api#answerprecheckoutquery 10 | #[derive(Debug, Clone, Serialize)] 11 | #[must_use = "methods do nothing unless turned into a future"] 12 | pub struct AnswerPreCheckoutQuery<'a> { 13 | #[serde(skip)] 14 | bot: &'a InnerBot, 15 | pre_checkout_query_id: pre_checkout_query::Id, 16 | ok: bool, 17 | #[serde(skip_serializing_if = "Option::is_none")] 18 | error_message: Option, 19 | } 20 | 21 | impl<'a> AnswerPreCheckoutQuery<'a> { 22 | pub(crate) fn new( 23 | bot: &'a InnerBot, 24 | pre_checkout_query_id: pre_checkout_query::Id, 25 | result: Result<(), impl Into>, 26 | ) -> Self { 27 | Self { 28 | bot, 29 | pre_checkout_query_id, 30 | ok: result.is_ok(), 31 | error_message: result.err().map(Into::into), 32 | } 33 | } 34 | } 35 | 36 | impl AnswerPreCheckoutQuery<'_> { 37 | /// Calls the method. 38 | pub async fn call(self) -> Result<(), errors::MethodCall> { 39 | call_method::( 40 | self.bot, 41 | "answerPreCheckoutQuery", 42 | None, 43 | serde_json::to_vec(&self).unwrap(), 44 | ) 45 | .await?; 46 | 47 | Ok(()) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/methods/close.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{bot::InnerBot, errors}; 3 | 4 | /// Logs out from a self-hosted Bot API server. 5 | /// 6 | /// Represents the [`Close`][docs] method. 7 | /// 8 | /// [docs]: https://core.telegram.org/bots/api#Close 9 | #[derive(Debug, Clone)] 10 | #[must_use = "methods do nothing unless turned into a future"] 11 | pub struct Close<'a> { 12 | bot: &'a InnerBot, 13 | } 14 | 15 | impl<'a> Close<'a> { 16 | pub(crate) const fn new(bot: &'a InnerBot) -> Self { 17 | Self { bot } 18 | } 19 | } 20 | 21 | impl Close<'_> { 22 | /// Calls the method. 23 | pub async fn call(self) -> Result<(), errors::MethodCall> { 24 | call_method::(self.bot, "close", None, Vec::new()).await?; 25 | Ok(()) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/methods/delete_chat_photo.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::parameters::{ChatId, ImplicitChatId}, 6 | }; 7 | use serde::Serialize; 8 | 9 | /// Deletes a chat's photo. 10 | /// 11 | /// Reflects the [`deleteChatPhoto`][docs] method. 12 | /// 13 | /// [docs]: https://core.telegram.org/bots/api#deletechatphoto 14 | #[derive(Serialize, Debug, Clone)] 15 | #[must_use = "methods do nothing unless turned into a future"] 16 | pub struct DeleteChatPhoto<'a> { 17 | #[serde(skip)] 18 | bot: &'a InnerBot, 19 | chat_id: ChatId, 20 | } 21 | 22 | impl<'a> DeleteChatPhoto<'a> { 23 | pub(crate) fn new(bot: &'a InnerBot, chat_id: impl ImplicitChatId) -> Self { 24 | Self { 25 | bot, 26 | chat_id: chat_id.into(), 27 | } 28 | } 29 | } 30 | 31 | impl DeleteChatPhoto<'_> { 32 | /// Calls the method. 33 | pub async fn call(self) -> Result<(), errors::MethodCall> { 34 | call_method::( 35 | self.bot, 36 | "deleteChatPhoto", 37 | None, 38 | serde_json::to_vec(&self).unwrap(), 39 | ) 40 | .await?; 41 | 42 | Ok(()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/methods/delete_chat_sticker_set.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::parameters::{ChatId, ImplicitChatId}, 6 | }; 7 | use serde::Serialize; 8 | 9 | /// Deletes a chat's sticker set. 10 | /// 11 | /// Reflects the [`deleteChatStickerSet`][docs] method. 12 | /// 13 | /// [docs]: https://core.telegram.org/bots/api#deletechatstickerset 14 | #[derive(Serialize, Debug, Clone)] 15 | #[must_use = "methods do nothing unless turned into a future"] 16 | pub struct DeleteChatStickerSet<'a> { 17 | #[serde(skip)] 18 | bot: &'a InnerBot, 19 | chat_id: ChatId, 20 | } 21 | 22 | impl<'a> DeleteChatStickerSet<'a> { 23 | pub(crate) fn new(bot: &'a InnerBot, chat_id: impl ImplicitChatId) -> Self { 24 | Self { 25 | bot, 26 | chat_id: chat_id.into(), 27 | } 28 | } 29 | } 30 | 31 | impl DeleteChatStickerSet<'_> { 32 | /// Calls the method. 33 | pub async fn call(self) -> Result<(), errors::MethodCall> { 34 | call_method::( 35 | self.bot, 36 | "deleteChatStickerSet", 37 | None, 38 | serde_json::to_vec(&self).unwrap(), 39 | ) 40 | .await?; 41 | 42 | Ok(()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/methods/delete_message.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{ 6 | message, 7 | parameters::{ChatId, ImplicitChatId}, 8 | }, 9 | }; 10 | use serde::Serialize; 11 | 12 | /// Deletes a message from a chat. 13 | /// 14 | /// Reflects the [`deleteMessage`][docs] method. 15 | /// 16 | /// [docs]: https://core.telegram.org/bots/api#deletemessage 17 | #[derive(Serialize, Debug, Clone)] 18 | #[must_use = "methods do nothing unless turned into a future"] 19 | pub struct DeleteMessage<'a> { 20 | #[serde(skip)] 21 | bot: &'a InnerBot, 22 | chat_id: ChatId, 23 | message_id: message::Id, 24 | } 25 | 26 | impl<'a> DeleteMessage<'a> { 27 | pub(crate) fn new( 28 | bot: &'a InnerBot, 29 | chat_id: impl ImplicitChatId, 30 | message_id: message::Id, 31 | ) -> Self { 32 | Self { 33 | bot, 34 | chat_id: chat_id.into(), 35 | message_id, 36 | } 37 | } 38 | } 39 | 40 | impl DeleteMessage<'_> { 41 | /// Calls the method. 42 | pub async fn call(self) -> Result<(), errors::MethodCall> { 43 | call_method::( 44 | self.bot, 45 | "deleteMessage", 46 | None, 47 | serde_json::to_vec(&self).unwrap(), 48 | ) 49 | .await?; 50 | 51 | Ok(()) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/methods/delete_sticker_from_set.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{bot::InnerBot, errors}; 3 | use serde::Serialize; 4 | 5 | /// Deletes a sticker from a sticker set. 6 | /// 7 | /// Reflects the [`deleteStickerFromSet`][docs] method 8 | /// 9 | /// [docs]: https://core.telegram.org/bots/api#deletestickerfromset 10 | #[derive(Serialize, Debug, Clone)] 11 | #[must_use = "methods do nothing unless turned into a future"] 12 | pub struct DeleteStickerFromSet<'a> { 13 | #[serde(skip)] 14 | bot: &'a InnerBot, 15 | sticker: String, 16 | } 17 | 18 | impl<'a> DeleteStickerFromSet<'a> { 19 | pub(crate) fn new(bot: &'a InnerBot, sticker: impl Into) -> Self { 20 | Self { 21 | bot, 22 | sticker: sticker.into(), 23 | } 24 | } 25 | } 26 | 27 | impl DeleteStickerFromSet<'_> { 28 | /// Calls the method. 29 | pub async fn call(self) -> Result<(), errors::MethodCall> { 30 | call_method::( 31 | self.bot, 32 | "deleteStickerFromSet", 33 | None, 34 | serde_json::to_vec(&self).unwrap(), 35 | ) 36 | .await?; 37 | 38 | Ok(()) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/methods/delete_webhook.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{bot::InnerBot, errors}; 3 | 4 | #[derive(Debug, Clone)] 5 | #[must_use] 6 | pub struct DeleteWebhook<'a> { 7 | bot: &'a InnerBot, 8 | } 9 | 10 | impl<'a> DeleteWebhook<'a> { 11 | pub(crate) const fn new(bot: &'a InnerBot) -> Self { 12 | Self { bot } 13 | } 14 | } 15 | 16 | impl DeleteWebhook<'_> { 17 | /// Calls the method. 18 | pub async fn call(self) -> Result<(), errors::MethodCall> { 19 | call_method::(self.bot, "deleteWebhook", None, Vec::new()) 20 | .await?; 21 | 22 | Ok(()) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/methods/edit_inline_reply_markup.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{keyboard::inline, InlineMessageId}, 6 | }; 7 | use serde::Serialize; 8 | 9 | /// Edits the inline keyboard of a message sent via the inline mode. 10 | /// 11 | /// Reflects the [`editMessageReplyMarkup`][docs] method. 12 | /// 13 | /// [docs]: https://core.telegram.org/bots/api#editmessagereplymarkup 14 | #[derive(Serialize, Debug, Clone)] 15 | #[must_use = "methods do nothing unless turned into a future"] 16 | pub struct EditInlineReplyMarkup<'a> { 17 | #[serde(skip)] 18 | bot: &'a InnerBot, 19 | inline_message_id: InlineMessageId, 20 | reply_markup: inline::Keyboard, 21 | } 22 | 23 | impl<'a> EditInlineReplyMarkup<'a> { 24 | pub(crate) const fn new( 25 | bot: &'a InnerBot, 26 | inline_message_id: InlineMessageId, 27 | reply_markup: inline::Keyboard, 28 | ) -> Self { 29 | Self { 30 | bot, 31 | inline_message_id, 32 | reply_markup, 33 | } 34 | } 35 | } 36 | 37 | impl EditInlineReplyMarkup<'_> { 38 | /// Calls the method. 39 | pub async fn call(self) -> Result<(), errors::MethodCall> { 40 | call_method::( 41 | self.bot, 42 | "editMessageReplyMarkup", 43 | None, 44 | serde_json::to_vec(&self).unwrap(), 45 | ) 46 | .await?; 47 | 48 | Ok(()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/methods/edit_message_reply_markup.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{ 6 | keyboard::inline, 7 | message::{self, Message}, 8 | parameters::{ChatId, ImplicitChatId}, 9 | }, 10 | }; 11 | use serde::Serialize; 12 | 13 | /// Edits the inline keyboard of a message sent by the bot itself. 14 | /// 15 | /// Reflects the [`editMessageReplyMarkup`][docs] method. 16 | /// 17 | /// [docs]: https://core.telegram.org/bots/api#editmessagereplymarkup 18 | #[derive(Serialize, Debug, Clone)] 19 | #[must_use = "methods do nothing unless turned into a future"] 20 | pub struct EditMessageReplyMarkup<'a> { 21 | #[serde(skip)] 22 | bot: &'a InnerBot, 23 | chat_id: ChatId, 24 | message_id: message::Id, 25 | reply_markup: inline::Keyboard, 26 | } 27 | 28 | impl<'a> EditMessageReplyMarkup<'a> { 29 | pub(crate) fn new( 30 | bot: &'a InnerBot, 31 | chat_id: impl ImplicitChatId, 32 | message_id: message::Id, 33 | reply_markup: inline::Keyboard, 34 | ) -> Self { 35 | Self { 36 | bot, 37 | chat_id: chat_id.into(), 38 | message_id, 39 | reply_markup, 40 | } 41 | } 42 | } 43 | 44 | impl EditMessageReplyMarkup<'_> { 45 | /// Calls the method. 46 | pub async fn call(self) -> Result { 47 | call_method( 48 | self.bot, 49 | "editMessageReplyMarkup", 50 | None, 51 | serde_json::to_vec(&self).unwrap(), 52 | ) 53 | .await 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/methods/export_chat_invite_link.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::parameters::{ChatId, ImplicitChatId}, 6 | }; 7 | use serde::Serialize; 8 | 9 | /// Exports a chat's invite link. 10 | /// 11 | /// Reflects the [`exportChatInviteLink`][docs] method. 12 | /// 13 | /// [docs]: https://core.telegram.org/bots/api#exportchatinvitelink 14 | #[derive(Serialize, Debug, Clone)] 15 | #[must_use = "methods do nothing unless turned into a future"] 16 | pub struct ExportChatInviteLink<'a> { 17 | #[serde(skip)] 18 | bot: &'a InnerBot, 19 | chat_id: ChatId, 20 | } 21 | 22 | impl<'a> ExportChatInviteLink<'a> { 23 | pub(crate) fn new(bot: &'a InnerBot, chat_id: impl ImplicitChatId) -> Self { 24 | Self { 25 | bot, 26 | chat_id: chat_id.into(), 27 | } 28 | } 29 | } 30 | 31 | impl ExportChatInviteLink<'_> { 32 | /// Calls the method. 33 | pub async fn call(self) -> Result { 34 | call_method( 35 | self.bot, 36 | "exportChatInviteLink", 37 | None, 38 | serde_json::to_vec(&self).unwrap(), 39 | ) 40 | .await 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/methods/get_chat.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{ 6 | parameters::{ChatId, ImplicitChatId}, 7 | Chat, 8 | }, 9 | }; 10 | use serde::Serialize; 11 | 12 | /// Gets information about a chat. 13 | /// 14 | /// Reflects the [`getChat`][docs] method. 15 | /// 16 | /// [docs]: https://core.telegram.org/bots/api#getchat 17 | #[derive(Serialize, Debug, Clone)] 18 | #[must_use = "methods do nothing unless turned into a future"] 19 | pub struct GetChat<'a> { 20 | #[serde(skip)] 21 | bot: &'a InnerBot, 22 | chat_id: ChatId, 23 | } 24 | 25 | impl<'a> GetChat<'a> { 26 | pub(crate) fn new(bot: &'a InnerBot, chat_id: impl ImplicitChatId) -> Self { 27 | Self { 28 | bot, 29 | chat_id: chat_id.into(), 30 | } 31 | } 32 | } 33 | 34 | impl GetChat<'_> { 35 | /// Calls the method. 36 | pub async fn call(self) -> Result { 37 | call_method( 38 | self.bot, 39 | "getChat", 40 | None, 41 | serde_json::to_vec(&self).unwrap(), 42 | ) 43 | .await 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/methods/get_chat_administrators.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{ 6 | chat, 7 | parameters::{ChatId, ImplicitChatId}, 8 | }, 9 | }; 10 | use serde::Serialize; 11 | 12 | /// Gets information about a chat's admins. 13 | /// 14 | /// Reflects the [`getChatAdministrators`][docs] method. 15 | /// 16 | /// [docs]: https://core.telegram.org/bots/api#getchatadministrators 17 | #[derive(Serialize, Debug, Clone)] 18 | #[must_use = "methods do nothing unless turned into a future"] 19 | pub struct GetChatAdministrators<'a> { 20 | #[serde(skip)] 21 | bot: &'a InnerBot, 22 | chat_id: ChatId, 23 | } 24 | 25 | impl<'a> GetChatAdministrators<'a> { 26 | pub(crate) fn new(bot: &'a InnerBot, chat_id: impl ImplicitChatId) -> Self { 27 | Self { 28 | bot, 29 | chat_id: chat_id.into(), 30 | } 31 | } 32 | } 33 | 34 | impl GetChatAdministrators<'_> { 35 | /// Calls the method. 36 | pub async fn call(self) -> Result, errors::MethodCall> { 37 | call_method( 38 | self.bot, 39 | "getChatAdministrators", 40 | None, 41 | serde_json::to_vec(&self).unwrap(), 42 | ) 43 | .await 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/methods/get_chat_member.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{ 6 | chat, 7 | parameters::{ChatId, ImplicitChatId}, 8 | user, 9 | }, 10 | }; 11 | use serde::Serialize; 12 | 13 | /// Gets information about a chat's member. 14 | /// 15 | /// Reflects the [`getChatMember`][docs] method. 16 | /// 17 | /// [docs]: https://core.telegram.org/bots/api#getchatmember 18 | #[derive(Serialize, Debug, Clone)] 19 | #[must_use = "methods do nothing unless turned into a future"] 20 | pub struct GetChatMember<'a> { 21 | #[serde(skip)] 22 | bot: &'a InnerBot, 23 | chat_id: ChatId, 24 | user_id: user::Id, 25 | } 26 | 27 | impl<'a> GetChatMember<'a> { 28 | pub(crate) fn new( 29 | bot: &'a InnerBot, 30 | chat_id: impl ImplicitChatId, 31 | user_id: user::Id, 32 | ) -> Self { 33 | Self { 34 | bot, 35 | chat_id: chat_id.into(), 36 | user_id, 37 | } 38 | } 39 | } 40 | 41 | impl GetChatMember<'_> { 42 | /// Calls the method. 43 | pub async fn call(self) -> Result { 44 | call_method( 45 | self.bot, 46 | "getChatMember", 47 | None, 48 | serde_json::to_vec(&self).unwrap(), 49 | ) 50 | .await 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/methods/get_chat_member_count.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::parameters::{ChatId, ImplicitChatId}, 6 | }; 7 | use serde::Serialize; 8 | 9 | /// Gets a chat's member count. 10 | /// 11 | /// Reflects the [`getChatMembersCount`][docs] method. 12 | /// 13 | /// [docs]: https://core.telegram.org/bots/api#getchatmemberscount 14 | #[derive(Serialize, Debug, Clone)] 15 | #[must_use = "methods do nothing unless turned into a future"] 16 | pub struct GetChatMemberCount<'a> { 17 | #[serde(skip)] 18 | bot: &'a InnerBot, 19 | chat_id: ChatId, 20 | } 21 | 22 | impl<'a> GetChatMemberCount<'a> { 23 | pub(crate) fn new(bot: &'a InnerBot, chat_id: impl ImplicitChatId) -> Self { 24 | Self { 25 | bot, 26 | chat_id: chat_id.into(), 27 | } 28 | } 29 | } 30 | 31 | impl GetChatMemberCount<'_> { 32 | /// Calls the method. 33 | pub async fn call(self) -> Result { 34 | call_method( 35 | self.bot, 36 | "getChatMemberCount", 37 | None, 38 | serde_json::to_vec(&self).unwrap(), 39 | ) 40 | .await 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/methods/get_file.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::file::{self, File}, 6 | }; 7 | use serde::Serialize; 8 | 9 | /// Gets information about a file. 10 | /// 11 | /// Reflects the [`getfile`][docs] method. 12 | /// 13 | /// [docs]: https://core.telegram.org/bots/api#getfile 14 | #[derive(Serialize, Debug, Clone)] 15 | #[must_use = "methods do nothing unless turned into a future"] 16 | pub struct GetFile<'a> { 17 | #[serde(skip)] 18 | bot: &'a InnerBot, 19 | file_id: file::Id, 20 | } 21 | 22 | impl<'a> GetFile<'a> { 23 | pub(crate) const fn new(bot: &'a InnerBot, file_id: file::Id) -> Self { 24 | Self { bot, file_id } 25 | } 26 | } 27 | 28 | impl GetFile<'_> { 29 | /// Calls the method. 30 | pub async fn call(self) -> Result { 31 | call_method( 32 | self.bot, 33 | "getFile", 34 | None, 35 | serde_json::to_vec(&self).unwrap(), 36 | ) 37 | .await 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/methods/get_inline_game_high_scores.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{game::HighScore, user, InlineMessageId}, 6 | }; 7 | use serde::Serialize; 8 | 9 | /// Gets an excerpt from the high score table of a game sent via the inline 10 | /// mode. 11 | /// 12 | /// Reflects the [`getGameHighScores`][docs] method. 13 | /// 14 | /// [docs]: https://core.telegram.org/bots/api#getgamehighscores 15 | #[derive(Serialize, Debug, Clone)] 16 | #[must_use = "methods do nothing unless turned into a future"] 17 | pub struct GetInlineGameHighScores<'a> { 18 | #[serde(skip)] 19 | bot: &'a InnerBot, 20 | user_id: user::Id, 21 | inline_message_id: InlineMessageId, 22 | } 23 | 24 | impl<'a> GetInlineGameHighScores<'a> { 25 | pub(crate) const fn new( 26 | bot: &'a InnerBot, 27 | inline_message_id: InlineMessageId, 28 | user_id: user::Id, 29 | ) -> Self { 30 | Self { 31 | bot, 32 | user_id, 33 | inline_message_id, 34 | } 35 | } 36 | } 37 | 38 | impl GetInlineGameHighScores<'_> { 39 | /// Calls the method. 40 | pub async fn call(self) -> Result, errors::MethodCall> { 41 | call_method( 42 | self.bot, 43 | "getGameHighScores", 44 | None, 45 | serde_json::to_vec(&self).unwrap(), 46 | ) 47 | .await 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/methods/get_me.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{bot::InnerBot, errors, types}; 3 | 4 | /// Gets information about the bot. 5 | /// 6 | /// Represents the [`getMe`][docs] method. 7 | /// 8 | /// [docs]: https://core.telegram.org/bots/api#getme 9 | #[derive(Debug, Clone)] 10 | #[must_use = "methods do nothing unless turned into a future"] 11 | pub struct GetMe<'a> { 12 | bot: &'a InnerBot, 13 | } 14 | 15 | impl<'a> GetMe<'a> { 16 | pub(crate) const fn new(bot: &'a InnerBot) -> Self { 17 | Self { bot } 18 | } 19 | } 20 | 21 | impl GetMe<'_> { 22 | /// Calls the method. 23 | pub async fn call(self) -> Result { 24 | call_method(self.bot, "getMe", None, Vec::new()).await 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/methods/get_message_game_high_scores.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{ 6 | game::HighScore, 7 | message, 8 | parameters::{ChatId, ImplicitChatId}, 9 | user, 10 | }, 11 | }; 12 | use serde::Serialize; 13 | 14 | /// Gets an excerpt from the high score table of a game sent by the bot itself. 15 | /// 16 | /// Reflects the [`getGameHighScores`][docs] method. 17 | /// 18 | /// [docs]: https://core.telegram.org/bots/api#getgamehighscores 19 | #[derive(Serialize, Debug, Clone)] 20 | #[must_use = "methods do nothing unless turned into a future"] 21 | pub struct GetMessageGameHighScores<'a> { 22 | #[serde(skip)] 23 | bot: &'a InnerBot, 24 | user_id: user::Id, 25 | chat_id: ChatId, 26 | message_id: message::Id, 27 | } 28 | 29 | impl<'a> GetMessageGameHighScores<'a> { 30 | pub(crate) fn new( 31 | bot: &'a InnerBot, 32 | chat_id: impl ImplicitChatId, 33 | message_id: message::Id, 34 | user_id: user::Id, 35 | ) -> Self { 36 | Self { 37 | bot, 38 | user_id, 39 | chat_id: chat_id.into(), 40 | message_id, 41 | } 42 | } 43 | } 44 | 45 | impl GetMessageGameHighScores<'_> { 46 | /// Calls the method. 47 | pub async fn call(self) -> Result, errors::MethodCall> { 48 | call_method( 49 | self.bot, 50 | "getGameHighScores", 51 | None, 52 | serde_json::to_vec(&self).unwrap(), 53 | ) 54 | .await 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/methods/get_my_commands.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{bot::InnerBot, errors, types::BotCommand}; 3 | 4 | /// Gets the list of the bot's commands. 5 | /// 6 | /// Represents the [`getMyCommands`][docs] method. 7 | /// 8 | /// [docs]: https://core.telegram.org/bots/api#getmycommands 9 | #[derive(Debug, Clone)] 10 | #[must_use = "methods do nothing unless turned into a future"] 11 | pub struct GetMyCommands<'a> { 12 | bot: &'a InnerBot, 13 | } 14 | 15 | impl<'a> GetMyCommands<'a> { 16 | pub(crate) const fn new(bot: &'a InnerBot) -> Self { 17 | Self { bot } 18 | } 19 | } 20 | 21 | impl GetMyCommands<'_> { 22 | /// Calls the method. 23 | pub async fn call(self) -> Result, errors::MethodCall> { 24 | call_method(self.bot, "getMyCommands", None, Vec::new()).await 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/methods/get_sticker_set.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{bot::InnerBot, errors, types::sticker}; 3 | use serde::Serialize; 4 | 5 | /// Gets a sticker set by its name. 6 | /// 7 | /// Reflects the [`getStickerSet`][docs] method. 8 | /// 9 | /// [docs]: https://core.telegram.org/bots/api#getstickerset 10 | #[derive(Serialize, Debug, Clone)] 11 | #[must_use = "methods do nothing unless turned into a future"] 12 | pub struct GetStickerSet<'a> { 13 | #[serde(skip)] 14 | bot: &'a InnerBot, 15 | name: String, 16 | } 17 | 18 | impl<'a> GetStickerSet<'a> { 19 | pub(crate) fn new(bot: &'a InnerBot, name: impl Into) -> Self { 20 | Self { 21 | bot, 22 | name: name.into(), 23 | } 24 | } 25 | } 26 | 27 | impl GetStickerSet<'_> { 28 | /// Calls the method. 29 | pub async fn call(self) -> Result { 30 | call_method( 31 | self.bot, 32 | "getStickerSet", 33 | None, 34 | serde_json::to_vec(&self).unwrap(), 35 | ) 36 | .await 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/methods/get_updates.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{parameters::AllowedUpdates, update::RawUpdate}, 6 | }; 7 | use serde::Serialize; 8 | 9 | #[derive(Serialize, Debug, Clone)] 10 | #[must_use] 11 | pub struct GetUpdates<'a> { 12 | #[serde(skip)] 13 | bot: &'a InnerBot, 14 | #[serde(skip_serializing_if = "Option::is_none")] 15 | offset: Option, 16 | #[serde(skip_serializing_if = "Option::is_none")] 17 | limit: Option, 18 | #[serde(skip_serializing_if = "Option::is_none")] 19 | timeout: Option, 20 | #[serde(skip_serializing_if = "Option::is_none")] 21 | allowed_updates: Option, 22 | } 23 | 24 | impl<'a> GetUpdates<'a> { 25 | pub(crate) const fn new( 26 | bot: &'a InnerBot, 27 | offset: Option, 28 | limit: Option, 29 | timeout: Option, 30 | allowed_updates: Option, 31 | ) -> Self { 32 | Self { 33 | bot, 34 | offset, 35 | limit, 36 | timeout, 37 | allowed_updates, 38 | } 39 | } 40 | } 41 | 42 | impl GetUpdates<'_> { 43 | /// Calls the method. 44 | pub(crate) async fn call( 45 | self, 46 | ) -> Result, errors::MethodCall> { 47 | call_method( 48 | self.bot, 49 | "getUpdates", 50 | None, 51 | serde_json::to_vec(&self).unwrap(), 52 | ) 53 | .await 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/methods/get_webhook_info.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{bot::InnerBot, errors, types}; 3 | 4 | /// Gets information about the bot's webhook. 5 | /// 6 | /// Reflects the [`getWebhookInfo`][docs] method. 7 | /// 8 | /// [docs]: https://core.telegram.org/bots/api#getwebhookinfo 9 | #[derive(Debug, Clone)] 10 | #[must_use = "methods do nothing unless turned into a future"] 11 | pub struct GetWebhookInfo<'a> { 12 | bot: &'a InnerBot, 13 | } 14 | 15 | impl<'a> GetWebhookInfo<'a> { 16 | pub(crate) const fn new(bot: &'a InnerBot) -> Self { 17 | Self { bot } 18 | } 19 | } 20 | 21 | impl GetWebhookInfo<'_> { 22 | /// Calls the method. 23 | pub async fn call(self) -> Result { 24 | call_method(self.bot, "getWebhookInfo", None, Vec::new()).await 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/methods/leave_chat.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::parameters::{ChatId, ImplicitChatId}, 6 | }; 7 | use serde::Serialize; 8 | 9 | /// Leaves a chat. 10 | /// 11 | /// Reflects the [`leaveChat`][docs] method. 12 | /// 13 | /// [docs]: https://core.telegram.org/bots/api#leavechat 14 | #[derive(Serialize, Debug, Clone)] 15 | #[must_use = "methods do nothing unless turned into a future"] 16 | pub struct LeaveChat<'a> { 17 | #[serde(skip)] 18 | bot: &'a InnerBot, 19 | chat_id: ChatId, 20 | } 21 | 22 | impl<'a> LeaveChat<'a> { 23 | pub(crate) fn new(bot: &'a InnerBot, chat_id: impl ImplicitChatId) -> Self { 24 | Self { 25 | bot, 26 | chat_id: chat_id.into(), 27 | } 28 | } 29 | } 30 | 31 | impl LeaveChat<'_> { 32 | /// Calls the method. 33 | pub async fn call(self) -> Result<(), errors::MethodCall> { 34 | call_method::( 35 | self.bot, 36 | "leaveChat", 37 | None, 38 | serde_json::to_vec(&self).unwrap(), 39 | ) 40 | .await?; 41 | 42 | Ok(()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/methods/log_out.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{bot::InnerBot, errors}; 3 | 4 | /// Logs out from the cloud Bot API server. 5 | /// 6 | /// Represents the [`logOut`][docs] method. 7 | /// 8 | /// [docs]: https://core.telegram.org/bots/api#logout 9 | #[derive(Debug, Clone)] 10 | #[must_use = "methods do nothing unless turned into a future"] 11 | pub struct LogOut<'a> { 12 | bot: &'a InnerBot, 13 | } 14 | 15 | impl<'a> LogOut<'a> { 16 | pub(crate) const fn new(bot: &'a InnerBot) -> Self { 17 | Self { bot } 18 | } 19 | } 20 | 21 | impl LogOut<'_> { 22 | /// Calls the method. 23 | pub async fn call(self) -> Result<(), errors::MethodCall> { 24 | call_method::(self.bot, "logOut", None, Vec::new()).await?; 25 | Ok(()) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/methods/pin_chat_message.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{ 6 | message, 7 | parameters::{ChatId, ImplicitChatId}, 8 | }, 9 | }; 10 | use serde::Serialize; 11 | 12 | /// Pins a message in a chat. 13 | /// 14 | /// Reflects the [`pinChatMessage`][docs] method. 15 | /// 16 | /// [docs]: https://core.telegram.org/bots/api#pinchatmessage 17 | #[derive(Serialize, Debug, Clone)] 18 | #[must_use = "methods do nothing unless turned into a future"] 19 | pub struct PinChatMessage<'a> { 20 | #[serde(skip)] 21 | bot: &'a InnerBot, 22 | chat_id: ChatId, 23 | message_id: message::Id, 24 | #[serde(skip_serializing_if = "Option::is_none")] 25 | disable_notification: Option, 26 | } 27 | 28 | impl<'a> PinChatMessage<'a> { 29 | pub(crate) fn new( 30 | bot: &'a InnerBot, 31 | chat_id: impl ImplicitChatId, 32 | message_id: message::Id, 33 | ) -> Self { 34 | Self { 35 | bot, 36 | chat_id: chat_id.into(), 37 | message_id, 38 | disable_notification: None, 39 | } 40 | } 41 | 42 | /// Configures whether the message is pinned silently. 43 | /// Reflects the `disable_notification` parameter. 44 | pub const fn is_notification_disabled(mut self, is_disabled: bool) -> Self { 45 | self.disable_notification = Some(is_disabled); 46 | self 47 | } 48 | } 49 | 50 | impl PinChatMessage<'_> { 51 | /// Calls the method. 52 | pub async fn call(self) -> Result<(), errors::MethodCall> { 53 | call_method::( 54 | self.bot, 55 | "pinChatMessage", 56 | None, 57 | serde_json::to_vec(&self).unwrap(), 58 | ) 59 | .await?; 60 | 61 | Ok(()) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/methods/restrict_chat_member.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{ 6 | chat, 7 | parameters::{ChatId, ImplicitChatId}, 8 | user, 9 | }, 10 | }; 11 | use serde::Serialize; 12 | 13 | /// Restricts a chat member. 14 | /// 15 | /// Reflects the [`restrictChatMember`][docs] method. 16 | /// 17 | /// [docs]: https://core.telegram.org/bots/api#restrictchatmember 18 | #[derive(Serialize, Debug, Clone)] 19 | #[must_use = "methods do nothing unless turned into a future"] 20 | pub struct RestrictChatMember<'a> { 21 | #[serde(skip)] 22 | bot: &'a InnerBot, 23 | chat_id: ChatId, 24 | user_id: user::Id, 25 | permissions: chat::Permissions, 26 | #[serde(skip_serializing_if = "Option::is_none")] 27 | until_date: Option, 28 | } 29 | 30 | impl<'a> RestrictChatMember<'a> { 31 | pub(crate) fn new( 32 | bot: &'a InnerBot, 33 | chat_id: impl ImplicitChatId, 34 | user_id: user::Id, 35 | permissions: chat::Permissions, 36 | ) -> Self { 37 | Self { 38 | bot, 39 | chat_id: chat_id.into(), 40 | user_id, 41 | permissions, 42 | until_date: None, 43 | } 44 | } 45 | 46 | /// Configures when the restrictions will be lifted. 47 | /// Reflects the `until_date` parameter. 48 | pub const fn until_date(mut self, date: i64) -> Self { 49 | self.until_date = Some(date); 50 | self 51 | } 52 | } 53 | 54 | impl RestrictChatMember<'_> { 55 | /// Calls the method. 56 | pub async fn call(self) -> Result<(), errors::MethodCall> { 57 | call_method::( 58 | self.bot, 59 | "restrictChatMember", 60 | None, 61 | serde_json::to_vec(&self).unwrap(), 62 | ) 63 | .await?; 64 | 65 | Ok(()) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/methods/revoke_chat_invite_link.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{ 6 | chat, 7 | parameters::{ChatId, ImplicitChatId}, 8 | }, 9 | }; 10 | use serde::Serialize; 11 | 12 | /// Revokes an invite link for a chat. 13 | /// 14 | /// Reflects the [`revokeChatInviteLink`][docs] method. 15 | /// 16 | /// [docs]: https://core.telegram.org/bots/api#revokechatinvitelink 17 | #[derive(Serialize, Debug, Clone)] 18 | #[must_use = "methods do nothing unless turned into a future"] 19 | pub struct RevokeChatInviteLink<'a> { 20 | #[serde(skip)] 21 | bot: &'a InnerBot, 22 | chat_id: ChatId, 23 | invite_link: String, 24 | } 25 | 26 | impl<'a> RevokeChatInviteLink<'a> { 27 | pub(crate) fn new( 28 | bot: &'a InnerBot, 29 | chat_id: impl ImplicitChatId, 30 | link: impl Into, 31 | ) -> Self { 32 | Self { 33 | bot, 34 | chat_id: chat_id.into(), 35 | invite_link: link.into(), 36 | } 37 | } 38 | } 39 | 40 | impl RevokeChatInviteLink<'_> { 41 | /// Calls the method. 42 | pub async fn call(self) -> Result { 43 | call_method( 44 | self.bot, 45 | "revokeChatInviteLink", 46 | None, 47 | serde_json::to_vec(&self).unwrap(), 48 | ) 49 | .await 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/methods/send_chat_action.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{ 6 | chat, 7 | parameters::{ChatId, ImplicitChatId}, 8 | }, 9 | }; 10 | use serde::Serialize; 11 | 12 | /// Sends a chat action. 13 | /// 14 | /// Reflects the [`sendChatAction`][docs] method. 15 | /// 16 | /// [docs]: https://core.telegram.org/bots/api#sendchataction 17 | #[derive(Serialize, Debug, Clone)] 18 | #[must_use = "methods do nothing unless turned into a future"] 19 | pub struct SendChatAction<'a> { 20 | #[serde(skip)] 21 | bot: &'a InnerBot, 22 | chat_id: ChatId, 23 | action: chat::Action, 24 | } 25 | 26 | impl<'a> SendChatAction<'a> { 27 | pub(crate) fn new( 28 | bot: &'a InnerBot, 29 | chat_id: impl ImplicitChatId, 30 | action: chat::Action, 31 | ) -> Self { 32 | Self { 33 | bot, 34 | chat_id: chat_id.into(), 35 | action, 36 | } 37 | } 38 | } 39 | 40 | impl SendChatAction<'_> { 41 | /// Calls the method. 42 | pub async fn call(self) -> Result<(), errors::MethodCall> { 43 | call_method::( 44 | self.bot, 45 | "sendChatAction", 46 | None, 47 | serde_json::to_vec(&self).unwrap(), 48 | ) 49 | .await?; 50 | 51 | Ok(()) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/methods/set_chat_administrator_custom_title.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{ 6 | parameters::{ChatId, ImplicitChatId}, 7 | user, 8 | }, 9 | }; 10 | use serde::Serialize; 11 | 12 | /// Sets a custom title for an admin in a supergroup promoted by the bot. 13 | /// 14 | /// Reflects the [`setChatAdministratorCustomTitle`][docs] method. 15 | /// 16 | /// [docs]: https://core.telegram.org/bots/api#setchatadministratorcustomtitle 17 | #[derive(Serialize, Debug, Clone)] 18 | #[must_use = "methods do nothing unless turned into a future"] 19 | pub struct SetChatAdministratorCustomTitle<'a> { 20 | #[serde(skip)] 21 | bot: &'a InnerBot, 22 | chat_id: ChatId, 23 | user_id: user::Id, 24 | custom_title: String, 25 | } 26 | 27 | impl<'a> SetChatAdministratorCustomTitle<'a> { 28 | pub(crate) fn new( 29 | bot: &'a InnerBot, 30 | chat_id: impl ImplicitChatId, 31 | user_id: user::Id, 32 | custom_title: impl Into, 33 | ) -> Self { 34 | Self { 35 | bot, 36 | chat_id: chat_id.into(), 37 | user_id, 38 | custom_title: custom_title.into(), 39 | } 40 | } 41 | } 42 | 43 | impl SetChatAdministratorCustomTitle<'_> { 44 | /// Calls the method. 45 | pub async fn call(self) -> Result<(), errors::MethodCall> { 46 | call_method::( 47 | self.bot, 48 | "setChatAdministratorCustomTitle", 49 | None, 50 | serde_json::to_vec(&self).unwrap(), 51 | ) 52 | .await?; 53 | 54 | Ok(()) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/methods/set_chat_description.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::parameters::{ChatId, ImplicitChatId}, 6 | }; 7 | use serde::Serialize; 8 | 9 | /// Sets a chat's description. 10 | /// 11 | /// Reflects the [`setChatDescription`][docs] method. 12 | /// 13 | /// [docs]: https://core.telegram.org/bots/api#setchatdescription 14 | #[derive(Serialize, Debug, Clone)] 15 | #[must_use = "methods do nothing unless turned into a future"] 16 | pub struct SetChatDescription<'a> { 17 | #[serde(skip)] 18 | bot: &'a InnerBot, 19 | chat_id: ChatId, 20 | description: String, 21 | } 22 | 23 | impl<'a> SetChatDescription<'a> { 24 | pub(crate) fn new( 25 | bot: &'a InnerBot, 26 | chat_id: impl ImplicitChatId, 27 | description: impl Into, 28 | ) -> Self { 29 | Self { 30 | bot, 31 | chat_id: chat_id.into(), 32 | description: description.into(), 33 | } 34 | } 35 | } 36 | 37 | impl SetChatDescription<'_> { 38 | /// Calls the method. 39 | pub async fn call(self) -> Result<(), errors::MethodCall> { 40 | call_method::( 41 | self.bot, 42 | "setChatDescription", 43 | None, 44 | serde_json::to_vec(&self).unwrap(), 45 | ) 46 | .await?; 47 | 48 | Ok(()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/methods/set_chat_permissions.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{ 6 | chat, 7 | parameters::{ChatId, ImplicitChatId}, 8 | }, 9 | }; 10 | use serde::Serialize; 11 | 12 | /// Sets a group's global permissions. 13 | /// 14 | /// Reflects the [`setChatPermissions`][docs] method. 15 | /// 16 | /// [docs]: https://core.telegram.org/bots/api#setchatpermissions 17 | #[derive(Serialize, Debug, Clone)] 18 | #[must_use = "methods do nothing unless turned into a future"] 19 | pub struct SetChatPermissions<'a> { 20 | #[serde(skip)] 21 | bot: &'a InnerBot, 22 | chat_id: ChatId, 23 | permissions: chat::Permissions, 24 | } 25 | 26 | impl<'a> SetChatPermissions<'a> { 27 | pub(crate) fn new( 28 | bot: &'a InnerBot, 29 | chat_id: impl ImplicitChatId, 30 | permissions: chat::Permissions, 31 | ) -> Self { 32 | Self { 33 | bot, 34 | chat_id: chat_id.into(), 35 | permissions, 36 | } 37 | } 38 | } 39 | 40 | impl SetChatPermissions<'_> { 41 | /// Calls the method. 42 | pub async fn call(self) -> Result<(), errors::MethodCall> { 43 | call_method::( 44 | self.bot, 45 | "setChatPermissions", 46 | None, 47 | serde_json::to_vec(&self).unwrap(), 48 | ) 49 | .await?; 50 | 51 | Ok(()) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/methods/set_chat_photo.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{ 6 | input_file::{ChatPhoto, InputFile}, 7 | parameters::{ChatId, ImplicitChatId}, 8 | }, 9 | Multipart, 10 | }; 11 | 12 | /// Sets a chat's photo. 13 | /// 14 | /// Reflects the [`setChatPhoto`][docs] method. 15 | /// 16 | /// [docs]: https://core.telegram.org/bots/api#setchatphoto 17 | #[derive(Debug, Clone)] 18 | #[must_use = "methods do nothing unless turned into a future"] 19 | pub struct SetChatPhoto<'a> { 20 | bot: &'a InnerBot, 21 | chat_id: ChatId, 22 | photo: ChatPhoto, 23 | } 24 | 25 | impl<'a> SetChatPhoto<'a> { 26 | pub(crate) fn new( 27 | bot: &'a InnerBot, 28 | chat_id: impl ImplicitChatId, 29 | photo: ChatPhoto, 30 | ) -> Self { 31 | Self { 32 | bot, 33 | chat_id: chat_id.into(), 34 | photo, 35 | } 36 | } 37 | } 38 | 39 | impl SetChatPhoto<'_> { 40 | /// Calls the method. 41 | pub async fn call(self) -> Result<(), errors::MethodCall> { 42 | let chat_id = match self.chat_id { 43 | ChatId::Id(id) => id.to_string(), 44 | ChatId::Username(username) => username, 45 | }; 46 | 47 | let mut multipart = Multipart::new(2).str("chat_id", &chat_id); 48 | 49 | if let InputFile::File { 50 | filename, bytes, .. 51 | } = &self.photo.0 52 | { 53 | multipart = multipart.file("photo", filename, bytes); 54 | } 55 | 56 | let (boundary, body) = multipart.finish(); 57 | 58 | call_method::(self.bot, "setChatPhoto", Some(boundary), body) 59 | .await?; 60 | 61 | Ok(()) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/methods/set_chat_sticker_set.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::parameters::{ChatId, ImplicitChatId}, 6 | }; 7 | use serde::Serialize; 8 | 9 | /// Sets a group's sticker set. 10 | /// 11 | /// Reflects the [`setChatStickerSet`][docs] method. 12 | /// 13 | /// [docs]: https://core.telegram.org/bots/api#setchatstickerset 14 | #[derive(Serialize, Debug, Clone)] 15 | #[must_use = "methods do nothing unless turned into a future"] 16 | pub struct SetChatStickerSet<'a> { 17 | #[serde(skip)] 18 | bot: &'a InnerBot, 19 | chat_id: ChatId, 20 | sticker_set_name: String, 21 | } 22 | 23 | impl<'a> SetChatStickerSet<'a> { 24 | pub(crate) fn new( 25 | bot: &'a InnerBot, 26 | chat_id: impl ImplicitChatId, 27 | sticker_set_name: impl Into, 28 | ) -> Self { 29 | Self { 30 | bot, 31 | chat_id: chat_id.into(), 32 | sticker_set_name: sticker_set_name.into(), 33 | } 34 | } 35 | } 36 | 37 | impl SetChatStickerSet<'_> { 38 | /// Calls the method. 39 | pub async fn call(self) -> Result<(), errors::MethodCall> { 40 | call_method::( 41 | self.bot, 42 | "setChatStickerSet", 43 | None, 44 | serde_json::to_vec(&self).unwrap(), 45 | ) 46 | .await?; 47 | 48 | Ok(()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/methods/set_chat_title.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::parameters::{ChatId, ImplicitChatId}, 6 | }; 7 | use serde::Serialize; 8 | 9 | /// Sets a group's title. 10 | /// 11 | /// Reflects the [`setChatTitle`][docs] method. 12 | /// 13 | /// [docs]: https://core.telegram.org/bots/api#setchattitle 14 | #[derive(Serialize, Debug, Clone)] 15 | #[must_use = "methods do nothing unless turned into a future"] 16 | pub struct SetChatTitle<'a> { 17 | #[serde(skip)] 18 | bot: &'a InnerBot, 19 | chat_id: ChatId, 20 | title: String, 21 | } 22 | 23 | impl<'a> SetChatTitle<'a> { 24 | pub(crate) fn new( 25 | bot: &'a InnerBot, 26 | chat_id: impl ImplicitChatId, 27 | title: impl Into, 28 | ) -> Self { 29 | Self { 30 | bot, 31 | chat_id: chat_id.into(), 32 | title: title.into(), 33 | } 34 | } 35 | } 36 | 37 | impl SetChatTitle<'_> { 38 | /// Calls the method. 39 | pub async fn call(self) -> Result<(), errors::MethodCall> { 40 | call_method::( 41 | self.bot, 42 | "setChatTitle", 43 | None, 44 | serde_json::to_vec(&self).unwrap(), 45 | ) 46 | .await?; 47 | 48 | Ok(()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/methods/set_my_commands.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{bot::InnerBot, errors, types::BotCommand}; 3 | use serde::Serialize; 4 | 5 | /// Sets the list of the bot's commands. 6 | /// 7 | /// Represents the [`setMyCommands`][docs] method. 8 | /// 9 | /// [docs]: https://core.telegram.org/bots/api#setmycommands 10 | #[derive(Serialize, Debug, Clone)] 11 | #[must_use = "methods do nothing unless turned into a future"] 12 | pub struct SetMyCommands<'a> { 13 | #[serde(skip)] 14 | bot: &'a InnerBot, 15 | commands: Vec, 16 | } 17 | 18 | impl<'a> SetMyCommands<'a> { 19 | pub(crate) fn new( 20 | bot: &'a InnerBot, 21 | commands: impl Into>, 22 | ) -> Self { 23 | Self { 24 | bot, 25 | commands: commands.into(), 26 | } 27 | } 28 | } 29 | 30 | impl SetMyCommands<'_> { 31 | /// Calls the method. 32 | pub async fn call(self) -> Result<(), errors::MethodCall> { 33 | call_method::( 34 | self.bot, 35 | "setMyCommands", 36 | None, 37 | serde_json::to_vec(&self).unwrap(), 38 | ) 39 | .await?; 40 | 41 | Ok(()) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/methods/set_passport_data_errors.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{passport, user}, 6 | }; 7 | use serde::Serialize; 8 | 9 | /// Reports passport errors to the user. 10 | /// 11 | /// Reflects the [`setPassportDataErrors`][docs] method. 12 | /// 13 | /// [docs]: https://core.telegram.org/bots/api#setpassportdataerrors 14 | #[derive(Serialize, Debug, Clone)] 15 | #[must_use = "methods do nothing unless turned into a future"] 16 | pub struct SetPassportDataErrors<'a> { 17 | #[serde(skip)] 18 | bot: &'a InnerBot, 19 | user_id: user::Id, 20 | errors: Vec, 21 | } 22 | 23 | impl<'a> SetPassportDataErrors<'a> { 24 | pub(crate) fn new( 25 | bot: &'a InnerBot, 26 | user_id: user::Id, 27 | errors: impl Into>, 28 | ) -> Self { 29 | Self { 30 | bot, 31 | user_id, 32 | errors: errors.into(), 33 | } 34 | } 35 | } 36 | 37 | impl SetPassportDataErrors<'_> { 38 | /// Calls the method. 39 | pub async fn call(self) -> Result<(), errors::MethodCall> { 40 | call_method::( 41 | self.bot, 42 | "setPassportDataErrors", 43 | None, 44 | serde_json::to_vec(&self).unwrap(), 45 | ) 46 | .await?; 47 | 48 | Ok(()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/methods/set_sticker_position_in_set.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{bot::InnerBot, errors}; 3 | use serde::Serialize; 4 | 5 | /// Changes a sticker's position in a sticker set. 6 | /// 7 | /// Reflects the [`setStickerPositionInSet`][docs] method. 8 | /// 9 | /// [docs]: https://core.telegram.org/bots/api#setstickerpositioninset 10 | #[derive(Serialize, Debug, Clone)] 11 | #[must_use = "methods do nothing unless turned into a future"] 12 | pub struct SetStickerPositionInSet<'a> { 13 | #[serde(skip)] 14 | bot: &'a InnerBot, 15 | sticker: String, 16 | position: u32, 17 | } 18 | 19 | impl<'a> SetStickerPositionInSet<'a> { 20 | pub(crate) fn new( 21 | bot: &'a InnerBot, 22 | sticker: impl Into, 23 | position: u32, 24 | ) -> Self { 25 | Self { 26 | bot, 27 | sticker: sticker.into(), 28 | position, 29 | } 30 | } 31 | } 32 | 33 | impl SetStickerPositionInSet<'_> { 34 | /// Calls the method. 35 | pub async fn call(self) -> Result<(), errors::MethodCall> { 36 | call_method::( 37 | self.bot, 38 | "setStickerPositionInSet", 39 | None, 40 | serde_json::to_vec(&self).unwrap(), 41 | ) 42 | .await?; 43 | 44 | Ok(()) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/methods/stop_inline_location.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{keyboard::inline, InlineMessageId}, 6 | }; 7 | use serde::Serialize; 8 | 9 | /// Stops a live location sent via the inline mode. 10 | /// 11 | /// Reflects the [`stopMessageLiveLocation`][docs] method. 12 | /// 13 | /// [docs]: https://core.telegram.org/bots/api#stopmessagelivelocation 14 | #[derive(Serialize, Debug, Clone)] 15 | #[must_use = "methods do nothing unless turned into a future"] 16 | pub struct StopInlineLocation<'a> { 17 | #[serde(skip)] 18 | bot: &'a InnerBot, 19 | inline_message_id: InlineMessageId, 20 | #[serde(skip_serializing_if = "Option::is_none")] 21 | reply_markup: Option, 22 | } 23 | 24 | impl<'a> StopInlineLocation<'a> { 25 | pub(crate) const fn new( 26 | bot: &'a InnerBot, 27 | inline_message_id: InlineMessageId, 28 | ) -> Self { 29 | Self { 30 | bot, 31 | inline_message_id, 32 | reply_markup: None, 33 | } 34 | } 35 | 36 | /// Configures an inline keyboard for the message. 37 | /// Reflects the `reply_markup` parameter. 38 | #[allow(clippy::missing_const_for_fn)] 39 | pub fn reply_markup(mut self, markup: inline::Keyboard) -> Self { 40 | self.reply_markup = Some(markup); 41 | self 42 | } 43 | } 44 | 45 | impl StopInlineLocation<'_> { 46 | /// Calls the method. 47 | pub async fn call(self) -> Result<(), errors::MethodCall> { 48 | call_method::( 49 | self.bot, 50 | "stopMessageLiveLocation", 51 | None, 52 | serde_json::to_vec(&self).unwrap(), 53 | ) 54 | .await?; 55 | 56 | Ok(()) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/methods/stop_poll.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{ 6 | keyboard::inline, 7 | message, 8 | parameters::{ChatId, ImplicitChatId}, 9 | Poll, 10 | }, 11 | }; 12 | use serde::Serialize; 13 | 14 | /// Stops a poll. 15 | /// 16 | /// Reflects the [`stopPoll`][docs] method. 17 | /// 18 | /// [docs]: https://core.telegram.org/bots/api#stoppoll 19 | #[derive(Serialize, Debug, Clone)] 20 | #[must_use = "methods do nothing unless turned into a future"] 21 | pub struct StopPoll<'a> { 22 | #[serde(skip)] 23 | bot: &'a InnerBot, 24 | chat_id: ChatId, 25 | message_id: message::Id, 26 | #[serde(skip_serializing_if = "Option::is_none")] 27 | reply_markup: Option, 28 | } 29 | 30 | impl<'a> StopPoll<'a> { 31 | pub(crate) fn new( 32 | bot: &'a InnerBot, 33 | chat_id: impl ImplicitChatId, 34 | message_id: message::Id, 35 | ) -> Self { 36 | Self { 37 | bot, 38 | chat_id: chat_id.into(), 39 | message_id, 40 | reply_markup: None, 41 | } 42 | } 43 | 44 | /// Configures an inline keyboard for the message. 45 | /// Reflects the `reply_markup` parameter. 46 | #[allow(clippy::missing_const_for_fn)] 47 | pub fn reply_markup(mut self, markup: inline::Keyboard) -> Self { 48 | self.reply_markup = Some(markup); 49 | self 50 | } 51 | } 52 | 53 | impl StopPoll<'_> { 54 | /// Calls the method. 55 | pub async fn call(self) -> Result { 56 | call_method( 57 | self.bot, 58 | "stopPoll", 59 | None, 60 | serde_json::to_vec(&self).unwrap(), 61 | ) 62 | .await 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/methods/unban_chat_member.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{ 6 | parameters::{ChatId, ImplicitChatId}, 7 | user, 8 | }, 9 | }; 10 | use serde::Serialize; 11 | 12 | /// Lifts all restrictions from a group's member. 13 | /// 14 | /// Reflects the [`unbanChatMember`][docs] method. 15 | /// 16 | /// [docs]: https://core.telegram.org/bots/api#unbanchatmember 17 | #[derive(Serialize, Debug, Clone)] 18 | #[must_use = "methods do nothing unless turned into a future"] 19 | pub struct UnbanChatMember<'a> { 20 | #[serde(skip)] 21 | bot: &'a InnerBot, 22 | chat_id: ChatId, 23 | user_id: user::Id, 24 | #[serde(skip_serializing_if = "Option::is_none")] 25 | only_if_banned: Option, 26 | } 27 | 28 | impl<'a> UnbanChatMember<'a> { 29 | pub(crate) fn new( 30 | bot: &'a InnerBot, 31 | chat_id: impl ImplicitChatId, 32 | user_id: user::Id, 33 | ) -> Self { 34 | Self { 35 | bot, 36 | chat_id: chat_id.into(), 37 | user_id, 38 | only_if_banned: None, 39 | } 40 | } 41 | 42 | /// If `true`, unban only if the user is banned. 43 | /// Reflects the `only_if_banned` parameter. 44 | pub const fn only_if_banned(mut self, only_if_banned: bool) -> Self { 45 | self.only_if_banned = Some(only_if_banned); 46 | self 47 | } 48 | } 49 | 50 | impl UnbanChatMember<'_> { 51 | /// Calls the method. 52 | pub async fn call(self) -> Result<(), errors::MethodCall> { 53 | call_method::( 54 | &*self.bot, 55 | "unbanChatMember", 56 | None, 57 | serde_json::to_vec(&self).unwrap(), 58 | ) 59 | .await?; 60 | 61 | Ok(()) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/methods/unpin_all_chat_messages.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::parameters::{ChatId, ImplicitChatId}, 6 | }; 7 | use serde::Serialize; 8 | 9 | /// Unpins all messages in a chat. 10 | /// 11 | /// Reflects the [`unpinAllChatMessages`][docs] method. 12 | /// 13 | /// [docs]: https://core.telegram.org/bots/api#unpinallchatmessages 14 | #[derive(Serialize, Debug, Clone)] 15 | #[must_use = "methods do nothing unless turned into a future"] 16 | pub struct UnpinAllChatMessages<'a> { 17 | #[serde(skip)] 18 | bot: &'a InnerBot, 19 | chat_id: ChatId, 20 | } 21 | 22 | impl<'a> UnpinAllChatMessages<'a> { 23 | pub(crate) fn new(bot: &'a InnerBot, chat_id: impl ImplicitChatId) -> Self { 24 | Self { 25 | bot, 26 | chat_id: chat_id.into(), 27 | } 28 | } 29 | } 30 | 31 | impl UnpinAllChatMessages<'_> { 32 | /// Calls the method. 33 | pub async fn call(self) -> Result<(), errors::MethodCall> { 34 | call_method::( 35 | self.bot, 36 | "unpinAllChatMessages", 37 | None, 38 | serde_json::to_vec(&self).unwrap(), 39 | ) 40 | .await?; 41 | 42 | Ok(()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/methods/unpin_chat_message.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::types::message::Id; 3 | use crate::{ 4 | bot::InnerBot, 5 | errors, 6 | types::parameters::{ChatId, ImplicitChatId}, 7 | }; 8 | use serde::Serialize; 9 | 10 | /// Unpins a chat message. 11 | /// 12 | /// Reflects the [`unpinChatMessage`][docs] method. 13 | /// 14 | /// [docs]: https://core.telegram.org/bots/api#unpinchatmessage 15 | #[derive(Serialize, Debug, Clone)] 16 | #[must_use = "methods do nothing unless turned into a future"] 17 | pub struct UnpinChatMessage<'a> { 18 | #[serde(skip)] 19 | bot: &'a InnerBot, 20 | chat_id: ChatId, 21 | message_id: Option, 22 | } 23 | 24 | impl<'a> UnpinChatMessage<'a> { 25 | pub(crate) fn new(bot: &'a InnerBot, chat_id: impl ImplicitChatId) -> Self { 26 | Self { 27 | bot, 28 | chat_id: chat_id.into(), 29 | message_id: None, 30 | } 31 | } 32 | 33 | /// Configures which message to unpin. 34 | /// Reflects `message_id` parameter. 35 | pub const fn message_id(mut self, message_id: Id) -> Self { 36 | self.message_id = Some(message_id); 37 | self 38 | } 39 | } 40 | 41 | impl UnpinChatMessage<'_> { 42 | /// Calls the method. 43 | pub async fn call(self) -> Result<(), errors::MethodCall> { 44 | call_method::( 45 | self.bot, 46 | "unpinChatMessage", 47 | None, 48 | serde_json::to_vec(&self).unwrap(), 49 | ) 50 | .await?; 51 | 52 | Ok(()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/methods/upload_sticker_file.rs: -------------------------------------------------------------------------------- 1 | use super::call_method; 2 | use crate::{ 3 | bot::InnerBot, 4 | errors, 5 | types::{user, File}, 6 | Multipart, 7 | }; 8 | 9 | /// Uploads a sticker file. 10 | /// 11 | /// Reflects the [`uploadStickerFile`][docs] method. 12 | /// 13 | /// [docs]: https://core.telegram.org/bots/api#uploadstickerfile 14 | #[derive(Debug, Clone)] 15 | #[must_use = "methods do nothing unless turned into a future"] 16 | pub struct UploadStickerFile<'a> { 17 | bot: &'a InnerBot, 18 | user_id: user::Id, 19 | png_sticker: Vec, 20 | } 21 | 22 | impl<'a> UploadStickerFile<'a> { 23 | pub(crate) fn new( 24 | bot: &'a InnerBot, 25 | user_id: user::Id, 26 | png_sticker: impl Into>, 27 | ) -> Self { 28 | Self { 29 | bot, 30 | user_id, 31 | png_sticker: png_sticker.into(), 32 | } 33 | } 34 | } 35 | 36 | impl UploadStickerFile<'_> { 37 | /// Calls the method. 38 | pub async fn call(self) -> Result { 39 | let (boundary, body) = Multipart::new(2) 40 | .string("user_id", &self.user_id) 41 | .file("png_sticker", "sticker.png", &self.png_sticker) 42 | .finish(); 43 | 44 | call_method(self.bot, "uploadStickerFile", Some(boundary), body).await 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/predicates.rs: -------------------------------------------------------------------------------- 1 | //! Useful predicates and utilities for them. 2 | 3 | // `tbot`'s types are `Send`, and users can't implement `tbot`'s traits anyway, 4 | // so adding `+ Send + Sync` will only make docs too explicit 5 | #![allow(clippy::future_not_send)] 6 | 7 | pub mod chat; 8 | pub mod media; 9 | pub mod message; 10 | mod traits; 11 | 12 | use futures::{future::BoxFuture, Future}; 13 | use std::sync::Arc; 14 | pub use traits::{ 15 | PredicateBooleanOperations, StatefulPredicateBooleanOperations, 16 | }; 17 | 18 | /// Allows running stateless predicates in the stateful event loop. 19 | pub fn without_state<'a, C, P, S, F>( 20 | predicate: P, 21 | ) -> impl Fn(Arc, Arc) -> BoxFuture<'a, bool> + Send + Sync + 'a 22 | where 23 | P: PredicateBooleanOperations, 24 | F: Future + Send, 25 | C: Send + Sync + 'static, 26 | S: Send + Sync + 'static, 27 | { 28 | let predicate = Arc::new(predicate); 29 | 30 | move |ctx, _state| { 31 | let predicate = Arc::clone(&predicate); 32 | Box::pin(async move { predicate(ctx).await }) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/predicates/chat.rs: -------------------------------------------------------------------------------- 1 | //! A few useful predicates for chats. 2 | 3 | use crate::contexts::fields::Message; 4 | use std::sync::Arc; 5 | 6 | /// Checks if the message is from a private chat. 7 | pub async fn is_private(context: Arc) -> bool { 8 | context.chat().kind.is_private() 9 | } 10 | 11 | /// Checks if the message is from a group. 12 | pub async fn is_group(context: Arc) -> bool { 13 | context.chat().kind.is_group() 14 | } 15 | 16 | /// Checks if the message is from a supergroup. 17 | pub async fn is_supergroup(context: Arc) -> bool { 18 | context.chat().kind.is_supergroup() 19 | } 20 | 21 | /// Checks if the message is from a channel. 22 | pub async fn is_channel(context: Arc) -> bool { 23 | context.chat().kind.is_channel() 24 | } 25 | -------------------------------------------------------------------------------- /src/predicates/media.rs: -------------------------------------------------------------------------------- 1 | //! A few useful predicates for media messages. 2 | 3 | use crate::contexts::fields::Document; 4 | use futures::future::BoxFuture; 5 | use std::{ops::Deref, path::Path, sync::Arc}; 6 | 7 | /// Checks if the document's extension matches one of the given extensions. 8 | pub fn match_extension<'a, I, T, C>( 9 | extensions: I, 10 | ) -> impl Fn(Arc) -> BoxFuture<'a, bool> + Send + Sync + 'a 11 | where 12 | for<'b> &'b I: IntoIterator, 13 | T: Deref, 14 | I: Send + Sync + 'a, 15 | C: Document + Send + Sync + 'a, 16 | { 17 | let extensions = Arc::new(extensions); 18 | 19 | move |context: Arc| { 20 | let extensions = Arc::clone(&extensions); 21 | 22 | Box::pin(async move { 23 | let file_name = if let Some(file_name) = 24 | context.document().file_name.as_ref() 25 | { 26 | file_name 27 | } else { 28 | return false; 29 | }; 30 | 31 | let extension = 32 | if let Some(extension) = Path::new(&file_name).extension() { 33 | extension 34 | } else { 35 | return false; 36 | }; 37 | 38 | let extension = extension.to_string_lossy(); 39 | 40 | extensions.into_iter().any(|x| **x == *extension) 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/predicates/message.rs: -------------------------------------------------------------------------------- 1 | //! A few useful predicates for messages. 2 | 3 | use crate::contexts::fields::{Forward, MediaMessage}; 4 | use std::sync::Arc; 5 | 6 | /// Checks if the message replies to another message. 7 | pub async fn is_in_reply(context: Arc) -> bool { 8 | context.reply_to().is_some() 9 | } 10 | 11 | /// Checks if the message is forwarded. 12 | pub async fn is_forwarded(context: Arc) -> bool { 13 | context.forward().is_some() 14 | } 15 | -------------------------------------------------------------------------------- /src/proxy.rs: -------------------------------------------------------------------------------- 1 | //! A module for working with proxy. 2 | 3 | use crate::connectors::Client; 4 | use hyper::Uri; 5 | pub use hyper_proxy as https; 6 | pub use hyper_socks2 as socks; 7 | use socks::Auth; 8 | 9 | /// An enum of possible proxies. 10 | #[derive(Debug, Clone)] 11 | pub enum Proxy { 12 | /// A HTTPS proxy. 13 | Https(https::Proxy), 14 | /// A SOCKS proxy. 15 | Socks { 16 | /// The proxy's address. 17 | uri: Uri, 18 | /// The proxy's authentication data. 19 | auth: Option, 20 | }, 21 | } 22 | 23 | impl Proxy { 24 | /// Configures an HTTPS proxy. 25 | pub const fn https(proxy: https::Proxy) -> Self { 26 | Self::Https(proxy) 27 | } 28 | 29 | /// Configures a SOCKS proxy. 30 | pub const fn socks(uri: Uri, auth: Option) -> Self { 31 | Self::Socks { uri, auth } 32 | } 33 | } 34 | 35 | impl From for Proxy { 36 | fn from(proxy: https::Proxy) -> Self { 37 | Self::https(proxy) 38 | } 39 | } 40 | 41 | impl From for Client { 42 | fn from(proxy: Proxy) -> Self { 43 | match proxy { 44 | Proxy::Https(https) => Self::https_proxy(https), 45 | Proxy::Socks { uri, auth } => Self::socks_proxy(uri, auth), 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/token.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Debug, Formatter}; 2 | 3 | #[derive(PartialEq, Eq, Clone, Hash)] 4 | pub struct Token(pub(crate) String); 5 | 6 | impl Debug for Token { 7 | fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { 8 | formatter.write_str("Token(..)") 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/types/animation.rs: -------------------------------------------------------------------------------- 1 | use crate::types::{file, PhotoSize}; 2 | use serde::Deserialize; 3 | 4 | /// Represents an [`Animation`]. 5 | /// 6 | /// [`Animation`]: https://core.telegram.org/bots/api#animation 7 | #[derive(Debug, PartialEq, Eq, Clone, Hash, Deserialize)] 8 | #[non_exhaustive] 9 | pub struct Animation { 10 | /// The file ID of the animation. 11 | pub file_id: file::Id, 12 | /// The unique ID of the animation. 13 | pub file_unique_id: String, 14 | /// The width of the animation. 15 | pub width: u32, 16 | /// The height of the animation. 17 | pub height: u32, 18 | /// The duration of the animation. 19 | pub duration: u32, 20 | /// The thumb of the animation. 21 | pub thumb: Option, 22 | /// The MIME type of the animation. 23 | pub mime_type: Option, 24 | /// The file size of the animation. 25 | pub file_size: Option, 26 | } 27 | -------------------------------------------------------------------------------- /src/types/audio.rs: -------------------------------------------------------------------------------- 1 | use crate::types::{file, PhotoSize}; 2 | use serde::Deserialize; 3 | 4 | /// Represents an [`Audio`]. 5 | /// 6 | /// [`Audio`]: https://core.telegram.org/bots/api#audio 7 | #[derive(Debug, PartialEq, Eq, Clone, Hash, Deserialize)] 8 | #[non_exhaustive] 9 | pub struct Audio { 10 | /// The file ID of the audio. 11 | pub file_id: file::Id, 12 | /// The unique ID of the audio. 13 | pub file_unique_id: String, 14 | /// The duration of the audio. 15 | pub duration: u32, 16 | /// The performer of the audio. 17 | pub performer: Option, 18 | /// The title of the audio. 19 | pub title: Option, 20 | /// The original file name as defined by sender. 21 | pub file_name: Option, 22 | /// The MIME type of the audio. 23 | pub mime_type: Option, 24 | /// The file size of the audio. 25 | pub file_size: Option, 26 | /// The thumb of the audio. 27 | pub thumb: Option, 28 | } 29 | -------------------------------------------------------------------------------- /src/types/bot_command.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// Represents a [`BotCommand`][docs]. 4 | /// 5 | /// [docs]: https://core.telegram.org/bots/api#botcommand 6 | #[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)] 7 | #[non_exhaustive] 8 | #[must_use] 9 | pub struct BotCommand { 10 | /// The command's text. 11 | pub command: String, 12 | /// The command's decription. 13 | pub description: String, 14 | } 15 | 16 | impl BotCommand { 17 | /// Constructs a new `BotCommand`. 18 | pub fn new( 19 | command: impl Into, 20 | description: impl Into, 21 | ) -> Self { 22 | Self { 23 | command: command.into(), 24 | description: description.into(), 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/types/callback.rs: -------------------------------------------------------------------------------- 1 | //! Types related to callbacks. 2 | 3 | mod game; 4 | pub mod query; 5 | 6 | pub use { 7 | game::Game, 8 | query::{Kind, Origin, Query}, 9 | }; 10 | -------------------------------------------------------------------------------- /src/types/callback/game.rs: -------------------------------------------------------------------------------- 1 | use serde::{ 2 | de::{Deserialize, Deserializer, MapAccess, Visitor}, 3 | ser::{Serialize, SerializeMap, Serializer}, 4 | }; 5 | use std::fmt::{self, Formatter}; 6 | 7 | /// A placeholder currently holding no information. See [Bots API docs]. 8 | /// 9 | /// [Bots API docs]: https://core.telegram.org/bots/api#callbackgame 10 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Default)] 11 | pub struct Game; 12 | 13 | impl Serialize for Game { 14 | fn serialize(&self, serializer: S) -> Result 15 | where 16 | S: Serializer, 17 | { 18 | serializer.serialize_map(Some(0))?.end() 19 | } 20 | } 21 | 22 | struct GameVisitor; 23 | 24 | impl<'v> Visitor<'v> for GameVisitor { 25 | type Value = Game; 26 | 27 | fn expecting(&self, fmt: &mut Formatter) -> fmt::Result { 28 | write!(fmt, "struct Game") 29 | } 30 | 31 | fn visit_map(self, _map: V) -> Result 32 | where 33 | V: MapAccess<'v>, 34 | { 35 | Ok(Game) 36 | } 37 | } 38 | 39 | impl<'de> Deserialize<'de> for Game { 40 | fn deserialize(deserializer: D) -> Result 41 | where 42 | D: Deserializer<'de>, 43 | { 44 | deserializer.deserialize_struct("Game", &[], GameVisitor) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/types/callback/query/id.rs: -------------------------------------------------------------------------------- 1 | //! Types representing a callback query ID. 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | /// Represents a callback query ID. 6 | #[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)] 7 | #[serde(transparent)] 8 | pub struct Id(pub String); 9 | 10 | impl From for Id { 11 | #[must_use] 12 | fn from(id: String) -> Self { 13 | Self(id) 14 | } 15 | } 16 | 17 | impl<'a> From<&'a String> for Id { 18 | #[must_use] 19 | fn from(id: &'a String) -> Self { 20 | Self(id.clone()) 21 | } 22 | } 23 | 24 | impl<'a> From<&'a str> for Id { 25 | #[must_use] 26 | fn from(id: &'a str) -> Self { 27 | Self(id.to_owned()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/types/chat/action.rs: -------------------------------------------------------------------------------- 1 | use is_macro::Is; 2 | use serde::Serialize; 3 | 4 | /// Represents possible chat actions. 5 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Serialize, Is)] 6 | #[serde(rename_all = "snake_case")] 7 | #[non_exhaustive] 8 | #[must_use] 9 | pub enum Action { 10 | /// About to send a text message. 11 | Typing, 12 | /// About to send a photo. 13 | UploadPhoto, 14 | /// About to send a generated video. 15 | RecordVideo, 16 | /// About to send a video. 17 | UploadVideo, 18 | /// About to send a generated voice message. 19 | RecordVoice, 20 | /// About to send a voice message. 21 | UploadVoice, 22 | /// About to send a document. 23 | UploadDocument, 24 | /// About to send a location. 25 | FindLocation, 26 | /// About to send a generated video note. 27 | RecordVideoNote, 28 | /// About to send a video note. 29 | UploadVideoNote, 30 | } 31 | -------------------------------------------------------------------------------- /src/types/chat/id.rs: -------------------------------------------------------------------------------- 1 | use crate::types::user; 2 | use serde::{Deserialize, Serialize}; 3 | use std::fmt::{self, Display, Formatter}; 4 | 5 | /// Represents a chat ID. 6 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Deserialize, Serialize)] 7 | #[serde(transparent)] 8 | pub struct Id(pub i64); 9 | 10 | impl From for Id { 11 | #[must_use] 12 | fn from(id: i64) -> Self { 13 | Self(id) 14 | } 15 | } 16 | 17 | impl Display for Id { 18 | fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { 19 | self.0.fmt(formatter) 20 | } 21 | } 22 | 23 | impl From for Id { 24 | #[must_use] 25 | fn from(id: user::Id) -> Self { 26 | Self(id.0) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/types/chat/invite_link.rs: -------------------------------------------------------------------------------- 1 | use crate::types::User; 2 | use serde::Deserialize; 3 | 4 | /// Represents an invite link of a chat. 5 | /// 6 | /// See [`ChatInviteLink`] from Bot API docs. 7 | /// 8 | /// [`ChatInviteLink`]: https://core.telegram.org/bots/api#chatinvitelink 9 | #[derive(Debug, PartialEq, Eq, Clone, Hash, Deserialize)] 10 | pub struct InviteLink { 11 | /// The invite link itself. 12 | #[serde(rename = "invite_link")] 13 | pub link: String, 14 | /// The user who created this invite link. 15 | pub creator: User, 16 | /// `true` if this link is the primary one. 17 | pub is_primary: bool, 18 | /// `true` if this link has been revoked. 19 | pub is_revoked: bool, 20 | /// Timestamp when this link expires. 21 | pub expire_date: Option, 22 | /// Maximum amount of users that can be chat members at the same time 23 | /// when joining via this invite link. In range `1..100_000`. 24 | pub member_limit: Option, 25 | } 26 | -------------------------------------------------------------------------------- /src/types/chat/location.rs: -------------------------------------------------------------------------------- 1 | use crate::types::location; 2 | use serde::Deserialize; 3 | 4 | /// Represents a location to which a chat is connected. 5 | #[derive(Debug, PartialEq, Clone, Deserialize)] 6 | #[non_exhaustive] 7 | pub struct Location { 8 | /// The location to which the supergroup is connected. 9 | /// Can't be a live location. 10 | pub location: location::Location, 11 | /// Location address; 1-64 characters, as defined by the chat owner. 12 | pub address: String, 13 | } 14 | -------------------------------------------------------------------------------- /src/types/chat/photo.rs: -------------------------------------------------------------------------------- 1 | use crate::types::file; 2 | use serde::Deserialize; 3 | 4 | /// Represents a [`ChatPhoto`]. 5 | /// 6 | /// [`ChatPhoto`]: https://core.telegram.org/bots/api#chatphoto 7 | #[derive(Debug, PartialEq, Eq, Clone, Hash, Deserialize)] 8 | #[non_exhaustive] 9 | pub struct Photo { 10 | /// The file ID of the small photo. 11 | #[serde(rename = "small_file_id")] 12 | pub small: file::Id, 13 | /// The unique file ID of the small photo. 14 | #[serde(rename = "small_file_unique_id")] 15 | pub small_unique: String, 16 | /// The file ID of the big photo. 17 | #[serde(rename = "big_file_id")] 18 | pub big: file::Id, 19 | /// The unique file ID of the big photo. 20 | #[serde(rename = "big_file_unique_id")] 21 | pub big_unique: String, 22 | } 23 | -------------------------------------------------------------------------------- /src/types/chosen_inline_result.rs: -------------------------------------------------------------------------------- 1 | use super::{InlineMessageId, Location, User}; 2 | use serde::Deserialize; 3 | 4 | /// Represents a [`ChosenInlineResult`][docs]. 5 | /// 6 | /// [docs]: https://core.telegram.org/bots/api#choseninlineresult 7 | #[derive(Debug, PartialEq, Clone, Deserialize)] 8 | #[non_exhaustive] 9 | pub struct ChosenInlineResult { 10 | /// ID of the chosen result. 11 | pub result_id: String, 12 | /// The user who chose the result. 13 | pub from: User, 14 | /// The location of the user, if enabled and allowed. 15 | pub location: Option, 16 | /// The ID of the sent message. 17 | pub inline_message_id: Option, 18 | /// The query used to obtain the result. 19 | pub query: String, 20 | } 21 | -------------------------------------------------------------------------------- /src/types/contact.rs: -------------------------------------------------------------------------------- 1 | use crate::types::user; 2 | use serde::Deserialize; 3 | 4 | /// Represents a [`Contact`]. 5 | /// 6 | /// [`Contact`]: https://core.telegram.org/bots/api#contact 7 | #[derive(Debug, PartialEq, Eq, Clone, Hash, Deserialize)] 8 | #[non_exhaustive] 9 | pub struct Contact { 10 | /// The phone number of the contact. 11 | pub phone_number: String, 12 | /// The first name of the contact. 13 | pub first_name: String, 14 | /// The last name of the contact. 15 | pub last_name: Option, 16 | /// The user id of the contact. 17 | pub user_id: Option, 18 | /// The vCard of the contact. 19 | pub vcard: Option, 20 | } 21 | -------------------------------------------------------------------------------- /src/types/document.rs: -------------------------------------------------------------------------------- 1 | use crate::types::{file, PhotoSize}; 2 | use serde::Deserialize; 3 | 4 | /// Represents a [`Document`]. 5 | /// 6 | /// [`Document`]: https://core.telegram.org/bots/api#document 7 | #[derive(Debug, PartialEq, Eq, Clone, Hash, Deserialize)] 8 | #[non_exhaustive] 9 | pub struct Document { 10 | /// The file ID of the document. 11 | pub file_id: file::Id, 12 | /// The unique ID of the document. 13 | pub file_unique_id: String, 14 | /// The thumb of the document. 15 | pub thumb: Option, 16 | /// The file name of the document. 17 | pub file_name: Option, 18 | /// The MIME type of the document. 19 | pub mime_type: Option, 20 | /// The file size of the document. 21 | pub file_size: Option, 22 | } 23 | -------------------------------------------------------------------------------- /src/types/file.rs: -------------------------------------------------------------------------------- 1 | //! Types related to downloadable files. 2 | 3 | use serde::Deserialize; 4 | 5 | pub mod id; 6 | 7 | pub use id::Id; 8 | 9 | /// Represents a [`File`]. 10 | /// 11 | /// [`File`]: https://core.telegram.org/bots/api#file 12 | #[derive(Debug, PartialEq, Eq, Clone, Hash, Deserialize)] 13 | #[non_exhaustive] 14 | pub struct File { 15 | /// The ID of the file. 16 | #[serde(rename = "file_id")] 17 | pub id: Id, 18 | /// The unique ID of the file. 19 | #[serde(rename = "file_unique_id")] 20 | pub unique_id: String, 21 | /// The size fo the file. 22 | #[serde(rename = "file_size")] 23 | pub size: Option, 24 | /// The path of the file. 25 | #[serde(rename = "file_path")] 26 | pub path: Option, 27 | } 28 | -------------------------------------------------------------------------------- /src/types/file/id.rs: -------------------------------------------------------------------------------- 1 | //! Types representing a file ID. 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | /// Represents a file ID. 6 | #[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)] 7 | #[serde(transparent)] 8 | pub struct Id(pub String); 9 | 10 | impl From for Id { 11 | #[must_use] 12 | fn from(id: String) -> Self { 13 | Self(id) 14 | } 15 | } 16 | 17 | impl<'a> From<&'a String> for Id { 18 | #[must_use] 19 | fn from(id: &'a String) -> Self { 20 | Self(id.clone()) 21 | } 22 | } 23 | 24 | impl<'a> From<&'a str> for Id { 25 | #[must_use] 26 | fn from(id: &'a str) -> Self { 27 | Self(id.to_owned()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/types/game/high_score.rs: -------------------------------------------------------------------------------- 1 | use crate::types::User; 2 | use serde::Deserialize; 3 | 4 | /// Represents a [`GameHighScore`]. 5 | /// 6 | /// [`GameHighScore`]: https://core.telegram.org/bots/api#gamehighscore 7 | #[derive(Debug, PartialEq, Eq, Clone, Hash, Deserialize)] 8 | #[non_exhaustive] 9 | pub struct HighScore { 10 | /// Position of the user in the high score table. 11 | pub position: u32, 12 | /// Information about the user. 13 | pub user: User, 14 | /// The user's score. 15 | pub score: i32, 16 | } 17 | -------------------------------------------------------------------------------- /src/types/inline_message_id.rs: -------------------------------------------------------------------------------- 1 | //! Types representing an inline message ID. 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | /// Represents an inline message ID. 6 | #[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)] 7 | #[serde(transparent)] 8 | pub struct InlineMessageId(pub String); 9 | 10 | impl From for InlineMessageId { 11 | #[must_use] 12 | fn from(id: String) -> Self { 13 | Self(id) 14 | } 15 | } 16 | 17 | impl<'a> From<&'a String> for InlineMessageId { 18 | #[must_use] 19 | fn from(id: &'a String) -> Self { 20 | Self(id.clone()) 21 | } 22 | } 23 | 24 | impl<'a> From<&'a str> for InlineMessageId { 25 | #[must_use] 26 | fn from(id: &'a str) -> Self { 27 | Self(id.to_owned()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/types/inline_query.rs: -------------------------------------------------------------------------------- 1 | //! Types related to inline queries. 2 | 3 | use crate::types::{Location, User}; 4 | use is_macro::Is; 5 | use serde::Deserialize; 6 | 7 | mod id; 8 | pub mod result; 9 | 10 | pub use {id::Id, result::Result}; 11 | 12 | /// Represents the kind of a chat. 13 | #[derive(Debug, Eq, PartialEq, Hash, Clone, Copy, Is, Deserialize)] 14 | #[serde(rename_all = "snake_case")] 15 | pub enum ChatKind { 16 | /// The chat is private. 17 | Sender, 18 | /// The chat is private. 19 | Private, 20 | /// The chat is a channel. 21 | Channel, 22 | /// The chat is a group. 23 | Group, 24 | /// The chat is a supergroup. 25 | Supergroup, 26 | } 27 | 28 | /// Represents an [`InlineQuery`]. 29 | /// 30 | /// [`InlineQuery`]: https://core.telegram.org/bots/api#inlinequery 31 | #[derive(Debug, PartialEq, Clone, Deserialize)] 32 | #[non_exhaustive] 33 | pub struct InlineQuery { 34 | /// The ID of the query. 35 | pub id: Id, 36 | /// The user who sent the query. 37 | pub from: User, 38 | /// The location of the user, if enabled and allowed. 39 | pub location: Option, 40 | /// The query itself. 41 | pub query: String, 42 | /// The offset of the result to be returned. 43 | pub offset: String, 44 | /// The type of chat inline query was sent from. 45 | #[serde(rename = "chat_type")] 46 | pub chat_kind: Option, 47 | } 48 | -------------------------------------------------------------------------------- /src/types/inline_query/id.rs: -------------------------------------------------------------------------------- 1 | //! Types representing an inline query ID. 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | /// Represents an inline query ID. 6 | #[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)] 7 | #[serde(transparent)] 8 | pub struct Id(pub String); 9 | 10 | impl From for Id { 11 | #[must_use] 12 | fn from(id: String) -> Self { 13 | Self(id) 14 | } 15 | } 16 | 17 | impl<'a> From<&'a String> for Id { 18 | #[must_use] 19 | fn from(id: &'a String) -> Self { 20 | Self(id.clone()) 21 | } 22 | } 23 | 24 | impl<'a> From<&'a str> for Id { 25 | #[must_use] 26 | fn from(id: &'a str) -> Self { 27 | Self(id.to_owned()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/types/inline_query/result/game.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | /// Represents an [`InlineQueryResultGame`][docs]. 4 | /// 5 | /// [docs]: https://core.telegram.org/bots/api#inlinequeryresultgame 6 | #[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize)] 7 | #[must_use] 8 | pub struct Game { 9 | game_short_name: String, 10 | } 11 | 12 | impl Game { 13 | /// Constructs a `Game`. 14 | pub fn new(game_short_name: impl Into) -> Self { 15 | Self { 16 | game_short_name: game_short_name.into(), 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/types/inline_query/result/sticker.rs: -------------------------------------------------------------------------------- 1 | use crate::types::{file, InputMessageContent}; 2 | use serde::Serialize; 3 | 4 | /// Represents an [`InlineQueryResultCachedSticker`][docs]. 5 | /// 6 | /// [docs]: https://core.telegram.org/bots/api#inlinequeryresultcachedsticker 7 | #[derive(Debug, PartialEq, Clone, Serialize)] 8 | #[must_use] 9 | pub struct Sticker { 10 | #[serde(rename = "sticker_file_id")] 11 | id: file::Id, 12 | #[serde(skip_serializing_if = "Option::is_none")] 13 | input_message_content: Option, 14 | } 15 | 16 | impl Sticker { 17 | /// Constructs a `Sticker`. 18 | pub const fn new(id: file::Id) -> Self { 19 | Self { 20 | id, 21 | input_message_content: None, 22 | } 23 | } 24 | 25 | /// Configures the content shown after sending the message. 26 | pub fn input_message_content( 27 | mut self, 28 | content: impl Into, 29 | ) -> Self { 30 | self.input_message_content = Some(content.into()); 31 | self 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/types/inline_query/result/thumb.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | /// Represents a thumb. 4 | #[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize)] 5 | #[must_use] 6 | pub struct Thumb { 7 | #[serde(rename = "thumb_url")] 8 | url: String, 9 | #[serde(rename = "thumb_width", skip_serializing_if = "Option::is_none")] 10 | width: Option, 11 | #[serde(rename = "thumb_height", skip_serializing_if = "Option::is_none")] 12 | height: Option, 13 | } 14 | 15 | impl Thumb { 16 | /// Constructs a `Thumb`. 17 | pub fn new(url: impl Into) -> Self { 18 | Self { 19 | url: url.into(), 20 | width: None, 21 | height: None, 22 | } 23 | } 24 | 25 | /// Configures the width of the thumb. 26 | pub const fn width(mut self, width: usize) -> Self { 27 | self.width = Some(width); 28 | self 29 | } 30 | 31 | /// Configures the height of the thumb. 32 | pub const fn height(mut self, height: usize) -> Self { 33 | self.height = Some(height); 34 | self 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/types/input_file/chat_photo.rs: -------------------------------------------------------------------------------- 1 | use super::InputFile; 2 | use serde::{Serialize, Serializer}; 3 | 4 | /// Represents a chat photo to be set. 5 | /// 6 | /// Note that a chat photo cannot be set via either a file ID or a URL. 7 | #[derive(Debug, PartialEq, Eq, Clone, Hash)] 8 | #[must_use] 9 | pub struct ChatPhoto(pub(crate) InputFile); 10 | 11 | impl ChatPhoto { 12 | /// Constructs a `ChatPhoto`. 13 | pub fn with_bytes(bytes: impl Into>) -> Self { 14 | let file = InputFile::File { 15 | filename: "photo.jpg".into(), 16 | bytes: bytes.into(), 17 | }; 18 | 19 | Self(file) 20 | } 21 | } 22 | 23 | impl Serialize for ChatPhoto { 24 | fn serialize(&self, serializer: S) -> Result 25 | where 26 | S: Serializer, 27 | { 28 | self.0.serialize(serializer, "photo") 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/types/input_file/editable_media.rs: -------------------------------------------------------------------------------- 1 | use super::{Animation, Audio, Document, Photo, Video}; 2 | use is_macro::Is; 3 | use serde::Serialize; 4 | 5 | /// Represents media that can be used to edit a message. 6 | #[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Is)] 7 | #[serde(untagged)] 8 | #[non_exhaustive] 9 | #[must_use] 10 | pub enum EditableMedia { 11 | /// An animation that will replace the old media. 12 | Animation(Animation), 13 | /// An audio that will replace the old media. 14 | Audio(Audio), 15 | /// A document that will replace the old media. 16 | Document(Document), 17 | /// A photo that will replace the old media. 18 | Photo(Photo), 19 | /// A video that will replace the old media. 20 | Video(Video), 21 | } 22 | 23 | impl EditableMedia { 24 | pub(crate) const fn name(&self) -> &'static str { 25 | match self { 26 | Self::Animation(..) => "animation", 27 | Self::Audio(..) => "audio", 28 | Self::Document(..) => "document", 29 | Self::Photo(..) => "photo", 30 | Self::Video(..) => "video", 31 | } 32 | } 33 | } 34 | 35 | impl From for EditableMedia { 36 | fn from(animation: Animation) -> Self { 37 | Self::Animation(animation) 38 | } 39 | } 40 | 41 | impl From