From a7981cc99b362f0769b3dd9f70aa45231f708537 Mon Sep 17 00:00:00 2001 From: Hirrolot Date: Fri, 7 Oct 2022 22:50:08 +0600 Subject: [PATCH 01/21] Simplify the brief description of `crate::stop` Usually, we have very brief descriptions of modules. I see no reason to make it elaborate -- it does even contain only a few items. --- src/stop.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/stop.rs b/src/stop.rs index f61caf3d1..71fcebe95 100644 --- a/src/stop.rs +++ b/src/stop.rs @@ -1,8 +1,5 @@ -//! This module contains stop [token] and stop [flag] that are used to stop -//! async tasks, for example [listeners]. +//! Stopping asynchronous tasks, e.g., [listeners]. //! -//! [token]: StopToken -//! [flag]: StopFlag //! [listeners]: crate::dispatching::update_listeners use std::{convert::Infallible, future::Future, pin::Pin, task}; From 6ebf51efeb068b0291922be16bc6bc7d0fadc9c4 Mon Sep 17 00:00:00 2001 From: Lev Khoroshansky Date: Sun, 9 Oct 2022 19:41:52 +0300 Subject: [PATCH 02/21] fix: Sync docs & comments with code --- examples/admin.rs | 4 ++-- src/utils/command.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/admin.rs b/examples/admin.rs index 4be9a5430..fbbc16154 100644 --- a/examples/admin.rs +++ b/examples/admin.rs @@ -5,8 +5,8 @@ use teloxide::{prelude::*, types::ChatPermissions, utils::command::BotCommands}; // Derive BotCommands to parse text with a command into this enumeration. // -// 1. rename = "lowercase" turns all the commands into lowercase letters. -// 2. `description = "..."` specifies a text before all the commands. +// 1. `rename_rule = "lowercase"` turns all the commands into lowercase +// letters. 2. `description = "..."` specifies a text before all the commands. // // That is, you can just call Command::descriptions() to get a description of // your commands in this format: diff --git a/src/utils/command.rs b/src/utils/command.rs index 24e001cfa..2d16f23b1 100644 --- a/src/utils/command.rs +++ b/src/utils/command.rs @@ -233,7 +233,7 @@ pub trait BotCommands: Sized { /// Returns `PhantomData` that is used as a param of [`commands_repl`] /// - /// [`commands_repl`]: (crate::repls2::commands_repl) + /// [`commands_repl`]: (crate::repls::commands_repl) #[must_use] fn ty() -> PhantomData { PhantomData From b3fe220d9929c55840411d68e6db42a4e76e1618 Mon Sep 17 00:00:00 2001 From: Lev Khoroshansky Date: Sun, 9 Oct 2022 19:42:20 +0300 Subject: [PATCH 03/21] fix: Grammar & typos --- CHANGELOG.md | 8 ++++---- MIGRATION_GUIDE.md | 6 +++--- README.md | 6 +++--- examples/db_remember.rs | 2 +- examples/dispatching_features.rs | 2 +- examples/heroku_ping_pong.rs | 2 +- src/dispatching/update_listeners/polling.rs | 2 +- src/utils/command.rs | 12 ++++++------ src/utils/html.rs | 4 ++-- src/utils/markdown.rs | 2 +- 10 files changed, 23 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ea743ff8..795ed08ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,7 +75,7 @@ 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 contains some escapable symbols eg '.' ## 0.9.1 - 2022-05-27 @@ -188,7 +188,7 @@ 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`. +- The `dispatching2` and `prelude2` modules. They present a new dispatching model based on `dptree`. ### Changed @@ -265,7 +265,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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)). +- A storage persistence 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]`. @@ -371,7 +371,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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)). +- Now methods which can send file to Telegram returns `tokio::io::Result`. Early it 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 diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 83ea0829e..1449cca18 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -193,7 +193,7 @@ In order to make `Dispatcher` implement `Send`, `DispatcherBuilder::{default_han v0.6 of teloxide introduces a new dispatching model based on the [chain of responsibility pattern]. To use it, you need to replace `prelude` with `prelude2` and `dispatching` with `dispatching2`. Instead of using old REPLs, you should now use `teloxide::repls2`. -The whole design is different than the previous one based on Tokio streams. In this section, we are only to address the most common usage scenarios. +The whole design is different from the previous one based on Tokio streams. In this section, we are only to address the most common usage scenarios. First of all, now there are no streams. Instead of using streams, you use [`dptree`], which is a more suitable alternative for our purposes. Thus, if you previously used `Dispatcher::messages_handler`, now you should use `Update::filter_message()`, and so on. @@ -237,7 +237,7 @@ List of changed types: In teloxide `v0.4` (core `v0.2`) some API methods had wrong return types. This made them practically unusable as they've always returned parsing error. -On the offchance you were using the methods, you may need to adjust types in your code. +On the off-chance you were using the methods, you may need to adjust types in your code. List of changed return types: - `get_chat_administrators`: `ChatMember` -> `Vec` @@ -324,7 +324,7 @@ List of renamed items: #### Added `impl Clone` for {`CacheMe`, `DefaultParseMode`, `Throttle`} Previously said bot adaptors were lacking `Clone` implementation. -To workaround this issue it was proposed to wrap bot in `Arc`. +To work around this issue it was proposed to wrap bot in `Arc`. Now it's not required, so you can remove the `Arc`: ```diff diff --git a/README.md b/README.md index 2fb0010a5..795a40060 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,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.64): + 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 @@ -82,7 +82,7 @@ tokio = { version = "1.8", features = ["rt-multi-thread", "macros"] } ### The dices bot -This bot replies with a dice throw to each received message: +This bot replies with a die throw to each received message: [[`examples/throw_dice.rs`](examples/throw_dice.rs)] @@ -326,7 +326,7 @@ Feel free to propose your own bot to our collection! - [`modos189/tg_blackbox_bot`](https://gitlab.com/modos189/tg_blackbox_bot) — Anonymous feedback for your Telegram project. - [`0xNima/spacecraft`](https://github.com/0xNima/spacecraft) — Yet another telegram bot to downloading Twitter spaces. - [`0xNima/Twideo`](https://github.com/0xNima/Twideo) — Simple Telegram Bot for downloading videos from Twitter via their links. - - [`mattrighetti/libgen-bot-rs`](https://github.com/mattrighetti/libgen-bot-rs) — Telgram bot to interface with libgen. + - [`mattrighetti/libgen-bot-rs`](https://github.com/mattrighetti/libgen-bot-rs) — Telegram bot to interface with libgen. - [`zamazan4ik/npaperbot-telegram`](https://github.com/zamazan4ik/npaperbot-telegram) — Telegram bot for searching via C++ proposals.
diff --git a/examples/db_remember.rs b/examples/db_remember.rs index de09db0d6..980d13571 100644 --- a/examples/db_remember.rs +++ b/examples/db_remember.rs @@ -91,7 +91,7 @@ async fn got_number( } Command::Reset => { dialogue.reset().await?; - bot.send_message(msg.chat.id, "Number resetted.").await?; + bot.send_message(msg.chat.id, "Number reset.").await?; } } Ok(()) diff --git a/examples/dispatching_features.rs b/examples/dispatching_features.rs index 983f00027..6ce059d98 100644 --- a/examples/dispatching_features.rs +++ b/examples/dispatching_features.rs @@ -32,7 +32,7 @@ async fn main() { .endpoint(simple_commands_handler), ) .branch( - // Filter a maintainer by a used ID. + // Filter a maintainer by a user ID. dptree::filter(|cfg: ConfigParameters, msg: Message| { msg.from().map(|user| user.id == cfg.bot_maintainer).unwrap_or_default() }) diff --git a/examples/heroku_ping_pong.rs b/examples/heroku_ping_pong.rs index 6fe508473..597670474 100644 --- a/examples/heroku_ping_pong.rs +++ b/examples/heroku_ping_pong.rs @@ -10,7 +10,7 @@ // heroku create --buildpack emk/rust // ``` // -// To set buildpack for existing applicaton: +// To set buildpack for existing application: // // ``` // heroku buildpacks:set emk/rust diff --git a/src/dispatching/update_listeners/polling.rs b/src/dispatching/update_listeners/polling.rs index 5fb863f07..ffe8aa5a3 100644 --- a/src/dispatching/update_listeners/polling.rs +++ b/src/dispatching/update_listeners/polling.rs @@ -225,7 +225,7 @@ where /// /// C->>P: next /// -/// P->>T: *Acknolegment of update(5)* +/// P->>T: *Acknowledgement of update(5)* /// T->>P: ok /// /// P->>C: None diff --git a/src/utils/command.rs b/src/utils/command.rs index 2d16f23b1..785479d7f 100644 --- a/src/utils/command.rs +++ b/src/utils/command.rs @@ -91,7 +91,7 @@ pub use teloxide_macros::BotCommands; /// Change a prefix for all commands (the default is `/`). /// /// 3. `#[command(description = "description")]` -/// Add a sumary description of commands before all commands. +/// Add a summary description of commands before all commands. /// /// 4. `#[command(parse_with = "parser")]` /// Change the parser of arguments. Possible values: @@ -115,7 +115,7 @@ pub use teloxide_macros::BotCommands; /// # } /// ``` /// -/// - `split` - separates a messsage by a given separator (the default is the +/// - `split` - separates a message by a given separator (the default is the /// space character) and parses each part into the corresponding arguments, /// which must implement [`FromStr`]. /// @@ -412,9 +412,9 @@ where return None; } let mut words = text.split_whitespace(); - let mut splited = words.next()?[prefix.len()..].split('@'); - let command = splited.next()?; - let bot = splited.next(); + let mut split = words.next()?[prefix.len()..].split('@'); + let command = split.next()?; + let bot = split.next(); match bot { Some(name) if name.eq_ignore_ascii_case(bot_name.as_ref()) => {} None => {} @@ -485,7 +485,7 @@ impl Display for CommandDescriptions<'_> { } } -// The rest of tests are integrational due to problems with macro expansion in +// The rest of tests are integration due to problems with macro expansion in // unit tests. #[cfg(test)] mod tests { diff --git a/src/utils/html.rs b/src/utils/html.rs index 407f07929..d2243d5d2 100644 --- a/src/utils/html.rs +++ b/src/utils/html.rs @@ -95,7 +95,7 @@ pub fn code_inline(s: &str) -> String { /// style. /// /// Does not escape ' and " characters (as should be for usual HTML), because -/// they shoudn't be escaped by the [spec]. +/// they shouldn't be escaped by the [spec]. /// /// [spec]: https://core.telegram.org/bots/api#html-style #[must_use = "This function returns a new string, rather than mutating the argument, so calling it \ @@ -176,7 +176,7 @@ mod tests { assert_eq!( code_block_with_lang( "

pre-'formatted'\n & fixed-width \\code `block`

", - "\"" + "\"", ), concat!( "
",
diff --git a/src/utils/markdown.rs b/src/utils/markdown.rs
index 9c3554445..976e0ab44 100644
--- a/src/utils/markdown.rs
+++ b/src/utils/markdown.rs
@@ -38,7 +38,7 @@ pub fn italic(s: &str) -> String {
               without using its output does nothing useful"]
 pub fn underline(s: &str) -> String {
     // In case of ambiguity between italic and underline entities
-    // ‘__’ is always greadily treated from left to right as beginning or end of
+    // ‘__’ is always greedily treated from left to right as beginning or end of
     // underline entity, so instead of ___italic underline___ we should use
     // ___italic underline_\r__, where \r is a character with code 13, which
     // will be ignored.

From a9739c7da4f5629a120409daa6d3f468686d7224 Mon Sep 17 00:00:00 2001
From: Lev Khoroshansky 
Date: Sun, 9 Oct 2022 19:47:45 +0300
Subject: [PATCH 04/21] fix: Spacing

---
 examples/admin.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/examples/admin.rs b/examples/admin.rs
index fbbc16154..17f7373f4 100644
--- a/examples/admin.rs
+++ b/examples/admin.rs
@@ -5,8 +5,8 @@ use teloxide::{prelude::*, types::ChatPermissions, utils::command::BotCommands};
 
 // Derive BotCommands to parse text with a command into this enumeration.
 //
-//  1. `rename_rule = "lowercase"` turns all the commands into lowercase
-// letters.  2. `description = "..."` specifies a text before all the commands.
+// 1. `rename_rule = "lowercase"` turns all the commands into lowercase letters.
+// 2. `description = "..."` specifies a text before all the commands.
 //
 // That is, you can just call Command::descriptions() to get a description of
 // your commands in this format:

From 643ef423e87aa4f22f7b81d22c0fc8f6e83e3e13 Mon Sep 17 00:00:00 2001
From: Lev Khoroshansky 
Date: Sun, 9 Oct 2022 19:49:42 +0300
Subject: [PATCH 05/21] fix: Plural verb and better wording

---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 795ed08ca..7687aa9bb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -371,7 +371,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Fixed
 
-- Now methods which can send file to Telegram returns `tokio::io::Result`. Early it could panic ([issue 216](https://github.com/teloxide/teloxide/issues/216)).
+- Now methods which can send file to Telegram return `tokio::io::Result`. Before that it 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

From aec777d89994acfe1f42b1568f5617bd90f135e7 Mon Sep 17 00:00:00 2001
From: Lev Khoroshansky 
Date: Sun, 9 Oct 2022 19:59:52 +0300
Subject: [PATCH 06/21] fix: No need for hyphens

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 795a40060..d25c150f2 100644
--- a/README.md
+++ b/README.md
@@ -58,7 +58,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.64):
+ 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

From 739772929bd7e13f42f12e9afa02fef450ec0222 Mon Sep 17 00:00:00 2001
From: Hirrolot 
Date: Tue, 11 Oct 2022 14:54:51 +0600
Subject: [PATCH 07/21] Implement the `CommandRepl` trait

---
 CHANGELOG.md                           |   8 ++
 MIGRATION_GUIDE.md                     |  16 +++
 examples/admin.rs                      |   2 +-
 examples/command.rs                    |   2 +-
 src/dispatching/repls.rs               |   2 +
 src/dispatching/repls/commands_repl.rs | 140 +++++++++++++++++++++++++
 src/lib.rs                             |   7 +-
 src/prelude.rs                         |   3 +-
 8 files changed, 174 insertions(+), 6 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7687aa9bb..b9b891ec9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## unreleased
 
+### Added
+
+ - `teloxide::dispatching::repls::CommandRepl`, `teloxide::prelude::CommandRepl` ([issue #740](https://github.com/teloxide/teloxide/issues/740))
+
+### Deprecated
+
+ - `teloxide::dispatching::repls::{commands_repl, commands_repl_with_listener}` (use `CommandRepl` instead)
+
 ## 0.11.0 - 2022-10-07
 
 ### Changed
diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md
index 1449cca18..937320a71 100644
--- a/MIGRATION_GUIDE.md
+++ b/MIGRATION_GUIDE.md
@@ -1,6 +1,22 @@
 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.xxx.xxx
+
+### teloxide
+
+We have introduced the new trait `CommandRepl` that replaces the old `commands_repl_(with_listener)` functions:
+
+```diff,rust
+- teloxide::commands_repl(bot, answer, Command::ty())
++ Command::repl(bot, answer)
+```
+
+```diff,rust
+- teloxide::commands_repl_with_listener(bot, answer, listener, Command::ty())
++ Command::repl_with_listener(bot, answer, listener)
+```
+
 ## 0.10 -> 0.11
 
 ### core
diff --git a/examples/admin.rs b/examples/admin.rs
index 17f7373f4..113d6f075 100644
--- a/examples/admin.rs
+++ b/examples/admin.rs
@@ -60,7 +60,7 @@ async fn main() {
 
     let bot = teloxide::Bot::from_env();
 
-    teloxide::commands_repl(bot, action, Command::ty()).await;
+    Command::repl(bot, action).await;
 }
 
 async fn action(bot: Bot, msg: Message, cmd: Command) -> ResponseResult<()> {
diff --git a/examples/command.rs b/examples/command.rs
index 00f443154..26848015b 100644
--- a/examples/command.rs
+++ b/examples/command.rs
@@ -7,7 +7,7 @@ async fn main() {
 
     let bot = Bot::from_env();
 
-    teloxide::commands_repl(bot, answer, Command::ty()).await;
+    Command::repl(bot, answer).await;
 }
 
 #[derive(BotCommands, Clone)]
diff --git a/src/dispatching/repls.rs b/src/dispatching/repls.rs
index 71d703430..e8e6c4acc 100644
--- a/src/dispatching/repls.rs
+++ b/src/dispatching/repls.rs
@@ -11,5 +11,7 @@
 mod commands_repl;
 mod repl;
 
+pub use commands_repl::CommandRepl;
+#[allow(deprecated)]
 pub use commands_repl::{commands_repl, commands_repl_with_listener};
 pub use repl::{repl, repl_with_listener};
diff --git a/src/dispatching/repls/commands_repl.rs b/src/dispatching/repls/commands_repl.rs
index fe9d9fb7a..0c2370442 100644
--- a/src/dispatching/repls/commands_repl.rs
+++ b/src/dispatching/repls/commands_repl.rs
@@ -8,8 +8,145 @@ use crate::{
     utils::command::BotCommands,
 };
 use dptree::di::{DependencyMap, Injectable};
+use futures::future::BoxFuture;
 use std::{fmt::Debug, marker::PhantomData};
 
+/// A [REPL] for commands.
+///
+/// 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?"](../index.html#dispatching-or-repls).
+///
+/// [`Dispatcher`]: crate::dispatching::Dispatcher
+///
+/// All errors from the handler and update listener will be logged.
+///
+/// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop
+///
+/// This trait extends your [`BotCommands`] type with REPL facilities.
+///
+/// ## Signatures
+///
+/// Don't be scared by many trait bounds in the signatures, in essence they
+/// require:
+///
+/// 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`].
+///
+/// All the other requirements are about thread safety and data validity and can
+/// be ignored for most of the time.
+///
+/// ## Handler arguments
+///
+/// `teloxide` provides the following types to the `handler`:
+/// - [`Message`]
+/// - `R` (type of the `bot`)
+/// - `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
+///
+/// ## Stopping
+//
+#[doc = include_str!("stopping.md")]
+///
+/// ## Caution
+//
+#[doc = include_str!("caution.md")]
+///
+#[cfg(feature = "ctrlc_handler")]
+pub trait CommandRepl {
+    /// A REPL for commands.
+    ///
+    /// See [`CommandRepl`] for more details.
+    fn repl<'a, R, H, Args>(bot: R, handler: H) -> BoxFuture<'a, ()>
+    where
+        R: Requester + Clone + Send + Sync + 'static,
+        ::GetUpdates: Send,
+        ::GetWebhookInfo: Send,
+        ::GetMe: Send,
+        ::DeleteWebhook: Send,
+        H: Injectable, Args> + Send + Sync + 'static;
+
+    /// A REPL for commands with a custom [`UpdateListener`].
+    ///
+    /// See [`CommandRepl`] for more details.
+    fn repl_with_listener<'a, R, H, L, Args>(bot: R, handler: H, listener: L) -> BoxFuture<'a, ()>
+    where
+        H: Injectable, Args> + Send + Sync + 'static,
+        L: UpdateListener + Send + 'a,
+        L::Err: Debug + Send + 'a,
+        R: Requester + Clone + Send + Sync + 'static,
+        ::GetMe: Send;
+}
+
+#[cfg(feature = "ctrlc_handler")]
+impl CommandRepl for Cmd
+where
+    Cmd: BotCommands + Send + Sync + 'static,
+{
+    fn repl<'a, R, H, Args>(bot: R, handler: H) -> BoxFuture<'a, ()>
+    where
+        R: Requester + Clone + Send + Sync + 'static,
+        ::GetUpdates: Send,
+        ::GetWebhookInfo: Send,
+        ::GetMe: Send,
+        ::DeleteWebhook: Send,
+        H: Injectable, Args> + Send + Sync + 'static,
+    {
+        let cloned_bot = bot.clone();
+
+        Box::pin(async move {
+            Self::repl_with_listener(
+                bot,
+                handler,
+                update_listeners::polling_default(cloned_bot).await,
+            )
+            .await
+        })
+    }
+
+    fn repl_with_listener<'a, R, H, L, Args>(bot: R, handler: H, listener: L) -> BoxFuture<'a, ()>
+    where
+        H: Injectable, Args> + Send + Sync + 'static,
+        L: UpdateListener + Send + 'a,
+        L::Err: Debug + Send + 'a,
+        R: Requester + Clone + Send + Sync + 'static,
+        ::GetMe: Send,
+    {
+        use crate::dispatching::Dispatcher;
+
+        // Other update types are of no interest to use since this REPL is only for
+        // commands. See .
+        let ignore_update = |_upd| Box::pin(async {});
+
+        Box::pin(async move {
+            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
+        })
+    }
+}
+
 /// A [REPL] for commands.
 //
 ///
@@ -59,6 +196,7 @@ use std::{fmt::Debug, marker::PhantomData};
 #[doc = include_str!("caution.md")]
 ///
 #[cfg(feature = "ctrlc_handler")]
+#[deprecated(note = "Use `CommandsRepl::repl` instead")]
 pub async fn commands_repl<'a, R, Cmd, H, Args>(bot: R, handler: H, cmd: PhantomData)
 where
     R: Requester + Clone + Send + Sync + 'static,
@@ -68,6 +206,7 @@ where
 {
     let cloned_bot = bot.clone();
 
+    #[allow(deprecated)]
     commands_repl_with_listener(
         bot,
         handler,
@@ -127,6 +266,7 @@ where
 #[doc = include_str!("caution.md")]
 ///
 #[cfg(feature = "ctrlc_handler")]
+#[deprecated(note = "Use `CommandsRepl::repl_with_listener` instead")]
 pub async fn commands_repl_with_listener<'a, R, Cmd, H, L, Args>(
     bot: R,
     handler: H,
diff --git a/src/lib.rs b/src/lib.rs
index 80237a9dc..04012a60c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -58,9 +58,10 @@
 #![allow(clippy::nonstandard_macro_braces)]
 
 #[cfg(feature = "ctrlc_handler")]
-pub use dispatching::repls::{
-    commands_repl, commands_repl_with_listener, repl, repl_with_listener,
-};
+pub use dispatching::repls::{repl, repl_with_listener};
+
+#[allow(deprecated)]
+pub use dispatching::repls::{commands_repl, commands_repl_with_listener};
 
 pub mod dispatching;
 pub mod error_handlers;
diff --git a/src/prelude.rs b/src/prelude.rs
index 6dbe94df2..499eaea7e 100644
--- a/src/prelude.rs
+++ b/src/prelude.rs
@@ -6,7 +6,8 @@ pub use crate::error_handlers::{LoggingErrorHandler, OnError};
 pub use crate::respond;
 
 pub use crate::dispatching::{
-    dialogue::Dialogue, Dispatcher, HandlerExt as _, MessageFilterExt as _, UpdateFilterExt as _,
+    dialogue::Dialogue, repls::CommandRepl as _, Dispatcher, HandlerExt as _,
+    MessageFilterExt as _, UpdateFilterExt as _,
 };
 
 pub use teloxide_core::{

From db30e9efbe6333ad836fc3cb78916fa69caf6d00 Mon Sep 17 00:00:00 2001
From: Hirrolot 
Date: Tue, 11 Oct 2022 14:57:40 +0600
Subject: [PATCH 08/21] Use `Command::repl` in `README.md`

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index d25c150f2..2b6594e4c 100644
--- a/README.md
+++ b/README.md
@@ -131,7 +131,7 @@ async fn main() {
 
     let bot = Bot::from_env();
 
-    teloxide::commands_repl(bot, answer, Command::ty()).await;
+    Command::repl(bot, answer).await;
 }
 
 #[derive(BotCommands, Clone)]

From d90a9ff2e466db4fdcdaa8a6b62ab86e6f9e76af Mon Sep 17 00:00:00 2001
From: Hirrolot 
Date: Tue, 11 Oct 2022 15:00:02 +0600
Subject: [PATCH 09/21] Deprecate `BotCommands::ty` too

---
 CHANGELOG.md         | 2 +-
 src/utils/command.rs | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index b9b891ec9..513e536d2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Deprecated
 
- - `teloxide::dispatching::repls::{commands_repl, commands_repl_with_listener}` (use `CommandRepl` instead)
+ - `teloxide::dispatching::repls::{commands_repl, commands_repl_with_listener}`, `teloxide::utils::command::BotCommands::ty` (use `CommandRepl` instead)
 
 ## 0.11.0 - 2022-10-07
 
diff --git a/src/utils/command.rs b/src/utils/command.rs
index 785479d7f..9c9814eb7 100644
--- a/src/utils/command.rs
+++ b/src/utils/command.rs
@@ -235,6 +235,7 @@ pub trait BotCommands: Sized {
     ///
     /// [`commands_repl`]: (crate::repls::commands_repl)
     #[must_use]
+    #[deprecated(note = "Use `CommandRepl` instead")]
     fn ty() -> PhantomData {
         PhantomData
     }

From 0fb9399201cfa24d5890311ad9cc3cdf7cb1d0ad Mon Sep 17 00:00:00 2001
From: Hirrolot 
Date: Sat, 15 Oct 2022 23:54:16 +0600
Subject: [PATCH 10/21] Make `CommandRepl`'s methods `#[must_use]`

---
 src/dispatching/repls/commands_repl.rs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/dispatching/repls/commands_repl.rs b/src/dispatching/repls/commands_repl.rs
index 0c2370442..5cbecd5bc 100644
--- a/src/dispatching/repls/commands_repl.rs
+++ b/src/dispatching/repls/commands_repl.rs
@@ -69,6 +69,7 @@ pub trait CommandRepl {
     /// A REPL for commands.
     ///
     /// See [`CommandRepl`] for more details.
+    #[must_use]
     fn repl<'a, R, H, Args>(bot: R, handler: H) -> BoxFuture<'a, ()>
     where
         R: Requester + Clone + Send + Sync + 'static,
@@ -81,6 +82,7 @@ pub trait CommandRepl {
     /// A REPL for commands with a custom [`UpdateListener`].
     ///
     /// See [`CommandRepl`] for more details.
+    #[must_use]
     fn repl_with_listener<'a, R, H, L, Args>(bot: R, handler: H, listener: L) -> BoxFuture<'a, ()>
     where
         H: Injectable, Args> + Send + Sync + 'static,

From d60a7d034156866dc37f478ee5bf2c110fb14a63 Mon Sep 17 00:00:00 2001
From: Sima Kinsart 
Date: Sat, 22 Oct 2022 10:11:18 +0600
Subject: [PATCH 11/21] Mention graceful shutdown in the highlights

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 2fb0010a5..878c4ad84 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 much 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, do graceful shutdown, 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 d75b46520578b9389384b749fb39416c3fff19cb Mon Sep 17 00:00:00 2001
From: xamgore 
Date: Sat, 22 Oct 2022 17:13:32 +0400
Subject: [PATCH 12/21] Extend dialogue storages with RocksDB

---
 CHANGELOG.md                                  |   4 +
 Cargo.toml                                    |   5 +
 README.md                                     |   3 +-
 src/dispatching/dialogue/storage.rs           |   8 ++
 .../dialogue/storage/rocksdb_storage.rs       | 113 ++++++++++++++++++
 tests/rocksdb.rs                              |  95 +++++++++++++++
 6 files changed, 227 insertions(+), 1 deletion(-)
 create mode 100644 src/dispatching/dialogue/storage/rocksdb_storage.rs
 create mode 100644 tests/rocksdb.rs

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7687aa9bb..9abe238c3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## unreleased
 
+### Added
+
+- The `rocksdb-storage` feature -- enables the RocksDB support.
+
 ## 0.11.0 - 2022-10-07
 
 ### Changed
diff --git a/Cargo.toml b/Cargo.toml
index 65e5135ae..8eb1b3515 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,6 +19,7 @@ webhooks-axum = ["webhooks", "axum", "tower", "tower-http"]
 
 sqlite-storage = ["sqlx"]
 redis-storage = ["redis"]
+rocksdb-storage = ["rocksdb"]
 cbor-serializer = ["serde_cbor"]
 bincode-serializer = ["bincode"]
 
@@ -42,6 +43,7 @@ full = [
     "webhooks-axum",
     "sqlite-storage",
     "redis-storage",
+    "rocksdb-storage",
     "cbor-serializer",
     "bincode-serializer",
     "macros",
@@ -92,6 +94,9 @@ sqlx = { version = "0.6", optional = true, default-features = false, features =
         "sqlite",
 ] }
 redis = { version = "0.21", features = ["tokio-comp"], optional = true }
+rocksdb = { version = "0.19", optional = true, default-features = false, features = [
+        "lz4",
+] }
 serde_cbor = { version = "0.11", optional = true }
 bincode = { version = "1.3", optional = true }
 axum = { version = "0.5.13", optional = true }
diff --git a/README.md b/README.md
index d25c150f2..64dfc8c57 100644
--- a/README.md
+++ b/README.md
@@ -31,10 +31,11 @@
 
  - **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].
+ - **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], [RocksDB] and [Sqlite].
 
 [persistence]: https://en.wikipedia.org/wiki/Persistence_(computer_science)
 [Redis]: https://redis.io/
+[RocksDB]: https://rocksdb.org/
 [Sqlite]: https://www.sqlite.org
 
  - **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`].
diff --git a/src/dispatching/dialogue/storage.rs b/src/dispatching/dialogue/storage.rs
index b78cd72e0..e8d2c1587 100644
--- a/src/dispatching/dialogue/storage.rs
+++ b/src/dispatching/dialogue/storage.rs
@@ -9,6 +9,9 @@ mod redis_storage;
 #[cfg(feature = "sqlite-storage")]
 mod sqlite_storage;
 
+#[cfg(feature = "rocksdb-storage")]
+mod rocksdb_storage;
+
 use futures::future::BoxFuture;
 use teloxide_core::types::ChatId;
 
@@ -25,6 +28,9 @@ use std::sync::Arc;
 #[cfg(feature = "sqlite-storage")]
 pub use sqlite_storage::{SqliteStorage, SqliteStorageError};
 
+#[cfg(feature = "rocksdb-storage")]
+pub use rocksdb_storage::{RocksDbStorage, RocksDbStorageError};
+
 /// A storage with an erased error type.
 pub type ErasedStorage =
     dyn Storage> + Send + Sync;
@@ -41,10 +47,12 @@ pub type ErasedStorage =
 ///
 /// - [`InMemStorage`] -- a storage based on [`std::collections::HashMap`].
 /// - [`RedisStorage`] -- a Redis-based storage.
+/// - [`RocksDbStorage`] -- a RocksDB-based persistent storage.
 /// - [`SqliteStorage`] -- an SQLite-based persistent storage.
 ///
 /// [`InMemStorage`]: crate::dispatching::dialogue::InMemStorage
 /// [`RedisStorage`]: crate::dispatching::dialogue::RedisStorage
+/// [`RocksDbStorage`]: crate::dispatching::dialogue::RocksDbStorage
 /// [`SqliteStorage`]: crate::dispatching::dialogue::SqliteStorage
 pub trait Storage {
     type Error;
diff --git a/src/dispatching/dialogue/storage/rocksdb_storage.rs b/src/dispatching/dialogue/storage/rocksdb_storage.rs
new file mode 100644
index 000000000..d01d8fd9b
--- /dev/null
+++ b/src/dispatching/dialogue/storage/rocksdb_storage.rs
@@ -0,0 +1,113 @@
+use super::{serializer::Serializer, Storage};
+use futures::future::BoxFuture;
+use rocksdb::{DBCompressionType, DBWithThreadMode, MultiThreaded};
+use serde::{de::DeserializeOwned, Serialize};
+use std::{
+    convert::Infallible,
+    fmt::{Debug, Display},
+    str,
+    sync::Arc,
+};
+use teloxide_core::types::ChatId;
+use thiserror::Error;
+
+/// A persistent dialogue storage based on [RocksDb](http://rocksdb.org/).
+pub struct RocksDbStorage {
+    db: DBWithThreadMode,
+    serializer: S,
+}
+
+/// An error returned from [`RocksDbStorage`].
+#[derive(Debug, Error)]
+pub enum RocksDbStorageError
+where
+    SE: Debug + Display,
+{
+    #[error("dialogue serialization error: {0}")]
+    SerdeError(SE),
+
+    #[error("RocksDb error: {0}")]
+    RocksDbError(#[from] rocksdb::Error),
+
+    /// Returned from [`RocksDbStorage::remove_dialogue`].
+    #[error("row not found")]
+    DialogueNotFound,
+}
+
+impl RocksDbStorage {
+    pub async fn open(
+        path: &str,
+        serializer: S,
+        options: Option,
+    ) -> Result, RocksDbStorageError> {
+        let options = match options {
+            Some(opts) => opts,
+            None => {
+                let mut opts = rocksdb::Options::default();
+                opts.set_compression_type(DBCompressionType::Lz4);
+                opts.create_if_missing(true);
+                opts
+            }
+        };
+
+        let db = DBWithThreadMode::::open(&options, path)?;
+        Ok(Arc::new(Self { db, serializer }))
+    }
+}
+
+impl Storage for RocksDbStorage
+where
+    S: Send + Sync + Serializer + 'static,
+    D: Send + Serialize + DeserializeOwned + 'static,
+    >::Error: Debug + Display,
+{
+    type Error = RocksDbStorageError<>::Error>;
+
+    /// Returns [`RocksDbStorageError::DialogueNotFound`] if a dialogue does not
+    /// exist.
+    fn remove_dialogue(
+        self: Arc,
+        ChatId(chat_id): ChatId,
+    ) -> BoxFuture<'static, Result<(), Self::Error>> {
+        Box::pin(async move {
+            let key = chat_id.to_le_bytes();
+
+            if self.db.get(&key)?.is_none() {
+                return Err(RocksDbStorageError::DialogueNotFound);
+            }
+
+            self.db.delete(&key).unwrap();
+
+            Ok(())
+        })
+    }
+
+    fn update_dialogue(
+        self: Arc,
+        ChatId(chat_id): ChatId,
+        dialogue: D,
+    ) -> BoxFuture<'static, Result<(), Self::Error>> {
+        Box::pin(async move {
+            let d =
+                self.serializer.serialize(&dialogue).map_err(RocksDbStorageError::SerdeError)?;
+
+            let key = chat_id.to_le_bytes();
+            self.db.put(&key, &d)?;
+
+            Ok(())
+        })
+    }
+
+    fn get_dialogue(
+        self: Arc,
+        ChatId(chat_id): ChatId,
+    ) -> BoxFuture<'static, Result, Self::Error>> {
+        Box::pin(async move {
+            let key = chat_id.to_le_bytes();
+            self.db
+                .get(&key)?
+                .map(|d| self.serializer.deserialize(&d).map_err(RocksDbStorageError::SerdeError))
+                .transpose()
+        })
+    }
+}
diff --git a/tests/rocksdb.rs b/tests/rocksdb.rs
new file mode 100644
index 000000000..7366a2629
--- /dev/null
+++ b/tests/rocksdb.rs
@@ -0,0 +1,95 @@
+use std::{
+    fmt::{Debug, Display},
+    fs,
+    sync::Arc,
+};
+use teloxide::{
+    dispatching::dialogue::{RocksDbStorage, RocksDbStorageError, Serializer, Storage},
+    types::ChatId,
+};
+
+#[tokio::test(flavor = "multi_thread")]
+async fn test_rocksdb_json() {
+    fs::remove_dir_all("./test_db1").ok();
+    fs::create_dir("./test_db1").unwrap();
+    let storage = RocksDbStorage::open(
+        "./test_db1/test_db1.rocksdb",
+        teloxide::dispatching::dialogue::serializer::Json,
+        None,
+    )
+    .await
+    .unwrap();
+    test_rocksdb(storage).await;
+    fs::remove_dir_all("./test_db1").unwrap();
+}
+
+#[tokio::test(flavor = "multi_thread")]
+async fn test_rocksdb_bincode() {
+    fs::remove_dir_all("./test_db2").ok();
+    fs::create_dir("./test_db2").unwrap();
+    let storage = RocksDbStorage::open(
+        "./test_db2/test_db2.rocksdb",
+        teloxide::dispatching::dialogue::serializer::Bincode,
+        None,
+    )
+    .await
+    .unwrap();
+    test_rocksdb(storage).await;
+    fs::remove_dir_all("./test_db2").unwrap();
+}
+
+#[tokio::test(flavor = "multi_thread")]
+async fn test_rocksdb_cbor() {
+    fs::remove_dir_all("./test_db3").ok();
+    fs::create_dir("./test_db3").unwrap();
+    let storage = RocksDbStorage::open(
+        "./test_db3/test_db3.rocksdb",
+        teloxide::dispatching::dialogue::serializer::Cbor,
+        None,
+    )
+    .await
+    .unwrap();
+    test_rocksdb(storage).await;
+    fs::remove_dir_all("./test_db3").unwrap();
+}
+
+type Dialogue = String;
+
+macro_rules! test_dialogues {
+    ($storage:expr, $_0:expr, $_1:expr, $_2:expr) => {
+        assert_eq!(Arc::clone(&$storage).get_dialogue(ChatId(1)).await.unwrap(), $_0);
+        assert_eq!(Arc::clone(&$storage).get_dialogue(ChatId(11)).await.unwrap(), $_1);
+        assert_eq!(Arc::clone(&$storage).get_dialogue(ChatId(256)).await.unwrap(), $_2);
+    };
+}
+
+async fn test_rocksdb(storage: Arc>)
+where
+    S: Send + Sync + Serializer + 'static,
+    >::Error: Debug + Display,
+{
+    test_dialogues!(storage, None, None, None);
+
+    Arc::clone(&storage).update_dialogue(ChatId(1), "ABC".to_owned()).await.unwrap();
+    Arc::clone(&storage).update_dialogue(ChatId(11), "DEF".to_owned()).await.unwrap();
+    Arc::clone(&storage).update_dialogue(ChatId(256), "GHI".to_owned()).await.unwrap();
+
+    test_dialogues!(
+        storage,
+        Some("ABC".to_owned()),
+        Some("DEF".to_owned()),
+        Some("GHI".to_owned())
+    );
+
+    Arc::clone(&storage).remove_dialogue(ChatId(1)).await.unwrap();
+    Arc::clone(&storage).remove_dialogue(ChatId(11)).await.unwrap();
+    Arc::clone(&storage).remove_dialogue(ChatId(256)).await.unwrap();
+
+    test_dialogues!(storage, None, None, None);
+
+    // Check that a try to remove a non-existing dialogue results in an error.
+    assert!(matches!(
+        Arc::clone(&storage).remove_dialogue(ChatId(1)).await.unwrap_err(),
+        RocksDbStorageError::DialogueNotFound
+    ));
+}

From e7c5317954943f6ca3e65f036796078a8c4f0525 Mon Sep 17 00:00:00 2001
From: Hirrolot 
Date: Sat, 29 Oct 2022 14:39:21 +0600
Subject: [PATCH 13/21] Rename `CommandRepl` => `CommandReplExt`

---
 CHANGELOG.md                           | 4 ++--
 src/dispatching/repls.rs               | 2 +-
 src/dispatching/repls/commands_repl.rs | 8 ++++----
 src/prelude.rs                         | 2 +-
 src/utils/command.rs                   | 2 +-
 5 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 513e536d2..5f6c5e262 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,11 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added
 
- - `teloxide::dispatching::repls::CommandRepl`, `teloxide::prelude::CommandRepl` ([issue #740](https://github.com/teloxide/teloxide/issues/740))
+ - `teloxide::dispatching::repls::CommandReplExt`, `teloxide::prelude::CommandReplExt` ([issue #740](https://github.com/teloxide/teloxide/issues/740))
 
 ### Deprecated
 
- - `teloxide::dispatching::repls::{commands_repl, commands_repl_with_listener}`, `teloxide::utils::command::BotCommands::ty` (use `CommandRepl` instead)
+ - `teloxide::dispatching::repls::{commands_repl, commands_repl_with_listener}`, `teloxide::utils::command::BotCommands::ty` (use `CommandReplExt` instead)
 
 ## 0.11.0 - 2022-10-07
 
diff --git a/src/dispatching/repls.rs b/src/dispatching/repls.rs
index e8e6c4acc..aea4806eb 100644
--- a/src/dispatching/repls.rs
+++ b/src/dispatching/repls.rs
@@ -11,7 +11,7 @@
 mod commands_repl;
 mod repl;
 
-pub use commands_repl::CommandRepl;
+pub use commands_repl::CommandReplExt;
 #[allow(deprecated)]
 pub use commands_repl::{commands_repl, commands_repl_with_listener};
 pub use repl::{repl, repl_with_listener};
diff --git a/src/dispatching/repls/commands_repl.rs b/src/dispatching/repls/commands_repl.rs
index 5cbecd5bc..15f2396d5 100644
--- a/src/dispatching/repls/commands_repl.rs
+++ b/src/dispatching/repls/commands_repl.rs
@@ -65,10 +65,10 @@ use std::{fmt::Debug, marker::PhantomData};
 #[doc = include_str!("caution.md")]
 ///
 #[cfg(feature = "ctrlc_handler")]
-pub trait CommandRepl {
+pub trait CommandReplExt {
     /// A REPL for commands.
     ///
-    /// See [`CommandRepl`] for more details.
+    /// See [`CommandReplExt`] for more details.
     #[must_use]
     fn repl<'a, R, H, Args>(bot: R, handler: H) -> BoxFuture<'a, ()>
     where
@@ -81,7 +81,7 @@ pub trait CommandRepl {
 
     /// A REPL for commands with a custom [`UpdateListener`].
     ///
-    /// See [`CommandRepl`] for more details.
+    /// See [`CommandReplExt`] for more details.
     #[must_use]
     fn repl_with_listener<'a, R, H, L, Args>(bot: R, handler: H, listener: L) -> BoxFuture<'a, ()>
     where
@@ -93,7 +93,7 @@ pub trait CommandRepl {
 }
 
 #[cfg(feature = "ctrlc_handler")]
-impl CommandRepl for Cmd
+impl CommandReplExt for Cmd
 where
     Cmd: BotCommands + Send + Sync + 'static,
 {
diff --git a/src/prelude.rs b/src/prelude.rs
index 499eaea7e..ca6afa901 100644
--- a/src/prelude.rs
+++ b/src/prelude.rs
@@ -6,7 +6,7 @@ pub use crate::error_handlers::{LoggingErrorHandler, OnError};
 pub use crate::respond;
 
 pub use crate::dispatching::{
-    dialogue::Dialogue, repls::CommandRepl as _, Dispatcher, HandlerExt as _,
+    dialogue::Dialogue, repls::CommandReplExt as _, Dispatcher, HandlerExt as _,
     MessageFilterExt as _, UpdateFilterExt as _,
 };
 
diff --git a/src/utils/command.rs b/src/utils/command.rs
index 9c9814eb7..8f96998e0 100644
--- a/src/utils/command.rs
+++ b/src/utils/command.rs
@@ -235,7 +235,7 @@ pub trait BotCommands: Sized {
     ///
     /// [`commands_repl`]: (crate::repls::commands_repl)
     #[must_use]
-    #[deprecated(note = "Use `CommandRepl` instead")]
+    #[deprecated(note = "Use `CommandReplExt` instead")]
     fn ty() -> PhantomData {
         PhantomData
     }

From 2b749ed15c32ade8dfaf0f478ab41db7de3c80fc Mon Sep 17 00:00:00 2001
From: Sima Kinsart 
Date: Sun, 30 Oct 2022 11:55:54 +0600
Subject: [PATCH 14/21] Link the PR to `CHANGELOG.md`

---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9abe238c3..dca7896d3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added
 
-- The `rocksdb-storage` feature -- enables the RocksDB support.
+- The `rocksdb-storage` feature -- enables the RocksDB support ([PR #753](https://github.com/teloxide/teloxide/pull/753)).
 
 ## 0.11.0 - 2022-10-07
 

From e2f6fde7432bc943d9cf78a03b247025b55668c3 Mon Sep 17 00:00:00 2001
From: Sima Kinsart 
Date: Sun, 30 Oct 2022 14:39:45 +0600
Subject: [PATCH 15/21] Update the feature set with `rocksdb-storage`

---
 src/features.md | 41 +++++++++++++++++++++--------------------
 1 file changed, 21 insertions(+), 20 deletions(-)

diff --git a/src/features.md b/src/features.md
index 1545af787..a04563779 100644
--- a/src/features.md
+++ b/src/features.md
@@ -1,27 +1,28 @@
 ## Cargo features
 
-| Feature              | Description                                                                                |
-|----------------------|--------------------------------------------------------------------------------------------|
-| `webhooks`           | Enables general webhook utilities (almost useless on its own)                              |
-| `webhooks-axum`      | Enables webhook implementation based on axum framework                                     |
-| `macros`             | Re-exports macros from [`teloxide-macros`].                                                |
+| Feature              | Description |
+|----------------------|-------------|
+| `webhooks`           | Enables general webhook utilities (almost useless on its own). |
+| `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; 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.                                        |
-| `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]).                          |
-| `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.                                         |
-| `sqlite-storage`     | Enables the [Sqlite] storage support for dialogues.                                        |
-| `cbor-serializer`    | Enables the [CBOR] serializer for dialogues.                                               |
-| `bincode-serializer` | Enables the [Bincode] serializer for dialogues.                                            |
-
+| `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. |
+| `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]). |
+| `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. |
+| `rocksdb-storage`    | Enables the [RocksDB] storage support for dialogues. |
+| `sqlite-storage`     | Enables the [Sqlite] storage support for dialogues. |
+| `cbor-serializer`    | Enables the [CBOR] serializer for dialogues. |
+| `bincode-serializer` | Enables the [Bincode] serializer for dialogues. |
 
 [Redis]: https://redis.io/
+[RocksDB]: https://rocksdb.org/
 [Sqlite]: https://www.sqlite.org/
 [CBOR]: https://en.wikipedia.org/wiki/CBOR
 [Bincode]: https://github.com/servo/bincode
@@ -31,4 +32,4 @@
 [`teloxide::utils::UpState`]: utils::UpState
 [`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
+[`DispatcherBuilder::enable_ctrlc_handler`]: dispatching::DispatcherBuilder::enable_ctrlc_handler

From 2f18b5f0603a99b7a3e4472171b3e1df506719bf Mon Sep 17 00:00:00 2001
From: Maybe Waffle 
Date: Mon, 31 Oct 2022 13:36:26 +0400
Subject: [PATCH 16/21] Remove `,rust` from languages in `MIGRATION_GUIDE.md`

Anyway no one is rendering the syntax for diffs, while some even stop
understanding that it's a diff.
---
 MIGRATION_GUIDE.md | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md
index 937320a71..de6665c4e 100644
--- a/MIGRATION_GUIDE.md
+++ b/MIGRATION_GUIDE.md
@@ -7,12 +7,12 @@ Note that the list of required changes is not fully exhaustive and it may lack s
 
 We have introduced the new trait `CommandRepl` that replaces the old `commands_repl_(with_listener)` functions:
 
-```diff,rust
+```diff
 - teloxide::commands_repl(bot, answer, Command::ty())
 + Command::repl(bot, answer)
 ```
 
-```diff,rust
+```diff
 - teloxide::commands_repl_with_listener(bot, answer, listener, Command::ty())
 + Command::repl_with_listener(bot, answer, listener)
 ```
@@ -24,12 +24,12 @@ We have introduced the new trait `CommandRepl` that replaces the old `commands_r
 Requests can now be `.await`ed directly, without need of `.send()` or `AutoSend`.
 If you previously used `AutoSend` adaptor, you can safely remove it:
 
-```diff,rust
+```diff
 -let bot = Bot::from_env().auto_send();
 +let bot = Bot::from_env();
 ```
 
-```diff,rust
+```diff
 -async fn start(bot: AutoSend, dialogue: MyDialogue, msg: Message) -> HandlerResult {
 +async fn start(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult {
 ```
@@ -57,7 +57,7 @@ You may need to change code accordingly:
 -let id: i32 = message.id;
 +let id: MessageId = message.id;
 ```
-```diff,rust
+```diff
 let (cid, mid): (ChatId, i32) = get_message_to_delete_from_db();
 -bot.delete_message(cid, mid).await?;
 +bot.delete_message(cid, MessageId(mid)).await?;
@@ -66,7 +66,7 @@ let (cid, mid): (ChatId, i32) = get_message_to_delete_from_db();
 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
+```diff
 -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);
@@ -80,7 +80,7 @@ See `Sticker` documentation for more information about the new structure.
 
 You can now write `Ok(())` instead of `respond(())` at the end of closures provided to RELPs:
 
-```diff,rust
+```diff
 teloxide::repl(bot, |bot: Bot, msg: Message| async move {
     bot.send_dice(msg.chat.id).await?;
 -    respond(())
@@ -95,7 +95,7 @@ This is because REPLs now require the closure to return `RequestError` instead o
 
 `parse_with` now accepts a Rust _path_ to a custom parser function instead of a string:
 
-```diff,rust
+```diff
 fn custom_parser(input: String) -> Result<(u8,), ParseError> {
     todo!()
 }
@@ -110,7 +110,7 @@ enum Command {
 
 `rename` now only renames a command literally; use `rename_rule` to change the case of a command:
 
-```diff,rust
+```diff
 #[derive(BotCommands)]
 - #[command(rename = "lowercase", description = "These commands are supported:")]
 + #[command(rename_rule = "lowercase", description = "These commands are supported:")]

From 7287ffe2f0e212a20843c709cdfbdb31fb462544 Mon Sep 17 00:00:00 2001
From: Maybe Waffle 
Date: Mon, 31 Oct 2022 13:44:32 +0400
Subject: [PATCH 17/21] Add stop token changes to the migration guide

---
 MIGRATION_GUIDE.md | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md
index de6665c4e..0c8a33bf7 100644
--- a/MIGRATION_GUIDE.md
+++ b/MIGRATION_GUIDE.md
@@ -91,6 +91,16 @@ 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.
 
+"Stop tokens" were refactored, the trait is now removed and the types were renamed:
+
+```diff
+-use teloxide::dispatching::stop_token::{AsyncStopToken, AsyncStopFlag};
++use teloxide::stop::{StopToken, StopFlag, mk_stop_token};
+
+-let (token, flag): (AsyncStopToken, AsyncStopFlag) = AsyncStopToken::new_pair();
++let (token, flag): (StopToken, StopFlag) = mk_stop_token();
+```
+
 ### macros
 
 `parse_with` now accepts a Rust _path_ to a custom parser function instead of a string:

From 69d86d91ddabfc854dc5978876e5821d59d47b4d Mon Sep 17 00:00:00 2001
From: Sima Kinsart 
Date: Mon, 31 Oct 2022 15:56:13 +0600
Subject: [PATCH 18/21] Don't list the `auto-send` feature in `README.md`

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index b1e528a13..bf649dd56 100644
--- a/README.md
+++ b/README.md
@@ -73,7 +73,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.11", features = ["macros", "auto-send"] }
+teloxide = { version = "0.11", features = ["macros"] }
 log = "0.4"
 pretty_env_logger = "0.4"
 tokio = { version =  "1.8", features = ["rt-multi-thread", "macros"] }

From b97c2c6063fece1c7ac501e3784d147046c61975 Mon Sep 17 00:00:00 2001
From: Hirrolot 
Date: Mon, 31 Oct 2022 16:30:49 +0600
Subject: [PATCH 19/21] Release v0.11.1

---
 CHANGELOG.md | 2 ++
 Cargo.toml   | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index b1b8937a4..5522bab0a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## unreleased
 
+# 0.11.1 - 2022-10-31
+
 ### Added
 
 - The `rocksdb-storage` feature -- enables the RocksDB support ([PR #753](https://github.com/teloxide/teloxide/pull/753))
diff --git a/Cargo.toml b/Cargo.toml
index 8eb1b3515..f4056c492 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "teloxide"
-version = "0.11.0"
+version = "0.11.1"
 edition = "2021"
 description = "An elegant Telegram bots framework for Rust"
 repository = "https://github.com/teloxide/teloxide"

From 08d2e4f10278a4040f615610f766a36fa5ac979f Mon Sep 17 00:00:00 2001
From: Hirrolot 
Date: Mon, 31 Oct 2022 21:26:28 +0600
Subject: [PATCH 20/21] Update the migration guide version

---
 MIGRATION_GUIDE.md | 2 +-
 README.md          | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md
index 0c8a33bf7..c5da2a04b 100644
--- a/MIGRATION_GUIDE.md
+++ b/MIGRATION_GUIDE.md
@@ -1,7 +1,7 @@
 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.xxx.xxx
+## 0.11 -> 0.11.1
 
 ### teloxide
 
diff --git a/README.md b/README.md
index bf649dd56..f211a5473 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-> [v0.10 -> v0.11 migration guide >>](MIGRATION_GUIDE.md#010---011)
+> [v0.11 -> v0.11.1 migration guide >>](MIGRATION_GUIDE.md#011---0111)
 
 
From bce24fdf8df8f40c960923e7838c290258bf9549 Mon Sep 17 00:00:00 2001 From: Sima Kinsart Date: Mon, 31 Oct 2022 21:29:38 +0600 Subject: [PATCH 21/21] Fix the version header (`CHANGELOG.md`) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5522bab0a..3e84df636 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## unreleased -# 0.11.1 - 2022-10-31 +## 0.11.1 - 2022-10-31 ### Added