From 4a52caab71ff05ecd5e26c78bdf55be8f0115398 Mon Sep 17 00:00:00 2001 From: jrx Date: Sat, 19 Aug 2023 00:34:38 +0200 Subject: [PATCH] triable bot --- crates/teloxide/src/dispatching/dispatcher.rs | 29 ++++- crates/teloxide/src/lib.rs | 2 +- crates/teloxide/src/repls.rs | 2 +- crates/teloxide/src/repls/repl.rs | 120 +++++++++++++++++- 4 files changed, 146 insertions(+), 7 deletions(-) diff --git a/crates/teloxide/src/dispatching/dispatcher.rs b/crates/teloxide/src/dispatching/dispatcher.rs index d7c14387..2c0b8a2a 100644 --- a/crates/teloxide/src/dispatching/dispatcher.rs +++ b/crates/teloxide/src/dispatching/dispatcher.rs @@ -295,15 +295,39 @@ where /// [`shutdown`]: ShutdownToken::shutdown pub async fn dispatch_with_listener<'a, UListener, Eh>( &'a mut self, - mut update_listener: UListener, + update_listener: UListener, update_listener_error_handler: Arc, ) where UListener: UpdateListener + 'a, Eh: ErrorHandler + 'a, UListener::Err: Debug, + { + self.try_dispatch_with_listener(update_listener, update_listener_error_handler) + .await + .expect("try_dispatch_with_listener failed") + } + + /// Same as `dispatch_with_listener` but returns a Result<_> instead of + /// panicking + /// + /// Starts your bot with custom `update_listener` and + /// `update_listener_error_handler`. + /// + /// This method adds the same dependencies as [`Dispatcher::dispatch`]. + /// + /// [`shutdown`]: ShutdownToken::shutdown + pub async fn try_dispatch_with_listener<'a, UListener, Eh>( + &'a mut self, + mut update_listener: UListener, + update_listener_error_handler: Arc, + ) -> Result<(), R::Err> + where + 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'"); + let me = self.bot.get_me().send().await?; self.dependencies.insert(me); self.dependencies.insert(self.bot.clone()); @@ -353,6 +377,7 @@ where .await; self.state.done(); + Ok(()) } async fn process_update( diff --git a/crates/teloxide/src/lib.rs b/crates/teloxide/src/lib.rs index 1a1c6797..d5bb39ca 100644 --- a/crates/teloxide/src/lib.rs +++ b/crates/teloxide/src/lib.rs @@ -60,7 +60,7 @@ #![allow(clippy::nonstandard_macro_braces)] #[cfg(feature = "ctrlc_handler")] -pub use repls::{repl, repl_with_listener}; +pub use repls::{repl, repl_with_listener, try_repl, try_repl_with_listener}; #[cfg(feature = "ctrlc_handler")] #[allow(deprecated)] diff --git a/crates/teloxide/src/repls.rs b/crates/teloxide/src/repls.rs index aea4806e..96df9dfa 100644 --- a/crates/teloxide/src/repls.rs +++ b/crates/teloxide/src/repls.rs @@ -14,4 +14,4 @@ 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}; +pub use repl::{repl, repl_with_listener, try_repl, try_repl_with_listener}; diff --git a/crates/teloxide/src/repls/repl.rs b/crates/teloxide/src/repls/repl.rs index dcb0a308..23bde903 100644 --- a/crates/teloxide/src/repls/repl.rs +++ b/crates/teloxide/src/repls/repl.rs @@ -50,13 +50,65 @@ use std::fmt::Debug; /// #[cfg(feature = "ctrlc_handler")] pub async fn repl(bot: R, handler: H) +where + R: Requester + Send + Sync + Clone + 'static, + ::GetUpdates: Send, + H: Injectable, Args> + Send + Sync + 'static, +{ + try_repl(bot, handler).await.expect("try_repl failed") +} + +/// Same as `repl` but returns a Result<_> instead of panicking +/// +/// A [REPL] for messages. +// +/// +// +#[doc = include_str!("preamble.md")] +/// +/// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop +/// +/// ## Signature +/// +/// 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. 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 +/// +/// `teloxide` provides the following types to the `handler`: +/// - [`Message`] +/// - `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 message without [`Me`]. +/// +/// [`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 async fn try_repl(bot: R, handler: H) -> Result<(), R::Err> where R: Requester + Send + Sync + Clone + 'static, ::GetUpdates: Send, H: Injectable, Args> + Send + Sync + 'static, { let cloned_bot = bot.clone(); - repl_with_listener(bot, handler, update_listeners::polling_default(cloned_bot).await).await; + try_repl_with_listener(bot, handler, update_listeners::polling_default(cloned_bot).await).await } /// A [REPL] for messages, with a custom [`UpdateListener`]. @@ -104,6 +156,66 @@ where /// #[cfg(feature = "ctrlc_handler")] 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, + L::Err: Debug, +{ + try_repl_with_listener(bot, handler, listener).await.expect("try_repl_with_listener failed") +} + +/// Same as `repl_with_listener` but returns a Result<_> instead of panicking +/// +/// A [REPL] for messages, with a custom [`UpdateListener`]. +// +/// +// +#[doc = include_str!("preamble.md")] +/// +/// [REPL]: https://en.wikipedia.org/wiki/Read-eval-print_loop +/// [`UpdateListener`]: crate::update_listeners::UpdateListener +/// +/// ## Signature +/// +/// 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. 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 +/// +/// `teloxide` provides the following types to the `handler`: +/// - [`Message`] +/// - `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 message without [`Me`]. +/// +/// [`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 async fn try_repl_with_listener( + bot: R, + handler: H, + listener: L, +) -> Result<(), R::Err> where R: Requester + Clone + Send + Sync + 'static, H: Injectable, Args> + Send + Sync + 'static, @@ -120,11 +232,13 @@ where .default_handler(ignore_update) .enable_ctrlc_handler() .build() - .dispatch_with_listener( + .try_dispatch_with_listener( listener, LoggingErrorHandler::with_custom_text("An error from the update listener"), ) - .await; + .await?; + + Ok(()) } #[test]