├── .github ├── dependabot.yml └── workflows │ ├── fmt.yml │ └── test.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── data ├── image.jpg ├── sound.mp3 └── thumb.jpg ├── lib ├── Cargo.toml ├── examples │ ├── errors.rs │ ├── features.rs │ ├── get_me.rs │ ├── live_location.rs │ ├── pin.rs │ ├── poll.rs │ ├── send.rs │ ├── simple.rs │ └── tracing.rs └── src │ ├── api.rs │ ├── connector │ ├── hyper.rs │ └── mod.rs │ ├── errors.rs │ ├── lib.rs │ ├── macros.rs │ ├── prelude.rs │ ├── stream.rs │ ├── types │ ├── mod.rs │ └── requests.rs │ └── util │ ├── messages.rs │ └── mod.rs ├── raw ├── Cargo.toml ├── src │ ├── lib.rs │ ├── requests │ │ ├── _base │ │ │ ├── _base.rs │ │ │ ├── errors.rs │ │ │ ├── http.rs │ │ │ ├── mod.rs │ │ │ ├── request_types │ │ │ │ ├── detached.rs │ │ │ │ ├── json.rs │ │ │ │ ├── mod.rs │ │ │ │ └── multipart.rs │ │ │ └── response_types │ │ │ │ ├── json.rs │ │ │ │ └── mod.rs │ │ ├── answer_callback_query.rs │ │ ├── answer_inline_query.rs │ │ ├── delete_message.rs │ │ ├── edit_message_caption.rs │ │ ├── edit_message_live_location.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_members_count.rs │ │ ├── get_file.rs │ │ ├── get_me.rs │ │ ├── get_updates.rs │ │ ├── get_user_profile_photos.rs │ │ ├── kick_chat_member.rs │ │ ├── leave_chat.rs │ │ ├── mod.rs │ │ ├── pin_chat_message.rs │ │ ├── send_audio.rs │ │ ├── send_chat_action.rs │ │ ├── send_contact.rs │ │ ├── send_document.rs │ │ ├── send_location.rs │ │ ├── send_message.rs │ │ ├── send_photo.rs │ │ ├── send_poll.rs │ │ ├── send_venue.rs │ │ ├── send_video.rs │ │ ├── stop_message_live_location.rs │ │ ├── stop_poll.rs │ │ ├── unban_chat_member.rs │ │ └── unpin_chat_message.rs │ ├── types │ │ ├── callback_query.rs │ │ ├── chat.rs │ │ ├── chat_invite_link.rs │ │ ├── chat_member.rs │ │ ├── chat_member_update.rs │ │ ├── chosen_inline_result.rs │ │ ├── inline_query.rs │ │ ├── inline_query_result.rs │ │ ├── input_file.rs │ │ ├── message.rs │ │ ├── mod.rs │ │ ├── pre_checkout_query.rs │ │ ├── primitive.rs │ │ ├── refs.rs │ │ ├── reply_markup.rs │ │ ├── response_parameters.rs │ │ ├── shipping_query.rs │ │ ├── text.rs │ │ └── update.rs │ └── url.rs └── tests │ ├── update.rs │ └── update_assets │ ├── inline_query.json │ ├── migrate_from_chat_id.json │ ├── migrate_to_chat_id.json │ └── regression_test_208.json └── rust-toolchain /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.github/workflows/fmt.yml: -------------------------------------------------------------------------------- 1 | name: Fmt 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | fmt: 7 | name: Fmt 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout sources 11 | uses: actions/checkout@v1 12 | 13 | - name: Install rustfmt 14 | run: rustup component add rustfmt 15 | 16 | - name: Run cargo fmt 17 | uses: actions-rs/cargo@v1 18 | with: 19 | command: fmt 20 | args: --all -- --check 21 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | 7 | test: 8 | name: Test 9 | runs-on: ${{ matrix.os }} 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | os: 14 | - ubuntu-latest 15 | - macOS-latest 16 | - windows-latest 17 | rust: 18 | - stable 19 | - beta 20 | - nightly 21 | - toolchain-file 22 | steps: 23 | - name: Checkout sources 24 | uses: actions/checkout@v1 25 | 26 | - name: Read MSRV toolchain 27 | uses: GenesisSam/get-simple-file-action@v1.0.4 28 | if: matrix.rust == 'toolchain-file' 29 | id: read-toolchain-file 30 | with: 31 | file-name: 'rust-toolchain' 32 | 33 | - name: Install toolchain 34 | if: matrix.rust != 'toolchain-file' 35 | uses: actions-rs/toolchain@v1 36 | with: 37 | toolchain: ${{ matrix.rust }} 38 | override: true 39 | 40 | - name: Install MSRV toolchain 41 | if: matrix.rust == 'toolchain-file' 42 | uses: actions-rs/toolchain@v1 43 | with: 44 | toolchain: ${{ steps.read-toolchain-file.outputs.data }} 45 | override: true 46 | 47 | - name: Run cargo check 48 | uses: actions-rs/cargo@v1 49 | with: 50 | command: check 51 | args: --all 52 | 53 | - name: Run cargo test 54 | uses: actions-rs/cargo@v1 55 | with: 56 | command: test 57 | args: --all 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | *.sublime-workspace 4 | *.sublime-project 5 | .idea/ 6 | 7 | .idea 8 | *.iml 9 | 10 | bot-token.sh 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | script: skip 4 | 5 | jobs: 6 | include: 7 | - stage: toolchain-test 8 | name: "Test toolchain" 9 | script: cargo test --verbose -p telegram-bot 10 | - stage: deploy 11 | name: "Deploy docs" 12 | rust: stable 13 | if: branch = master 14 | script: cargo doc --all 15 | deploy: 16 | provider: pages 17 | local_dir: target/doc 18 | skip_cleanup: true 19 | github_token: $GH_TOKEN 20 | keep_history: false 21 | on: 22 | branch: master 23 | # - stage: test 24 | # name: "Test stable" 25 | # rust: stable 26 | # env: 27 | # - RUSTUP_TOOLCHAIN=stable 28 | # script: 29 | # - cargo test --verbose -p telegram-bot 30 | - stage: test 31 | name: "Test beta" 32 | rust: stable 33 | env: 34 | - RUSTUP_TOOLCHAIN=beta 35 | script: 36 | - cargo test --verbose -p telegram-bot 37 | - stage: test 38 | name: "Test nightly" 39 | rust: stable 40 | env: 41 | - RUSTUP_TOOLCHAIN=nightly 42 | script: 43 | - cargo test --verbose -p telegram-bot 44 | 45 | notifications: 46 | webhooks: 47 | - https://heimdallr.mournival.net/travis 48 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## 0.6.3 - 2019-07-17 5 | 6 | ### Fixes 7 | - Fix InlineQueryResultArticle serialization 8 | 9 | ## 0.6.2 - 2018-09-30 10 | 11 | ### Refactor 12 | - telegram-bot and telegram-bot-raw now uses rust edition 2019 13 | - Updated hyper to 0.12 14 | - Updated hyper-tls to 0.3 15 | 16 | ### Features 17 | - `TELEGRAM_API_URL` environment variable allowing to do E2E-testing of the bot. 18 | - Bots can now answer inline queries, audios and documents 19 | 20 | ## 0.6.1 - 2018-02-17 21 | 22 | ### Fixes 23 | - Re-export forgotten types ([#85](https://github.com/telegram-rs/telegram-bot/issues/85)) 24 | 25 | ### Features 26 | - pinChatMessage and unpinChatMessage methods 27 | - Bots can now send and receive Live Locations ([#83](https://github.com/telegram-rs/telegram-bot/issues/83)) 28 | 29 | ## 0.6.0 - 2018-01-09 30 | 31 | Rewritten from scratch. 32 | 33 | ## 0.5.0 - 2016-10-21 34 | 35 | ### Fixed 36 | - Update dependencies. 37 | - Handle unknown messages. 38 | 39 | ## 0.4.1 - 2016-02-25 40 | 41 | ### Fixed 42 | - Fix a bug with broken forward messages. 43 | 44 | ## 0.4.0 - 2016-02-18 45 | 46 | ### Added 47 | - Supergroups support. 48 | - `ParseMode` structure. 49 | 50 | ### Changed 51 | - `Integer` type to be an alias to i64 instead of i32 because of supergroups. 52 | - New `parse_mode` parameter in `API::send_message` method. 53 | - `Chat` enum to support supergroups and channels. 54 | - Specified dependencies versions in Cargo.toml. 55 | 56 | ### Fixed 57 | - Update type of `user_id` field in `Contact` struct 58 | - Handling of replies to a message. 59 | 60 | ## 0.3.0 - 2015-08-29 61 | 62 | ## 0.2.0 - 2015-08-10 63 | 64 | ## 0.1.2 - 2015-07-30 65 | 66 | ### Changed 67 | - `Api::long_poll` method to take `FnMut` instead of `Fn`. 68 | 69 | ## 0.1.1 - 2015-07-26 70 | 71 | ## 0.1.0 - 2015-06-30 72 | 73 | - Initial release 74 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["lib", "raw"] 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright © 2015 Lukas Kalbertodt 3 | 4 | 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: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | 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. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rust Telegram Bot Library 2 | ========================= 3 | [![Build Status](https://img.shields.io/travis/telegram-rs/telegram-bot/master.svg)](https://travis-ci.org/telegram-rs/telegram-bot) 4 | [![Tests](https://github.com/telegram-rs/telegram-bot/workflows/Tests/badge.svg)](https://github.com/telegram-rs/telegram-bot/actions?workflow=Tests) 5 | [![Tests](https://github.com/telegram-rs/telegram-bot/workflows/Fmt/badge.svg)](https://github.com/telegram-rs/telegram-bot/actions?workflow=Fmt) 6 | [![License](https://img.shields.io/github/license/telegram-rs/telegram-bot.svg)]() 7 | [![Crates.io](https://img.shields.io/crates/v/telegram-bot.svg)](https://crates.io/crates/telegram-bot) 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
Documentation:Latest crates.io versionmaster
18 | 19 | A library for writing your own [Telegram](https://telegram.org/) bots. More information [here](https://core.telegram.org/bots). Official API [here](https://core.telegram.org/bots/api). 20 | 21 | ## Example 22 | Here is a simple example (see [`example/simple.rs`](https://github.com/telegram-rs/telegram-bot/blob/master/lib/examples/simple.rs)): 23 | 24 | ``` rust 25 | use std::env; 26 | 27 | use futures::StreamExt; 28 | use telegram_bot::*; 29 | 30 | #[tokio::main] 31 | async fn main() -> Result<(), Error> { 32 | let token = env::var("TELEGRAM_BOT_TOKEN").expect("TELEGRAM_BOT_TOKEN not set"); 33 | let api = Api::new(token); 34 | 35 | // Fetch new updates via long poll method 36 | let mut stream = api.stream(); 37 | while let Some(update) = stream.next().await { 38 | // If the received update contains a new message... 39 | let update = update?; 40 | if let UpdateKind::Message(message) = update.kind { 41 | if let MessageKind::Text { ref data, .. } = message.kind { 42 | // Print received text message to stdout. 43 | println!("<{}>: {}", &message.from.first_name, data); 44 | 45 | // Answer message with "Hi". 46 | api.send(message.text_reply(format!( 47 | "Hi, {}! You just wrote '{}'", 48 | &message.from.first_name, data 49 | ))) 50 | .await?; 51 | } 52 | } 53 | } 54 | Ok(()) 55 | } 56 | ``` 57 | You can find a bigger examples in the `examples`. 58 | 59 | ## Usage 60 | This library is available via `crates.io`. In order to use it, just add this to your `Cargo.toml`: 61 | 62 | ``` 63 | telegram-bot = "0.7" 64 | ``` 65 | 66 | The library allows you to do E2E-testing of your bot easily: just specify `TELEGRAM_API_URL` environment variable to point to your fake Telegram test server. 67 | A lot of diagnostic information can be collected with [tracing](https://crates.io/crates/tracing) framework, see [`example/tracing.rs`](https://github.com/telegram-rs/telegram-bot/blob/master/lib/examples/tracing.rs)). 68 | 69 | ## Collaboration 70 | Yes please! Every type of contribution is welcome: Create issues, hack some code or make suggestions. Don't know where to start? Good first issues are tagged with [up for grab](https://github.com/telegram-rs/telegram-bot/issues?q=is%3Aissue+is%3Aopen+label%3A%22up+for+grab%22). 71 | -------------------------------------------------------------------------------- /data/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telegram-rs/telegram-bot/65ad5cfd578e9a1260ce6daac714eb2153c0bec7/data/image.jpg -------------------------------------------------------------------------------- /data/sound.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telegram-rs/telegram-bot/65ad5cfd578e9a1260ce6daac714eb2153c0bec7/data/sound.mp3 -------------------------------------------------------------------------------- /data/thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telegram-rs/telegram-bot/65ad5cfd578e9a1260ce6daac714eb2153c0bec7/data/thumb.jpg -------------------------------------------------------------------------------- /lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "telegram-bot" 3 | version = "0.9.0" 4 | authors = ["Lukas Kalbertodt ", "Fedor Gogolev ", "Gustavo Aguiar "] 5 | edition = "2018" 6 | 7 | description = "A library for creating Telegram bots" 8 | 9 | documentation = "https://docs.rs/telegram-bot/" 10 | repository = "https://github.com/telegram-rs/telegram-bot" 11 | readme = "../README.md" 12 | 13 | keywords = ["telegram", "bot", "chat", "api"] 14 | categories = ["api-bindings", "asynchronous"] 15 | license = "MIT" 16 | 17 | [features] 18 | openssl = ["hyper-tls"] 19 | rustls = ["hyper-rustls"] 20 | default = ["openssl"] 21 | [dependencies] 22 | bytes = "1.0.1" 23 | tokio = { version = "1.2", features = ["fs", "rt"]} 24 | 25 | tracing = "0.1.23" 26 | tracing-futures = "0.2" 27 | multipart = { version = "0.17", default-features = false, features = ["client"] } 28 | 29 | telegram-bot-raw = { version = "0.9.0", path = "../raw" } 30 | 31 | hyper = { version = "0.14", features = ["client", "http1"] } 32 | hyper-tls = { version = "0.5", optional = true } 33 | futures = "0.3" 34 | hyper-rustls = { version = "0.22", optional = true } 35 | [dev-dependencies] 36 | tracing-subscriber = "0.2.15" 37 | tokio = { version = "1.2", features = ["macros", "time", "fs", "rt-multi-thread"] } 38 | -------------------------------------------------------------------------------- /lib/examples/errors.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | use futures::StreamExt; 4 | use telegram_bot::*; 5 | 6 | #[tokio::main] 7 | async fn main() { 8 | let token = env::var("TELEGRAM_BOT_TOKEN").expect("TELEGRAM_BOT_TOKEN not set"); 9 | let mut stream = Api::new(token).stream(); 10 | 11 | // Print update or error for each update. 12 | while let Some(mb_update) = stream.next().await { 13 | println!("{:?}", mb_update); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/examples/features.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::time::Duration; 3 | 4 | use futures::StreamExt; 5 | use telegram_bot::prelude::*; 6 | use telegram_bot::{Api, Error, Message, MessageKind, ParseMode, UpdateKind}; 7 | use tokio::time::sleep; 8 | 9 | async fn test_message(api: Api, message: Message) -> Result<(), Error> { 10 | api.send(message.text_reply("Simple message")).await?; 11 | 12 | let mut reply = message.text_reply("`Markdown message`"); 13 | api.send(reply.parse_mode(ParseMode::Markdown)).await?; 14 | 15 | let mut reply = message.text_reply("Bold HTML message"); 16 | 17 | api.send(reply.parse_mode(ParseMode::Html)).await?; 18 | Ok(()) 19 | } 20 | 21 | async fn test_preview(api: Api, message: Message) -> Result<(), Error> { 22 | api.send(message.text_reply("Message with preview https://telegram.org")) 23 | .await?; 24 | 25 | let mut reply = message.text_reply("Message without preview https://telegram.org"); 26 | 27 | api.send(reply.disable_preview()).await?; 28 | Ok(()) 29 | } 30 | 31 | async fn test_reply(api: Api, message: Message) -> Result<(), Error> { 32 | api.send(message.text_reply("Reply to message")).await?; 33 | api.send(message.chat.text("Text to message chat")).await?; 34 | 35 | api.send(message.from.text("Private text")).await?; 36 | Ok(()) 37 | } 38 | 39 | async fn test_forward(api: Api, message: Message) -> Result<(), Error> { 40 | api.send(message.forward(&message.chat)).await?; 41 | 42 | api.send(message.forward(&message.from)).await?; 43 | Ok(()) 44 | } 45 | 46 | async fn test_edit_message(api: Api, message: Message) -> Result<(), Error> { 47 | let message1 = api.send(message.text_reply("Round 1")).await?; 48 | 49 | sleep(Duration::from_secs(2)).await; 50 | 51 | let message2 = api.send(message1.edit_text("Round 2")).await?; 52 | 53 | sleep(Duration::from_secs(4)).await; 54 | 55 | api.send(message2.edit_text("Round 3")).await?; 56 | Ok(()) 57 | } 58 | 59 | async fn test_get_chat(api: Api, message: Message) -> Result<(), Error> { 60 | let chat = api.send(message.chat.get_chat()).await?; 61 | api.send(chat.text(format!("Chat id {}", chat.id()))) 62 | .await?; 63 | Ok(()) 64 | } 65 | 66 | async fn test_get_chat_administrators(api: Api, message: Message) -> Result<(), Error> { 67 | let administrators = api.send(message.chat.get_administrators()).await?; 68 | let mut response = Vec::new(); 69 | for member in administrators { 70 | response.push(member.user.first_name.clone()) 71 | } 72 | api.send(message.text_reply(format!("Administrators: {}", response.join(", ")))) 73 | .await?; 74 | Ok(()) 75 | } 76 | 77 | async fn test_get_chat_members_count(api: Api, message: Message) -> Result<(), Error> { 78 | let count = api.send(message.chat.get_members_count()).await?; 79 | api.send(message.text_reply(format!("Members count: {}", count))) 80 | .await?; 81 | Ok(()) 82 | } 83 | 84 | async fn test_get_chat_member(api: Api, message: Message) -> Result<(), Error> { 85 | let member = api.send(message.chat.get_member(&message.from)).await?; 86 | let first_name = member.user.first_name.clone(); 87 | let status = member.status; 88 | api.send(message.text_reply(format!("Member {}, status {:?}", first_name, status))) 89 | .await?; 90 | Ok(()) 91 | } 92 | 93 | async fn test_get_user_profile_photos(api: Api, message: Message) -> Result<(), Error> { 94 | let photos = api.send(message.from.get_user_profile_photos()).await?; 95 | 96 | api.send(message.text_reply(format!("Found photos: {}", photos.total_count))) 97 | .await?; 98 | Ok(()) 99 | } 100 | 101 | async fn test_leave(api: Api, message: Message) -> Result<(), Error> { 102 | api.send(message.chat.leave()).await?; 103 | Ok(()) 104 | } 105 | 106 | async fn test(api: Api, message: Message) -> Result<(), Error> { 107 | match message.kind { 108 | MessageKind::Text { ref data, .. } => match data.as_str() { 109 | "/message" => test_message(api, message).await?, 110 | "/preview" => test_preview(api, message).await?, 111 | "/reply" => test_reply(api, message).await?, 112 | "/forward" => test_forward(api, message).await?, 113 | "/edit-message" => test_edit_message(api, message).await?, 114 | "/get_chat" => test_get_chat(api, message).await?, 115 | "/get_chat_administrators" => test_get_chat_administrators(api, message).await?, 116 | "/get_chat_members_count" => test_get_chat_members_count(api, message).await?, 117 | "/get_chat_member" => test_get_chat_member(api, message).await?, 118 | "/get_user_profile_photos" => test_get_user_profile_photos(api, message).await?, 119 | "/leave" => test_leave(api, message).await?, 120 | _ => (), 121 | }, 122 | _ => (), 123 | }; 124 | 125 | Ok(()) 126 | } 127 | 128 | #[tokio::main] 129 | async fn main() -> Result<(), Error> { 130 | let token = env::var("TELEGRAM_BOT_TOKEN").expect("TELEGRAM_BOT_TOKEN not set"); 131 | 132 | let api = Api::new(token); 133 | let mut stream = api.stream(); 134 | 135 | while let Some(update) = stream.next().await { 136 | let update = update?; 137 | if let UpdateKind::Message(message) = update.kind { 138 | test(api.clone(), message).await?; 139 | } 140 | } 141 | 142 | Ok(()) 143 | } 144 | -------------------------------------------------------------------------------- /lib/examples/get_me.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | use telegram_bot::{Api, Error, GetMe}; 4 | 5 | #[tokio::main] 6 | async fn main() -> Result<(), Error> { 7 | let token = env::var("TELEGRAM_BOT_TOKEN").expect("TELEGRAM_BOT_TOKEN not set"); 8 | 9 | let api = Api::new(token); 10 | let result = api.send(GetMe).await?; 11 | println!("{:?}", result); 12 | Ok(()) 13 | } 14 | -------------------------------------------------------------------------------- /lib/examples/live_location.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::time::Duration; 3 | 4 | use futures::StreamExt; 5 | use telegram_bot::*; 6 | use tokio::time::sleep; 7 | 8 | const DELAY_DURATION: Duration = Duration::from_secs(2); 9 | 10 | async fn test(api: Api, message: Message) -> Result<(), Error> { 11 | let reply = api 12 | .send(message.location_reply(0.0, 0.0).live_period(60)) 13 | .await?; 14 | 15 | sleep(DELAY_DURATION).await; 16 | api.send(reply.edit_live_location(10.0, 10.0)).await?; 17 | 18 | sleep(DELAY_DURATION).await; 19 | api.send(reply.edit_live_location(20.0, 20.0)).await?; 20 | 21 | sleep(DELAY_DURATION).await; 22 | api.send(reply.edit_live_location(30.0, 30.0)).await?; 23 | 24 | Ok(()) 25 | } 26 | 27 | #[tokio::main] 28 | async fn main() -> Result<(), Error> { 29 | let token = env::var("TELEGRAM_BOT_TOKEN").expect("TELEGRAM_BOT_TOKEN not set"); 30 | 31 | let api = Api::new(token); 32 | let mut stream = api.stream(); 33 | 34 | while let Some(update) = stream.next().await { 35 | let update = update?; 36 | if let UpdateKind::Message(message) = update.kind { 37 | if let MessageKind::Text { ref data, .. } = message.kind { 38 | match data.as_str() { 39 | "/livelocation" => test(api.clone(), message.clone()).await?, 40 | _ => (), 41 | } 42 | } 43 | } 44 | } 45 | Ok(()) 46 | } 47 | -------------------------------------------------------------------------------- /lib/examples/pin.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | use futures::StreamExt; 4 | use telegram_bot::*; 5 | 6 | async fn process(api: Api, message: Message) -> Result<(), Error> { 7 | if let MessageKind::Text { ref data, .. } = message.kind { 8 | match data.as_str() { 9 | "/pin" => { 10 | if let Some(reply) = message.reply_to_message { 11 | api.send(reply.pin()).await?; 12 | } 13 | } 14 | "/unpin" => api.send(message.chat.unpin_message()).await?, 15 | 16 | _ => (), 17 | } 18 | } 19 | Ok(()) 20 | } 21 | 22 | #[tokio::main] 23 | async fn main() -> Result<(), Error> { 24 | let token = env::var("TELEGRAM_BOT_TOKEN").expect("TELEGRAM_BOT_TOKEN not set"); 25 | 26 | let api = Api::new(token); 27 | let mut stream = api.stream(); 28 | 29 | // Fetch new updates via long poll method 30 | while let Some(update) = stream.next().await { 31 | let update = update?; 32 | if let UpdateKind::Message(message) = update.kind { 33 | process(api.clone(), message).await? 34 | } 35 | } 36 | 37 | Ok(()) 38 | } 39 | -------------------------------------------------------------------------------- /lib/examples/poll.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::time::Duration; 3 | 4 | use futures::StreamExt; 5 | use tokio::time::sleep; 6 | 7 | use telegram_bot::prelude::*; 8 | use telegram_bot::{ 9 | Api, Error, Message, MessageKind, Poll, PollAnswer, SendPoll, UpdateKind, User, 10 | }; 11 | 12 | fn make_test_poll<'p>(message: Message) -> SendPoll<'p, 'p, 'p> { 13 | let question = "Simple poll"; 14 | let options = vec!["Option 1", "Option 2"]; 15 | 16 | message.poll_reply(question, options) 17 | } 18 | 19 | async fn send_and_stop_poll<'p>(api: Api, poll: SendPoll<'p, 'p, 'p>) -> Result<(), Error> { 20 | let poll_message = api.send(poll).await?; 21 | 22 | sleep(Duration::from_secs(10)).await; 23 | 24 | api.send(poll_message.stop_poll()).await?; 25 | Ok(()) 26 | } 27 | 28 | async fn test_anonymous_poll(api: Api, message: Message) -> Result<(), Error> { 29 | let poll = make_test_poll(message); 30 | send_and_stop_poll(api, poll).await 31 | } 32 | 33 | async fn test_public_poll(api: Api, message: Message) -> Result<(), Error> { 34 | let poll = make_test_poll(message.clone()).not_anonymous().to_owned(); 35 | 36 | send_and_stop_poll(api, poll).await 37 | } 38 | 39 | async fn test_quiz_poll(api: Api, message: Message) -> Result<(), Error> { 40 | let poll = make_test_poll(message.clone()) 41 | .quiz() 42 | .correct_option_id(0) 43 | .explanation("Some explanation") 44 | .to_owned(); 45 | 46 | send_and_stop_poll(api, poll).await 47 | } 48 | 49 | async fn test_multiple_answers(api: Api, message: Message) -> Result<(), Error> { 50 | let poll = make_test_poll(message.clone()) 51 | .allows_multiple_answers() 52 | .to_owned(); 53 | 54 | send_and_stop_poll(api, poll).await 55 | } 56 | 57 | async fn test_closed_poll(api: Api, message: Message) -> Result<(), Error> { 58 | let poll = make_test_poll(message.clone()).closed().to_owned(); 59 | 60 | api.send(poll).await?; 61 | Ok(()) 62 | } 63 | 64 | #[tokio::main] 65 | async fn main() -> Result<(), Error> { 66 | let token = env::var("TELEGRAM_BOT_TOKEN").expect("TELEGRAM_BOT_TOKEN not set"); 67 | 68 | let api = Api::new(token); 69 | let mut stream = api.stream(); 70 | 71 | while let Some(update) = stream.next().await { 72 | let update = update?; 73 | 74 | match update.kind { 75 | UpdateKind::Message(message) => match message.kind { 76 | MessageKind::Text { ref data, .. } => match data.as_str() { 77 | "/poll" => test_anonymous_poll(api.clone(), message).await?, 78 | "/quiz" => test_quiz_poll(api.clone(), message).await?, 79 | "/public" => test_public_poll(api.clone(), message).await?, 80 | "/multiple" => test_multiple_answers(api.clone(), message).await?, 81 | "/closed" => test_closed_poll(api.clone(), message).await?, 82 | _ => (), 83 | }, 84 | _ => (), 85 | }, 86 | UpdateKind::Poll(Poll { 87 | total_voter_count, 88 | id, 89 | .. 90 | }) => println!( 91 | "Poll update - {} with total voters {}", 92 | id, total_voter_count 93 | ), 94 | UpdateKind::PollAnswer(PollAnswer { 95 | poll_id, 96 | user: User { first_name, .. }, 97 | option_ids, 98 | }) => println!( 99 | "In poll {} {} voted for {:?}", 100 | poll_id, first_name, option_ids 101 | ), 102 | _ => (), 103 | } 104 | } 105 | 106 | Ok(()) 107 | } 108 | -------------------------------------------------------------------------------- /lib/examples/send.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | use futures::StreamExt; 4 | 5 | use telegram_bot::prelude::*; 6 | use telegram_bot::{Api, Error, InputFileRef, InputFileUpload, Message, MessageKind, UpdateKind}; 7 | 8 | async fn run_test(api: Api, message: Message) -> Result<(), Error> { 9 | let chat = message.chat.clone(); 10 | 11 | // Send a document from memory 12 | let document = "foo\nbar\nbaz".as_bytes(); 13 | let file = InputFileUpload::with_data(document, "doc.txt"); 14 | 15 | api.send(message.document_reply(&file).caption("Reply to message")) 16 | .await?; 17 | api.send(chat.document(&file).caption("Direct to chat")) 18 | .await?; 19 | api.send(message.from.document(&file).caption("Send to user")) 20 | .await?; 21 | 22 | // With custom thumbnail 23 | api.send( 24 | chat.document(&file) 25 | .thumb(InputFileUpload::with_path("data/thumb.jpg")), 26 | ) 27 | .await?; 28 | 29 | // Send a document from disk 30 | let file = InputFileUpload::with_path("data/image.jpg"); 31 | api.send(chat.document(&file)).await?; 32 | 33 | // With custom name 34 | api.send(chat.document(file.file_name("picture.png"))) 35 | .await?; 36 | 37 | // Send an image from disk 38 | api.send(chat.photo(&file)).await?; 39 | 40 | // Send an audio file from disk 41 | let file = InputFileUpload::with_path("data/sound.mp3"); 42 | let resp = api.send(chat.audio(file)).await?; 43 | 44 | // Resend an audio file by file_id 45 | if let MessageKind::Audio { data } = resp.kind { 46 | api.send(chat.audio(InputFileRef::new(data.file_id))) 47 | .await?; 48 | } 49 | 50 | // Send an image by url 51 | api.send(chat.photo(InputFileRef::new("https://telegram.org/img/t_logo.png"))) 52 | .await?; 53 | 54 | Ok(()) 55 | } 56 | 57 | #[tokio::main] 58 | async fn main() -> Result<(), Error> { 59 | let token = env::var("TELEGRAM_BOT_TOKEN").expect("TELEGRAM_BOT_TOKEN not set"); 60 | 61 | let api = Api::new(token); 62 | let mut stream = api.stream(); 63 | 64 | while let Some(update) = stream.next().await { 65 | let update = update?; 66 | if let UpdateKind::Message(message) = update.kind { 67 | match message.kind { 68 | MessageKind::Text { ref data, .. } if data.as_str() == "/test" => { 69 | let api = api.clone(); 70 | tokio::spawn(async move { 71 | if let Err(err) = run_test(api, message).await { 72 | eprintln!("Error: {:?}", err); 73 | } 74 | }); 75 | } 76 | _ => (), 77 | }; 78 | } 79 | } 80 | 81 | Ok(()) 82 | } 83 | -------------------------------------------------------------------------------- /lib/examples/simple.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | use futures::StreamExt; 4 | use telegram_bot::*; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<(), Error> { 8 | let token = env::var("TELEGRAM_BOT_TOKEN").expect("TELEGRAM_BOT_TOKEN not set"); 9 | let api = Api::new(token); 10 | 11 | // Fetch new updates via long poll method 12 | let mut stream = api.stream(); 13 | while let Some(update) = stream.next().await { 14 | // If the received update contains a new message... 15 | let update = update?; 16 | if let UpdateKind::Message(message) = update.kind { 17 | if let MessageKind::Text { ref data, .. } = message.kind { 18 | // Print received text message to stdout. 19 | println!("<{}>: {}", &message.from.first_name, data); 20 | 21 | // Answer message with "Hi". 22 | api.send(message.text_reply(format!( 23 | "Hi, {}! You just wrote '{}'", 24 | &message.from.first_name, data 25 | ))) 26 | .await?; 27 | } 28 | } 29 | } 30 | Ok(()) 31 | } 32 | -------------------------------------------------------------------------------- /lib/examples/tracing.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | use futures::StreamExt; 4 | use telegram_bot::*; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<(), Error> { 8 | let token = env::var("TELEGRAM_BOT_TOKEN").expect("TELEGRAM_BOT_TOKEN not set"); 9 | let api = Api::new(token); 10 | 11 | tracing::subscriber::set_global_default( 12 | tracing_subscriber::FmtSubscriber::builder() 13 | .with_env_filter("telegram_bot=trace") 14 | .finish(), 15 | ) 16 | .unwrap(); 17 | 18 | let mut stream = api.stream(); 19 | while let Some(update) = stream.next().await { 20 | let update = update?; 21 | if let UpdateKind::Message(message) = update.kind { 22 | if let MessageKind::Text { ref data, .. } = message.kind { 23 | api.send(message.text_reply(format!( 24 | "Hi, {}! You just wrote '{}'", 25 | &message.from.first_name, data 26 | ))) 27 | .await?; 28 | } 29 | } 30 | } 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /lib/src/api.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{ 2 | atomic::{AtomicUsize, Ordering}, 3 | Arc, 4 | }; 5 | use std::time::Duration; 6 | 7 | use futures::{Future, FutureExt}; 8 | use tokio::time::timeout; 9 | use tracing_futures::Instrument; 10 | 11 | use telegram_bot_raw::{HttpRequest, Request, ResponseType}; 12 | 13 | use crate::connector::{default_connector, Connector}; 14 | use crate::errors::{Error, ErrorKind}; 15 | use crate::stream::UpdatesStream; 16 | 17 | /// Main type for sending requests to the Telegram bot API. 18 | #[derive(Clone)] 19 | pub struct Api(Arc); 20 | 21 | struct ApiInner { 22 | token: String, 23 | connector: Box, 24 | next_request_id: AtomicUsize, 25 | } 26 | 27 | impl Api { 28 | /// Create a new `Api` instance. 29 | /// 30 | /// # Example 31 | /// 32 | /// Using default connector. 33 | /// 34 | /// ```rust 35 | /// use telegram_bot::Api; 36 | /// 37 | /// # fn main() { 38 | /// # let telegram_token = "token"; 39 | /// let api = Api::new(telegram_token); 40 | /// # } 41 | /// ``` 42 | pub fn new>(token: T) -> Self { 43 | Self::with_connector(token, default_connector()) 44 | } 45 | 46 | /// Create a new `Api` instance wtih custom connector. 47 | pub fn with_connector>(token: T, connector: Box) -> Self { 48 | Api(Arc::new(ApiInner { 49 | token: token.as_ref().to_string(), 50 | connector, 51 | next_request_id: AtomicUsize::new(0), 52 | })) 53 | } 54 | 55 | /// Create a stream which produces updates from the Telegram server. 56 | /// 57 | /// # Examples 58 | /// 59 | /// ```rust 60 | /// # use telegram_bot::Api; 61 | /// use futures::StreamExt; 62 | /// 63 | /// # #[tokio::main] 64 | /// # async fn main() { 65 | /// # let api: Api = Api::new("token"); 66 | /// 67 | /// let mut stream = api.stream(); 68 | /// let update = stream.next().await; 69 | /// println!("{:?}", update); 70 | /// # } 71 | /// ``` 72 | pub fn stream(&self) -> UpdatesStream { 73 | UpdatesStream::new(&self) 74 | } 75 | 76 | /// Send a request to the Telegram server and do not wait for a response. 77 | /// 78 | /// # Examples 79 | /// 80 | /// ```rust 81 | /// # use telegram_bot::{Api, ChatId, prelude::*}; 82 | /// # use std::time::Duration; 83 | /// # 84 | /// # #[tokio::main] 85 | /// # async fn main() { 86 | /// # let telegram_token = "token"; 87 | /// # let api = Api::new(telegram_token); 88 | /// # if false { 89 | /// let chat = ChatId::new(61031); 90 | /// api.spawn(chat.text("Message")); 91 | /// # } 92 | /// # } 93 | /// ``` 94 | pub fn spawn(&self, request: Req) { 95 | let api = self.clone(); 96 | if let Ok(request) = request.serialize() { 97 | tokio::spawn(async move { 98 | let _ = api.send_http_request::(request).await; 99 | }); 100 | } 101 | } 102 | 103 | /// Send a request to the Telegram server and wait for a response, timing out after `duration`. 104 | /// Future will resolve to `None` if timeout fired. 105 | /// 106 | /// # Examples 107 | /// 108 | /// ```rust 109 | /// # use telegram_bot::{Api, GetMe}; 110 | /// # use std::time::Duration; 111 | /// # 112 | /// # #[tokio::main] 113 | /// # async fn main() { 114 | /// # let telegram_token = "token"; 115 | /// # let api = Api::new(telegram_token); 116 | /// # if false { 117 | /// let result = api.send_timeout(GetMe, Duration::from_secs(2)).await; 118 | /// println!("{:?}", result); 119 | /// # } 120 | /// # } 121 | /// ``` 122 | pub fn send_timeout( 123 | &self, 124 | request: Req, 125 | duration: Duration, 126 | ) -> impl Future::Type>, Error>> + Send 127 | { 128 | let api = self.clone(); 129 | let request = request.serialize(); 130 | async move { 131 | match timeout( 132 | duration, 133 | api.send_http_request::(request.map_err(ErrorKind::from)?), 134 | ) 135 | .await 136 | { 137 | Err(_) => Ok(None), 138 | Ok(Ok(result)) => Ok(Some(result)), 139 | Ok(Err(error)) => Err(error), 140 | } 141 | } 142 | } 143 | 144 | /// Send a request to the Telegram server and wait for a response. 145 | /// 146 | /// # Examples 147 | /// 148 | /// ```rust 149 | /// # use telegram_bot::{Api, GetMe}; 150 | /// # 151 | /// # #[tokio::main] 152 | /// # async fn main() { 153 | /// # let telegram_token = "token"; 154 | /// # let api = Api::new(telegram_token); 155 | /// # if false { 156 | /// let result = api.send(GetMe).await; 157 | /// println!("{:?}", result); 158 | /// # } 159 | /// # } 160 | /// ``` 161 | pub fn send( 162 | &self, 163 | request: Req, 164 | ) -> impl Future::Type, Error>> + Send { 165 | let api = self.clone(); 166 | let request = request.serialize(); 167 | async move { 168 | api.send_http_request::(request.map_err(ErrorKind::from)?) 169 | .await 170 | } 171 | } 172 | 173 | async fn send_http_request( 174 | &self, 175 | request: HttpRequest, 176 | ) -> Result { 177 | let request_id = self.0.next_request_id.fetch_add(1, Ordering::Relaxed); 178 | let span = tracing::trace_span!("send_http_request", request_id = request_id); 179 | async { 180 | tracing::trace!(name = %request.name(), body = %request.body, "sending request"); 181 | let http_response = self.0.connector.request(&self.0.token, request).await?; 182 | tracing::trace!( 183 | response = %match http_response.body { 184 | Some(ref vec) => match std::str::from_utf8(vec) { 185 | Ok(str) => str, 186 | Err(_) => "" 187 | }, 188 | None => "", 189 | }, "response received" 190 | ); 191 | 192 | let response = Resp::deserialize(http_response).map_err(ErrorKind::from)?; 193 | tracing::trace!("response deserialized"); 194 | Ok(response) 195 | } 196 | .map(|result| { 197 | if let Err(ref error) = result { 198 | tracing::error!(error = %error); 199 | } 200 | result 201 | }) 202 | .instrument(span) 203 | .await 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /lib/src/connector/hyper.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Cursor, Read}; 2 | use std::path::Path; 3 | use std::pin::Pin; 4 | use std::str::FromStr; 5 | 6 | use bytes::Bytes; 7 | use futures::{Future, FutureExt}; 8 | use hyper::{ 9 | body::to_bytes, 10 | client::{connect::Connect, Client}, 11 | header::CONTENT_TYPE, 12 | http::Error as HttpError, 13 | Method, Request, Uri, 14 | }; 15 | #[cfg(feature = "rustls")] 16 | use hyper_rustls::HttpsConnector; 17 | #[cfg(feature = "openssl")] 18 | use hyper_tls::HttpsConnector; 19 | use multipart::client::lazy::Multipart; 20 | use telegram_bot_raw::{ 21 | Body as TelegramBody, HttpRequest, HttpResponse, Method as TelegramMethod, MultipartValue, Text, 22 | }; 23 | 24 | use super::Connector; 25 | use crate::errors::{Error, ErrorKind}; 26 | 27 | #[derive(Debug)] 28 | pub struct HyperConnector(Client); 29 | 30 | enum MultipartTemporaryValue { 31 | Text(Text), 32 | Data { file_name: Text, data: Bytes }, 33 | } 34 | 35 | impl HyperConnector { 36 | pub fn new(client: Client) -> Self { 37 | HyperConnector(client) 38 | } 39 | } 40 | 41 | impl Connector for HyperConnector { 42 | fn request( 43 | &self, 44 | token: &str, 45 | req: HttpRequest, 46 | ) -> Pin> + Send>> { 47 | let uri = Uri::from_str(&req.url.url(token)); 48 | let client = self.0.clone(); 49 | 50 | let future = async move { 51 | let uri = uri.map_err(HttpError::from).map_err(ErrorKind::from)?; 52 | 53 | let method = match req.method { 54 | TelegramMethod::Get => Method::GET, 55 | TelegramMethod::Post => Method::POST, 56 | }; 57 | 58 | let mut http_request = Request::builder().method(method).uri(uri); 59 | 60 | let request = match req.body { 61 | TelegramBody::Empty => http_request.body(Into::::into(vec![])), 62 | TelegramBody::Json(body) => { 63 | let content_type = "application/json" 64 | .parse() 65 | .map_err(HttpError::from) 66 | .map_err(ErrorKind::from)?; 67 | http_request 68 | .headers_mut() 69 | .map(move |headers| headers.insert(CONTENT_TYPE, content_type)); 70 | http_request.body(Into::::into(body)) 71 | } 72 | TelegramBody::Multipart(parts) => { 73 | let mut fields = Vec::new(); 74 | for (key, value) in parts { 75 | match value { 76 | MultipartValue::Text(text) => { 77 | fields.push((key, MultipartTemporaryValue::Text(text))) 78 | } 79 | MultipartValue::Path { file_name, path } => { 80 | let file_name = file_name 81 | .or_else(|| { 82 | AsRef::::as_ref(&path) 83 | .file_name() 84 | .and_then(|s| s.to_str()) 85 | .map(Into::into) 86 | }) 87 | .ok_or(ErrorKind::InvalidMultipartFilename)?; 88 | 89 | let data = tokio::fs::read(path).await.map_err(ErrorKind::from)?; 90 | fields.push(( 91 | key, 92 | MultipartTemporaryValue::Data { 93 | file_name, 94 | data: data.into(), 95 | }, 96 | )) 97 | } 98 | MultipartValue::Data { file_name, data } => fields 99 | .push((key, MultipartTemporaryValue::Data { file_name, data })), 100 | } 101 | } 102 | 103 | let mut prepared = { 104 | let mut part = Multipart::new(); 105 | for (key, value) in &fields { 106 | match value { 107 | MultipartTemporaryValue::Text(text) => { 108 | part.add_text(*key, text.as_str()); 109 | } 110 | MultipartTemporaryValue::Data { file_name, data } => { 111 | part.add_stream( 112 | *key, 113 | Cursor::new(data), 114 | Some(file_name.as_str()), 115 | None, 116 | ); 117 | } 118 | } 119 | } 120 | part.prepare().map_err(|err| err.error) 121 | } 122 | .map_err(ErrorKind::from)?; 123 | 124 | let boundary = prepared.boundary(); 125 | 126 | let content_type = 127 | format!("multipart/form-data;boundary={bound}", bound = boundary) 128 | .parse() 129 | .map_err(HttpError::from) 130 | .map_err(ErrorKind::from)?; 131 | http_request.headers_mut().map(move |headers| { 132 | headers.insert(CONTENT_TYPE, content_type); 133 | }); 134 | 135 | let mut bytes = Vec::new(); 136 | prepared.read_to_end(&mut bytes).map_err(ErrorKind::from)?; 137 | http_request.body(bytes.into()) 138 | } 139 | body => panic!("Unknown body type {:?}", body), 140 | } 141 | .map_err(ErrorKind::from)?; 142 | 143 | let response = client.request(request).await.map_err(ErrorKind::from)?; 144 | let whole_chunk = to_bytes(response.into_body()).await; 145 | 146 | let body = whole_chunk 147 | .iter() 148 | .fold(vec![], |mut acc, chunk| -> Vec { 149 | acc.extend_from_slice(&chunk); 150 | acc 151 | }); 152 | 153 | Ok::(HttpResponse { body: Some(body) }) 154 | }; 155 | 156 | future.boxed() 157 | } 158 | } 159 | 160 | pub fn default_connector() -> Result, Error> { 161 | #[cfg(feature = "rustls")] 162 | let connector = HttpsConnector::with_native_roots(); 163 | 164 | #[cfg(feature = "openssl")] 165 | let connector = HttpsConnector::new(); 166 | 167 | Ok(Box::new(HyperConnector::new( 168 | Client::builder().build(connector), 169 | ))) 170 | } 171 | -------------------------------------------------------------------------------- /lib/src/connector/mod.rs: -------------------------------------------------------------------------------- 1 | //! Connector with hyper backend. 2 | 3 | pub mod hyper; 4 | 5 | use std::fmt::Debug; 6 | use std::pin::Pin; 7 | 8 | use futures::Future; 9 | use telegram_bot_raw::{HttpRequest, HttpResponse}; 10 | 11 | use crate::errors::Error; 12 | 13 | pub trait Connector: Debug + Send + Sync { 14 | fn request( 15 | &self, 16 | token: &str, 17 | req: HttpRequest, 18 | ) -> Pin> + Send>>; 19 | } 20 | 21 | pub fn default_connector() -> Box { 22 | hyper::default_connector().unwrap() 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/errors.rs: -------------------------------------------------------------------------------- 1 | use std::error; 2 | use std::fmt; 3 | 4 | #[derive(Debug)] 5 | pub struct Error(ErrorKind); 6 | 7 | #[derive(Debug)] 8 | pub(crate) enum ErrorKind { 9 | Raw(telegram_bot_raw::Error), 10 | Hyper(hyper::Error), 11 | Http(hyper::http::Error), 12 | Io(std::io::Error), 13 | InvalidMultipartFilename, 14 | } 15 | 16 | impl From for ErrorKind { 17 | fn from(error: telegram_bot_raw::Error) -> Self { 18 | ErrorKind::Raw(error) 19 | } 20 | } 21 | 22 | impl From for ErrorKind { 23 | fn from(error: hyper::Error) -> Self { 24 | ErrorKind::Hyper(error) 25 | } 26 | } 27 | 28 | impl From for ErrorKind { 29 | fn from(error: hyper::http::Error) -> Self { 30 | ErrorKind::Http(error) 31 | } 32 | } 33 | 34 | impl From for ErrorKind { 35 | fn from(error: std::io::Error) -> Self { 36 | ErrorKind::Io(error) 37 | } 38 | } 39 | 40 | impl From for Error { 41 | fn from(kind: ErrorKind) -> Self { 42 | Error(kind) 43 | } 44 | } 45 | 46 | impl fmt::Display for Error { 47 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 48 | match &self.0 { 49 | ErrorKind::Raw(error) => write!(f, "{}", error), 50 | ErrorKind::Hyper(error) => write!(f, "{}", error), 51 | ErrorKind::Http(error) => write!(f, "{}", error), 52 | ErrorKind::Io(error) => write!(f, "{}", error), 53 | ErrorKind::InvalidMultipartFilename => write!(f, "invalid multipart filename"), 54 | } 55 | } 56 | } 57 | 58 | impl error::Error for Error {} 59 | -------------------------------------------------------------------------------- /lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate helps writing bots for the messenger Telegram. 2 | //! See [readme](https://github.com/telegram-rs/telegram-bot) for details. 3 | 4 | mod api; 5 | mod errors; 6 | mod macros; 7 | mod stream; 8 | 9 | pub mod connector; 10 | pub mod prelude; 11 | pub mod types; 12 | pub mod util; 13 | 14 | pub use self::api::Api; 15 | pub use self::errors::Error; 16 | pub use prelude::*; 17 | pub use stream::UpdatesStream; 18 | pub use types::*; 19 | -------------------------------------------------------------------------------- /lib/src/macros.rs: -------------------------------------------------------------------------------- 1 | /// Create a reply markup. 2 | /// 3 | /// # Examples 4 | /// ``` 5 | /// # #[macro_use] extern crate telegram_bot; 6 | /// # fn main() { 7 | /// let reply_keyboard = reply_markup!(reply_keyboard, selective, one_time, resize, 8 | /// ["button", "button"], 9 | /// ["button" contact], // Request contact 10 | /// ["button" location] // Request location 11 | /// ); 12 | /// 13 | /// let remove_keyboard = reply_markup!(remove_keyboard); 14 | /// let selective_remove_keyboard = reply_markup!(remove_keyboard, selective); 15 | /// 16 | /// let force_reply = reply_markup!(force_reply); 17 | /// let selective_force_reply = reply_markup!(force_reply, selective); 18 | /// 19 | /// let inline_keyboard = reply_markup!(inline_keyboard, 20 | /// ["button" callback "0,0", "button" callback "0,1"], 21 | /// ["button" callback "1,0", "button" callback "1,1", "button" callback "1,2"] 22 | /// ); 23 | /// 24 | /// # drop(inline_keyboard); 25 | /// # drop(selective_force_reply); 26 | /// # drop(force_reply); 27 | /// # drop(selective_remove_keyboard); 28 | /// # drop(remove_keyboard); 29 | /// # drop(reply_keyboard); 30 | /// # } 31 | /// ``` 32 | #[macro_export] 33 | macro_rules! reply_markup { 34 | (remove_keyboard) => ({ 35 | $crate::ReplyKeyboardRemove::new() 36 | }); 37 | 38 | (remove_keyboard, selective) => ({ 39 | let mut keyboard = reply_markup!(remove_keyboard); 40 | keyboard.selective(); 41 | keyboard 42 | }); 43 | 44 | (force_reply) => ({ 45 | $crate::ForceReply::new() 46 | }); 47 | 48 | (force_reply, selective) => ({ 49 | let mut keyboard = reply_markup!(force_reply); 50 | keyboard.selective(); 51 | keyboard 52 | }); 53 | 54 | (reply_keyboard, $($content:tt)*) => ({ 55 | reply_markup!(_reply_keyboard, $($content)*) 56 | }); 57 | 58 | (_reply_keyboard, resize, $($content:tt)*) => ({ 59 | let mut keyboard = reply_markup!(_reply_keyboard, $($content)*); 60 | keyboard.resize_keyboard(); 61 | keyboard 62 | }); 63 | 64 | (_reply_keyboard, one_time, $($content:tt)*) => ({ 65 | let mut keyboard = reply_markup!(_reply_keyboard, $($content)*); 66 | keyboard.one_time_keyboard(); 67 | keyboard 68 | }); 69 | 70 | (_reply_keyboard, selective, $($content:tt)*) => ({ 71 | let mut keyboard = reply_markup!(_reply_keyboard, $($content)*); 72 | keyboard.selective(); 73 | keyboard 74 | }); 75 | 76 | (_reply_keyboard, $([$($content:tt)*]), *) => ( 77 | $crate::ReplyKeyboardMarkup::from(vec![$(reply_markup![_reply_keyboard_row, $($content)*]), *]) 78 | ); 79 | 80 | (_reply_keyboard_row, ($($acc:tt)*); $value:expr) => (vec![$($acc)* reply_markup!(_reply_keyboard_button, $value)]); 81 | (_reply_keyboard_row, ($($acc:tt)*); $value:expr, $($remaining:tt)*) => ( 82 | reply_markup!(_reply_keyboard_row, ($($acc)* reply_markup!(_reply_keyboard_button, $value),); $($remaining)*) 83 | 84 | ); 85 | 86 | (_reply_keyboard_row, ($($acc:tt)*); $value:tt $request:tt) => (vec![$($acc)* reply_markup!(_reply_keyboard_button, $value, $request)]); 87 | (_reply_keyboard_row, ($($acc:tt)*); $value:tt $request:tt, $($remaining: tt)*) => ( 88 | reply_markup!(_reply_keyboard_row, ($($acc)* reply_markup!(_reply_keyboard_button, $value, $request),); $($remaining)*) 89 | ); 90 | 91 | (_reply_keyboard_row, $($content:expr), *) => (vec![$(reply_markup!(_reply_keyboard_button, $content)), *]); 92 | (_reply_keyboard_row, $($content:tt)*) => (reply_markup!(_reply_keyboard_row, (); $($content)*)); 93 | 94 | (_reply_keyboard_button, $value:expr, contact) => ({ 95 | let mut button: $crate::KeyboardButton = reply_markup!(_reply_keyboard_button, $value); 96 | button.request_contact(); 97 | button 98 | }); 99 | 100 | (_reply_keyboard_button, $value:expr, location) => ({ 101 | let mut button: $crate::KeyboardButton = reply_markup!(_reply_keyboard_button, $value); 102 | button.request_location(); 103 | button 104 | }); 105 | (_reply_keyboard_button, $value:expr) => ($value.into()); 106 | 107 | (inline_keyboard, $([$($content:tt)*]), *) => ( 108 | $crate::InlineKeyboardMarkup::from(vec![$(reply_markup![_inline_keyboard_row, $($content)*]), *]) 109 | ); 110 | 111 | (_inline_keyboard_row, ($($acc:tt)*); $text:tt $request:tt $callback:tt) => ( 112 | vec![$($acc)* reply_markup!(_inline_keyboard_button, $request, $text, $callback)] 113 | ); 114 | (_inline_keyboard_row, $($text:tt $request:tt $callback:tt), *) => ( 115 | vec![$(reply_markup!(_inline_keyboard_button, $request, $text, $callback)), *] 116 | ); 117 | (_inline_keyboard_row, $($content:tt)*) => (reply_markup!(_inline_keyboard_row, (); $($content)*)); 118 | 119 | (_inline_keyboard_button, callback, $text:expr, $callback:expr) => ( 120 | $crate::InlineKeyboardButton::callback($text, $callback) 121 | ); 122 | (_inline_keyboard_button, url, $text:expr, $url:expr) => ( 123 | $crate::InlineKeyboardButton::url($text, $url) 124 | ); 125 | } 126 | 127 | #[cfg(test)] 128 | mod tests { 129 | use telegram_bot_raw::*; 130 | 131 | #[test] 132 | fn test_simple() { 133 | let mut remove_keyboard = ReplyKeyboardRemove::new(); 134 | assert_eq!(remove_keyboard, reply_markup!(remove_keyboard)); 135 | 136 | remove_keyboard.selective(); 137 | assert_eq!(remove_keyboard, reply_markup!(remove_keyboard, selective)); 138 | 139 | let mut force_reply = ForceReply::new(); 140 | assert_eq!(force_reply, reply_markup!(force_reply)); 141 | 142 | force_reply.selective(); 143 | assert_eq!(force_reply, reply_markup!(force_reply, selective)); 144 | } 145 | 146 | #[test] 147 | fn test_reply_keyboard() { 148 | let mut keyboard = ReplyKeyboardMarkup::new(); 149 | assert_eq!(keyboard, reply_markup!(reply_keyboard,)); 150 | 151 | keyboard.add_empty_row(); 152 | assert_eq!(keyboard, reply_markup!(reply_keyboard, [])); 153 | 154 | { 155 | let row = keyboard.add_empty_row(); 156 | row.push(KeyboardButton::new("foo")); 157 | row.push(KeyboardButton::new("bar")); 158 | } 159 | assert_eq!(keyboard, reply_markup!(reply_keyboard, [], ["foo", "bar"])); 160 | 161 | { 162 | let row = keyboard.add_empty_row(); 163 | row.push(KeyboardButton::new("baz")); 164 | } 165 | assert_eq!( 166 | keyboard, 167 | reply_markup!(reply_keyboard, [], ["foo", "bar"], ["baz"]) 168 | ); 169 | 170 | { 171 | let row = keyboard.add_empty_row(); 172 | 173 | let mut contact_button = KeyboardButton::new("contact"); 174 | contact_button.request_contact(); 175 | row.push(contact_button); 176 | 177 | let mut location_button = KeyboardButton::new("location"); 178 | location_button.request_location(); 179 | row.push(location_button) 180 | } 181 | assert_eq!( 182 | keyboard, 183 | reply_markup!( 184 | reply_keyboard, [], ["foo", "bar"], ["baz"], 185 | ["contact" contact, "location" location] 186 | ) 187 | ); 188 | 189 | { 190 | let row = keyboard.add_empty_row(); 191 | row.push(KeyboardButton::new("spam")); 192 | } 193 | assert_eq!( 194 | keyboard, 195 | reply_markup!( 196 | reply_keyboard, [], ["foo", "bar"], ["baz"], 197 | ["contact" contact, "location" location], 198 | ["spam"] 199 | ) 200 | ); 201 | 202 | keyboard.selective(); 203 | assert_eq!( 204 | keyboard, 205 | reply_markup!( 206 | reply_keyboard, selective, [], ["foo", "bar"], ["baz"], 207 | ["contact" contact, "location" location], 208 | ["spam"] 209 | ) 210 | ); 211 | 212 | keyboard.resize_keyboard(); 213 | assert_eq!( 214 | keyboard, 215 | reply_markup!( 216 | reply_keyboard, resize, selective, [], ["foo", "bar"], ["baz"], 217 | ["contact" contact, "location" location], 218 | ["spam"] 219 | ) 220 | ); 221 | 222 | keyboard.one_time_keyboard(); 223 | assert_eq!( 224 | keyboard, 225 | reply_markup!( 226 | reply_keyboard, resize, selective, one_time, [], ["foo", "bar"], ["baz"], 227 | ["contact" contact, "location" location], 228 | ["spam"] 229 | ) 230 | ); 231 | } 232 | 233 | #[test] 234 | fn test_inline_keyboard() { 235 | let mut markup = InlineKeyboardMarkup::new(); 236 | assert_eq!(markup, reply_markup!(inline_keyboard,)); 237 | 238 | markup.add_empty_row(); 239 | assert_eq!(markup, reply_markup!(inline_keyboard, [])); 240 | 241 | { 242 | let row = markup.add_empty_row(); 243 | row.push(InlineKeyboardButton::callback("foo", "bar")); 244 | row.push(InlineKeyboardButton::callback("baz", "quux")); 245 | } 246 | assert_eq!( 247 | markup, 248 | reply_markup!(inline_keyboard, [], ["foo" callback "bar", "baz" callback "quux"]) 249 | ); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /lib/src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! Telegram bot prelude. 2 | //! 3 | //! This module re-exports request builder traits from telegram-bot-raw. 4 | 5 | pub use telegram_bot_raw::CanAnswerCallbackQuery; 6 | pub use telegram_bot_raw::CanAnswerInlineQuery; 7 | pub use telegram_bot_raw::CanExportChatInviteLink; 8 | pub use telegram_bot_raw::CanLeaveChat; 9 | pub use telegram_bot_raw::CanSendChatAction; 10 | pub use telegram_bot_raw::{CanDeleteMessage, CanForwardMessage}; 11 | pub use telegram_bot_raw::{CanEditMessageCaption, CanEditMessageReplyMarkup, CanEditMessageText}; 12 | pub use telegram_bot_raw::{CanEditMessageLiveLocation, CanStopMessageLiveLocation}; 13 | pub use telegram_bot_raw::{CanGetChat, CanGetChatAdministrators, CanGetChatMembersCount}; 14 | pub use telegram_bot_raw::{CanGetChatMemberForChat, CanGetChatMemberForUser}; 15 | pub use telegram_bot_raw::{CanGetFile, CanGetUserProfilePhotos}; 16 | pub use telegram_bot_raw::{CanKickChatMemberForChat, CanKickChatMemberForUser}; 17 | pub use telegram_bot_raw::{CanPinMessage, CanUnpinMessage}; 18 | pub use telegram_bot_raw::{CanReplySendAudio, CanSendAudio}; 19 | pub use telegram_bot_raw::{CanReplySendContact, CanSendContact}; 20 | pub use telegram_bot_raw::{CanReplySendDocument, CanSendDocument}; 21 | pub use telegram_bot_raw::{CanReplySendLocation, CanSendLocation}; 22 | pub use telegram_bot_raw::{CanReplySendMessage, CanSendMessage}; 23 | pub use telegram_bot_raw::{CanReplySendPhoto, CanSendPhoto}; 24 | pub use telegram_bot_raw::{CanReplySendPoll, CanSendPoll, CanStopPoll}; 25 | pub use telegram_bot_raw::{CanReplySendVenue, CanSendVenue}; 26 | pub use telegram_bot_raw::{CanReplySendVideo, CanSendVideo}; 27 | pub use telegram_bot_raw::{CanUnbanChatMemberForChat, CanUnbanChatMemberForUser}; 28 | pub use telegram_bot_raw::{ToReplyRequest, ToRequest}; 29 | 30 | pub use crate::util::messages::{MessageGetFiles, MessageText}; 31 | -------------------------------------------------------------------------------- /lib/src/stream.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::max; 2 | use std::collections::VecDeque; 3 | use std::future::Future; 4 | use std::pin::Pin; 5 | use std::task::Context; 6 | use std::task::Poll; 7 | use std::time::Duration; 8 | 9 | use futures::Stream; 10 | 11 | use telegram_bot_raw::{AllowedUpdate, GetUpdates, Integer, Update}; 12 | 13 | use crate::api::Api; 14 | use crate::errors::Error; 15 | 16 | const TELEGRAM_LONG_POLL_TIMEOUT_SECONDS: u64 = 5; 17 | const TELEGRAM_LONG_POLL_LIMIT_MESSAGES: Integer = 100; 18 | const TELEGRAM_LONG_POLL_ERROR_DELAY_MILLISECONDS: u64 = 500; 19 | 20 | /// This type represents stream of Telegram API updates and uses 21 | /// long polling method under the hood. 22 | #[must_use = "streams do nothing unless polled"] 23 | pub struct UpdatesStream { 24 | api: Api, 25 | last_update: Integer, 26 | buffer: VecDeque, 27 | current_request: 28 | Option>, Error>> + Send>>>, 29 | timeout: Duration, 30 | allowed_updates: Vec, 31 | limit: Integer, 32 | error_delay: Duration, 33 | next_poll_id: usize, 34 | } 35 | 36 | impl Stream for UpdatesStream { 37 | type Item = Result; 38 | 39 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { 40 | let ref_mut = self.get_mut(); 41 | let poll_id = ref_mut.next_poll_id; 42 | ref_mut.next_poll_id += 1; 43 | let span = tracing::trace_span!("stream", poll_id = poll_id); 44 | let _enter = span.enter(); 45 | 46 | tracing::trace!("start stream polling"); 47 | 48 | if let Some(value) = ref_mut.buffer.pop_front() { 49 | tracing::trace!(update = ?value, "returning buffered update"); 50 | return Poll::Ready(Some(Ok(value))); 51 | } 52 | tracing::trace!("processing request"); 53 | 54 | let result = match ref_mut.current_request { 55 | None => { 56 | tracing::trace!("there is no current request"); 57 | Ok(false) 58 | } 59 | Some(ref mut current_request) => { 60 | let cc = current_request.as_mut(); 61 | let polled_update = cc.poll(cx); 62 | match polled_update { 63 | Poll::Pending => { 64 | tracing::trace!("request is pending"); 65 | return Poll::Pending; 66 | } 67 | Poll::Ready(Ok(None)) => { 68 | tracing::trace!("request timed out"); 69 | Ok(false) 70 | } 71 | Poll::Ready(Ok(Some(ref updates))) if updates.is_empty() => { 72 | tracing::trace!("request resolved to empty update list"); 73 | Ok(false) 74 | } 75 | Poll::Ready(Ok(Some(updates))) => { 76 | for update in updates { 77 | tracing::trace!(update = ?update, "processing update"); 78 | ref_mut.last_update = max(update.id, ref_mut.last_update); 79 | tracing::trace!(last_update = ref_mut.last_update); 80 | ref_mut.buffer.push_back(update) 81 | } 82 | 83 | Ok(true) 84 | } 85 | Poll::Ready(Err(err)) => { 86 | tracing::error!(error = %err, "request error"); 87 | Err(err) 88 | } 89 | } 90 | } 91 | }; 92 | 93 | match result { 94 | Err(err) => { 95 | let timeout = ref_mut.timeout + Duration::from_secs(1); 96 | let mut get_updates = GetUpdates::new(); 97 | get_updates 98 | .offset(ref_mut.last_update + 1) 99 | .timeout(ref_mut.error_delay.as_secs() as Integer) 100 | .limit(ref_mut.limit) 101 | .allowed_updates(&ref_mut.allowed_updates); 102 | tracing::trace!(request = ?get_updates, timeout=?timeout, "preparing new request"); 103 | 104 | let request = ref_mut.api.send_timeout(get_updates, timeout); 105 | ref_mut.current_request = Some(Box::pin(request)); 106 | return Poll::Ready(Some(Err(err))); 107 | } 108 | Ok(false) => { 109 | let timeout = ref_mut.timeout + Duration::from_secs(1); 110 | let mut get_updates = GetUpdates::new(); 111 | get_updates 112 | .offset(ref_mut.last_update + 1) 113 | .timeout(ref_mut.error_delay.as_secs() as Integer) 114 | .limit(ref_mut.limit) 115 | .allowed_updates(&ref_mut.allowed_updates); 116 | tracing::trace!(request = ?get_updates, timeout=?timeout, "preparing new request"); 117 | 118 | let request = ref_mut.api.send_timeout(get_updates, timeout); 119 | ref_mut.current_request = Some(Box::pin(request)); 120 | 121 | tracing::trace!("executing recursive call"); 122 | Pin::new(ref_mut).poll_next(cx) 123 | } 124 | Ok(true) => { 125 | tracing::trace!("dropping request"); 126 | ref_mut.current_request = None; 127 | tracing::trace!("executing recursive call"); 128 | Pin::new(ref_mut).poll_next(cx) 129 | } 130 | } 131 | } 132 | } 133 | 134 | impl UpdatesStream { 135 | /// create a new `UpdatesStream` instance. 136 | pub fn new(api: &Api) -> Self { 137 | UpdatesStream { 138 | api: api.clone(), 139 | last_update: 0, 140 | buffer: VecDeque::new(), 141 | current_request: None, 142 | timeout: Duration::from_secs(TELEGRAM_LONG_POLL_TIMEOUT_SECONDS), 143 | allowed_updates: Vec::new(), 144 | limit: TELEGRAM_LONG_POLL_LIMIT_MESSAGES, 145 | error_delay: Duration::from_millis(TELEGRAM_LONG_POLL_ERROR_DELAY_MILLISECONDS), 146 | next_poll_id: 0, 147 | } 148 | } 149 | 150 | /// Set timeout for long polling requests, this corresponds with `timeout` field 151 | /// in [getUpdates](https://core.telegram.org/bots/api#getupdates) method, 152 | /// also this stream sets an additional request timeout for `timeout + 1 second` 153 | /// in case of invalid Telegram API server behaviour. 154 | /// 155 | /// Default timeout is 5 seconds. 156 | pub fn timeout(&mut self, timeout: Duration) -> &mut Self { 157 | self.timeout = timeout; 158 | self 159 | } 160 | 161 | /// Set allowed updates to receive, this corresponds with `allowed_updates` field 162 | /// in [getUpdates](https://core.telegram.org/bots/api#getupdates) method. 163 | /// List the types of updates you want your bot to receive. For example, 164 | /// specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. 165 | /// See Update for a complete list of available update types. Specify an empty list to receive all 166 | /// updates regardless of type (default). If not specified, the previous setting will be used. 167 | /// 168 | /// Please note that this parameter doesn't affect updates created before the call to the getUpdates, 169 | /// so unwanted updates may be received for a short period of time. 170 | pub fn allowed_updates(&mut self, allowed_updates: &[AllowedUpdate]) -> &mut Self { 171 | self.allowed_updates = allowed_updates.to_vec(); 172 | self 173 | } 174 | 175 | /// Set limits the number of updates to be retrieved, this corresponds with `limit` field 176 | /// in [getUpdates](https://core.telegram.org/bots/api#getupdates) method. 177 | /// Values between 1—100 are accepted. 178 | /// 179 | /// Defaults to 100. 180 | pub fn limit(&mut self, limit: Integer) -> &mut Self { 181 | self.limit = limit; 182 | self 183 | } 184 | 185 | /// Set a delay between erroneous request and next request. 186 | /// This delay prevents busy looping in some cases. 187 | /// 188 | /// Default delay is 500 ms. 189 | pub fn error_delay(&mut self, delay: Duration) -> &mut Self { 190 | self.error_delay = delay; 191 | self 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /lib/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | //! Telegram bot types. 2 | 3 | pub mod requests; 4 | pub use requests::*; 5 | 6 | pub use telegram_bot_raw::types::*; 7 | -------------------------------------------------------------------------------- /lib/src/types/requests.rs: -------------------------------------------------------------------------------- 1 | //! Telegram Bot API methods. 2 | 3 | pub use telegram_bot_raw::requests::*; 4 | -------------------------------------------------------------------------------- /lib/src/util/messages.rs: -------------------------------------------------------------------------------- 1 | //! Utility traits that extends [`telegram_bot_raw::types::message`] 2 | //! 3 | //! [`telegram_bot_raw::types::message`]: ../../telegram_bot_raw/types/message/index.html 4 | 5 | use crate::prelude::CanGetFile; 6 | use crate::types::{ 7 | requests::get_file::GetFile, ChannelPost, Message, MessageKind, MessageOrChannelPost, 8 | }; 9 | 10 | /// A trait to obtain text from a message. 11 | /// 12 | /// For example, this will return the text from text messages, or the caption of a photo. 13 | pub trait MessageText { 14 | /// Obtain text from a message if available. 15 | fn text<'a>(&'a self) -> Option; 16 | } 17 | 18 | impl MessageText for MessageOrChannelPost { 19 | fn text<'a>(&'a self) -> Option { 20 | match self { 21 | MessageOrChannelPost::Message(msg) => msg.text(), 22 | MessageOrChannelPost::ChannelPost(post) => post.text(), 23 | } 24 | } 25 | } 26 | 27 | impl MessageText for Message { 28 | fn text<'a>(&'a self) -> Option { 29 | self.kind.text() 30 | } 31 | } 32 | 33 | impl MessageText for MessageKind { 34 | fn text<'a>(&'a self) -> Option { 35 | match self { 36 | MessageKind::Text { data, .. } => Some(data.to_owned()), 37 | MessageKind::Audio { data } => data.title.to_owned(), 38 | MessageKind::Document { data, caption } => { 39 | caption.clone().or_else(|| data.file_name.clone()) 40 | } 41 | MessageKind::Photo { caption, .. } => caption.to_owned(), 42 | MessageKind::Sticker { .. } => None, 43 | MessageKind::Video { caption, .. } => caption.to_owned(), 44 | MessageKind::Voice { .. } => None, 45 | MessageKind::VideoNote { .. } => None, 46 | MessageKind::Contact { data } => Some(data.first_name.to_owned()), 47 | MessageKind::Location { .. } => None, 48 | MessageKind::Poll { data } => Some(data.question.to_owned()), 49 | MessageKind::Venue { data } => Some(data.title.to_owned()), 50 | MessageKind::NewChatMembers { .. } => None, 51 | MessageKind::LeftChatMember { .. } => None, 52 | MessageKind::NewChatTitle { data } => Some(data.to_owned()), 53 | MessageKind::NewChatPhoto { .. } => None, 54 | MessageKind::DeleteChatPhoto => None, 55 | MessageKind::GroupChatCreated => None, 56 | MessageKind::SupergroupChatCreated => None, 57 | MessageKind::ChannelChatCreated => None, 58 | MessageKind::MigrateToChatId { .. } => None, 59 | MessageKind::MigrateFromChatId { .. } => None, 60 | MessageKind::PinnedMessage { data } => data.text(), 61 | MessageKind::Unknown { .. } => None, 62 | } 63 | } 64 | } 65 | 66 | impl MessageText for ChannelPost { 67 | fn text<'a>(&'a self) -> Option { 68 | self.kind.text() 69 | } 70 | } 71 | 72 | /// A trait to obtain `GetFile` requests from a message. 73 | /// 74 | /// Many message kinds such as `Sticker` return a single `GetFile`. 75 | /// Message kinds like `Photo` might return more if an album is posted. 76 | /// A video, video note or document returns any thumbnail as well. 77 | pub trait MessageGetFiles { 78 | /// Obtain files from a message if available. 79 | fn get_files<'a>(&'a self) -> Option>; 80 | } 81 | 82 | impl MessageGetFiles for MessageOrChannelPost { 83 | fn get_files<'a>(&'a self) -> Option> { 84 | match self { 85 | MessageOrChannelPost::Message(msg) => msg.get_files(), 86 | MessageOrChannelPost::ChannelPost(post) => post.get_files(), 87 | } 88 | } 89 | } 90 | 91 | impl MessageGetFiles for Message { 92 | fn get_files<'a>(&'a self) -> Option> { 93 | self.kind.get_files() 94 | } 95 | } 96 | 97 | impl MessageGetFiles for MessageKind { 98 | fn get_files<'a>(&'a self) -> Option> { 99 | match self { 100 | MessageKind::Text { .. } => None, 101 | MessageKind::Audio { data } => Some(vec![data.get_file()]), 102 | MessageKind::Document { data, .. } => { 103 | let mut files = vec![data.get_file()]; 104 | if let Some(thumb) = &data.thumb { 105 | files.push(thumb.get_file()); 106 | } 107 | Some(files) 108 | } 109 | MessageKind::Photo { data, .. } => { 110 | Some(data.into_iter().map(|f| f.get_file()).collect()) 111 | } 112 | MessageKind::Sticker { data } => Some(vec![data.get_file()]), 113 | MessageKind::Video { data, .. } => { 114 | let mut files = vec![data.get_file()]; 115 | if let Some(thumb) = &data.thumb { 116 | files.push(thumb.get_file()); 117 | } 118 | Some(files) 119 | } 120 | MessageKind::Voice { data } => Some(vec![data.get_file()]), 121 | MessageKind::VideoNote { data, .. } => { 122 | let mut files = vec![data.get_file()]; 123 | if let Some(thumb) = &data.thumb { 124 | files.push(thumb.get_file()); 125 | } 126 | Some(files) 127 | } 128 | MessageKind::Contact { .. } => None, 129 | MessageKind::Location { .. } => None, 130 | MessageKind::Poll { .. } => None, 131 | MessageKind::Venue { .. } => None, 132 | MessageKind::NewChatMembers { .. } => None, 133 | MessageKind::LeftChatMember { .. } => None, 134 | MessageKind::NewChatTitle { .. } => None, 135 | MessageKind::NewChatPhoto { data } => { 136 | Some(data.into_iter().map(|f| f.get_file()).collect()) 137 | } 138 | MessageKind::DeleteChatPhoto => None, 139 | MessageKind::GroupChatCreated => None, 140 | MessageKind::SupergroupChatCreated => None, 141 | MessageKind::ChannelChatCreated => None, 142 | MessageKind::MigrateToChatId { .. } => None, 143 | MessageKind::MigrateFromChatId { .. } => None, 144 | MessageKind::PinnedMessage { .. } => None, 145 | MessageKind::Unknown { .. } => None, 146 | } 147 | } 148 | } 149 | 150 | impl MessageGetFiles for ChannelPost { 151 | fn get_files<'a>(&'a self) -> Option> { 152 | self.kind.get_files() 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /lib/src/util/mod.rs: -------------------------------------------------------------------------------- 1 | //! Traits that extends raw types functionality 2 | 3 | pub mod messages; 4 | -------------------------------------------------------------------------------- /raw/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "telegram-bot-raw" 3 | version = "0.9.0" 4 | authors = ["Lukas Kalbertodt ", "Fedor Gogolev ", "Gustavo Aguiar "] 5 | edition = "2018" 6 | 7 | description = "Telegram Bot API types" 8 | 9 | documentation = "https://docs.rs/telegram-bot-raw/" 10 | repository = "https://github.com/telegram-rs/telegram-bot" 11 | readme = "../README.md" 12 | 13 | keywords = ["telegram", "bot", "chat", "api"] 14 | categories = ["api-bindings"] 15 | license = "MIT" 16 | 17 | [dependencies] 18 | bytes = "1.0" 19 | serde = { version = "1", features = ["derive"] } 20 | serde_derive = "1" 21 | serde_json = "1" 22 | serde-value = "0.7.0" 23 | -------------------------------------------------------------------------------- /raw/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate serde_derive; 3 | 4 | pub mod requests; 5 | pub mod types; 6 | pub mod url; 7 | 8 | pub use crate::requests::*; 9 | pub use crate::types::*; 10 | pub use crate::url::*; 11 | -------------------------------------------------------------------------------- /raw/src/requests/_base/_base.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | pub trait RequestType { 5 | type Options; 6 | type Request; 7 | 8 | fn serialize(options: Self::Options, request: &Self::Request) -> Result; 9 | } 10 | 11 | pub trait ResponseType { 12 | type Type; 13 | 14 | fn deserialize(resp: HttpResponse) -> Result; 15 | } 16 | 17 | pub trait Request { 18 | type Type: RequestType; 19 | type Response: ResponseType + 'static; 20 | 21 | fn serialize(&self) -> Result; 22 | 23 | fn detach(&self) -> DetachedRequest { 24 | DetachedRequest { 25 | http_request: self.serialize(), 26 | phantom: ::std::marker::PhantomData, 27 | } 28 | } 29 | } 30 | 31 | impl<'a, Req: Request> Request for &'a Req { 32 | type Type = Req::Type; 33 | type Response = Req::Response; 34 | 35 | fn serialize(&self) -> Result { 36 | (*self).serialize() 37 | } 38 | } 39 | 40 | impl<'a, Req: Request> Request for &'a mut Req { 41 | type Type = Req::Type; 42 | type Response = Req::Response; 43 | 44 | fn serialize(&self) -> Result { 45 | (**self).serialize() 46 | } 47 | } 48 | 49 | pub struct DetachedRequest { 50 | http_request: Result, 51 | phantom: ::std::marker::PhantomData, 52 | } 53 | 54 | impl Request for DetachedRequest { 55 | type Type = DetachedRequestType; 56 | type Response = Resp; 57 | 58 | fn serialize(&self) -> Result { 59 | Ok(Self::Type::serialize((), &self.http_request)?) 60 | } 61 | } 62 | 63 | /// Use this trait to convert a complex type to corresponding request and send it to the chat. 64 | pub trait ToRequest<'b> { 65 | /// Request type. 66 | type Request: Request; 67 | 68 | /// Convert type to request and send it to the chat. 69 | fn to_request(&'b self, chat: C) -> Self::Request 70 | where 71 | C: ToChatRef; 72 | } 73 | 74 | /// Use this trait to convert a complex type to corresponding request and reply to the message. 75 | pub trait ToReplyRequest<'b> { 76 | /// Request type. 77 | type Request: Request; 78 | 79 | /// Convert type to request and reply to the message. 80 | fn to_reply_request(&'b self, message: M) -> Self::Request 81 | where 82 | M: ToMessageId + ToSourceChat; 83 | } 84 | -------------------------------------------------------------------------------- /raw/src/requests/_base/errors.rs: -------------------------------------------------------------------------------- 1 | use std::error; 2 | use std::fmt; 3 | 4 | use crate::types::*; 5 | 6 | #[derive(Debug)] 7 | pub struct Error(ErrorKind); 8 | 9 | #[derive(Debug)] 10 | pub(crate) enum ErrorKind { 11 | EmptyBody, 12 | TelegramError { 13 | description: String, 14 | parameters: Option, 15 | }, 16 | DetachedError(String), 17 | Json(::serde_json::Error), 18 | } 19 | 20 | impl From<::serde_json::Error> for ErrorKind { 21 | fn from(error: ::serde_json::Error) -> Self { 22 | ErrorKind::Json(error) 23 | } 24 | } 25 | 26 | impl From for Error { 27 | fn from(kind: ErrorKind) -> Self { 28 | Error(kind) 29 | } 30 | } 31 | 32 | impl fmt::Display for Error { 33 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 34 | match &self.0 { 35 | ErrorKind::EmptyBody => write!(f, "empty body"), 36 | ErrorKind::TelegramError { 37 | description, 38 | parameters, 39 | } => { 40 | f.write_str(&description)?; 41 | if let Some(parameters) = parameters { 42 | if let Some(chat_id) = parameters.migrate_to_chat_id { 43 | write!(f, ", migrate to chat id: {}", chat_id)?; 44 | } 45 | if let Some(seconds) = parameters.retry_after { 46 | write!(f, ", retry after: {}", seconds)?; 47 | } 48 | } 49 | Ok(()) 50 | } 51 | ErrorKind::DetachedError(s) => f.write_str(&s), 52 | ErrorKind::Json(error) => write!(f, "{}", error), 53 | } 54 | } 55 | } 56 | 57 | impl error::Error for Error {} 58 | -------------------------------------------------------------------------------- /raw/src/requests/_base/http.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use bytes::Bytes; 4 | 5 | use crate::types::Text; 6 | use crate::url::telegram_api_url; 7 | 8 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] 9 | pub enum RequestUrl { 10 | Method(&'static str), 11 | } 12 | 13 | impl RequestUrl { 14 | pub fn method(method: &'static str) -> Self { 15 | RequestUrl::Method(method) 16 | } 17 | 18 | pub fn url(&self, token: &str) -> String { 19 | match self { 20 | &RequestUrl::Method(method) => format!("{}bot{}/{}", telegram_api_url(), token, method), 21 | } 22 | } 23 | } 24 | 25 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] 26 | pub enum Method { 27 | Get, 28 | Post, 29 | } 30 | 31 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] 32 | pub enum MultipartValue { 33 | Text(Text), 34 | Path { path: Text, file_name: Option }, 35 | Data { file_name: Text, data: Bytes }, 36 | } 37 | 38 | pub type Multipart = Vec<(&'static str, MultipartValue)>; 39 | 40 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] 41 | pub enum Body { 42 | Empty, 43 | Multipart(Multipart), 44 | Json(String), 45 | #[doc(hidden)] 46 | __Nonexhaustive, 47 | } 48 | 49 | impl fmt::Display for Body { 50 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { 51 | match self { 52 | Body::Empty => "".fmt(f), 53 | Body::Multipart(multipart) => write!(f, "{:?}", multipart), 54 | Body::Json(s) => s.fmt(f), 55 | Body::__Nonexhaustive => unreachable!(), 56 | } 57 | } 58 | } 59 | 60 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] 61 | pub struct HttpRequest { 62 | pub url: RequestUrl, 63 | pub method: Method, 64 | pub body: Body, 65 | } 66 | 67 | impl HttpRequest { 68 | pub fn name(&self) -> &'static str { 69 | match self.url { 70 | RequestUrl::Method(method) => method, 71 | } 72 | } 73 | } 74 | 75 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] 76 | pub struct HttpResponse { 77 | pub body: Option>, 78 | } 79 | -------------------------------------------------------------------------------- /raw/src/requests/_base/mod.rs: -------------------------------------------------------------------------------- 1 | mod _base; 2 | pub use self::_base::*; 3 | 4 | mod errors; 5 | pub use self::errors::Error; 6 | pub(crate) use self::errors::ErrorKind; 7 | 8 | mod http; 9 | pub use self::http::{Body, Multipart, MultipartValue, RequestUrl}; 10 | pub use self::http::{HttpRequest, HttpResponse, Method}; 11 | 12 | #[macro_use] 13 | mod request_types; 14 | pub use self::request_types::*; 15 | 16 | mod response_types; 17 | pub use self::response_types::*; 18 | -------------------------------------------------------------------------------- /raw/src/requests/_base/request_types/detached.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] 4 | pub struct DetachedRequestType; 5 | 6 | impl RequestType for DetachedRequestType { 7 | type Options = (); 8 | type Request = Result; 9 | 10 | fn serialize(_options: Self::Options, request: &Self::Request) -> Result { 11 | match request { 12 | &Ok(ref req) => Ok(req.clone()), 13 | &Err(ref err) => Err(ErrorKind::DetachedError(err.to_string()).into()), 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /raw/src/requests/_base/request_types/json.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | use crate::requests::*; 4 | 5 | #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] 6 | pub struct JsonRequestType { 7 | phantom: ::std::marker::PhantomData, 8 | } 9 | 10 | impl RequestType for JsonRequestType { 11 | type Options = RequestUrl; 12 | type Request = Request; 13 | 14 | fn serialize(url: Self::Options, request: &Self::Request) -> Result { 15 | let body = serde_json::to_string(&request).map_err(ErrorKind::from)?; 16 | Ok(HttpRequest { 17 | url: url, 18 | method: Method::Post, 19 | body: Body::Json(body), 20 | }) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /raw/src/requests/_base/request_types/mod.rs: -------------------------------------------------------------------------------- 1 | mod json; 2 | pub use self::json::*; 3 | 4 | mod detached; 5 | pub use self::detached::*; 6 | 7 | #[macro_use] 8 | mod multipart; 9 | pub use self::multipart::*; 10 | -------------------------------------------------------------------------------- /raw/src/requests/_base/request_types/multipart.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] 4 | pub struct MultipartRequestType { 5 | phantom: ::std::marker::PhantomData, 6 | } 7 | 8 | pub trait ToMultipartValue { 9 | fn to_multipart_value(&self) -> MultipartValue; 10 | } 11 | 12 | pub trait ToMultipart { 13 | fn to_multipart(&self) -> Result; 14 | } 15 | 16 | impl RequestType for MultipartRequestType { 17 | type Options = RequestUrl; 18 | type Request = Request; 19 | 20 | fn serialize(url: Self::Options, request: &Self::Request) -> Result { 21 | let multipart = request.to_multipart()?; 22 | 23 | Ok(HttpRequest { 24 | url: url, 25 | method: Method::Post, 26 | body: Body::Multipart(multipart), 27 | }) 28 | } 29 | } 30 | 31 | #[macro_export] 32 | macro_rules! multipart_map { 33 | ($self:expr, $( ( $($opts:tt)* ) ; )*) => { 34 | let mut result = Vec::new(); 35 | $( 36 | multipart_field!($self, result, $($opts)*); 37 | )* 38 | Ok(result) 39 | } 40 | } 41 | 42 | macro_rules! multipart_field { 43 | ($self:expr, $result:expr, $field:ident($type:ident)) => {{ 44 | let value = &$self.$field; 45 | multipart_field!($self, $result, $field ($type) => value); 46 | }}; 47 | 48 | ($self:expr, $result:expr, $field:ident($type:ident), $($t:tt)*) => {{ 49 | let value = &$self.$field; 50 | multipart_field!($self, $result, $field ($type) => value, $($t)*); 51 | }}; 52 | 53 | ($self:expr, $result:expr, $field:ident($type:ident) => $val:expr,skip_if $cond:expr) => {{ 54 | if *$cond { 55 | multipart_field!($self, $result, $field ($type) => $val); 56 | } 57 | }}; 58 | 59 | ($self:expr, $result:expr, $field:ident($type:ident) => $val:expr,optional) => {{ 60 | let value = $val.as_ref(); 61 | if let Some(value) = value { 62 | multipart_field!($self, $result, $field ($type) => value); 63 | } 64 | }}; 65 | 66 | ($self:expr, $result:expr, $field:ident($type:ident) => $val:expr,when_true) => {{ 67 | let value = $val; 68 | multipart_field!($self, $result, $field ($type) => value, skip_if value); 69 | }}; 70 | 71 | ($self:expr, $result:expr, $field:ident(text) => $val:expr) => {{ 72 | let value = MultipartValue::Text($val.to_string().into()); 73 | $result.push((stringify!($field), value)); 74 | }}; 75 | 76 | ($self:expr, $result:expr, $field:ident(json) => $val:expr) => {{ 77 | let s = ::serde_json::to_string($val).map_err(ErrorKind::from)?; 78 | let value = MultipartValue::Text(s.into()); 79 | $result.push((stringify!($field), value)); 80 | }}; 81 | ($self:expr, $result:expr, $field:ident(raw) => $val:expr) => {{ 82 | let value = $val.to_multipart_value(); 83 | $result.push((stringify!($field), value)); 84 | }}; 85 | } 86 | -------------------------------------------------------------------------------- /raw/src/requests/_base/response_types/json.rs: -------------------------------------------------------------------------------- 1 | use serde::de::DeserializeOwned; 2 | 3 | use crate::requests::*; 4 | use crate::types::*; 5 | 6 | pub trait JsonResponse { 7 | type Raw; 8 | type Type; 9 | 10 | fn map(raw: Self::Raw) -> Self::Type; 11 | } 12 | 13 | pub struct JsonIdResponse { 14 | phantom: ::std::marker::PhantomData, 15 | } 16 | 17 | impl JsonResponse for JsonIdResponse { 18 | type Raw = Type; 19 | type Type = Type; 20 | 21 | fn map(raw: Self::Raw) -> Self::Type { 22 | raw 23 | } 24 | } 25 | 26 | pub struct JsonTrueToUnitResponse; 27 | 28 | impl JsonResponse for JsonTrueToUnitResponse { 29 | type Raw = True; 30 | type Type = (); 31 | 32 | fn map(_: Self::Raw) -> Self::Type { 33 | () 34 | } 35 | } 36 | 37 | impl ResponseType for Resp 38 | where 39 | ::Raw: DeserializeOwned, 40 | { 41 | type Type = ::Type; 42 | 43 | fn deserialize(resp: HttpResponse) -> Result { 44 | if let Some(body) = resp.body.as_ref() { 45 | let raw = serde_json::from_slice(body).map_err(ErrorKind::from)?; 46 | match raw { 47 | ResponseWrapper::Success { result } => Ok(::map(result)), 48 | ResponseWrapper::Error { 49 | description, 50 | parameters, 51 | } => Err(ErrorKind::TelegramError { 52 | description, 53 | parameters, 54 | } 55 | .into()), 56 | } 57 | } else { 58 | Err(ErrorKind::EmptyBody.into()) 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /raw/src/requests/_base/response_types/mod.rs: -------------------------------------------------------------------------------- 1 | mod json; 2 | pub use self::json::*; 3 | -------------------------------------------------------------------------------- /raw/src/requests/answer_callback_query.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::ops::Not; 3 | 4 | use crate::requests::*; 5 | use crate::types::*; 6 | 7 | /// Use this method to send answers to callback queries sent from inline keyboards. 8 | /// The answer will be displayed to the user as a notification at the top of 9 | /// the chat screen or as an alert. 10 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 11 | #[must_use = "requests do nothing unless sent"] 12 | pub struct AnswerCallbackQuery<'t> { 13 | callback_query_id: CallbackQueryId, 14 | #[serde(skip_serializing_if = "Option::is_none")] 15 | text: Option>, 16 | #[serde(skip_serializing_if = "Not::not")] 17 | show_alert: bool, 18 | #[serde(skip_serializing_if = "Option::is_none")] 19 | url: Option>, 20 | #[serde(skip_serializing_if = "Option::is_none")] 21 | cache_time: Option, 22 | } 23 | 24 | impl<'i, 't> Request for AnswerCallbackQuery<'t> { 25 | type Type = JsonRequestType; 26 | type Response = JsonTrueToUnitResponse; 27 | 28 | fn serialize(&self) -> Result { 29 | Self::Type::serialize(RequestUrl::method("answerCallbackQuery"), self) 30 | } 31 | } 32 | 33 | impl<'t> AnswerCallbackQuery<'t> { 34 | fn new(query: Q, text: T) -> Self 35 | where 36 | Q: ToCallbackQueryId, 37 | T: Into>, 38 | { 39 | Self { 40 | callback_query_id: query.to_callback_query_id(), 41 | text: Some(text.into()), 42 | show_alert: false, 43 | url: None, 44 | cache_time: None, 45 | } 46 | } 47 | 48 | fn acknowledge(query: Q) -> Self 49 | where 50 | Q: ToCallbackQueryId, 51 | { 52 | Self { 53 | callback_query_id: query.to_callback_query_id(), 54 | text: None, 55 | show_alert: false, 56 | url: None, 57 | cache_time: None, 58 | } 59 | } 60 | 61 | /// An alert will be shown by the client instead of a notification 62 | /// at the top of the chat screen. 63 | pub fn show_alert(&mut self) -> &mut Self { 64 | self.show_alert = true; 65 | self 66 | } 67 | 68 | /// URL that will be opened by the user's client. If you have created a 69 | /// Game and accepted the conditions via @Botfather, specify the URL 70 | /// that opens your game – note that this will only work if the query 71 | /// comes from a callback_game button. 72 | /// 73 | /// Otherwise, you may use links like t.me/your_bot?start=XXXX that open your bot with a parameter. 74 | pub fn url(&mut self, url: T) -> &mut Self 75 | where 76 | T: Into>, 77 | { 78 | self.url = Some(url.into()); 79 | self 80 | } 81 | 82 | /// The maximum amount of time in seconds that the result of the callback query 83 | /// may be cached client-side. Telegram apps will support caching starting in 84 | /// version 3.14. Defaults to 0. 85 | pub fn cache_time(&mut self, time: i64) -> &mut Self { 86 | self.cache_time = Some(time); 87 | self 88 | } 89 | } 90 | 91 | /// Send answers to callback queries sent from inline keyboards. 92 | pub trait CanAnswerCallbackQuery { 93 | fn answer<'t, T>(&self, text: T) -> AnswerCallbackQuery<'t> 94 | where 95 | T: Into>; 96 | fn acknowledge<'t>(&self) -> AnswerCallbackQuery<'t>; 97 | } 98 | 99 | impl CanAnswerCallbackQuery for Q 100 | where 101 | Q: ToCallbackQueryId, 102 | { 103 | fn answer<'t, T>(&self, text: T) -> AnswerCallbackQuery<'t> 104 | where 105 | T: Into>, 106 | { 107 | AnswerCallbackQuery::new(&self, text) 108 | } 109 | fn acknowledge<'t>(&self) -> AnswerCallbackQuery<'t> { 110 | AnswerCallbackQuery::acknowledge(&self) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /raw/src/requests/answer_inline_query.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Not; 2 | 3 | use crate::requests::*; 4 | use crate::types::*; 5 | 6 | #[derive(Serialize, Debug)] 7 | pub struct AnswerInlineQuery { 8 | inline_query_id: InlineQueryId, 9 | results: Vec, 10 | #[serde(skip_serializing_if = "Option::is_none")] 11 | cache_time: Option, 12 | #[serde(skip_serializing_if = "Not::not")] 13 | is_personal: bool, 14 | #[serde(skip_serializing_if = "Option::is_none")] 15 | next_offset: Option, 16 | #[serde(skip_serializing_if = "Option::is_none")] 17 | switch_pm_text: Option, 18 | #[serde(skip_serializing_if = "Option::is_none")] 19 | switch_pm_parameter: Option, 20 | } 21 | 22 | impl Request for AnswerInlineQuery { 23 | type Type = JsonRequestType; 24 | type Response = JsonTrueToUnitResponse; 25 | 26 | fn serialize(&self) -> Result { 27 | Self::Type::serialize(RequestUrl::method("answerInlineQuery"), self) 28 | } 29 | } 30 | 31 | pub trait CanAnswerInlineQuery { 32 | fn answer(self, results: Vec) -> AnswerInlineQuery; 33 | } 34 | 35 | impl CanAnswerInlineQuery for T 36 | where 37 | T: Into, 38 | { 39 | fn answer(self, results: Vec) -> AnswerInlineQuery { 40 | AnswerInlineQuery::new(self.into(), results) 41 | } 42 | } 43 | 44 | impl AnswerInlineQuery { 45 | pub fn new( 46 | inline_query_id: InlineQueryId, 47 | results: Vec, 48 | ) -> AnswerInlineQuery { 49 | AnswerInlineQuery { 50 | inline_query_id, 51 | results, 52 | cache_time: None, 53 | is_personal: false, 54 | next_offset: None, 55 | switch_pm_text: None, 56 | switch_pm_parameter: None, 57 | } 58 | } 59 | 60 | pub fn add_inline_result>(&mut self, result: T) { 61 | self.results.push(result.into()); 62 | } 63 | 64 | pub fn cache_time(&mut self, cache_time: Integer) -> &mut Self { 65 | self.cache_time = Some(cache_time); 66 | self 67 | } 68 | 69 | pub fn is_personal(&mut self) -> &mut Self { 70 | self.is_personal = true; 71 | self 72 | } 73 | 74 | pub fn next_offset(&mut self, next_offset: String) -> &mut Self { 75 | self.next_offset = Some(next_offset); 76 | self 77 | } 78 | 79 | pub fn switch_pm_text(&mut self, switch_pm_text: String) -> &mut Self { 80 | self.switch_pm_text = Some(switch_pm_text); 81 | self 82 | } 83 | 84 | pub fn switch_pm_parameter(&mut self, switch_pm_parameter: String) -> &mut Self { 85 | self.switch_pm_parameter = Some(switch_pm_parameter); 86 | self 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /raw/src/requests/delete_message.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | // Use this method to delete a message. 5 | // A message can only be deleted if it was sent less than 48 hours ago. 6 | // Any such recently sent outgoing message may be deleted. 7 | // Additionally, if the bot is an administrator in a group chat, it can delete any message. 8 | // If the bot is an administrator in a supergroup, it can delete messages from any 9 | // other user and service messages about people joining or leaving the 10 | // group (other types of service messages may only be removed by the group creator). 11 | // In channels, bots can only remove their own messages. 12 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 13 | #[must_use = "requests do nothing unless sent"] 14 | pub struct DeleteMessage { 15 | chat_id: ChatRef, 16 | message_id: MessageId, 17 | } 18 | 19 | impl Request for DeleteMessage { 20 | type Type = JsonRequestType; 21 | type Response = JsonTrueToUnitResponse; 22 | 23 | fn serialize(&self) -> Result { 24 | Self::Type::serialize(RequestUrl::method("deleteMessage"), self) 25 | } 26 | } 27 | 28 | impl DeleteMessage { 29 | pub fn new(chat: C, message_id: M) -> Self 30 | where 31 | C: ToChatRef, 32 | M: ToMessageId, 33 | { 34 | DeleteMessage { 35 | chat_id: chat.to_chat_ref(), 36 | message_id: message_id.to_message_id(), 37 | } 38 | } 39 | } 40 | 41 | /// Delete messages.. 42 | pub trait CanDeleteMessage { 43 | fn delete(&self) -> DeleteMessage; 44 | } 45 | 46 | impl CanDeleteMessage for M 47 | where 48 | M: ToMessageId + ToSourceChat, 49 | { 50 | fn delete(&self) -> DeleteMessage { 51 | DeleteMessage::new(self.to_source_chat(), self.to_message_id()) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /raw/src/requests/edit_message_caption.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use crate::requests::*; 4 | use crate::types::*; 5 | 6 | /// Use this method to edit captions of messages sent by the bot. 7 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 8 | #[must_use = "requests do nothing unless sent"] 9 | pub struct EditMessageCaption<'s> { 10 | chat_id: ChatRef, 11 | message_id: MessageId, 12 | caption: Cow<'s, str>, 13 | #[serde(skip_serializing_if = "Option::is_none")] 14 | reply_markup: Option, 15 | } 16 | 17 | impl<'s> Request for EditMessageCaption<'s> { 18 | type Type = JsonRequestType; 19 | type Response = JsonIdResponse; 20 | 21 | fn serialize(&self) -> Result { 22 | Self::Type::serialize(RequestUrl::method("editMessageCaption"), self) 23 | } 24 | } 25 | 26 | impl<'s> EditMessageCaption<'s> { 27 | pub fn new(chat: C, message_id: M, caption: T) -> Self 28 | where 29 | C: ToChatRef, 30 | M: ToMessageId, 31 | T: Into>, 32 | { 33 | EditMessageCaption { 34 | chat_id: chat.to_chat_ref(), 35 | message_id: message_id.to_message_id(), 36 | caption: caption.into(), 37 | reply_markup: None, 38 | } 39 | } 40 | 41 | pub fn reply_markup(&mut self, reply_markup: R) -> &mut Self 42 | where 43 | R: Into, 44 | { 45 | self.reply_markup = Some(reply_markup.into()); 46 | self 47 | } 48 | } 49 | 50 | /// Edit captions of messages sent by the bot. 51 | pub trait CanEditMessageCaption { 52 | fn edit_caption<'s, T>(&self, caption: T) -> EditMessageCaption<'s> 53 | where 54 | T: Into>; 55 | } 56 | 57 | impl CanEditMessageCaption for M 58 | where 59 | M: ToMessageId + ToSourceChat, 60 | { 61 | fn edit_caption<'s, T>(&self, caption: T) -> EditMessageCaption<'s> 62 | where 63 | T: Into>, 64 | { 65 | EditMessageCaption::new(self.to_source_chat(), self.to_message_id(), caption) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /raw/src/requests/edit_message_live_location.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | /// Use this method to edit live location messages sent by the bot. 5 | /// A location can be edited until its live_period expires or editing 6 | /// is explicitly disabled by a call to stopMessageLiveLocation. 7 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 8 | #[must_use = "requests do nothing unless sent"] 9 | pub struct EditMessageLiveLocation { 10 | chat_id: ChatRef, 11 | message_id: MessageId, 12 | latitude: Float, 13 | longitude: Float, 14 | #[serde(skip_serializing_if = "Option::is_none")] 15 | reply_markup: Option, 16 | } 17 | 18 | impl Request for EditMessageLiveLocation { 19 | type Type = JsonRequestType; 20 | type Response = JsonIdResponse; 21 | 22 | fn serialize(&self) -> Result { 23 | Self::Type::serialize(RequestUrl::method("editMessageLiveLocation"), self) 24 | } 25 | } 26 | 27 | impl EditMessageLiveLocation { 28 | pub fn new(chat: C, message_id: M, latitude: Float, longitude: Float) -> Self 29 | where 30 | C: ToChatRef, 31 | M: ToMessageId, 32 | { 33 | EditMessageLiveLocation { 34 | chat_id: chat.to_chat_ref(), 35 | message_id: message_id.to_message_id(), 36 | latitude: latitude, 37 | longitude: longitude, 38 | reply_markup: None, 39 | } 40 | } 41 | 42 | pub fn reply_markup(&mut self, reply_markup: R) -> &mut Self 43 | where 44 | R: Into, 45 | { 46 | self.reply_markup = Some(reply_markup.into()); 47 | self 48 | } 49 | } 50 | 51 | /// Edit live location messages sent by the bot. 52 | pub trait CanEditMessageLiveLocation { 53 | fn edit_live_location(&self, latitude: Float, longitude: Float) -> EditMessageLiveLocation; 54 | } 55 | 56 | impl CanEditMessageLiveLocation for M 57 | where 58 | M: ToMessageId + ToSourceChat, 59 | { 60 | fn edit_live_location(&self, latitude: Float, longitude: Float) -> EditMessageLiveLocation { 61 | EditMessageLiveLocation::new( 62 | self.to_source_chat(), 63 | self.to_message_id(), 64 | latitude, 65 | longitude, 66 | ) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /raw/src/requests/edit_message_reply_markup.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | /// Use this method to edit only the reply markup of messages sent by the bot. 5 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 6 | #[must_use = "requests do nothing unless sent"] 7 | pub struct EditMessageReplyMarkup { 8 | chat_id: ChatRef, 9 | message_id: MessageId, 10 | #[serde(skip_serializing_if = "Option::is_none")] 11 | reply_markup: Option, 12 | } 13 | 14 | impl Request for EditMessageReplyMarkup { 15 | type Type = JsonRequestType; 16 | type Response = JsonIdResponse; 17 | 18 | fn serialize(&self) -> Result { 19 | Self::Type::serialize(RequestUrl::method("editMessageReplyMarkup"), self) 20 | } 21 | } 22 | 23 | impl EditMessageReplyMarkup { 24 | pub fn new(chat: C, message_id: M, reply_markup: Option) -> Self 25 | where 26 | C: ToChatRef, 27 | M: ToMessageId, 28 | R: Into, 29 | { 30 | EditMessageReplyMarkup { 31 | chat_id: chat.to_chat_ref(), 32 | message_id: message_id.to_message_id(), 33 | reply_markup: reply_markup.map(|r| r.into()), 34 | } 35 | } 36 | } 37 | 38 | /// Edit reply markup of messages sent by the bot. 39 | pub trait CanEditMessageReplyMarkup { 40 | fn edit_reply_markup(&self, reply_markup: Option) -> EditMessageReplyMarkup 41 | where 42 | R: Into; 43 | } 44 | 45 | impl CanEditMessageReplyMarkup for M 46 | where 47 | M: ToMessageId + ToSourceChat, 48 | { 49 | fn edit_reply_markup(&self, reply_markup: Option) -> EditMessageReplyMarkup 50 | where 51 | R: Into, 52 | { 53 | EditMessageReplyMarkup::new(self.to_source_chat(), self.to_message_id(), reply_markup) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /raw/src/requests/edit_message_text.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::ops::Not; 3 | 4 | use crate::requests::*; 5 | use crate::types::*; 6 | 7 | /// Use this method to edit text messages sent by the bot. 8 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 9 | #[must_use = "requests do nothing unless sent"] 10 | pub struct EditMessageText<'s> { 11 | chat_id: ChatRef, 12 | message_id: MessageId, 13 | text: Cow<'s, str>, 14 | #[serde(skip_serializing_if = "Option::is_none")] 15 | parse_mode: Option, 16 | #[serde(skip_serializing_if = "Not::not")] 17 | disable_web_page_preview: bool, 18 | #[serde(skip_serializing_if = "Option::is_none")] 19 | reply_markup: Option, 20 | } 21 | 22 | impl<'s> Request for EditMessageText<'s> { 23 | type Type = JsonRequestType; 24 | type Response = JsonIdResponse; 25 | 26 | fn serialize(&self) -> Result { 27 | Self::Type::serialize(RequestUrl::method("editMessageText"), self) 28 | } 29 | } 30 | 31 | impl<'s> EditMessageText<'s> { 32 | pub fn new(chat: C, message_id: M, text: T) -> Self 33 | where 34 | C: ToChatRef, 35 | M: ToMessageId, 36 | T: Into>, 37 | { 38 | EditMessageText { 39 | chat_id: chat.to_chat_ref(), 40 | message_id: message_id.to_message_id(), 41 | text: text.into(), 42 | parse_mode: None, 43 | disable_web_page_preview: false, 44 | reply_markup: None, 45 | } 46 | } 47 | 48 | pub fn parse_mode(&mut self, parse_mode: ParseMode) -> &mut Self { 49 | self.parse_mode = Some(parse_mode); 50 | self 51 | } 52 | 53 | pub fn disable_preview(&mut self) -> &mut Self { 54 | self.disable_web_page_preview = true; 55 | self 56 | } 57 | 58 | pub fn reply_markup(&mut self, reply_markup: R) -> &mut Self 59 | where 60 | R: Into, 61 | { 62 | self.reply_markup = Some(reply_markup.into()); 63 | self 64 | } 65 | } 66 | 67 | /// Edit text of messages sent by the bot. 68 | pub trait CanEditMessageText { 69 | fn edit_text<'s, T>(&self, text: T) -> EditMessageText<'s> 70 | where 71 | T: Into>; 72 | } 73 | 74 | impl CanEditMessageText for M 75 | where 76 | M: ToMessageId + ToSourceChat, 77 | { 78 | fn edit_text<'s, T>(&self, text: T) -> EditMessageText<'s> 79 | where 80 | T: Into>, 81 | { 82 | EditMessageText::new(self.to_source_chat(), self.to_message_id(), text) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /raw/src/requests/export_chat_invite_link.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | /// Use this method to export chat invite links. 5 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 6 | #[must_use = "requests do nothing unless sent"] 7 | pub struct ExportChatInviteLink { 8 | chat_id: ChatRef, 9 | } 10 | 11 | impl Request for ExportChatInviteLink { 12 | type Type = JsonRequestType; 13 | type Response = JsonIdResponse; 14 | 15 | fn serialize(&self) -> Result { 16 | Self::Type::serialize(RequestUrl::method("exportChatInviteLink"), self) 17 | } 18 | } 19 | 20 | impl ExportChatInviteLink { 21 | pub fn new(chat: C) -> Self 22 | where 23 | C: ToChatRef, 24 | { 25 | ExportChatInviteLink { 26 | chat_id: chat.to_chat_ref(), 27 | } 28 | } 29 | } 30 | 31 | /// Export chat invite link. 32 | pub trait CanExportChatInviteLink { 33 | fn export_invite_link(&self) -> ExportChatInviteLink; 34 | } 35 | 36 | impl CanExportChatInviteLink for C 37 | where 38 | C: ToChatRef, 39 | { 40 | fn export_invite_link(&self) -> ExportChatInviteLink { 41 | ExportChatInviteLink::new(self) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /raw/src/requests/forward_message.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Not; 2 | 3 | use crate::requests::*; 4 | use crate::types::*; 5 | 6 | /// Use this method to forward messages of any kind. 7 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 8 | #[must_use = "requests do nothing unless sent"] 9 | pub struct ForwardMessage { 10 | chat_id: ChatRef, 11 | from_chat_id: ChatRef, 12 | #[serde(skip_serializing_if = "Not::not")] 13 | disable_notification: bool, 14 | message_id: MessageId, 15 | } 16 | 17 | impl Request for ForwardMessage { 18 | type Type = JsonRequestType; 19 | type Response = JsonIdResponse; 20 | 21 | fn serialize(&self) -> Result { 22 | Self::Type::serialize(RequestUrl::method("forwardMessage"), self) 23 | } 24 | } 25 | 26 | impl ForwardMessage { 27 | pub fn new(message: M, from: F, to: T) -> Self 28 | where 29 | M: ToMessageId, 30 | F: ToChatRef, 31 | T: ToChatRef, 32 | { 33 | ForwardMessage { 34 | chat_id: to.to_chat_ref(), 35 | from_chat_id: from.to_chat_ref(), 36 | disable_notification: false, 37 | message_id: message.to_message_id(), 38 | } 39 | } 40 | 41 | pub fn disable_notification(&mut self) -> &mut Self { 42 | self.disable_notification = true; 43 | self 44 | } 45 | } 46 | 47 | /// Forward message. 48 | pub trait CanForwardMessage { 49 | fn forward(&self, to: T) -> ForwardMessage 50 | where 51 | T: ToChatRef; 52 | } 53 | 54 | impl CanForwardMessage for M 55 | where 56 | M: ToMessageId + ToSourceChat, 57 | { 58 | fn forward(&self, to: T) -> ForwardMessage 59 | where 60 | T: ToChatRef, 61 | { 62 | ForwardMessage::new(self.to_message_id(), self.to_source_chat(), to) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /raw/src/requests/get_chat.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | /// Use this method to get up to date information about the chat. 5 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 6 | #[must_use = "requests do nothing unless sent"] 7 | pub struct GetChat { 8 | chat_id: ChatRef, 9 | } 10 | 11 | impl Request for GetChat { 12 | type Type = JsonRequestType; 13 | type Response = JsonIdResponse; 14 | 15 | fn serialize(&self) -> Result { 16 | Self::Type::serialize(RequestUrl::method("getChat"), self) 17 | } 18 | } 19 | 20 | impl GetChat { 21 | pub fn new(chat: C) -> Self 22 | where 23 | C: ToChatRef, 24 | { 25 | GetChat { 26 | chat_id: chat.to_chat_ref(), 27 | } 28 | } 29 | } 30 | 31 | /// Get up to date information about the chat. 32 | pub trait CanGetChat { 33 | fn get_chat(&self) -> GetChat; 34 | } 35 | 36 | impl CanGetChat for C 37 | where 38 | C: ToChatRef, 39 | { 40 | fn get_chat(&self) -> GetChat { 41 | GetChat::new(self) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /raw/src/requests/get_chat_administrators.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | /// Use this method to get a list of administrators in a chat. 5 | /// If the chat is a group or a supergroup and no administrators were appointed, 6 | /// only the creator will be returned. 7 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 8 | #[must_use = "requests do nothing unless sent"] 9 | pub struct GetChatAdministrators { 10 | chat_id: ChatRef, 11 | } 12 | 13 | impl Request for GetChatAdministrators { 14 | type Type = JsonRequestType; 15 | type Response = JsonIdResponse>; 16 | 17 | fn serialize(&self) -> Result { 18 | Self::Type::serialize(RequestUrl::method("getChatAdministrators"), self) 19 | } 20 | } 21 | 22 | impl GetChatAdministrators { 23 | pub fn new(chat: C) -> Self 24 | where 25 | C: ToChatRef, 26 | { 27 | GetChatAdministrators { 28 | chat_id: chat.to_chat_ref(), 29 | } 30 | } 31 | } 32 | 33 | /// Get a list of administrators in a chat. 34 | pub trait CanGetChatAdministrators { 35 | fn get_administrators(&self) -> GetChatAdministrators; 36 | } 37 | 38 | impl CanGetChatAdministrators for C 39 | where 40 | C: ToChatRef, 41 | { 42 | fn get_administrators(&self) -> GetChatAdministrators { 43 | GetChatAdministrators::new(self) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /raw/src/requests/get_chat_member.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | /// Use this method to get information about a member of a chat. 5 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 6 | #[must_use = "requests do nothing unless sent"] 7 | pub struct GetChatMember { 8 | chat_id: ChatRef, 9 | user_id: UserId, 10 | } 11 | 12 | impl Request for GetChatMember { 13 | type Type = JsonRequestType; 14 | type Response = JsonIdResponse; 15 | 16 | fn serialize(&self) -> Result { 17 | Self::Type::serialize(RequestUrl::method("getChatMember"), self) 18 | } 19 | } 20 | 21 | impl GetChatMember { 22 | pub fn new(chat: C, user: U) -> Self 23 | where 24 | C: ToChatRef, 25 | U: ToUserId, 26 | { 27 | GetChatMember { 28 | chat_id: chat.to_chat_ref(), 29 | user_id: user.to_user_id(), 30 | } 31 | } 32 | } 33 | 34 | /// Get information about a member of a chat. 35 | pub trait CanGetChatMemberForChat { 36 | fn get_member(&self, other: O) -> GetChatMember 37 | where 38 | O: ToUserId; 39 | } 40 | 41 | impl CanGetChatMemberForChat for C 42 | where 43 | C: ToChatRef, 44 | { 45 | fn get_member(&self, other: O) -> GetChatMember 46 | where 47 | O: ToUserId, 48 | { 49 | GetChatMember::new(self, other) 50 | } 51 | } 52 | 53 | /// Get information about a member of a chat. 54 | pub trait CanGetChatMemberForUser { 55 | fn get_member_from(&self, other: O) -> GetChatMember 56 | where 57 | O: ToChatRef; 58 | } 59 | 60 | impl CanGetChatMemberForUser for U 61 | where 62 | U: ToUserId, 63 | { 64 | fn get_member_from(&self, other: O) -> GetChatMember 65 | where 66 | O: ToChatRef, 67 | { 68 | GetChatMember::new(other, self) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /raw/src/requests/get_chat_members_count.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | /// Use this method to get the number of members in a chat. 5 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 6 | #[must_use = "requests do nothing unless sent"] 7 | pub struct GetChatMembersCount { 8 | chat_id: ChatRef, 9 | } 10 | 11 | impl Request for GetChatMembersCount { 12 | type Type = JsonRequestType; 13 | type Response = JsonIdResponse; 14 | 15 | fn serialize(&self) -> Result { 16 | Self::Type::serialize(RequestUrl::method("getChatMembersCount"), self) 17 | } 18 | } 19 | 20 | impl GetChatMembersCount { 21 | pub fn new(chat: C) -> Self 22 | where 23 | C: ToChatRef, 24 | { 25 | GetChatMembersCount { 26 | chat_id: chat.to_chat_ref(), 27 | } 28 | } 29 | } 30 | 31 | /// Get the number of members in a chat. 32 | pub trait CanGetChatMembersCount { 33 | fn get_members_count(&self) -> GetChatMembersCount; 34 | } 35 | 36 | impl CanGetChatMembersCount for C 37 | where 38 | C: ToChatRef, 39 | { 40 | fn get_members_count(&self) -> GetChatMembersCount { 41 | GetChatMembersCount::new(self) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /raw/src/requests/get_file.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | /// Use this method to get basic info about a file and prepare it for downloading. 5 | /// For the moment, bots can download files of up to 20MB in size. 6 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 7 | #[must_use = "requests do nothing unless sent"] 8 | pub struct GetFile { 9 | file_id: FileRef, 10 | } 11 | 12 | impl<'s> Request for GetFile { 13 | type Type = JsonRequestType; 14 | type Response = JsonIdResponse; 15 | 16 | fn serialize(&self) -> Result { 17 | Self::Type::serialize(RequestUrl::method("getFile"), self) 18 | } 19 | } 20 | 21 | impl GetFile { 22 | pub fn new(file: F) -> Self 23 | where 24 | F: ToFileRef, 25 | { 26 | Self { 27 | file_id: file.to_file_ref(), 28 | } 29 | } 30 | } 31 | 32 | /// Get basic info about a file and prepare it for downloading. 33 | pub trait CanGetFile { 34 | fn get_file(&self) -> GetFile; 35 | } 36 | 37 | impl CanGetFile for F 38 | where 39 | F: ToFileRef, 40 | { 41 | fn get_file(&self) -> GetFile { 42 | GetFile::new(self) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /raw/src/requests/get_me.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | /// A simple method for testing your bot's auth token. Requires no parameters. 5 | /// Returns basic information about the bot in form of a User object. 6 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 7 | #[must_use = "requests do nothing unless sent"] 8 | pub struct GetMe; 9 | 10 | impl Request for GetMe { 11 | type Type = JsonRequestType; 12 | type Response = JsonIdResponse; 13 | 14 | fn serialize(&self) -> Result { 15 | Self::Type::serialize(RequestUrl::method("getMe"), self) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /raw/src/requests/get_updates.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | /// Use this method to receive incoming updates using long polling. 5 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 6 | #[must_use = "requests do nothing unless sent"] 7 | pub struct GetUpdates { 8 | #[serde(skip_serializing_if = "Option::is_none")] 9 | offset: Option, 10 | #[serde(skip_serializing_if = "Option::is_none")] 11 | limit: Option, // TODO(knsd): Values between 1—100 are accepted 12 | #[serde(skip_serializing_if = "Option::is_none")] 13 | timeout: Option, // TODO(knsd): Should be positive 14 | allowed_updates: Vec, // TODO(knsd) BitSet? HashSet? BTreeSet? 15 | } 16 | 17 | impl Request for GetUpdates { 18 | type Type = JsonRequestType; 19 | type Response = JsonIdResponse>; 20 | 21 | fn serialize(&self) -> Result { 22 | Self::Type::serialize(RequestUrl::method("getUpdates"), self) 23 | } 24 | } 25 | 26 | impl GetUpdates { 27 | pub fn new() -> Self { 28 | GetUpdates { 29 | offset: None, 30 | limit: None, 31 | timeout: None, 32 | allowed_updates: Vec::new(), 33 | } 34 | } 35 | 36 | pub fn offset(&mut self, offset: Integer) -> &mut Self { 37 | self.offset = Some(offset); 38 | self 39 | } 40 | 41 | pub fn limit(&mut self, limit: Integer) -> &mut Self { 42 | self.limit = Some(limit); 43 | self 44 | } 45 | 46 | pub fn timeout(&mut self, timeout: Integer) -> &mut Self { 47 | self.timeout = Some(timeout); 48 | self 49 | } 50 | 51 | pub fn allowed_updates(&mut self, updates: &[AllowedUpdate]) -> &mut Self { 52 | self.allowed_updates = updates.to_vec(); 53 | self 54 | } 55 | } 56 | 57 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 58 | pub enum AllowedUpdate { 59 | #[serde(rename = "message")] 60 | Message, 61 | #[serde(rename = "edited_message")] 62 | EditedMessage, 63 | #[serde(rename = "channel_post")] 64 | ChannelPost, 65 | #[serde(rename = "edited_channel_post")] 66 | EditedChannelPost, 67 | #[serde(rename = "inline_query")] 68 | InlineQuery, 69 | #[serde(rename = "chosen_inline_query")] 70 | ChosenInlineResult, 71 | #[serde(rename = "callback_query")] 72 | CallbackQuery, 73 | #[serde(rename = "shipping_query")] 74 | ShippingQuery, 75 | #[serde(rename = "pre_checkout_query")] 76 | PreCheckoutQuery, 77 | } 78 | -------------------------------------------------------------------------------- /raw/src/requests/get_user_profile_photos.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | /// Use this method to get a list of profile pictures for a user. 5 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 6 | #[must_use = "requests do nothing unless sent"] 7 | pub struct GetUserProfilePhotos { 8 | user_id: UserId, 9 | offset: Option, 10 | limit: Option, 11 | } 12 | 13 | impl Request for GetUserProfilePhotos { 14 | type Type = JsonRequestType; 15 | type Response = JsonIdResponse; 16 | 17 | fn serialize(&self) -> Result { 18 | Self::Type::serialize(RequestUrl::method("getUserProfilePhotos"), self) 19 | } 20 | } 21 | 22 | impl GetUserProfilePhotos { 23 | pub fn new(user: U) -> Self 24 | where 25 | U: ToUserId, 26 | { 27 | GetUserProfilePhotos { 28 | user_id: user.to_user_id(), 29 | offset: None, 30 | limit: None, 31 | } 32 | } 33 | 34 | pub fn offset(&mut self, offset: Integer) -> &mut Self { 35 | self.offset = Some(offset); 36 | self 37 | } 38 | 39 | pub fn limit(&mut self, limit: Integer) -> &mut Self { 40 | self.limit = Some(limit); 41 | self 42 | } 43 | } 44 | 45 | /// Get a list of profile pictures for a user. 46 | pub trait CanGetUserProfilePhotos { 47 | fn get_user_profile_photos(&self) -> GetUserProfilePhotos; 48 | } 49 | 50 | impl<'b, U> CanGetUserProfilePhotos for U 51 | where 52 | U: ToUserId, 53 | { 54 | fn get_user_profile_photos(&self) -> GetUserProfilePhotos { 55 | GetUserProfilePhotos::new(self) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /raw/src/requests/kick_chat_member.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | /// Use this method to kick a user from a group or a supergroup. 5 | /// In the case of supergroups, the user will not be able to return to the group on 6 | /// their own using invite links, etc., unless unbanned first. 7 | /// The bot must be an administrator in the group for this to work. 8 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 9 | #[must_use = "requests do nothing unless sent"] 10 | pub struct KickChatMember { 11 | chat_id: ChatRef, 12 | user_id: UserId, 13 | } 14 | 15 | impl Request for KickChatMember { 16 | type Type = JsonRequestType; 17 | type Response = JsonTrueToUnitResponse; 18 | 19 | fn serialize(&self) -> Result { 20 | Self::Type::serialize(RequestUrl::method("kickChatMember"), self) 21 | } 22 | } 23 | 24 | impl KickChatMember { 25 | pub fn new(chat: C, user: U) -> Self 26 | where 27 | C: ToChatRef, 28 | U: ToUserId, 29 | { 30 | KickChatMember { 31 | chat_id: chat.to_chat_ref(), 32 | user_id: user.to_user_id(), 33 | } 34 | } 35 | } 36 | 37 | /// Kick a user from a group or a supergroup. 38 | pub trait CanKickChatMemberForChat { 39 | fn kick(&self, other: O) -> KickChatMember 40 | where 41 | O: ToUserId; 42 | } 43 | 44 | impl CanKickChatMemberForChat for C 45 | where 46 | C: ToChatRef, 47 | { 48 | fn kick(&self, other: O) -> KickChatMember 49 | where 50 | O: ToUserId, 51 | { 52 | KickChatMember::new(self, other) 53 | } 54 | } 55 | 56 | /// Kick a user from a group or a supergroup. 57 | pub trait CanKickChatMemberForUser { 58 | fn kick_from(&self, other: O) -> KickChatMember 59 | where 60 | O: ToChatRef; 61 | } 62 | 63 | impl CanKickChatMemberForUser for U 64 | where 65 | U: ToUserId, 66 | { 67 | fn kick_from(&self, other: O) -> KickChatMember 68 | where 69 | O: ToChatRef, 70 | { 71 | KickChatMember::new(other, self) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /raw/src/requests/leave_chat.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | /// Use this method for your bot to leave a group, supergroup or channel. 5 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 6 | #[must_use = "requests do nothing unless sent"] 7 | pub struct LeaveChat { 8 | chat_id: ChatRef, 9 | } 10 | 11 | impl Request for LeaveChat { 12 | type Type = JsonRequestType; 13 | type Response = JsonTrueToUnitResponse; 14 | 15 | fn serialize(&self) -> Result { 16 | Self::Type::serialize(RequestUrl::method("leaveChat"), self) 17 | } 18 | } 19 | 20 | impl LeaveChat { 21 | pub fn new(chat: C) -> Self 22 | where 23 | C: ToChatRef, 24 | { 25 | LeaveChat { 26 | chat_id: chat.to_chat_ref(), 27 | } 28 | } 29 | } 30 | 31 | /// Leave a group, supergroup or channel. 32 | pub trait CanLeaveChat { 33 | fn leave(&self) -> LeaveChat; 34 | } 35 | 36 | impl CanLeaveChat for C 37 | where 38 | C: ToChatRef, 39 | { 40 | fn leave(&self) -> LeaveChat { 41 | LeaveChat::new(self) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /raw/src/requests/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | pub mod _base; 3 | pub mod answer_callback_query; 4 | pub mod answer_inline_query; 5 | pub mod delete_message; 6 | pub mod edit_message_caption; 7 | pub mod edit_message_live_location; 8 | pub mod edit_message_reply_markup; 9 | pub mod edit_message_text; 10 | pub mod export_chat_invite_link; 11 | pub mod forward_message; 12 | pub mod get_chat; 13 | pub mod get_chat_administrators; 14 | pub mod get_chat_member; 15 | pub mod get_chat_members_count; 16 | pub mod get_file; 17 | pub mod get_me; 18 | pub mod get_updates; 19 | pub mod get_user_profile_photos; 20 | pub mod kick_chat_member; 21 | pub mod leave_chat; 22 | pub mod pin_chat_message; 23 | pub mod send_audio; 24 | pub mod send_chat_action; 25 | pub mod send_contact; 26 | pub mod send_document; 27 | pub mod send_location; 28 | pub mod send_message; 29 | pub mod send_photo; 30 | pub mod send_poll; 31 | pub mod send_venue; 32 | pub mod send_video; 33 | pub mod stop_message_live_location; 34 | pub mod stop_poll; 35 | pub mod unban_chat_member; 36 | pub mod unpin_chat_message; 37 | 38 | pub use self::_base::*; 39 | pub use self::answer_callback_query::*; 40 | pub use self::answer_inline_query::*; 41 | pub use self::delete_message::*; 42 | pub use self::edit_message_caption::*; 43 | pub use self::edit_message_live_location::*; 44 | pub use self::edit_message_reply_markup::*; 45 | pub use self::edit_message_text::*; 46 | pub use self::export_chat_invite_link::*; 47 | pub use self::forward_message::*; 48 | pub use self::get_chat::*; 49 | pub use self::get_chat_administrators::*; 50 | pub use self::get_chat_member::*; 51 | pub use self::get_chat_members_count::*; 52 | pub use self::get_file::*; 53 | pub use self::get_me::*; 54 | pub use self::get_updates::*; 55 | pub use self::get_user_profile_photos::*; 56 | pub use self::kick_chat_member::*; 57 | pub use self::leave_chat::*; 58 | pub use self::pin_chat_message::*; 59 | pub use self::send_audio::*; 60 | pub use self::send_chat_action::*; 61 | pub use self::send_contact::*; 62 | pub use self::send_document::*; 63 | pub use self::send_location::*; 64 | pub use self::send_message::*; 65 | pub use self::send_photo::*; 66 | pub use self::send_poll::*; 67 | pub use self::send_venue::*; 68 | pub use self::send_video::*; 69 | pub use self::stop_message_live_location::*; 70 | pub use self::stop_poll::*; 71 | pub use self::unban_chat_member::*; 72 | pub use self::unpin_chat_message::*; 73 | -------------------------------------------------------------------------------- /raw/src/requests/pin_chat_message.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Not; 2 | 3 | use crate::requests::*; 4 | use crate::types::*; 5 | 6 | /// Use this method to pin a message in a supergroup or a channel. 7 | /// The bot must be an administrator in the chat for this to work 8 | /// and must have the ‘can_pin_messages’ admin right in the supergroup 9 | /// or ‘can_edit_messages’ admin right in the channel. 10 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 11 | #[must_use = "requests do nothing unless sent"] 12 | pub struct PinChatMessage { 13 | chat_id: ChatRef, 14 | message_id: MessageId, 15 | #[serde(skip_serializing_if = "Not::not")] 16 | disable_notification: bool, 17 | } 18 | 19 | impl Request for PinChatMessage { 20 | type Type = JsonRequestType; 21 | type Response = JsonTrueToUnitResponse; 22 | 23 | fn serialize(&self) -> Result { 24 | Self::Type::serialize(RequestUrl::method("pinChatMessage"), self) 25 | } 26 | } 27 | 28 | impl PinChatMessage { 29 | fn new(chat: C, message: M) -> Self 30 | where 31 | C: ToChatRef, 32 | M: ToMessageId, 33 | { 34 | Self { 35 | chat_id: chat.to_chat_ref(), 36 | message_id: message.to_message_id(), 37 | disable_notification: false, 38 | } 39 | } 40 | 41 | pub fn disable_notification(&mut self) -> &mut Self { 42 | self.disable_notification = true; 43 | self 44 | } 45 | } 46 | 47 | pub trait CanPinMessage { 48 | fn pin(&self) -> PinChatMessage; 49 | } 50 | 51 | impl CanPinMessage for M 52 | where 53 | M: ToMessageId + ToSourceChat, 54 | { 55 | fn pin(&self) -> PinChatMessage { 56 | PinChatMessage::new(self.to_source_chat(), self.to_message_id()) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /raw/src/requests/send_audio.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use crate::requests::*; 4 | use crate::types::*; 5 | 6 | /// Use this method to send an audio 7 | #[derive(Debug, Clone, PartialEq, PartialOrd)] 8 | #[must_use = "requests do nothing unless sent"] 9 | pub struct SendAudio<'c, 'p, 't> { 10 | chat_id: ChatRef, 11 | audio: InputFile, 12 | caption: Option>, 13 | parse_mode: Option, 14 | duration: Option, 15 | performer: Option>, 16 | title: Option>, 17 | thumb: Option, 18 | reply_to_message_id: Option, 19 | disable_notification: bool, 20 | reply_markup: Option, 21 | } 22 | 23 | impl<'c, 'p, 't> ToMultipart for SendAudio<'c, 'p, 't> { 24 | fn to_multipart(&self) -> Result { 25 | multipart_map! { 26 | self, 27 | (chat_id (text)); 28 | (audio (raw)); 29 | (caption (text), optional); 30 | (parse_mode (text), optional); 31 | (duration (text), optional); 32 | (performer (text), optional); 33 | (title (text), optional); 34 | (thumb (raw), optional); 35 | (reply_to_message_id (text), optional); 36 | (disable_notification (text), when_true); 37 | (reply_markup (json), optional); 38 | } 39 | } 40 | } 41 | 42 | impl<'c, 'p, 't> Request for SendAudio<'c, 'p, 't> { 43 | type Type = MultipartRequestType; 44 | type Response = JsonIdResponse; 45 | 46 | fn serialize(&self) -> Result { 47 | Self::Type::serialize(RequestUrl::method("sendAudio"), self) 48 | } 49 | } 50 | 51 | impl<'c, 'p, 't> SendAudio<'c, 'p, 't> { 52 | pub fn new(chat: C, audio: V) -> Self 53 | where 54 | C: ToChatRef, 55 | V: Into, 56 | { 57 | Self { 58 | chat_id: chat.to_chat_ref(), 59 | audio: audio.into(), 60 | caption: None, 61 | parse_mode: None, 62 | duration: None, 63 | performer: None, 64 | title: None, 65 | thumb: None, 66 | reply_to_message_id: None, 67 | reply_markup: None, 68 | disable_notification: false, 69 | } 70 | } 71 | 72 | pub fn thumb(&mut self, thumb: V) -> &mut Self 73 | where 74 | V: Into, 75 | { 76 | self.thumb = Some(thumb.into().into()); 77 | self 78 | } 79 | 80 | pub fn caption(&mut self, caption: T) -> &mut Self 81 | where 82 | T: Into>, 83 | { 84 | self.caption = Some(caption.into()); 85 | self 86 | } 87 | 88 | pub fn parse_mode(&mut self, parse_mode: ParseMode) -> &mut Self { 89 | self.parse_mode = Some(parse_mode); 90 | self 91 | } 92 | 93 | pub fn duration(&mut self, duration: Integer) -> &mut Self { 94 | self.duration = Some(duration); 95 | self 96 | } 97 | 98 | pub fn performer(&mut self, performer: T) -> &mut Self 99 | where 100 | T: Into>, 101 | { 102 | self.performer = Some(performer.into()); 103 | self 104 | } 105 | 106 | pub fn title(&mut self, title: T) -> &mut Self 107 | where 108 | T: Into>, 109 | { 110 | self.title = Some(title.into()); 111 | self 112 | } 113 | 114 | pub fn reply_to(&mut self, to: R) -> &mut Self 115 | where 116 | R: ToMessageId, 117 | { 118 | self.reply_to_message_id = Some(to.to_message_id()); 119 | self 120 | } 121 | 122 | pub fn reply_markup(&mut self, reply_markup: R) -> &mut Self 123 | where 124 | R: Into, 125 | { 126 | self.reply_markup = Some(reply_markup.into()); 127 | self 128 | } 129 | 130 | pub fn disable_notification(&mut self) -> &mut Self { 131 | self.disable_notification = true; 132 | self 133 | } 134 | } 135 | 136 | /// Can reply with an audio 137 | pub trait CanReplySendAudio { 138 | fn audio_reply<'c, 'p, 't, T>(&self, audio: T) -> SendAudio<'c, 'p, 't> 139 | where 140 | T: Into; 141 | } 142 | 143 | impl CanReplySendAudio for M 144 | where 145 | M: ToMessageId + ToSourceChat, 146 | { 147 | fn audio_reply<'c, 'p, 't, T>(&self, audio: T) -> SendAudio<'c, 'p, 't> 148 | where 149 | T: Into, 150 | { 151 | let mut req = SendAudio::new(self.to_source_chat(), audio); 152 | req.reply_to(self); 153 | req 154 | } 155 | } 156 | 157 | /// Send an audio 158 | pub trait CanSendAudio { 159 | fn audio<'c, 'p, 't, T>(&self, audio: T) -> SendAudio<'c, 'p, 't> 160 | where 161 | T: Into; 162 | } 163 | 164 | impl CanSendAudio for M 165 | where 166 | M: ToChatRef, 167 | { 168 | fn audio<'c, 'p, 't, T>(&self, audio: T) -> SendAudio<'c, 'p, 't> 169 | where 170 | T: Into, 171 | { 172 | SendAudio::new(self.to_chat_ref(), audio) 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /raw/src/requests/send_chat_action.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | /// Strongly typed ChatAction. Instead of passing a String to the 5 | /// `chat_action` method, this is used. 6 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 7 | #[must_use = "requests do nothing unless sent"] 8 | pub enum ChatAction { 9 | #[serde(rename = "typing")] 10 | Typing, 11 | #[serde(rename = "upload_photo")] 12 | UploadPhoto, 13 | #[serde(rename = "record_video")] 14 | RecordVideo, 15 | #[serde(rename = "upload_video")] 16 | UploadVideo, 17 | #[serde(rename = "record_audio")] 18 | RecordAudio, 19 | #[serde(rename = "upload_audio")] 20 | UploadAudio, 21 | #[serde(rename = "upload_document")] 22 | UploadDocument, 23 | #[serde(rename = "find_location")] 24 | FindLocation, 25 | } 26 | 27 | /// Use this method when you need to tell the user that something is happening on the bot's side. 28 | /// The status is set for 5 seconds or less (when a message arrives from your bot, 29 | /// Telegram clients clear its typing status). 30 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 31 | pub struct SendChatAction { 32 | chat_id: ChatRef, 33 | action: ChatAction, 34 | } 35 | 36 | impl Request for SendChatAction { 37 | type Type = JsonRequestType; 38 | type Response = JsonTrueToUnitResponse; 39 | 40 | fn serialize(&self) -> Result { 41 | Self::Type::serialize(RequestUrl::method("sendChatAction"), self) 42 | } 43 | } 44 | 45 | impl SendChatAction { 46 | pub fn new(chat: C, action: ChatAction) -> Self 47 | where 48 | C: ToChatRef, 49 | { 50 | SendChatAction { 51 | chat_id: chat.to_chat_ref(), 52 | action: action, 53 | } 54 | } 55 | } 56 | 57 | /// Send `action` to a chat. 58 | pub trait CanSendChatAction { 59 | fn chat_action(&self, action: ChatAction) -> SendChatAction; 60 | } 61 | 62 | impl CanSendChatAction for C 63 | where 64 | C: ToChatRef, 65 | { 66 | fn chat_action(&self, action: ChatAction) -> SendChatAction { 67 | SendChatAction::new(self, action) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /raw/src/requests/send_contact.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::ops::Not; 3 | 4 | use crate::requests::*; 5 | use crate::types::*; 6 | 7 | /// Use this method to send phone contacts. 8 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 9 | #[must_use = "requests do nothing unless sent"] 10 | pub struct SendContact<'p, 'f, 'l> { 11 | chat_id: ChatRef, 12 | phone_number: Cow<'p, str>, 13 | first_name: Cow<'f, str>, 14 | #[serde(skip_serializing_if = "Option::is_none")] 15 | last_name: Option>, 16 | #[serde(skip_serializing_if = "Not::not")] 17 | disable_notification: bool, 18 | #[serde(skip_serializing_if = "Option::is_none")] 19 | reply_to_message_id: Option, 20 | #[serde(skip_serializing_if = "Option::is_none")] 21 | reply_markup: Option, 22 | } 23 | 24 | impl<'p, 'f, 'l> Request for SendContact<'p, 'f, 'l> { 25 | type Type = JsonRequestType; 26 | type Response = JsonIdResponse; 27 | 28 | fn serialize(&self) -> Result { 29 | Self::Type::serialize(RequestUrl::method("sendContact"), self) 30 | } 31 | } 32 | 33 | impl<'p, 'f, 'l> SendContact<'p, 'f, 'l> { 34 | pub fn new(chat: C, phone_number: P, first_name: F) -> Self 35 | where 36 | C: ToChatRef, 37 | P: Into>, 38 | F: Into>, 39 | { 40 | SendContact { 41 | chat_id: chat.to_chat_ref(), 42 | phone_number: phone_number.into(), 43 | first_name: first_name.into(), 44 | last_name: None, 45 | disable_notification: false, 46 | reply_to_message_id: None, 47 | reply_markup: None, 48 | } 49 | } 50 | 51 | pub fn last_name(&mut self, last_name: F) -> &mut Self 52 | where 53 | F: Into>, 54 | { 55 | self.last_name = Some(last_name.into()); 56 | self 57 | } 58 | 59 | pub fn disable_notification(&mut self) -> &mut Self { 60 | self.disable_notification = true; 61 | self 62 | } 63 | 64 | pub fn reply_to(&mut self, to: R) -> &mut Self 65 | where 66 | R: ToMessageId, 67 | { 68 | self.reply_to_message_id = Some(to.to_message_id()); 69 | self 70 | } 71 | 72 | pub fn reply_markup(&mut self, reply_markup: R) -> &mut Self 73 | where 74 | R: Into, 75 | { 76 | self.reply_markup = Some(reply_markup.into()); 77 | self 78 | } 79 | } 80 | 81 | /// Send phone contact. 82 | pub trait CanSendContact<'p, 'f, 'l> { 83 | fn contact(&self, phone_number: P, first_name: F) -> SendContact<'p, 'f, 'l> 84 | where 85 | P: Into>, 86 | F: Into>; 87 | } 88 | 89 | impl<'p, 'f, 'l, C> CanSendContact<'p, 'f, 'l> for C 90 | where 91 | C: ToChatRef, 92 | { 93 | fn contact(&self, phone_number: P, first_name: F) -> SendContact<'p, 'f, 'l> 94 | where 95 | P: Into>, 96 | F: Into>, 97 | { 98 | SendContact::new(self, phone_number, first_name) 99 | } 100 | } 101 | 102 | /// Reply with phone contact. 103 | pub trait CanReplySendContact { 104 | fn contact_reply<'p, 'f, 'l, P: 'p, F: 'f>( 105 | &self, 106 | phone_number: P, 107 | first_name: F, 108 | ) -> SendContact<'p, 'f, 'l> 109 | where 110 | P: Into>, 111 | F: Into>; 112 | } 113 | 114 | impl CanReplySendContact for M 115 | where 116 | M: ToMessageId + ToSourceChat, 117 | { 118 | fn contact_reply<'p, 'f, 'l, P: 'p, F: 'f>( 119 | &self, 120 | phone_number: P, 121 | first_name: F, 122 | ) -> SendContact<'p, 'f, 'l> 123 | where 124 | P: Into>, 125 | F: Into>, 126 | { 127 | let mut rq = self.to_source_chat().contact(phone_number, first_name); 128 | rq.reply_to(self.to_message_id()); 129 | rq 130 | } 131 | } 132 | 133 | impl<'b> ToRequest<'b> for Contact { 134 | type Request = SendContact<'b, 'b, 'b>; 135 | 136 | fn to_request(&'b self, chat: C) -> Self::Request 137 | where 138 | C: ToChatRef, 139 | { 140 | let mut rq = chat.contact(self.phone_number.as_str(), self.first_name.as_str()); 141 | if let Some(ref last_name) = self.last_name { 142 | rq.last_name(last_name.as_str()); 143 | } 144 | rq 145 | } 146 | } 147 | 148 | impl<'b> ToReplyRequest<'b> for Contact { 149 | type Request = SendContact<'b, 'b, 'b>; 150 | 151 | fn to_reply_request(&'b self, message: M) -> Self::Request 152 | where 153 | M: ToMessageId + ToSourceChat, 154 | { 155 | let mut rq = message.contact_reply(self.phone_number.as_str(), self.first_name.as_str()); 156 | if let Some(ref last_name) = self.last_name { 157 | rq.last_name(last_name.as_str()); 158 | } 159 | rq 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /raw/src/requests/send_document.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use crate::requests::*; 4 | use crate::types::*; 5 | 6 | /// Use this method to send general files. On success, the sent Message is returned. 7 | /// Bots can currently send files of any type of up to 50 MB in size, this limit may be changed in the future. 8 | #[derive(Debug, Clone, PartialEq, PartialOrd)] 9 | #[must_use = "requests do nothing unless sent"] 10 | pub struct SendDocument<'c> { 11 | chat_id: ChatRef, 12 | document: InputFile, 13 | thumb: Option, 14 | caption: Option>, 15 | parse_mode: Option, 16 | reply_to_message_id: Option, 17 | disable_notification: bool, 18 | reply_markup: Option, 19 | } 20 | 21 | impl<'c> ToMultipart for SendDocument<'c> { 22 | fn to_multipart(&self) -> Result { 23 | multipart_map! { 24 | self, 25 | (chat_id (text)); 26 | (document (raw)); 27 | (thumb (raw), optional); 28 | (caption (text), optional); 29 | (parse_mode (text), optional); 30 | (reply_to_message_id (text), optional); 31 | (disable_notification (text), when_true); 32 | (reply_markup (json), optional); 33 | } 34 | } 35 | } 36 | 37 | impl<'c> Request for SendDocument<'c> { 38 | type Type = MultipartRequestType; 39 | type Response = JsonIdResponse; 40 | 41 | fn serialize(&self) -> Result { 42 | Self::Type::serialize(RequestUrl::method("sendDocument"), self) 43 | } 44 | } 45 | 46 | impl<'c> SendDocument<'c> { 47 | pub fn new(chat: C, document: V) -> Self 48 | where 49 | C: ToChatRef, 50 | V: Into, 51 | { 52 | Self { 53 | chat_id: chat.to_chat_ref(), 54 | document: document.into(), 55 | thumb: None, 56 | caption: None, 57 | parse_mode: None, 58 | reply_to_message_id: None, 59 | reply_markup: None, 60 | disable_notification: false, 61 | } 62 | } 63 | 64 | pub fn thumb(&mut self, thumb: V) -> &mut Self 65 | where 66 | V: Into, 67 | { 68 | self.thumb = Some(thumb.into().into()); 69 | self 70 | } 71 | 72 | pub fn caption(&mut self, caption: T) -> &mut Self 73 | where 74 | T: Into>, 75 | { 76 | self.caption = Some(caption.into()); 77 | self 78 | } 79 | 80 | pub fn parse_mode(&mut self, parse_mode: ParseMode) -> &mut Self { 81 | self.parse_mode = Some(parse_mode); 82 | self 83 | } 84 | 85 | pub fn reply_to(&mut self, to: R) -> &mut Self 86 | where 87 | R: ToMessageId, 88 | { 89 | self.reply_to_message_id = Some(to.to_message_id()); 90 | self 91 | } 92 | 93 | pub fn disable_notification(&mut self) -> &mut Self { 94 | self.disable_notification = true; 95 | self 96 | } 97 | 98 | pub fn reply_markup(&mut self, reply_markup: R) -> &mut Self 99 | where 100 | R: Into, 101 | { 102 | self.reply_markup = Some(reply_markup.into()); 103 | self 104 | } 105 | } 106 | 107 | /// Can reply with a document 108 | pub trait CanReplySendDocument { 109 | fn document_reply<'c, T>(&self, document: T) -> SendDocument<'c> 110 | where 111 | T: Into; 112 | } 113 | 114 | impl CanReplySendDocument for M 115 | where 116 | M: ToMessageId + ToSourceChat, 117 | { 118 | fn document_reply<'c, T>(&self, document: T) -> SendDocument<'c> 119 | where 120 | T: Into, 121 | { 122 | let mut req = SendDocument::new(self.to_source_chat(), document); 123 | req.reply_to(self); 124 | req 125 | } 126 | } 127 | 128 | /// Send a document 129 | pub trait CanSendDocument { 130 | fn document<'c, T>(&self, document: T) -> SendDocument<'c> 131 | where 132 | T: Into; 133 | } 134 | 135 | impl CanSendDocument for M 136 | where 137 | M: ToChatRef, 138 | { 139 | fn document<'c, T>(&self, document: T) -> SendDocument<'c> 140 | where 141 | T: Into, 142 | { 143 | SendDocument::new(self.to_chat_ref(), document) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /raw/src/requests/send_location.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Not; 2 | 3 | use crate::requests::*; 4 | use crate::types::*; 5 | 6 | /// Use this method to send point on the map. 7 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 8 | #[must_use = "requests do nothing unless sent"] 9 | pub struct SendLocation { 10 | chat_id: ChatRef, 11 | latitude: Float, 12 | longitude: Float, 13 | #[serde(skip_serializing_if = "Option::is_none")] 14 | live_period: Option, 15 | #[serde(skip_serializing_if = "Not::not")] 16 | disable_notification: bool, 17 | #[serde(skip_serializing_if = "Option::is_none")] 18 | reply_to_message_id: Option, 19 | #[serde(skip_serializing_if = "Option::is_none")] 20 | reply_markup: Option, 21 | } 22 | 23 | impl Request for SendLocation { 24 | type Type = JsonRequestType; 25 | type Response = JsonIdResponse; 26 | 27 | fn serialize(&self) -> Result { 28 | Self::Type::serialize(RequestUrl::method("sendLocation"), self) 29 | } 30 | } 31 | 32 | impl SendLocation { 33 | pub fn new(chat: C, latitude: Float, longitude: Float) -> Self 34 | where 35 | C: ToChatRef, 36 | { 37 | SendLocation { 38 | chat_id: chat.to_chat_ref(), 39 | latitude: latitude, 40 | longitude: longitude, 41 | live_period: None, 42 | disable_notification: false, 43 | reply_to_message_id: None, 44 | reply_markup: None, 45 | } 46 | } 47 | 48 | /// Period in seconds for which the location will be updated, should be between 60 and 86400. 49 | pub fn live_period(&mut self, period: Integer) -> &mut Self { 50 | self.live_period = Some(period); 51 | self 52 | } 53 | 54 | pub fn disable_notification(&mut self) -> &mut Self { 55 | self.disable_notification = true; 56 | self 57 | } 58 | 59 | pub fn reply_to(&mut self, to: R) -> &mut Self 60 | where 61 | R: ToMessageId, 62 | { 63 | self.reply_to_message_id = Some(to.to_message_id()); 64 | self 65 | } 66 | 67 | pub fn reply_markup(&mut self, reply_markup: R) -> &mut Self 68 | where 69 | R: Into, 70 | { 71 | self.reply_markup = Some(reply_markup.into()); 72 | self 73 | } 74 | } 75 | 76 | /// Send point on the map. 77 | pub trait CanSendLocation { 78 | fn location(&self, latitude: Float, longitude: Float) -> SendLocation; 79 | } 80 | 81 | impl CanSendLocation for C 82 | where 83 | C: ToChatRef, 84 | { 85 | fn location(&self, latitude: Float, longitude: Float) -> SendLocation { 86 | SendLocation::new(self, latitude, longitude) 87 | } 88 | } 89 | 90 | /// Reply with point on the map. 91 | pub trait CanReplySendLocation { 92 | fn location_reply(&self, latitude: Float, longitude: Float) -> SendLocation; 93 | } 94 | 95 | impl CanReplySendLocation for M 96 | where 97 | M: ToMessageId + ToSourceChat, 98 | { 99 | fn location_reply(&self, latitude: Float, longitude: Float) -> SendLocation { 100 | let mut rq = self.to_source_chat().location(latitude, longitude); 101 | rq.reply_to(self.to_message_id()); 102 | rq 103 | } 104 | } 105 | 106 | impl<'b> ToRequest<'b> for Location { 107 | type Request = SendLocation; 108 | 109 | fn to_request(&'b self, chat: C) -> Self::Request 110 | where 111 | C: ToChatRef, 112 | { 113 | chat.location(self.latitude, self.longitude) 114 | } 115 | } 116 | 117 | impl<'b> ToReplyRequest<'b> for Location { 118 | type Request = SendLocation; 119 | 120 | fn to_reply_request(&'b self, message: M) -> Self::Request 121 | where 122 | M: ToMessageId + ToSourceChat, 123 | { 124 | message.location_reply(self.latitude, self.longitude) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /raw/src/requests/send_message.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::ops::Not; 3 | 4 | use crate::requests::*; 5 | use crate::types::*; 6 | 7 | /// Use this method to send text messages. 8 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 9 | #[must_use = "requests do nothing unless sent"] 10 | pub struct SendMessage<'s> { 11 | chat_id: ChatRef, 12 | text: Cow<'s, str>, 13 | #[serde(skip_serializing_if = "Option::is_none")] 14 | parse_mode: Option, 15 | #[serde(skip_serializing_if = "Not::not")] 16 | disable_web_page_preview: bool, 17 | #[serde(skip_serializing_if = "Not::not")] 18 | disable_notification: bool, 19 | #[serde(skip_serializing_if = "Option::is_none")] 20 | reply_to_message_id: Option, 21 | #[serde(skip_serializing_if = "Option::is_none")] 22 | reply_markup: Option, 23 | } 24 | 25 | impl<'c, 's> Request for SendMessage<'s> { 26 | type Type = JsonRequestType; 27 | type Response = JsonIdResponse; 28 | 29 | fn serialize(&self) -> Result { 30 | Self::Type::serialize(RequestUrl::method("sendMessage"), self) 31 | } 32 | } 33 | 34 | impl<'s> SendMessage<'s> { 35 | pub fn new(chat: C, text: T) -> Self 36 | where 37 | C: ToChatRef, 38 | T: Into>, 39 | { 40 | SendMessage { 41 | chat_id: chat.to_chat_ref(), 42 | text: text.into(), 43 | parse_mode: None, 44 | disable_web_page_preview: false, 45 | disable_notification: false, 46 | reply_to_message_id: None, 47 | reply_markup: None, 48 | } 49 | } 50 | 51 | pub fn parse_mode(&mut self, parse_mode: ParseMode) -> &mut Self { 52 | self.parse_mode = Some(parse_mode); 53 | self 54 | } 55 | 56 | pub fn disable_preview(&mut self) -> &mut Self { 57 | self.disable_web_page_preview = true; 58 | self 59 | } 60 | 61 | pub fn disable_notification(&mut self) -> &mut Self { 62 | self.disable_notification = true; 63 | self 64 | } 65 | 66 | pub fn reply_to(&mut self, to: R) -> &mut Self 67 | where 68 | R: ToMessageId, 69 | { 70 | self.reply_to_message_id = Some(to.to_message_id()); 71 | self 72 | } 73 | 74 | pub fn reply_markup(&mut self, reply_markup: R) -> &mut Self 75 | where 76 | R: Into, 77 | { 78 | self.reply_markup = Some(reply_markup.into()); 79 | self 80 | } 81 | } 82 | 83 | /// Send text message. 84 | pub trait CanSendMessage { 85 | fn text<'s, T>(&self, text: T) -> SendMessage<'s> 86 | where 87 | T: Into>; 88 | } 89 | 90 | impl CanSendMessage for C 91 | where 92 | C: ToChatRef, 93 | { 94 | fn text<'s, T>(&self, text: T) -> SendMessage<'s> 95 | where 96 | T: Into>, 97 | { 98 | SendMessage::new(self, text) 99 | } 100 | } 101 | 102 | /// Reply with text message. 103 | pub trait CanReplySendMessage { 104 | fn text_reply<'c, 's, T>(&self, text: T) -> SendMessage<'s> 105 | where 106 | T: Into>; 107 | } 108 | 109 | impl CanReplySendMessage for M 110 | where 111 | M: ToMessageId + ToSourceChat, 112 | { 113 | fn text_reply<'c, 's, T>(&self, text: T) -> SendMessage<'s> 114 | where 115 | T: Into>, 116 | { 117 | let mut rq = self.to_source_chat().text(text); 118 | rq.reply_to(self.to_message_id()); 119 | rq 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /raw/src/requests/send_photo.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use crate::requests::*; 4 | use crate::types::*; 5 | 6 | /// Use this method to send photos 7 | #[derive(Debug, Clone, PartialEq, PartialOrd)] 8 | #[must_use = "requests do nothing unless sent"] 9 | pub struct SendPhoto<'c> { 10 | chat_id: ChatRef, 11 | photo: InputFile, 12 | caption: Option>, 13 | parse_mode: Option, 14 | reply_to_message_id: Option, 15 | disable_notification: bool, 16 | reply_markup: Option, 17 | } 18 | 19 | impl<'c> ToMultipart for SendPhoto<'c> { 20 | fn to_multipart(&self) -> Result { 21 | multipart_map! { 22 | self, 23 | (chat_id (text)); 24 | (photo (raw)); 25 | (caption (text), optional); 26 | (parse_mode (text), optional); 27 | (reply_to_message_id (text), optional); 28 | (disable_notification (text), when_true); 29 | (reply_markup (json), optional); 30 | } 31 | } 32 | } 33 | 34 | impl<'c> Request for SendPhoto<'c> { 35 | type Type = MultipartRequestType; 36 | type Response = JsonIdResponse; 37 | 38 | fn serialize(&self) -> Result { 39 | Self::Type::serialize(RequestUrl::method("sendPhoto"), self) 40 | } 41 | } 42 | 43 | impl<'c> SendPhoto<'c> { 44 | pub fn new(chat: C, photo: V) -> Self 45 | where 46 | C: ToChatRef, 47 | V: Into, 48 | { 49 | Self { 50 | chat_id: chat.to_chat_ref(), 51 | photo: photo.into(), 52 | caption: None, 53 | parse_mode: None, 54 | reply_to_message_id: None, 55 | reply_markup: None, 56 | disable_notification: false, 57 | } 58 | } 59 | 60 | pub fn caption(&mut self, caption: T) -> &mut Self 61 | where 62 | T: Into>, 63 | { 64 | self.caption = Some(caption.into()); 65 | self 66 | } 67 | 68 | pub fn parse_mode(&mut self, parse_mode: ParseMode) -> &mut Self { 69 | self.parse_mode = Some(parse_mode); 70 | self 71 | } 72 | 73 | pub fn reply_to(&mut self, to: R) -> &mut Self 74 | where 75 | R: ToMessageId, 76 | { 77 | self.reply_to_message_id = Some(to.to_message_id()); 78 | self 79 | } 80 | 81 | pub fn disable_notification(&mut self) -> &mut Self { 82 | self.disable_notification = true; 83 | self 84 | } 85 | 86 | pub fn reply_markup(&mut self, reply_markup: R) -> &mut Self 87 | where 88 | R: Into, 89 | { 90 | self.reply_markup = Some(reply_markup.into()); 91 | self 92 | } 93 | } 94 | 95 | /// Can reply with an photo 96 | pub trait CanReplySendPhoto { 97 | fn photo_reply<'c, T>(&self, photo: T) -> SendPhoto<'c> 98 | where 99 | T: Into; 100 | } 101 | 102 | impl CanReplySendPhoto for M 103 | where 104 | M: ToMessageId + ToSourceChat, 105 | { 106 | fn photo_reply<'c, T>(&self, photo: T) -> SendPhoto<'c> 107 | where 108 | T: Into, 109 | { 110 | let mut req = SendPhoto::new(self.to_source_chat(), photo); 111 | req.reply_to(self); 112 | req 113 | } 114 | } 115 | 116 | /// Send an photo 117 | pub trait CanSendPhoto { 118 | fn photo<'c, T>(&self, photo: T) -> SendPhoto<'c> 119 | where 120 | T: Into; 121 | } 122 | 123 | impl CanSendPhoto for M 124 | where 125 | M: ToChatRef, 126 | { 127 | fn photo<'c, T>(&self, photo: T) -> SendPhoto<'c> 128 | where 129 | T: Into, 130 | { 131 | SendPhoto::new(self.to_chat_ref(), photo) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /raw/src/requests/send_poll.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::clone::Clone; 3 | use std::ops::Not; 4 | 5 | use crate::requests::*; 6 | use crate::types::*; 7 | 8 | /// Use this method to send polls. 9 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 10 | #[must_use = "requests do nothing unless sent"] 11 | pub struct SendPoll<'q, 'o, 'e> { 12 | chat_id: ChatRef, 13 | question: Cow<'q, str>, 14 | options: Vec>, 15 | #[serde(skip_serializing_if = "Clone::clone")] 16 | // This defaults to true, so don't skip serializing if false. 17 | is_anonymous: bool, 18 | #[serde(rename = "type", skip_serializing_if = "Option::is_none")] 19 | type_: Option, 20 | #[serde(skip_serializing_if = "Not::not")] 21 | allows_multiple_answers: bool, 22 | #[serde(skip_serializing_if = "Option::is_none")] 23 | // TODO: required for quiz polls 24 | correct_option_id: Option, 25 | #[serde(skip_serializing_if = "Option::is_none")] 26 | explanation: Option>, 27 | #[serde(skip_serializing_if = "Option::is_none")] 28 | explanation_parse_mode: Option, 29 | #[serde(skip_serializing_if = "Option::is_none")] 30 | open_period: Option, 31 | #[serde(skip_serializing_if = "Option::is_none")] 32 | close_date: Option, 33 | #[serde(skip_serializing_if = "Not::not")] 34 | is_closed: bool, 35 | #[serde(skip_serializing_if = "Option::is_none")] 36 | reply_to_message_id: Option, 37 | #[serde(skip_serializing_if = "Option::is_none")] 38 | reply_markup: Option, 39 | } 40 | 41 | impl<'q, 'o, 'e> Request for SendPoll<'q, 'o, 'e> { 42 | type Type = JsonRequestType; 43 | type Response = JsonIdResponse; 44 | 45 | fn serialize(&self) -> Result { 46 | Self::Type::serialize(RequestUrl::method("sendPoll"), self) 47 | } 48 | } 49 | 50 | impl<'q, 'o, 'e> SendPoll<'q, 'o, 'e> { 51 | // TODO: allow creating requests only with at least 2 options 52 | pub fn new(chat: C, question: Q, options: Vec) -> Self 53 | where 54 | C: ToChatRef, 55 | Q: Into>, 56 | O: Into>, 57 | { 58 | let mut req_options: Vec> = Vec::new(); 59 | 60 | for option in options { 61 | req_options.push(option.into()); 62 | } 63 | 64 | SendPoll { 65 | chat_id: chat.to_chat_ref(), 66 | question: question.into(), 67 | options: req_options, 68 | is_anonymous: true, 69 | type_: None, 70 | allows_multiple_answers: false, 71 | correct_option_id: None, 72 | explanation: None, 73 | explanation_parse_mode: None, 74 | open_period: None, 75 | close_date: None, 76 | is_closed: false, 77 | reply_to_message_id: None, 78 | reply_markup: None, 79 | } 80 | } 81 | 82 | pub fn add_option(&mut self, option: O) -> &mut Self 83 | where 84 | O: Into>, 85 | { 86 | self.options.push(option.into()); 87 | self 88 | } 89 | 90 | pub fn not_anonymous(&mut self) -> &mut Self { 91 | self.is_anonymous = false; 92 | self 93 | } 94 | 95 | pub fn quiz(&mut self) -> &mut Self { 96 | self.type_ = Some(PollType::Quiz); 97 | self 98 | } 99 | 100 | pub fn regular(&mut self) -> &mut Self { 101 | self.type_ = Some(PollType::Regular); 102 | self 103 | } 104 | 105 | pub fn allows_multiple_answers(&mut self) -> &mut Self { 106 | self.allows_multiple_answers = true; 107 | self 108 | } 109 | 110 | pub fn correct_option_id(&mut self, id: Integer) -> &mut Self { 111 | self.correct_option_id = Some(id); 112 | self 113 | } 114 | 115 | pub fn explanation(&mut self, text: E) -> &mut Self 116 | where 117 | E: Into>, 118 | { 119 | self.explanation = Some(text.into()); 120 | self 121 | } 122 | 123 | pub fn explanation_parse_mode(&mut self, parse_mode: ParseMode) -> &mut Self { 124 | self.explanation_parse_mode = Some(parse_mode); 125 | self 126 | } 127 | 128 | pub fn open_period(&mut self, period: Integer) -> &mut Self { 129 | self.open_period = Some(period); 130 | self 131 | } 132 | 133 | // TODO: some real date format? 134 | pub fn close_date(&mut self, date: Integer) -> &mut Self { 135 | self.close_date = Some(date); 136 | self 137 | } 138 | 139 | pub fn closed(&mut self) -> &mut Self { 140 | self.is_closed = true; 141 | self 142 | } 143 | 144 | pub fn reply_to(&mut self, to: R) -> &mut Self 145 | where 146 | R: ToMessageId, 147 | { 148 | self.reply_to_message_id = Some(to.to_message_id()); 149 | self 150 | } 151 | 152 | pub fn reply_markup(&mut self, reply_markup: R) -> &mut Self 153 | where 154 | R: Into, 155 | { 156 | self.reply_markup = Some(reply_markup.into()); 157 | self 158 | } 159 | } 160 | 161 | /// Send message with a poll. 162 | pub trait CanSendPoll { 163 | fn poll<'q, 'o, 'e, Q, O>(&self, question: Q, options: Vec) -> SendPoll<'q, 'o, 'e> 164 | where 165 | Q: Into>, 166 | O: Into>; 167 | } 168 | 169 | impl CanSendPoll for C 170 | where 171 | C: ToChatRef, 172 | { 173 | fn poll<'q, 'o, 'e, Q, O>(&self, question: Q, options: Vec) -> SendPoll<'q, 'o, 'e> 174 | where 175 | Q: Into>, 176 | O: Into>, 177 | { 178 | SendPoll::new(self, question, options) 179 | } 180 | } 181 | 182 | pub trait CanReplySendPoll { 183 | fn poll_reply<'q, 'o, 'e, Q, O>(&self, question: Q, options: Vec) -> SendPoll<'q, 'o, 'e> 184 | where 185 | Q: Into>, 186 | O: Into>; 187 | } 188 | 189 | impl CanReplySendPoll for M 190 | where 191 | M: ToMessageId + ToSourceChat, 192 | { 193 | fn poll_reply<'q, 'o, 'e, Q, O>(&self, question: Q, options: Vec) -> SendPoll<'q, 'o, 'e> 194 | where 195 | Q: Into>, 196 | O: Into>, 197 | { 198 | let mut rq = self.to_source_chat().poll(question, options); 199 | rq.reply_to(self.to_message_id()); 200 | rq 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /raw/src/requests/send_venue.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::ops::Not; 3 | 4 | use crate::requests::*; 5 | use crate::types::*; 6 | 7 | /// Use this method to send information about a venue. 8 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 9 | #[must_use = "requests do nothing unless sent"] 10 | pub struct SendVenue<'t, 'a, 'f> { 11 | chat_id: ChatRef, 12 | latitude: Float, 13 | longitude: Float, 14 | title: Cow<'t, str>, 15 | address: Cow<'a, str>, 16 | #[serde(skip_serializing_if = "Option::is_none")] 17 | foursquare_id: Option>, 18 | #[serde(skip_serializing_if = "Not::not")] 19 | disable_notification: bool, 20 | #[serde(skip_serializing_if = "Option::is_none")] 21 | reply_to_message_id: Option, 22 | #[serde(skip_serializing_if = "Option::is_none")] 23 | reply_markup: Option, 24 | } 25 | 26 | impl<'t, 'a, 'f> Request for SendVenue<'t, 'a, 'f> { 27 | type Type = JsonRequestType; 28 | type Response = JsonIdResponse; 29 | 30 | fn serialize(&self) -> Result { 31 | Self::Type::serialize(RequestUrl::method("sendVenue"), self) 32 | } 33 | } 34 | 35 | impl<'t, 'a, 'f> SendVenue<'t, 'a, 'f> { 36 | pub fn new(chat: C, latitude: Float, longitude: Float, title: T, address: A) -> Self 37 | where 38 | C: ToChatRef, 39 | T: Into>, 40 | A: Into>, 41 | { 42 | SendVenue { 43 | chat_id: chat.to_chat_ref(), 44 | latitude: latitude, 45 | longitude: longitude, 46 | title: title.into(), 47 | address: address.into(), 48 | disable_notification: false, 49 | foursquare_id: None, 50 | reply_to_message_id: None, 51 | reply_markup: None, 52 | } 53 | } 54 | 55 | pub fn disable_notification(&mut self) -> &mut Self { 56 | self.disable_notification = true; 57 | self 58 | } 59 | 60 | pub fn foursquare_id(&mut self, id: F) -> &mut Self 61 | where 62 | F: Into>, 63 | { 64 | self.foursquare_id = Some(id.into()); 65 | self 66 | } 67 | 68 | pub fn reply_to(&mut self, to: R) -> &mut Self 69 | where 70 | R: ToMessageId, 71 | { 72 | self.reply_to_message_id = Some(to.to_message_id()); 73 | self 74 | } 75 | 76 | pub fn reply_markup(&mut self, reply_markup: R) -> &mut Self 77 | where 78 | R: Into, 79 | { 80 | self.reply_markup = Some(reply_markup.into()); 81 | self 82 | } 83 | } 84 | 85 | /// Send information about a venue. 86 | pub trait CanSendVenue<'t, 'a, 'f> { 87 | fn venue( 88 | &self, 89 | latitude: Float, 90 | longitude: Float, 91 | title: T, 92 | address: A, 93 | ) -> SendVenue<'t, 'a, 'f> 94 | where 95 | T: Into>, 96 | A: Into>; 97 | } 98 | 99 | impl<'t, 'a, 'f, C> CanSendVenue<'t, 'a, 'f> for C 100 | where 101 | C: ToChatRef, 102 | { 103 | fn venue( 104 | &self, 105 | latitude: Float, 106 | longitude: Float, 107 | title: T, 108 | address: A, 109 | ) -> SendVenue<'t, 'a, 'f> 110 | where 111 | T: Into>, 112 | A: Into>, 113 | { 114 | SendVenue::new(self, latitude, longitude, title, address) 115 | } 116 | } 117 | 118 | /// Reply with information about a venue. 119 | pub trait CanReplySendVenue { 120 | fn venue_reply<'t, 'a, 'f, T, A>( 121 | &self, 122 | latitude: Float, 123 | longitude: Float, 124 | title: T, 125 | address: A, 126 | ) -> SendVenue<'t, 'a, 'f> 127 | where 128 | T: Into>, 129 | A: Into>; 130 | } 131 | 132 | impl CanReplySendVenue for M 133 | where 134 | M: ToMessageId + ToSourceChat, 135 | { 136 | fn venue_reply<'t, 'a, 'f, T, A>( 137 | &self, 138 | latitude: Float, 139 | longitude: Float, 140 | title: T, 141 | address: A, 142 | ) -> SendVenue<'t, 'a, 'f> 143 | where 144 | T: Into>, 145 | A: Into>, 146 | { 147 | let mut rq = self 148 | .to_source_chat() 149 | .venue(latitude, longitude, title, address); 150 | rq.reply_to(self.to_message_id()); 151 | rq 152 | } 153 | } 154 | 155 | impl<'b> ToRequest<'b> for Venue { 156 | type Request = SendVenue<'b, 'b, 'b>; 157 | 158 | fn to_request(&'b self, chat: C) -> Self::Request 159 | where 160 | C: ToChatRef, 161 | { 162 | let mut rq = chat.venue( 163 | self.location.latitude, 164 | self.location.longitude, 165 | self.title.as_str(), 166 | self.address.as_str(), 167 | ); 168 | if let Some(ref foursquare_id) = self.foursquare_id { 169 | rq.foursquare_id(foursquare_id.as_str()); 170 | } 171 | rq 172 | } 173 | } 174 | 175 | impl<'b> ToReplyRequest<'b> for Venue { 176 | type Request = SendVenue<'b, 'b, 'b>; 177 | 178 | fn to_reply_request(&'b self, message: M) -> Self::Request 179 | where 180 | M: ToMessageId + ToSourceChat, 181 | { 182 | let mut rq = message.venue_reply( 183 | self.location.latitude, 184 | self.location.longitude, 185 | self.title.as_str(), 186 | self.address.as_str(), 187 | ); 188 | if let Some(ref foursquare_id) = self.foursquare_id { 189 | rq.foursquare_id(foursquare_id.as_str()); 190 | } 191 | rq 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /raw/src/requests/send_video.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use crate::requests::*; 4 | use crate::types::*; 5 | 6 | /// Use this method to send an video 7 | #[derive(Debug, Clone, PartialEq, PartialOrd)] 8 | #[must_use = "requests do nothing unless sent"] 9 | pub struct SendVideo<'c> { 10 | chat_id: ChatRef, 11 | video: InputFile, 12 | caption: Option>, 13 | parse_mode: Option, 14 | duration: Option, 15 | width: Option, 16 | height: Option, 17 | supports_streaming: bool, 18 | thumb: Option, 19 | reply_to_message_id: Option, 20 | disable_notification: bool, 21 | reply_markup: Option, 22 | } 23 | 24 | impl<'c> ToMultipart for SendVideo<'c> { 25 | fn to_multipart(&self) -> Result { 26 | multipart_map! { 27 | self, 28 | (chat_id (text)); 29 | (video (raw)); 30 | (caption (text), optional); 31 | (parse_mode (text), optional); 32 | (duration (text), optional); 33 | (width (text), optional); 34 | (height (text), optional); 35 | (supports_streaming (text), when_true); 36 | (thumb (raw), optional); 37 | (reply_to_message_id (text), optional); 38 | (disable_notification (text), when_true); 39 | (reply_markup (json), optional); 40 | } 41 | } 42 | } 43 | 44 | impl<'c> Request for SendVideo<'c> { 45 | type Type = MultipartRequestType; 46 | type Response = JsonIdResponse; 47 | 48 | fn serialize(&self) -> Result { 49 | Self::Type::serialize(RequestUrl::method("sendVideo"), self) 50 | } 51 | } 52 | 53 | impl<'c> SendVideo<'c> { 54 | pub fn new(chat: C, video: V) -> Self 55 | where 56 | C: ToChatRef, 57 | V: Into, 58 | { 59 | Self { 60 | chat_id: chat.to_chat_ref(), 61 | video: video.into(), 62 | caption: None, 63 | parse_mode: None, 64 | duration: None, 65 | width: None, 66 | height: None, 67 | supports_streaming: false, 68 | thumb: None, 69 | reply_to_message_id: None, 70 | reply_markup: None, 71 | disable_notification: false, 72 | } 73 | } 74 | 75 | pub fn thumb(&mut self, thumb: V) -> &mut Self 76 | where 77 | V: Into, 78 | { 79 | self.thumb = Some(thumb.into().into()); 80 | self 81 | } 82 | 83 | pub fn caption(&mut self, caption: T) -> &mut Self 84 | where 85 | T: Into>, 86 | { 87 | self.caption = Some(caption.into()); 88 | self 89 | } 90 | 91 | pub fn parse_mode(&mut self, parse_mode: ParseMode) -> &mut Self { 92 | self.parse_mode = Some(parse_mode); 93 | self 94 | } 95 | 96 | pub fn duration(&mut self, duration: Integer) -> &mut Self { 97 | self.duration = Some(duration); 98 | self 99 | } 100 | 101 | pub fn width(&mut self, width: Integer) -> &mut Self { 102 | self.width = Some(width); 103 | self 104 | } 105 | 106 | pub fn height(&mut self, height: Integer) -> &mut Self { 107 | self.height = Some(height); 108 | self 109 | } 110 | 111 | pub fn supports_streaming(&mut self) -> &mut Self { 112 | self.supports_streaming = true; 113 | self 114 | } 115 | 116 | pub fn reply_to(&mut self, to: R) -> &mut Self 117 | where 118 | R: ToMessageId, 119 | { 120 | self.reply_to_message_id = Some(to.to_message_id()); 121 | self 122 | } 123 | 124 | pub fn reply_markup(&mut self, reply_markup: R) -> &mut Self 125 | where 126 | R: Into, 127 | { 128 | self.reply_markup = Some(reply_markup.into()); 129 | self 130 | } 131 | 132 | pub fn disable_notification(&mut self) -> &mut Self { 133 | self.disable_notification = true; 134 | self 135 | } 136 | } 137 | 138 | /// Can reply with an video 139 | pub trait CanReplySendVideo { 140 | fn video_reply<'c, T>(&self, video: T) -> SendVideo<'c> 141 | where 142 | T: Into; 143 | } 144 | 145 | impl CanReplySendVideo for M 146 | where 147 | M: ToMessageId + ToSourceChat, 148 | { 149 | fn video_reply<'c, T>(&self, video: T) -> SendVideo<'c> 150 | where 151 | T: Into, 152 | { 153 | let mut req = SendVideo::new(self.to_source_chat(), video); 154 | req.reply_to(self); 155 | req 156 | } 157 | } 158 | 159 | /// Send an video 160 | pub trait CanSendVideo { 161 | fn video<'c, T>(&self, video: T) -> SendVideo<'c> 162 | where 163 | T: Into; 164 | } 165 | 166 | impl CanSendVideo for M 167 | where 168 | M: ToChatRef, 169 | { 170 | fn video<'c, T>(&self, video: T) -> SendVideo<'c> 171 | where 172 | T: Into, 173 | { 174 | SendVideo::new(self.to_chat_ref(), video) 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /raw/src/requests/stop_message_live_location.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | /// Use this method to stop updating a live location message sent by the bot 5 | /// before live_period expires. 6 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 7 | #[must_use = "requests do nothing unless sent"] 8 | pub struct StopMessageLiveLocation { 9 | chat_id: ChatRef, 10 | message_id: MessageId, 11 | #[serde(skip_serializing_if = "Option::is_none")] 12 | reply_markup: Option, 13 | } 14 | 15 | impl Request for StopMessageLiveLocation { 16 | type Type = JsonRequestType; 17 | type Response = JsonIdResponse; 18 | 19 | fn serialize(&self) -> Result { 20 | Self::Type::serialize(RequestUrl::method("stopMessageLiveLocation"), self) 21 | } 22 | } 23 | 24 | impl StopMessageLiveLocation { 25 | pub fn new(chat: C, message_id: M) -> Self 26 | where 27 | C: ToChatRef, 28 | M: ToMessageId, 29 | { 30 | StopMessageLiveLocation { 31 | chat_id: chat.to_chat_ref(), 32 | message_id: message_id.to_message_id(), 33 | reply_markup: None, 34 | } 35 | } 36 | 37 | pub fn reply_markup(&mut self, reply_markup: R) -> &mut Self 38 | where 39 | R: Into, 40 | { 41 | self.reply_markup = Some(reply_markup.into()); 42 | self 43 | } 44 | } 45 | 46 | /// Stop updating a live location message sent by the bot. 47 | pub trait CanStopMessageLiveLocation { 48 | fn stop_live_location(&self) -> StopMessageLiveLocation; 49 | } 50 | 51 | impl CanStopMessageLiveLocation for M 52 | where 53 | M: ToMessageId + ToSourceChat, 54 | { 55 | fn stop_live_location(&self) -> StopMessageLiveLocation { 56 | StopMessageLiveLocation::new(self.to_source_chat(), self.to_message_id()) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /raw/src/requests/stop_poll.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | /// Use this method to stop a poll which was sent by the bot. 5 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 6 | #[must_use = "requests do nothing unless sent"] 7 | pub struct StopPoll { 8 | chat_id: ChatRef, 9 | message_id: MessageId, 10 | #[serde(skip_serializing_if = "Option::is_none")] 11 | reply_markup: Option, 12 | } 13 | 14 | impl Request for StopPoll { 15 | type Type = JsonRequestType; 16 | type Response = JsonIdResponse; 17 | 18 | fn serialize(&self) -> Result { 19 | Self::Type::serialize(RequestUrl::method("stopPoll"), self) 20 | } 21 | } 22 | 23 | impl StopPoll { 24 | pub fn new(chat: C, message: M) -> Self 25 | where 26 | C: ToChatRef, 27 | M: ToMessageId, 28 | { 29 | StopPoll { 30 | chat_id: chat.to_chat_ref(), 31 | message_id: message.to_message_id(), 32 | reply_markup: None, 33 | } 34 | } 35 | 36 | pub fn reply_markup(&mut self, reply_markup: R) -> &mut Self 37 | where 38 | R: Into, 39 | { 40 | self.reply_markup = Some(reply_markup.into()); 41 | self 42 | } 43 | } 44 | 45 | /// Stop a poll which was sent by the bot. 46 | pub trait CanStopPoll { 47 | fn stop_poll(&self) -> StopPoll; 48 | } 49 | 50 | impl CanStopPoll for M 51 | where 52 | M: ToMessageId + ToSourceChat, 53 | { 54 | fn stop_poll(&self) -> StopPoll { 55 | StopPoll::new(self.to_source_chat(), self.to_message_id()) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /raw/src/requests/unban_chat_member.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | /// Use this method to unban a previously kicked user in a supergroup or channel. 5 | /// The user will not return to the group or channel automatically, but will be able to 6 | /// join via link, etc. The bot must be an administrator in the group for this to work. 7 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 8 | #[must_use = "requests do nothing unless sent"] 9 | pub struct UnbanChatMember { 10 | chat_id: ChatRef, 11 | user_id: UserId, 12 | } 13 | 14 | impl Request for UnbanChatMember { 15 | type Type = JsonRequestType; 16 | type Response = JsonTrueToUnitResponse; 17 | 18 | fn serialize(&self) -> Result { 19 | Self::Type::serialize(RequestUrl::method("unbanChatMember"), self) 20 | } 21 | } 22 | 23 | impl UnbanChatMember { 24 | pub fn new(chat: C, user: U) -> Self 25 | where 26 | C: ToChatRef, 27 | U: ToUserId, 28 | { 29 | UnbanChatMember { 30 | chat_id: chat.to_chat_ref(), 31 | user_id: user.to_user_id(), 32 | } 33 | } 34 | } 35 | 36 | /// Unban a previously kicked user in a supergroup or channel. 37 | pub trait CanUnbanChatMemberForChat { 38 | fn unban(&self, other: O) -> UnbanChatMember 39 | where 40 | O: ToUserId; 41 | } 42 | 43 | impl CanUnbanChatMemberForChat for C 44 | where 45 | C: ToChatRef, 46 | { 47 | fn unban(&self, other: O) -> UnbanChatMember 48 | where 49 | O: ToUserId, 50 | { 51 | UnbanChatMember::new(self, other) 52 | } 53 | } 54 | 55 | /// Unban a previously kicked user in a supergroup or channel. 56 | pub trait CanUnbanChatMemberForUser { 57 | fn unban_in(&self, other: O) -> UnbanChatMember 58 | where 59 | O: ToChatRef; 60 | } 61 | 62 | impl CanUnbanChatMemberForUser for U 63 | where 64 | U: ToUserId, 65 | { 66 | fn unban_in(&self, other: O) -> UnbanChatMember 67 | where 68 | O: ToChatRef, 69 | { 70 | UnbanChatMember::new(other, self) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /raw/src/requests/unpin_chat_message.rs: -------------------------------------------------------------------------------- 1 | use crate::requests::*; 2 | use crate::types::*; 3 | 4 | ///Use this method to unpin a message in a supergroup or a channel. 5 | /// The bot must be an administrator in the chat for this to work 6 | /// and must have the ‘can_pin_messages’ admin right in the 7 | /// supergroup or ‘can_edit_messages’ admin right in the channel. 8 | #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)] 9 | #[must_use = "requests do nothing unless sent"] 10 | pub struct UnpinChatMessage { 11 | chat_id: ChatRef, 12 | } 13 | 14 | impl Request for UnpinChatMessage { 15 | type Type = JsonRequestType; 16 | type Response = JsonTrueToUnitResponse; 17 | 18 | fn serialize(&self) -> Result { 19 | Self::Type::serialize(RequestUrl::method("unpinChatMessage"), self) 20 | } 21 | } 22 | 23 | impl UnpinChatMessage { 24 | fn new(chat: C) -> Self 25 | where 26 | C: ToChatRef, 27 | { 28 | Self { 29 | chat_id: chat.to_chat_ref(), 30 | } 31 | } 32 | } 33 | 34 | pub trait CanUnpinMessage { 35 | fn unpin_message(&self) -> UnpinChatMessage; 36 | } 37 | 38 | impl CanUnpinMessage for C 39 | where 40 | C: ToChatRef, 41 | { 42 | fn unpin_message(&self) -> UnpinChatMessage { 43 | UnpinChatMessage::new(self) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /raw/src/types/callback_query.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | 3 | /// This object represents an incoming callback query from a callback button in an inline keyboard. 4 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)] 5 | pub struct CallbackQuery { 6 | /// Unique identifier for this query 7 | pub id: CallbackQueryId, 8 | /// Sender 9 | pub from: User, 10 | /// Message with the callback button that originated the query. 11 | /// Note that message content and message date will not be available if the message is too old 12 | pub message: Option, 13 | /// Identifier of the message sent via the bot in inline mode, that originated the query. 14 | pub inline_message_id: Option, 15 | /// Global identifier, uniquely corresponding to the chat to which the message 16 | /// with the callback button was sent. Useful for high scores in games. 17 | pub chat_instance: String, 18 | /// Data associated with the callback button. Be aware that a bad client can 19 | /// send arbitrary data in this field. 20 | pub data: Option, 21 | } 22 | -------------------------------------------------------------------------------- /raw/src/types/chat.rs: -------------------------------------------------------------------------------- 1 | use serde::de::{Deserialize, Deserializer, Error}; 2 | 3 | use crate::types::*; 4 | 5 | /// This object represents a Telegram user or bot. 6 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Deserialize)] 7 | pub struct User { 8 | /// Unique identifier for this user or bot. 9 | pub id: UserId, 10 | /// User‘s or bot’s first name. 11 | pub first_name: String, 12 | /// User‘s or bot’s last name. 13 | pub last_name: Option, 14 | /// User‘s or bot’s username. 15 | pub username: Option, 16 | /// True, if this user is a bot. 17 | pub is_bot: bool, 18 | /// IETF language tag of the user's language 19 | pub language_code: Option, 20 | } 21 | 22 | /// This object represents a group. 23 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Deserialize)] 24 | pub struct Group { 25 | /// Unique identifier for this chat. 26 | pub id: GroupId, 27 | /// Title, for supergroups, channels and group chats. 28 | pub title: String, 29 | /// True if a group has ‘All Members Are Admins’ enabled. 30 | pub all_members_are_administrators: bool, 31 | /// Invite link for this group, specific to this bot. 32 | /// You can generate a new invite link by using the 33 | /// export_invite_link method. 34 | pub invite_link: Option, 35 | } 36 | 37 | /// This object represents a supergroup. 38 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Deserialize)] 39 | pub struct Supergroup { 40 | /// Unique identifier for this chat. 41 | pub id: SupergroupId, 42 | /// Title, for supergroups, channels and group chats. 43 | pub title: String, 44 | /// Username for supergroup. 45 | pub username: Option, 46 | /// Invite link for this supergroup, specific to this bot. 47 | /// You can generate a new invite link by using the 48 | /// export_invite_link method. 49 | pub invite_link: Option, 50 | } 51 | 52 | /// This object represents a channel. 53 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Deserialize)] 54 | pub struct Channel { 55 | /// Unique identifier for this chat. 56 | pub id: ChannelId, 57 | /// Title, for supergroups, channels and group chats. 58 | pub title: String, 59 | /// Username for channel. 60 | pub username: Option, 61 | /// Invite link for this channel, specific to this bot. 62 | /// You can generate a new invite link by using the 63 | /// export_invite_link method. 64 | pub invite_link: Option, 65 | } 66 | 67 | /// This object represents a private, group or supergroup. 68 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] 69 | pub enum MessageChat { 70 | Private(User), 71 | Group(Group), 72 | Supergroup(Supergroup), 73 | #[doc(hidden)] 74 | Unknown(RawChat), 75 | } 76 | 77 | impl MessageChat { 78 | pub fn id(&self) -> ChatId { 79 | match *self { 80 | MessageChat::Private(ref x) => x.id.into(), 81 | MessageChat::Group(ref x) => x.id.into(), 82 | MessageChat::Supergroup(ref x) => x.id.into(), 83 | MessageChat::Unknown(ref x) => x.id.into(), 84 | } 85 | } 86 | } 87 | 88 | /// This object represents a chat. 89 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] 90 | pub enum Chat { 91 | Private(User), 92 | Group(Group), 93 | Supergroup(Supergroup), 94 | Channel(Channel), 95 | #[doc(hidden)] 96 | Unknown(RawChat), 97 | } 98 | 99 | impl Chat { 100 | pub fn id(&self) -> ChatId { 101 | match *self { 102 | Chat::Private(ref x) => x.id.into(), 103 | Chat::Group(ref x) => x.id.into(), 104 | Chat::Supergroup(ref x) => x.id.into(), 105 | Chat::Channel(ref x) => x.id.into(), 106 | Chat::Unknown(ref x) => x.id.into(), 107 | } 108 | } 109 | } 110 | 111 | impl<'de> Deserialize<'de> for Chat { 112 | fn deserialize(deserializer: D) -> Result 113 | where 114 | D: Deserializer<'de>, 115 | { 116 | let raw: RawChat = Deserialize::deserialize(deserializer)?; 117 | 118 | macro_rules! required_field { 119 | ($name:ident) => {{ 120 | match raw.$name { 121 | Some(val) => val, 122 | None => return Err(D::Error::missing_field(stringify!($name))), 123 | } 124 | }}; 125 | } 126 | 127 | Ok(match raw.type_.as_ref() { 128 | "private" => Chat::Private(User { 129 | id: raw.id.into(), 130 | username: raw.username, 131 | first_name: required_field!(first_name), 132 | last_name: raw.last_name, 133 | is_bot: false, 134 | language_code: raw.language_code, 135 | }), 136 | "group" => Chat::Group(Group { 137 | id: raw.id.into(), 138 | title: required_field!(title), 139 | all_members_are_administrators: required_field!(all_members_are_administrators), 140 | invite_link: raw.invite_link, 141 | }), 142 | "supergroup" => Chat::Supergroup(Supergroup { 143 | id: raw.id.into(), 144 | title: required_field!(title), 145 | username: raw.username, 146 | invite_link: raw.invite_link, 147 | }), 148 | "channel" => Chat::Channel(Channel { 149 | id: raw.id.into(), 150 | title: required_field!(title), 151 | username: raw.username, 152 | invite_link: raw.invite_link, 153 | }), 154 | _ => Chat::Unknown(raw), 155 | }) 156 | } 157 | } 158 | 159 | /// This object represents a chat, directly mapped. 160 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Deserialize)] 161 | pub struct RawChat { 162 | /// Unique identifier for this chat. 163 | pub id: Integer, 164 | /// Type of chat, can be either “private”, “group”, “supergroup” or “channel” 165 | #[serde(rename = "type")] 166 | pub type_: String, 167 | /// Title, for supergroups, channels and group chats 168 | pub title: Option, 169 | /// Username, for private chats, supergroups and channels if available 170 | pub username: Option, 171 | /// First name of the other party in a private chat 172 | pub first_name: Option, 173 | /// Last name of the other party in a private chat 174 | pub last_name: Option, 175 | /// Invite link for this chat, specific to this bot. 176 | /// Does not apply to private chats. 177 | pub invite_link: Option, 178 | /// IETF language tag of the other party in a private chat 179 | pub language_code: Option, 180 | /// True if a group has ‘All Members Are Admins’ enabled. 181 | pub all_members_are_administrators: Option, 182 | } 183 | -------------------------------------------------------------------------------- /raw/src/types/chat_invite_link.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | 3 | /// Represents an invite link for a chat. 4 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)] 5 | pub struct ChatInviteLink { 6 | /// The invite link. If the link was created by another chat administrator, then the second part of the link will be replaced with “…”. 7 | pub invite_link: String, 8 | /// Creator of the link 9 | pub creator: User, 10 | /// True, if the link is primary 11 | pub is_primary: bool, 12 | /// True, if the link is revoked 13 | pub is_revoked: bool, 14 | /// Point in time (Unix timestamp) when the link will expire or has been expired 15 | pub expire_date: Option, 16 | /// Maximum number of users that can be members of the chat simultaneously after joining the chat via this invite link; 1-99999 17 | pub member_limit: Option, 18 | } 19 | -------------------------------------------------------------------------------- /raw/src/types/chat_member.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use serde::de; 4 | use serde::de::{Deserialize, Deserializer, Visitor}; 5 | 6 | use crate::types::*; 7 | 8 | /// The member's status in the chat 9 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] 10 | pub enum ChatMemberStatus { 11 | Creator, 12 | Administrator, 13 | Member, 14 | Left, 15 | Kicked, 16 | #[doc(hidden)] 17 | Unknown(String), 18 | } 19 | 20 | impl<'de> Deserialize<'de> for ChatMemberStatus { 21 | fn deserialize(deserializer: D) -> Result 22 | where 23 | D: Deserializer<'de>, 24 | { 25 | struct ChatMemberStatusVisitor; 26 | use self::ChatMemberStatus::*; 27 | 28 | impl<'de> Visitor<'de> for ChatMemberStatusVisitor { 29 | type Value = ChatMemberStatus; 30 | 31 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 32 | formatter.write_str("creator | administrator | member | left | kicked") 33 | } 34 | 35 | fn visit_str(self, value: &str) -> Result 36 | where 37 | E: de::Error, 38 | { 39 | Ok(match value { 40 | "creator" => Creator, 41 | "administrator" => Administrator, 42 | "member" => Member, 43 | "left" => Left, 44 | "kicked" => Kicked, 45 | _unknown => Unknown(value.to_string()), 46 | }) 47 | } 48 | } 49 | 50 | deserializer.deserialize_str(ChatMemberStatusVisitor) 51 | } 52 | } 53 | 54 | /// This object contains information about one member of the chat. 55 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Deserialize)] 56 | pub struct ChatMember { 57 | /// Information about the user. 58 | pub user: User, 59 | /// The member's status in the chat. 60 | pub status: ChatMemberStatus, 61 | ///Optional. Restricted and kicked only. Date when restrictions will be lifted for this user, unix time 62 | pub until_date: Option, 63 | ///Optional. Administrators only. True, if the bot is allowed to edit administrator privileges of that user 64 | pub can_be_edited: Option, 65 | ///Optional. Administrators only. True, if the administrator can change the chat title, photo and other settings 66 | pub can_change_info: Option, 67 | ///Optional. Administrators only. True, if the administrator can post in the channel, channels only 68 | pub can_post_messages: Option, 69 | ///Optional. Administrators only. True, if the administrator can edit messages of other users and can pin messages, channels only 70 | pub can_edit_messages: Option, 71 | ///Optional. Administrators only. True, if the administrator can delete messages of other users 72 | pub can_delete_messages: Option, 73 | ///Optional. Administrators only. True, if the administrator can invite new users to the chat 74 | pub can_invite_users: Option, 75 | ///Optional. Administrators only. True, if the administrator can restrict, ban or unban chat members 76 | pub can_restrict_members: Option, 77 | ///Optional. Administrators only. True, if the administrator can pin messages, supergroups only 78 | pub can_pin_messages: Option, 79 | ///Optional. Administrators only. True, if the administrator can add new administrators with a subset of his own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by the user) 80 | pub can_promote_members: Option, 81 | ///Optional. Restricted only. True, if the user can send text messages, contacts, locations and venues 82 | pub can_send_messages: Option, 83 | ///Optional. Restricted only. True, if the user can send audios, documents, photos, videos, video notes and voice notes, implies can_send_messages 84 | pub can_send_media_messages: Option, 85 | ///Optional. Restricted only. True, if the user can send animations, games, stickers and use inline bots, implies can_send_media_messages 86 | pub can_send_other_messages: Option, 87 | ///Optional. Restricted only. True, if user may add web page previews to his messages, implies can_send_media_messages 88 | pub can_add_web_page_previews: Option, 89 | } 90 | -------------------------------------------------------------------------------- /raw/src/types/chat_member_update.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | 3 | /// This object represents changes in the status of a chat member. 4 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)] 5 | pub struct ChatMemberUpdate { 6 | /// Chat the user belongs to 7 | pub chat: Chat, 8 | /// Performer of the action, which resulted in the change 9 | pub from: User, 10 | /// Date the change was done in Unix time 11 | pub date: Integer, 12 | /// Previous information about the chat member 13 | pub old_chat_member: ChatMember, 14 | /// New information about the chat member 15 | pub new_chat_member: ChatMember, 16 | /// Chat invite link, which was used by the user to join the chat; for joining by invite link events only. 17 | pub invite_link: Option, 18 | } 19 | -------------------------------------------------------------------------------- /raw/src/types/chosen_inline_result.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | 3 | /// Represents a result of an inline query that was chosen by the user and sent to their chat partner. 4 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)] 5 | pub struct ChosenInlineResult { 6 | /// The unique identifier for the result that was chosen 7 | pub result_id: String, 8 | /// The user that chose the result 9 | pub from: User, 10 | /// Sender location, only for bots that require user location 11 | pub location: Option, 12 | /// Identifier of the sent inline message. Available only if there is an inline keyboard attached to the message. Will be also received in callback queries and can be used to edit the message. 13 | pub inline_message_id: Option, 14 | /// The query that was used to obtain the result 15 | pub query: String, 16 | } 17 | -------------------------------------------------------------------------------- /raw/src/types/inline_query.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | use serde::Deserialize; 3 | 4 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)] 5 | pub struct InlineQuery { 6 | pub id: InlineQueryId, 7 | pub from: User, 8 | pub location: Option, 9 | pub query: String, 10 | pub offset: String, 11 | } 12 | 13 | impl Into for InlineQuery { 14 | fn into(self) -> InlineQueryId { 15 | self.id 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /raw/src/types/input_file.rs: -------------------------------------------------------------------------------- 1 | use bytes::Bytes; 2 | 3 | use crate::requests::*; 4 | use crate::types::*; 5 | 6 | #[derive(Debug, Clone, PartialEq, PartialOrd)] 7 | pub struct InputFile(InputFileImpl); 8 | 9 | #[derive(Debug, Clone, PartialEq, PartialOrd)] 10 | pub struct InputFileRef(Text); 11 | 12 | #[derive(Debug, Clone, PartialEq, PartialOrd)] 13 | pub struct InputFileUpload(InputFileUploadImpl); 14 | 15 | #[derive(Debug, Clone, PartialEq, PartialOrd)] 16 | enum InputFileImpl { 17 | Ref(Text), 18 | Path { path: Text, file_name: Option }, 19 | Data { file_name: Text, data: Bytes }, 20 | } 21 | 22 | #[derive(Debug, Clone, PartialEq, PartialOrd)] 23 | enum InputFileUploadImpl { 24 | Path { path: Text, file_name: Option }, 25 | Data { file_name: Text, data: Bytes }, 26 | } 27 | 28 | impl InputFileRef { 29 | pub fn new(r: impl Into) -> Self { 30 | InputFileRef(r.into()) 31 | } 32 | } 33 | 34 | impl InputFileUpload { 35 | pub fn with_path(path: impl Into) -> Self { 36 | InputFileUpload(InputFileUploadImpl::Path { 37 | path: path.into(), 38 | file_name: None, 39 | }) 40 | } 41 | 42 | pub fn with_data(data: impl Into, file_name: impl Into) -> Self { 43 | InputFileUpload(InputFileUploadImpl::Data { 44 | file_name: file_name.into(), 45 | data: data.into(), 46 | }) 47 | } 48 | 49 | pub fn file_name(&self, new_file_name: impl Into) -> Self { 50 | let mut this = self.clone(); 51 | match &mut this.0 { 52 | InputFileUploadImpl::Path { file_name, .. } => *file_name = Some(new_file_name.into()), 53 | InputFileUploadImpl::Data { file_name, .. } => *file_name = new_file_name.into(), 54 | }; 55 | this 56 | } 57 | } 58 | 59 | impl From for InputFile { 60 | fn from(value: FileRef) -> Self { 61 | InputFile::from(InputFileRef::from(value)) 62 | } 63 | } 64 | 65 | impl<'a> From<&'a FileRef> for InputFile { 66 | fn from(value: &'a FileRef) -> Self { 67 | InputFile::from(InputFileRef::from(value)) 68 | } 69 | } 70 | 71 | impl<'a> From<&'a mut FileRef> for InputFile { 72 | fn from(value: &'a mut FileRef) -> Self { 73 | InputFile::from(InputFileRef::from(value)) 74 | } 75 | } 76 | 77 | impl From for InputFileRef { 78 | fn from(value: FileRef) -> Self { 79 | InputFileRef::new(value.inner) 80 | } 81 | } 82 | 83 | impl<'a> From<&'a FileRef> for InputFileRef { 84 | fn from(value: &'a FileRef) -> Self { 85 | InputFileRef::new(value.inner.as_str()) 86 | } 87 | } 88 | 89 | impl<'a> From<&'a mut FileRef> for InputFileRef { 90 | fn from(value: &'a mut FileRef) -> Self { 91 | InputFileRef::new(value.inner.as_str()) 92 | } 93 | } 94 | 95 | impl<'a> From<&'a InputFile> for InputFile { 96 | fn from(value: &'a InputFile) -> Self { 97 | value.clone() 98 | } 99 | } 100 | 101 | impl<'a> From<&'a mut InputFile> for InputFile { 102 | fn from(value: &'a mut InputFile) -> Self { 103 | value.clone() 104 | } 105 | } 106 | 107 | impl From for InputFile { 108 | fn from(value: InputFileRef) -> Self { 109 | InputFile(InputFileImpl::Ref(value.0)) 110 | } 111 | } 112 | 113 | impl<'a> From<&'a InputFileRef> for InputFile { 114 | fn from(value: &'a InputFileRef) -> Self { 115 | InputFile(InputFileImpl::Ref(value.0.clone())) 116 | } 117 | } 118 | 119 | impl<'a> From<&'a mut InputFileRef> for InputFile { 120 | fn from(value: &'a mut InputFileRef) -> Self { 121 | InputFile(InputFileImpl::Ref(value.0.clone())) 122 | } 123 | } 124 | 125 | impl From for InputFile { 126 | fn from(value: InputFileUpload) -> Self { 127 | InputFile(match value.0 { 128 | InputFileUploadImpl::Path { path, file_name } => { 129 | InputFileImpl::Path { path, file_name } 130 | } 131 | InputFileUploadImpl::Data { data, file_name } => { 132 | InputFileImpl::Data { data, file_name } 133 | } 134 | }) 135 | } 136 | } 137 | 138 | impl<'a> From<&'a InputFileUpload> for InputFile { 139 | fn from(value: &'a InputFileUpload) -> Self { 140 | InputFile(match &value.0 { 141 | InputFileUploadImpl::Path { path, file_name } => InputFileImpl::Path { 142 | path: path.clone(), 143 | file_name: file_name.clone(), 144 | }, 145 | InputFileUploadImpl::Data { data, file_name } => InputFileImpl::Data { 146 | data: data.clone(), 147 | file_name: file_name.clone(), 148 | }, 149 | }) 150 | } 151 | } 152 | 153 | impl<'a> From<&'a mut InputFileUpload> for InputFile { 154 | fn from(value: &'a mut InputFileUpload) -> Self { 155 | InputFile(match &value.0 { 156 | InputFileUploadImpl::Path { path, file_name } => InputFileImpl::Path { 157 | path: path.clone(), 158 | file_name: file_name.clone(), 159 | }, 160 | InputFileUploadImpl::Data { data, file_name } => InputFileImpl::Data { 161 | data: data.clone(), 162 | file_name: file_name.clone(), 163 | }, 164 | }) 165 | } 166 | } 167 | 168 | impl<'a> From<&'a InputFileRef> for InputFileRef { 169 | fn from(value: &'a InputFileRef) -> Self { 170 | value.clone() 171 | } 172 | } 173 | 174 | impl<'a> From<&'a mut InputFileRef> for InputFileRef { 175 | fn from(value: &'a mut InputFileRef) -> Self { 176 | value.clone() 177 | } 178 | } 179 | 180 | impl<'a> From<&'a InputFileUpload> for InputFileUpload { 181 | fn from(value: &'a InputFileUpload) -> Self { 182 | value.clone() 183 | } 184 | } 185 | 186 | impl<'a> From<&'a mut InputFileUpload> for InputFileUpload { 187 | fn from(value: &'a mut InputFileUpload) -> Self { 188 | value.clone() 189 | } 190 | } 191 | 192 | impl ToMultipartValue for InputFile { 193 | fn to_multipart_value(&self) -> MultipartValue { 194 | match &self.0 { 195 | InputFileImpl::Ref(r) => MultipartValue::Text(r.clone()), 196 | InputFileImpl::Path { path, file_name } => MultipartValue::Path { 197 | path: path.clone(), 198 | file_name: file_name.clone(), 199 | }, 200 | InputFileImpl::Data { file_name, data } => MultipartValue::Data { 201 | file_name: file_name.clone(), 202 | data: data.clone(), 203 | }, 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /raw/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod callback_query; 2 | pub mod chat; 3 | pub mod chat_invite_link; 4 | pub mod chat_member; 5 | pub mod chat_member_update; 6 | pub mod chosen_inline_result; 7 | pub mod inline_query; 8 | pub mod inline_query_result; 9 | pub mod input_file; 10 | pub mod message; 11 | pub mod pre_checkout_query; 12 | pub mod primitive; 13 | pub mod refs; 14 | pub mod reply_markup; 15 | pub mod response_parameters; 16 | pub mod shipping_query; 17 | pub mod text; 18 | pub mod update; 19 | 20 | pub use self::callback_query::*; 21 | pub use self::chat::*; 22 | pub use self::chat_invite_link::*; 23 | pub use self::chat_member::*; 24 | pub use self::chat_member_update::*; 25 | pub use self::chosen_inline_result::*; 26 | pub use self::inline_query::*; 27 | pub use self::inline_query_result::*; 28 | pub use self::input_file::*; 29 | pub use self::message::*; 30 | pub use self::pre_checkout_query::*; 31 | pub use self::primitive::*; 32 | pub use self::refs::*; 33 | pub use self::reply_markup::*; 34 | pub use self::response_parameters::*; 35 | pub use self::shipping_query::*; 36 | pub use self::text::*; 37 | pub use self::update::*; 38 | -------------------------------------------------------------------------------- /raw/src/types/pre_checkout_query.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | 3 | /// This object contains information about an incoming pre-checkout query. 4 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)] 5 | pub struct PreCheckoutQuery { 6 | /// Unique query identifier 7 | pub id: CallbackQueryId, 8 | /// User who sent the query 9 | pub from: User, 10 | /// Three-letter ISO 4217 currency code 11 | pub currency: String, 12 | /// Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). 13 | pub total_amount: Integer, 14 | /// Bot specified invoice payload 15 | pub invoice_payload: String, 16 | /// Optional. Identifier of the shipping option chosen by the user 17 | pub shipping_option_id: Option, 18 | /// Optional. Order info provided by the user 19 | pub order_info: Option, 20 | } 21 | 22 | /// This object represents information about an order. 23 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)] 24 | pub struct OrderInfo { 25 | /// Optional. User name 26 | pub name: Option, 27 | /// Optional. User's phone number 28 | pub phone_number: Option, 29 | /// Optional. User email 30 | pub email: Option, 31 | /// Optional. User shipping address 32 | pub shipping_address: Option, 33 | } 34 | 35 | /// This object represents a shipping address. 36 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)] 37 | pub struct ShippingAddress { 38 | /// ISO 3166-1 alpha-2 country code 39 | pub country_code: String, 40 | /// State, if applicable 41 | pub state: String, 42 | /// City 43 | pub city: String, 44 | /// First line for the address 45 | pub street_line1: String, 46 | /// Second line for the address 47 | pub street_line2: String, 48 | /// Address post code 49 | pub post_code: String, 50 | } 51 | -------------------------------------------------------------------------------- /raw/src/types/primitive.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use serde::de; 4 | use serde::de::{Deserialize, Deserializer, Unexpected, Visitor}; 5 | use serde::ser::{Serialize, Serializer}; 6 | 7 | /// The Telegram `Integer`, currently i64. 8 | pub type Integer = i64; 9 | 10 | /// The Telegram `Float`, currently f32. 11 | pub type Float = f32; 12 | 13 | #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)] 14 | pub struct True; 15 | 16 | impl<'de> Deserialize<'de> for True { 17 | fn deserialize(deserializer: D) -> Result 18 | where 19 | D: Deserializer<'de>, 20 | { 21 | struct TrueVisitor; 22 | 23 | impl<'de> Visitor<'de> for TrueVisitor { 24 | type Value = True; 25 | 26 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 27 | formatter.write_str("true") 28 | } 29 | 30 | fn visit_bool(self, value: bool) -> Result 31 | where 32 | E: de::Error, 33 | { 34 | match value { 35 | true => Ok(True), 36 | false => Err(E::invalid_value(Unexpected::Bool(value), &self)), 37 | } 38 | } 39 | } 40 | 41 | deserializer.deserialize_bool(TrueVisitor) 42 | } 43 | } 44 | 45 | impl Serialize for True { 46 | fn serialize(&self, serializer: S) -> Result 47 | where 48 | S: Serializer, 49 | { 50 | serializer.serialize_bool(true) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /raw/src/types/response_parameters.rs: -------------------------------------------------------------------------------- 1 | use serde::de::{Deserialize, Deserializer, Error}; 2 | 3 | use crate::types::*; 4 | 5 | /// All API responses are from this type. Mostly used internal. 6 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] 7 | pub enum ResponseWrapper { 8 | /// Request was successful. 9 | Success { 10 | /// Response result. 11 | result: T, 12 | }, 13 | /// Request was unsuccessful. 14 | Error { 15 | /// Human-readable description of the result. 16 | description: String, 17 | /// Contains information about why a request was unsuccessful. 18 | parameters: Option, 19 | }, 20 | } 21 | 22 | impl<'de, T: Deserialize<'de>> Deserialize<'de> for ResponseWrapper { 23 | fn deserialize(deserializer: D) -> Result, D::Error> 24 | where 25 | D: Deserializer<'de>, 26 | { 27 | let raw: RawResponse = Deserialize::deserialize(deserializer)?; 28 | match (raw.ok, raw.description, raw.result) { 29 | (false, Some(description), None) => Ok(ResponseWrapper::Error { 30 | description: description, 31 | parameters: raw.parameters, 32 | }), 33 | (true, None, Some(result)) => Ok(ResponseWrapper::Success { result: result }), 34 | _ => Err(D::Error::custom("ambiguous response")), 35 | } 36 | } 37 | } 38 | 39 | /// Directly mapped telegram API response. 40 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Deserialize)] 41 | pub struct RawResponse { 42 | /// If ‘ok’ equals true, the request was successful. 43 | ok: bool, 44 | /// Human-readable description of the result. 45 | description: Option, 46 | /// Result of the query. 47 | result: Option, 48 | /// Information about why a request was unsuccessful. 49 | parameters: Option, 50 | } 51 | 52 | /// Contains information about why a request was unsuccessful. 53 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Deserialize)] 54 | pub struct ResponseParameters { 55 | /// The group has been migrated to a supergroup with the specified identifier. 56 | pub migrate_to_chat_id: Option, 57 | /// In case of exceeding flood control, the number of seconds left to wait 58 | /// before the request can be repeated. 59 | pub retry_after: Option, 60 | } 61 | -------------------------------------------------------------------------------- /raw/src/types/shipping_query.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | 3 | /// This object contains information about an incoming shipping query. 4 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)] 5 | pub struct ShippingQuery { 6 | /// Unique query identifier 7 | pub id: CallbackQueryId, 8 | /// User who sent the query 9 | pub from: User, 10 | /// Bot specified invoice payload 11 | pub invoice_payload: String, 12 | /// User specified shipping address 13 | pub shipping_address: ShippingAddress, 14 | } 15 | -------------------------------------------------------------------------------- /raw/src/types/text.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use bytes::Bytes; 4 | 5 | #[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)] 6 | pub struct Text(Bytes); 7 | 8 | impl Text { 9 | pub fn as_str(&self) -> &str { 10 | unsafe { std::str::from_utf8_unchecked(self.0.as_ref()) } 11 | } 12 | } 13 | 14 | impl From for Text { 15 | fn from(value: String) -> Self { 16 | Text(value.into()) 17 | } 18 | } 19 | 20 | impl<'a> From<&'a str> for Text { 21 | fn from(value: &'a str) -> Self { 22 | Text(value.to_owned().into()) 23 | } 24 | } 25 | 26 | impl AsRef for Text { 27 | fn as_ref(&self) -> &str { 28 | self.as_str() 29 | } 30 | } 31 | 32 | impl AsRef for Text { 33 | fn as_ref(&self) -> &Path { 34 | self.as_str().as_ref() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /raw/src/types/update.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | 3 | /// This object represents an incoming update. 4 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)] 5 | pub struct Update { 6 | /// The update‘s unique identifier. Update identifiers start from a certain 7 | /// positive number and increase sequentially. 8 | #[serde(rename = "update_id")] 9 | pub id: Integer, 10 | /// Kind of the incoming update. 11 | #[serde(flatten)] 12 | pub kind: UpdateKind, 13 | } 14 | 15 | /// Kind of the incoming update. 16 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)] 17 | pub enum UpdateKind { 18 | /// New incoming message of any kind — text, photo, sticker, etc. 19 | #[serde(rename = "message")] 20 | Message(Message), 21 | /// New version of a message that is known to the bot and was edited 22 | #[serde(rename = "edited_message")] 23 | EditedMessage(Message), 24 | /// New incoming channel post of any kind — text, photo, sticker, etc. 25 | #[serde(rename = "channel_post")] 26 | ChannelPost(ChannelPost), 27 | /// New version of a channel post that is known to the bot and was edited 28 | #[serde(rename = "edited_channel_post")] 29 | EditedChannelPost(ChannelPost), 30 | #[serde(rename = "inline_query")] 31 | InlineQuery(InlineQuery), 32 | /// The result of an inline query that was chosen by a user and sent to their chat partner. Please see our documentation on the feedback collecting for details on how to enable these updates for your bot. 33 | #[serde(rename = "chosen_inline_result")] 34 | ChosenInlineResult(ChosenInlineResult), 35 | #[serde(rename = "callback_query")] 36 | CallbackQuery(CallbackQuery), 37 | /// New incoming shipping query. Only for invoices with flexible price 38 | #[serde(rename = "shipping_query")] 39 | ShippingQuery(ShippingQuery), 40 | /// New incoming pre-checkout query. Contains full information about checkout 41 | #[serde(rename = "pre_checkout_query")] 42 | PreCheckoutQuery(PreCheckoutQuery), 43 | /// New poll state. Bots receive only updates about stopped polls and polls, which are sent by the bot 44 | #[serde(rename = "poll")] 45 | Poll(Poll), 46 | /// A user changed their answer in a non-anonymous poll. Bots receive new votes only in polls that were sent by the bot itself 47 | #[serde(rename = "poll_answer")] 48 | PollAnswer(PollAnswer), 49 | /// The bot's chat member status was updated in a chat. For private chats, this update is received only when the bot is blocked or unblocked by the user. 50 | #[serde(rename = "my_chat_member")] 51 | MyChatMember(ChatMemberUpdate), 52 | /// A chat member's status was updated in a chat. The bot must be an administrator in the chat and must explicitly specify “chat_member” in the list of allowed_updates to receive these updates. 53 | #[serde(rename = "chat_member")] 54 | ChatMember(ChatMemberUpdate), 55 | #[doc(hidden)] 56 | Error(String), 57 | #[doc(hidden)] 58 | Unknown, 59 | } 60 | -------------------------------------------------------------------------------- /raw/src/url.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | const TELEGRAM_API_URL_DEFAULT: &str = "https://api.telegram.org/"; 4 | 5 | /// Obtains URL to the Telegram Bot API. You're able to change this URL to point to fake Telegram server 6 | /// for E2E-testing by setting `TELEGRAM_API_URL` environment variable. 7 | pub fn telegram_api_url() -> String { 8 | match env::var("TELEGRAM_API_URL") { 9 | Ok(url) => url, 10 | Err(_) => String::from(TELEGRAM_API_URL_DEFAULT), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /raw/tests/update.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::prelude::*; 3 | 4 | use telegram_bot_raw::types::message::MessageKind; 5 | use telegram_bot_raw::types::update::{Update, UpdateKind}; 6 | 7 | macro_rules! make_test { 8 | ($asset: ident, $test: expr) => { 9 | #[test] 10 | fn $asset() { 11 | let data = { 12 | let filename = format!("tests/update_assets/{}.json", stringify!($asset)); 13 | let mut data = Vec::new(); 14 | let mut file = File::open(filename).unwrap(); 15 | file.read_to_end(&mut data).unwrap(); 16 | data 17 | }; 18 | let update = serde_json::from_slice::(&data).unwrap(); 19 | $test(update) 20 | } 21 | }; 22 | } 23 | 24 | make_test!(migrate_from_chat_id, |update: Update| { 25 | if let UpdateKind::Message(message) = update.kind { 26 | if let MessageKind::MigrateFromChatId { .. } = message.kind { 27 | return (); 28 | } 29 | } 30 | assert!(false) 31 | }); 32 | 33 | make_test!(migrate_to_chat_id, |update: Update| { 34 | if let UpdateKind::Message(message) = update.kind { 35 | if let MessageKind::MigrateToChatId { .. } = message.kind { 36 | return (); 37 | } 38 | } 39 | assert!(false) 40 | }); 41 | 42 | make_test!(inline_query, |update: Update| { 43 | if let UpdateKind::InlineQuery(_query) = update.kind { 44 | return (); 45 | } 46 | 47 | assert!(false) 48 | }); 49 | 50 | make_test!(regression_test_208, |update: Update| { 51 | if let UpdateKind::CallbackQuery(_query) = update.kind { 52 | return (); 53 | } 54 | 55 | assert!(false) 56 | }); 57 | -------------------------------------------------------------------------------- /raw/tests/update_assets/inline_query.json: -------------------------------------------------------------------------------- 1 | { 2 | "update_id": 424151280, 3 | "inline_query": { 4 | "id": "inline query id", 5 | "from": { 6 | "id": 174976101, 7 | "first_name": "Fedor", 8 | "last_name": "Gogolev", 9 | "username": "FedorGogolev", 10 | "is_bot": false 11 | }, 12 | "query": "user query", 13 | "offset": "0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /raw/tests/update_assets/migrate_from_chat_id.json: -------------------------------------------------------------------------------- 1 | { 2 | "update_id": 424151280, 3 | "message": { 4 | "message_id": 1, 5 | "from": { 6 | "id": 174976101, 7 | "first_name": "Fedor", 8 | "last_name": "Gogolev", 9 | "username": "FedorGogolev", 10 | "is_bot": false 11 | }, 12 | "chat": { 13 | "id": -1001113717682, 14 | "title": "supergroup-test-knsd", 15 | "type": "supergroup" 16 | }, 17 | "date": 1487852026, 18 | "migrate_from_chat_id": -216055857 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /raw/tests/update_assets/migrate_to_chat_id.json: -------------------------------------------------------------------------------- 1 | { 2 | "update_id": 424151279, 3 | "message": { 4 | "message_id": 80416, 5 | "from": { 6 | "id": 174976101, 7 | "first_name": "Fedor", 8 | "last_name": "Gogolev", 9 | "username": "FedorGogolev", 10 | "is_bot": false 11 | }, 12 | "chat": { 13 | "id": -216055857, 14 | "title": "supergroup-test-knsd", 15 | "type": "group", 16 | "all_members_are_administrators": true 17 | }, 18 | "date": 1487852026, 19 | "migrate_to_chat_id": -1001113717682 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /raw/tests/update_assets/regression_test_208.json: -------------------------------------------------------------------------------- 1 | { 2 | "update_id": 846954218, 3 | "callback_query": { 4 | "id": "3869607449858959801", 5 | "from": { 6 | "id": 900963191, 7 | "is_bot": false, 8 | "first_name": "Aldis", 9 | "username": "aldis_aaa", 10 | "language_code": "en" 11 | }, 12 | "message": { 13 | "message_id": 58, 14 | "chat": { 15 | "id": -1001254477603, 16 | "title": "RedditWeekly", 17 | "type": "channel" 18 | }, 19 | "date": 1593844893, 20 | "text": "Select subreddit", 21 | "reply_markup": { 22 | "inline_keyboard": [ 23 | [ 24 | { 25 | "text": "test", 26 | "callback_data": "test" 27 | } 28 | ] 29 | ] 30 | } 31 | }, 32 | "chat_instance": "-7315430269816637685", 33 | "data": "test" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | stable 2 | --------------------------------------------------------------------------------