├── .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 | [](https://travis-ci.org/telegram-rs/telegram-bot)
4 | [](https://github.com/telegram-rs/telegram-bot/actions?workflow=Tests)
5 | [](https://github.com/telegram-rs/telegram-bot/actions?workflow=Fmt)
6 | []()
7 | [](https://crates.io/crates/telegram-bot)
8 |
9 |
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