mirror of
https://github.com/teloxide/teloxide.git
synced 2025-01-03 17:52:12 +01:00
A nicer approach to manage dialogues via #[derive(Transition)] + #[teloxide(transition)]
This commit is contained in:
parent
840fe93b61
commit
bf114de249
12 changed files with 120 additions and 114 deletions
|
@ -7,8 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
## [0.3.0] - ???
|
## [0.3.0] - ???
|
||||||
### Added
|
### Added
|
||||||
- `BotBuilder`, which allows setting a default `ParseMode`.
|
- `BotBuilder`, which allows setting a default `ParseMode`.
|
||||||
- The `BotDialogue` trait.
|
- The `Transition`, `SubTransition`, `SubTransitionOutputType` traits.
|
||||||
- Automatic `dispatch` function generation via `#[derive(BotDialogue)]` + `#[transition(transition_fn)]`.
|
- A nicer approach to manage dialogues via `#[derive(Transition)]` + `#[teloxide(transition)]`.
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
- `Bot::{from_env_with_client, new, with_client}`.
|
- `Bot::{from_env_with_client, new, with_client}`.
|
||||||
|
|
35
README.md
35
README.md
|
@ -195,19 +195,12 @@ States and transition functions are placed into separated modules. For example,
|
||||||
```rust
|
```rust
|
||||||
// Imports are omitted...
|
// Imports are omitted...
|
||||||
|
|
||||||
#[derive(BotDialogue, SmartDefault, From)]
|
#[derive(Transition, SmartDefault, From)]
|
||||||
pub enum Dialogue {
|
pub enum Dialogue {
|
||||||
#[default]
|
#[default]
|
||||||
#[transition(start)]
|
|
||||||
Start(StartState),
|
Start(StartState),
|
||||||
|
|
||||||
#[transition(receive_days_of_week)]
|
|
||||||
ReceiveDaysOfWeek(ReceiveDaysOfWeekState),
|
ReceiveDaysOfWeek(ReceiveDaysOfWeekState),
|
||||||
|
|
||||||
#[transition(receive_10x5_answer)]
|
|
||||||
Receive10x5Answer(Receive10x5AnswerState),
|
Receive10x5Answer(Receive10x5AnswerState),
|
||||||
|
|
||||||
#[transition(receive_gandalf_alternative_name)]
|
|
||||||
ReceiveGandalfAlternativeName(ReceiveGandalfAlternativeNameState),
|
ReceiveGandalfAlternativeName(ReceiveGandalfAlternativeNameState),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,16 +221,10 @@ pub struct ReceiveGandalfAlternativeNameState {
|
||||||
_10x5_answer: u8,
|
_10x5_answer: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ExitState {
|
|
||||||
rest: ReceiveGandalfAlternativeNameState,
|
|
||||||
gandalf_alternative_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
up!(
|
up!(
|
||||||
StartState -> ReceiveDaysOfWeekState,
|
StartState -> ReceiveDaysOfWeekState,
|
||||||
ReceiveDaysOfWeekState + [days_of_week: u8] -> Receive10x5AnswerState,
|
ReceiveDaysOfWeekState + [days_of_week: u8] -> Receive10x5AnswerState,
|
||||||
Receive10x5AnswerState + [_10x5_answer: u8] -> ReceiveGandalfAlternativeNameState,
|
Receive10x5AnswerState + [_10x5_answer: u8] -> ReceiveGandalfAlternativeNameState,
|
||||||
ReceiveGandalfAlternativeNameState + [gandalf_alternative_name: String] -> ExitState,
|
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -249,15 +236,17 @@ The handy `up!` macro automatically generates functions that complete one state
|
||||||
|
|
||||||
pub type Out = TransitionOut<Dialogue>;
|
pub type Out = TransitionOut<Dialogue>;
|
||||||
|
|
||||||
pub async fn start(cx: TransitionIn, state: StartState) -> Out {
|
#[teloxide(transition)]
|
||||||
|
async fn start(state: StartState, cx: TransitionIn) -> Out {
|
||||||
cx.answer_str("Let's start our test! How many days per week are there?")
|
cx.answer_str("Let's start our test! How many days per week are there?")
|
||||||
.await?;
|
.await?;
|
||||||
next(state.up())
|
next(state.up())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn receive_days_of_week(
|
#[teloxide(transition)]
|
||||||
cx: TransitionIn,
|
async fn receive_days_of_week(
|
||||||
state: ReceiveDaysOfWeekState,
|
state: ReceiveDaysOfWeekState,
|
||||||
|
cx: TransitionIn,
|
||||||
) -> Out {
|
) -> Out {
|
||||||
match cx.update.text().map(str::parse) {
|
match cx.update.text().map(str::parse) {
|
||||||
Some(Ok(ans)) if ans == 7 => {
|
Some(Ok(ans)) if ans == 7 => {
|
||||||
|
@ -271,9 +260,10 @@ pub async fn receive_days_of_week(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn receive_10x5_answer(
|
#[teloxide(transition)]
|
||||||
cx: TransitionIn,
|
async fn receive_10x5_answer(
|
||||||
state: Receive10x5AnswerState,
|
state: Receive10x5AnswerState,
|
||||||
|
cx: TransitionIn,
|
||||||
) -> Out {
|
) -> Out {
|
||||||
match cx.update.text().map(str::parse) {
|
match cx.update.text().map(str::parse) {
|
||||||
Some(Ok(ans)) if ans == 50 => {
|
Some(Ok(ans)) if ans == 50 => {
|
||||||
|
@ -287,9 +277,10 @@ pub async fn receive_10x5_answer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn receive_gandalf_alternative_name(
|
#[teloxide(transition)]
|
||||||
cx: TransitionIn,
|
async fn receive_gandalf_alternative_name(
|
||||||
state: ReceiveGandalfAlternativeNameState,
|
state: ReceiveGandalfAlternativeNameState,
|
||||||
|
cx: TransitionIn,
|
||||||
) -> Out {
|
) -> Out {
|
||||||
match cx.update.text() {
|
match cx.update.text() {
|
||||||
Some(ans) if ans == "Mithrandir" => {
|
Some(ans) if ans == "Mithrandir" => {
|
||||||
|
@ -327,7 +318,7 @@ async fn main() {
|
||||||
input
|
input
|
||||||
.dialogue
|
.dialogue
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.dispatch(input.cx)
|
.react(input.cx)
|
||||||
.await
|
.await
|
||||||
.expect("Something wrong with the bot!")
|
.expect("Something wrong with the bot!")
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
// This is a bot that asks your full name, your age, your favourite kind of
|
// This is a bot that asks you three questions, e.g. a simple test.
|
||||||
// music and sends all the gathered information back.
|
|
||||||
//
|
//
|
||||||
// # Example
|
// # Example
|
||||||
// ```
|
// ```
|
||||||
// - Let's start! First, what's your full name?
|
// - Let's start our test! How many days per week are there?
|
||||||
// - Luke Skywalker
|
// - 7
|
||||||
// - What a wonderful name! Your age?
|
// - 10*5 = ?
|
||||||
// - 26
|
// - 50
|
||||||
// - Good. Now choose your favourite music
|
// - What's an alternative name of Gandalf?
|
||||||
// *A keyboard of music kinds is displayed*
|
// - Mithrandir
|
||||||
// *You select Metal*
|
// - Congratulations! You've successfully passed the test!
|
||||||
// - Metal
|
|
||||||
// - Fine. Your full name: Luke Skywalker, your age: 26, your favourite music: Metal
|
|
||||||
// ```
|
// ```
|
||||||
|
|
||||||
#![allow(clippy::trivial_regex)]
|
#![allow(clippy::trivial_regex)]
|
||||||
|
@ -21,8 +18,6 @@
|
||||||
extern crate smart_default;
|
extern crate smart_default;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate derive_more;
|
extern crate derive_more;
|
||||||
#[macro_use]
|
|
||||||
extern crate teloxide_macros;
|
|
||||||
|
|
||||||
mod states;
|
mod states;
|
||||||
mod transitions;
|
mod transitions;
|
||||||
|
@ -50,7 +45,7 @@ async fn run() {
|
||||||
input
|
input
|
||||||
.dialogue
|
.dialogue
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.dispatch(input.cx)
|
.react(input.cx)
|
||||||
.await
|
.await
|
||||||
.expect("Something wrong with the bot!")
|
.expect("Something wrong with the bot!")
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,23 +1,12 @@
|
||||||
use teloxide::prelude::*;
|
use teloxide::prelude::*;
|
||||||
|
use teloxide_macros::Transition;
|
||||||
|
|
||||||
use super::transitions::{
|
#[derive(Transition, SmartDefault, From)]
|
||||||
receive_10x5_answer, receive_days_of_week,
|
|
||||||
receive_gandalf_alternative_name, start,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(BotDialogue, SmartDefault, From)]
|
|
||||||
pub enum Dialogue {
|
pub enum Dialogue {
|
||||||
#[default]
|
#[default]
|
||||||
#[transition(start)]
|
|
||||||
Start(StartState),
|
Start(StartState),
|
||||||
|
|
||||||
#[transition(receive_days_of_week)]
|
|
||||||
ReceiveDaysOfWeek(ReceiveDaysOfWeekState),
|
ReceiveDaysOfWeek(ReceiveDaysOfWeekState),
|
||||||
|
|
||||||
#[transition(receive_10x5_answer)]
|
|
||||||
Receive10x5Answer(Receive10x5AnswerState),
|
Receive10x5Answer(Receive10x5AnswerState),
|
||||||
|
|
||||||
#[transition(receive_gandalf_alternative_name)]
|
|
||||||
ReceiveGandalfAlternativeName(ReceiveGandalfAlternativeNameState),
|
ReceiveGandalfAlternativeName(ReceiveGandalfAlternativeNameState),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,14 +27,8 @@ pub struct ReceiveGandalfAlternativeNameState {
|
||||||
_10x5_answer: u8,
|
_10x5_answer: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ExitState {
|
|
||||||
rest: ReceiveGandalfAlternativeNameState,
|
|
||||||
gandalf_alternative_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
up!(
|
up!(
|
||||||
StartState -> ReceiveDaysOfWeekState,
|
StartState -> ReceiveDaysOfWeekState,
|
||||||
ReceiveDaysOfWeekState + [days_of_week: u8] -> Receive10x5AnswerState,
|
ReceiveDaysOfWeekState + [days_of_week: u8] -> Receive10x5AnswerState,
|
||||||
Receive10x5AnswerState + [_10x5_answer: u8] -> ReceiveGandalfAlternativeNameState,
|
Receive10x5AnswerState + [_10x5_answer: u8] -> ReceiveGandalfAlternativeNameState,
|
||||||
ReceiveGandalfAlternativeNameState + [gandalf_alternative_name: String] -> ExitState,
|
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,19 +2,23 @@ use crate::states::{
|
||||||
Dialogue, Receive10x5AnswerState, ReceiveDaysOfWeekState,
|
Dialogue, Receive10x5AnswerState, ReceiveDaysOfWeekState,
|
||||||
ReceiveGandalfAlternativeNameState, StartState,
|
ReceiveGandalfAlternativeNameState, StartState,
|
||||||
};
|
};
|
||||||
|
|
||||||
use teloxide::prelude::*;
|
use teloxide::prelude::*;
|
||||||
|
use teloxide_macros::teloxide;
|
||||||
|
|
||||||
pub type Out = TransitionOut<Dialogue>;
|
pub type Out = TransitionOut<Dialogue>;
|
||||||
|
|
||||||
pub async fn start(cx: TransitionIn, state: StartState) -> Out {
|
#[teloxide(transition)]
|
||||||
|
async fn start(state: StartState, cx: TransitionIn) -> Out {
|
||||||
cx.answer_str("Let's start our test! How many days per week are there?")
|
cx.answer_str("Let's start our test! How many days per week are there?")
|
||||||
.await?;
|
.await?;
|
||||||
next(state.up())
|
next(state.up())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn receive_days_of_week(
|
#[teloxide(transition)]
|
||||||
cx: TransitionIn,
|
async fn receive_days_of_week(
|
||||||
state: ReceiveDaysOfWeekState,
|
state: ReceiveDaysOfWeekState,
|
||||||
|
cx: TransitionIn,
|
||||||
) -> Out {
|
) -> Out {
|
||||||
match cx.update.text().map(str::parse) {
|
match cx.update.text().map(str::parse) {
|
||||||
Some(Ok(ans)) if ans == 7 => {
|
Some(Ok(ans)) if ans == 7 => {
|
||||||
|
@ -28,9 +32,10 @@ pub async fn receive_days_of_week(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn receive_10x5_answer(
|
#[teloxide(transition)]
|
||||||
cx: TransitionIn,
|
async fn receive_10x5_answer(
|
||||||
state: Receive10x5AnswerState,
|
state: Receive10x5AnswerState,
|
||||||
|
cx: TransitionIn,
|
||||||
) -> Out {
|
) -> Out {
|
||||||
match cx.update.text().map(str::parse) {
|
match cx.update.text().map(str::parse) {
|
||||||
Some(Ok(ans)) if ans == 50 => {
|
Some(Ok(ans)) if ans == 50 => {
|
||||||
|
@ -44,9 +49,10 @@ pub async fn receive_10x5_answer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn receive_gandalf_alternative_name(
|
#[teloxide(transition)]
|
||||||
cx: TransitionIn,
|
async fn receive_gandalf_alternative_name(
|
||||||
state: ReceiveGandalfAlternativeNameState,
|
state: ReceiveGandalfAlternativeNameState,
|
||||||
|
cx: TransitionIn,
|
||||||
) -> Out {
|
) -> Out {
|
||||||
match cx.update.text() {
|
match cx.update.text() {
|
||||||
Some(ans) if ans == "Mithrandir" => {
|
Some(ans) if ans == "Mithrandir" => {
|
||||||
|
|
|
@ -39,7 +39,7 @@ async fn run() {
|
||||||
let (cx, dialogue) = input.unpack();
|
let (cx, dialogue) = input.unpack();
|
||||||
|
|
||||||
dialogue
|
dialogue
|
||||||
.dispatch(cx)
|
.react(cx)
|
||||||
.await
|
.await
|
||||||
.expect("Something is wrong with the bot!")
|
.expect("Something is wrong with the bot!")
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
use teloxide::prelude::*;
|
use teloxide::prelude::*;
|
||||||
use teloxide_macros::BotDialogue;
|
use teloxide_macros::Transition;
|
||||||
|
|
||||||
use super::transitions::{have_number, start};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -18,12 +16,9 @@ up!(
|
||||||
StartState + [number: i32] -> HaveNumberState,
|
StartState + [number: i32] -> HaveNumberState,
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(BotDialogue, SmartDefault, From, Serialize, Deserialize)]
|
#[derive(Transition, SmartDefault, From, Serialize, Deserialize)]
|
||||||
pub enum Dialogue {
|
pub enum Dialogue {
|
||||||
#[default]
|
#[default]
|
||||||
#[transition(start)]
|
|
||||||
Start(StartState),
|
Start(StartState),
|
||||||
|
|
||||||
#[transition(have_number)]
|
|
||||||
HaveNumber(HaveNumberState),
|
HaveNumber(HaveNumberState),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use teloxide::prelude::*;
|
use teloxide::prelude::*;
|
||||||
|
use teloxide_macros::teloxide;
|
||||||
|
|
||||||
use super::states::*;
|
use super::states::*;
|
||||||
|
|
||||||
|
@ -17,7 +18,8 @@ macro_rules! extract_text {
|
||||||
|
|
||||||
pub type Out = TransitionOut<Dialogue>;
|
pub type Out = TransitionOut<Dialogue>;
|
||||||
|
|
||||||
pub async fn start(cx: TransitionIn, state: StartState) -> Out {
|
#[teloxide(transition)]
|
||||||
|
async fn start(state: StartState, cx: TransitionIn) -> Out {
|
||||||
let text = extract_text!(cx);
|
let text = extract_text!(cx);
|
||||||
|
|
||||||
if let Ok(number) = text.parse() {
|
if let Ok(number) = text.parse() {
|
||||||
|
@ -33,7 +35,8 @@ pub async fn start(cx: TransitionIn, state: StartState) -> Out {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn have_number(cx: TransitionIn, state: HaveNumberState) -> Out {
|
#[teloxide(transition)]
|
||||||
|
async fn have_number(state: HaveNumberState, cx: TransitionIn) -> Out {
|
||||||
let text = extract_text!(cx);
|
let text = extract_text!(cx);
|
||||||
let num = state.number;
|
let num = state.number;
|
||||||
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
use crate::dispatching::dialogue::{TransitionIn, TransitionOut};
|
|
||||||
use futures::future::BoxFuture;
|
|
||||||
|
|
||||||
/// Represents a dialogue FSM.
|
|
||||||
pub trait BotDialogue: Default {
|
|
||||||
/// Turns itself into another state, depending on the input message.
|
|
||||||
fn dispatch(
|
|
||||||
self,
|
|
||||||
cx: TransitionIn,
|
|
||||||
) -> BoxFuture<'static, TransitionOut<Self>>;
|
|
||||||
}
|
|
|
@ -2,9 +2,11 @@
|
||||||
//!
|
//!
|
||||||
//! There are three main components:
|
//! There are three main components:
|
||||||
//!
|
//!
|
||||||
//! 1. Your type `D` (typically an enumeration), implementing [`BotDialogue`].
|
//! 1. Your type `D` (typically an enumeration), implementing [`Transition`].
|
||||||
//! It is essentially a [FSM]: its variants are possible dialogue states and
|
//! It is essentially a [FSM]: its variants are possible dialogue states and
|
||||||
//! [`BotDialogue::dispatch`] is a transition function.
|
//! [`Transition::react`] is a transition function.
|
||||||
|
//!
|
||||||
|
//! 2. State types, forming `D`. They implement [`SubTransition`].
|
||||||
//!
|
//!
|
||||||
//! 2. [`Storage<D>`], which encapsulates all the dialogues.
|
//! 2. [`Storage<D>`], which encapsulates all the dialogues.
|
||||||
//!
|
//!
|
||||||
|
@ -30,7 +32,7 @@
|
||||||
//! use std::convert::Infallible;
|
//! use std::convert::Infallible;
|
||||||
//!
|
//!
|
||||||
//! use teloxide::prelude::*;
|
//! use teloxide::prelude::*;
|
||||||
//! use teloxide_macros::BotDialogue;
|
//! use teloxide_macros::{teloxide, Transition};
|
||||||
//!
|
//!
|
||||||
//! struct _1State;
|
//! struct _1State;
|
||||||
//! struct _2State;
|
//! struct _2State;
|
||||||
|
@ -38,23 +40,25 @@
|
||||||
//!
|
//!
|
||||||
//! type Out = TransitionOut<D>;
|
//! type Out = TransitionOut<D>;
|
||||||
//!
|
//!
|
||||||
//! async fn _1_transition(_cx: TransitionIn, _state: _1State) -> Out {
|
//! #[teloxide(transition)]
|
||||||
//! todo!()
|
//! async fn _1_transition(_state: _1State, _cx: TransitionIn) -> Out {
|
||||||
//! }
|
|
||||||
//! async fn _2_transition(_cx: TransitionIn, _state: _2State) -> Out {
|
|
||||||
//! todo!()
|
|
||||||
//! }
|
|
||||||
//! async fn _3_transition(_cx: TransitionIn, _state: _3State) -> Out {
|
|
||||||
//! todo!()
|
//! todo!()
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! #[derive(BotDialogue)]
|
//! #[teloxide(transition)]
|
||||||
|
//! async fn _2_transition(_state: _2State, _cx: TransitionIn) -> Out {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[teloxide(transition)]
|
||||||
|
//! async fn _3_transition(_state: _3State, _cx: TransitionIn) -> Out {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[derive(Transition)]
|
||||||
//! enum D {
|
//! enum D {
|
||||||
//! #[transition(_1_transition)]
|
|
||||||
//! _1(_1State),
|
//! _1(_1State),
|
||||||
//! #[transition(_2_transition)]
|
|
||||||
//! _2(_2State),
|
//! _2(_2State),
|
||||||
//! #[transition(_3_transition)]
|
|
||||||
//! _3(_3State),
|
//! _3(_3State),
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
|
@ -94,9 +98,10 @@
|
||||||
//!
|
//!
|
||||||
//! See [examples/dialogue_bot] as a real example.
|
//! See [examples/dialogue_bot] as a real example.
|
||||||
//!
|
//!
|
||||||
//! [`BotDialogue`]: crate::dispatching::dialogue::BotDialogue
|
//! [`Transition`]: crate::dispatching::dialogue::Transition
|
||||||
//! [`BotDialogue::dispatch`]:
|
//! [`SubTransition`]: crate::dispatching::dialogue::SubTransition
|
||||||
//! crate::dispatching::dialogue::BotDialogue::dispatch
|
//! [`Transition::react`]:
|
||||||
|
//! crate::dispatching::dialogue::Transition::react
|
||||||
//! [FSM]: https://en.wikipedia.org/wiki/Finite-state_machine
|
//! [FSM]: https://en.wikipedia.org/wiki/Finite-state_machine
|
||||||
//!
|
//!
|
||||||
//! [`Storage<D>`]: crate::dispatching::dialogue::Storage
|
//! [`Storage<D>`]: crate::dispatching::dialogue::Storage
|
||||||
|
@ -122,26 +127,27 @@
|
||||||
|
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
mod bot_dialogue;
|
|
||||||
mod dialogue_dispatcher;
|
mod dialogue_dispatcher;
|
||||||
mod dialogue_dispatcher_handler;
|
mod dialogue_dispatcher_handler;
|
||||||
mod dialogue_stage;
|
mod dialogue_stage;
|
||||||
mod dialogue_with_cx;
|
mod dialogue_with_cx;
|
||||||
mod get_chat_id;
|
mod get_chat_id;
|
||||||
mod storage;
|
mod storage;
|
||||||
|
mod transition;
|
||||||
|
|
||||||
use crate::{requests::ResponseResult, types::Message};
|
|
||||||
pub use bot_dialogue::BotDialogue;
|
|
||||||
pub use dialogue_dispatcher::DialogueDispatcher;
|
pub use dialogue_dispatcher::DialogueDispatcher;
|
||||||
pub use dialogue_dispatcher_handler::DialogueDispatcherHandler;
|
pub use dialogue_dispatcher_handler::DialogueDispatcherHandler;
|
||||||
pub use dialogue_stage::{exit, next, DialogueStage};
|
pub use dialogue_stage::{exit, next, DialogueStage};
|
||||||
pub use dialogue_with_cx::DialogueWithCx;
|
pub use dialogue_with_cx::DialogueWithCx;
|
||||||
pub use get_chat_id::GetChatId;
|
pub use get_chat_id::GetChatId;
|
||||||
|
pub use transition::{
|
||||||
|
SubTransition, SubTransitionOutputType, Transition, TransitionIn,
|
||||||
|
TransitionOut,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "redis-storage")]
|
#[cfg(feature = "redis-storage")]
|
||||||
pub use storage::{RedisStorage, RedisStorageError};
|
pub use storage::{RedisStorage, RedisStorageError};
|
||||||
|
|
||||||
use crate::dispatching::UpdateWithCx;
|
|
||||||
pub use storage::{serializer, InMemStorage, Serializer, Storage};
|
pub use storage::{serializer, InMemStorage, Serializer, Storage};
|
||||||
|
|
||||||
/// Generates `.up(field)` methods for dialogue states.
|
/// Generates `.up(field)` methods for dialogue states.
|
||||||
|
@ -192,9 +198,3 @@ macro_rules! up {
|
||||||
)+
|
)+
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An input passed into a FSM transition function.
|
|
||||||
pub type TransitionIn = UpdateWithCx<Message>;
|
|
||||||
|
|
||||||
/// A type returned from a FSM transition function.
|
|
||||||
pub type TransitionOut<D> = ResponseResult<DialogueStage<D>>;
|
|
||||||
|
|
44
src/dispatching/dialogue/transition.rs
Normal file
44
src/dispatching/dialogue/transition.rs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
use crate::{
|
||||||
|
dispatching::{dialogue::DialogueStage, UpdateWithCx},
|
||||||
|
requests::ResponseResult,
|
||||||
|
types::Message,
|
||||||
|
};
|
||||||
|
use futures::future::BoxFuture;
|
||||||
|
|
||||||
|
/// Represents a transition function of a dialogue FSM.
|
||||||
|
pub trait Transition: Sized {
|
||||||
|
/// Turns itself into another state, depending on the input message.
|
||||||
|
fn react(self, cx: TransitionIn)
|
||||||
|
-> BoxFuture<'static, TransitionOut<Self>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Like [`Transition`], but from `StateN` -> `Dialogue`.
|
||||||
|
///
|
||||||
|
/// [`Transition`]: crate::dispatching::dialogue::Transition
|
||||||
|
pub trait SubTransition<Dialogue>
|
||||||
|
where
|
||||||
|
Dialogue: Transition,
|
||||||
|
{
|
||||||
|
/// Turns itself into another state, depending on the input message.
|
||||||
|
fn react(
|
||||||
|
self,
|
||||||
|
cx: TransitionIn,
|
||||||
|
) -> BoxFuture<'static, TransitionOut<Dialogue>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A type returned from a FSM subtransition function.
|
||||||
|
///
|
||||||
|
/// Now it is used only inside `#[teloxide(transition)]` for type inference.
|
||||||
|
pub trait SubTransitionOutputType {
|
||||||
|
type Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> SubTransitionOutputType for TransitionOut<D> {
|
||||||
|
type Output = D;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An input passed into a FSM (sub)transition function.
|
||||||
|
pub type TransitionIn = UpdateWithCx<Message>;
|
||||||
|
|
||||||
|
/// A type returned from a FSM (sub)transition function.
|
||||||
|
pub type TransitionOut<D> = ResponseResult<DialogueStage<D>>;
|
|
@ -3,8 +3,8 @@
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
dispatching::{
|
dispatching::{
|
||||||
dialogue::{
|
dialogue::{
|
||||||
exit, next, BotDialogue, DialogueDispatcher, DialogueStage,
|
exit, next, DialogueDispatcher, DialogueStage, DialogueWithCx,
|
||||||
DialogueWithCx, GetChatId, TransitionIn, TransitionOut,
|
GetChatId, Transition, TransitionIn, TransitionOut,
|
||||||
},
|
},
|
||||||
Dispatcher, DispatcherHandlerRx, DispatcherHandlerRxExt, UpdateWithCx,
|
Dispatcher, DispatcherHandlerRx, DispatcherHandlerRxExt, UpdateWithCx,
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue