Skip to content

Commit

Permalink
Merge pull request #746 from teloxide/command-repl
Browse files Browse the repository at this point in the history
Implement the `CommandRepl` trait

Former-commit-id: cb2298d
  • Loading branch information
Hirrolot committed Oct 29, 2022
2 parents b147b98 + 5532a4c commit 9a078b7
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 7 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## unreleased

### Added

- `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 `CommandReplExt` instead)

## 0.11.0 - 2022-10-07

### Changed
Expand Down
16 changes: 16 additions & 0 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
2 changes: 1 addition & 1 deletion examples/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<()> {
Expand Down
2 changes: 1 addition & 1 deletion examples/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
2 changes: 2 additions & 0 deletions src/dispatching/repls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@
mod commands_repl;
mod repl;

pub use commands_repl::CommandReplExt;
#[allow(deprecated)]
pub use commands_repl::{commands_repl, commands_repl_with_listener};
pub use repl::{repl, repl_with_listener};
142 changes: 142 additions & 0 deletions src/dispatching/repls/commands_repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,147 @@ 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 CommandReplExt {
/// A REPL for commands.
///
/// See [`CommandReplExt`] for more details.
#[must_use]
fn repl<'a, R, H, Args>(bot: R, handler: H) -> BoxFuture<'a, ()>
where
R: Requester + Clone + Send + Sync + 'static,
<R as Requester>::GetUpdates: Send,
<R as Requester>::GetWebhookInfo: Send,
<R as Requester>::GetMe: Send,
<R as Requester>::DeleteWebhook: Send,
H: Injectable<DependencyMap, ResponseResult<()>, Args> + Send + Sync + 'static;

/// A REPL for commands with a custom [`UpdateListener`].
///
/// 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
H: Injectable<DependencyMap, ResponseResult<()>, Args> + Send + Sync + 'static,
L: UpdateListener + Send + 'a,
L::Err: Debug + Send + 'a,
R: Requester + Clone + Send + Sync + 'static,
<R as Requester>::GetMe: Send;
}

#[cfg(feature = "ctrlc_handler")]
impl<Cmd> CommandReplExt 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,
<R as Requester>::GetUpdates: Send,
<R as Requester>::GetWebhookInfo: Send,
<R as Requester>::GetMe: Send,
<R as Requester>::DeleteWebhook: Send,
H: Injectable<DependencyMap, ResponseResult<()>, 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<DependencyMap, ResponseResult<()>, Args> + Send + Sync + 'static,
L: UpdateListener + Send + 'a,
L::Err: Debug + Send + 'a,
R: Requester + Clone + Send + Sync + 'static,
<R as Requester>::GetMe: Send,
{
use crate::dispatching::Dispatcher;

// Other update types are of no interest to use since this REPL is only for
// commands. See <https://github.com/teloxide/teloxide/issues/557>.
let ignore_update = |_upd| Box::pin(async {});

Box::pin(async move {
Dispatcher::builder(
bot,
Update::filter_message().filter_command::<Cmd>().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.
//
///
Expand Down Expand Up @@ -59,6 +198,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<Cmd>)
where
R: Requester + Clone + Send + Sync + 'static,
Expand All @@ -68,6 +208,7 @@ where
{
let cloned_bot = bot.clone();

#[allow(deprecated)]
commands_repl_with_listener(
bot,
handler,
Expand Down Expand Up @@ -127,6 +268,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,
Expand Down
7 changes: 4 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::CommandReplExt as _, Dispatcher, HandlerExt as _,
MessageFilterExt as _, UpdateFilterExt as _,
};

pub use teloxide_core::{
Expand Down
1 change: 1 addition & 0 deletions src/utils/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ pub trait BotCommands: Sized {
///
/// [`commands_repl`]: (crate::repls::commands_repl)
#[must_use]
#[deprecated(note = "Use `CommandReplExt` instead")]
fn ty() -> PhantomData<Self> {
PhantomData
}
Expand Down

0 comments on commit 9a078b7

Please sign in to comment.