From e7ad0e4c808a79cff37429f292ffbcde18221c8e Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Sun, 24 Jul 2022 14:53:28 +0600 Subject: [PATCH 01/66] Simplify the implementation of REPLs --- src/dispatching/repls/commands_repl.rs | 21 +++++++++------------ src/dispatching/repls/repl.rs | 2 +- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/dispatching/repls/commands_repl.rs b/src/dispatching/repls/commands_repl.rs index 7f7db10fc..6967dfa0b 100644 --- a/src/dispatching/repls/commands_repl.rs +++ b/src/dispatching/repls/commands_repl.rs @@ -91,16 +91,13 @@ pub async fn commands_repl_with_listener<'a, R, Cmd, H, L, ListenerE, E, Args>( // commands. See . let ignore_update = |_upd| Box::pin(async {}); - Dispatcher::builder( - bot, - Update::filter_message().filter_command::().chain(dptree::endpoint(handler)), - ) - .default_handler(ignore_update) - .enable_ctrlc_handler() - .build() - .dispatch_with_listener( - listener, - LoggingErrorHandler::with_custom_text("An error from the update listener"), - ) - .await; + Dispatcher::builder(bot, Update::filter_message().filter_command::().endpoint(handler)) + .default_handler(ignore_update) + .enable_ctrlc_handler() + .build() + .dispatch_with_listener( + listener, + LoggingErrorHandler::with_custom_text("An error from the update listener"), + ) + .await; } diff --git a/src/dispatching/repls/repl.rs b/src/dispatching/repls/repl.rs index cecf90ade..38a8093d4 100644 --- a/src/dispatching/repls/repl.rs +++ b/src/dispatching/repls/repl.rs @@ -69,7 +69,7 @@ where // messages. See . let ignore_update = |_upd| Box::pin(async {}); - Dispatcher::builder(bot, Update::filter_message().chain(dptree::endpoint(handler))) + Dispatcher::builder(bot, Update::filter_message().endpoint(handler)) .default_handler(ignore_update) .enable_ctrlc_handler() .build() From 3bdd6bdcb79f4dcd7967c805466ff3561d0b9233 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Sun, 24 Jul 2022 18:56:53 +0600 Subject: [PATCH 02/66] Lay out the difference between REPLs and dispatching --- src/dispatching.rs | 24 ++++++++++++++++++++++++ src/dispatching/dispatcher.rs | 6 ++++++ src/dispatching/repls/commands_repl.rs | 6 ++++++ src/dispatching/repls/repl.rs | 6 ++++++ 4 files changed, 42 insertions(+) diff --git a/src/dispatching.rs b/src/dispatching.rs index e0440705b..fb482e343 100644 --- a/src/dispatching.rs +++ b/src/dispatching.rs @@ -187,12 +187,36 @@ //! useful features. See [`examples/dispatching_features.rs`] as a more involved //! example. //! +//! ## Dispatching or REPLs? +//! +//! The difference between dispatching and the REPLs ([`crate::repl`] & co) is +//! that dispatching allows you a greater degree of flexibility at the cost of a +//! bit more complicated setup. +//! +//! Here are things that dispatching can do, but REPLs can't: +//! - Handle different kinds of [`Update`]; +//! - [Pass dependencies](struct.DispatcherBuilder.html#method.dependencies) to +//! handlers; +//! - Disable a [default Ctrl-C +//! handling](struct.DispatcherBuilder.html#method.enable_ctrlc_handler); +//! - Control your +//! [default](struct.DispatcherBuilder.html#method.default_handler) and +//! [error](struct.DispatcherBuilder.html#method.error_handler) handlers; +//! - Use [dialogues](dialogue/index.html). +//! - Use [`dptree`]-related functionality. +//! - Probably more. +//! +//! Thus, REPLs are good for simple bots and rapid prototyping, but for more +//! involved scenarios, we recommend using [`DispatcherBuilder`]/[`Dispatcher`] +//! together with [`dptree`]. +//! //! [`examples/purchase.rs`]: https://github.com/teloxide/teloxide/blob/master/examples/purchase.rs //! [`Update::filter_message`]: crate::types::Update::filter_message //! [`Update::filter_callback_query`]: crate::types::Update::filter_callback_query //! [chain of responsibility]: https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern //! [dependency injection (DI)]: https://en.wikipedia.org/wiki/Dependency_injection //! [`examples/dispatching_features.rs`]: https://github.com/teloxide/teloxide/blob/master/examples/dispatching_features.rs +//! [`Update`]: crate::types::Update #[cfg(all(feature = "ctrlc_handler"))] pub mod repls; diff --git a/src/dispatching/dispatcher.rs b/src/dispatching/dispatcher.rs index 6ff568e66..27a82f47d 100644 --- a/src/dispatching/dispatcher.rs +++ b/src/dispatching/dispatcher.rs @@ -27,6 +27,9 @@ use std::{ }; /// The builder for [`Dispatcher`]. +/// +/// See also: ["Dispatching or +/// REPLs?"](../dispatching/index.html#dispatching-or-repls) pub struct DispatcherBuilder { bot: R, dependencies: DependencyMap, @@ -176,6 +179,9 @@ where /// determine a chat ID of an incoming update, it will be handled concurrently. /// Note that this behaviour can be altered with [`distribution_function`]. /// +/// See also: ["Dispatching or +/// REPLs?"](../dispatching/index.html#dispatching-or-repls) +/// /// [`distribution_function`]: DispatcherBuilder::distribution_function pub struct Dispatcher { bot: R, diff --git a/src/dispatching/repls/commands_repl.rs b/src/dispatching/repls/commands_repl.rs index 6967dfa0b..9f8f9b93a 100644 --- a/src/dispatching/repls/commands_repl.rs +++ b/src/dispatching/repls/commands_repl.rs @@ -18,6 +18,9 @@ use teloxide_core::requests::Requester; /// supply dependencies or describe more complex dispatch logic, please use /// [`Dispatcher`]. /// +/// See also: ["Dispatching or +/// REPLs?"](dispatching/index.html#dispatching-or-repls) +/// /// ## Caution /// /// **DO NOT** use this function together with [`Dispatcher`] and other REPLs, @@ -58,6 +61,9 @@ where /// supply dependencies or describe more complex dispatch logic, please use /// [`Dispatcher`]. /// +/// See also: ["Dispatching or +/// REPLs?"](dispatching/index.html#dispatching-or-repls) +/// /// ## Caution /// /// **DO NOT** use this function together with [`Dispatcher`] and other REPLs, diff --git a/src/dispatching/repls/repl.rs b/src/dispatching/repls/repl.rs index 38a8093d4..329c1e514 100644 --- a/src/dispatching/repls/repl.rs +++ b/src/dispatching/repls/repl.rs @@ -14,6 +14,9 @@ use teloxide_core::requests::Requester; /// REPLs are meant only for simple bots and rapid prototyping. If you need to /// supply dependencies or describe more complex dispatch logic, please use /// [`Dispatcher`]. +/// +/// See also: ["Dispatching or +/// REPLs?"](dispatching/index.html#dispatching-or-repls) /// /// ## Caution /// @@ -44,6 +47,9 @@ where /// supply dependencies or describe more complex dispatch logic, please use /// [`Dispatcher`]. /// +/// See also: ["Dispatching or +/// REPLs?"](dispatching/index.html#dispatching-or-repls) +/// /// # Caution /// /// **DO NOT** use this function together with [`Dispatcher`] and other REPLs, From 7b18d62296b8fb8e7e656a7de41f1003de16d816 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Sun, 24 Jul 2022 18:57:33 +0600 Subject: [PATCH 03/66] A doc fix for `Dispatcher` --- src/dispatching/dispatcher.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dispatching/dispatcher.rs b/src/dispatching/dispatcher.rs index 27a82f47d..9e0094018 100644 --- a/src/dispatching/dispatcher.rs +++ b/src/dispatching/dispatcher.rs @@ -174,7 +174,7 @@ where /// The base for update dispatching. /// -/// Updates from different chats are handles concurrently, whereas updates from +/// Updates from different chats are handled concurrently, whereas updates from /// the same chats are handled sequentially. If the dispatcher is unable to /// determine a chat ID of an incoming update, it will be handled concurrently. /// Note that this behaviour can be altered with [`distribution_function`]. From a59fb0ddea9e2a32fb5598973f2622e5a21c2a12 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Sun, 24 Jul 2022 19:01:36 +0600 Subject: [PATCH 04/66] Fix formatting --- src/dispatching/repls/repl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dispatching/repls/repl.rs b/src/dispatching/repls/repl.rs index 329c1e514..8ae7cbba8 100644 --- a/src/dispatching/repls/repl.rs +++ b/src/dispatching/repls/repl.rs @@ -14,7 +14,7 @@ use teloxide_core::requests::Requester; /// REPLs are meant only for simple bots and rapid prototyping. If you need to /// supply dependencies or describe more complex dispatch logic, please use /// [`Dispatcher`]. -/// +/// /// See also: ["Dispatching or /// REPLs?"](dispatching/index.html#dispatching-or-repls) /// From 4e2265a217d4d74f446a223e67510ba36ba88878 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Sun, 24 Jul 2022 19:03:37 +0600 Subject: [PATCH 05/66] Hide the definitions of endpoints in the doc Makes scrolling a bit easier. --- src/dispatching.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/dispatching.rs b/src/dispatching.rs index e0440705b..3be7897a7 100644 --- a/src/dispatching.rs +++ b/src/dispatching.rs @@ -104,6 +104,9 @@ //! //! Finally, we define our endpoints like this: //! +//!
+//! Show the endpoints +//! //! ```no_run //! # use teloxide::{Bot, adaptors::AutoSend}; //! # use teloxide::types::{Message, CallbackQuery}; @@ -147,6 +150,8 @@ //! } //! ``` //! +//!
+//! //! Each parameter is supplied as a dependency by teloxide. In particular: //! - `bot: AutoSend` comes from the dispatcher (see below); //! - `msg: Message` comes from [`Update::filter_message`]; From 793dbcfcdb1276f03a84fb617c6bce651fbf609f Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Sun, 24 Jul 2022 19:14:22 +0600 Subject: [PATCH 06/66] Make `dialogue` doc examples verifiable --- src/dispatching/dialogue.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/dispatching/dialogue.rs b/src/dispatching/dialogue.rs index 3af1ecc0a..724af2e29 100644 --- a/src/dispatching/dialogue.rs +++ b/src/dispatching/dialogue.rs @@ -13,21 +13,30 @@ //! [`examples/dialogue.rs`] clearly demonstrates the typical usage of //! dialogues. Your dialogue state can be represented as an enumeration: //! -//! ```ignore +//! ```no_run //! #[derive(Clone, Default)] //! pub enum State { //! #[default] //! Start, //! ReceiveFullName, -//! ReceiveAge { full_name: String }, -//! ReceiveLocation { full_name: String, age: u8 }, +//! ReceiveAge { +//! full_name: String, +//! }, +//! ReceiveLocation { +//! full_name: String, +//! age: u8, +//! }, //! } //! ``` //! //! Each state is associated with its respective handler: e.g., when a dialogue //! state is `ReceiveAge`, `receive_age` is invoked: //! -//! ```ignore +//! ```no_run +//! # use teloxide::{dispatching::dialogue::InMemStorage, prelude::*}; +//! # type MyDialogue = Dialogue>; +//! # type HandlerResult = Result<(), Box>; +//! # #[derive(Clone, Debug)] enum State { ReceiveLocation { full_name: String, age: u8 } } //! async fn receive_age( //! bot: AutoSend, //! msg: Message, @@ -55,13 +64,17 @@ //! the dialogue, just call [`Dialogue::exit`] and it will be removed from the //! underlying storage: //! -//! ```ignore +//! ```no_run +//! # use teloxide::{dispatching::dialogue::InMemStorage, prelude::*}; +//! # type MyDialogue = Dialogue>; +//! # type HandlerResult = Result<(), Box>; +//! # #[derive(Clone, Debug)] enum State {} //! async fn receive_location( //! bot: AutoSend, //! msg: Message, //! dialogue: MyDialogue, //! (full_name, age): (String, u8), // Available from `State::ReceiveLocation`. -//! ) -> anyhow::Result<()> { +//! ) -> HandlerResult { //! match msg.text() { //! Some(location) => { //! let message = From 8eaadf8f940cb17f11a7cf9b95d3c69bd8266ee9 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Sun, 24 Jul 2022 19:30:41 +0600 Subject: [PATCH 07/66] Expand a doc sentence according to code review --- src/dispatching.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dispatching.rs b/src/dispatching.rs index 3be7897a7..c0418a185 100644 --- a/src/dispatching.rs +++ b/src/dispatching.rs @@ -102,7 +102,7 @@ //! -- no problem, reuse [`dptree::Handler::filter`], [`dptree::case!`], and //! other combinators in the same way! //! -//! Finally, we define our endpoints like this: +//! Finally, we define our endpoints via simple `async` functions like this: //! //!
//! Show the endpoints From ab0292649fb73cbba8bbbb5aa8eb911b981e5e7a Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Sun, 24 Jul 2022 19:32:09 +0600 Subject: [PATCH 08/66] Apply a tiny doc review suggestion Co-authored-by: Waffle Maybe --- src/dispatching.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dispatching.rs b/src/dispatching.rs index fb482e343..bb2a7e7bb 100644 --- a/src/dispatching.rs +++ b/src/dispatching.rs @@ -190,7 +190,7 @@ //! ## Dispatching or REPLs? //! //! The difference between dispatching and the REPLs ([`crate::repl`] & co) is -//! that dispatching allows you a greater degree of flexibility at the cost of a +//! that dispatching gives you a greater degree of flexibility at the cost of a //! bit more complicated setup. //! //! Here are things that dispatching can do, but REPLs can't: From 0f526ebe99b75a6aeff3f577256dd8a572bb2bd5 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Sun, 24 Jul 2022 19:40:35 +0600 Subject: [PATCH 09/66] Simplify the doc sentence (code review) --- src/dispatching.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dispatching.rs b/src/dispatching.rs index fb482e343..4d4a985a8 100644 --- a/src/dispatching.rs +++ b/src/dispatching.rs @@ -207,8 +207,7 @@ //! - Probably more. //! //! Thus, REPLs are good for simple bots and rapid prototyping, but for more -//! involved scenarios, we recommend using [`DispatcherBuilder`]/[`Dispatcher`] -//! together with [`dptree`]. +//! involved scenarios, we recommend using dispatching over REPLs. //! //! [`examples/purchase.rs`]: https://github.com/teloxide/teloxide/blob/master/examples/purchase.rs //! [`Update::filter_message`]: crate::types::Update::filter_message From 1942b8f49b7987e205cc8444b0a4d890eacc049e Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 24 Jul 2022 19:11:57 +0400 Subject: [PATCH 10/66] Improve link formatting --- src/dispatching.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/dispatching.rs b/src/dispatching.rs index 25a60e525..0fc1ceda7 100644 --- a/src/dispatching.rs +++ b/src/dispatching.rs @@ -195,20 +195,21 @@ //! //! Here are things that dispatching can do, but REPLs can't: //! - Handle different kinds of [`Update`]; -//! - [Pass dependencies](struct.DispatcherBuilder.html#method.dependencies) to -//! handlers; -//! - Disable a [default Ctrl-C -//! handling](struct.DispatcherBuilder.html#method.enable_ctrlc_handler); -//! - Control your -//! [default](struct.DispatcherBuilder.html#method.default_handler) and -//! [error](struct.DispatcherBuilder.html#method.error_handler) handlers; -//! - Use [dialogues](dialogue/index.html). +//! - [Pass dependencies] to handlers; +//! - Disable a [default Ctrl-C handling]; +//! - Control your [default] and [error] handlers; +//! - Use [dialogues]. //! - Use [`dptree`]-related functionality. //! - Probably more. //! //! Thus, REPLs are good for simple bots and rapid prototyping, but for more //! involved scenarios, we recommend using dispatching over REPLs. //! +//! [Pass dependencies]: DispatcherBuilder#method.dependencies +//! [default Ctrl-C handling]: DispatcherBuilder#method.enable_ctrlc_handler +//! [default]: DispatcherBuilder#method.default_handler +//! [error]: DispatcherBuilder#method.error_handler +//! [dialogues]: dialogue //! [`examples/purchase.rs`]: https://github.com/teloxide/teloxide/blob/master/examples/purchase.rs //! [`Update::filter_message`]: crate::types::Update::filter_message //! [`Update::filter_callback_query`]: crate::types::Update::filter_callback_query From 62cbda51d2205a4fa74ffe81a9fc8b1b494fe183 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 24 Jul 2022 19:46:48 +0400 Subject: [PATCH 11/66] Make filter ext docs a little bit nicer --- src/dispatching/filter_ext.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dispatching/filter_ext.rs b/src/dispatching/filter_ext.rs index 69fb9bb71..029a52459 100644 --- a/src/dispatching/filter_ext.rs +++ b/src/dispatching/filter_ext.rs @@ -56,11 +56,11 @@ mod private { macro_rules! define_message_ext { ($( ($func:ident, $fn_name:path) ,)*) => { define_ext! { - MessageFilterExt, crate::types::Message => + MessageFilterExt, Message => $(( $func, (|x| $fn_name(&x).map(ToOwned::to_owned)), - concat!("Applies the [`crate::types::", stringify!($fn_name), "`] filter.") + concat!("Applies the [`", stringify!($fn_name), "`] filter.") ),)* } } @@ -89,14 +89,14 @@ define_message_ext! { macro_rules! define_update_ext { ($( ($func:ident, $kind:path, $Allowed:ident) ,)*) => { define_ext! { - UpdateFilterExt, crate::types::Update => + UpdateFilterExt, Update => $(( $func, |update: Update| match update.kind { $kind(x) => Some(x), _ => None, }, - concat!("Filters out [`crate::types::", stringify!($kind), "`] objects."), + concat!("Filters out [`", stringify!($kind), "`] objects."), $Allowed ),)* } From d0add19b293143a9b599fce6260ae226b9d619a0 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Sat, 20 Aug 2022 16:16:08 +0600 Subject: [PATCH 12/66] Avoid ending punctuation in short list items --- CODE_STYLE.md | 26 +++++++++++++++++++++++++- src/dispatching.rs | 24 ++++++++++++------------ src/dispatching/repls/commands_repl.rs | 4 ++-- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/CODE_STYLE.md b/CODE_STYLE.md index 0fa0756b1..503844c20 100644 --- a/CODE_STYLE.md +++ b/CODE_STYLE.md @@ -42,7 +42,31 @@ Good: pub fn make_request(url: &str) -> String { ... } ``` - 2. Also, link resources in your comments when possible: + 2. Do not use ending punctuation in short list items (usually containing just one phrase or sentence). Bad: + +```md + - Handle different kinds of Update. + - Pass dependencies to handlers. + - Disable a default Ctrl-C handling. +``` + +Bad: + +```md + - Handle different kinds of Update; + - Pass dependencies to handlers; + - Disable a default Ctrl-C handling. +``` + +Good: + +```md + - Handle different kinds of Update + - Pass dependencies to handlers + - Disable a default Ctrl-C handling +``` + + 3. Link resources in your comments when possible: ```rust /// Download a file from Telegram. diff --git a/src/dispatching.rs b/src/dispatching.rs index 18bf5eabb..0fe237bd1 100644 --- a/src/dispatching.rs +++ b/src/dispatching.rs @@ -153,12 +153,12 @@ //!
//! //! Each parameter is supplied as a dependency by teloxide. In particular: -//! - `bot: AutoSend` comes from the dispatcher (see below); -//! - `msg: Message` comes from [`Update::filter_message`]; -//! - `q: CallbackQuery` comes from [`Update::filter_callback_query`]; -//! - `dialogue: MyDialogue` comes from [`dialogue::enter`]; +//! - `bot: AutoSend` comes from the dispatcher (see below) +//! - `msg: Message` comes from [`Update::filter_message`] +//! - `q: CallbackQuery` comes from [`Update::filter_callback_query`] +//! - `dialogue: MyDialogue` comes from [`dialogue::enter`] //! - `full_name: String` comes from `dptree::case![State::ReceiveProductChoice -//! { full_name }]`. +//! { full_name }]` //! //! Inside `main`, we plug the schema into [`Dispatcher`] like this: //! @@ -199,13 +199,13 @@ //! bit more complicated setup. //! //! Here are things that dispatching can do, but REPLs can't: -//! - Handle different kinds of [`Update`]; -//! - [Pass dependencies] to handlers; -//! - Disable a [default Ctrl-C handling]; -//! - Control your [default] and [error] handlers; -//! - Use [dialogues]. -//! - Use [`dptree`]-related functionality. -//! - Probably more. +//! - Handle different kinds of [`Update`] +//! - [Pass dependencies] to handlers +//! - Disable a [default Ctrl-C handling] +//! - Control your [default] and [error] handlers +//! - Use [dialogues] +//! - Use [`dptree`]-related functionality +//! - Probably more //! //! Thus, REPLs are good for simple bots and rapid prototyping, but for more //! involved scenarios, we recommend using dispatching over REPLs. diff --git a/src/dispatching/repls/commands_repl.rs b/src/dispatching/repls/commands_repl.rs index 9f8f9b93a..fb89b21a3 100644 --- a/src/dispatching/repls/commands_repl.rs +++ b/src/dispatching/repls/commands_repl.rs @@ -29,7 +29,7 @@ use teloxide_core::requests::Requester; /// /// ## Dependency requirements /// -/// - Those of [`HandlerExt::filter_command`]. +/// - Those of [`HandlerExt::filter_command`] /// /// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop /// [`Dispatcher`]: crate::dispatching::Dispatcher @@ -72,7 +72,7 @@ where /// /// ## Dependency requirements /// -/// - Those of [`HandlerExt::filter_command`]. +/// - Those of [`HandlerExt::filter_command`] /// /// [`Dispatcher`]: crate::dispatching::Dispatcher /// [`commands_repl`]: crate::dispatching::repls::commands_repl() From 44e134c47ef177073f64e6cad4f62ea43b7517d6 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 28 Aug 2022 15:19:38 +0400 Subject: [PATCH 13/66] update `teloxide-macros` --- Cargo.toml | 4 +-- tests/command.rs | 13 ++++++--- tests/dialogue_state.rs | 64 ----------------------------------------- 3 files changed, 11 insertions(+), 70 deletions(-) delete mode 100644 tests/dialogue_state.rs diff --git a/Cargo.toml b/Cargo.toml index 076616913..aa7ab5710 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ full = [ [dependencies] teloxide-core = { version = "0.7.0", default-features = false } -teloxide-macros = { version = "0.6.3", optional = true } +#teloxide-macros = { version = "0.6.3", optional = true } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } @@ -67,7 +67,7 @@ dptree = "0.3.0" # These lines are used only for development. # teloxide-core = { git = "https://github.com/teloxide/teloxide-core", rev = "b13393d", default-features = false } -# teloxide-macros = { git = "https://github.com/teloxide/teloxide-macros", rev = "44d91c5", optional = true } +teloxide-macros = { git = "https://github.com/teloxide/teloxide-macros", rev = "c851482", optional = true } # dptree = { git = "https://github.com/teloxide/dptree", rev = "df578e4" } tokio = { version = "1.8", features = ["fs"] } diff --git a/tests/command.rs b/tests/command.rs index 4b59ac485..d3322ea4a 100644 --- a/tests/command.rs +++ b/tests/command.rs @@ -233,8 +233,8 @@ fn rename_rules() { GggGgg, #[command(rename = "SCREAMING-KEBAB-CASE")] HhhHhh, - #[command(rename = "Bar")] - Foo, + //#[command(rename = "Bar")] + //Foo, } assert_eq!(DefaultCommands::AaaAaa, DefaultCommands::parse("/aaaaaa", "").unwrap()); @@ -245,10 +245,15 @@ fn rename_rules() { assert_eq!(DefaultCommands::FffFff, DefaultCommands::parse("/FFF_FFF", "").unwrap()); assert_eq!(DefaultCommands::GggGgg, DefaultCommands::parse("/ggg-ggg", "").unwrap()); assert_eq!(DefaultCommands::HhhHhh, DefaultCommands::parse("/HHH-HHH", "").unwrap()); - assert_eq!(DefaultCommands::Foo, DefaultCommands::parse("/Bar", "").unwrap()); + //assert_eq!(DefaultCommands::Foo, DefaultCommands::parse("/Bar", + // "").unwrap()); + // assert_eq!( + // "/aaaaaa\n/BBBBBB\n/CccCcc\n/dddDdd\n/eee_eee\n/FFF_FFF\n/ggg-ggg\n/ + // HHH-HHH\n/Bar", DefaultCommands::descriptions().to_string() + // ); assert_eq!( - "/aaaaaa\n/BBBBBB\n/CccCcc\n/dddDdd\n/eee_eee\n/FFF_FFF\n/ggg-ggg\n/HHH-HHH\n/Bar", + "/aaaaaa\n/BBBBBB\n/CccCcc\n/dddDdd\n/eee_eee\n/FFF_FFF\n/ggg-ggg\n/HHH-HHH", DefaultCommands::descriptions().to_string() ); } diff --git a/tests/dialogue_state.rs b/tests/dialogue_state.rs deleted file mode 100644 index 567d8f51f..000000000 --- a/tests/dialogue_state.rs +++ /dev/null @@ -1,64 +0,0 @@ -#![allow(deprecated)] - -#[cfg(feature = "macros")] -use teloxide::macros::DialogueState; -// We put tests here because macro expand in unit tests in the crate was a -// failure - -#[test] -#[cfg(feature = "macros")] -fn compile_test() { - #[allow(dead_code)] - #[derive(DialogueState, Clone)] - #[handler_out(Result<(), teloxide::RequestError>)] - enum State { - #[handler(handle_start)] - Start, - - #[handler(handle_have_data)] - HaveData(String), - } - - impl Default for State { - fn default() -> Self { - Self::Start - } - } - - async fn handle_start() -> Result<(), teloxide::RequestError> { - Ok(()) - } - - async fn handle_have_data() -> Result<(), teloxide::RequestError> { - Ok(()) - } -} - -#[test] -#[cfg(feature = "macros")] -fn compile_test_generics() { - #[allow(dead_code)] - #[derive(DialogueState, Clone)] - #[handler_out(Result<(), teloxide::RequestError>)] - enum State { - #[handler(handle_start)] - Start, - - #[handler(handle_have_data)] - HaveData(X), - } - - impl Default for State { - fn default() -> Self { - Self::Start - } - } - - async fn handle_start() -> Result<(), teloxide::RequestError> { - Ok(()) - } - - async fn handle_have_data() -> Result<(), teloxide::RequestError> { - Ok(()) - } -} From 4cb9d93d591e2aae313e51ed46b19f5c97228ec3 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 4 Sep 2022 17:01:13 +0400 Subject: [PATCH 14/66] Update changelog --- CHANGELOG.md | 5 +++++ Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a82450e9..6b6c8f829 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## unreleased +### Changed + + +- Updated `teloxide-macros` see its [changelog](https://github.com/teloxide/teloxide-macros/blob/master/CHANGELOG.md#unreleased) for more + ## 0.10.1 - 2022-07-22 ### Fixed diff --git a/Cargo.toml b/Cargo.toml index aa7ab5710..01810db71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,7 +67,7 @@ dptree = "0.3.0" # These lines are used only for development. # teloxide-core = { git = "https://github.com/teloxide/teloxide-core", rev = "b13393d", default-features = false } -teloxide-macros = { git = "https://github.com/teloxide/teloxide-macros", rev = "c851482", optional = true } +teloxide-macros = { git = "https://github.com/teloxide/teloxide-macros", rev = "0e79c37", optional = true } # dptree = { git = "https://github.com/teloxide/dptree", rev = "df578e4" } tokio = { version = "1.8", features = ["fs"] } From 8917e05bf8f450696f622e1b06be157641136be4 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 5 Sep 2022 18:52:24 +0400 Subject: [PATCH 15/66] Turn `UpdateListener`'s generic error into an associated type --- CHANGELOG.md | 5 +++++ src/dispatching/dispatcher.rs | 8 +++---- src/dispatching/repls/commands_repl.rs | 6 ++--- src/dispatching/repls/repl.rs | 6 ++--- src/dispatching/update_listeners.rs | 22 ++++++++++++++----- src/dispatching/update_listeners/polling.rs | 6 +++-- .../update_listeners/stateful_listener.rs | 18 +++++---------- .../update_listeners/webhooks/axum.rs | 12 +++++++--- src/utils/shutdown_token.rs | 2 +- 9 files changed, 52 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a82450e9..0c21257fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## unreleased +### Changed + +- `UpdateListener` now has an associated type `Err` instead of a generic +- `AsUpdateStream` now has an associated type `StreamErr` instead of a generic + ## 0.10.1 - 2022-07-22 ### Fixed diff --git a/src/dispatching/dispatcher.rs b/src/dispatching/dispatcher.rs index 9e0094018..42ae20bd7 100644 --- a/src/dispatching/dispatcher.rs +++ b/src/dispatching/dispatcher.rs @@ -287,14 +287,14 @@ where /// This method adds the same dependencies as [`Dispatcher::dispatch`]. /// /// [`shutdown`]: ShutdownToken::shutdown - pub async fn dispatch_with_listener<'a, UListener, ListenerE, Eh>( + pub async fn dispatch_with_listener<'a, UListener, Eh>( &'a mut self, mut update_listener: UListener, update_listener_error_handler: Arc, ) where - UListener: UpdateListener + 'a, - Eh: ErrorHandler + 'a, - ListenerE: Debug, + UListener: UpdateListener + 'a, + Eh: ErrorHandler + 'a, + UListener::Err: Debug, { // FIXME: there should be a way to check if dependency is already inserted let me = self.bot.get_me().send().await.expect("Failed to retrieve 'me'"); diff --git a/src/dispatching/repls/commands_repl.rs b/src/dispatching/repls/commands_repl.rs index fb89b21a3..ceeeb9179 100644 --- a/src/dispatching/repls/commands_repl.rs +++ b/src/dispatching/repls/commands_repl.rs @@ -78,7 +78,7 @@ where /// [`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, L, ListenerE, E, Args>( +pub async fn commands_repl_with_listener<'a, R, Cmd, H, L, E, Args>( bot: R, handler: H, listener: L, @@ -86,8 +86,8 @@ pub async fn commands_repl_with_listener<'a, R, Cmd, H, L, ListenerE, E, Args>( ) where Cmd: BotCommands + Send + Sync + 'static, H: Injectable, Args> + Send + Sync + 'static, - L: UpdateListener + Send + 'a, - ListenerE: Debug + Send + 'a, + L: UpdateListener + Send + 'a, + L::Err: Debug + Send + 'a, R: Requester + Clone + Send + Sync + 'static, E: Debug + Send + Sync + 'static, { diff --git a/src/dispatching/repls/repl.rs b/src/dispatching/repls/repl.rs index 8ae7cbba8..529cc711c 100644 --- a/src/dispatching/repls/repl.rs +++ b/src/dispatching/repls/repl.rs @@ -60,11 +60,11 @@ where /// [`repl`]: crate::dispatching::repls::repl() /// [`UpdateListener`]: crate::dispatching::update_listeners::UpdateListener #[cfg(feature = "ctrlc_handler")] -pub async fn repl_with_listener<'a, R, H, E, L, ListenerE, Args>(bot: R, handler: H, listener: L) +pub async fn repl_with_listener<'a, R, H, E, L, Args>(bot: R, handler: H, listener: L) where H: Injectable, Args> + Send + Sync + 'static, - L: UpdateListener + Send + 'a, - ListenerE: Debug, + L: UpdateListener + Send + 'a, + L::Err: Debug, Result<(), E>: OnError, E: Debug + Send + Sync + 'static, R: Requester + Clone + Send + Sync + 'static, diff --git a/src/dispatching/update_listeners.rs b/src/dispatching/update_listeners.rs index 9c4abc2fb..f7153ee11 100644 --- a/src/dispatching/update_listeners.rs +++ b/src/dispatching/update_listeners.rs @@ -59,7 +59,12 @@ pub use self::{ /// - [`AsUpdateStream::as_stream`] /// /// [module-level documentation]: mod@self -pub trait UpdateListener: for<'a> AsUpdateStream<'a, E> { +pub trait UpdateListener: + for<'a> AsUpdateStream<'a, StreamErr = ::Err> +{ + /// The type of errors that can be returned from this listener. + type Err; + /// The type of token which allows to stop this listener. type StopToken: StopToken + Send; @@ -110,7 +115,14 @@ pub trait UpdateListener: for<'a> AsUpdateStream<'a, E> { /// [`UpdateListener`]'s supertrait/extension. /// /// This trait is a workaround to not require GAT. -pub trait AsUpdateStream<'a, E> { +pub trait AsUpdateStream<'a> { + /// Error that can be returned from the [`Stream`] + /// + /// [`Stream`]: AsUpdateStream::Stream + // NB: This should be named differently to `UpdateListener::Err`, so that it's + // unambiguous + type StreamErr; + /// The stream of updates from Telegram. // HACK: There is currently no way to write something like // `-> impl for<'a> AsUpdateStream<'a, E, Stream: Send>`. Since we return @@ -119,7 +131,7 @@ pub trait AsUpdateStream<'a, E> { // // Without this it's, for example, impossible to spawn a tokio task with // teloxide polling. - type Stream: Stream> + Send + 'a; + type Stream: Stream> + Send + 'a; /// Creates the update [`Stream`]. /// @@ -128,9 +140,9 @@ pub trait AsUpdateStream<'a, E> { } #[inline(always)] -pub(crate) fn assert_update_listener(listener: L) -> L +pub(crate) fn assert_update_listener(listener: L) -> L where - L: UpdateListener, + L: UpdateListener, { listener } diff --git a/src/dispatching/update_listeners/polling.rs b/src/dispatching/update_listeners/polling.rs index 118fc02ec..74824bbaf 100644 --- a/src/dispatching/update_listeners/polling.rs +++ b/src/dispatching/update_listeners/polling.rs @@ -291,7 +291,8 @@ pub struct PollingStream<'a, B: Requester> { in_flight: Option<::Send>, } -impl UpdateListener for Polling { +impl UpdateListener for Polling { + type Err = B::Err; type StopToken = AsyncStopToken; fn stop_token(&mut self) -> Self::StopToken { @@ -309,7 +310,8 @@ impl UpdateListener for Polling { } } -impl<'a, B: Requester + Send + 'a> AsUpdateStream<'a, B::Err> for Polling { +impl<'a, B: Requester + Send + 'a> AsUpdateStream<'a> for Polling { + type StreamErr = B::Err; type Stream = PollingStream<'a, B>; fn as_stream(&'a mut self) -> Self::Stream { diff --git a/src/dispatching/update_listeners/stateful_listener.rs b/src/dispatching/update_listeners/stateful_listener.rs index e37a9efc1..7f23d795b 100644 --- a/src/dispatching/update_listeners/stateful_listener.rs +++ b/src/dispatching/update_listeners/stateful_listener.rs @@ -5,7 +5,7 @@ use futures::Stream; use crate::{ dispatching::{ stop_token::{self, StopToken}, - update_listeners::{AsUpdateStream, UpdateListener}, + update_listeners::{assert_update_listener, AsUpdateStream, UpdateListener}, }, types::{AllowedUpdate, Update}, }; @@ -103,7 +103,7 @@ where } } -impl<'a, St, Assf, Sf, Hauf, Thf, Strm, E> AsUpdateStream<'a, E> +impl<'a, St, Assf, Sf, Hauf, Thf, Strm, E> AsUpdateStream<'a> for StatefulListener where (St, Strm): 'a, @@ -111,6 +111,7 @@ where Assf: FnMut(&'a mut St) -> Strm, Strm: Stream>, { + type StreamErr = E; type Stream = Strm; fn as_stream(&'a mut self) -> Self::Stream { @@ -118,15 +119,15 @@ where } } -impl UpdateListener - for StatefulListener +impl UpdateListener for StatefulListener where - Self: for<'a> AsUpdateStream<'a, E>, + Self: for<'a> AsUpdateStream<'a, StreamErr = E>, Sf: FnMut(&mut St) -> Stt, Stt: StopToken + Send, Hauf: FnMut(&mut St, &mut dyn Iterator), Thf: Fn(&St) -> Option, { + type Err = E; type StopToken = Stt; fn stop_token(&mut self) -> Stt { @@ -143,10 +144,3 @@ where self.timeout_hint.as_ref().and_then(|f| f(&self.state)) } } - -fn assert_update_listener(l: L) -> L -where - L: UpdateListener, -{ - l -} diff --git a/src/dispatching/update_listeners/webhooks/axum.rs b/src/dispatching/update_listeners/webhooks/axum.rs index 6c16bdf0d..82669a0f3 100644 --- a/src/dispatching/update_listeners/webhooks/axum.rs +++ b/src/dispatching/update_listeners/webhooks/axum.rs @@ -38,7 +38,10 @@ use crate::{ /// /// [`axum_to_router`] and [`axum_no_setup`] for lower-level versions of this /// function. -pub async fn axum(bot: R, options: Options) -> Result, R::Err> +pub async fn axum( + bot: R, + options: Options, +) -> Result, R::Err> where R: Requester + Send + 'static, ::DeleteWebhook: Send, @@ -107,7 +110,10 @@ where pub async fn axum_to_router( bot: R, mut options: Options, -) -> Result<(impl UpdateListener, impl Future + Send, axum::Router), R::Err> +) -> Result< + (impl UpdateListener, impl Future + Send, axum::Router), + R::Err, +> where R: Requester + Send, ::DeleteWebhook: Send, @@ -148,7 +154,7 @@ where /// function. pub fn axum_no_setup( options: Options, -) -> (impl UpdateListener, impl Future, axum::Router) { +) -> (impl UpdateListener, impl Future, axum::Router) { use crate::{ dispatching::{ stop_token::AsyncStopToken, diff --git a/src/utils/shutdown_token.rs b/src/utils/shutdown_token.rs index 2447f6fd2..998a64ae1 100644 --- a/src/utils/shutdown_token.rs +++ b/src/utils/shutdown_token.rs @@ -93,7 +93,7 @@ impl fmt::Display for IdleShutdownError { impl std::error::Error for IdleShutdownError {} -pub(crate) fn shutdown_check_timeout_for(update_listener: &impl UpdateListener) -> Duration { +pub(crate) fn shutdown_check_timeout_for(update_listener: &impl UpdateListener) -> Duration { const MIN_SHUTDOWN_CHECK_TIMEOUT: Duration = Duration::from_secs(1); const DZERO: Duration = Duration::ZERO; From 11fe8baebff1855b7897199071d8ccd1b4b9fa96 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 5 Sep 2022 18:56:24 +0400 Subject: [PATCH 16/66] Correct a comment --- src/dispatching/update_listeners.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/dispatching/update_listeners.rs b/src/dispatching/update_listeners.rs index f7153ee11..421690378 100644 --- a/src/dispatching/update_listeners.rs +++ b/src/dispatching/update_listeners.rs @@ -124,13 +124,9 @@ pub trait AsUpdateStream<'a> { type StreamErr; /// The stream of updates from Telegram. - // 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. + // NB: `Send` is not strictly required here, but it makes it easier to return + // `impl AsUpdateStream` and also you want `Send` streams almost (?) always + // anyway. type Stream: Stream> + Send + 'a; /// Creates the update [`Stream`]. From f39180543e5b97a6d4fe504ab99e8510971dff46 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 8 Sep 2022 18:36:08 +0400 Subject: [PATCH 17/66] Fix command parsing in `examples/button.rs` --- examples/buttons.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/buttons.rs b/examples/buttons.rs index b510e24ac..8ce0e21ac 100644 --- a/examples/buttons.rs +++ b/examples/buttons.rs @@ -4,7 +4,7 @@ use teloxide::{ prelude::*, types::{ InlineKeyboardButton, InlineKeyboardMarkup, InlineQueryResultArticle, InputMessageContent, - InputMessageContentText, + InputMessageContentText, Me, }, utils::command::BotCommands, }; @@ -61,9 +61,10 @@ fn make_keyboard() -> InlineKeyboardMarkup { async fn message_handler( m: Message, bot: AutoSend, + me: Me, ) -> Result<(), Box> { if let Some(text) = m.text() { - match BotCommands::parse(text, "buttons") { + match BotCommands::parse(text, me.username()) { Ok(Command::Help) => { // Just send the description of all commands. bot.send_message(m.chat.id, Command::descriptions().to_string()).await?; From 9527f82608eda0b1a737aa04f473e5dc50d0e677 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 9 Sep 2022 21:14:44 +0400 Subject: [PATCH 18/66] Replace `StopToken` trait with a single type --- CHANGELOG.md | 5 ++ src/dispatching.rs | 1 - src/dispatching/dispatcher.rs | 2 +- src/dispatching/stop_token.rs | 79 ------------------- src/dispatching/update_listeners.rs | 7 +- src/dispatching/update_listeners/polling.rs | 15 ++-- .../update_listeners/stateful_listener.rs | 51 ++---------- .../update_listeners/webhooks/axum.rs | 22 +++--- src/lib.rs | 1 + src/stop.rs | 67 ++++++++++++++++ 10 files changed, 97 insertions(+), 153 deletions(-) delete mode 100644 src/dispatching/stop_token.rs create mode 100644 src/stop.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index a27331e8b..c4995282a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated `teloxide-macros` see its [changelog](https://github.com/teloxide/teloxide-macros/blob/master/CHANGELOG.md#unreleased) for more - `UpdateListener` now has an associated type `Err` instead of a generic - `AsUpdateStream` now has an associated type `StreamErr` instead of a generic +- Rename `dispatching::stop_token::{AsyncStopToken, AsyncStopFlag}` => `stop::{StopToken, StopFlag}` + +### Removed + +- `dispatching::stop_token::StopToken` trait (all uses are replaced with `stop::StopToken` structure) ## 0.10.1 - 2022-07-22 diff --git a/src/dispatching.rs b/src/dispatching.rs index 0fe237bd1..b650b308a 100644 --- a/src/dispatching.rs +++ b/src/dispatching.rs @@ -233,7 +233,6 @@ mod filter_ext; mod handler_description; mod handler_ext; mod handler_factory; -pub mod stop_token; pub mod update_listeners; pub use crate::utils::shutdown_token::{IdleShutdownError, ShutdownToken}; diff --git a/src/dispatching/dispatcher.rs b/src/dispatching/dispatcher.rs index 42ae20bd7..c44ac97e2 100644 --- a/src/dispatching/dispatcher.rs +++ b/src/dispatching/dispatcher.rs @@ -1,6 +1,6 @@ use crate::{ dispatching::{ - distribution::default_distribution_function, stop_token::StopToken, update_listeners, + distribution::default_distribution_function, update_listeners, update_listeners::UpdateListener, DefaultKey, DpHandlerDescription, ShutdownToken, }, error_handlers::{ErrorHandler, LoggingErrorHandler}, diff --git a/src/dispatching/stop_token.rs b/src/dispatching/stop_token.rs deleted file mode 100644 index f9c25aff3..000000000 --- a/src/dispatching/stop_token.rs +++ /dev/null @@ -1,79 +0,0 @@ -//! A stop token used to stop a listener. - -use std::{future::Future, pin::Pin, task}; - -use futures::future::{pending, AbortHandle, Abortable, Pending}; - -/// A stop token allows you to stop a listener. -/// -/// See also: [`UpdateListener::stop_token`]. -/// -/// [`UpdateListener::stop_token`]: -/// crate::dispatching::update_listeners::UpdateListener::stop_token -pub trait StopToken { - /// Stop the listener linked to this token. - fn stop(self); -} - -/// A stop token which does nothing. May be used in prototyping or in cases -/// where you do not care about graceful shutdowning. -pub struct Noop; - -impl StopToken for Noop { - fn stop(self) {} -} - -/// A stop token which corresponds to [`AsyncStopFlag`]. -#[derive(Clone)] -pub struct AsyncStopToken(AbortHandle); - -/// A flag which corresponds to [`AsyncStopToken`]. -/// -/// To know if the stop token was used you can either repeatedly call -/// [`is_stopped`] or use this type as a `Future`. -/// -/// [`is_stopped`]: AsyncStopFlag::is_stopped -#[pin_project::pin_project] -#[derive(Clone)] -pub struct AsyncStopFlag(#[pin] Abortable>); - -impl AsyncStopToken { - /// Create a new token/flag pair. - #[must_use = "This function is pure, that is does nothing unless its output is used"] - pub fn new_pair() -> (Self, AsyncStopFlag) { - let (handle, reg) = AbortHandle::new_pair(); - let token = Self(handle); - let flag = AsyncStopFlag(Abortable::new(pending(), reg)); - - (token, flag) - } -} - -impl StopToken for AsyncStopToken { - fn stop(self) { - self.0.abort() - } -} - -impl AsyncStopFlag { - /// Returns true if the stop token linked to `self` was used. - #[must_use = "This function is pure, that is does nothing unless its output is used"] - pub fn is_stopped(&self) -> bool { - self.0.is_aborted() - } -} - -/// This future resolves when a stop token was used. -impl Future for AsyncStopFlag { - type Output = (); - - fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { - self.project().0.poll(cx).map(|res| { - debug_assert!( - res.is_err(), - "Pending Future can't ever be resolved, so Abortable is only resolved when \ - canceled" - ); - }) - } -} diff --git a/src/dispatching/update_listeners.rs b/src/dispatching/update_listeners.rs index 421690378..4103e3984 100644 --- a/src/dispatching/update_listeners.rs +++ b/src/dispatching/update_listeners.rs @@ -35,7 +35,7 @@ use futures::Stream; use std::time::Duration; use crate::{ - dispatching::stop_token::StopToken, + stop::StopToken, types::{AllowedUpdate, Update}, }; @@ -65,9 +65,6 @@ pub trait UpdateListener: /// The type of errors that can be returned from this listener. type Err; - /// The type of token which allows to stop this listener. - type StopToken: StopToken + Send; - /// Returns a token which stops this listener. /// /// The [`stop`] function of the token is not guaranteed to have an @@ -81,7 +78,7 @@ pub trait UpdateListener: /// soon as all cached updates are returned. #[must_use = "This function doesn't stop listening, to stop listening you need to call `stop` \ on the returned token"] - fn stop_token(&mut self) -> Self::StopToken; + fn stop_token(&mut self) -> StopToken; /// Hint which updates should the listener listen for. /// diff --git a/src/dispatching/update_listeners/polling.rs b/src/dispatching/update_listeners/polling.rs index 74824bbaf..1543b3226 100644 --- a/src/dispatching/update_listeners/polling.rs +++ b/src/dispatching/update_listeners/polling.rs @@ -13,11 +13,9 @@ use std::{ use futures::{ready, stream::Stream}; use crate::{ - dispatching::{ - stop_token::{AsyncStopFlag, AsyncStopToken}, - update_listeners::{assert_update_listener, AsUpdateStream, UpdateListener}, - }, + dispatching::update_listeners::{assert_update_listener, AsUpdateStream, UpdateListener}, requests::{HasPayload, Request, Requester}, + stop::{mk_stop_token, StopFlag, StopToken}, types::{AllowedUpdate, Update}, }; @@ -98,7 +96,7 @@ where /// See also: [`polling_default`], [`Polling`]. pub fn build(self) -> Polling { let Self { bot, timeout, limit, allowed_updates, drop_pending_updates } = self; - let (token, flag) = AsyncStopToken::new_pair(); + let (token, flag) = mk_stop_token(); let polling = Polling { bot, timeout, limit, allowed_updates, drop_pending_updates, flag, token }; @@ -242,8 +240,8 @@ pub struct Polling { limit: Option, allowed_updates: Option>, drop_pending_updates: bool, - flag: AsyncStopFlag, - token: AsyncStopToken, + flag: StopFlag, + token: StopToken, } impl Polling @@ -293,9 +291,8 @@ pub struct PollingStream<'a, B: Requester> { impl UpdateListener for Polling { type Err = B::Err; - type StopToken = AsyncStopToken; - fn stop_token(&mut self) -> Self::StopToken { + fn stop_token(&mut self) -> StopToken { self.token.clone() } diff --git a/src/dispatching/update_listeners/stateful_listener.rs b/src/dispatching/update_listeners/stateful_listener.rs index 7f23d795b..0eec921da 100644 --- a/src/dispatching/update_listeners/stateful_listener.rs +++ b/src/dispatching/update_listeners/stateful_listener.rs @@ -3,10 +3,8 @@ use std::time::Duration; use futures::Stream; use crate::{ - dispatching::{ - stop_token::{self, StopToken}, - update_listeners::{assert_update_listener, AsUpdateStream, UpdateListener}, - }, + dispatching::update_listeners::{AsUpdateStream, UpdateListener}, + stop::StopToken, types::{AllowedUpdate, Update}, }; @@ -30,7 +28,7 @@ pub struct StatefulListener { /// The function used as [`UpdateListener::stop_token`]. /// - /// Must implement `FnMut(&mut St) -> impl StopToken`. + /// Must implement `FnMut(&mut St) -> StopToken`. pub stop_token: Sf, /// The function used as [`UpdateListener::hint_allowed_updates`]. @@ -68,41 +66,6 @@ impl StatefulListener { } } -impl - StatefulListener< - S, - for<'a> fn(&'a mut S) -> &'a mut S, - for<'a> fn(&'a mut S) -> stop_token::Noop, - Haufn, - Thfn, - > -where - S: Stream> + Unpin + Send + 'static, -{ - /// Creates a new update listener from a stream of updates which ignores - /// stop signals. - /// - /// It won't be possible to ever stop this listener with a stop token. - pub fn from_stream_without_graceful_shutdown(stream: S) -> Self { - let this = Self::new_with_hints( - stream, - |s| s, - |_| stop_token::Noop, - None, - Some(|_| { - // FIXME: replace this by just Duration::MAX once 1.53 releases - // be released - const NANOS_PER_SEC: u32 = 1_000_000_000; - let dmax = Duration::new(u64::MAX, NANOS_PER_SEC - 1); - - Some(dmax) - }), - ); - - assert_update_listener(this) - } -} - impl<'a, St, Assf, Sf, Hauf, Thf, Strm, E> AsUpdateStream<'a> for StatefulListener where @@ -119,18 +82,16 @@ where } } -impl UpdateListener for StatefulListener +impl UpdateListener for StatefulListener where Self: for<'a> AsUpdateStream<'a, StreamErr = E>, - Sf: FnMut(&mut St) -> Stt, - Stt: StopToken + Send, + Sf: FnMut(&mut St) -> StopToken, Hauf: FnMut(&mut St, &mut dyn Iterator), Thf: Fn(&St) -> Option, { type Err = E; - type StopToken = Stt; - fn stop_token(&mut self) -> Stt { + fn stop_token(&mut self) -> StopToken { (self.stop_token)(&mut self.state) } diff --git a/src/dispatching/update_listeners/webhooks/axum.rs b/src/dispatching/update_listeners/webhooks/axum.rs index 82669a0f3..7bbe03bae 100644 --- a/src/dispatching/update_listeners/webhooks/axum.rs +++ b/src/dispatching/update_listeners/webhooks/axum.rs @@ -6,11 +6,9 @@ use axum::{ }; use crate::{ - dispatching::{ - stop_token::{AsyncStopFlag, StopToken}, - update_listeners::{webhooks::Options, UpdateListener}, - }, + dispatching::update_listeners::{webhooks::Options, UpdateListener}, requests::Requester, + stop::StopFlag, }; /// Webhook implementation based on the [mod@axum] framework. @@ -22,7 +20,7 @@ use crate::{ /// /// [`set_webhook`]: crate::payloads::SetWebhook /// [`delete_webhook`]: crate::payloads::DeleteWebhook -/// [`stop`]: StopToken::stop +/// [`stop`]: crate::stop::StopToken::stop /// /// ## Panics /// @@ -88,7 +86,7 @@ where /// /// [`set_webhook`]: crate::payloads::SetWebhook /// [`delete_webhook`]: crate::payloads::DeleteWebhook -/// [`stop`]: StopToken::stop +/// [`stop`]: crate::stop::StopToken::stop /// [`options.address`]: Options::address /// [`with_graceful_shutdown`]: axum::Server::with_graceful_shutdown /// @@ -156,10 +154,8 @@ pub fn axum_no_setup( options: Options, ) -> (impl UpdateListener, impl Future, axum::Router) { use crate::{ - dispatching::{ - stop_token::AsyncStopToken, - update_listeners::{self, webhooks::tuple_first_mut}, - }, + dispatching::update_listeners::{self, webhooks::tuple_first_mut}, + stop::{mk_stop_token, StopToken}, types::Update, }; use axum::{extract::Extension, response::IntoResponse, routing::post}; @@ -178,7 +174,7 @@ pub fn axum_no_setup( secret_header: XTelegramBotApiSecretToken, secret: Extension>, tx: Extension, - flag: Extension, + flag: Extension, ) -> impl IntoResponse { // FIXME: use constant time comparison here if secret_header.0.as_deref() != secret.as_deref().map(str::as_bytes) { @@ -214,7 +210,7 @@ pub fn axum_no_setup( StatusCode::OK } - let (stop_token, stop_flag) = AsyncStopToken::new_pair(); + let (stop_token, stop_flag) = mk_stop_token(); let app = axum::Router::new().route(options.url.path(), post(telegram_request)).layer( ServiceBuilder::new() @@ -231,7 +227,7 @@ pub fn axum_no_setup( let listener = update_listeners::StatefulListener::new( (stream, stop_token), tuple_first_mut, - |state: &mut (_, AsyncStopToken)| state.1.clone(), + |state: &mut (_, StopToken)| state.1.clone(), ); (listener, stop_flag, app) diff --git a/src/lib.rs b/src/lib.rs index 2964cd746..21ae03386 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,6 +67,7 @@ mod logging; pub mod dispatching; pub mod error_handlers; pub mod prelude; +pub mod stop; pub mod utils; #[doc(inline)] diff --git a/src/stop.rs b/src/stop.rs new file mode 100644 index 000000000..4fdfc4783 --- /dev/null +++ b/src/stop.rs @@ -0,0 +1,67 @@ +//! This module contains stop [token] and stop [flag] that are used to stop +//! async tasks, for example [listeners]. +//! +//! [token]: StopToken +//! [flag]: StopFlag +//! [listeners]: crate::dispatching::update_listeners + +use std::{future::Future, pin::Pin, task}; + +use futures::future::{pending, AbortHandle, Abortable, Pending}; + +/// Create a new token/flag pair. +#[must_use] +pub fn mk_stop_token() -> (StopToken, StopFlag) { + let (handle, reg) = AbortHandle::new_pair(); + let token = StopToken(handle); + let flag = StopFlag(Abortable::new(pending(), reg)); + + (token, flag) +} + +/// A stop token which corresponds to a [`StopFlag`]. +#[derive(Clone)] +pub struct StopToken(AbortHandle); + +/// A flag which corresponds to [`StopToken`]. +/// +/// To know if the stop token was used you can either repeatedly call +/// [`is_stopped`] or use this type as a `Future`. +/// +/// [`is_stopped`]: StopFlag::is_stopped +#[pin_project::pin_project] +#[derive(Clone)] +pub struct StopFlag(#[pin] Abortable>); + +impl StopToken { + /// "Stops" the flag associated with this token. + /// + /// Note that calling this function multiple times does nothing, only the + /// first call changes the state. + pub fn stop(&self) { + self.0.abort() + } +} + +impl StopFlag { + /// Returns true if the stop token linked to `self` was used. + #[must_use] + pub fn is_stopped(&self) -> bool { + self.0.is_aborted() + } +} + +/// This future resolves when a stop token was used. +impl Future for StopFlag { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + self.project().0.poll(cx).map(|res| { + debug_assert!( + res.is_err(), + "Pending Future can't ever be resolved, so Abortable is only resolved when \ + canceled" + ); + }) + } +} From 0807eb57e15e2ea00b14392dde22e917e04d6a19 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 9 Sep 2022 21:39:39 +0400 Subject: [PATCH 19/66] Improve `StopFlag`'s implementation --- src/stop.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/stop.rs b/src/stop.rs index 4fdfc4783..f61caf3d1 100644 --- a/src/stop.rs +++ b/src/stop.rs @@ -5,7 +5,7 @@ //! [flag]: StopFlag //! [listeners]: crate::dispatching::update_listeners -use std::{future::Future, pin::Pin, task}; +use std::{convert::Infallible, future::Future, pin::Pin, task}; use futures::future::{pending, AbortHandle, Abortable, Pending}; @@ -31,7 +31,7 @@ pub struct StopToken(AbortHandle); /// [`is_stopped`]: StopFlag::is_stopped #[pin_project::pin_project] #[derive(Clone)] -pub struct StopFlag(#[pin] Abortable>); +pub struct StopFlag(#[pin] Abortable>); impl StopToken { /// "Stops" the flag associated with this token. @@ -56,12 +56,9 @@ impl Future for StopFlag { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { - self.project().0.poll(cx).map(|res| { - debug_assert!( - res.is_err(), - "Pending Future can't ever be resolved, so Abortable is only resolved when \ - canceled" - ); + self.project().0.poll(cx).map(|res| match res { + Err(_aborted) => (), + Ok(unreachable) => match unreachable {}, }) } } From 721908c08ef7c7dc4485155d953ebed707e2c620 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 9 Sep 2022 21:59:11 +0400 Subject: [PATCH 20/66] Update nightly version in CI --- .github/workflows/ci.yml | 4 ++-- rust-toolchain.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2ccac7ea..2f87ab7eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ env: CARGO_NET_RETRY: 10 RUSTUP_MAX_RETRIES: 10 - rust_nightly: nightly-2022-07-01 + rust_nightly: nightly-2022-09-01 # When updating this, also update: # - README.md # - src/lib.rs @@ -82,7 +82,7 @@ jobs: toolchain: beta features: "--features full" - rust: nightly - toolchain: nightly-2022-07-01 + toolchain: nightly-2022-09-01 features: "--all-features" - rust: msrv toolchain: 1.58.0 diff --git a/rust-toolchain.toml b/rust-toolchain.toml index ea79a4343..eca557709 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2022-07-01" +channel = "nightly-2022-09-01" components = ["rustfmt", "clippy"] profile = "minimal" From bde0345e17ad1f4ad094fd1271bf5d2e30e4f329 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 9 Sep 2022 23:54:52 +0400 Subject: [PATCH 21/66] Update docs of REPLs --- src/dispatching/repls.rs | 9 +- src/dispatching/repls/caution.md | 2 + src/dispatching/repls/commands_repl.rs | 110 +++++++++++++++++-------- src/dispatching/repls/preamble.md | 7 ++ src/dispatching/repls/repl.rs | 105 +++++++++++++++-------- src/dispatching/repls/stopping.md | 4 + 6 files changed, 168 insertions(+), 69 deletions(-) create mode 100644 src/dispatching/repls/caution.md create mode 100644 src/dispatching/repls/preamble.md create mode 100644 src/dispatching/repls/stopping.md diff --git a/src/dispatching/repls.rs b/src/dispatching/repls.rs index b0f450a38..0d41d7ba6 100644 --- a/src/dispatching/repls.rs +++ b/src/dispatching/repls.rs @@ -1,4 +1,11 @@ -//! REPLs for dispatching updates. +//! [REPL]s for dispatching updates. +//! +//! This module provides functions for easy update handling, that accept a +//! single "handler" function that processes all updates of a certain kind. Note +//! that REPLs are meant to be used as a prototyping tool and lack configuration +//! and some advanced features. +//! +//! [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop mod commands_repl; mod repl; diff --git a/src/dispatching/repls/caution.md b/src/dispatching/repls/caution.md new file mode 100644 index 000000000..a5968cb36 --- /dev/null +++ b/src/dispatching/repls/caution.md @@ -0,0 +1,2 @@ +**DO NOT** use this function together with [`Dispatcher`] and other REPLs, +because Telegram disallow multiple requests at the same time from the same bot. diff --git a/src/dispatching/repls/commands_repl.rs b/src/dispatching/repls/commands_repl.rs index ceeeb9179..b5dd39e7d 100644 --- a/src/dispatching/repls/commands_repl.rs +++ b/src/dispatching/repls/commands_repl.rs @@ -11,36 +11,57 @@ use std::{fmt::Debug, marker::PhantomData}; use teloxide_core::requests::Requester; /// A [REPL] for commands. +// +#[doc = include_str!("preamble.md")] /// -/// All errors from an update listener and handler will be logged. +/// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop /// -/// REPLs are meant only for simple bots and rapid prototyping. If you need to -/// supply dependencies or describe more complex dispatch logic, please use -/// [`Dispatcher`]. +/// ## Signature /// -/// See also: ["Dispatching or -/// REPLs?"](dispatching/index.html#dispatching-or-repls) +/// Don't be scared by many trait bounds in the signature, in essence they +/// require: /// -/// ## Caution +/// 1. `bot` is a bot, client for the Telegram bot API +/// - in teloxide this is represented via a [`Requester`] trait +/// 2. `handler` is an async function that returns `Result<(), E>` +/// - Such that `E` can be printed with [`Debug`] formatting +/// - And all arguments can be extracted from [`DependencyMap`] +/// - Which is the same, as all arguments implementing `Send + Sync + +/// 'static` +/// 3. `cmd` is a type of the command that will be parsed, +/// - The command type must implement [`BotCommands`] trait +/// - It can be acquired by writing `TheCommandType::ty()` /// -/// **DO NOT** use this function together with [`Dispatcher`] and other REPLs, -/// because Telegram disallow multiple requests at the same time from the same -/// bot. +/// All other requirements are about thread safety and data validity and can be +/// ignored for most of the time. /// -/// ## Dependency requirements +/// ## Handler arguments /// -/// - Those of [`HandlerExt::filter_command`] +/// Teloxide provides the following types to the `handler`: +/// - [`Message`] +/// - `R` (type of the `bot`) +/// - `Cmd` (type of the parsed command) +/// - [`Me`] +/// +/// [`Me`]: crate::types::Me +/// [`Message`]: crate::types::Message +/// +/// ## Stopping +// +#[doc = include_str!("stopping.md")] +/// +/// ## Caution +// +#[doc = include_str!("caution.md")] /// -/// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop -/// [`Dispatcher`]: crate::dispatching::Dispatcher #[cfg(feature = "ctrlc_handler")] pub async fn commands_repl<'a, R, Cmd, H, E, Args>(bot: R, handler: H, cmd: PhantomData) where - Cmd: BotCommands + Send + Sync + 'static, - H: Injectable, Args> + Send + Sync + 'static, R: Requester + Clone + Send + Sync + 'static, ::GetUpdates: Send, + H: Injectable, Args> + Send + Sync + 'static, E: Debug + Send + Sync + 'static, + Cmd: BotCommands + Send + Sync + 'static, { let cloned_bot = bot.clone(); @@ -53,36 +74,57 @@ where .await; } -/// Like [`commands_repl`], but with a custom [`UpdateListener`]. +/// A [REPL] for commands, with a custom [`UpdateListener`]. +// +#[doc = include_str!("preamble.md")] /// -/// All errors from an update listener and handler will be logged. +/// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop /// -/// REPLs are meant only for simple bots and rapid prototyping. If you need to -/// supply dependencies or describe more complex dispatch logic, please use -/// [`Dispatcher`]. +/// ## Signature /// -/// See also: ["Dispatching or -/// REPLs?"](dispatching/index.html#dispatching-or-repls) +/// Don't be scared by many trait bounds in the signature, in essence they +/// require: /// -/// ## Caution +/// 1. `bot` is a bot, client for the Telegram bot API +/// - in teloxide this is represented via a [`Requester`] trait +/// 2. `handler` is an async function that returns `Result<(), E>` +/// - Such that `E` can be printed with [`Debug`] formatting +/// - And all arguments can be extracted from [`DependencyMap`] +/// - Which is the same, as all arguments implementing `Send + Sync + +/// 'static` +/// 3. `listener` is an [`UpdateListener`] +/// 4. `cmd` is a type of the command that will be parsed, +/// - The command type must implement [`BotCommands`] trait +/// - It can be acquired by writing `TheCommandType::ty()` +/// +/// All other requirements are about thread safety and data validity and can be +/// ignored for most of the time. /// -/// **DO NOT** use this function together with [`Dispatcher`] and other REPLs, -/// because Telegram disallow multiple requests at the same time from the same -/// bot. +/// ## Handler arguments /// -/// ## Dependency requirements +/// Teloxide provides the following types to the `handler`: +/// - [`Message`] +/// - `R` (type of the `bot`) +/// - `Cmd` (type of the parsed command) +/// - [`Me`] /// -/// - Those of [`HandlerExt::filter_command`] +/// [`Me`]: crate::types::Me +/// [`Message`]: crate::types::Message +/// +/// ## Stopping +// +#[doc = include_str!("stopping.md")] +/// +/// ## Caution +// +#[doc = include_str!("caution.md")] /// -/// [`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, L, E, Args>( bot: R, handler: H, listener: L, - _cmd: PhantomData, + cmd: PhantomData, ) where Cmd: BotCommands + Send + Sync + 'static, H: Injectable, Args> + Send + Sync + 'static, @@ -93,6 +135,8 @@ pub async fn commands_repl_with_listener<'a, R, Cmd, H, L, E, Args>( { use crate::dispatching::Dispatcher; + let _ = cmd; + // Other update types are of no interest to use since this REPL is only for // commands. See . let ignore_update = |_upd| Box::pin(async {}); diff --git a/src/dispatching/repls/preamble.md b/src/dispatching/repls/preamble.md new file mode 100644 index 000000000..5c21f0796 --- /dev/null +++ b/src/dispatching/repls/preamble.md @@ -0,0 +1,7 @@ +REPLs are meant only for simple bots and rapid prototyping. +If you need to supply dependencies or describe more complex dispatch logic, please use [`Dispatcher`]. +See also: ["Dispatching or REPLs?"](dispatching/index.html#dispatching-or-repls). + +[`Dispatcher`]: crate::dispatching::Dispatcher + +All errors from a the handler and will be logged. diff --git a/src/dispatching/repls/repl.rs b/src/dispatching/repls/repl.rs index 529cc711c..a755708f1 100644 --- a/src/dispatching/repls/repl.rs +++ b/src/dispatching/repls/repl.rs @@ -1,6 +1,6 @@ use crate::{ dispatching::{update_listeners, update_listeners::UpdateListener, UpdateFilterExt}, - error_handlers::{LoggingErrorHandler, OnError}, + error_handlers::LoggingErrorHandler, types::Update, }; use dptree::di::{DependencyMap, Injectable}; @@ -8,66 +8,101 @@ use std::fmt::Debug; use teloxide_core::requests::Requester; /// A [REPL] for messages. +// +#[doc = include_str!("preamble.md")] /// -/// All errors from an update listener and a handler will be logged. +/// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop /// -/// REPLs are meant only for simple bots and rapid prototyping. If you need to -/// supply dependencies or describe more complex dispatch logic, please use -/// [`Dispatcher`]. +/// ## Signature /// -/// See also: ["Dispatching or -/// REPLs?"](dispatching/index.html#dispatching-or-repls) +/// Don't be scared by many trait bounds in the signature, in essence they +/// require: /// -/// ## Caution +/// 1. `bot` is a bot, client for the Telegram bot API +/// - in teloxide this is represented via a [`Requester`] trait +/// 2. `handler` is an async function that returns `Result<(), E>` +/// - Such that `E` can be printed with [`Debug`] formatting +/// - And all arguments can be extracted from [`DependencyMap`] +/// - Which is the same, as all arguments implementing `Send + Sync + +/// 'static` /// -/// **DO NOT** use this function together with [`Dispatcher`] and other REPLs, -/// because Telegram disallow multiple requests at the same time from the same -/// bot. +/// ## Handler arguments +/// +/// Teloxide provides the following types to the `handler`: +/// - [`Message`] +/// - `R` (type of the `bot`) +/// - [`Me`] +/// +/// [`Me`]: crate::types::Me +/// [`Message`]: crate::types::Message +/// +/// ## Stopping +// +#[doc = include_str!("stopping.md")] +/// +/// ## Caution +// +#[doc = include_str!("caution.md")] /// -/// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop -/// [`Dispatcher`]: crate::dispatching::Dispatcher #[cfg(feature = "ctrlc_handler")] pub async fn repl(bot: R, handler: H) where - H: Injectable, Args> + Send + Sync + 'static, - Result<(), E>: OnError, - E: Debug + Send + Sync + 'static, R: Requester + Send + Sync + Clone + 'static, ::GetUpdates: Send, + H: Injectable, Args> + Send + Sync + 'static, + E: Debug + Send + Sync + 'static, { let cloned_bot = bot.clone(); repl_with_listener(bot, handler, update_listeners::polling_default(cloned_bot).await).await; } -/// Like [`repl`], but with a custom [`UpdateListener`]. +/// A [REPL] for messages, with a custom [`UpdateListener`]. +// +#[doc = include_str!("preamble.md")] /// -/// All errors from an update listener and handler will be logged. +/// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop +/// [`UpdateListener`]: crate::dispatching::update_listeners::UpdateListener /// -/// REPLs are meant only for simple bots and rapid prototyping. If you need to -/// supply dependencies or describe more complex dispatch logic, please use -/// [`Dispatcher`]. +/// ## Signature /// -/// See also: ["Dispatching or -/// REPLs?"](dispatching/index.html#dispatching-or-repls) +/// Don't be scared by many trait bounds in the signature, in essence they +/// require: /// -/// # Caution +/// 1. `bot` is a bot, client for the Telegram bot API +/// - in teloxide this is represented via a [`Requester`] trait +/// 2. `handler` is an async function that returns `Result<(), E>` +/// - Such that `E` can be printed with [`Debug`] formatting +/// - And all arguments can be extracted from [`DependencyMap`] +/// - Which is the same, as all arguments implementing `Send + Sync + +/// 'static` +/// 3. `listener` is an [`UpdateListener`] /// -/// **DO NOT** use this function together with [`Dispatcher`] and other REPLs, -/// because Telegram disallow multiple requests at the same time from the same -/// bot. +/// ## Handler arguments +/// +/// Teloxide provides the following types to the `handler`: +/// - [`Message`] +/// - `R` (type of the `bot`) +/// - [`Me`] +/// +/// [`Me`]: crate::types::Me +/// [`Message`]: crate::types::Message +/// +/// ## Stopping +// +#[doc = include_str!("stopping.md")] +/// +/// ## Caution +// +#[doc = include_str!("caution.md")] /// -/// [`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, E, L, Args>(bot: R, handler: H, listener: L) +pub async fn repl_with_listener(bot: R, handler: H, listener: L) where + R: Requester + Clone + Send + Sync + 'static, H: Injectable, Args> + Send + Sync + 'static, - L: UpdateListener + Send + 'a, - L::Err: Debug, - Result<(), E>: OnError, E: Debug + Send + Sync + 'static, - R: Requester + Clone + Send + Sync + 'static, + L: UpdateListener + Send, + L::Err: Debug, { use crate::dispatching::Dispatcher; diff --git a/src/dispatching/repls/stopping.md b/src/dispatching/repls/stopping.md new file mode 100644 index 000000000..63c65e28f --- /dev/null +++ b/src/dispatching/repls/stopping.md @@ -0,0 +1,4 @@ +To stop repl, simply press `Ctrl`+`C` in the terminal where you run the program. +Note that gracefully stopping can take some time (we plan to improve this, see [#711]). + +[#711]: https://github.com/teloxide/teloxide/issues/711 From 376ffc85f9a89a38aa26d544d515fcca121854ce Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 12 Sep 2022 16:50:59 +0400 Subject: [PATCH 22/66] Properly handle callback queries in `examples/buttons.rs` --- examples/buttons.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/examples/buttons.rs b/examples/buttons.rs index 8ce0e21ac..e2a54bfe6 100644 --- a/examples/buttons.rs +++ b/examples/buttons.rs @@ -112,15 +112,17 @@ async fn callback_handler( if let Some(version) = q.data { let text = format!("You chose: {version}"); - match q.message { - Some(Message { id, chat, .. }) => { - bot.edit_message_text(chat.id, id, text).await?; - } - None => { - if let Some(id) = q.inline_message_id { - bot.edit_message_text_inline(id, text).await?; - } - } + // Tell telegram that we've seen this query, to remove 🕑 icons from the + // + // clients. You could also use `answer_callback_query`'s optional + // parameters to tweak what happens on the client side. + bot.answer_callback_query(q.id).await?; + + // Edit text of the message to which the buttons were attached + if let Some(Message { id, chat, .. }) = q.message { + bot.edit_message_text(chat.id, id, text).await?; + } else if let Some(id) = q.inline_message_id { + bot.edit_message_text_inline(id, text).await?; } log::info!("You chose: {}", version); From 78a96381dcc3f83c7969cc516cd3c44a0302629c Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 25 Sep 2022 01:06:11 +0400 Subject: [PATCH 23/66] Remove `{token}` from webhook url in heroku example --- examples/heroku_ping_pong.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/heroku_ping_pong.rs b/examples/heroku_ping_pong.rs index 8238dd9ae..6bf7f1583 100644 --- a/examples/heroku_ping_pong.rs +++ b/examples/heroku_ping_pong.rs @@ -21,7 +21,6 @@ use std::env; use teloxide::{dispatching::update_listeners::webhooks, prelude::*}; -use url::Url; #[tokio::main] async fn main() { @@ -29,7 +28,6 @@ async fn main() { log::info!("Starting Heroku ping-pong bot..."); let bot = Bot::from_env().auto_send(); - let token = bot.inner().token(); // Heroku auto defines a port value let port: u16 = env::var("PORT") @@ -41,7 +39,7 @@ async fn main() { // Heroku host example: "heroku-ping-pong-bot.herokuapp.com" let host = env::var("HOST").expect("HOST env variable is not set"); - let url = Url::parse(&format!("https://{host}/webhooks/{token}")).unwrap(); + let url = format!("https://{host}/webhook").parse().unwrap(); let listener = webhooks::axum(bot.clone(), webhooks::Options::new(addr, url)) .await From 00efbe163a4f3217f3fa8141d08d4b6559879bb1 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Thu, 29 Sep 2022 09:37:20 +0600 Subject: [PATCH 24/66] Stop using `AutoSend` in the examples and docs --- Cargo.toml | 6 ++--- README.md | 40 ++++++++++++++------------------ examples/admin.rs | 4 +--- examples/buttons.rs | 15 ++++-------- examples/command.rs | 4 ++-- examples/db_remember.rs | 8 +++---- examples/dialogue.rs | 14 ++++------- examples/dispatching_features.rs | 40 ++++++++++++++------------------ examples/heroku_ping_pong.rs | 4 ++-- examples/inline.rs | 4 ++-- examples/ngrok_ping_pong.rs | 4 ++-- examples/purchase.rs | 18 ++++++-------- examples/shared_state.rs | 4 ++-- examples/throw_dice.rs | 4 ++-- src/dispatching.rs | 22 +++++++----------- src/dispatching/dialogue.rs | 4 ++-- src/features.md | 2 +- src/lib.rs | 4 ++-- src/prelude.rs | 1 + 19 files changed, 87 insertions(+), 115 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 01810db71..02aaf40ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,8 +57,8 @@ full = [ ] [dependencies] -teloxide-core = { version = "0.7.0", default-features = false } -#teloxide-macros = { version = "0.6.3", optional = true } +# teloxide-core = { version = "0.7.0", default-features = false } +# teloxide-macros = { version = "0.6.3", optional = true } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } @@ -66,7 +66,7 @@ serde = { version = "1.0", features = ["derive"] } dptree = "0.3.0" # These lines are used only for development. -# teloxide-core = { git = "https://github.com/teloxide/teloxide-core", rev = "b13393d", default-features = false } +teloxide-core = { git = "https://github.com/teloxide/teloxide-core", rev = "00165e6", default-features = false } teloxide-macros = { git = "https://github.com/teloxide/teloxide-macros", rev = "0e79c37", optional = true } # dptree = { git = "https://github.com/teloxide/dptree", rev = "df578e4" } diff --git a/README.md b/README.md index 8ece9b1b9..ae5fdd3d0 100644 --- a/README.md +++ b/README.md @@ -92,9 +92,9 @@ async fn main() { pretty_env_logger::init(); log::info!("Starting throw dice bot..."); - let bot = Bot::from_env().auto_send(); + let bot = Bot::from_env(); - teloxide::repl(bot, |message: Message, bot: AutoSend| async move { + teloxide::repl(bot, |message: Message, bot: Bot| async move { bot.send_dice(message.chat.id).await?; respond(()) }) @@ -129,7 +129,7 @@ async fn main() { pretty_env_logger::init(); log::info!("Starting command bot..."); - let bot = Bot::from_env().auto_send(); + let bot = Bot::from_env(); teloxide::commands_repl(bot, answer, Command::ty()).await; } @@ -146,7 +146,7 @@ enum Command { } async fn answer( - bot: AutoSend, + bot: Bot, message: Message, command: Command, ) -> Result<(), Box> { @@ -190,18 +190,18 @@ use teloxide::{dispatching::dialogue::InMemStorage, prelude::*}; type MyDialogue = Dialogue>; type HandlerResult = Result<(), Box>; -#[derive(Clone)] +#[derive(Clone, Default)] pub enum State { + #[default] Start, ReceiveFullName, - ReceiveAge { full_name: String }, - ReceiveLocation { full_name: String, age: u8 }, -} - -impl Default for State { - fn default() -> Self { - Self::Start - } + ReceiveAge { + full_name: String, + }, + ReceiveLocation { + full_name: String, + age: u8, + }, } #[tokio::main] @@ -209,7 +209,7 @@ async fn main() { pretty_env_logger::init(); log::info!("Starting dialogue bot..."); - let bot = Bot::from_env().auto_send(); + let bot = Bot::from_env(); Dispatcher::builder( bot, @@ -229,17 +229,13 @@ async fn main() { .await; } -async fn start(bot: AutoSend, msg: Message, dialogue: MyDialogue) -> HandlerResult { +async fn start(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { bot.send_message(msg.chat.id, "Let's start! What's your full name?").await?; dialogue.update(State::ReceiveFullName).await?; Ok(()) } -async fn receive_full_name( - bot: AutoSend, - msg: Message, - dialogue: MyDialogue, -) -> HandlerResult { +async fn receive_full_name(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { match msg.text() { Some(text) => { bot.send_message(msg.chat.id, "How old are you?").await?; @@ -254,7 +250,7 @@ async fn receive_full_name( } async fn receive_age( - bot: AutoSend, + bot: Bot, msg: Message, dialogue: MyDialogue, full_name: String, // Available from `State::ReceiveAge`. @@ -273,7 +269,7 @@ async fn receive_age( } async fn receive_location( - bot: AutoSend, + bot: Bot, msg: Message, dialogue: MyDialogue, (full_name, age): (String, u8), // Available from `State::ReceiveLocation`. diff --git a/examples/admin.rs b/examples/admin.rs index 298d62b52..db45e3fa8 100644 --- a/examples/admin.rs +++ b/examples/admin.rs @@ -58,13 +58,11 @@ async fn main() { pretty_env_logger::init(); log::info!("Starting admin bot..."); - let bot = teloxide::Bot::from_env().auto_send(); + let bot = teloxide::Bot::from_env(); teloxide::commands_repl(bot, action, Command::ty()).await; } -type Bot = AutoSend; - async fn action( bot: Bot, msg: Message, diff --git a/examples/buttons.rs b/examples/buttons.rs index e2a54bfe6..f3bf3cba3 100644 --- a/examples/buttons.rs +++ b/examples/buttons.rs @@ -23,7 +23,7 @@ async fn main() -> Result<(), Box> { pretty_env_logger::init(); log::info!("Starting buttons bot..."); - let bot = Bot::from_env().auto_send(); + let bot = Bot::from_env(); let handler = dptree::entry() .branch(Update::filter_message().endpoint(message_handler)) @@ -58,11 +58,7 @@ fn make_keyboard() -> InlineKeyboardMarkup { /// 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( - m: Message, - bot: AutoSend, - me: Me, -) -> Result<(), Box> { +async fn message_handler(m: Message, bot: Bot, me: Me) -> Result<(), Box> { if let Some(text) = m.text() { match BotCommands::parse(text, me.username()) { Ok(Command::Help) => { @@ -86,7 +82,7 @@ async fn message_handler( async fn inline_query_handler( q: InlineQuery, - bot: AutoSend, + bot: Bot, ) -> Result<(), Box> { let choose_debian_version = InlineQueryResultArticle::new( "0", @@ -105,10 +101,7 @@ async fn inline_query_handler( /// /// **IMPORTANT**: do not send privacy-sensitive data this way!!! /// Anyone can read data stored in the callback button. -async fn callback_handler( - q: CallbackQuery, - bot: AutoSend, -) -> Result<(), Box> { +async fn callback_handler(q: CallbackQuery, bot: Bot) -> Result<(), Box> { if let Some(version) = q.data { let text = format!("You chose: {version}"); diff --git a/examples/command.rs b/examples/command.rs index 5b30240a6..11a2ce55f 100644 --- a/examples/command.rs +++ b/examples/command.rs @@ -7,7 +7,7 @@ async fn main() { pretty_env_logger::init(); log::info!("Starting command bot..."); - let bot = Bot::from_env().auto_send(); + let bot = Bot::from_env(); teloxide::commands_repl(bot, answer, Command::ty()).await; } @@ -24,7 +24,7 @@ enum Command { } async fn answer( - bot: AutoSend, + bot: Bot, message: Message, command: Command, ) -> Result<(), Box> { diff --git a/examples/db_remember.rs b/examples/db_remember.rs index 1aed2808b..f1e5e4c9d 100644 --- a/examples/db_remember.rs +++ b/examples/db_remember.rs @@ -35,7 +35,7 @@ async fn main() { pretty_env_logger::init(); log::info!("Starting DB remember bot..."); - let bot = Bot::from_env().auto_send(); + let bot = Bot::from_env(); let storage: MyStorage = if std::env::var("DB_REMEMBER_REDIS").is_ok() { RedisStorage::open("redis://127.0.0.1:6379", Bincode).await.unwrap().erase() @@ -60,7 +60,7 @@ async fn main() { .await; } -async fn start(bot: AutoSend, msg: Message, dialogue: MyDialogue) -> HandlerResult { +async fn start(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { match msg.text().map(|text| text.parse::()) { Some(Ok(n)) => { dialogue.update(State::GotNumber(n)).await?; @@ -79,7 +79,7 @@ async fn start(bot: AutoSend, msg: Message, dialogue: MyDialogue) -> Handle } async fn got_number( - bot: AutoSend, + bot: Bot, msg: Message, dialogue: MyDialogue, num: i32, @@ -97,7 +97,7 @@ async fn got_number( Ok(()) } -async fn invalid_command(bot: AutoSend, msg: Message) -> HandlerResult { +async fn invalid_command(bot: Bot, msg: Message) -> HandlerResult { bot.send_message(msg.chat.id, "Please, send /get or /reset.").await?; Ok(()) } diff --git a/examples/dialogue.rs b/examples/dialogue.rs index 8e30219de..5fc54b3b9 100644 --- a/examples/dialogue.rs +++ b/examples/dialogue.rs @@ -37,7 +37,7 @@ async fn main() { pretty_env_logger::init(); log::info!("Starting dialogue bot..."); - let bot = Bot::from_env().auto_send(); + let bot = Bot::from_env(); Dispatcher::builder( bot, @@ -57,17 +57,13 @@ async fn main() { .await; } -async fn start(bot: AutoSend, msg: Message, dialogue: MyDialogue) -> HandlerResult { +async fn start(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { bot.send_message(msg.chat.id, "Let's start! What's your full name?").await?; dialogue.update(State::ReceiveFullName).await?; Ok(()) } -async fn receive_full_name( - bot: AutoSend, - msg: Message, - dialogue: MyDialogue, -) -> HandlerResult { +async fn receive_full_name(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { match msg.text() { Some(text) => { bot.send_message(msg.chat.id, "How old are you?").await?; @@ -82,7 +78,7 @@ async fn receive_full_name( } async fn receive_age( - bot: AutoSend, + bot: Bot, msg: Message, dialogue: MyDialogue, full_name: String, // Available from `State::ReceiveAge`. @@ -101,7 +97,7 @@ async fn receive_age( } async fn receive_location( - bot: AutoSend, + bot: Bot, msg: Message, dialogue: MyDialogue, (full_name, age): (String, u8), // Available from `State::ReceiveLocation`. diff --git a/examples/dispatching_features.rs b/examples/dispatching_features.rs index dcaf4fcc5..1f34dc1d3 100644 --- a/examples/dispatching_features.rs +++ b/examples/dispatching_features.rs @@ -14,7 +14,7 @@ async fn main() { pretty_env_logger::init(); log::info!("Starting dispatching features bot..."); - let bot = Bot::from_env().auto_send(); + let bot = Bot::from_env(); let parameters = ConfigParameters { bot_maintainer: UserId(0), // Paste your ID to run this bot. @@ -37,25 +37,23 @@ async fn main() { msg.from().map(|user| user.id == cfg.bot_maintainer).unwrap_or_default() }) .filter_command::() - .endpoint( - |msg: Message, bot: AutoSend, cmd: MaintainerCommands| async move { - match cmd { - MaintainerCommands::Rand { from, to } => { - let mut rng = rand::rngs::OsRng::default(); - let value: u64 = rng.gen_range(from..=to); + .endpoint(|msg: Message, bot: Bot, cmd: MaintainerCommands| async move { + match cmd { + MaintainerCommands::Rand { from, to } => { + let mut rng = rand::rngs::OsRng::default(); + let value: u64 = rng.gen_range(from..=to); - bot.send_message(msg.chat.id, value.to_string()).await?; - Ok(()) - } + bot.send_message(msg.chat.id, value.to_string()).await?; + Ok(()) } - }, - ), + } + }), ) .branch( // Filtering allow you to filter updates by some condition. dptree::filter(|msg: Message| msg.chat.is_group() || msg.chat.is_supergroup()) // An endpoint is the last update handler. - .endpoint(|msg: Message, bot: AutoSend| async move { + .endpoint(|msg: Message, bot: Bot| async move { log::info!("Received a message from a group chat."); bot.send_message(msg.chat.id, "This is a group chat.").await?; respond(()) @@ -64,14 +62,12 @@ async fn main() { .branch( // There are some extension filtering functions on `Message`. The following filter will // filter only messages with dices. - Message::filter_dice().endpoint( - |msg: Message, dice: Dice, bot: AutoSend| async move { - bot.send_message(msg.chat.id, format!("Dice value: {}", dice.value)) - .reply_to_message_id(msg.id) - .await?; - Ok(()) - }, - ), + Message::filter_dice().endpoint(|msg: Message, dice: Dice, bot: Bot| async move { + bot.send_message(msg.chat.id, format!("Dice value: {}", dice.value)) + .reply_to_message_id(msg.id) + .await?; + Ok(()) + }), ); Dispatcher::builder(bot, handler) @@ -119,7 +115,7 @@ enum MaintainerCommands { async fn simple_commands_handler( msg: Message, - bot: AutoSend, + bot: Bot, cmd: SimpleCommand, cfg: ConfigParameters, me: teloxide::types::Me, diff --git a/examples/heroku_ping_pong.rs b/examples/heroku_ping_pong.rs index 6bf7f1583..5c8def6aa 100644 --- a/examples/heroku_ping_pong.rs +++ b/examples/heroku_ping_pong.rs @@ -27,7 +27,7 @@ async fn main() { pretty_env_logger::init(); log::info!("Starting Heroku ping-pong bot..."); - let bot = Bot::from_env().auto_send(); + let bot = Bot::from_env(); // Heroku auto defines a port value let port: u16 = env::var("PORT") @@ -47,7 +47,7 @@ async fn main() { teloxide::repl_with_listener( bot, - |msg: Message, bot: AutoSend| async move { + |msg: Message, bot: Bot| async move { bot.send_message(msg.chat.id, "pong").await?; respond(()) }, diff --git a/examples/inline.rs b/examples/inline.rs index 837fa30d2..f9536b029 100644 --- a/examples/inline.rs +++ b/examples/inline.rs @@ -11,10 +11,10 @@ async fn main() { pretty_env_logger::init(); log::info!("Starting inline bot..."); - let bot = Bot::from_env().auto_send(); + let bot = Bot::from_env(); let handler = Update::filter_inline_query().branch(dptree::endpoint( - |query: InlineQuery, bot: AutoSend| async move { + |query: InlineQuery, bot: Bot| async move { // First, create your actual response let google_search = InlineQueryResultArticle::new( // Each item needs a unique ID, as well as the response container for the diff --git a/examples/ngrok_ping_pong.rs b/examples/ngrok_ping_pong.rs index f44086c6f..6f9eefb0a 100644 --- a/examples/ngrok_ping_pong.rs +++ b/examples/ngrok_ping_pong.rs @@ -8,7 +8,7 @@ async fn main() { pretty_env_logger::init(); log::info!("Starting ngrok ping-pong bot..."); - let bot = Bot::from_env().auto_send(); + let bot = Bot::from_env(); let addr = ([127, 0, 0, 1], 8443).into(); let url = "Your HTTPS ngrok URL here. Get it by `ngrok http 8443`".parse().unwrap(); @@ -18,7 +18,7 @@ async fn main() { teloxide::repl_with_listener( bot, - |msg: Message, bot: AutoSend| async move { + |msg: Message, bot: Bot| async move { bot.send_message(msg.chat.id, "pong").await?; respond(()) }, diff --git a/examples/purchase.rs b/examples/purchase.rs index 0eac1ad34..79b4e81cc 100644 --- a/examples/purchase.rs +++ b/examples/purchase.rs @@ -48,7 +48,7 @@ async fn main() { pretty_env_logger::init(); log::info!("Starting purchase bot..."); - let bot = Bot::from_env().auto_send(); + let bot = Bot::from_env(); Dispatcher::builder(bot, schema()) .dependencies(dptree::deps![InMemStorage::::new()]) @@ -83,34 +83,30 @@ fn schema() -> UpdateHandler> .branch(callback_query_handler) } -async fn start(bot: AutoSend, msg: Message, dialogue: MyDialogue) -> HandlerResult { +async fn start(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { bot.send_message(msg.chat.id, "Let's start! What's your full name?").await?; dialogue.update(State::ReceiveFullName).await?; Ok(()) } -async fn help(bot: AutoSend, msg: Message) -> HandlerResult { +async fn help(bot: Bot, msg: Message) -> HandlerResult { bot.send_message(msg.chat.id, Command::descriptions().to_string()).await?; Ok(()) } -async fn cancel(bot: AutoSend, msg: Message, dialogue: MyDialogue) -> HandlerResult { +async fn cancel(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { bot.send_message(msg.chat.id, "Cancelling the dialogue.").await?; dialogue.exit().await?; Ok(()) } -async fn invalid_state(bot: AutoSend, msg: Message) -> HandlerResult { +async fn invalid_state(bot: Bot, msg: Message) -> HandlerResult { bot.send_message(msg.chat.id, "Unable to handle the message. Type /help to see the usage.") .await?; Ok(()) } -async fn receive_full_name( - bot: AutoSend, - msg: Message, - dialogue: MyDialogue, -) -> HandlerResult { +async fn receive_full_name(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { match msg.text().map(ToOwned::to_owned) { Some(full_name) => { let products = ["Apple", "Banana", "Orange", "Potato"] @@ -130,7 +126,7 @@ async fn receive_full_name( } async fn receive_product_selection( - bot: AutoSend, + bot: Bot, q: CallbackQuery, dialogue: MyDialogue, full_name: String, diff --git a/examples/shared_state.rs b/examples/shared_state.rs index 21a0fcc0b..4caf9995f 100644 --- a/examples/shared_state.rs +++ b/examples/shared_state.rs @@ -12,11 +12,11 @@ async fn main() { pretty_env_logger::init(); log::info!("Starting shared state bot..."); - let bot = Bot::from_env().auto_send(); + let bot = Bot::from_env(); let messages_total = Arc::new(AtomicU64::new(0)); let handler = Update::filter_message().endpoint( - |msg: Message, bot: AutoSend, messages_total: Arc| async move { + |msg: Message, bot: Bot, messages_total: Arc| async move { let previous = messages_total.fetch_add(1, Ordering::Relaxed); bot.send_message(msg.chat.id, format!("I received {previous} messages in total.")) .await?; diff --git a/examples/throw_dice.rs b/examples/throw_dice.rs index 758778b18..39272378e 100644 --- a/examples/throw_dice.rs +++ b/examples/throw_dice.rs @@ -7,9 +7,9 @@ async fn main() { pretty_env_logger::init(); log::info!("Starting throw dice bot..."); - let bot = Bot::from_env().auto_send(); + let bot = Bot::from_env(); - teloxide::repl(bot, |message: Message, bot: AutoSend| async move { + teloxide::repl(bot, |message: Message, bot: Bot| async move { bot.send_dice(message.chat.id).await?; respond(()) }) diff --git a/src/dispatching.rs b/src/dispatching.rs index b650b308a..c22cf4b98 100644 --- a/src/dispatching.rs +++ b/src/dispatching.rs @@ -108,7 +108,7 @@ //! Show the endpoints //! //! ```no_run -//! # use teloxide::{Bot, adaptors::AutoSend}; +//! # use teloxide::Bot; //! # use teloxide::types::{Message, CallbackQuery}; //! # use teloxide::dispatching::dialogue::{InMemStorage, Dialogue}; //! # enum State{} @@ -116,32 +116,28 @@ //! type MyDialogue = Dialogue>; //! type HandlerResult = Result<(), Box>; //! -//! async fn start(bot: AutoSend, msg: Message, dialogue: MyDialogue) -> HandlerResult { +//! async fn start(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { //! todo!() //! } //! -//! async fn help(bot: AutoSend, msg: Message) -> HandlerResult { +//! async fn help(bot: Bot, msg: Message) -> HandlerResult { //! todo!() //! } //! -//! async fn cancel(bot: AutoSend, msg: Message, dialogue: MyDialogue) -> HandlerResult { +//! async fn cancel(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { //! todo!() //! } //! -//! async fn invalid_state(bot: AutoSend, msg: Message) -> HandlerResult { +//! async fn invalid_state(bot: Bot, msg: Message) -> HandlerResult { //! todo!() //! } //! -//! async fn receive_full_name( -//! bot: AutoSend, -//! msg: Message, -//! dialogue: MyDialogue, -//! ) -> HandlerResult { +//! async fn receive_full_name(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { //! todo!() //! } //! //! async fn receive_product_selection( -//! bot: AutoSend, +//! bot: Bot, //! q: CallbackQuery, //! dialogue: MyDialogue, //! full_name: String, @@ -153,7 +149,7 @@ //! //! //! Each parameter is supplied as a dependency by teloxide. In particular: -//! - `bot: AutoSend` comes from the dispatcher (see below) +//! - `bot: Bot` comes from the dispatcher (see below) //! - `msg: Message` comes from [`Update::filter_message`] //! - `q: CallbackQuery` comes from [`Update::filter_callback_query`] //! - `dialogue: MyDialogue` comes from [`dialogue::enter`] @@ -170,7 +166,7 @@ //! # fn schema() -> teloxide::dispatching::UpdateHandler> { teloxide::dptree::entry() } //! #[tokio::main] //! async fn main() { -//! let bot = Bot::from_env().auto_send(); +//! let bot = Bot::from_env(); //! //! Dispatcher::builder(bot, schema()) //! .dependencies(dptree::deps![InMemStorage::::new()]) diff --git a/src/dispatching/dialogue.rs b/src/dispatching/dialogue.rs index 724af2e29..1ecffde24 100644 --- a/src/dispatching/dialogue.rs +++ b/src/dispatching/dialogue.rs @@ -38,7 +38,7 @@ //! # type HandlerResult = Result<(), Box>; //! # #[derive(Clone, Debug)] enum State { ReceiveLocation { full_name: String, age: u8 } } //! async fn receive_age( -//! bot: AutoSend, +//! bot: Bot, //! msg: Message, //! dialogue: MyDialogue, //! full_name: String, // Available from `State::ReceiveAge`. @@ -70,7 +70,7 @@ //! # type HandlerResult = Result<(), Box>; //! # #[derive(Clone, Debug)] enum State {} //! async fn receive_location( -//! bot: AutoSend, +//! bot: Bot, //! msg: Message, //! dialogue: MyDialogue, //! (full_name, age): (String, u8), // Available from `State::ReceiveLocation`. diff --git a/src/features.md b/src/features.md index 2c4b2951e..c7aa4e6aa 100644 --- a/src/features.md +++ b/src/features.md @@ -6,7 +6,7 @@ | `webhooks-axum` | Enables webhook implementation based on axum framework | | `macros` | Re-exports macros from [`teloxide-macros`]. | | `ctrlc_handler` | Enables the [`DispatcherBuilder::enable_ctrlc_handler`] function (**enabled by default**). | -| `auto-send` | Enables the [`AutoSend`](adaptors::AutoSend) bot adaptor (**enabled by default**). | +| `auto-send` | Enables the [`AutoSend`](adaptors::AutoSend) bot adaptor (**enabled by default; DEPRECATED**). | | `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. | diff --git a/src/lib.rs b/src/lib.rs index 21ae03386..aad4c98c1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,9 +13,9 @@ //! pretty_env_logger::init(); //! log::info!("Starting throw dice bot..."); //! -//! let bot = Bot::from_env().auto_send(); +//! let bot = Bot::from_env(); //! -//! teloxide::repl(bot, |message: Message, bot: AutoSend| async move { +//! teloxide::repl(bot, |message: Message, bot: Bot| async move { //! bot.send_dice(message.chat.id).await?; //! respond(()) //! }) diff --git a/src/prelude.rs b/src/prelude.rs index 3cbf6ca52..46e4d0b7d 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -15,6 +15,7 @@ pub use teloxide_core::types::{ }; #[cfg(feature = "auto-send")] +#[allow(deprecated)] pub use crate::adaptors::AutoSend; #[doc(no_inline)] From 94e6f0bd4dee5fb750e37092af0fe8a92d07fa4f Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Thu, 29 Sep 2022 09:42:50 +0600 Subject: [PATCH 25/66] Collapse and show the endpoints in the docs If we remove empty lines between these endpoints, we'll make the code shorter. Additionally, since we do no longer need `AutoSend`, the code is also a bit less convoluted. --- src/dispatching.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/dispatching.rs b/src/dispatching.rs index c22cf4b98..e9166c744 100644 --- a/src/dispatching.rs +++ b/src/dispatching.rs @@ -102,10 +102,7 @@ //! -- no problem, reuse [`dptree::Handler::filter`], [`dptree::case!`], and //! other combinators in the same way! //! -//! Finally, we define our endpoints via simple `async` functions like this: -//! -//!
-//! Show the endpoints +//! Finally, we define our endpoints: //! //! ```no_run //! # use teloxide::Bot; @@ -119,23 +116,18 @@ //! async fn start(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { //! todo!() //! } -//! //! async fn help(bot: Bot, msg: Message) -> HandlerResult { //! todo!() //! } -//! //! async fn cancel(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { //! todo!() //! } -//! //! async fn invalid_state(bot: Bot, msg: Message) -> HandlerResult { //! todo!() //! } -//! //! async fn receive_full_name(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { //! todo!() //! } -//! //! async fn receive_product_selection( //! bot: Bot, //! q: CallbackQuery, @@ -146,8 +138,6 @@ //! } //! ``` //! -//!
-//! //! Each parameter is supplied as a dependency by teloxide. In particular: //! - `bot: Bot` comes from the dispatcher (see below) //! - `msg: Message` comes from [`Update::filter_message`] From 1796e2c9f129b831f5d5ccaeda8effd48210afc8 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Thu, 29 Sep 2022 09:51:56 +0600 Subject: [PATCH 26/66] Update MSRV to 1.64.0 (CI) --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2f87ab7eb..e83969932 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ env: # - README.md # - src/lib.rs # - down below in a matrix - rust_msrv: 1.58.0 + rust_msrv: 1.64.0 jobs: # Depends on all action that are required for a "successful" CI run. @@ -85,7 +85,7 @@ jobs: toolchain: nightly-2022-09-01 features: "--all-features" - rust: msrv - toolchain: 1.58.0 + toolchain: 1.64.0 features: "--features full" steps: From 36f0226751b254e0c70f4f2c8153f09bc254e97f Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 29 Sep 2022 19:01:03 +0400 Subject: [PATCH 27/66] Improve readability of `CODE_STYLE.md` --- CODE_STYLE.md | 267 +++++++++++++++++++++++++++++--------------------- 1 file changed, 157 insertions(+), 110 deletions(-) diff --git a/CODE_STYLE.md b/CODE_STYLE.md index 503844c20..eb0eced30 100644 --- a/CODE_STYLE.md +++ b/CODE_STYLE.md @@ -1,151 +1,198 @@ # Code style -This is a description of a coding style that every contributor must follow. Please, read the whole document before you start pushing code. + +This is a description of a coding style that every contributor must follow. +Please, read the whole document before you start pushing code. ## Generics -Generics are always written with `where`. -Bad: +All trait bounds should be written in `where` ```rust - pub fn new, - T: Into, - P: Into, - E: Into> +// GOOD +pub fn new(user_id: i32, name: N, title: T, png_sticker: P, emojis: E) -> Self +where + N: Into, + T: Into, + P: Into, + E: Into, +{ ... } + +// BAD +pub fn new, + T: Into, + P: Into, + E: Into> (user_id: i32, name: N, title: T, png_sticker: P, emojis: E) -> Self { ... } ``` +```rust +// GOOD +impl Trait for Wrap +where + T: Trait +{ ... } + +// BAD +impl Trait for Wrap { ... } +``` -Good: +**Rationale:** `where` clauses are easier to read when there are a lot of bounds, uniformity. + +## Documentation comments + +1. Documentation must describe what your code does and mustn't describe how your code does it and bla-bla-bla. +2. Be sure that your comments follow the grammar, including punctuation, the first capital letter and so on. + ```rust + // GOOD + /// This function makes a request to Telegram. + pub fn make_request(url: &str) -> String { ... } + + // BAD + /// this function make request to telegram + pub fn make_request(url: &str) -> String { ... } + ``` +3. Do not use ending punctuation in short list items (usually containing just one phrase or sentence). + ```md + + - Handle different kinds of Update + - Pass dependencies to handlers + - Disable a default Ctrl-C handling + + + - Handle different kinds of Update. + - Pass dependencies to handlers. + - Disable a default Ctrl-C handling. + + + - Handle different kinds of Update; + - Pass dependencies to handlers; + - Disable a default Ctrl-C handling; + ``` +3. Link resources in your comments when possible, for example: + ```rust + /// Download a file from Telegram. + /// + /// `path` can be obtained from the [`Bot::get_file`]. + /// + /// To download into [`AsyncWrite`] (e.g. [`tokio::fs::File`]), see + /// [`Bot::download_file`]. + /// + /// [`Bot::get_file`]: crate::bot::Bot::get_file + /// [`AsyncWrite`]: tokio::io::AsyncWrite + /// [`tokio::fs::File`]: tokio::fs::File + /// [`Bot::download_file`]: crate::Bot::download_file + ``` + +## Use `Self` where possible + +When referring to the type for which block is implemented, prefer using `Self`, rather than the name of the type. ```rust - pub fn new(user_id: i32, name: N, title: T, png_sticker: P, emojis: E) -> Self +impl ErrorKind { + // GOOD + fn print(&self) { + Self::Io => println!("Io"), + Self::Network => println!("Network"), + Self::Json => println!("Json"), + } + + // BAD + fn print(&self) { + ErrorKind::Io => println!("Io"), + ErrorKind::Network => println!("Network"), + ErrorKind::Json => println!("Json"), + } +} +``` +```rust +impl<'a> AnswerCallbackQuery<'a> { + // GOOD + fn new(bot: &'a Bot, callback_query_id: C) -> Self + where + C: Into, + { ... } + + // BAD + fn new(bot: &'a Bot, callback_query_id: C) -> AnswerCallbackQuery<'a> where - N: Into, - T: Into, - P: Into, - E: Into { ... } + C: Into, + { ... } +} ``` -## Comments - 1. Comments must describe what your code does and mustn't describe how your code does it and bla-bla-bla. Be sure that your comments follow the grammar, including punctuation, the first capital letter and so on. +**Rationale:** `Self` is generally shorter and it's easier to copy-paste code or rename the type. -Bad: +## Avoid duplication in fields names ```rust -/// this function make request to telegram -pub fn make_request(url: &str) -> String { ... } +struct Message { + // GOOD + #[serde(rename = "message_id")] + id: MessageId, + + // BAD + message_id: MessageId, +} ``` -Good: +**Rationale:** duplication is unnecessary -```rust -/// This function makes a request to Telegram. -pub fn make_request(url: &str) -> String { ... } -``` +## Conventional generic names - 2. Do not use ending punctuation in short list items (usually containing just one phrase or sentence). Bad: +Use a generic parameter name `S` for streams, `Fut` for futures, `F` for functions (where possible). -```md - - Handle different kinds of Update. - - Pass dependencies to handlers. - - Disable a default Ctrl-C handling. -``` +**Rationale:** uniformity. -Bad: +## Deriving traits -```md - - Handle different kinds of Update; - - Pass dependencies to handlers; - - Disable a default Ctrl-C handling. -``` +Derive `Copy`, `Clone`, `Eq`, `PartialEq`, `Hash` and `Debug` for public types when possible. -Good: +**Rationale:** these traits can be useful for users and can be implemented for most types. -```md - - Handle different kinds of Update - - Pass dependencies to handlers - - Disable a default Ctrl-C handling -``` +Derive `Default` when there is a reasonable default value for the type. - 3. Link resources in your comments when possible: +**Rationale:** `Default` plays nicely with generic code (for example `mem::take`). -```rust -/// Download a file from Telegram. -/// -/// `path` can be obtained from the [`Bot::get_file`]. -/// -/// To download into [`AsyncWrite`] (e.g. [`tokio::fs::File`]), see -/// [`Bot::download_file`]. -/// -/// [`Bot::get_file`]: crate::bot::Bot::get_file -/// [`AsyncWrite`]: tokio::io::AsyncWrite -/// [`tokio::fs::File`]: tokio::fs::File -/// [`Bot::download_file`]: crate::Bot::download_file -#[cfg(feature = "unstable-stream")] -pub async fn download_file_stream( - &self, - path: &str, -) -> Result>, reqwest::Error> -{ - download_file_stream(&self.client, &self.token, path).await -} -``` +## `Into`-polymorphism -## Use Self where possible -Bad: +Use `T: Into` when this can simplify user code. +I.e. when there are types that implement `Into` that are likely to be passed to this function. -```rust -impl ErrorKind { - fn print(&self) { - ErrorKind::Io => println!("Io"), - ErrorKind::Network => println!("Network"), - ErrorKind::Json => println!("Json"), - } -} -``` +**Rationale:** conversions unnecessarily complicate caller code and can be confusing for beginners. + +## `must_use` + +Always mark a functions as `#[must_use]` if they don't have side-effects and the only reason to call them is to get the result. -Good: ```rust -impl ErrorKind { - fn print(&self) { - Self::Io => println!("Io"), - Self::Network => println!("Network"), - Self::Json => println!("Json"), +impl User { + // GOOD + #[must_use] + fn full_name(&self) -> String { + format!("{} {}", user.first_name, user.last_name) } } ``` -
- More examples - -Bad: - -```rust -impl<'a> AnswerCallbackQuery<'a> { - pub(crate) fn new(bot: &'a Bot, callback_query_id: C) -> AnswerCallbackQuery<'a> - where -C: Into, { ... } -``` +**Rationale:** users will get warnings if they forgot to do something with the result, potentially preventing bugs. + +## Creating boxed futures + +Prefer `Box::pin(async { ... })` instead of `async { ... }.boxed()`. + +**Rationale:** the former is generally formatted better by rustfmt. + +## Full paths for logging + +Always write `log::!(...)` instead of importing `use log::;` and invoking `!(...)`. -Good: - ```rust -impl<'a> AnswerCallbackQuery<'a> { - pub(crate) fn new(bot: &'a Bot, callback_query_id: C) -> Self - where -C: Into, { ... } -``` -
+// GOOD +log::warn!("Everything is on fire"); -## Naming - 1. Avoid unnecessary duplication (`Message::message_id` -> `Message::id` using `#[serde(rename = "message_id")]`). - 2. Use a generic parameter name `S` for streams, `Fut` for futures, `F` for functions (where possible). +// BAD +use log::warn; -## Deriving - 1. Derive `Copy`, `Eq`, `Hash`, `PartialEq`, `Clone`, `Debug` for public types when possible (note: if the default `Debug` implementation is weird, you should manually implement it by yourself). - 2. Derive `Default` when there is an algorithm to get a default value for your type. +warn!("Everything is on fire"); +``` -## Misc - 1. Use `Into<...>` only where there exists at least one conversion **and** it will be logically to use. - 2. Always mark a function as `#[must_use]` if its return value **must** be used. - 3. `Box::pin(async [move] { ... })` instead of `async [move] { ... }.boxed()`. - 4. Always write `log::!(...)` instead of importing `use log::;` and invoking `!(...)`. For example, write `log::info!("blah")`. +**Rationale:** uniformity, it's clearer which log crate is used. From 5e87accfc73ae15389738bb71caf51d6c39e536e Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 29 Sep 2022 19:05:31 +0400 Subject: [PATCH 28/66] Codify `&str` -> `String` conversions in code style --- CODE_STYLE.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CODE_STYLE.md b/CODE_STYLE.md index eb0eced30..515ca25bd 100644 --- a/CODE_STYLE.md +++ b/CODE_STYLE.md @@ -196,3 +196,9 @@ warn!("Everything is on fire"); ``` **Rationale:** uniformity, it's clearer which log crate is used. + +## `&str` -> `String` conversion + +Prefer using `.to_owned()`, rather than `.to_string()`, `.into()`, `String::from`, etc. + +**Rationale:** uniformity, intent clarity. From 562f0479024bfc8299b01120d4927136e50a0df0 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 29 Sep 2022 19:17:16 +0400 Subject: [PATCH 29/66] COpy some code style rules from r-a --- CODE_STYLE.md | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) diff --git a/CODE_STYLE.md b/CODE_STYLE.md index 515ca25bd..21534c952 100644 --- a/CODE_STYLE.md +++ b/CODE_STYLE.md @@ -202,3 +202,191 @@ warn!("Everything is on fire"); Prefer using `.to_owned()`, rather than `.to_string()`, `.into()`, `String::from`, etc. **Rationale:** uniformity, intent clarity. + +## Order of imports + +Separate import groups with blank lines. Use one use per crate. + +Module declarations come before the imports. +Order them in "suggested reading order" for a person new to the code base. + +```rust +mod x; +mod y; + +// First std. +use std::{ ... } + +// Second, external crates (both crates.io crates and other rust-analyzer crates). +use crate_foo::{ ... } +use crate_bar::{ ... } + +// Then current crate. +use crate::{} + +// Finally, parent and child modules, but prefer `use crate::`. +use super::{} + +// Re-exports are treated as item definitions rather than imports, so they go +// after imports and modules. Use them sparingly. +pub use crate::x::Z; +``` + +**Rationale:** consistency. Reading order is important for new contributors. Grouping by crate allows spotting unwanted dependencies easier. + +## Import Style + +When implementing traits from `std::fmt` import the module: + +```rust +// GOOD +use std::fmt; + +impl fmt::Display for RenameError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { .. } +} + +// BAD +impl std::fmt::Display for RenameError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { .. } +} +``` + +**Rationale:** overall, less typing. Makes it clear that a trait is implemented, rather than used. + +Prefer `use crate::foo::bar` to `use super::bar` or `use self::bar::baz`. **Rationale:** consistency, this is the style which works in all cases. + +## Order of Items + +Optimize for the reader who sees the file for the first time, and wants to get a general idea about what's going on. People read things from top to bottom, so place most important things first. + +Specifically, if all items except one are private, always put the non-private item on top. +```rust +// GOOD +pub(crate) fn frobnicate() { + Helper::act() +} + +#[derive(Default)] +struct Helper { stuff: i32 } + +impl Helper { + fn act(&self) { + + } +} + +// BAD +#[derive(Default)] +struct Helper { stuff: i32 } + +pub(crate) fn frobnicate() { + Helper::act() +} + +impl Helper { + fn act(&self) { + + } +} +``` + +If there's a mixture of private and public items, put public items first. + +Put structs and enums first, functions and impls last. Order type declarations in top-down manner. + +```rust +// GOOD +struct Parent { + children: Vec +} + +struct Child; + +impl Parent { +} + +impl Child { +} + +// BAD +struct Child; + +impl Child { +} + +struct Parent { + children: Vec +} + +impl Parent { +} +``` + +**Rationale:** easier to get the sense of the API by visually scanning the file. If function bodies are folded in the editor, the source code should read as documentation for the public API. + +## Early Returns + +Do use early returns + +```rust +// GOOD +fn foo() -> Option { + if !condition() { + return None; + } + + Some(...) +} + +// BAD +fn foo() -> Option { + if condition() { + Some(...) + } else { + None + } +} +``` + +**Rationale:** reduce cognitive stack usage. + +## If-let + +Avoid if let ... { } else { } construct, use match instead. + +```rust +// GOOD +match ctx.expected_type.as_ref() { + Some(expected_type) => completion_ty == expected_type && !expected_type.is_unit(), + None => false, +} + +// BAD +if let Some(expected_type) = ctx.expected_type.as_ref() { + completion_ty == expected_type && !expected_type.is_unit() +} else { + false +} +``` + +**Rationale:** `match is almost always more compact. The `else` branch can get a more precise pattern: `None` or `Err(_)` instead of `_`. + +## Empty Match Arms + +Use `=> (),` when a match arm is intentionally empty: +```rust +// GOOD +match result { + Ok(_) => (), + Err(err) => error!("{}", err), +} + +// BAD +match result { + Ok(_) => {} + Err(err) => error!("{}", err), +} +``` + +**Rationale:** consistency. From d1bef5112e4dfb4498a2932ae8c5b47caa6d9cf1 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Sat, 1 Oct 2022 17:11:15 +0600 Subject: [PATCH 30/66] Update the number of repos dependant on teloxide --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ae5fdd3d0..375ee95a5 100644 --- a/README.md +++ b/README.md @@ -351,7 +351,7 @@ Feel free to propose your own bot to our collection! -See [600+ other public repositories using teloxide >>](https://github.com/teloxide/teloxide/network/dependents) +See [700+ other public repositories using teloxide >>](https://github.com/teloxide/teloxide/network/dependents) ## Contributing From 044e87a98519d7f0319d636f450ec8c686bfeb6a Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 2 Oct 2022 00:34:14 +0400 Subject: [PATCH 31/66] Remove some previously deprecated items --- CHANGELOG.md | 3 ++ src/dispatching.rs | 3 -- src/dispatching/handler_ext.rs | 18 ---------- src/dispatching/handler_factory.rs | 11 ------- src/lib.rs | 2 -- src/logging.rs | 53 ------------------------------ 6 files changed, 3 insertions(+), 87 deletions(-) delete mode 100644 src/dispatching/handler_factory.rs delete mode 100644 src/logging.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index c4995282a..8bfe7543d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed - `dispatching::stop_token::StopToken` trait (all uses are replaced with `stop::StopToken` structure) +- Some previously deprecated items + - `enable_logging!`, `enable_logging_with_filter!` + - `HandlerFactory`, `HandlerExt::dispatch_by` ## 0.10.1 - 2022-07-22 diff --git a/src/dispatching.rs b/src/dispatching.rs index e9166c744..23a03268c 100644 --- a/src/dispatching.rs +++ b/src/dispatching.rs @@ -218,7 +218,6 @@ mod distribution; mod filter_ext; mod handler_description; mod handler_ext; -mod handler_factory; pub mod update_listeners; pub use crate::utils::shutdown_token::{IdleShutdownError, ShutdownToken}; @@ -227,5 +226,3 @@ pub use distribution::DefaultKey; pub use filter_ext::{MessageFilterExt, UpdateFilterExt}; pub use handler_description::DpHandlerDescription; pub use handler_ext::{filter_command, HandlerExt}; -#[allow(deprecated)] -pub use handler_factory::HandlerFactory; diff --git a/src/dispatching/handler_ext.rs b/src/dispatching/handler_ext.rs index 11ebc6bff..a6e80f0c7 100644 --- a/src/dispatching/handler_ext.rs +++ b/src/dispatching/handler_ext.rs @@ -8,9 +8,6 @@ use crate::{ }; use dptree::{di::DependencyMap, Handler}; -#[allow(deprecated)] -use crate::dispatching::HandlerFactory; - use std::fmt::Debug; /// Extension methods for working with `dptree` handlers. @@ -51,13 +48,6 @@ pub trait HandlerExt { >::Error: Debug + Send, D: Default + Send + Sync + 'static, Upd: GetChatId + Clone + Send + Sync + 'static; - - #[must_use] - #[deprecated(note = "Use the teloxide::handler! API")] - #[allow(deprecated)] - fn dispatch_by(self) -> Self - where - F: HandlerFactory; } impl HandlerExt for Handler<'static, DependencyMap, Output, DpHandlerDescription> @@ -80,14 +70,6 @@ where { self.chain(super::dialogue::enter::()) } - - #[allow(deprecated)] - fn dispatch_by(self) -> Self - where - F: HandlerFactory, - { - self.chain(F::handler()) - } } /// Returns a handler that accepts a parsed command `C`. diff --git a/src/dispatching/handler_factory.rs b/src/dispatching/handler_factory.rs deleted file mode 100644 index 25122f338..000000000 --- a/src/dispatching/handler_factory.rs +++ /dev/null @@ -1,11 +0,0 @@ -use dptree::{di::DependencyMap, Handler}; - -use crate::dispatching::DpHandlerDescription; - -/// Something that can construct a handler. -#[deprecated(note = "Use the teloxide::handler! API")] -pub trait HandlerFactory { - type Out; - - fn handler() -> Handler<'static, DependencyMap, Self::Out, DpHandlerDescription>; -} diff --git a/src/lib.rs b/src/lib.rs index aad4c98c1..c7a2e1a82 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,8 +62,6 @@ pub use dispatching::repls::{ commands_repl, commands_repl_with_listener, repl, repl_with_listener, }; -mod logging; - pub mod dispatching; pub mod error_handlers; pub mod prelude; diff --git a/src/logging.rs b/src/logging.rs deleted file mode 100644 index 770c17a73..000000000 --- a/src/logging.rs +++ /dev/null @@ -1,53 +0,0 @@ -/// Enables logging through [pretty-env-logger]. -/// -/// A logger will **only** print errors, warnings, and general information from -/// teloxide and **all** logs from your program. -/// -/// # Note -/// -/// Calling this macro **is not mandatory**; you can setup if your own logger if -/// you want. -/// -/// [pretty-env-logger]: https://crates.io/crates/pretty_env_logger -#[macro_export] -#[deprecated = "Choose logging implementation yourself"] -macro_rules! enable_logging { - () => { - #[allow(deprecated)] - teloxide::enable_logging_with_filter!(log::LevelFilter::Trace); - }; -} - -/// Enables logging through [pretty-env-logger] with a custom filter for your -/// program. -/// -/// A logger will **only** print errors, warnings, and general information from -/// teloxide and restrict logs from your program by the specified filter. -/// -/// # Example -/// -/// Allow printing all logs from your program up to [`LevelFilter::Debug`] (i.e. -/// do not print traces): -/// -/// ```no_compile -/// teloxide::enable_logging_with_filter!(log::LevelFilter::Debug); -/// ``` -/// -/// # Note -/// -/// Calling this macro **is not mandatory**; you can setup if your own logger if -/// you want. -/// -/// [pretty-env-logger]: https://crates.io/crates/pretty_env_logger -/// [`LevelFilter::Debug`]: https://docs.rs/log/0.4.10/log/enum.LevelFilter.html -#[macro_export] -#[deprecated = "Choose logging implementation yourself"] -macro_rules! enable_logging_with_filter { - ($filter:expr) => { - pretty_env_logger::formatted_builder() - .write_style(pretty_env_logger::env_logger::WriteStyle::Auto) - .filter(Some(&env!("CARGO_CRATE_NAME").replace("-", "_")), $filter) - .filter(Some("teloxide"), log::LevelFilter::Info) - .init(); - }; -} From df58faab9748bfeb61bad968d08331c5a54dddd8 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Sun, 2 Oct 2022 10:15:20 +0600 Subject: [PATCH 32/66] More `#[must_use]` functions. --- CHANGELOG.md | 21 +++++++++++++-------- src/dispatching/dialogue.rs | 1 + src/dispatching/handler_ext.rs | 1 + src/utils/command.rs | 5 +++++ 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bfe7543d..10bf0b514 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,17 +9,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Updated `teloxide-macros` see its [changelog](https://github.com/teloxide/teloxide-macros/blob/master/CHANGELOG.md#unreleased) for more -- `UpdateListener` now has an associated type `Err` instead of a generic -- `AsUpdateStream` now has an associated type `StreamErr` instead of a generic -- Rename `dispatching::stop_token::{AsyncStopToken, AsyncStopFlag}` => `stop::{StopToken, StopFlag}` + - Updated `teloxide-macros` see its [changelog](https://github.com/teloxide/teloxide-macros/blob/master/CHANGELOG.md#unreleased) for more + - `UpdateListener` now has an associated type `Err` instead of a generic + - `AsUpdateStream` now has an associated type `StreamErr` instead of a generic + - Rename `dispatching::stop_token::{AsyncStopToken, AsyncStopFlag}` => `stop::{StopToken, StopFlag}` + - The following functions are now `#[must_use]`: + - `BotCommands::ty`. + - `CommandDescriptions::{new, global_description, username, username_from_me}`. + - `teloxide::filter_command`. + - `teloxide::dispatching::dialogue::enter`. ### Removed -- `dispatching::stop_token::StopToken` trait (all uses are replaced with `stop::StopToken` structure) -- Some previously deprecated items - - `enable_logging!`, `enable_logging_with_filter!` - - `HandlerFactory`, `HandlerExt::dispatch_by` + - `dispatching::stop_token::StopToken` trait (all uses are replaced with `stop::StopToken` structure) + - Some previously deprecated items + - `enable_logging!`, `enable_logging_with_filter!` + - `HandlerFactory`, `HandlerExt::dispatch_by` ## 0.10.1 - 2022-07-22 diff --git a/src/dispatching/dialogue.rs b/src/dispatching/dialogue.rs index 1ecffde24..6f9aa824e 100644 --- a/src/dispatching/dialogue.rs +++ b/src/dispatching/dialogue.rs @@ -211,6 +211,7 @@ where /// - `Upd` /// /// [`HandlerExt::enter_dialogue`]: super::HandlerExt::enter_dialogue +#[must_use] pub fn enter() -> Handler<'static, DependencyMap, Output, DpHandlerDescription> where S: Storage + ?Sized + Send + Sync + 'static, diff --git a/src/dispatching/handler_ext.rs b/src/dispatching/handler_ext.rs index a6e80f0c7..f73dfb0a2 100644 --- a/src/dispatching/handler_ext.rs +++ b/src/dispatching/handler_ext.rs @@ -82,6 +82,7 @@ where /// /// - [`crate::types::Message`] /// - [`crate::types::Me`] +#[must_use] pub fn filter_command() -> Handler<'static, DependencyMap, Output, DpHandlerDescription> where C: BotCommands + Send + Sync + 'static, diff --git a/src/utils/command.rs b/src/utils/command.rs index 8d29ee7bb..d84efc0c2 100644 --- a/src/utils/command.rs +++ b/src/utils/command.rs @@ -235,6 +235,7 @@ pub trait BotCommands: Sized { /// Returns `PhantomData` that is used as a param of [`commands_repl`] /// /// [`commands_repl`]: (crate::repls2::commands_repl) + #[must_use] fn ty() -> PhantomData { PhantomData } @@ -296,11 +297,13 @@ pub struct CommandDescription<'a> { impl<'a> CommandDescriptions<'a> { /// Creates new [`CommandDescriptions`] from a list of command descriptions. + #[must_use] pub fn new(descriptions: &'a [CommandDescription<'a>]) -> Self { Self { global_description: None, descriptions, bot_username: None } } /// Sets the global description of these commands. + #[must_use] pub fn global_description(self, global_description: &'a str) -> Self { Self { global_description: Some(global_description), ..self } } @@ -328,6 +331,7 @@ impl<'a> CommandDescriptions<'a> { /// message" /// ); /// ``` + #[must_use] pub fn username(self, bot_username: &'a str) -> Self { Self { bot_username: Some(bot_username), ..self } } @@ -338,6 +342,7 @@ impl<'a> CommandDescriptions<'a> { /// method to get the username. /// /// [`username`]: self::CommandDescriptions::username + #[must_use] pub fn username_from_me(self, me: &'a Me) -> CommandDescriptions<'a> { self.username(me.user.username.as_deref().expect("Bots must have usernames")) } From d95f0c717fda0d452994e199c14767aa08dc2a76 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Sun, 2 Oct 2022 21:14:35 +0600 Subject: [PATCH 33/66] Refactor indentation in `CHANGELOG.md` --- CHANGELOG.md | 297 +++++++++++++++++++++++++++------------------------ 1 file changed, 156 insertions(+), 141 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10bf0b514..fd8dc97f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,52 +9,52 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - - Updated `teloxide-macros` see its [changelog](https://github.com/teloxide/teloxide-macros/blob/master/CHANGELOG.md#unreleased) for more - - `UpdateListener` now has an associated type `Err` instead of a generic - - `AsUpdateStream` now has an associated type `StreamErr` instead of a generic - - Rename `dispatching::stop_token::{AsyncStopToken, AsyncStopFlag}` => `stop::{StopToken, StopFlag}` - - The following functions are now `#[must_use]`: - - `BotCommands::ty`. - - `CommandDescriptions::{new, global_description, username, username_from_me}`. - - `teloxide::filter_command`. - - `teloxide::dispatching::dialogue::enter`. +- Updated `teloxide-macros` see its [changelog](https://github.com/teloxide/teloxide-macros/blob/master/CHANGELOG.md#unreleased) for more +- `UpdateListener` now has an associated type `Err` instead of a generic +- `AsUpdateStream` now has an associated type `StreamErr` instead of a generic +- Rename `dispatching::stop_token::{AsyncStopToken, AsyncStopFlag}` => `stop::{StopToken, StopFlag}` +- The following functions are now `#[must_use]`: + - `BotCommands::ty`. + - `CommandDescriptions::{new, global_description, username, username_from_me}`. + - `teloxide::filter_command`. + - `teloxide::dispatching::dialogue::enter`. ### Removed - - `dispatching::stop_token::StopToken` trait (all uses are replaced with `stop::StopToken` structure) - - Some previously deprecated items - - `enable_logging!`, `enable_logging_with_filter!` - - `HandlerFactory`, `HandlerExt::dispatch_by` +- `dispatching::stop_token::StopToken` trait (all uses are replaced with `stop::StopToken` structure) +- Some previously deprecated items + - `enable_logging!`, `enable_logging_with_filter!` + - `HandlerFactory`, `HandlerExt::dispatch_by` ## 0.10.1 - 2022-07-22 ### Fixed - - Mark the following functions with `#[must_use]` ([PR 457](https://github.com/teloxide/teloxide/pull/457)): - - `TraceStorage::into_inner`. - - `AsyncStopToken::new_pair`. - - `AsyncStopFlag::is_stopped`. - - All from `crate::utils::{html, markdown}`. - - Rendering of GIFs in lib.rs and crates.io ([PR 681](https://github.com/teloxide/teloxide/pull/681)). +- Mark the following functions with `#[must_use]` ([PR 457](https://github.com/teloxide/teloxide/pull/457)): + - `TraceStorage::into_inner`. + - `AsyncStopToken::new_pair`. + - `AsyncStopFlag::is_stopped`. + - All from `crate::utils::{html, markdown}`. +- Rendering of GIFs in lib.rs and crates.io ([PR 681](https://github.com/teloxide/teloxide/pull/681)). ## 0.10.0 - 2022-07-21 ### Added - - Security checks based on `secret_token` param of `set_webhook` to built-in webhooks. - - `dispatching::update_listeners::{PollingBuilder, Polling, PollingStream}`. - - `DispatcherBuilder::enable_ctrlc_handler` method. +- Security checks based on `secret_token` param of `set_webhook` to built-in webhooks. +- `dispatching::update_listeners::{PollingBuilder, Polling, PollingStream}`. +- `DispatcherBuilder::enable_ctrlc_handler` method. ### Fixed - - `Dispatcher` no longer "leaks" memory for every inactive user ([PR 657](https://github.com/teloxide/teloxide/pull/657)). - - Allow specifying a path to a custom command parser in `parse_with` ([issue 668](https://github.com/teloxide/teloxide/issues/668)). +- `Dispatcher` no longer "leaks" memory for every inactive user ([PR 657](https://github.com/teloxide/teloxide/pull/657)). +- Allow specifying a path to a custom command parser in `parse_with` ([issue 668](https://github.com/teloxide/teloxide/issues/668)). ### Changed - - Add the `Key: Clone` requirement for `impl Dispatcher` [**BC**]. - - `dispatching::update_listeners::{polling_default, polling}` now return a named, `Polling<_>` type. - - Update teloxide-core to v0.7.0 with Bot API 6.1 support, see [its changelog][core07c] for more information [**BC**]. +- Add the `Key: Clone` requirement for `impl Dispatcher` [**BC**]. +- `dispatching::update_listeners::{polling_default, polling}` now return a named, `Polling<_>` type. +- Update teloxide-core to v0.7.0 with Bot API 6.1 support, see [its changelog][core07c] for more information [**BC**]. [core07c]: https://github.com/teloxide/teloxide-core/blob/master/CHANGELOG.md#070---2022-07-19 @@ -67,24 +67,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - - Fix Api Unknown error (Can't parse entities) on message created with `utils::markdown::user_mention_or_link` if user full name contaiins some escapable symbols eg '.' +- Fix Api Unknown error (Can't parse entities) on message created with `utils::markdown::user_mention_or_link` if user full name contaiins some escapable symbols eg '.' ## 0.9.1 - 2022-05-27 ### Fixed - - Fix `#[command(rename = "...")]` for custom command names ([issue 633](https://github.com/teloxide/teloxide/issues/633)). +- Fix `#[command(rename = "...")]` for custom command names ([issue 633](https://github.com/teloxide/teloxide/issues/633)). ## 0.9.0 - 2022-04-27 ### Added - - The `dispatching::filter_command` function (also accessible as `teloxide::filter_command`) as a shortcut for `dptree::entry().filter_command()`. - - Re-export `dptree::case!` as `teloxide::handler!` (the former is preferred for new code). +- The `dispatching::filter_command` function (also accessible as `teloxide::filter_command`) as a shortcut for `dptree::entry().filter_command()`. +- Re-export `dptree::case!` as `teloxide::handler!` (the former is preferred for new code). ### Changed - - Update teloxide-core to v0.6.0 with [Bot API 6.0] support [**BC**]. +- Update teloxide-core to v0.6.0 with [Bot API 6.0] support [**BC**]. [Bot API 6.0]: https://core.telegram.org/bots/api#april-16-2022 @@ -92,46 +92,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - - Fix the broken `#[derive(DialogueState)]` (function return type `dptree::Handler`). +- Fix the broken `#[derive(DialogueState)]` (function return type `dptree::Handler`). ## 0.8.1 - 2022-04-24 ### Added - - Implement `GetChatId` for `Update`. - - The `dialogue::enter()` function as a shortcut for `dptree::entry().enter_dialogue()`. +- Implement `GetChatId` for `Update`. +- The `dialogue::enter()` function as a shortcut for `dptree::entry().enter_dialogue()`. ## 0.8.0 - 2022-04-18 ### Removed - - The old dispatching system and related stuff: `dispatching`, `utils::UpState`, `prelude`, `repls2`, `crate::{dialogues_repl, dialogues_repl_with_listener}`, and `#[teloxide(subtransition)]` [**BC**]. +- The old dispatching system and related stuff: `dispatching`, `utils::UpState`, `prelude`, `repls2`, `crate::{dialogues_repl, dialogues_repl_with_listener}`, and `#[teloxide(subtransition)]` [**BC**]. ### Added - - The new API for dialogue handlers: `teloxide::handler!` ([issue 567](https://github.com/teloxide/teloxide/issues/567)). - - Built-in webhooks support via `teloxide::dispatching::update_listeners::webhooks` module. - - `Dialogue::chat_id` for retrieving a chat ID from a dialogue. +- The new API for dialogue handlers: `teloxide::handler!` ([issue 567](https://github.com/teloxide/teloxide/issues/567)). +- Built-in webhooks support via `teloxide::dispatching::update_listeners::webhooks` module. +- `Dialogue::chat_id` for retrieving a chat ID from a dialogue. ### Changed - - Updated `teloxide-core` from version `0.4.5` to version [`0.5.0`](https://github.com/teloxide/teloxide-core/releases/tag/v0.5.0) [**BC**] - - Rename `dispatching2` => `dispatching` [**BC**]. - - Rename `prelude2` => `prelude` [**BC**]. - - Move `update_listeners`, `stop_token`, `IdleShutdownError`, and `ShutdownToken` from the old `dispatching` to the new `dispatching` (previously `dispatching2`). - - Replace `crate::{commands_repl, commands_repl_with_listener, repl, repl_with_listener}` with those of the new `dispatching` [**BC**]. - - `UpdateListener::StopToken` is now always `Send` [**BC**]. - - Rename `BotCommand` trait to `BotCommands` [**BC**]. - - `BotCommands::descriptions` now returns `CommandDescriptions` instead of `String` [**BC**]. - - Mark `Dialogue::new` as `#[must_use]`. +- Updated `teloxide-core` from version `0.4.5` to version [`0.5.0`](https://github.com/teloxide/teloxide-core/releases/tag/v0.5.0) [**BC**] +- Rename `dispatching2` => `dispatching` [**BC**]. +- Rename `prelude2` => `prelude` [**BC**]. +- Move `update_listeners`, `stop_token`, `IdleShutdownError`, and `ShutdownToken` from the old `dispatching` to the new `dispatching` (previously `dispatching2`). +- Replace `crate::{commands_repl, commands_repl_with_listener, repl, repl_with_listener}` with those of the new `dispatching` [**BC**]. +- `UpdateListener::StopToken` is now always `Send` [**BC**]. +- Rename `BotCommand` trait to `BotCommands` [**BC**]. +- `BotCommands::descriptions` now returns `CommandDescriptions` instead of `String` [**BC**]. +- Mark `Dialogue::new` as `#[must_use]`. ### Fixed - - Concurrent update handling in the new dispatcher ([issue 536](https://github.com/teloxide/teloxide/issues/536)). +- Concurrent update handling in the new dispatcher ([issue 536](https://github.com/teloxide/teloxide/issues/536)). ### Deprecated - - `HandlerFactory` and `HandlerExt::dispatch_by` in favour of `teloxide::handler!`. +- `HandlerFactory` and `HandlerExt::dispatch_by` in favour of `teloxide::handler!`. ## 0.7.3 - 2022-04-03 @@ -179,8 +179,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - - `BotCommand::bot_commands` to obtain Telegram API commands ([issue 262](https://github.com/teloxide/teloxide/issues/262)). - - The `dispatching2` and `prelude2` modules. They presents a new dispatching model based on `dptree`. +- `BotCommand::bot_commands` to obtain Telegram API commands ([issue 262](https://github.com/teloxide/teloxide/issues/262)). +- The `dispatching2` and `prelude2` modules. They presents a new dispatching model based on `dptree`. ### Changed @@ -219,81 +219,83 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - - `Storage::get_dialogue` to obtain a dialogue indexed by a chat ID. - - `InMemStorageError` with a single variant `DialogueNotFound` to be returned from `InMemStorage::remove_dialogue`. - - `RedisStorageError::DialogueNotFound` and `SqliteStorageError::DialogueNotFound` to be returned from `Storage::remove_dialogue`. - - A way to `shutdown` dispatcher - - `Dispatcher::shutdown_token` function. - - `ShutdownToken` with a `shutdown` function. - - `Dispatcher::setup_ctrlc_handler` function ([issue 153](https://github.com/teloxide/teloxide/issues/153)). - - `IdleShutdownError` - - Automatic update filtering ([issue 389](https://github.com/teloxide/teloxide/issues/389)). - - Added reply shortcut to every kind of messages ([PR 404](https://github.com/teloxide/teloxide/pull/404)). +- `Storage::get_dialogue` to obtain a dialogue indexed by a chat ID. +- `InMemStorageError` with a single variant `DialogueNotFound` to be returned from `InMemStorage::remove_dialogue`. +- `RedisStorageError::DialogueNotFound` and `SqliteStorageError::DialogueNotFound` to be returned from `Storage::remove_dialogue`. +- A way to `shutdown` dispatcher + - `Dispatcher::shutdown_token` function. + - `ShutdownToken` with a `shutdown` function. +- `Dispatcher::setup_ctrlc_handler` function ([issue 153](https://github.com/teloxide/teloxide/issues/153)). +- `IdleShutdownError` +- Automatic update filtering ([issue 389](https://github.com/teloxide/teloxide/issues/389)). +- Added reply shortcut to every kind of messages ([PR 404](https://github.com/teloxide/teloxide/pull/404)). ### Changed - - Do not return a dialogue from `Storage::{remove_dialogue, update_dialogue}`. - - Return an error from `Storage::remove_dialogue` if a dialogue does not exist. - - Require `D: Clone` in `dialogues_repl(_with_listener)` and `InMemStorage`. - - Automatically delete a webhook if it was set up in `update_listeners::polling_default` (thereby making it `async`, [issue 319](https://github.com/teloxide/teloxide/issues/319)). - - `polling` and `polling_default` now require `R: 'static` - - Refactor `UpdateListener` trait: - - Add a `StopToken` associated type. - - It must implement a new `StopToken` trait which has the only function `fn stop(self);` - - Add a `stop_token` function that returns `Self::StopToken` and allows stopping the listener later ([issue 166](https://github.com/teloxide/teloxide/issues/166)). - - Remove blanked implementation. - - Remove `Stream` from super traits. - - Add `AsUpdateStream` to super traits. - - Add an `AsUpdateStream` trait that allows turning implementors into streams of updates (GAT workaround). - - Add a `timeout_hint` function (with a default implementation). - - `Dispatcher::dispatch` and `Dispatcher::dispatch_with_listener` now require mutable reference to self. - - Repls can now be stopped by `^C` signal. - - `Noop` and `AsyncStopToken`stop tokens. - - `StatefulListener`. - - Emit not only errors but also warnings and general information from teloxide, when set up by `enable_logging!`. - - Use `i64` instead of `i32` for `user_id` in `html::user_mention` and `markdown::user_mention`. - - Updated to `teloxide-core` `v0.3.0` (see it's [changelog](https://github.com/teloxide/teloxide-core/blob/master/CHANGELOG.md#030---2021-07-05) for more) +- Do not return a dialogue from `Storage::{remove_dialogue, update_dialogue}`. +- Return an error from `Storage::remove_dialogue` if a dialogue does not exist. +- Require `D: Clone` in `dialogues_repl(_with_listener)` and `InMemStorage`. +- Automatically delete a webhook if it was set up in `update_listeners::polling_default` (thereby making it `async`, [issue 319](https://github.com/teloxide/teloxide/issues/319)). +- `polling` and `polling_default` now require `R: 'static` +- Refactor `UpdateListener` trait: + - Add a `StopToken` associated type. + - It must implement a new `StopToken` trait which has the only function `fn stop(self);` + - Add a `stop_token` function that returns `Self::StopToken` and allows stopping the listener later ([issue 166](https://github.com/teloxide/teloxide/issues/166)). + - Remove blanked implementation. + - Remove `Stream` from super traits. + - Add `AsUpdateStream` to super traits. + - Add an `AsUpdateStream` trait that allows turning implementors into streams of updates (GAT workaround). + - Add a `timeout_hint` function (with a default implementation). +- `Dispatcher::dispatch` and `Dispatcher::dispatch_with_listener` now require mutable reference to self. +- Repls can now be stopped by `^C` signal. +- `Noop` and `AsyncStopToken`stop tokens. +- `StatefulListener`. +- Emit not only errors but also warnings and general information from teloxide, when set up by `enable_logging!`. +- Use `i64` instead of `i32` for `user_id` in `html::user_mention` and `markdown::user_mention`. +- Updated to `teloxide-core` `v0.3.0` (see it's [changelog](https://github.com/teloxide/teloxide-core/blob/master/CHANGELOG.md#030---2021-07-05) for more) ### Fixed - - Remove the `reqwest` dependency. It's not needed after the [teloxide-core] integration. - - A storage persistency bug ([issue 304](https://github.com/teloxide/teloxide/issues/304)). - - 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]`. +- Remove the `reqwest` dependency. It's not needed after the [teloxide-core] integration. +- A storage persistency bug ([issue 304](https://github.com/teloxide/teloxide/issues/304)). +- 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 ### Added - - Integrate [teloxide-core]. - - Allow arbitrary error types to be returned from (sub)transitions ([issue 242](https://github.com/teloxide/teloxide/issues/242)). - - The `respond` function, a shortcut for `ResponseResult::Ok(())`. - - The `sqlite-storage` feature -- enables SQLite support. - - `Dispatcher::{my_chat_members_handler, chat_members_handler}` + +- Integrate [teloxide-core]. +- Allow arbitrary error types to be returned from (sub)transitions ([issue 242](https://github.com/teloxide/teloxide/issues/242)). +- The `respond` function, a shortcut for `ResponseResult::Ok(())`. +- The `sqlite-storage` feature -- enables SQLite support. +- `Dispatcher::{my_chat_members_handler, chat_members_handler}` [teloxide-core]: https://github.com/teloxide/teloxide-core ### Deprecated - - `UpdateWithCx::answer_str` +- `UpdateWithCx::answer_str` ### Fixed - - Hide `SubtransitionOutputType` from the docs. +- Hide `SubtransitionOutputType` from the docs. ### Changed - - Export `teloxide_macros::teloxide` in `prelude`. - - `dispatching::dialogue::serializer::{JSON -> Json, CBOR -> Cbor}` - - Allow `bot_name` be `N`, where `N: Into + ...` in `commands_repl` & `commands_repl_with_listener`. - - 'Edit methods' (namely `edit_message_live_location`, `stop_message_live_location`, `edit_message_text`, + +- Export `teloxide_macros::teloxide` in `prelude`. +- `dispatching::dialogue::serializer::{JSON -> Json, CBOR -> Cbor}` +- Allow `bot_name` be `N`, where `N: Into + ...` in `commands_repl` & `commands_repl_with_listener`. +- 'Edit methods' (namely `edit_message_live_location`, `stop_message_live_location`, `edit_message_text`, `edit_message_caption`, `edit_message_media` and `edit_message_reply_markup`) are split into common and inline versions (e.g.: `edit_message_text` and `edit_inline_message_text`). Instead of `ChatOrInlineMessage` common versions accept `chat_id: impl Into` and `message_id: i32` whereas inline versions accept `inline_message_id: impl Into`. Also note that return type of inline versions is `True` ([issue 253], [pr 257]) - - `ChatOrInlineMessage` is renamed to `TargetMessage`, it's `::Chat` variant is renamed to `::Common`, +- `ChatOrInlineMessage` is renamed to `TargetMessage`, it's `::Chat` variant is renamed to `::Common`, `#[non_exhaustive]` annotation is removed from the enum, type of `TargetMessage::Inline::inline_message_id` changed `i32` => `String`. `TargetMessage` now implements `From`, `get_game_high_scores` and `set_game_score` use `Into` to accept `String`s. ([issue 253], [pr 257]) - - Remove `ResponseResult` from `prelude`. +- Remove `ResponseResult` from `prelude`. [issue 253]: https://github.com/teloxide/teloxide/issues/253 [pr 257]: https://github.com/teloxide/teloxide/pull/257 @@ -302,75 +304,88 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - - Failing compilation with `serde::export` ([issue 328](https://github.com/teloxide/teloxide/issues/328)). +- Failing compilation with `serde::export` ([issue 328](https://github.com/teloxide/teloxide/issues/328)). ## 0.3.3 - 2020-10-30 ### Fixed - - The `dice` field from `MessageDice` is public now ([issue 306](https://github.com/teloxide/teloxide/issues/306)) + +- The `dice` field from `MessageDice` is public now ([issue 306](https://github.com/teloxide/teloxide/issues/306)) ## 0.3.2 - 2020-10-23 ### Added - - `LoginUrl::new` ([issue 298](https://github.com/teloxide/teloxide/issues/298)) + +- `LoginUrl::new` ([issue 298](https://github.com/teloxide/teloxide/issues/298)) ## 0.3.1 - 2020-08-25 ### Added - - `Bot::builder` method ([PR 269](https://github.com/teloxide/teloxide/pull/269)). + +- `Bot::builder` method ([PR 269](https://github.com/teloxide/teloxide/pull/269)). ## 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`. - - The `Transition`, `Subtransition`, `SubtransitionOutputType` traits. - - A nicer approach to manage dialogues via `#[derive(Transition)]` + `#[teloxide(subtransition)]` (see [`examples/dialogue_bot`](https://github.com/teloxide/teloxide/tree/af2aa218e7bfc442ab4475023a1c661834f576fc/examples/dialogue_bot)). - - The `redis-storage` feature -- enables the Redis support. - - The `cbor-serializer` feature -- enables the `CBOR` serializer for dialogues. - - The `bincode-serializer` feature -- enables the `Bincode` serializer for dialogues. - - The `frunk` feature -- enables `teloxide::utils::UpState`, which allows mapping from a structure of `field1, ..., fieldN` to a structure of `field1, ..., fieldN, fieldN+1`. - - Upgrade to v4.9 Telegram bots API. - - `teloxide::utils::client_from_env` -- constructs a client from the `TELOXIDE_TOKEN` environmental variable. - - Import `Transition`, `TransitionIn`, `TransitionOut`, `UpState` to `teloxide::prelude`. - - Import `repl`, `commands_repl` to `teloxide`. - - Let users inspect an unknown API error using `ApiErrorKind::Unknown(String)`. All the known API errors are placed into `KnownApiErrorKind`. - - Setters to all the API types. - - `teloxide::dispatching::dialogue::serializer` -- various serializers for memory storages. The `Serializer` trait, `Bincode`, `CBOR`, `JSON`. - - `teloxide::{repl, repl_with_listener, commands_repl, commands_repl_with_listener, dialogues_repl, dialogues_repl_with_listener}` - - `InputFile::Memory` - - Option to hide a command from description ([issue 217](https://github.com/teloxide/teloxide/issues/217)). - - Respect the `TELOXIDE_PROXY` environment variable in `Bot::from_env`. + +- Support for typed bot commands ([issue 152](https://github.com/teloxide/teloxide/issues/152)). +- `BotBuilder`, which allows setting a default `ParseMode`. +- The `Transition`, `Subtransition`, `SubtransitionOutputType` traits. +- A nicer approach to manage dialogues via `#[derive(Transition)]` + `#[teloxide(subtransition)]` (see [`examples/dialogue_bot`](https://github.com/teloxide/teloxide/tree/af2aa218e7bfc442ab4475023a1c661834f576fc/examples/dialogue_bot)). +- The `redis-storage` feature -- enables the Redis support. +- The `cbor-serializer` feature -- enables the `CBOR` serializer for dialogues. +- The `bincode-serializer` feature -- enables the `Bincode` serializer for dialogues. +- The `frunk` feature -- enables `teloxide::utils::UpState`, which allows mapping from a structure of `field1, ..., fieldN` to a structure of `field1, ..., fieldN, fieldN+1`. +- Upgrade to v4.9 Telegram bots API. +- `teloxide::utils::client_from_env` -- constructs a client from the `TELOXIDE_TOKEN` environmental variable. +- Import `Transition`, `TransitionIn`, `TransitionOut`, `UpState` to `teloxide::prelude`. +- Import `repl`, `commands_repl` to `teloxide`. +- Let users inspect an unknown API error using `ApiErrorKind::Unknown(String)`. All the known API errors are placed into `KnownApiErrorKind`. +- Setters to all the API types. +- `teloxide::dispatching::dialogue::serializer` -- various serializers for memory storages. The `Serializer` trait, `Bincode`, `CBOR`, `JSON`. +- `teloxide::{repl, repl_with_listener, commands_repl, commands_repl_with_listener, dialogues_repl, dialogues_repl_with_listener}` +- `InputFile::Memory` +- Option to hide a command from description ([issue 217](https://github.com/teloxide/teloxide/issues/217)). +- Respect the `TELOXIDE_PROXY` environment variable in `Bot::from_env`. ### Deprecated - - `Bot::{from_env_with_client, new, with_client}` + +- `Bot::{from_env_with_client, new, with_client}` ### Changed - - `DialogueDispatcherHandlerCx` -> `DialogueWithCx`. - - `DispatcherHandlerCx` -> `UpdateWithCx`. - - Now provided description of unknown telegram error, by splitting ApiErrorKind at `ApiErrorKind` and `ApiErrorKindKnown` enums ([issue 199](https://github.com/teloxide/teloxide/issues/199)). - - Extract `Bot` from `Arc` ([issue 216](https://github.com/teloxide/teloxide/issues/230)). - - Mark all the API types as `#[non_exhaustive]`. - - Replace all `mime_type: String` with `MimeWrapper`. + +- `DialogueDispatcherHandlerCx` -> `DialogueWithCx`. +- `DispatcherHandlerCx` -> `UpdateWithCx`. +- Now provided description of unknown telegram error, by splitting ApiErrorKind at `ApiErrorKind` and `ApiErrorKindKnown` enums ([issue 199](https://github.com/teloxide/teloxide/issues/199)). +- Extract `Bot` from `Arc` ([issue 216](https://github.com/teloxide/teloxide/issues/230)). +- Mark all the API types as `#[non_exhaustive]`. +- Replace all `mime_type: String` with `MimeWrapper`. ### Fixed - - 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)). + +- 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 + ### 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`. + +- 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`. ### Fixed - - Fix parsing a pinned message ([Issue 167](https://github.com/teloxide/teloxide/issues/167)). - - Replace `LanguageCode` with `String`, because [the official Telegram documentation](https://core.telegram.org/bots/api#getchat) doesn't specify a concrete version of IETF language tag. - - Problems with the `poll_type` field ([Issue 178](https://github.com/teloxide/teloxide/issues/178)). - - Make `polling_default` actually a long polling update listener ([PR 182](https://github.com/teloxide/teloxide/pull/182)). + +- Fix parsing a pinned message ([Issue 167](https://github.com/teloxide/teloxide/issues/167)). +- Replace `LanguageCode` with `String`, because [the official Telegram documentation](https://core.telegram.org/bots/api#getchat) doesn't specify a concrete version of IETF language tag. +- Problems with the `poll_type` field ([Issue 178](https://github.com/teloxide/teloxide/issues/178)). +- Make `polling_default` actually a long polling update listener ([PR 182](https://github.com/teloxide/teloxide/pull/182)). ### Removed - - [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. + +- [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 + ### Added - - This project. +- This project. From cde6827cee5eb482ed9c2b900a29c1f31c2f9359 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Sun, 2 Oct 2022 21:44:04 +0600 Subject: [PATCH 34/66] Use `RequestError` in REPLs --- CHANGELOG.md | 5 +++++ README.md | 10 ++-------- examples/admin.rs | 22 +++++----------------- examples/command.rs | 8 +------- examples/heroku_ping_pong.rs | 2 +- examples/ngrok_ping_pong.rs | 2 +- examples/throw_dice.rs | 2 +- src/dispatching/repls/commands_repl.rs | 11 +++++------ src/dispatching/repls/repl.rs | 17 +++++++---------- src/lib.rs | 2 +- src/prelude.rs | 17 ++++++++++------- 11 files changed, 39 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bfe7543d..7e02e5c10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `UpdateListener` now has an associated type `Err` instead of a generic - `AsUpdateStream` now has an associated type `StreamErr` instead of a generic - Rename `dispatching::stop_token::{AsyncStopToken, AsyncStopFlag}` => `stop::{StopToken, StopFlag}` +- Replace the generic error type `E` with `RequestError` for REPLs (`repl(_with_listener)`, `commands_repl(_with_listener)`) + +### Added + +- `requests::ResponseResult` to `prelude` ### Removed diff --git a/README.md b/README.md index 375ee95a5..ed8251ac1 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ async fn main() { teloxide::repl(bot, |message: Message, bot: Bot| async move { bot.send_dice(message.chat.id).await?; - respond(()) + Ok(()) }) .await; } @@ -122,8 +122,6 @@ Commands are strongly typed and defined declaratively, similar to how we define ```rust,no_run use teloxide::{prelude::*, utils::command::BotCommands}; -use std::error::Error; - #[tokio::main] async fn main() { pretty_env_logger::init(); @@ -145,11 +143,7 @@ enum Command { UsernameAndAge { username: String, age: u8 }, } -async fn answer( - bot: Bot, - message: Message, - command: Command, -) -> Result<(), Box> { +async fn answer(bot: Bot, message: Message, command: Command) -> ResponseResult<()> { match command { Command::Help => { bot.send_message(message.chat.id, Command::descriptions().to_string()).await? diff --git a/examples/admin.rs b/examples/admin.rs index db45e3fa8..a9d800e65 100644 --- a/examples/admin.rs +++ b/examples/admin.rs @@ -1,4 +1,4 @@ -use std::{error::Error, str::FromStr}; +use std::str::FromStr; use chrono::Duration; use teloxide::{prelude::*, types::ChatPermissions, utils::command::BotCommands}; @@ -63,11 +63,7 @@ async fn main() { teloxide::commands_repl(bot, action, Command::ty()).await; } -async fn action( - bot: Bot, - msg: Message, - command: Command, -) -> Result<(), Box> { +async fn action(bot: Bot, msg: Message, command: Command) -> ResponseResult<()> { match command { Command::Help => { bot.send_message(msg.chat.id, Command::descriptions().to_string()).await?; @@ -81,7 +77,7 @@ async fn action( } // Kick a user with a replied message. -async fn kick_user(bot: Bot, msg: Message) -> Result<(), Box> { +async fn kick_user(bot: Bot, msg: Message) -> ResponseResult<()> { match msg.reply_to_message() { Some(replied) => { // bot.unban_chat_member can also kicks a user from a group chat. @@ -95,11 +91,7 @@ async fn kick_user(bot: Bot, msg: Message) -> Result<(), Box Result<(), Box> { +async fn ban_user(bot: Bot, msg: Message, time: Duration) -> ResponseResult<()> { match msg.reply_to_message() { Some(replied) => { bot.kick_chat_member( @@ -118,11 +110,7 @@ async fn ban_user( } // Mute a user with a replied message. -async fn mute_user( - bot: Bot, - msg: Message, - time: Duration, -) -> Result<(), Box> { +async fn mute_user(bot: Bot, msg: Message, time: Duration) -> ResponseResult<()> { match msg.reply_to_message() { Some(replied) => { bot.restrict_chat_member( diff --git a/examples/command.rs b/examples/command.rs index 11a2ce55f..d0f671eca 100644 --- a/examples/command.rs +++ b/examples/command.rs @@ -1,7 +1,5 @@ use teloxide::{prelude::*, utils::command::BotCommands}; -use std::error::Error; - #[tokio::main] async fn main() { pretty_env_logger::init(); @@ -23,11 +21,7 @@ enum Command { UsernameAndAge { username: String, age: u8 }, } -async fn answer( - bot: Bot, - message: Message, - command: Command, -) -> Result<(), Box> { +async fn answer(bot: Bot, message: Message, command: Command) -> ResponseResult<()> { match command { Command::Help => { bot.send_message(message.chat.id, Command::descriptions().to_string()).await? diff --git a/examples/heroku_ping_pong.rs b/examples/heroku_ping_pong.rs index 5c8def6aa..1c104eb83 100644 --- a/examples/heroku_ping_pong.rs +++ b/examples/heroku_ping_pong.rs @@ -49,7 +49,7 @@ async fn main() { bot, |msg: Message, bot: Bot| async move { bot.send_message(msg.chat.id, "pong").await?; - respond(()) + Ok(()) }, listener, ) diff --git a/examples/ngrok_ping_pong.rs b/examples/ngrok_ping_pong.rs index 6f9eefb0a..214868109 100644 --- a/examples/ngrok_ping_pong.rs +++ b/examples/ngrok_ping_pong.rs @@ -20,7 +20,7 @@ async fn main() { bot, |msg: Message, bot: Bot| async move { bot.send_message(msg.chat.id, "pong").await?; - respond(()) + Ok(()) }, listener, ) diff --git a/examples/throw_dice.rs b/examples/throw_dice.rs index 39272378e..0de45aecc 100644 --- a/examples/throw_dice.rs +++ b/examples/throw_dice.rs @@ -11,7 +11,7 @@ async fn main() { teloxide::repl(bot, |message: Message, bot: Bot| async move { bot.send_dice(message.chat.id).await?; - respond(()) + Ok(()) }) .await; } diff --git a/src/dispatching/repls/commands_repl.rs b/src/dispatching/repls/commands_repl.rs index ceeeb9179..eed3bf15d 100644 --- a/src/dispatching/repls/commands_repl.rs +++ b/src/dispatching/repls/commands_repl.rs @@ -5,6 +5,7 @@ use crate::{ error_handlers::LoggingErrorHandler, types::Update, utils::command::BotCommands, + RequestError, }; use dptree::di::{DependencyMap, Injectable}; use std::{fmt::Debug, marker::PhantomData}; @@ -34,13 +35,12 @@ use teloxide_core::requests::Requester; /// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop /// [`Dispatcher`]: crate::dispatching::Dispatcher #[cfg(feature = "ctrlc_handler")] -pub async fn commands_repl<'a, R, Cmd, H, E, Args>(bot: R, handler: H, cmd: PhantomData) +pub async fn commands_repl<'a, R, Cmd, H, Args>(bot: R, handler: H, cmd: PhantomData) where Cmd: BotCommands + Send + Sync + 'static, - H: Injectable, Args> + Send + Sync + 'static, + H: Injectable, Args> + Send + Sync + 'static, R: Requester + Clone + Send + Sync + 'static, ::GetUpdates: Send, - E: Debug + Send + Sync + 'static, { let cloned_bot = bot.clone(); @@ -78,18 +78,17 @@ where /// [`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, L, E, Args>( +pub async fn commands_repl_with_listener<'a, R, Cmd, H, L, Args>( bot: R, handler: H, listener: L, _cmd: PhantomData, ) where Cmd: BotCommands + Send + Sync + 'static, - H: Injectable, Args> + Send + Sync + 'static, + H: Injectable, Args> + Send + Sync + 'static, L: UpdateListener + Send + 'a, L::Err: Debug + Send + 'a, R: Requester + Clone + Send + Sync + 'static, - E: Debug + Send + Sync + 'static, { use crate::dispatching::Dispatcher; diff --git a/src/dispatching/repls/repl.rs b/src/dispatching/repls/repl.rs index 529cc711c..783dcb906 100644 --- a/src/dispatching/repls/repl.rs +++ b/src/dispatching/repls/repl.rs @@ -1,7 +1,8 @@ use crate::{ dispatching::{update_listeners, update_listeners::UpdateListener, UpdateFilterExt}, - error_handlers::{LoggingErrorHandler, OnError}, + error_handlers::LoggingErrorHandler, types::Update, + RequestError, }; use dptree::di::{DependencyMap, Injectable}; use std::fmt::Debug; @@ -27,11 +28,9 @@ use teloxide_core::requests::Requester; /// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop /// [`Dispatcher`]: crate::dispatching::Dispatcher #[cfg(feature = "ctrlc_handler")] -pub async fn repl(bot: R, handler: H) +pub async fn repl(bot: R, handler: H) where - H: Injectable, Args> + Send + Sync + 'static, - Result<(), E>: OnError, - E: Debug + Send + Sync + 'static, + H: Injectable, Args> + Send + Sync + 'static, R: Requester + Send + Sync + Clone + 'static, ::GetUpdates: Send, { @@ -60,13 +59,11 @@ where /// [`repl`]: crate::dispatching::repls::repl() /// [`UpdateListener`]: crate::dispatching::update_listeners::UpdateListener #[cfg(feature = "ctrlc_handler")] -pub async fn repl_with_listener<'a, R, H, E, L, Args>(bot: R, handler: H, listener: L) +pub async fn repl_with_listener<'a, R, H, L, Args>(bot: R, handler: H, listener: L) where - H: Injectable, Args> + Send + Sync + 'static, + H: Injectable, Args> + Send + Sync + 'static, L: UpdateListener + Send + 'a, L::Err: Debug, - Result<(), E>: OnError, - E: Debug + Send + Sync + 'static, R: Requester + Clone + Send + Sync + 'static, { use crate::dispatching::Dispatcher; @@ -89,7 +86,7 @@ where #[test] fn repl_is_send() { let bot = crate::Bot::new(""); - let repl = crate::repl(bot, || async { crate::respond(()) }); + let repl = crate::repl(bot, || async { Ok(()) }); assert_send(&repl); fn assert_send(_: &impl Send) {} diff --git a/src/lib.rs b/src/lib.rs index c7a2e1a82..b262d957e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,7 @@ //! //! teloxide::repl(bot, |message: Message, bot: Bot| async move { //! bot.send_dice(message.chat.id).await?; -//! respond(()) +//! Ok(()) //! }) //! .await; //! # } diff --git a/src/prelude.rs b/src/prelude.rs index 46e4d0b7d..6dbe94df2 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,17 +1,20 @@ //! Commonly used items. -pub use crate::{ - error_handlers::{LoggingErrorHandler, OnError}, - respond, -}; +pub use crate::error_handlers::{LoggingErrorHandler, OnError}; + +#[allow(deprecated)] +pub use crate::respond; pub use crate::dispatching::{ dialogue::Dialogue, Dispatcher, HandlerExt as _, MessageFilterExt as _, UpdateFilterExt as _, }; -pub use teloxide_core::types::{ - CallbackQuery, ChatMemberUpdated, ChosenInlineResult, InlineQuery, Message, Poll, PollAnswer, - PreCheckoutQuery, ShippingQuery, Update, +pub use teloxide_core::{ + requests::ResponseResult, + types::{ + CallbackQuery, ChatMemberUpdated, ChosenInlineResult, InlineQuery, Message, Poll, + PollAnswer, PreCheckoutQuery, ShippingQuery, Update, + }, }; #[cfg(feature = "auto-send")] From 1b983e043f2d744bf5e971fe8d642f805ea26074 Mon Sep 17 00:00:00 2001 From: Waffle Maybe Date: Mon, 3 Oct 2022 13:48:14 +0400 Subject: [PATCH 35/66] Apply suggestions from code review Co-authored-by: Hirrolot --- CODE_STYLE.md | 51 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/CODE_STYLE.md b/CODE_STYLE.md index 21534c952..67c3d42e2 100644 --- a/CODE_STYLE.md +++ b/CODE_STYLE.md @@ -5,7 +5,7 @@ Please, read the whole document before you start pushing code. ## Generics -All trait bounds should be written in `where` +All trait bounds should be written in `where`: ```rust // GOOD @@ -35,11 +35,13 @@ where impl Trait for Wrap { ... } ``` -**Rationale:** `where` clauses are easier to read when there are a lot of bounds, uniformity. +**Rationale:** +- `where` clauses are easier to read when there are a lot of bounds +- uniformity ## Documentation comments -1. Documentation must describe what your code does and mustn't describe how your code does it and bla-bla-bla. +1. Documentation must describe _what_ your code does and mustn't describe _how_ your code does it and bla-bla-bla. 2. Be sure that your comments follow the grammar, including punctuation, the first capital letter and so on. ```rust // GOOD @@ -67,7 +69,7 @@ impl Trait for Wrap { ... } - Pass dependencies to handlers; - Disable a default Ctrl-C handling; ``` -3. Link resources in your comments when possible, for example: +3. Link resources in your comments when possible: ```rust /// Download a file from Telegram. /// @@ -84,7 +86,7 @@ impl Trait for Wrap { ... } ## Use `Self` where possible -When referring to the type for which block is implemented, prefer using `Self`, rather than the name of the type. +When referring to the type for which block is implemented, prefer using `Self`, rather than the name of the type: ```rust impl ErrorKind { @@ -134,7 +136,7 @@ struct Message { } ``` -**Rationale:** duplication is unnecessary +**Rationale:** duplication blurs the focus of code, making it unnecessarily longer. ## Conventional generic names @@ -150,7 +152,7 @@ Derive `Copy`, `Clone`, `Eq`, `PartialEq`, `Hash` and `Debug` for public types w Derive `Default` when there is a reasonable default value for the type. -**Rationale:** `Default` plays nicely with generic code (for example `mem::take`). +**Rationale:** `Default` plays nicely with generic code (for example, `mem::take`). ## `Into`-polymorphism @@ -161,7 +163,7 @@ I.e. when there are types that implement `Into` that are likely to be passed ## `must_use` -Always mark a functions as `#[must_use]` if they don't have side-effects and the only reason to call them is to get the result. +Always mark functions as `#[must_use]` if they don't have side effects and the only reason to call them is to get the result: ```rust impl User { @@ -195,7 +197,9 @@ use log::warn; warn!("Everything is on fire"); ``` -**Rationale:** uniformity, it's clearer which log crate is used. +**Rationale:** +- Less polluted import blocks +- Uniformity ## `&str` -> `String` conversion @@ -232,7 +236,10 @@ use super::{} pub use crate::x::Z; ``` -**Rationale:** consistency. Reading order is important for new contributors. Grouping by crate allows spotting unwanted dependencies easier. +**Rationale:** +- Reading order is important for new contributors +- Grouping by crate allows spotting unwanted dependencies easier +- Consistency ## Import Style @@ -252,15 +259,19 @@ impl std::fmt::Display for RenameError { } ``` -**Rationale:** overall, less typing. Makes it clear that a trait is implemented, rather than used. +**Rationale:** +- Makes it clear that a trait is implemented, rather than used +- Less typing -Prefer `use crate::foo::bar` to `use super::bar` or `use self::bar::baz`. **Rationale:** consistency, this is the style which works in all cases. +Prefer `use crate::foo::bar` to `use super::bar` or `use self::bar::baz`. **Rationale:** +- Works in all cases +- Consistency ## Order of Items Optimize for the reader who sees the file for the first time, and wants to get a general idea about what's going on. People read things from top to bottom, so place most important things first. -Specifically, if all items except one are private, always put the non-private item on top. +Specifically, if all items except one are private, always put the non-private item on top: ```rust // GOOD pub(crate) fn frobnicate() { @@ -293,7 +304,7 @@ impl Helper { If there's a mixture of private and public items, put public items first. -Put structs and enums first, functions and impls last. Order type declarations in top-down manner. +Put structs and enums first, functions and impls last. Order type declarations in a top-down manner: ```rust // GOOD @@ -323,11 +334,13 @@ impl Parent { } ``` -**Rationale:** easier to get the sense of the API by visually scanning the file. If function bodies are folded in the editor, the source code should read as documentation for the public API. +**Rationale:** +- Easier to get a sense of the API by visually scanning the file +- If function bodies are folded in the editor, the source code should be read as documentation for the public API ## Early Returns -Do use early returns +Do use early returns: ```rust // GOOD @@ -353,7 +366,7 @@ fn foo() -> Option { ## If-let -Avoid if let ... { } else { } construct, use match instead. +Avoid the `if let ... { } else { }` construct, use `match` instead: ```rust // GOOD @@ -370,7 +383,9 @@ if let Some(expected_type) = ctx.expected_type.as_ref() { } ``` -**Rationale:** `match is almost always more compact. The `else` branch can get a more precise pattern: `None` or `Err(_)` instead of `_`. +**Rationale:** +- `match` is almost always more compact +- The `else` branch can get a more precise pattern: `None` or `Err(_)` instead of `_` ## Empty Match Arms From a137b41ab27eed2db64737f099f606cb829fceb3 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Mon, 3 Oct 2022 17:07:38 +0600 Subject: [PATCH 36/66] Use same identifiers across the examples --- examples/admin.rs | 4 ++-- examples/buttons.rs | 14 +++++++++----- examples/command.rs | 17 ++++++----------- examples/dialogue.rs | 4 ++-- examples/inline.rs | 8 ++++---- examples/throw_dice.rs | 4 ++-- 6 files changed, 25 insertions(+), 26 deletions(-) diff --git a/examples/admin.rs b/examples/admin.rs index a9d800e65..0a810d318 100644 --- a/examples/admin.rs +++ b/examples/admin.rs @@ -63,8 +63,8 @@ async fn main() { teloxide::commands_repl(bot, action, Command::ty()).await; } -async fn action(bot: Bot, msg: Message, command: Command) -> ResponseResult<()> { - match command { +async fn action(bot: Bot, msg: Message, cmd: Command) -> ResponseResult<()> { + match cmd { Command::Help => { bot.send_message(msg.chat.id, Command::descriptions().to_string()).await?; } diff --git a/examples/buttons.rs b/examples/buttons.rs index f3bf3cba3..405441974 100644 --- a/examples/buttons.rs +++ b/examples/buttons.rs @@ -58,21 +58,25 @@ fn make_keyboard() -> InlineKeyboardMarkup { /// 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(m: Message, bot: Bot, me: Me) -> Result<(), Box> { - if let Some(text) = m.text() { +async fn message_handler( + msg: Message, + bot: Bot, + me: Me, +) -> Result<(), Box> { + if let Some(text) = msg.text() { match BotCommands::parse(text, me.username()) { Ok(Command::Help) => { // Just send the description of all commands. - bot.send_message(m.chat.id, Command::descriptions().to_string()).await?; + bot.send_message(msg.chat.id, Command::descriptions().to_string()).await?; } Ok(Command::Start) => { // Create a list of buttons and send them. let keyboard = make_keyboard(); - bot.send_message(m.chat.id, "Debian versions:").reply_markup(keyboard).await?; + bot.send_message(msg.chat.id, "Debian versions:").reply_markup(keyboard).await?; } Err(_) => { - bot.send_message(m.chat.id, "Command not found!").await?; + bot.send_message(msg.chat.id, "Command not found!").await?; } } } diff --git a/examples/command.rs b/examples/command.rs index d0f671eca..901d3866b 100644 --- a/examples/command.rs +++ b/examples/command.rs @@ -21,20 +21,15 @@ enum Command { UsernameAndAge { username: String, age: u8 }, } -async fn answer(bot: Bot, message: Message, command: Command) -> ResponseResult<()> { - match command { - Command::Help => { - bot.send_message(message.chat.id, Command::descriptions().to_string()).await? - } +async fn answer(bot: Bot, msg: Message, cmd: Command) -> ResponseResult<()> { + match cmd { + Command::Help => bot.send_message(msg.chat.id, Command::descriptions().to_string()).await?, Command::Username(username) => { - bot.send_message(message.chat.id, format!("Your username is @{username}.")).await? + bot.send_message(msg.chat.id, format!("Your username is @{username}.")).await? } Command::UsernameAndAge { username, age } => { - bot.send_message( - message.chat.id, - format!("Your username is @{username} and age is {age}."), - ) - .await? + bot.send_message(msg.chat.id, format!("Your username is @{username} and age is {age}.")) + .await? } }; diff --git a/examples/dialogue.rs b/examples/dialogue.rs index 5fc54b3b9..a61803491 100644 --- a/examples/dialogue.rs +++ b/examples/dialogue.rs @@ -104,8 +104,8 @@ async fn receive_location( ) -> HandlerResult { match msg.text() { Some(location) => { - let message = format!("Full name: {full_name}\nAge: {age}\nLocation: {location}"); - bot.send_message(msg.chat.id, message).await?; + let report = format!("Full name: {full_name}\nAge: {age}\nLocation: {location}"); + bot.send_message(msg.chat.id, report).await?; dialogue.exit().await?; } None => { diff --git a/examples/inline.rs b/examples/inline.rs index f9536b029..819c7336f 100644 --- a/examples/inline.rs +++ b/examples/inline.rs @@ -14,7 +14,7 @@ async fn main() { let bot = Bot::from_env(); let handler = Update::filter_inline_query().branch(dptree::endpoint( - |query: InlineQuery, bot: Bot| async move { + |q: InlineQuery, bot: Bot| async move { // First, create your actual response let google_search = InlineQueryResultArticle::new( // Each item needs a unique ID, as well as the response container for the @@ -26,7 +26,7 @@ async fn main() { // What message will be sent when clicked/tapped InputMessageContent::Text(InputMessageContentText::new(format!( "https://www.google.com/search?q={}", - query.query, + q.query, ))), ); // While constructing them from the struct itself is possible, it is preferred @@ -38,7 +38,7 @@ async fn main() { "DuckDuckGo Search".to_string(), InputMessageContent::Text(InputMessageContentText::new(format!( "https://duckduckgo.com/?q={}", - query.query + q.query ))), ) .description("DuckDuckGo Search") @@ -52,7 +52,7 @@ async fn main() { // Send it off! One thing to note -- the ID we use here must be of the query // we're responding to. - let response = bot.answer_inline_query(&query.id, results).send().await; + let response = bot.answer_inline_query(&q.id, results).send().await; if let Err(err) = response { log::error!("Error in handler: {:?}", err); } diff --git a/examples/throw_dice.rs b/examples/throw_dice.rs index 0de45aecc..b0503e196 100644 --- a/examples/throw_dice.rs +++ b/examples/throw_dice.rs @@ -9,8 +9,8 @@ async fn main() { let bot = Bot::from_env(); - teloxide::repl(bot, |message: Message, bot: Bot| async move { - bot.send_dice(message.chat.id).await?; + teloxide::repl(bot, |msg: Message, bot: Bot| async move { + bot.send_dice(msg.chat.id).await?; Ok(()) }) .await; From 5922984f6c69d600d15556023904a82afdc36229 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Mon, 3 Oct 2022 17:54:06 +0600 Subject: [PATCH 37/66] Reorder parameters in the examples In reordering the parameters, I stick the following principle: place parameters from least changing to most changing. Thus, we have config and bot right from the beginning, next a dialogue with a possible payload, and next updates such as messages, inline queries, etc. This principle is used in languages with a native support for currying, although in Rust people appear to order parameters arbitrarily, so this commit is mostly for the sake of consistency. --- README.md | 31 +++++++++++++------------------ examples/buttons.rs | 6 +++--- examples/db_remember.rs | 6 +++--- examples/dialogue.rs | 8 ++++---- examples/dispatching_features.rs | 10 +++++----- examples/heroku_ping_pong.rs | 2 +- examples/inline.rs | 2 +- examples/ngrok_ping_pong.rs | 2 +- examples/purchase.rs | 10 +++++----- examples/shared_state.rs | 2 +- examples/throw_dice.rs | 2 +- src/dispatching.rs | 10 +++++----- src/dispatching/dialogue.rs | 4 ++-- src/lib.rs | 4 ++-- 14 files changed, 47 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index ed8251ac1..20ac1f975 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ async fn main() { let bot = Bot::from_env(); - teloxide::repl(bot, |message: Message, bot: Bot| async move { + teloxide::repl(bot, |bot: Bot, msg: Message| async move { bot.send_dice(message.chat.id).await?; Ok(()) }) @@ -143,20 +143,15 @@ enum Command { UsernameAndAge { username: String, age: u8 }, } -async fn answer(bot: Bot, message: Message, command: Command) -> ResponseResult<()> { - match command { - Command::Help => { - bot.send_message(message.chat.id, Command::descriptions().to_string()).await? - } +async fn answer(bot: Bot, msg: Message, cmd: Command) -> ResponseResult<()> { + match cmd { + Command::Help => bot.send_message(msg.chat.id, Command::descriptions().to_string()).await?, Command::Username(username) => { - bot.send_message(message.chat.id, format!("Your username is @{username}.")).await? + bot.send_message(msg.chat.id, format!("Your username is @{username}.")).await? } Command::UsernameAndAge { username, age } => { - bot.send_message( - message.chat.id, - format!("Your username is @{username} and age is {age}."), - ) - .await? + bot.send_message(msg.chat.id, format!("Your username is @{username} and age is {age}.")) + .await? } }; @@ -223,13 +218,13 @@ async fn main() { .await; } -async fn start(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { +async fn start(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult { bot.send_message(msg.chat.id, "Let's start! What's your full name?").await?; dialogue.update(State::ReceiveFullName).await?; Ok(()) } -async fn receive_full_name(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { +async fn receive_full_name(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult { match msg.text() { Some(text) => { bot.send_message(msg.chat.id, "How old are you?").await?; @@ -245,9 +240,9 @@ async fn receive_full_name(bot: Bot, msg: Message, dialogue: MyDialogue) -> Hand async fn receive_age( bot: Bot, - msg: Message, dialogue: MyDialogue, full_name: String, // Available from `State::ReceiveAge`. + msg: Message, ) -> HandlerResult { match msg.text().map(|text| text.parse::()) { Some(Ok(age)) => { @@ -264,14 +259,14 @@ async fn receive_age( async fn receive_location( bot: Bot, - msg: Message, dialogue: MyDialogue, (full_name, age): (String, u8), // Available from `State::ReceiveLocation`. + msg: Message, ) -> HandlerResult { match msg.text() { Some(location) => { - let message = format!("Full name: {full_name}\nAge: {age}\nLocation: {location}"); - bot.send_message(msg.chat.id, message).await?; + let report = format!("Full name: {full_name}\nAge: {age}\nLocation: {location}"); + bot.send_message(msg.chat.id, report).await?; dialogue.exit().await?; } None => { diff --git a/examples/buttons.rs b/examples/buttons.rs index 405441974..84b12ae1a 100644 --- a/examples/buttons.rs +++ b/examples/buttons.rs @@ -59,8 +59,8 @@ fn make_keyboard() -> InlineKeyboardMarkup { /// or not, then match the command. If the command is `/start` it writes a /// markup with the `InlineKeyboardMarkup`. async fn message_handler( - msg: Message, bot: Bot, + msg: Message, me: Me, ) -> Result<(), Box> { if let Some(text) = msg.text() { @@ -85,8 +85,8 @@ async fn message_handler( } async fn inline_query_handler( - q: InlineQuery, bot: Bot, + q: InlineQuery, ) -> Result<(), Box> { let choose_debian_version = InlineQueryResultArticle::new( "0", @@ -105,7 +105,7 @@ async fn inline_query_handler( /// /// **IMPORTANT**: do not send privacy-sensitive data this way!!! /// Anyone can read data stored in the callback button. -async fn callback_handler(q: CallbackQuery, bot: Bot) -> Result<(), Box> { +async fn callback_handler(bot: Bot, q: CallbackQuery) -> Result<(), Box> { if let Some(version) = q.data { let text = format!("You chose: {version}"); diff --git a/examples/db_remember.rs b/examples/db_remember.rs index f1e5e4c9d..c102cbe20 100644 --- a/examples/db_remember.rs +++ b/examples/db_remember.rs @@ -60,7 +60,7 @@ async fn main() { .await; } -async fn start(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { +async fn start(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult { match msg.text().map(|text| text.parse::()) { Some(Ok(n)) => { dialogue.update(State::GotNumber(n)).await?; @@ -80,9 +80,9 @@ async fn start(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { async fn got_number( bot: Bot, - msg: Message, dialogue: MyDialogue, - num: i32, + num: i32, // Available from `State::GotNumber`. + msg: Message, cmd: Command, ) -> HandlerResult { match cmd { diff --git a/examples/dialogue.rs b/examples/dialogue.rs index a61803491..72ea6b2e0 100644 --- a/examples/dialogue.rs +++ b/examples/dialogue.rs @@ -57,13 +57,13 @@ async fn main() { .await; } -async fn start(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { +async fn start(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult { bot.send_message(msg.chat.id, "Let's start! What's your full name?").await?; dialogue.update(State::ReceiveFullName).await?; Ok(()) } -async fn receive_full_name(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { +async fn receive_full_name(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult { match msg.text() { Some(text) => { bot.send_message(msg.chat.id, "How old are you?").await?; @@ -79,9 +79,9 @@ async fn receive_full_name(bot: Bot, msg: Message, dialogue: MyDialogue) -> Hand async fn receive_age( bot: Bot, - msg: Message, dialogue: MyDialogue, full_name: String, // Available from `State::ReceiveAge`. + msg: Message, ) -> HandlerResult { match msg.text().map(|text| text.parse::()) { Some(Ok(age)) => { @@ -98,9 +98,9 @@ async fn receive_age( async fn receive_location( bot: Bot, - msg: Message, dialogue: MyDialogue, (full_name, age): (String, u8), // Available from `State::ReceiveLocation`. + msg: Message, ) -> HandlerResult { match msg.text() { Some(location) => { diff --git a/examples/dispatching_features.rs b/examples/dispatching_features.rs index 1f34dc1d3..e8ed5e162 100644 --- a/examples/dispatching_features.rs +++ b/examples/dispatching_features.rs @@ -33,7 +33,7 @@ async fn main() { ) .branch( // Filter a maintainer by a used ID. - dptree::filter(|msg: Message, cfg: ConfigParameters| { + dptree::filter(|cfg: ConfigParameters, msg: Message| { msg.from().map(|user| user.id == cfg.bot_maintainer).unwrap_or_default() }) .filter_command::() @@ -62,7 +62,7 @@ async fn main() { .branch( // There are some extension filtering functions on `Message`. The following filter will // filter only messages with dices. - Message::filter_dice().endpoint(|msg: Message, dice: Dice, bot: Bot| async move { + Message::filter_dice().endpoint(|bot: Bot, msg: Message, dice: Dice| async move { bot.send_message(msg.chat.id, format!("Dice value: {}", dice.value)) .reply_to_message_id(msg.id) .await?; @@ -114,11 +114,11 @@ enum MaintainerCommands { } async fn simple_commands_handler( - msg: Message, - bot: Bot, - cmd: SimpleCommand, cfg: ConfigParameters, + bot: Bot, me: teloxide::types::Me, + msg: Message, + cmd: SimpleCommand, ) -> Result<(), teloxide::RequestError> { let text = match cmd { SimpleCommand::Help => { diff --git a/examples/heroku_ping_pong.rs b/examples/heroku_ping_pong.rs index 1c104eb83..6fe508473 100644 --- a/examples/heroku_ping_pong.rs +++ b/examples/heroku_ping_pong.rs @@ -47,7 +47,7 @@ async fn main() { teloxide::repl_with_listener( bot, - |msg: Message, bot: Bot| async move { + |bot: Bot, msg: Message| async move { bot.send_message(msg.chat.id, "pong").await?; Ok(()) }, diff --git a/examples/inline.rs b/examples/inline.rs index 819c7336f..444e78589 100644 --- a/examples/inline.rs +++ b/examples/inline.rs @@ -14,7 +14,7 @@ async fn main() { let bot = Bot::from_env(); let handler = Update::filter_inline_query().branch(dptree::endpoint( - |q: InlineQuery, bot: Bot| async move { + |bot: Bot, q: InlineQuery| async move { // First, create your actual response let google_search = InlineQueryResultArticle::new( // Each item needs a unique ID, as well as the response container for the diff --git a/examples/ngrok_ping_pong.rs b/examples/ngrok_ping_pong.rs index 214868109..3ea157ed0 100644 --- a/examples/ngrok_ping_pong.rs +++ b/examples/ngrok_ping_pong.rs @@ -18,7 +18,7 @@ async fn main() { teloxide::repl_with_listener( bot, - |msg: Message, bot: Bot| async move { + |bot: Bot, msg: Message| async move { bot.send_message(msg.chat.id, "pong").await?; Ok(()) }, diff --git a/examples/purchase.rs b/examples/purchase.rs index 79b4e81cc..89ec56541 100644 --- a/examples/purchase.rs +++ b/examples/purchase.rs @@ -83,7 +83,7 @@ fn schema() -> UpdateHandler> .branch(callback_query_handler) } -async fn start(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { +async fn start(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult { bot.send_message(msg.chat.id, "Let's start! What's your full name?").await?; dialogue.update(State::ReceiveFullName).await?; Ok(()) @@ -94,7 +94,7 @@ async fn help(bot: Bot, msg: Message) -> HandlerResult { Ok(()) } -async fn cancel(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { +async fn cancel(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult { bot.send_message(msg.chat.id, "Cancelling the dialogue.").await?; dialogue.exit().await?; Ok(()) @@ -106,7 +106,7 @@ async fn invalid_state(bot: Bot, msg: Message) -> HandlerResult { Ok(()) } -async fn receive_full_name(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { +async fn receive_full_name(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult { match msg.text().map(ToOwned::to_owned) { Some(full_name) => { let products = ["Apple", "Banana", "Orange", "Potato"] @@ -127,9 +127,9 @@ async fn receive_full_name(bot: Bot, msg: Message, dialogue: MyDialogue) -> Hand async fn receive_product_selection( bot: Bot, - q: CallbackQuery, dialogue: MyDialogue, - full_name: String, + full_name: String, // Available from `State::ReceiveProductChoice`. + q: CallbackQuery, ) -> HandlerResult { if let Some(product) = &q.data { bot.send_message( diff --git a/examples/shared_state.rs b/examples/shared_state.rs index 4caf9995f..bb8ef8faa 100644 --- a/examples/shared_state.rs +++ b/examples/shared_state.rs @@ -16,7 +16,7 @@ async fn main() { let messages_total = Arc::new(AtomicU64::new(0)); let handler = Update::filter_message().endpoint( - |msg: Message, bot: Bot, messages_total: Arc| async move { + |bot: Bot, messages_total: Arc, msg: Message| async move { let previous = messages_total.fetch_add(1, Ordering::Relaxed); bot.send_message(msg.chat.id, format!("I received {previous} messages in total.")) .await?; diff --git a/examples/throw_dice.rs b/examples/throw_dice.rs index b0503e196..71f850a4b 100644 --- a/examples/throw_dice.rs +++ b/examples/throw_dice.rs @@ -9,7 +9,7 @@ async fn main() { let bot = Bot::from_env(); - teloxide::repl(bot, |msg: Message, bot: Bot| async move { + teloxide::repl(bot, |bot: Bot, msg: Message| async move { bot.send_dice(msg.chat.id).await?; Ok(()) }) diff --git a/src/dispatching.rs b/src/dispatching.rs index 23a03268c..c19b2b932 100644 --- a/src/dispatching.rs +++ b/src/dispatching.rs @@ -113,26 +113,26 @@ //! type MyDialogue = Dialogue>; //! type HandlerResult = Result<(), Box>; //! -//! async fn start(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { +//! async fn start(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult { //! todo!() //! } //! async fn help(bot: Bot, msg: Message) -> HandlerResult { //! todo!() //! } -//! async fn cancel(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { +//! async fn cancel(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult { //! todo!() //! } //! async fn invalid_state(bot: Bot, msg: Message) -> HandlerResult { //! todo!() //! } -//! async fn receive_full_name(bot: Bot, msg: Message, dialogue: MyDialogue) -> HandlerResult { +//! async fn receive_full_name(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult { //! todo!() //! } //! async fn receive_product_selection( //! bot: Bot, -//! q: CallbackQuery, //! dialogue: MyDialogue, -//! full_name: String, +//! full_name: String, // Available from `State::ReceiveProductChoice`. +//! q: CallbackQuery, //! ) -> HandlerResult { //! todo!() //! } diff --git a/src/dispatching/dialogue.rs b/src/dispatching/dialogue.rs index 6f9aa824e..c8d27896a 100644 --- a/src/dispatching/dialogue.rs +++ b/src/dispatching/dialogue.rs @@ -39,9 +39,9 @@ //! # #[derive(Clone, Debug)] enum State { ReceiveLocation { full_name: String, age: u8 } } //! async fn receive_age( //! bot: Bot, -//! msg: Message, //! dialogue: MyDialogue, //! full_name: String, // Available from `State::ReceiveAge`. +//! msg: Message, //! ) -> HandlerResult { //! match msg.text().map(|text| text.parse::()) { //! Some(Ok(age)) => { @@ -71,9 +71,9 @@ //! # #[derive(Clone, Debug)] enum State {} //! async fn receive_location( //! bot: Bot, -//! msg: Message, //! dialogue: MyDialogue, //! (full_name, age): (String, u8), // Available from `State::ReceiveLocation`. +//! msg: Message, //! ) -> HandlerResult { //! match msg.text() { //! Some(location) => { diff --git a/src/lib.rs b/src/lib.rs index b262d957e..80237a9dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,8 +15,8 @@ //! //! let bot = Bot::from_env(); //! -//! teloxide::repl(bot, |message: Message, bot: Bot| async move { -//! bot.send_dice(message.chat.id).await?; +//! teloxide::repl(bot, |bot: Bot, msg: Message| async move { +//! bot.send_dice(msg.chat.id).await?; //! Ok(()) //! }) //! .await; From a3116bb0d63723d6f6bd5367de62cee30880adba Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Mon, 3 Oct 2022 18:01:24 +0600 Subject: [PATCH 38/66] Fix a code typo in `README.md` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 20ac1f975..f03246ad4 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ async fn main() { let bot = Bot::from_env(); teloxide::repl(bot, |bot: Bot, msg: Message| async move { - bot.send_dice(message.chat.id).await?; + bot.send_dice(msg.chat.id).await?; Ok(()) }) .await; From 243bfa84608d0f70838658724d59a1238e8c6a28 Mon Sep 17 00:00:00 2001 From: Waffle Maybe Date: Mon, 3 Oct 2022 20:49:25 +0400 Subject: [PATCH 39/66] Apply suggestions from code review Co-authored-by: Hirrolot --- CODE_STYLE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CODE_STYLE.md b/CODE_STYLE.md index 67c3d42e2..77803ce38 100644 --- a/CODE_STYLE.md +++ b/CODE_STYLE.md @@ -42,7 +42,7 @@ impl Trait for Wrap { ... } ## Documentation comments 1. Documentation must describe _what_ your code does and mustn't describe _how_ your code does it and bla-bla-bla. -2. Be sure that your comments follow the grammar, including punctuation, the first capital letter and so on. +2. Be sure that your comments follow the grammar, including punctuation, the first capital letter and so on: ```rust // GOOD /// This function makes a request to Telegram. @@ -52,7 +52,7 @@ impl Trait for Wrap { ... } /// this function make request to telegram pub fn make_request(url: &str) -> String { ... } ``` -3. Do not use ending punctuation in short list items (usually containing just one phrase or sentence). +3. Do not use ending punctuation in short list items (usually containing just one phrase or sentence): ```md - Handle different kinds of Update From 80a586575228b1e456fe09eab3a92315009c5e1b Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 3 Oct 2022 21:10:32 +0400 Subject: [PATCH 40/66] Start writing migration guide --- MIGRATION_GUIDE.md | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 78003f486..ec2f7a6c4 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -1,6 +1,59 @@ This document describes breaking changes of `teloxide` crate, as well as the ways to update code. Note that the list of required changes is not fully exhaustive and it may lack something in rare cases. +## 0.10 -> 0.11 + +### core + +Requests can now be `.await`ed directly, without need of `.send()` or `AutoSend`. +If you previously used `AutoSend` adaptor, you can safely remove it. + +`File`'s and `FileMeta`'s fields now don't have `file_` prefix. +If you previously accessed the fields, you'll need to change remove the prefix: + +```diff +-_ = file.file_size; ++_ = file.size; +``` + +`Animation`, `Audio`, `Document`, `PassportFile`, `PhotoSize`, `Video`, `VideoNote` and `Voice` now contain `FileMeta` instead of its fields. +Together with rename of `FileMeta`'s fields, you'll need to change `_` to `.`: + +```diff +-_ = animation.file_size; ++_ = animation.file.size; +``` + +Message id fields and parameters now use `MessageId` type, instead of `i32`. +You may need to change code accordingly: + +```diff +-let id: i32 = message.id; ++let id: MessageId = message.id; +``` +```diff,rust +let (cid, mid): (ChatId, i32) = get_message_to_delete_from_db(); +-bot.delete_message(cid, mid).await?; ++bot.delete_message(cid, MessageId(mid)).await?; +``` + +Note that at the same time `MessageId` is now a tuple struct. +If you've accessed its only field you'll need to change it too: + +```diff,rust +-let MessageId { message_id } = bot.copy_message(dst_chat, src_chat, mid).await?; ++let MessageId(message_id) = bot.copy_message(dst_chat, src_chat, mid).await?; +save_to_db(message_id); +``` + +Because of API updates `Sticker` type was refactored again. +You may need to change code accordingly. +See `Sticker` documentation for more information about the new structure. + +### teloxide + + + ## 0.9 -> 0.10 ### core From c4dbff1afe349d3eec11ab7e05ab660e9ee97482 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Tue, 4 Oct 2022 09:56:51 +0600 Subject: [PATCH 41/66] Enhance the v0.11 migration guide --- MIGRATION_GUIDE.md | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index ec2f7a6c4..c0e3566c3 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -6,7 +6,17 @@ Note that the list of required changes is not fully exhaustive and it may lack s ### core Requests can now be `.await`ed directly, without need of `.send()` or `AutoSend`. -If you previously used `AutoSend` adaptor, you can safely remove it. +If you previously used `AutoSend` adaptor, you can safely remove it: + +```diff,rust +-let bot = Bot::from_env().auto_send(); ++let bot = Bot::from_env(); +``` + +```diff,rust +-async fn start(bot: AutoSend, dialogue: MyDialogue, msg: Message) -> HandlerResult { ++async fn start(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult { +``` `File`'s and `FileMeta`'s fields now don't have `file_` prefix. If you previously accessed the fields, you'll need to change remove the prefix: @@ -52,7 +62,18 @@ See `Sticker` documentation for more information about the new structure. ### teloxide - +You can now write `Ok(())` instead of `respond(())` at the end of closures provided to RELPs: + +```diff,rust +teloxide::repl(bot, |bot: Bot, msg: Message| async move { + bot.send_dice(msg.chat.id).await?; +- respond(()) ++ Ok(()) +}) +.await; +``` + +This is because REPLs now require the closure to return `RequestError` instead of a generic error type, so type inference works perfectly for a return value. If you use something other than `RequestError`, you can transfer your code to `teloxide::dispatching`, which still permits a generic error type. ## 0.9 -> 0.10 From bc609ba9b3232467bd519592843adaa66626216b Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Tue, 4 Oct 2022 11:13:03 +0600 Subject: [PATCH 42/66] Update the docs of `BotCommands` --- src/utils/command.rs | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/utils/command.rs b/src/utils/command.rs index d84efc0c2..49dec1986 100644 --- a/src/utils/command.rs +++ b/src/utils/command.rs @@ -82,11 +82,10 @@ pub use teloxide_macros::BotCommands; /// ``` /// /// # Enum attributes -/// 1. `#[command(rename = "rule")]` -/// Rename all commands by `rule`. If you will not use this attribute, commands -/// will be parsed by their original names. Allowed rules are `lowercase`, -/// `UPPERCASE`, `PascalCase`, `camelCase`, `snake_case`, -/// `SCREAMING_SNAKE_CASE`, `kebab-case`, and `SCREAMING-KEBAB-CASE`. +/// 1. `#[command(rename_rule = "rule")]` +/// Rename all commands by `rule`. Allowed rules are `lowercase`, `UPPERCASE`, +/// `PascalCase`, `camelCase`, `snake_case`, `SCREAMING_SNAKE_CASE`, +/// `kebab-case`, and `SCREAMING-KEBAB-CASE`. /// /// 2. `#[command(prefix = "prefix")]` /// Change a prefix for all commands (the default is `/`). @@ -159,21 +158,23 @@ pub use teloxide_macros::BotCommands; /// # Variant attributes /// All variant attributes override the corresponding `enum` attributes. /// -/// 1. `#[command(rename = "rule")]` +/// 1. `#[command(rename_rule = "rule")]` /// Rename one command by a rule. Allowed rules are `lowercase`, `UPPERCASE`, /// `PascalCase`, `camelCase`, `snake_case`, `SCREAMING_SNAKE_CASE`, -/// `kebab-case`, `SCREAMING-KEBAB-CASE`, and `%some_name%`, where `%some_name%` -/// is any string, a new name. +/// `kebab-case`, `SCREAMING-KEBAB-CASE`. +/// +/// 2. `#[command(rename = "name")]` +/// Rename one command to `name` (literal renaming; do not confuse with +/// `rename_rule`). /// -/// 2. `#[command(description = "description")]` +/// 3. `#[command(description = "description")]` /// Give your command a description. Write `"off"` for `"description"` to hide a /// command. /// -/// 3. `#[command(parse_with = "parser")]` -/// One more option is available for variants. -/// - `custom_parser` - your own parser of the signature `fn(String) -> -/// Result`, where `Tuple` corresponds to the variant's -/// arguments. +/// 4. `#[command(parse_with = "parser")]` +/// Parse arguments of one command with a given parser. `parser` must be a +/// function of the signature `fn(String) -> Result`, where +/// `Tuple` corresponds to the variant's arguments. /// /// ## Example /// ``` @@ -204,8 +205,8 @@ pub use teloxide_macros::BotCommands; /// # } /// ``` /// -/// 4. `#[command(prefix = "prefix")]` -/// 5. `#[command(separator = "sep")]` +/// 5. `#[command(prefix = "prefix")]` +/// 6. `#[command(separator = "sep")]` /// /// These attributes just override the corresponding `enum` attributes for a /// specific variant. From 8dfb5268bec63a3cebd67a7e27c5cd18432f6e97 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 5 Oct 2022 09:52:00 +0400 Subject: [PATCH 43/66] Make `BotCommands::parse` accept bot username as `&str` --- src/dispatching/handler_ext.rs | 2 +- src/utils/command.rs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/dispatching/handler_ext.rs b/src/dispatching/handler_ext.rs index f73dfb0a2..932d5079f 100644 --- a/src/dispatching/handler_ext.rs +++ b/src/dispatching/handler_ext.rs @@ -90,6 +90,6 @@ where { dptree::entry().chain(dptree::filter_map(move |message: Message, me: Me| { let bot_name = me.user.username.expect("Bots must have a username"); - message.text().and_then(|text| C::parse(text, bot_name).ok()) + message.text().and_then(|text| C::parse(text, &bot_name).ok()) })) } diff --git a/src/utils/command.rs b/src/utils/command.rs index 49dec1986..c0f9cd2cf 100644 --- a/src/utils/command.rs +++ b/src/utils/command.rs @@ -218,9 +218,7 @@ pub trait BotCommands: Sized { /// /// `bot_username` is required to parse commands like /// `/cmd@username_of_the_bot`. - fn parse(s: &str, bot_username: N) -> Result - where - N: Into; + fn parse(s: &str, bot_username: &str) -> Result; /// Returns descriptions of the commands suitable to be shown to the user /// (for example when `/help` command is used). From 6ded398d5d306ca79b28fed481e44852cd30c12b Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 5 Oct 2022 09:59:21 +0400 Subject: [PATCH 44/66] Update `teloxide-macros` --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 02aaf40ef..5295cccdf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,7 +67,7 @@ dptree = "0.3.0" # These lines are used only for development. teloxide-core = { git = "https://github.com/teloxide/teloxide-core", rev = "00165e6", default-features = false } -teloxide-macros = { git = "https://github.com/teloxide/teloxide-macros", rev = "0e79c37", optional = true } +teloxide-macros = { git = "https://github.com/teloxide/teloxide-macros", rev = "a2a79e29cb106a5d3052cf32a23e698db859cc2a", optional = true } # dptree = { git = "https://github.com/teloxide/dptree", rev = "df578e4" } tokio = { version = "1.8", features = ["fs"] } From 107d2c8b99c7c0911c485909da47cc0372d7d153 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 5 Oct 2022 10:00:04 +0400 Subject: [PATCH 45/66] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1eacc0152..3f83a4fbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `CommandDescriptions::{new, global_description, username, username_from_me}`. - `teloxide::filter_command`. - `teloxide::dispatching::dialogue::enter`. +- `BotCommands::parse` now accept `bot_username` as `&str` ### Added From dc652da1ac03d464262d0d7b39509ac5a00fde57 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 5 Oct 2022 10:08:50 +0400 Subject: [PATCH 46/66] fixup tests and examples that use `derive(BotCommands)` --- README.md | 2 +- examples/admin.rs | 2 +- examples/buttons.rs | 2 +- examples/command.rs | 2 +- examples/db_remember.rs | 2 +- examples/dispatching_features.rs | 4 +- examples/purchase.rs | 2 +- src/dispatching.rs | 2 +- src/dispatching/handler_description.rs | 2 +- src/utils/command.rs | 12 +++--- tests/command.rs | 51 ++++++++++++-------------- 11 files changed, 39 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index f03246ad4..ab74b6d74 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ async fn main() { } #[derive(BotCommands, Clone)] -#[command(rename = "lowercase", description = "These commands are supported:")] +#[command(rename_rule = "lowercase", description = "These commands are supported:")] enum Command { #[command(description = "display this text.")] Help, diff --git a/examples/admin.rs b/examples/admin.rs index 0a810d318..4be9a5430 100644 --- a/examples/admin.rs +++ b/examples/admin.rs @@ -14,7 +14,7 @@ use teloxide::{prelude::*, types::ChatPermissions, utils::command::BotCommands}; // %PREFIX%%COMMAND% - %DESCRIPTION% #[derive(BotCommands, Clone)] #[command( - rename = "lowercase", + rename_rule = "lowercase", description = "Use commands in format /%command% %num% %unit%", parse_with = "split" )] diff --git a/examples/buttons.rs b/examples/buttons.rs index 84b12ae1a..ffb9846b0 100644 --- a/examples/buttons.rs +++ b/examples/buttons.rs @@ -10,7 +10,7 @@ use teloxide::{ }; #[derive(BotCommands)] -#[command(rename = "lowercase", description = "These commands are supported:")] +#[command(rename_rule = "lowercase", description = "These commands are supported:")] enum Command { #[command(description = "Display this text")] Help, diff --git a/examples/command.rs b/examples/command.rs index 901d3866b..00f443154 100644 --- a/examples/command.rs +++ b/examples/command.rs @@ -11,7 +11,7 @@ async fn main() { } #[derive(BotCommands, Clone)] -#[command(rename = "lowercase", description = "These commands are supported:")] +#[command(rename_rule = "lowercase", description = "These commands are supported:")] enum Command { #[command(description = "display this text.")] Help, diff --git a/examples/db_remember.rs b/examples/db_remember.rs index c102cbe20..de09db0d6 100644 --- a/examples/db_remember.rs +++ b/examples/db_remember.rs @@ -22,7 +22,7 @@ pub enum State { } #[derive(Clone, BotCommands)] -#[command(rename = "lowercase", description = "These commands are supported:")] +#[command(rename_rule = "lowercase", description = "These commands are supported:")] pub enum Command { #[command(description = "get your number.")] Get, diff --git a/examples/dispatching_features.rs b/examples/dispatching_features.rs index e8ed5e162..983f00027 100644 --- a/examples/dispatching_features.rs +++ b/examples/dispatching_features.rs @@ -96,7 +96,7 @@ struct ConfigParameters { } #[derive(BotCommands, Clone)] -#[command(rename = "lowercase", description = "Simple commands")] +#[command(rename_rule = "lowercase", description = "Simple commands")] enum SimpleCommand { #[command(description = "shows this message.")] Help, @@ -107,7 +107,7 @@ enum SimpleCommand { } #[derive(BotCommands, Clone)] -#[command(rename = "lowercase", description = "Maintainer commands")] +#[command(rename_rule = "lowercase", description = "Maintainer commands")] enum MaintainerCommands { #[command(parse_with = "split", description = "generate a number within range")] Rand { from: u64, to: u64 }, diff --git a/examples/purchase.rs b/examples/purchase.rs index 89ec56541..daf27cddc 100644 --- a/examples/purchase.rs +++ b/examples/purchase.rs @@ -33,7 +33,7 @@ pub enum State { } #[derive(BotCommands, Clone)] -#[command(rename = "lowercase", description = "These commands are supported:")] +#[command(rename_rule = "lowercase", description = "These commands are supported:")] enum Command { #[command(description = "display this text.")] Help, diff --git a/src/dispatching.rs b/src/dispatching.rs index c19b2b932..f22c99c1a 100644 --- a/src/dispatching.rs +++ b/src/dispatching.rs @@ -25,7 +25,7 @@ //! ```no_run //! # use teloxide::utils::command::BotCommands; //! #[derive(BotCommands, Clone)] -//! #[command(rename = "lowercase", description = "These commands are supported:")] +//! #[command(rename_rule = "lowercase", description = "These commands are supported:")] //! enum Command { //! #[command(description = "display this text.")] //! Help, diff --git a/src/dispatching/handler_description.rs b/src/dispatching/handler_description.rs index 3ed2f6219..92bbda38c 100644 --- a/src/dispatching/handler_description.rs +++ b/src/dispatching/handler_description.rs @@ -86,7 +86,7 @@ mod tests { use crate as teloxide; // fixup for the `BotCommands` macro #[derive(BotCommands, Clone)] - #[command(rename = "lowercase")] + #[command(rename_rule = "lowercase")] enum Cmd { B, } diff --git a/src/utils/command.rs b/src/utils/command.rs index c0f9cd2cf..57b9d5ffa 100644 --- a/src/utils/command.rs +++ b/src/utils/command.rs @@ -13,7 +13,7 @@ //! type UnitOfTime = u8; //! //! #[derive(BotCommands, PartialEq, Debug)] -//! #[command(rename = "lowercase", parse_with = "split")] +//! #[command(rename_rule = "lowercase", parse_with = "split")] //! enum AdminCommand { //! Mute(UnitOfTime, char), //! Ban(UnitOfTime, char), @@ -70,7 +70,7 @@ pub use teloxide_macros::BotCommands; /// type UnitOfTime = u8; /// /// #[derive(BotCommands, PartialEq, Debug)] -/// #[command(rename = "lowercase", parse_with = "split")] +/// #[command(rename_rule = "lowercase", parse_with = "split")] /// enum AdminCommand { /// Mute(UnitOfTime, char), /// Ban(UnitOfTime, char), @@ -105,7 +105,7 @@ pub use teloxide_macros::BotCommands; /// use teloxide::utils::command::BotCommands; /// /// #[derive(BotCommands, PartialEq, Debug)] -/// #[command(rename = "lowercase")] +/// #[command(rename_rule = "lowercase")] /// enum Command { /// Text(String), /// } @@ -125,7 +125,7 @@ pub use teloxide_macros::BotCommands; /// use teloxide::utils::command::BotCommands; /// /// #[derive(BotCommands, PartialEq, Debug)] -/// #[command(rename = "lowercase", parse_with = "split")] +/// #[command(rename_rule = "lowercase", parse_with = "split")] /// enum Command { /// Nums(u8, u16, i32), /// } @@ -145,7 +145,7 @@ pub use teloxide_macros::BotCommands; /// use teloxide::utils::command::BotCommands; /// /// #[derive(BotCommands, PartialEq, Debug)] -/// #[command(rename = "lowercase", parse_with = "split", separator = "|")] +/// #[command(rename_rule = "lowercase", parse_with = "split", separator = "|")] /// enum Command { /// Nums(u8, u16, i32), /// } @@ -192,7 +192,7 @@ pub use teloxide_macros::BotCommands; /// } /// /// #[derive(BotCommands, PartialEq, Debug)] -/// #[command(rename = "lowercase")] +/// #[command(rename_rule = "lowercase")] /// enum Command { /// #[command(parse_with = "accept_two_digits")] /// Num(u8), diff --git a/tests/command.rs b/tests/command.rs index d3322ea4a..a42fca6bb 100644 --- a/tests/command.rs +++ b/tests/command.rs @@ -11,7 +11,7 @@ use teloxide::utils::command::BotCommands; #[cfg(feature = "macros")] fn parse_command_with_args() { #[derive(BotCommands, Debug, PartialEq)] - #[command(rename = "lowercase")] + #[command(rename_rule = "lowercase")] enum DefaultCommands { Start(String), Help, @@ -27,7 +27,7 @@ fn parse_command_with_args() { #[cfg(feature = "macros")] fn parse_command_with_non_string_arg() { #[derive(BotCommands, Debug, PartialEq)] - #[command(rename = "lowercase")] + #[command(rename_rule = "lowercase")] enum DefaultCommands { Start(i32), Help, @@ -43,7 +43,7 @@ fn parse_command_with_non_string_arg() { #[cfg(feature = "macros")] fn attribute_prefix() { #[derive(BotCommands, Debug, PartialEq)] - #[command(rename = "lowercase")] + #[command(rename_rule = "lowercase")] enum DefaultCommands { #[command(prefix = "!")] Start(String), @@ -60,7 +60,7 @@ fn attribute_prefix() { #[cfg(feature = "macros")] fn many_attributes() { #[derive(BotCommands, Debug, PartialEq)] - #[command(rename = "lowercase")] + #[command(rename_rule = "lowercase")] enum DefaultCommands { #[command(prefix = "!", description = "desc")] Start, @@ -75,7 +75,7 @@ fn many_attributes() { #[cfg(feature = "macros")] fn global_attributes() { #[derive(BotCommands, Debug, PartialEq)] - #[command(prefix = "!", rename = "lowercase", description = "Bot commands")] + #[command(prefix = "!", rename_rule = "lowercase", description = "Bot commands")] enum DefaultCommands { #[command(prefix = "/")] Start, @@ -91,7 +91,7 @@ fn global_attributes() { #[cfg(feature = "macros")] fn parse_command_with_bot_name() { #[derive(BotCommands, Debug, PartialEq)] - #[command(rename = "lowercase")] + #[command(rename_rule = "lowercase")] enum DefaultCommands { #[command(prefix = "/")] Start, @@ -108,7 +108,7 @@ fn parse_command_with_bot_name() { #[cfg(feature = "macros")] fn parse_with_split() { #[derive(BotCommands, Debug, PartialEq)] - #[command(rename = "lowercase")] + #[command(rename_rule = "lowercase")] #[command(parse_with = "split")] enum DefaultCommands { Start(u8, String), @@ -125,7 +125,7 @@ fn parse_with_split() { #[cfg(feature = "macros")] fn parse_with_split2() { #[derive(BotCommands, Debug, PartialEq)] - #[command(rename = "lowercase")] + #[command(rename_rule = "lowercase")] #[command(parse_with = "split", separator = "|")] enum DefaultCommands { Start(u8, String), @@ -159,7 +159,7 @@ fn parse_custom_parser() { use parser::custom_parse_function; #[derive(BotCommands, Debug, PartialEq)] - #[command(rename = "lowercase")] + #[command(rename_rule = "lowercase")] enum DefaultCommands { #[command(parse_with = "custom_parse_function")] Start(u8, String), @@ -185,7 +185,7 @@ fn parse_custom_parser() { #[cfg(feature = "macros")] fn parse_named_fields() { #[derive(BotCommands, Debug, PartialEq)] - #[command(rename = "lowercase")] + #[command(rename_rule = "lowercase")] #[command(parse_with = "split")] enum DefaultCommands { Start { num: u8, data: String }, @@ -202,7 +202,7 @@ fn parse_named_fields() { #[cfg(feature = "macros")] fn descriptions_off() { #[derive(BotCommands, Debug, PartialEq)] - #[command(rename = "lowercase")] + #[command(rename_rule = "lowercase")] enum DefaultCommands { #[command(description = "off")] Start, @@ -217,24 +217,24 @@ fn descriptions_off() { fn rename_rules() { #[derive(BotCommands, Debug, PartialEq)] enum DefaultCommands { - #[command(rename = "lowercase")] + #[command(rename_rule = "lowercase")] AaaAaa, - #[command(rename = "UPPERCASE")] + #[command(rename_rule = "UPPERCASE")] BbbBbb, - #[command(rename = "PascalCase")] + #[command(rename_rule = "PascalCase")] CccCcc, - #[command(rename = "camelCase")] + #[command(rename_rule = "camelCase")] DddDdd, - #[command(rename = "snake_case")] + #[command(rename_rule = "snake_case")] EeeEee, - #[command(rename = "SCREAMING_SNAKE_CASE")] + #[command(rename_rule = "SCREAMING_SNAKE_CASE")] FffFff, - #[command(rename = "kebab-case")] + #[command(rename_rule = "kebab-case")] GggGgg, - #[command(rename = "SCREAMING-KEBAB-CASE")] + #[command(rename_rule = "SCREAMING-KEBAB-CASE")] HhhHhh, - //#[command(rename = "Bar")] - //Foo, + #[command(rename = "Bar")] + Foo, } assert_eq!(DefaultCommands::AaaAaa, DefaultCommands::parse("/aaaaaa", "").unwrap()); @@ -245,15 +245,10 @@ fn rename_rules() { assert_eq!(DefaultCommands::FffFff, DefaultCommands::parse("/FFF_FFF", "").unwrap()); assert_eq!(DefaultCommands::GggGgg, DefaultCommands::parse("/ggg-ggg", "").unwrap()); assert_eq!(DefaultCommands::HhhHhh, DefaultCommands::parse("/HHH-HHH", "").unwrap()); - //assert_eq!(DefaultCommands::Foo, DefaultCommands::parse("/Bar", - // "").unwrap()); + assert_eq!(DefaultCommands::Foo, DefaultCommands::parse("/Bar", "").unwrap()); - // assert_eq!( - // "/aaaaaa\n/BBBBBB\n/CccCcc\n/dddDdd\n/eee_eee\n/FFF_FFF\n/ggg-ggg\n/ - // HHH-HHH\n/Bar", DefaultCommands::descriptions().to_string() - // ); assert_eq!( - "/aaaaaa\n/BBBBBB\n/CccCcc\n/dddDdd\n/eee_eee\n/FFF_FFF\n/ggg-ggg\n/HHH-HHH", + "/aaaaaa\n/BBBBBB\n/CccCcc\n/dddDdd\n/eee_eee\n/FFF_FFF\n/ggg-ggg\n/HHH-HHH\n/Bar", DefaultCommands::descriptions().to_string() ); } From 64359db3dad7623a847cfade700ea7971c708868 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Thu, 6 Oct 2022 22:35:32 +0600 Subject: [PATCH 47/66] Update to the latest `teloxide-macros` --- Cargo.toml | 2 +- src/utils/command.rs | 2 +- tests/command.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5295cccdf..537c56f6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,7 +67,7 @@ dptree = "0.3.0" # These lines are used only for development. teloxide-core = { git = "https://github.com/teloxide/teloxide-core", rev = "00165e6", default-features = false } -teloxide-macros = { git = "https://github.com/teloxide/teloxide-macros", rev = "a2a79e29cb106a5d3052cf32a23e698db859cc2a", optional = true } +teloxide-macros = { git = "https://github.com/teloxide/teloxide-macros", rev = "e715105", optional = true } # dptree = { git = "https://github.com/teloxide/dptree", rev = "df578e4" } tokio = { version = "1.8", features = ["fs"] } diff --git a/src/utils/command.rs b/src/utils/command.rs index 57b9d5ffa..24e001cfa 100644 --- a/src/utils/command.rs +++ b/src/utils/command.rs @@ -194,7 +194,7 @@ pub use teloxide_macros::BotCommands; /// #[derive(BotCommands, PartialEq, Debug)] /// #[command(rename_rule = "lowercase")] /// enum Command { -/// #[command(parse_with = "accept_two_digits")] +/// #[command(parse_with = accept_two_digits)] /// Num(u8), /// } /// diff --git a/tests/command.rs b/tests/command.rs index a42fca6bb..cac62e790 100644 --- a/tests/command.rs +++ b/tests/command.rs @@ -161,11 +161,11 @@ fn parse_custom_parser() { #[derive(BotCommands, Debug, PartialEq)] #[command(rename_rule = "lowercase")] enum DefaultCommands { - #[command(parse_with = "custom_parse_function")] + #[command(parse_with = custom_parse_function)] Start(u8, String), // Test . - #[command(parse_with = "parser::custom_parse_function")] + #[command(parse_with = parser::custom_parse_function)] TestPath(u8, String), Help, From 19830daf8d8371e571640d7b28cf7b85c46b14a5 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Thu, 6 Oct 2022 23:52:49 +0600 Subject: [PATCH 48/66] Update `teloxide-macros` to v0.7.0 --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 537c56f6a..72ccf372b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ full = [ [dependencies] # teloxide-core = { version = "0.7.0", default-features = false } -# teloxide-macros = { version = "0.6.3", optional = true } +teloxide-macros = { version = "0.7.0", optional = true } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } @@ -67,7 +67,7 @@ dptree = "0.3.0" # These lines are used only for development. teloxide-core = { git = "https://github.com/teloxide/teloxide-core", rev = "00165e6", default-features = false } -teloxide-macros = { git = "https://github.com/teloxide/teloxide-macros", rev = "e715105", optional = true } +# teloxide-macros = { git = "https://github.com/teloxide/teloxide-macros", rev = "e715105", optional = true } # dptree = { git = "https://github.com/teloxide/dptree", rev = "df578e4" } tokio = { version = "1.8", features = ["fs"] } From d45a962a6385209dd9b621b4bb2eb09f7ee369bb Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 6 Oct 2022 21:59:54 +0400 Subject: [PATCH 49/66] Update `teloxide-core` to `0.8.0` --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 72ccf372b..83a5ab08b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,7 @@ full = [ ] [dependencies] -# teloxide-core = { version = "0.7.0", default-features = false } +teloxide-core = { version = "0.8.0", default-features = false } teloxide-macros = { version = "0.7.0", optional = true } serde_json = "1.0" @@ -66,7 +66,7 @@ serde = { version = "1.0", features = ["derive"] } dptree = "0.3.0" # These lines are used only for development. -teloxide-core = { git = "https://github.com/teloxide/teloxide-core", rev = "00165e6", default-features = false } +# teloxide-core = { git = "https://github.com/teloxide/teloxide-core", rev = "00165e6", default-features = false } # teloxide-macros = { git = "https://github.com/teloxide/teloxide-macros", rev = "e715105", optional = true } # dptree = { git = "https://github.com/teloxide/dptree", rev = "df578e4" } From 59b5047fa362673633544600214cbe11f3b09e5d Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Fri, 7 Oct 2022 09:55:58 +0600 Subject: [PATCH 50/66] Update the migration guide with `BotCommands` changes --- MIGRATION_GUIDE.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index c0e3566c3..9860c54cb 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -75,6 +75,32 @@ teloxide::repl(bot, |bot: Bot, msg: Message| async move { This is because REPLs now require the closure to return `RequestError` instead of a generic error type, so type inference works perfectly for a return value. If you use something other than `RequestError`, you can transfer your code to `teloxide::dispatching`, which still permits a generic error type. +`parse_with` now accepts a Rust _path_ to a custom parser function instead of a string: + +```diff,rust +fn custom_parser(input: String) -> Result<(u8,), ParseError> { + todo!() +} + +#[derive(BotCommands)] +enum Command { +- #[command(parse_with = "custom_parser")] ++ #[command(parse_with = custom_parser)] + Num(u8), +} +``` + +`rename` now only renames a command literally; use `rename_rule` to change the case of a command: + +```diff,rust +#[derive(BotCommands)] +- #[command(rename = "lowercase", description = "These commands are supported:")] ++ #[command(rename_rule = "lowercase", description = "These commands are supported:")] +enum Command { + // ... +} +``` + ## 0.9 -> 0.10 ### core From fe3aa2e7983db0fe0457063429fa108338cbc020 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Fri, 7 Oct 2022 10:14:36 +0600 Subject: [PATCH 51/66] Refactor `dptree` usage --- src/dispatching/dialogue.rs | 25 ++++++++++++------------- src/dispatching/handler_ext.rs | 4 ++-- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/dispatching/dialogue.rs b/src/dispatching/dialogue.rs index c8d27896a..89d163265 100644 --- a/src/dispatching/dialogue.rs +++ b/src/dispatching/dialogue.rs @@ -220,18 +220,17 @@ where Upd: GetChatId + Clone + Send + Sync + 'static, Output: Send + Sync + 'static, { - dptree::entry() - .chain(dptree::filter_map(|storage: Arc, upd: Upd| { - let chat_id = upd.chat_id()?; - Some(Dialogue::new(storage, chat_id)) - })) - .chain(dptree::filter_map_async(|dialogue: Dialogue| async move { - match dialogue.get_or_default().await { - Ok(dialogue) => Some(dialogue), - Err(err) => { - log::error!("dialogue.get_or_default() failed: {:?}", err); - None - } + dptree::filter_map(|storage: Arc, upd: Upd| { + let chat_id = upd.chat_id()?; + Some(Dialogue::new(storage, chat_id)) + }) + .filter_map_async(|dialogue: Dialogue| async move { + match dialogue.get_or_default().await { + Ok(dialogue) => Some(dialogue), + Err(err) => { + log::error!("dialogue.get_or_default() failed: {:?}", err); + None } - })) + } + }) } diff --git a/src/dispatching/handler_ext.rs b/src/dispatching/handler_ext.rs index 932d5079f..0013acb45 100644 --- a/src/dispatching/handler_ext.rs +++ b/src/dispatching/handler_ext.rs @@ -88,8 +88,8 @@ where C: BotCommands + Send + Sync + 'static, Output: Send + Sync + 'static, { - dptree::entry().chain(dptree::filter_map(move |message: Message, me: Me| { + dptree::filter_map(move |message: Message, me: Me| { let bot_name = me.user.username.expect("Bots must have a username"); message.text().and_then(|text| C::parse(text, &bot_name).ok()) - })) + }) } From 6043f5566e02dd7a9c72ee6c2c56bfa904ea5d04 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Fri, 7 Oct 2022 10:38:52 +0600 Subject: [PATCH 52/66] Update the 'Highlights' section I've simplified the point about dialogues and added the point "Feature-rich", which mentions some functionality typically asked by users in our support chat. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ab74b6d74..ae498ce6b 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,9 @@ [`dptree`]: https://github.com/teloxide/dptree [chain of responsibility]: https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern - - **Dialogues management subsystem.** Our dialogues management subsystem is simple and easy-to-use, and, furthermore, is agnostic of how/where dialogues are stored. For example, you can just replace a one line to achieve [persistence]. Out-of-the-box storages include [Redis] and [Sqlite]. + - **Feature-rich.** You can use both long polling and webhooks, configure an underlying HTTPS client, set a custom URL of a Telegram API server, and a lot more. + + - **Simple dialogues.** Our dialogues subsystem is simple and easy-to-use, and, furthermore, is agnostic of how/where dialogues are stored. For example, you can just replace a one line to achieve [persistence]. Out-of-the-box storages include [Redis] and [Sqlite]. [persistence]: https://en.wikipedia.org/wiki/Persistence_(computer_science) [Redis]: https://redis.io/ From 6966ab9ce111e7e932d852f51d560e4b64e7d491 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Fri, 7 Oct 2022 10:45:35 +0600 Subject: [PATCH 53/66] Simplify the highlights more --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ae498ce6b..da4d421ac 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ [Redis]: https://redis.io/ [Sqlite]: https://www.sqlite.org - - **Strongly typed commands.** You can describe bot commands as enumerations, and then they'll be automatically constructed from strings — just like JSON structures in [`serde-json`] and command-line arguments in [`structopt`]. + - **Strongly typed commands.** Define bot commands as an `enum` and teloxide will parse them automatically — just like JSON structures in [`serde-json`] and command-line arguments in [`structopt`]. [`structopt`]: https://github.com/TeXitoi/structopt [`serde-json`]: https://github.com/serde-rs/json From 83d3a11be9702838bc0322b985d4e29b7f510d89 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 7 Oct 2022 12:03:34 +0400 Subject: [PATCH 54/66] Apply suggestions from code review --- src/dispatching/repls.rs | 6 ++-- src/dispatching/repls/commands_repl.rs | 50 ++++++++++++-------------- src/dispatching/repls/repl.rs | 25 ++++++------- src/dispatching/repls/stopping.md | 4 +-- 4 files changed, 38 insertions(+), 47 deletions(-) diff --git a/src/dispatching/repls.rs b/src/dispatching/repls.rs index 0d41d7ba6..18fac935d 100644 --- a/src/dispatching/repls.rs +++ b/src/dispatching/repls.rs @@ -1,9 +1,9 @@ //! [REPL]s for dispatching updates. //! -//! This module provides functions for easy update handling, that accept a +//! This module provides utilities for easy update handling. They accept a //! single "handler" function that processes all updates of a certain kind. Note -//! that REPLs are meant to be used as a prototyping tool and lack configuration -//! and some advanced features. +//! that REPLs are meant to be used for simple scenarios, such as prototyping, +//! inasmuch they lack configuration and some advanced features. //! //! [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop diff --git a/src/dispatching/repls/commands_repl.rs b/src/dispatching/repls/commands_repl.rs index f3c25cfe6..b988d06bf 100644 --- a/src/dispatching/repls/commands_repl.rs +++ b/src/dispatching/repls/commands_repl.rs @@ -22,19 +22,17 @@ use teloxide_core::requests::Requester; /// Don't be scared by many trait bounds in the signature, in essence they /// require: /// -/// 1. `bot` is a bot, client for the Telegram bot API -/// - in teloxide this is represented via a [`Requester`] trait -/// 2. `handler` is an async function that returns `Result<(), E>` -/// - Such that `E` can be printed with [`Debug`] formatting -/// - And all arguments can be extracted from [`DependencyMap`] -/// - Which is the same, as all arguments implementing `Send + Sync + -/// 'static` -/// 3. `cmd` is a type of the command that will be parsed, -/// - The command type must implement [`BotCommands`] trait -/// - It can be acquired by writing `TheCommandType::ty()` -/// -/// All other requirements are about thread safety and data validity and can be -/// ignored for most of the time. +/// 1. `bot` is a bot, client for the Telegram bot API. It is represented via +/// the [`Requester`] trait. +/// 2. `handler` is an `async` function that takes arguments from +/// [`DependencyMap`] (see below) and returns [`ResponseResult`]. +/// 3. `cmd` is a type hint for your command enumeration +/// `MyCommand`: just write `MyCommand::ty()`. Note that `MyCommand` must +/// implement the [`BotCommands`] trait, typically via +/// `#[derive(BotCommands)]`. +/// +/// All the other requirements are about thread safety and data validity and can +/// be ignored for most of the time. /// /// ## Handler arguments /// @@ -85,20 +83,18 @@ where /// Don't be scared by many trait bounds in the signature, in essence they /// require: /// -/// 1. `bot` is a bot, client for the Telegram bot API -/// - in teloxide this is represented via a [`Requester`] trait -/// 2. `handler` is an async function that returns `Result<(), E>` -/// - Such that `E` can be printed with [`Debug`] formatting -/// - And all arguments can be extracted from [`DependencyMap`] -/// - Which is the same, as all arguments implementing `Send + Sync + -/// 'static` -/// 3. `listener` is an [`UpdateListener`] -/// 4. `cmd` is a type of the command that will be parsed, -/// - The command type must implement [`BotCommands`] trait -/// - It can be acquired by writing `TheCommandType::ty()` -/// -/// All other requirements are about thread safety and data validity and can be -/// ignored for most of the time. +/// 1. `bot` is a bot, client for the Telegram bot API. It is represented via +/// the [`Requester`] trait. +/// 2. `handler` is an `async` function that takes arguments from +/// [`DependencyMap`] (see below) and returns [`ResponseResult`]. +/// 3. `listener` is something that takes updates from a Telegram server and +/// implements [`UpdateListener`]. +/// 4. `cmd` is a type hint for your command enumeration `MyCommand`: just +/// write `MyCommand::ty()`. Note that `MyCommand` must implement the +/// [`BotCommands`] trait, typically via `#[derive(BotCommands)]`. +/// +/// All the other requirements are about thread safety and data validity and can +/// be ignored for most of the time. /// /// ## Handler arguments /// diff --git a/src/dispatching/repls/repl.rs b/src/dispatching/repls/repl.rs index 3a63d4400..8edf5828f 100644 --- a/src/dispatching/repls/repl.rs +++ b/src/dispatching/repls/repl.rs @@ -19,13 +19,10 @@ use teloxide_core::requests::Requester; /// Don't be scared by many trait bounds in the signature, in essence they /// require: /// -/// 1. `bot` is a bot, client for the Telegram bot API -/// - in teloxide this is represented via a [`Requester`] trait -/// 2. `handler` is an async function that returns `Result<(), E>` -/// - Such that `E` can be printed with [`Debug`] formatting -/// - And all arguments can be extracted from [`DependencyMap`] -/// - Which is the same, as all arguments implementing `Send + Sync + -/// 'static` +/// 1. `bot` is a bot, client for the Telegram bot API. It is represented via +/// the [`Requester`] trait. +/// 2. `handler` is an `async` function that takes arguments from +/// [`DependencyMap`] (see below) and returns [`ResponseResult`]. /// /// ## Handler arguments /// @@ -68,14 +65,12 @@ where /// Don't be scared by many trait bounds in the signature, in essence they /// require: /// -/// 1. `bot` is a bot, client for the Telegram bot API -/// - in teloxide this is represented via a [`Requester`] trait -/// 2. `handler` is an async function that returns `Result<(), E>` -/// - Such that `E` can be printed with [`Debug`] formatting -/// - And all arguments can be extracted from [`DependencyMap`] -/// - Which is the same, as all arguments implementing `Send + Sync + -/// 'static` -/// 3. `listener` is an [`UpdateListener`] +/// 1. `bot` is a bot, client for the Telegram bot API. It is represented via +/// the [`Requester`] trait. +/// 2. `handler` is an `async` function that takes arguments from +/// [`DependencyMap`] (see below) and returns [`ResponseResult`]. +/// 3. `listener` is something that takes updates from a Telegram server and +/// implements [`UpdateListener`]. /// /// ## Handler arguments /// diff --git a/src/dispatching/repls/stopping.md b/src/dispatching/repls/stopping.md index 63c65e28f..b5733e842 100644 --- a/src/dispatching/repls/stopping.md +++ b/src/dispatching/repls/stopping.md @@ -1,4 +1,4 @@ -To stop repl, simply press `Ctrl`+`C` in the terminal where you run the program. -Note that gracefully stopping can take some time (we plan to improve this, see [#711]). +To stop a REPL, simply press `Ctrl`+`C` in the terminal where you run the program. +Note that graceful shutdown may take some time (we plan to improve this, see [#711]). [#711]: https://github.com/teloxide/teloxide/issues/711 From d9b18abd5597180394898bdb45c32d0e9ca26f93 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 7 Oct 2022 12:19:54 +0400 Subject: [PATCH 55/66] fix docs --- src/dispatching/repls/commands_repl.rs | 7 +++---- src/dispatching/repls/repl.rs | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/dispatching/repls/commands_repl.rs b/src/dispatching/repls/commands_repl.rs index b988d06bf..9b1360060 100644 --- a/src/dispatching/repls/commands_repl.rs +++ b/src/dispatching/repls/commands_repl.rs @@ -3,13 +3,12 @@ use crate::{ update_listeners, update_listeners::UpdateListener, HandlerExt, UpdateFilterExt, }, error_handlers::LoggingErrorHandler, + requests::{Requester, ResponseResult}, types::Update, utils::command::BotCommands, - RequestError, }; use dptree::di::{DependencyMap, Injectable}; use std::{fmt::Debug, marker::PhantomData}; -use teloxide_core::requests::Requester; /// A [REPL] for commands. // @@ -58,7 +57,7 @@ pub async fn commands_repl<'a, R, Cmd, H, Args>(bot: R, handler: H, cmd: Phantom where R: Requester + Clone + Send + Sync + 'static, ::GetUpdates: Send, - H: Injectable, Args> + Send + Sync + 'static, + H: Injectable, Args> + Send + Sync + 'static, Cmd: BotCommands + Send + Sync + 'static, { let cloned_bot = bot.clone(); @@ -123,7 +122,7 @@ pub async fn commands_repl_with_listener<'a, R, Cmd, H, L, Args>( cmd: PhantomData, ) where Cmd: BotCommands + Send + Sync + 'static, - H: Injectable, Args> + Send + Sync + 'static, + H: Injectable, Args> + Send + Sync + 'static, L: UpdateListener + Send + 'a, L::Err: Debug + Send + 'a, R: Requester + Clone + Send + Sync + 'static, diff --git a/src/dispatching/repls/repl.rs b/src/dispatching/repls/repl.rs index 8edf5828f..1c6fd6595 100644 --- a/src/dispatching/repls/repl.rs +++ b/src/dispatching/repls/repl.rs @@ -1,12 +1,11 @@ use crate::{ dispatching::{update_listeners, update_listeners::UpdateListener, UpdateFilterExt}, error_handlers::LoggingErrorHandler, + requests::{Requester, ResponseResult}, types::Update, - RequestError, }; use dptree::di::{DependencyMap, Injectable}; use std::fmt::Debug; -use teloxide_core::requests::Requester; /// A [REPL] for messages. // @@ -47,7 +46,7 @@ pub async fn repl(bot: R, handler: H) where R: Requester + Send + Sync + Clone + 'static, ::GetUpdates: Send, - H: Injectable, Args> + Send + Sync + 'static, + H: Injectable, Args> + Send + Sync + 'static, { let cloned_bot = bot.clone(); repl_with_listener(bot, handler, update_listeners::polling_default(cloned_bot).await).await; @@ -94,7 +93,7 @@ where pub async fn repl_with_listener(bot: R, handler: H, listener: L) where R: Requester + Clone + Send + Sync + 'static, - H: Injectable, Args> + Send + Sync + 'static, + H: Injectable, Args> + Send + Sync + 'static, L: UpdateListener + Send, L::Err: Debug, { From db22e202211071c8c7ff3184f8fcb9b9d61079e6 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 7 Oct 2022 13:09:57 +0400 Subject: [PATCH 56/66] Apply suggestions from the review --- src/dispatching/repls/commands_repl.rs | 8 ++++++++ src/dispatching/repls/repl.rs | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/src/dispatching/repls/commands_repl.rs b/src/dispatching/repls/commands_repl.rs index 9b1360060..b4f988668 100644 --- a/src/dispatching/repls/commands_repl.rs +++ b/src/dispatching/repls/commands_repl.rs @@ -41,6 +41,10 @@ use std::{fmt::Debug, marker::PhantomData}; /// - `Cmd` (type of the parsed command) /// - [`Me`] /// +/// Each of these types can be accepted as a handler parameter. Note that they +/// aren't all required at the same time: e.g., you can take only the bot and +/// the command without [`Me`] and [`Message`]. +/// /// [`Me`]: crate::types::Me /// [`Message`]: crate::types::Message /// @@ -103,6 +107,10 @@ where /// - `Cmd` (type of the parsed command) /// - [`Me`] /// +/// Each of these types can be accepted as a handler parameter. Note that they +/// aren't all required at the same time: e.g., you can take only the bot and +/// the command without [`Me`] and [`Message`]. +/// /// [`Me`]: crate::types::Me /// [`Message`]: crate::types::Message /// diff --git a/src/dispatching/repls/repl.rs b/src/dispatching/repls/repl.rs index 1c6fd6595..e0d52eaed 100644 --- a/src/dispatching/repls/repl.rs +++ b/src/dispatching/repls/repl.rs @@ -30,6 +30,10 @@ use std::fmt::Debug; /// - `R` (type of the `bot`) /// - [`Me`] /// +/// Each of these types can be accepted as a handler parameter. Note that they +/// aren't all required at the same time: e.g., you can take only the bot and +/// the command without [`Me`] and [`Message`]. +/// /// [`Me`]: crate::types::Me /// [`Message`]: crate::types::Message /// @@ -78,6 +82,10 @@ where /// - `R` (type of the `bot`) /// - [`Me`] /// +/// Each of these types can be accepted as a handler parameter. Note that they +/// aren't all required at the same time: e.g., you can take only the bot and +/// the command without [`Me`] and [`Message`]. +/// /// [`Me`]: crate::types::Me /// [`Message`]: crate::types::Message /// From c6dd6bed441497e508ccd79b4071556447064916 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 7 Oct 2022 13:59:20 +0400 Subject: [PATCH 57/66] I hate rustfmt --- src/dispatching/repls/commands_repl.rs | 4 ++++ src/dispatching/repls/repl.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/dispatching/repls/commands_repl.rs b/src/dispatching/repls/commands_repl.rs index b4f988668..d4e829540 100644 --- a/src/dispatching/repls/commands_repl.rs +++ b/src/dispatching/repls/commands_repl.rs @@ -12,6 +12,8 @@ use std::{fmt::Debug, marker::PhantomData}; /// A [REPL] for commands. // +/// +// #[doc = include_str!("preamble.md")] /// /// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop @@ -77,6 +79,8 @@ where /// A [REPL] for commands, with a custom [`UpdateListener`]. // +/// +// #[doc = include_str!("preamble.md")] /// /// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop diff --git a/src/dispatching/repls/repl.rs b/src/dispatching/repls/repl.rs index e0d52eaed..e6fe69cbf 100644 --- a/src/dispatching/repls/repl.rs +++ b/src/dispatching/repls/repl.rs @@ -9,6 +9,8 @@ use std::fmt::Debug; /// A [REPL] for messages. // +/// +// #[doc = include_str!("preamble.md")] /// /// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop @@ -58,6 +60,8 @@ where /// A [REPL] for messages, with a custom [`UpdateListener`]. // +/// +// #[doc = include_str!("preamble.md")] /// /// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop From dc9ba4dd11c82bfc100eeee498f614a579a16d36 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 7 Oct 2022 14:06:42 +0400 Subject: [PATCH 58/66] fixups --- src/dispatching/repls/repl.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dispatching/repls/repl.rs b/src/dispatching/repls/repl.rs index e6fe69cbf..fd26e6e2a 100644 --- a/src/dispatching/repls/repl.rs +++ b/src/dispatching/repls/repl.rs @@ -34,7 +34,7 @@ use std::fmt::Debug; /// /// Each of these types can be accepted as a handler parameter. Note that they /// aren't all required at the same time: e.g., you can take only the bot and -/// the command without [`Me`] and [`Message`]. +/// the message without [`Me`]. /// /// [`Me`]: crate::types::Me /// [`Message`]: crate::types::Message @@ -88,7 +88,7 @@ where /// /// Each of these types can be accepted as a handler parameter. Note that they /// aren't all required at the same time: e.g., you can take only the bot and -/// the command without [`Me`] and [`Message`]. +/// the message without [`Me`]. /// /// [`Me`]: crate::types::Me /// [`Message`]: crate::types::Message From 583e5ea4abeeb94456b1e1bb04c192d19ad89d8d Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 7 Oct 2022 14:11:56 +0400 Subject: [PATCH 59/66] link "advanced features" in `repls` to `dispatching` --- src/dispatching/repls.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dispatching/repls.rs b/src/dispatching/repls.rs index 18fac935d..71d703430 100644 --- a/src/dispatching/repls.rs +++ b/src/dispatching/repls.rs @@ -3,9 +3,10 @@ //! This module provides utilities for easy update handling. They accept a //! single "handler" function that processes all updates of a certain kind. Note //! that REPLs are meant to be used for simple scenarios, such as prototyping, -//! inasmuch they lack configuration and some advanced features. +//! inasmuch they lack configuration and some [advanced features]. //! //! [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop +//! [advanced features]: crate::dispatching#dispatching-or-repls mod commands_repl; mod repl; From 1343015e6c5673a28f02882e810052ecad3bda71 Mon Sep 17 00:00:00 2001 From: Waffle Maybe Date: Fri, 7 Oct 2022 14:22:02 +0400 Subject: [PATCH 60/66] Update src/dispatching/repls/preamble.md Co-authored-by: Hirrolot --- src/dispatching/repls/preamble.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dispatching/repls/preamble.md b/src/dispatching/repls/preamble.md index 5c21f0796..f434a3256 100644 --- a/src/dispatching/repls/preamble.md +++ b/src/dispatching/repls/preamble.md @@ -4,4 +4,4 @@ See also: ["Dispatching or REPLs?"](dispatching/index.html#dispatching-or-repls) [`Dispatcher`]: crate::dispatching::Dispatcher -All errors from a the handler and will be logged. +All errors from the handler and update listener will be logged. From 3d65f81eae9c57a47e2cccdc2850f613bbda99cc Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Fri, 7 Oct 2022 16:30:33 +0600 Subject: [PATCH 61/66] Apply a tiny review suggestion Co-authored-by: Waffle Maybe --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index da4d421ac..7c726708a 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ [`dptree`]: https://github.com/teloxide/dptree [chain of responsibility]: https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern - - **Feature-rich.** You can use both long polling and webhooks, configure an underlying HTTPS client, set a custom URL of a Telegram API server, and a lot more. + - **Feature-rich.** You can use both long polling and webhooks, configure an underlying HTTPS client, set a custom URL of a Telegram API server, and much more. - **Simple dialogues.** Our dialogues subsystem is simple and easy-to-use, and, furthermore, is agnostic of how/where dialogues are stored. For example, you can just replace a one line to achieve [persistence]. Out-of-the-box storages include [Redis] and [Sqlite]. From 1ab5bdfc77e7899791dcf6d21e075146255656d9 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Fri, 7 Oct 2022 16:34:42 +0600 Subject: [PATCH 62/66] Use consistent naming of our crates --- CHANGELOG.md | 4 ++-- CODE_STYLE.md | 1 + README.md | 12 ++++++------ src/dispatching/repls/commands_repl.rs | 4 ++-- src/dispatching/repls/repl.rs | 4 ++-- src/dispatching/update_listeners/polling.rs | 2 +- src/dispatching/update_listeners/webhooks.rs | 2 +- src/features.md | 4 ++-- 8 files changed, 17 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f83a4fbd..4a1ce0bb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,7 +60,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add the `Key: Clone` requirement for `impl Dispatcher` [**BC**]. - `dispatching::update_listeners::{polling_default, polling}` now return a named, `Polling<_>` type. -- Update teloxide-core to v0.7.0 with Bot API 6.1 support, see [its changelog][core07c] for more information [**BC**]. +- Update `teloxide-core` to v0.7.0 with Bot API 6.1 support, see [its changelog][core07c] for more information [**BC**]. [core07c]: https://github.com/teloxide/teloxide-core/blob/master/CHANGELOG.md#070---2022-07-19 @@ -90,7 +90,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Update teloxide-core to v0.6.0 with [Bot API 6.0] support [**BC**]. +- Update `teloxide-core` to v0.6.0 with [Bot API 6.0] support [**BC**]. [Bot API 6.0]: https://core.telegram.org/bots/api#april-16-2022 diff --git a/CODE_STYLE.md b/CODE_STYLE.md index 77803ce38..40b65d862 100644 --- a/CODE_STYLE.md +++ b/CODE_STYLE.md @@ -83,6 +83,7 @@ impl Trait for Wrap { ... } /// [`tokio::fs::File`]: tokio::fs::File /// [`Bot::download_file`]: crate::Bot::download_file ``` +4. Write `teloxide`, `teloxide-macros`, and `teloxide-core`, not "teloxide", "Teloxide", "teloxide-macros" or any other variant. ## Use `Self` where possible diff --git a/README.md b/README.md index ab74b6d74..01955c354 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@
-

teloxide

+

`teloxide`

@@ -24,7 +24,7 @@ ## Highlights - - **Declarative design.** teloxide is based upon [`dptree`], a functional [chain of responsibility] pattern that allows you to express pipelines of message processing in a highly declarative and extensible style. + - **Declarative design.** `teloxide` is based upon [`dptree`], a functional [chain of responsibility] pattern that allows you to express pipelines of message processing in a highly declarative and extensible style. [`dptree`]: https://github.com/teloxide/dptree [chain of responsibility]: https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern @@ -56,7 +56,7 @@ $ set TELOXIDE_TOKEN= $ $env:TELOXIDE_TOKEN= ``` - 4. Make sure that your Rust compiler is up to date (teloxide currently requires rustc at least version 1.58): + 4. Make sure that your Rust compiler is up to date (`teloxide` currently requires rustc at least version 1.58): ```bash # If you're using stable $ rustup update stable @@ -304,7 +304,7 @@ A: No, only the bots API. **Q: Can I use webhooks?** -A: You can! Teloxide has a built-in support for webhooks in `dispatching::update_listeners::webhooks` module. See how it's used in [`examples/ngrok_ping_pong_bot`](examples/ngrok_ping_pong.rs) and [`examples/heroku_ping_pong_bot`](examples/heroku_ping_pong.rs). +A: You can! `teloxide` has a built-in support for webhooks in `dispatching::update_listeners::webhooks` module. See how it's used in [`examples/ngrok_ping_pong_bot`](examples/ngrok_ping_pong.rs) and [`examples/heroku_ping_pong_bot`](examples/heroku_ping_pong.rs). **Q: Can I handle both callback queries and messages within a single dialogue?** @@ -328,7 +328,7 @@ Feel free to propose your own bot to our collection! - [`zamazan4ik/npaperbot-telegram`](https://github.com/zamazan4ik/npaperbot-telegram) — Telegram bot for searching via C++ proposals.
-Show bots using teloxide older than v0.6.0 +Show bots using `teloxide` older than v0.6.0 - [`mxseev/logram`](https://github.com/mxseev/logram) — Utility that takes logs from anywhere and sends them to Telegram. - [`alexkonovalov/PedigreeBot`](https://github.com/alexkonovalov/PedigreeBot) — A Telegram bot for building family trees. @@ -340,7 +340,7 @@ Feel free to propose your own bot to our collection!
-See [700+ other public repositories using teloxide >>](https://github.com/teloxide/teloxide/network/dependents) +See [700+ other public repositories using `teloxide` >>](https://github.com/teloxide/teloxide/network/dependents) ## Contributing diff --git a/src/dispatching/repls/commands_repl.rs b/src/dispatching/repls/commands_repl.rs index d4e829540..fe9d9fb7a 100644 --- a/src/dispatching/repls/commands_repl.rs +++ b/src/dispatching/repls/commands_repl.rs @@ -37,7 +37,7 @@ use std::{fmt::Debug, marker::PhantomData}; /// /// ## Handler arguments /// -/// Teloxide provides the following types to the `handler`: +/// `teloxide` provides the following types to the `handler`: /// - [`Message`] /// - `R` (type of the `bot`) /// - `Cmd` (type of the parsed command) @@ -105,7 +105,7 @@ where /// /// ## Handler arguments /// -/// Teloxide provides the following types to the `handler`: +/// `teloxide` provides the following types to the `handler`: /// - [`Message`] /// - `R` (type of the `bot`) /// - `Cmd` (type of the parsed command) diff --git a/src/dispatching/repls/repl.rs b/src/dispatching/repls/repl.rs index fd26e6e2a..b352d8e3b 100644 --- a/src/dispatching/repls/repl.rs +++ b/src/dispatching/repls/repl.rs @@ -27,7 +27,7 @@ use std::fmt::Debug; /// /// ## Handler arguments /// -/// Teloxide provides the following types to the `handler`: +/// `teloxide` provides the following types to the `handler`: /// - [`Message`] /// - `R` (type of the `bot`) /// - [`Me`] @@ -81,7 +81,7 @@ where /// /// ## Handler arguments /// -/// Teloxide provides the following types to the `handler`: +/// `teloxide` provides the following types to the `handler`: /// - [`Message`] /// - `R` (type of the `bot`) /// - [`Me`] diff --git a/src/dispatching/update_listeners/polling.rs b/src/dispatching/update_listeners/polling.rs index 1543b3226..5fb863f07 100644 --- a/src/dispatching/update_listeners/polling.rs +++ b/src/dispatching/update_listeners/polling.rs @@ -67,7 +67,7 @@ where /// /// ## Note /// - /// Teloxide normally (when using [`Dispatcher`] or [`repl`]s) sets this + /// `teloxide` normally (when using [`Dispatcher`] or [`repl`]s) sets this /// automatically via [`hint_allowed_updates`], so you rarely need to use /// `allowed_updates` explicitly. /// diff --git a/src/dispatching/update_listeners/webhooks.rs b/src/dispatching/update_listeners/webhooks.rs index ef94fec46..e327016b1 100644 --- a/src/dispatching/update_listeners/webhooks.rs +++ b/src/dispatching/update_listeners/webhooks.rs @@ -49,7 +49,7 @@ pub struct Options { /// `a-z`, `0-9`, `_` and `-` are allowed. The header is useful to ensure /// that the request comes from a webhook set by you. /// - /// Default - teloxide will generate a random token. + /// Default - `teloxide` will generate a random token. pub secret_token: Option, } diff --git a/src/features.md b/src/features.md index c7aa4e6aa..1545af787 100644 --- a/src/features.md +++ b/src/features.md @@ -12,7 +12,7 @@ | `trace-adaptor` | Enables the [`Trace`](adaptors::Trace) bot adaptor. | | `erased` | Enables the [`ErasedRequester`](adaptors::ErasedRequester) bot adaptor. | | `full` | Enables all the features except `nightly`. | -| `nightly` | Enables nightly-only features (see the [teloxide-core features]). | +| `nightly` | Enables nightly-only features (see the [`teloxide-core` features]). | | `native-tls` | Enables the [`native-tls`] TLS implementation (**enabled by default**). | | `rustls` | Enables the [`rustls`] TLS implementation. | | `redis-storage` | Enables the [Redis] storage support for dialogues. | @@ -29,6 +29,6 @@ [`native-tls`]: https://docs.rs/native-tls [`rustls`]: https://docs.rs/rustls [`teloxide::utils::UpState`]: utils::UpState -[teloxide-core features]: https://docs.rs/teloxide-core/latest/teloxide_core/#cargo-features +[`teloxide-core` features]: https://docs.rs/teloxide-core/latest/teloxide_core/#cargo-features [`DispatcherBuilder::enable_ctrlc_handler`]: dispatching::DispatcherBuilder::enable_ctrlc_handler \ No newline at end of file From 60f0cd128db6819f98e430ef22b780e93f9d3847 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Fri, 7 Oct 2022 16:39:49 +0600 Subject: [PATCH 63/66] Use `teloxide` in `README.md` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 01955c354..5d48677dd 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@
-

`teloxide`

+

teloxide

From d690709dfb585adab582fbff54eb7ffecc3975ba Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Fri, 7 Oct 2022 16:45:16 +0600 Subject: [PATCH 64/66] Apply a review suggestion Co-authored-by: Waffle Maybe --- MIGRATION_GUIDE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 9860c54cb..83ea0829e 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -75,6 +75,8 @@ teloxide::repl(bot, |bot: Bot, msg: Message| async move { This is because REPLs now require the closure to return `RequestError` instead of a generic error type, so type inference works perfectly for a return value. If you use something other than `RequestError`, you can transfer your code to `teloxide::dispatching`, which still permits a generic error type. +### macros + `parse_with` now accepts a Rust _path_ to a custom parser function instead of a string: ```diff,rust From 4e56dd29b12fc0e737a7faf89647a68dcc712601 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Fri, 7 Oct 2022 16:55:57 +0600 Subject: [PATCH 65/66] Release v0.11.0 --- CHANGELOG.md | 6 ++++-- Cargo.toml | 2 +- README.md | 6 +++--- src/dispatching.rs | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f83a4fbd..0d8908432 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## unreleased +## 0.11.0 - 2022-10-07 + ### Changed - -- Updated `teloxide-macros` see its [changelog](https://github.com/teloxide/teloxide-macros/blob/master/CHANGELOG.md#unreleased) for more +- Updated `teloxide-macros` to v0.7.0; see its [changelog](https://github.com/teloxide/teloxide-macros/blob/master/CHANGELOG.md#070---2022-10-06) for more +- Updated `teloxide-core` to v0.8.0; see its [changelog](https://github.com/teloxide/teloxide-core/blob/master/CHANGELOG.md#080---2022-10-03) for more - `UpdateListener` now has an associated type `Err` instead of a generic - `AsUpdateStream` now has an associated type `StreamErr` instead of a generic - Rename `dispatching::stop_token::{AsyncStopToken, AsyncStopFlag}` => `stop::{StopToken, StopFlag}` diff --git a/Cargo.toml b/Cargo.toml index 83a5ab08b..65e5135ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "teloxide" -version = "0.10.1" +version = "0.11.0" edition = "2021" description = "An elegant Telegram bots framework for Rust" repository = "https://github.com/teloxide/teloxide" diff --git a/README.md b/README.md index ab74b6d74..296cd7703 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -> [v0.9 -> v0.10 migration guide >>](MIGRATION_GUIDE.md#09---010) +> [v0.10 -> v0.11 migration guide >>](MIGRATION_GUIDE.md#010---011)
@@ -13,7 +13,7 @@ - + @@ -70,7 +70,7 @@ $ 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.10", features = ["macros", "auto-send"] } +teloxide = { version = "0.11", features = ["macros", "auto-send"] } log = "0.4" pretty_env_logger = "0.4" tokio = { version = "1.8", features = ["rt-multi-thread", "macros"] } diff --git a/src/dispatching.rs b/src/dispatching.rs index f22c99c1a..e65eca20d 100644 --- a/src/dispatching.rs +++ b/src/dispatching.rs @@ -138,7 +138,7 @@ //! } //! ``` //! -//! Each parameter is supplied as a dependency by teloxide. In particular: +//! Each parameter is supplied as a dependency by `teloxide`. In particular: //! - `bot: Bot` comes from the dispatcher (see below) //! - `msg: Message` comes from [`Update::filter_message`] //! - `q: CallbackQuery` comes from [`Update::filter_callback_query`] From 5bed819c9fc0206e770432ea805f13e0c5a59f6a Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Fri, 7 Oct 2022 17:22:46 +0600 Subject: [PATCH 66/66] Update MSRV in `README.md` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 296cd7703..f3fae40d9 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ $ set TELOXIDE_TOKEN= $ $env:TELOXIDE_TOKEN= ``` - 4. Make sure that your Rust compiler is up to date (teloxide currently requires rustc at least version 1.58): + 4. Make sure that your Rust compiler is up to date (teloxide currently requires rustc at least version 1.64): ```bash # If you're using stable $ rustup update stable