mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 14:35:36 +01:00
added DialogueState macro
This commit is contained in:
parent
6c0f1b9fc4
commit
72dfef4963
9 changed files with 104 additions and 66 deletions
|
@ -73,8 +73,8 @@ full = [
|
|||
[dependencies]
|
||||
teloxide-core = { version = "0.3.3", default-features = false }
|
||||
#teloxide-core = { git = "https://github.com/teloxide/teloxide-core.git", rev = "...", default-features = false }
|
||||
teloxide-macros = { version = "0.4", optional = true }
|
||||
|
||||
#teloxide-macros = { version = "0.4", optional = true }
|
||||
teloxide-macros = { path = "../teloxide-macros", optional = true }
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
|
|
10
README.md
10
README.md
|
@ -173,7 +173,7 @@ A dialogue is described by an enumeration where each variant is one of possible
|
|||
|
||||
Below is a bot that asks you three questions and then sends the answers back to you. First, let's start with an enumeration (a collection of our dialogue's states):
|
||||
|
||||
([dialogue_bot/src/dialogue/mod.rs](./examples/dialogue_bot/src/dialogue/mod.rs))
|
||||
([dialogue_bot/src/dialogue/mod.rs](examples/dialogue_bot/src/state/mod.rs))
|
||||
```rust,ignore
|
||||
// Imports are omitted...
|
||||
|
||||
|
@ -197,7 +197,7 @@ When a user sends a message to our bot and such a dialogue does not exist yet, a
|
|||
<details>
|
||||
<summary>Dialogue::Start</summary>
|
||||
|
||||
([dialogue_bot/src/dialogue/states/start.rs](./examples/dialogue_bot/src/dialogue/states/start.rs))
|
||||
([dialogue_bot/src/dialogue/states/start.rs](examples/dialogue_bot/src/state/states/start.rs))
|
||||
```rust,ignore
|
||||
// Imports are omitted...
|
||||
|
||||
|
@ -219,7 +219,7 @@ async fn start(
|
|||
<details>
|
||||
<summary>Dialogue::ReceiveFullName</summary>
|
||||
|
||||
([dialogue_bot/src/dialogue/states/receive_full_name.rs](./examples/dialogue_bot/src/dialogue/states/receive_full_name.rs))
|
||||
([dialogue_bot/src/dialogue/states/receive_full_name.rs](examples/dialogue_bot/src/state/states/receive_full_name.rs))
|
||||
```rust,ignore
|
||||
// Imports are omitted...
|
||||
|
||||
|
@ -242,7 +242,7 @@ async fn receive_full_name(
|
|||
<details>
|
||||
<summary>Dialogue::ReceiveAge</summary>
|
||||
|
||||
([dialogue_bot/src/dialogue/states/receive_age.rs](./examples/dialogue_bot/src/dialogue/states/receive_age.rs))
|
||||
([dialogue_bot/src/dialogue/states/receive_age.rs](examples/dialogue_bot/src/state/states/receive_age.rs))
|
||||
```rust,ignore
|
||||
// Imports are omitted...
|
||||
|
||||
|
@ -275,7 +275,7 @@ async fn receive_age_state(
|
|||
<details>
|
||||
<summary>Dialogue::ReceiveLocation</summary>
|
||||
|
||||
([dialogue_bot/src/dialogue/states/receive_location.rs](./examples/dialogue_bot/src/dialogue/states/receive_location.rs))
|
||||
([dialogue_bot/src/dialogue/states/receive_location.rs](examples/dialogue_bot/src/state/states/receive_location.rs))
|
||||
```rust,ignore
|
||||
// Imports are omitted...
|
||||
|
||||
|
|
|
@ -15,62 +15,36 @@
|
|||
// ```
|
||||
use teloxide::{
|
||||
dispatching2::dialogue::{serializer::Json, SqliteStorage},
|
||||
macros::DialogueState,
|
||||
prelude::*,
|
||||
};
|
||||
use teloxide::dispatching2::HandlerFactory;
|
||||
|
||||
// FIXME: naming
|
||||
type MyBot = AutoSend<Bot>;
|
||||
type Store = SqliteStorage<Json>;
|
||||
type BotDialogue = Dialogue<State, Store>;
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(DialogueState, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[out(anyhow::Result<()>)]
|
||||
#[store(SqliteStorage<Json>)]
|
||||
pub enum State {
|
||||
#[handler(handle_start)]
|
||||
Start,
|
||||
|
||||
#[handler(handle_receive_full_name)]
|
||||
ReceiveFullName,
|
||||
|
||||
#[handler(handle_receive_age)]
|
||||
ReceiveAge(String),
|
||||
|
||||
#[handler(handle_receive_location)]
|
||||
ReceiveLocation(ReceiveLocation),
|
||||
}
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ReceiveLocation { full_name: String, age: u8 }
|
||||
|
||||
impl HandlerFactory for State {
|
||||
type Out = anyhow::Result<()>;
|
||||
|
||||
fn handler() -> dptree::Handler<'static, DependencyMap, anyhow::Result<()>> {
|
||||
dptree::entry()
|
||||
.branch(
|
||||
dptree::filter(|dialogue: Dialogue<State, Store>| async move {
|
||||
let state = match dialogue.current_state_or_default().await {
|
||||
Ok(state) => state,
|
||||
Err(_) => return false,
|
||||
};
|
||||
match state { State::Start => true, _ => false }
|
||||
}).endpoint(handle_start)
|
||||
)
|
||||
.branch(
|
||||
dptree::filter(|dialogue: Dialogue<State, Store>| async move {
|
||||
let state = match dialogue.current_state_or_default().await {
|
||||
Ok(state) => state,
|
||||
Err(_) => return false,
|
||||
};
|
||||
match state { State::ReceiveFullName => true, _ => false }
|
||||
}).endpoint(handle_receive_full_name)
|
||||
)
|
||||
.branch(
|
||||
dptree::filter_map(|dialogue: Dialogue<State, Store>| async move {
|
||||
let state = dialogue.current_state_or_default().await.ok()?;
|
||||
match state { State::ReceiveAge(arg) => Some(arg), _ => None }
|
||||
}).endpoint(handle_receive_age)
|
||||
)
|
||||
.branch(
|
||||
dptree::filter_map(|dialogue: Dialogue<State, Store>| async move {
|
||||
let state = dialogue.current_state_or_default().await.ok()?;
|
||||
match state { State::ReceiveLocation(arg) => Some(arg), _ => None }
|
||||
}).endpoint(handle_receive_location)
|
||||
)
|
||||
}
|
||||
pub struct ReceiveLocation {
|
||||
full_name: String,
|
||||
age: u8,
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
|
@ -89,9 +63,7 @@ async fn main() {
|
|||
|
||||
Dispatcher::new(bot)
|
||||
.dependencies(dptree::deps![storage])
|
||||
.messages_handler(|h| {
|
||||
h.add_dialogue::<Message, Store, State>().dispatch_by::<State>()
|
||||
})
|
||||
.messages_handler(|h| h.add_dialogue::<Message, Store, State>().dispatch_by::<State>())
|
||||
.dispatch()
|
||||
.await;
|
||||
}
|
||||
|
@ -134,10 +106,11 @@ async fn handle_receive_location(
|
|||
bot: MyBot,
|
||||
mes: Message,
|
||||
dialogue: BotDialogue,
|
||||
state: ReceiveLocation
|
||||
state: ReceiveLocation,
|
||||
) -> anyhow::Result<()> {
|
||||
let location = mes.text().unwrap();
|
||||
let message = format!("Full name: {}\nAge: {}\nLocation: {}", state.full_name, state.age, location);
|
||||
let message =
|
||||
format!("Full name: {}\nAge: {}\nLocation: {}", state.full_name, state.age, location);
|
||||
bot.send_message(mes.chat_id(), message).await?;
|
||||
dialogue.exit().await?;
|
||||
Ok(())
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
use crate::{types::Message, utils::command::BotCommand};
|
||||
use dptree::{
|
||||
Handler,
|
||||
};
|
||||
use crate::dispatching2::HandlerFactory;
|
||||
use dptree::di::DependencyMap;
|
||||
use crate::{dispatching2::HandlerFactory, types::Message, utils::command::BotCommand};
|
||||
use dptree::{di::DependencyMap, Handler};
|
||||
|
||||
pub trait HandlerExt<Output> {
|
||||
fn add_command<C>(self, bot_name: String) -> Self
|
||||
|
@ -11,7 +7,8 @@ pub trait HandlerExt<Output> {
|
|||
C: BotCommand + Send + Sync + 'static;
|
||||
|
||||
fn dispatch_by<F>(self) -> Self
|
||||
where F: HandlerFactory<Out = Output>;
|
||||
where
|
||||
F: HandlerFactory<Out = Output>;
|
||||
}
|
||||
|
||||
impl<Output> HandlerExt<Output> for Handler<'static, DependencyMap, Output>
|
||||
|
@ -28,7 +25,10 @@ where
|
|||
}))
|
||||
}
|
||||
|
||||
fn dispatch_by<F>(self) -> Self where F: HandlerFactory<Out = Output> {
|
||||
fn dispatch_by<F>(self) -> Self
|
||||
where
|
||||
F: HandlerFactory<Out = Output>,
|
||||
{
|
||||
self.chain(F::handler())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use dptree::di::DependencyMap;
|
||||
use dptree::Handler;
|
||||
use dptree::{di::DependencyMap, Handler};
|
||||
|
||||
pub trait HandlerFactory {
|
||||
type Out;
|
||||
|
||||
fn handler() -> Handler<'static, DependencyMap, Self::Out>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,5 +6,5 @@ mod handler_ext;
|
|||
mod handler_factory;
|
||||
|
||||
pub use dispatcher::Dispatcher;
|
||||
pub use handler_factory::HandlerFactory;
|
||||
pub use handler_ext::HandlerExt;
|
||||
pub use handler_factory::HandlerFactory;
|
||||
|
|
|
@ -91,6 +91,9 @@ pub use teloxide_core::*;
|
|||
#[cfg_attr(all(docsrs, feature = "nightly"), doc(cfg(feature = "macros")))]
|
||||
pub use teloxide_macros as macros;
|
||||
|
||||
#[cfg_attr(all(docsrs, feature = "nightly"), doc(cfg(feature = "new-dispatching")))]
|
||||
#[cfg(feature = "new-dispatching")]
|
||||
pub use dptree;
|
||||
#[cfg_attr(all(docsrs, feature = "nightly"), doc(cfg(feature = "macros")))]
|
||||
#[cfg(feature = "macros")]
|
||||
pub use teloxide_macros::teloxide;
|
||||
|
|
|
@ -17,8 +17,7 @@ pub use crate::dispatching::{
|
|||
#[cfg(feature = "new-dispatching")]
|
||||
pub use crate::dispatching2::{
|
||||
dialogue::{Dialogue, DialogueHandlerExt as _},
|
||||
HandlerExt as _,
|
||||
Dispatcher,
|
||||
Dispatcher, HandlerExt as _,
|
||||
};
|
||||
|
||||
#[cfg_attr(all(docsrs, feature = "nightly"), doc(cfg(feature = "macros")))]
|
||||
|
|
64
tests/bot_dialogue.rs
Normal file
64
tests/bot_dialogue.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
#[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)]
|
||||
#[out(Result<(), teloxide::RequestError>)]
|
||||
#[store(teloxide::dispatching2::dialogue::InMemStorage<State>)]
|
||||
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)]
|
||||
#[out(Result<(), teloxide::RequestError>)]
|
||||
#[store(teloxide::dispatching2::dialogue::InMemStorage<State<X>>)]
|
||||
enum State<X: Clone + Send + Sync + 'static> {
|
||||
#[handler(handle_start)]
|
||||
Start,
|
||||
|
||||
#[handler(handle_have_data)]
|
||||
HaveData(X),
|
||||
}
|
||||
|
||||
impl<X: Clone + Send + Sync + 'static> Default for State<X> {
|
||||
fn default() -> Self {
|
||||
Self::Start
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_start() -> Result<(), teloxide::RequestError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_have_data() -> Result<(), teloxide::RequestError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue