mirror of
https://github.com/teloxide/teloxide.git
synced 2025-01-05 10:24:32 +01:00
Update examples in the README
This commit is contained in:
parent
6ad7a5b0a2
commit
d48659b93e
1 changed files with 123 additions and 154 deletions
275
README.md
275
README.md
|
@ -81,11 +81,12 @@ tokio = { version = "1.8", features = ["rt-multi-thread", "macros"] }
|
||||||
## API overview
|
## API overview
|
||||||
|
|
||||||
### The dices bot
|
### The dices bot
|
||||||
|
|
||||||
This bot replies with a dice throw to each received message:
|
This bot replies with a dice throw to each received message:
|
||||||
|
|
||||||
([Full](./examples/dices_bot/src/main.rs))
|
([Full](./examples/dices.rs))
|
||||||
```rust,no_run
|
```rust,no_run
|
||||||
use teloxide::prelude::*;
|
use teloxide::prelude2::*;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
@ -94,8 +95,8 @@ async fn main() {
|
||||||
|
|
||||||
let bot = Bot::from_env().auto_send();
|
let bot = Bot::from_env().auto_send();
|
||||||
|
|
||||||
teloxide::repl(bot, |message| async move {
|
teloxide::repls2::repl(bot, |message: Message, bot: AutoSend<Bot>| async move {
|
||||||
message.answer_dice().await?;
|
bot.send_dice(message.chat.id).await?;
|
||||||
respond(())
|
respond(())
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -109,6 +110,7 @@ async fn main() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
### Commands
|
### Commands
|
||||||
|
|
||||||
Commands are strongly typed and defined declaratively, similar to how we define CLI using [structopt] and JSON structures in [serde-json]. The following bot accepts these commands:
|
Commands are strongly typed and defined declaratively, similar to how we define CLI using [structopt] and JSON structures in [serde-json]. The following bot accepts these commands:
|
||||||
|
|
||||||
- `/username <your username>`
|
- `/username <your username>`
|
||||||
|
@ -118,13 +120,14 @@ Commands are strongly typed and defined declaratively, similar to how we define
|
||||||
[structopt]: https://docs.rs/structopt/0.3.9/structopt/
|
[structopt]: https://docs.rs/structopt/0.3.9/structopt/
|
||||||
[serde-json]: https://github.com/serde-rs/json
|
[serde-json]: https://github.com/serde-rs/json
|
||||||
|
|
||||||
([Full](./examples/simple_commands_bot/src/main.rs))
|
([Full](./examples/simple_commands.rs))
|
||||||
|
|
||||||
```rust,no_run
|
```rust,no_run
|
||||||
use teloxide::{prelude::*, utils::command::BotCommand};
|
use teloxide::{prelude2::*, utils::command::BotCommand};
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
#[derive(BotCommand)]
|
#[derive(BotCommand, Clone)]
|
||||||
#[command(rename = "lowercase", description = "These commands are supported:")]
|
#[command(rename = "lowercase", description = "These commands are supported:")]
|
||||||
enum Command {
|
enum Command {
|
||||||
#[command(description = "display this text.")]
|
#[command(description = "display this text.")]
|
||||||
|
@ -136,16 +139,21 @@ enum Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn answer(
|
async fn answer(
|
||||||
cx: UpdateWithCx<AutoSend<Bot>, Message>,
|
bot: AutoSend<Bot>,
|
||||||
|
message: Message,
|
||||||
command: Command,
|
command: Command,
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
match command {
|
match command {
|
||||||
Command::Help => cx.answer(Command::descriptions()).await?,
|
Command::Help => bot.send_message(message.chat.id, Command::descriptions()).await?,
|
||||||
Command::Username(username) => {
|
Command::Username(username) => {
|
||||||
cx.answer(format!("Your username is @{}.", username)).await?
|
bot.send_message(message.chat.id, format!("Your username is @{}.", username)).await?
|
||||||
}
|
}
|
||||||
Command::UsernameAndAge { username, age } => {
|
Command::UsernameAndAge { username, age } => {
|
||||||
cx.answer(format!("Your username is @{} and age is {}.", username, age)).await?
|
bot.send_message(
|
||||||
|
message.chat.id,
|
||||||
|
format!("Your username is @{} and age is {}.", username, age),
|
||||||
|
)
|
||||||
|
.await?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -159,8 +167,7 @@ async fn main() {
|
||||||
|
|
||||||
let bot = Bot::from_env().auto_send();
|
let bot = Bot::from_env().auto_send();
|
||||||
|
|
||||||
let bot_name: String = panic!("Your bot's name here");
|
teloxide::repls2::commands_repl(bot, answer, Command::ty()).await;
|
||||||
teloxide::commands_repl(bot, bot_name, answer).await;
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -171,145 +178,41 @@ async fn main() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
### Dialogues management
|
### Dialogues management
|
||||||
A dialogue is described by an enumeration where each variant is one of possible dialogue's states. There are also _subtransition functions_, which turn a dialogue from one state to another, thereby forming an [FSM].
|
|
||||||
|
A dialogue is typically described by an enumeration where each variant is one of possible dialogue's states. There are also _state handler functions_, which may turn a dialogue from one state to another, thereby forming an [FSM].
|
||||||
|
|
||||||
[FSM]: https://en.wikipedia.org/wiki/Finite-state_machine
|
[FSM]: https://en.wikipedia.org/wiki/Finite-state_machine
|
||||||
|
|
||||||
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):
|
Below is a bot that asks you three questions and then sends the answers back to you:
|
||||||
|
|
||||||
|
([Full](examples/dialogue.rs))
|
||||||
|
|
||||||
([dialogue_bot/src/dialogue/mod.rs](examples/dialogue_bot/src/state/mod.rs))
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
// Imports are omitted...
|
use teloxide::{dispatching2::dialogue::InMemStorage, macros::DialogueState, prelude2::*};
|
||||||
|
|
||||||
#[derive(Transition, From)]
|
type MyDialogue = Dialogue<State, InMemStorage<State>>;
|
||||||
pub enum Dialogue {
|
|
||||||
Start(StartState),
|
#[derive(DialogueState, Clone)]
|
||||||
ReceiveFullName(ReceiveFullNameState),
|
#[handler_out(anyhow::Result<()>)]
|
||||||
ReceiveAge(ReceiveAgeState),
|
pub enum State {
|
||||||
ReceiveLocation(ReceiveLocationState),
|
#[handler(handle_start)]
|
||||||
|
Start,
|
||||||
|
|
||||||
|
#[handler(handle_receive_full_name)]
|
||||||
|
ReceiveFullName,
|
||||||
|
|
||||||
|
#[handler(handle_receive_age)]
|
||||||
|
ReceiveAge { full_name: String },
|
||||||
|
|
||||||
|
#[handler(handle_receive_location)]
|
||||||
|
ReceiveLocation { full_name: String, age: u8 },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Dialogue {
|
impl Default for State {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Start(StartState)
|
Self::Start
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
|
||||||
|
|
||||||
When a user sends a message to our bot and such a dialogue does not exist yet, a `Dialogue::default()` is invoked, which is a `Dialogue::Start` in this case. Every time a message is received, an associated dialogue is extracted and then passed to a corresponding subtransition function:
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Dialogue::Start</summary>
|
|
||||||
|
|
||||||
([dialogue_bot/src/dialogue/states/start.rs](examples/dialogue_bot/src/state/states/start.rs))
|
|
||||||
```rust,ignore
|
|
||||||
// Imports are omitted...
|
|
||||||
|
|
||||||
pub struct StartState;
|
|
||||||
|
|
||||||
#[teloxide(subtransition)]
|
|
||||||
async fn start(
|
|
||||||
_state: StartState,
|
|
||||||
cx: TransitionIn<AutoSend<Bot>>,
|
|
||||||
_ans: String,
|
|
||||||
) -> TransitionOut<Dialogue> {
|
|
||||||
cx.answer("Let's start! What's your full name?").await?;
|
|
||||||
next(ReceiveFullNameState)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Dialogue::ReceiveFullName</summary>
|
|
||||||
|
|
||||||
([dialogue_bot/src/dialogue/states/receive_full_name.rs](examples/dialogue_bot/src/state/states/receive_full_name.rs))
|
|
||||||
```rust,ignore
|
|
||||||
// Imports are omitted...
|
|
||||||
|
|
||||||
#[derive(Generic)]
|
|
||||||
pub struct ReceiveFullNameState;
|
|
||||||
|
|
||||||
#[teloxide(subtransition)]
|
|
||||||
async fn receive_full_name(
|
|
||||||
state: ReceiveFullNameState,
|
|
||||||
cx: TransitionIn<AutoSend<Bot>>,
|
|
||||||
ans: String,
|
|
||||||
) -> TransitionOut<Dialogue> {
|
|
||||||
cx.answer("How old are you?").await?;
|
|
||||||
next(ReceiveAgeState::up(state, ans))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Dialogue::ReceiveAge</summary>
|
|
||||||
|
|
||||||
([dialogue_bot/src/dialogue/states/receive_age.rs](examples/dialogue_bot/src/state/states/receive_age.rs))
|
|
||||||
```rust,ignore
|
|
||||||
// Imports are omitted...
|
|
||||||
|
|
||||||
#[derive(Generic)]
|
|
||||||
pub struct ReceiveAgeState {
|
|
||||||
pub full_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[teloxide(subtransition)]
|
|
||||||
async fn receive_age_state(
|
|
||||||
state: ReceiveAgeState,
|
|
||||||
cx: TransitionIn<AutoSend<Bot>>,
|
|
||||||
ans: String,
|
|
||||||
) -> TransitionOut<Dialogue> {
|
|
||||||
match ans.parse::<u8>() {
|
|
||||||
Ok(ans) => {
|
|
||||||
cx.answer("What's your location?").await?;
|
|
||||||
next(ReceiveLocationState::up(state, ans))
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
cx.answer("Send me a number.").await?;
|
|
||||||
next(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Dialogue::ReceiveLocation</summary>
|
|
||||||
|
|
||||||
([dialogue_bot/src/dialogue/states/receive_location.rs](examples/dialogue_bot/src/state/states/receive_location.rs))
|
|
||||||
```rust,ignore
|
|
||||||
// Imports are omitted...
|
|
||||||
|
|
||||||
#[derive(Generic)]
|
|
||||||
pub struct ReceiveLocationState {
|
|
||||||
pub full_name: String,
|
|
||||||
pub age: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[teloxide(subtransition)]
|
|
||||||
async fn receive_location(
|
|
||||||
state: ReceiveLocationState,
|
|
||||||
cx: TransitionIn<AutoSend<Bot>>,
|
|
||||||
ans: String,
|
|
||||||
) -> TransitionOut<Dialogue> {
|
|
||||||
cx.answer(format!("Full name: {}\nAge: {}\nLocation: {}", state.full_name, state.age, ans))
|
|
||||||
.await?;
|
|
||||||
exit()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
All these subtransition functions accept a corresponding state (one of the many variants of `Dialogue`), a context, and a textual message. They return `TransitionOut<Dialogue>`, e.g. a mapping from `<your state type>` to `Dialogue`.
|
|
||||||
|
|
||||||
Finally, the `main` function looks like this:
|
|
||||||
|
|
||||||
([dialogue_bot/src/main.rs](./examples/dialogue_bot/src/main.rs))
|
|
||||||
```rust,ignore
|
|
||||||
// Imports are omitted...
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
@ -318,23 +221,89 @@ async fn main() {
|
||||||
|
|
||||||
let bot = Bot::from_env().auto_send();
|
let bot = Bot::from_env().auto_send();
|
||||||
|
|
||||||
teloxide::dialogues_repl(bot, |message, dialogue| async move {
|
DispatcherBuilder::new(
|
||||||
handle_message(message, dialogue).await.expect("Something wrong with the bot!")
|
bot,
|
||||||
})
|
Update::filter_message()
|
||||||
|
.add_dialogue::<Message, InMemStorage<State>, State>()
|
||||||
|
.dispatch_by::<State>(),
|
||||||
|
)
|
||||||
|
.dependencies(dptree::deps![InMemStorage::<State>::new()])
|
||||||
|
.build()
|
||||||
|
.setup_ctrlc_handler()
|
||||||
|
.dispatch()
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_message(
|
async fn handle_start(
|
||||||
cx: UpdateWithCx<AutoSend<Bot>, Message>,
|
bot: AutoSend<Bot>,
|
||||||
dialogue: Dialogue,
|
msg: Message,
|
||||||
) -> TransitionOut<Dialogue> {
|
dialogue: MyDialogue,
|
||||||
match cx.update.text().map(ToOwned::to_owned) {
|
) -> anyhow::Result<()> {
|
||||||
|
bot.send_message(msg.chat_id(), "Let's start! What's your full name?").await?;
|
||||||
|
dialogue.update(State::ReceiveFullName).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_receive_full_name(
|
||||||
|
bot: AutoSend<Bot>,
|
||||||
|
msg: Message,
|
||||||
|
dialogue: MyDialogue,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
match msg.text() {
|
||||||
|
Some(text) => {
|
||||||
|
bot.send_message(msg.chat_id(), "How old are you?").await?;
|
||||||
|
dialogue.update(State::ReceiveAge { full_name: text.into() }).await?;
|
||||||
|
}
|
||||||
None => {
|
None => {
|
||||||
cx.answer("Send me a text message.").await?;
|
bot.send_message(msg.chat_id(), "Send me a text message.").await?;
|
||||||
next(dialogue)
|
|
||||||
}
|
}
|
||||||
Some(ans) => dialogue.react(cx, ans).await,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_receive_age(
|
||||||
|
bot: AutoSend<Bot>,
|
||||||
|
msg: Message,
|
||||||
|
dialogue: MyDialogue,
|
||||||
|
(full_name,): (String,),
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
match msg.text() {
|
||||||
|
Some(number) => match number.parse::<u8>() {
|
||||||
|
Ok(age) => {
|
||||||
|
bot.send_message(msg.chat_id(), "What's your location?").await?;
|
||||||
|
dialogue.update(State::ReceiveLocation { full_name, age }).await?;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
bot.send_message(msg.chat_id(), "Send me a number.").await?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
bot.send_message(msg.chat_id(), "Send me a text message.").await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_receive_location(
|
||||||
|
bot: AutoSend<Bot>,
|
||||||
|
msg: Message,
|
||||||
|
dialogue: MyDialogue,
|
||||||
|
(full_name, age): (String, u8),
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
match msg.text() {
|
||||||
|
Some(location) => {
|
||||||
|
let message = format!("Full name: {}\nAge: {}\nLocation: {}", full_name, age, location);
|
||||||
|
bot.send_message(msg.chat_id(), message).await?;
|
||||||
|
dialogue.exit().await?;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
bot.send_message(msg.chat_id(), "Send me a text message.").await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -344,7 +313,7 @@ async fn handle_message(
|
||||||
</kbd>
|
</kbd>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
[More examples!](./examples)
|
[More examples >>](./examples)
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
**Q: Where I can ask questions?**
|
**Q: Where I can ask questions?**
|
||||||
|
|
Loading…
Reference in a new issue