diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f33adb24..ffd40f1e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly + toolchain: nightly-2021-10-24 override: true components: rustfmt @@ -34,7 +34,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly + toolchain: nightly-2021-10-24 override: true components: clippy diff --git a/CHANGELOG.md b/CHANGELOG.md index e3a0be3e..e246e8b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,15 +4,31 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [unreleased] +## unreleased -## [0.5.1] - 2021-08-05 +### Changed + +- Require that `AsUpdateStream::Stream` is `Send` + +## 0.5.3 - 2021-10-25 + +### Fixed + +- Compilation when the `ctrlc_handler` feature is disabled ([issue 462](https://github.com/teloxide/teloxide/issues/462)) + +## 0.5.2 - 2021-08-25 + +### Fixed + +- Depend on a correct `futures` version (v0.3.15). + +## 0.5.1 - 2021-08-05 ### Changed - Improved log messages when `^C` is received with `^C` handler set up -## [0.5.0] - 2021-07-08 +## 0.5.0 - 2021-07-08 ### Added @@ -58,7 +74,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Log errors from `Storage::{remove_dialogue, update_dialogue}` in `DialogueDispatcher` ([issue 302](https://github.com/teloxide/teloxide/issues/302)). - Mark all the functions of `Storage` as `#[must_use]`. -## [0.4.0] - 2021-03-22 +## 0.4.0 - 2021-03-22 ### Added - Integrate [teloxide-core]. @@ -95,28 +111,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [issue 253]: https://github.com/teloxide/teloxide/issues/253 [pr 257]: https://github.com/teloxide/teloxide/pull/257 -## [0.3.4] - 2020-01-13 +## 0.3.4 - 2020-01-13 ### Fixed - Failing compilation with `serde::export` ([issue 328](https://github.com/teloxide/teloxide/issues/328)). -## [0.3.3] - 2020-10-30 +## 0.3.3 - 2020-10-30 ### Fixed - The `dice` field from `MessageDice` is public now ([issue 306](https://github.com/teloxide/teloxide/issues/306)) -## [0.3.2] - 2020-10-23 +## 0.3.2 - 2020-10-23 ### Added - `LoginUrl::new` ([issue 298](https://github.com/teloxide/teloxide/issues/298)) -## [0.3.1] - 2020-08-25 +## 0.3.1 - 2020-08-25 ### Added - `Bot::builder` method ([PR 269](https://github.com/teloxide/teloxide/pull/269)). -## [0.3.0] - 2020-07-31 +## 0.3.0 - 2020-07-31 ### Added - Support for typed bot commands ([issue 152](https://github.com/teloxide/teloxide/issues/152)). - `BotBuilder`, which allows setting a default `ParseMode`. @@ -153,7 +169,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Now methods which can send file to Telegram returns `tokio::io::Result`. Early its could panic ([issue 216](https://github.com/teloxide/teloxide/issues/216)). - If a bot wasn't triggered for several days, it stops responding ([issue 223](https://github.com/teloxide/teloxide/issues/223)). -## [0.2.0] - 2020-02-25 +## 0.2.0 - 2020-02-25 ### Added - The functionality to parse commands only with a correct bot's name (breaks backwards compatibility) ([Issue 168](https://github.com/teloxide/teloxide/issues/168)). - This `CHANGELOG.md`. @@ -168,6 +184,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [either](https://crates.io/crates/either) from the dependencies in `Cargo.toml`. - `teloxide-macros` migrated into [the separate repository](https://github.com/teloxide/teloxide-macros) to easier releases and testing. -## [0.1.0] - 2020-02-19 +## 0.1.0 - 2020-02-19 ### Added - This project. diff --git a/Cargo.toml b/Cargo.toml index 81534709..892b5022 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "teloxide" -version = "0.5.1" +version = "0.5.3" edition = "2018" description = "An elegant Telegram bots framework for Rust" repository = "https://github.com/teloxide/teloxide" @@ -10,22 +10,9 @@ keywords = ["teloxide", "telegram", "telegram-bot", "telegram-bot-api"] categories = ["web-programming", "api-bindings", "asynchronous"] license = "MIT" exclude = ["media"] -authors = [ - "Hirrolot ", - "Waffle Lapkin ", - "p0lunin ", - "Mishko torop'izhko", - "Mr-Andersen", - "Sergey Levitin ", - "Rustem B. ", - "Alexey Fedechkin " -] - -[badges] -maintenance = { status = "actively-developed" } [features] -default = ["native-tls", "ctrlc_handler", "teloxide-core/default", "new-dispatching"] +default = ["native-tls", "ctrlc_handler", "teloxide-core/default", "auto-send", "new-dispatching"] old-dispatching = [] new-dispatching = ["dptree"] diff --git a/LICENSE b/LICENSE index 2aa8a5ce..8e5427af 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019-2020 teloxide +Copyright (c) 2019-2022 teloxide Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index db0b46c9..ddbe097f 100644 --- a/README.md +++ b/README.md @@ -51,8 +51,12 @@ # Unix-like $ export TELOXIDE_TOKEN= -# Windows +# Windows command line $ set TELOXIDE_TOKEN= + +# Windows PowerShell +$ $env:TELOXIDE_TOKEN= + ``` 4. Make sure that your Rust compiler is up to date: ```bash @@ -68,10 +72,10 @@ $ rustup override set nightly 5. Run `cargo new my_bot`, enter the directory and put these lines into your `Cargo.toml`: ```toml [dependencies] -teloxide = { version = "0.4", features = ["auto-send", "macros"] } -log = "0.4.8" +teloxide = { version = "0.5", features = ["macros", "auto-send"] } +log = "0.4" pretty_env_logger = "0.4.0" -tokio = { version = "1.3", features = ["rt-multi-thread", "macros"] } +tokio = { version = "1.8", features = ["rt-multi-thread", "macros"] } ``` ## API overview @@ -387,21 +391,24 @@ A: Yes. You can setup any logger, for example, [fern], e.g. teloxide has no spec Feel free to propose your own bot to our collection! - [WaffleLapkin/crate_upd_bot](https://github.com/WaffleLapkin/crate_upd_bot) -- A bot that notifies about crate updates. - - [dracarys18/grpmr-rs](https://github.com/dracarys18/grpmr-rs) -- A telegram group manager bot with variety of extra features. + - [mxseev/logram](https://github.com/mxseev/logram) -- Utility that takes logs from anywhere and sends them to Telegram. + - [Hermitter/tepe](https://github.com/Hermitter/tepe) -- A CLI to command a bot to send messages and files over Telegram. + - [dracarys18/grpmr-rs](https://github.com/dracarys18/grpmr-rs) -- A Telegram group manager bot with variety of extra features. - [steadylearner/subreddit_reader](https://github.com/steadylearner/Rust-Full-Stack/tree/master/commits/teloxide/subreddit_reader) -- A bot that shows the latest posts at Rust subreddit. + - [myblackbeard/basketball-betting-bot](https://github.com/myblackbeard/basketball-betting-bot) -- The bot lets you bet on NBA games against your buddies. - [ArtHome12/vzmuinebot](https://github.com/ArtHome12/vzmuinebot) -- Telegram bot for food menu navigate. - [ArtHome12/cognito_bot](https://github.com/ArtHome12/cognito_bot) -- The bot is designed to anonymize messages to a group. - - [Hermitter/tepe](https://github.com/Hermitter/tepe) -- A CLI to command a bot to send messages and files over Telegram. - [pro-vim/tg-vimhelpbot](https://github.com/pro-vim/tg-vimhelpbot) -- Link `:help` for Vim in Telegram. - [sschiz/janitor-bot](https://github.com/sschiz/janitor-bot) -- A bot that removes users trying to join to a chat that is designed for comments. - - [myblackbeard/basketball-betting-bot](https://github.com/myblackbeard/basketball-betting-bot) -- The bot lets you bet on NBA games against your buddies. - [slondr/BeerHolderBot](https://gitlab.com/slondr/BeerHolderBot) -- A bot that holds your beer. - - [mxseev/logram](https://github.com/mxseev/logram) -- Utility that takes logs from anywhere and sends them to Telegram. - - [msfjarvis/walls-bot-rs](https://github.com/msfjarvis/walls-bot-rs) -- Telegram bot for my wallpapers collection, in Rust. - - [MustafaSalih1993/Miss-Vodka-Telegram-Bot](https://github.com/MustafaSalih1993/Miss-Vodka-Telegram-Bot) -- A telegram bot written in rust using "Teloxide" library. + - [MustafaSalih1993/Miss-Vodka-Telegram-Bot](https://github.com/MustafaSalih1993/Miss-Vodka-Telegram-Bot) -- A Telegram bot written in rust using "Teloxide" library. - [x13a/tg-prompt](https://github.com/x13a/tg-prompt) -- Telegram prompt. - [magnickolas/remindee-bot](https://github.com/magnickolas/remindee-bot) -- Telegram bot for managing reminders. - - [cyberknight777/knight-bot](https://gitlab.com/cyberknight777/knight-bot) -- A telegram bot with variety of fun features. + - [cyberknight777/knight-bot](https://gitlab.com/cyberknight777/knight-bot) -- A Telegram bot with variety of fun features. + - [wa7sa34cx/the-black-box-bot](https://github.com/wa7sa34cx/the-black-box-bot) -- This is the Black Box Telegram bot. You can hold any items in it. + - [crapstone/hsctt](https://codeberg.org/crapstones-bots/hsctt) -- A Telegram bot that searches for HTTP status codes in all messages and replies with the text form. + - [alenpaul2001/AurSearchBot](https://gitlab.com/alenpaul2001/aursearchbot) -- Telegram bot for searching AUR in inline mode. + ## Contributing See [CONRIBUTING.md](https://github.com/teloxide/teloxide/blob/master/CONTRIBUTING.md). diff --git a/examples/admin_bot/Cargo.toml b/examples/admin_bot/Cargo.toml index 52c6f017..4ee67f87 100644 --- a/examples/admin_bot/Cargo.toml +++ b/examples/admin_bot/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -teloxide = { path = "../../", features = ["macros", "auto-send"] } +teloxide = { path = "../../", features = ["macros"] } log = "0.4.8" pretty_env_logger = "0.4.0" tokio = { version = "1.3.0", features = ["rt-multi-thread", "macros"] } diff --git a/examples/buttons/Cargo.toml b/examples/buttons/Cargo.toml new file mode 100644 index 00000000..9922e0ae --- /dev/null +++ b/examples/buttons/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "buttons" +version = "0.1.0" +edition = "2018" + +[dependencies] +teloxide = { path = "../../", features = ["macros"] } + +tokio = { version = "1.3", features = ["rt-multi-thread", "macros"] } +log = "0.4.8" +pretty_env_logger = "0.4.0" +tokio-stream = "0.1.6" diff --git a/examples/buttons/src/main.rs b/examples/buttons/src/main.rs new file mode 100644 index 00000000..97bde1ed --- /dev/null +++ b/examples/buttons/src/main.rs @@ -0,0 +1,147 @@ +use std::error::Error; +use teloxide::{ + payloads::SendMessageSetters, + prelude::*, + types::{ + InlineKeyboardButton, InlineKeyboardMarkup, InlineQueryResultArticle, InputMessageContent, + InputMessageContentText, + }, + utils::command::BotCommand, +}; + +use tokio_stream::wrappers::UnboundedReceiverStream; + +#[derive(BotCommand)] +#[command(rename = "lowercase", description = "These commands are supported:")] +enum Command { + #[command(description = "Display this text")] + Help, + #[command(description = "Start")] + Start, +} + +/// Creates a keyboard made by buttons in a big column. +fn make_keyboard() -> InlineKeyboardMarkup { + let mut keyboard: Vec> = vec![]; + + let debian_versions = [ + "Buzz", "Rex", "Bo", "Hamm", "Slink", "Potato", "Woody", "Sarge", "Etch", "Lenny", + "Squeeze", "Wheezy", "Jessie", "Stretch", "Buster", "Bullseye", + ]; + + for versions in debian_versions.chunks(3) { + let row = versions + .iter() + .map(|&version| InlineKeyboardButton::callback(version.to_owned(), version.to_owned())) + .collect(); + + keyboard.push(row); + } + + InlineKeyboardMarkup::new(keyboard) +} + +/// Parse the text wrote on Telegram and check if that text is a valid command +/// or not, then match the command. If the command is `/start` it writes a +/// markup with the `InlineKeyboardMarkup`. +async fn message_handler( + cx: UpdateWithCx, Message>, +) -> Result<(), Box> { + match cx.update.text() { + Some(text) => { + match BotCommand::parse(text, "buttons") { + Ok(Command::Help) => { + // Just send the description of all commands. + cx.answer(Command::descriptions()).await?; + } + Ok(Command::Start) => { + // Create a list of buttons and send them. + let keyboard = make_keyboard(); + cx.answer("Debian versions:").reply_markup(keyboard).await?; + } + + Err(_) => { + cx.reply_to("Command not found!").await?; + } + } + } + None => {} + } + + Ok(()) +} + +async fn inline_query_handler( + cx: UpdateWithCx, InlineQuery>, +) -> Result<(), Box> { + let UpdateWithCx { requester: bot, update: query } = cx; + + let choose_debian_version = InlineQueryResultArticle::new( + "0", + "Chose debian version", + InputMessageContent::Text(InputMessageContentText::new("Debian versions:")), + ) + .reply_markup(make_keyboard()); + + bot.answer_inline_query(query.id, vec![choose_debian_version.into()]).await?; + + Ok(()) +} + +/// When it receives a callback from a button it edits the message with all +/// those buttons writing a text with the selected Debian version. +async fn callback_handler( + cx: UpdateWithCx, CallbackQuery>, +) -> Result<(), Box> { + let UpdateWithCx { requester: bot, update: query } = cx; + + if let Some(version) = query.data { + let text = format!("You chose: {}", version); + + match query.message { + Some(Message { id, chat, .. }) => { + bot.edit_message_text(chat.id, id, text).await?; + } + None => { + if let Some(id) = query.inline_message_id { + bot.edit_message_text_inline(dbg!(id), text).await?; + } + } + } + + log::info!("You chose: {}", version); + } + + Ok(()) +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + teloxide::enable_logging!(); + log::info!("Starting bot..."); + + let bot = Bot::from_env().auto_send(); + + Dispatcher::new(bot) + .messages_handler(|rx: DispatcherHandlerRx, Message>| { + UnboundedReceiverStream::new(rx).for_each_concurrent(None, |cx| async move { + message_handler(cx).await.log_on_error().await; + }) + }) + .callback_queries_handler(|rx: DispatcherHandlerRx, CallbackQuery>| { + UnboundedReceiverStream::new(rx).for_each_concurrent(None, |cx| async move { + callback_handler(cx).await.log_on_error().await; + }) + }) + .inline_queries_handler(|rx: DispatcherHandlerRx, InlineQuery>| { + UnboundedReceiverStream::new(rx).for_each_concurrent(None, |cx| async move { + inline_query_handler(cx).await.log_on_error().await; + }) + }) + .dispatch() + .await; + + log::info!("Closing bot... Goodbye!"); + + Ok(()) +} diff --git a/examples/dialogue_bot/Cargo.toml b/examples/dialogue_bot/Cargo.toml index a885196b..31439f77 100644 --- a/examples/dialogue_bot/Cargo.toml +++ b/examples/dialogue_bot/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -teloxide = { path = "../../", features = ["frunk", "macros", "auto-send", "sqlite-storage"] } +teloxide = { path = "../../", features = ["frunk", "macros", "sqlite-storage"] } anyhow = "1.0.52" serde = "1" diff --git a/examples/dices_bot/Cargo.toml b/examples/dices_bot/Cargo.toml index 763f2c8b..b6feb4b2 100644 --- a/examples/dices_bot/Cargo.toml +++ b/examples/dices_bot/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -teloxide = { path = "../../", features = ["auto-send"] } +teloxide = { path = "../../" } log = "0.4.8" pretty_env_logger = "0.4.0" tokio = { version = "1.3.0", features = ["rt-multi-thread", "macros"] } diff --git a/examples/heroku_ping_pong_bot/Cargo.toml b/examples/heroku_ping_pong_bot/Cargo.toml index edcc76f5..713b4fd4 100644 --- a/examples/heroku_ping_pong_bot/Cargo.toml +++ b/examples/heroku_ping_pong_bot/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -teloxide = { path = "../../", features = ["auto-send"] } +teloxide = { path = "../../" } log = "0.4.8" pretty_env_logger = "0.4.0" tokio = { version = "1.3.0", features = ["rt-multi-thread", "macros"] } diff --git a/examples/inline_bot/Cargo.toml b/examples/inline_bot/Cargo.toml index 40e7281d..913b25c8 100644 --- a/examples/inline_bot/Cargo.toml +++ b/examples/inline_bot/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -teloxide = { path = "../../", features = ["macros", "auto-send"] } +teloxide = { path = "../../", features = ["macros"] } log = "0.4.8" pretty_env_logger = "0.4.0" tokio = { version = "1.3.0", features = ["rt-multi-thread", "macros"] } diff --git a/examples/inline_bot/src/main.rs b/examples/inline_bot/src/main.rs index cb0f22a7..99273c40 100644 --- a/examples/inline_bot/src/main.rs +++ b/examples/inline_bot/src/main.rs @@ -33,7 +33,7 @@ async fn main() { // While constructing them from the struct itself is possible, it is preferred // to use the builder pattern if you wish to add more // information to your result. Please refer to the documentation - // for more detailed information about each field. https://docs.rs/teloxide/0.5.1/teloxide/types/struct.InlineQueryResultArticle.html + // for more detailed information about each field. https://docs.rs/teloxide/latest/teloxide/types/struct.InlineQueryResultArticle.html let ddg_search = InlineQueryResultArticle::new( "02".to_string(), "DuckDuckGo Search".to_string(), diff --git a/examples/ngrok_ping_pong_bot/Cargo.toml b/examples/ngrok_ping_pong_bot/Cargo.toml index 571fe1ff..5f05b0d7 100644 --- a/examples/ngrok_ping_pong_bot/Cargo.toml +++ b/examples/ngrok_ping_pong_bot/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -teloxide = { path = "../../", features = ["auto-send"] } +teloxide = { path = "../../" } log = "0.4.8" pretty_env_logger = "0.4.0" tokio = { version = "1.3.0", features = ["rt-multi-thread", "macros"] } diff --git a/examples/redis_remember_bot/Cargo.toml b/examples/redis_remember_bot/Cargo.toml index c134f0a4..57d7b547 100644 --- a/examples/redis_remember_bot/Cargo.toml +++ b/examples/redis_remember_bot/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] # You can also choose "cbor-serializer" or built-in JSON serializer -teloxide = { path = "../../", features = ["redis-storage", "bincode-serializer", "macros", "auto-send"] } +teloxide = { path = "../../", features = ["redis-storage", "bincode-serializer", "macros"] } log = "0.4.8" pretty_env_logger = "0.4.0" diff --git a/examples/shared_state_bot/Cargo.toml b/examples/shared_state_bot/Cargo.toml index eb613ba1..dbaacc86 100644 --- a/examples/shared_state_bot/Cargo.toml +++ b/examples/shared_state_bot/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -teloxide = { path = "../../", features = ["auto-send"] } +teloxide = { path = "../../" } log = "0.4.8" pretty_env_logger = "0.4.0" tokio = { version = "1.3.0", features = ["rt-multi-thread", "macros"] } diff --git a/examples/simple_commands_bot/Cargo.toml b/examples/simple_commands_bot/Cargo.toml index 7aef1030..d5368de3 100644 --- a/examples/simple_commands_bot/Cargo.toml +++ b/examples/simple_commands_bot/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -teloxide = { path = "../../", features = ["macros", "auto-send"] } +teloxide = { path = "../../", features = ["macros"] } log = "0.4.8" pretty_env_logger = "0.4.0" tokio = { version = "1.3.0", features = ["rt-multi-thread", "macros"] } diff --git a/examples/sqlite_remember_bot/Cargo.toml b/examples/sqlite_remember_bot/Cargo.toml index 25a28c6c..ba0f255f 100644 --- a/examples/sqlite_remember_bot/Cargo.toml +++ b/examples/sqlite_remember_bot/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] # You can also choose "cbor-serializer" or built-in JSON serializer -teloxide = { path = "../../", features = ["sqlite-storage", "bincode-serializer", "redis-storage", "macros", "auto-send"] } +teloxide = { path = "../../", features = ["sqlite-storage", "bincode-serializer", "redis-storage", "macros"] } log = "0.4.8" pretty_env_logger = "0.4.0" diff --git a/src/dispatching/dialogue/dialogue_dispatcher.rs b/src/dispatching/dialogue/dialogue_dispatcher.rs index cee26a29..3232e633 100644 --- a/src/dispatching/dialogue/dialogue_dispatcher.rs +++ b/src/dispatching/dialogue/dialogue_dispatcher.rs @@ -156,15 +156,17 @@ where match this.senders.pin().get(&chat_id) { // An old dialogue Some(tx) => { - if tx.send(cx).is_err() { - panic!("We are not dropping a receiver or call .close() on it",); - } + assert!( + tx.send(cx).is_ok(), + "We are not dropping a receiver or call .close() on it" + ); } None => { let tx = this.new_tx(); - if tx.send(cx).is_err() { - panic!("We are not dropping a receiver or call .close() on it",); - } + assert!( + tx.send(cx).is_ok(), + "We are not dropping a receiver or call .close() on it" + ); this.senders.pin().insert(chat_id, tx); } } @@ -271,9 +273,7 @@ mod tests { let tx = tx.clone(); async move { - if tx.send(update).is_err() { - panic!("tx.send(update) failed"); - } + assert!(tx.send(update).is_ok(), "tx.send(update) failed"); } }) .await; diff --git a/src/dispatching/dispatcher.rs b/src/dispatching/dispatcher.rs index edcc15d7..49068405 100644 --- a/src/dispatching/dispatcher.rs +++ b/src/dispatching/dispatcher.rs @@ -106,7 +106,7 @@ where /// /// [`shutdown`]: ShutdownToken::shutdown #[cfg(feature = "ctrlc_handler")] - #[cfg_attr(docsrs, doc(cfg(feature = "ctrlc_handler")))] + #[cfg_attr(all(docsrs, feature = "nightly"), doc(cfg(feature = "ctrlc_handler")))] pub fn setup_ctrlc_handler(self) -> Self { let state = Arc::clone(&self.state); tokio::spawn(async move { diff --git a/src/dispatching/repls/commands_repl.rs b/src/dispatching/repls/commands_repl.rs index a3625910..c1799d58 100644 --- a/src/dispatching/repls/commands_repl.rs +++ b/src/dispatching/repls/commands_repl.rs @@ -22,7 +22,6 @@ use tokio_stream::wrappers::UnboundedReceiverStream; /// /// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop /// [`Dispatcher`]: crate::dispatching::Dispatcher -#[cfg(feature = "ctrlc_handler")] pub async fn commands_repl(requester: R, bot_name: N, handler: H) where Cmd: BotCommand + Send + 'static, @@ -57,7 +56,6 @@ where /// [`Dispatcher`]: crate::dispatching::Dispatcher /// [`commands_repl`]: crate::dispatching::repls::commands_repl() /// [`UpdateListener`]: crate::dispatching::update_listeners::UpdateListener -#[cfg(feature = "ctrlc_handler")] pub async fn commands_repl_with_listener<'a, R, Cmd, H, Fut, L, ListenerE, HandlerE, N>( requester: R, bot_name: N, diff --git a/src/dispatching/repls/dialogues_repl.rs b/src/dispatching/repls/dialogues_repl.rs index 755d4dd3..345c6bcb 100644 --- a/src/dispatching/repls/dialogues_repl.rs +++ b/src/dispatching/repls/dialogues_repl.rs @@ -23,7 +23,6 @@ use teloxide_core::{requests::Requester, types::Message}; /// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop /// [`Dispatcher`]: crate::dispatching::Dispatcher /// [`InMemStorage`]: crate::dispatching::dialogue::InMemStorage -#[cfg(feature = "ctrlc_handler")] pub async fn dialogues_repl<'a, R, H, D, Fut>(requester: R, handler: H) where H: Fn(UpdateWithCx, D) -> Fut + Send + Sync + 'static, @@ -56,7 +55,6 @@ where /// [`dialogues_repl`]: crate::dispatching::repls::dialogues_repl() /// [`UpdateListener`]: crate::dispatching::update_listeners::UpdateListener /// [`InMemStorage`]: crate::dispatching::dialogue::InMemStorage -#[cfg(feature = "ctrlc_handler")] pub async fn dialogues_repl_with_listener<'a, R, H, D, Fut, L, ListenerE>( requester: R, handler: H, diff --git a/src/dispatching/repls/repl.rs b/src/dispatching/repls/repl.rs index a298e768..635a030c 100644 --- a/src/dispatching/repls/repl.rs +++ b/src/dispatching/repls/repl.rs @@ -21,7 +21,6 @@ use tokio_stream::wrappers::UnboundedReceiverStream; /// /// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop /// [`Dispatcher`]: crate::dispatching::Dispatcher -#[cfg(feature = "ctrlc_handler")] pub async fn repl(requester: R, handler: H) where H: Fn(UpdateWithCx) -> Fut + Send + Sync + 'static, @@ -52,7 +51,6 @@ where /// [`Dispatcher`]: crate::dispatching::Dispatcher /// [`repl`]: crate::dispatching::repls::repl() /// [`UpdateListener`]: crate::dispatching::update_listeners::UpdateListener -#[cfg(feature = "ctrlc_handler")] pub async fn repl_with_listener<'a, R, H, Fut, E, L, ListenerE>( requester: R, handler: H, diff --git a/src/dispatching/update_listeners.rs b/src/dispatching/update_listeners.rs index a0d9edca..078f52a2 100644 --- a/src/dispatching/update_listeners.rs +++ b/src/dispatching/update_listeners.rs @@ -188,7 +188,14 @@ pub trait UpdateListener: for<'a> AsUpdateStream<'a, E> { /// This trait is a workaround to not require GAT. pub trait AsUpdateStream<'a, E> { /// The stream of updates from Telegram. - type Stream: Stream> + 'a; + // HACK: There is currently no way to write something like + // `-> impl for<'a> AsUpdateStream<'a, E, Stream: Send>`. Since we return + // `impl UpdateListener` from `polling`, we need to have `Send` bound here, + // to make the stream `Send`. + // + // Without this it's, for example, impossible to spawn a tokio task with + // teloxide polling. + type Stream: Stream> + Send + 'a; /// Creates the update [`Stream`]. /// diff --git a/src/dispatching/update_listeners/polling.rs b/src/dispatching/update_listeners/polling.rs index 5440fd6a..f7050bff 100644 --- a/src/dispatching/update_listeners/polling.rs +++ b/src/dispatching/update_listeners/polling.rs @@ -24,7 +24,7 @@ use crate::{ /// This function will automatically delete a webhook if it was set up. pub async fn polling_default(requester: R) -> impl UpdateListener where - R: Requester + 'static, + R: Requester + Send + 'static, ::GetUpdatesFaultTolerant: Send, { delete_webhook_if_setup(&requester).await; @@ -50,7 +50,7 @@ pub fn polling( allowed_updates: Option>, ) -> impl UpdateListener where - R: Requester + 'static, + R: Requester + Send + 'static, ::GetUpdatesFaultTolerant: Send, { struct State { @@ -63,9 +63,10 @@ where token: AsyncStopToken, } - fn stream(st: &mut State) -> impl Stream> + '_ + fn stream(st: &mut State) -> impl Stream> + Send + '_ where - B: Requester, + B: Requester + Send, + ::GetUpdatesFaultTolerant: Send, { stream::unfold(st, move |state| async move { let State { timeout, limit, allowed_updates, bot, offset, flag, .. } = &mut *state; @@ -177,3 +178,16 @@ where } } } + +#[test] +fn polling_is_send() { + use crate::dispatching::update_listeners::AsUpdateStream; + + let bot = crate::Bot::new("TOKEN"); + let mut polling = polling(bot, None, None, None); + + assert_send(&polling); + assert_send(&polling.as_stream()); + + fn assert_send(_: &impl Send) {} +} diff --git a/src/dispatching/update_listeners/stateful_listener.rs b/src/dispatching/update_listeners/stateful_listener.rs index a9c26576..2b36baa5 100644 --- a/src/dispatching/update_listeners/stateful_listener.rs +++ b/src/dispatching/update_listeners/stateful_listener.rs @@ -79,7 +79,7 @@ impl Thfn, > where - S: Stream> + Unpin + 'static, + S: Stream> + Unpin + Send + 'static, { /// Creates a new update listener from a stream of updates which ignores /// stop signals. @@ -109,6 +109,7 @@ impl<'a, St, Assf, Sf, Hauf, Thf, Strm, E> AsUpdateStream<'a, E> for StatefulListener where (St, Strm): 'a, + Strm: Send, Assf: FnMut(&'a mut St) -> Strm, Strm: Stream>, { diff --git a/src/features.txt b/src/features.txt index f6ef21ba..7edd2f39 100644 --- a/src/features.txt +++ b/src/features.txt @@ -10,8 +10,11 @@ | `native-tls` | Enables the [`native-tls`] TLS implementation (enabled by default). | | `rustls` | Enables the [`rustls`] TLS implementation. | | `ctrlc_handler` | Enables the [`Dispatcher::setup_ctrlc_handler`](dispatching::Dispatcher::setup_ctrlc_handler) function. | -| `auto-send` | Enables the `AutoSend` bot adaptor. | -| `cache-me` | Enables the `CacheMe` bot adaptor. | +| `auto-send` | Enables the [`AutoSend`](adaptors::AutoSend) bot adaptor. | +| `throttle` | Enables the [`Throttle`](adaptors::Throttle) bot adaptor. | +| `cache-me` | Enables the [`CacheMe`](adaptors::CacheMe) bot adaptor. | +| `trace-adaptor` | Enables the [`Trace`](adaptors::Trace) bot adaptor. | +| `erased` | Enables the [`ErasedRequester`](adaptors::ErasedRequester) bot adaptor. | | `frunk` | Enables [`teloxide::utils::UpState`]. | | `full` | Enables all the features except `nightly`. | | `nightly` | Enables nightly-only features (see the [teloxide-core features]). |