2019-09-02 09:28:56 +02:00
< div align = "center" >
2020-01-06 11:25:37 +01:00
< img src = "ICON.png" width = "250" / >
2019-12-07 20:30:15 +01:00
< h1 > teloxide< / h1 >
2019-10-15 07:53:51 +02:00
2019-12-07 20:30:15 +01:00
< a href = "https://docs.rs/teloxide/" >
2020-01-06 09:27:05 +01:00
< img src = "https://img.shields.io/badge/docs.rs-v0.1.0-blue.svg" >
2019-10-14 19:07:58 +02:00
< / a >
2019-12-08 07:57:53 +01:00
< a href = "https://github.com/teloxide/teloxide/actions" >
< img src = "https://github.com/teloxide/teloxide/workflows/Continuous%20integration/badge.svg" >
2019-09-02 09:28:56 +02:00
< / a >
2019-12-07 20:30:15 +01:00
< a href = "https://crates.io/crates/teloxide" >
2019-10-14 19:04:55 +02:00
< img src = "https://img.shields.io/badge/crates.io-v0.1.0-orange.svg" >
< / a >
2019-10-14 19:10:41 +02:00
2019-10-15 08:03:40 +02:00
A full-featured framework that empowers you to easily build [Telegram bots ](https://telegram.org/blog/bot-revolution ) using the [`async`/`.await` ](https://rust-lang.github.io/async-book/01_getting_started/01_chapter.html ) syntax in [Rust ](https://www.rust-lang.org/ ). It handles all the difficult stuff so you can focus only on your business logic.
2019-09-02 09:28:56 +02:00
< / div >
2020-01-26 22:36:36 +01:00
2020-02-13 21:19:08 +01:00
## Features
2020-02-13 23:25:42 +01:00
- **Type-safe.** teloxide leverages the Rust's type system with two serious implications: resistance to human mistakes and tight integration with IDEs. Write fast, avoid debugging as possible.
2020-02-13 23:47:53 +01:00
2020-02-13 23:47:38 +01:00
- **Persistency.** By default, teloxide stores all user dialogues in RAM, but you can store them somewhere else (for example, in DB) just by implementing 2 functions.
2020-02-13 23:58:36 +01:00
- **Convenient dialogues system.** Define a type-safe [finite automaton ](https://en.wikipedia.org/wiki/Finite-state_machine )
and transition functions to drive a user dialogue with ease (see the examples below).
2020-02-13 23:52:44 +01:00
2020-02-13 23:53:22 +01:00
- **Convenient API.** Automatic conversions are used to avoid boilerplate. For example, functions accept `Into<String>` , rather than `&str` or `String` , so you can call them without `.to_string()` /`.as_str()`/etc.
2020-02-13 21:19:08 +01:00
2020-02-12 11:17:20 +01:00
## Getting started
2020-02-12 11:20:15 +01:00
1. Create a new bot using [@Botfather ](https://t.me/botfather ) to get a token in the format `123456789:blablabla` .
2020-02-12 11:47:51 +01:00
2. Initialise the `TELOXIDE_TOKEN` environmental variable to your token:
```bash
2020-02-14 11:28:35 +01:00
# Unix
2020-02-12 11:49:59 +01:00
$ export TELOXIDE_TOKEN=MyAwesomeToken
2020-02-14 11:28:35 +01:00
# Windows
$ set TELOXITE_TOKEN=MyAwesomeToken
2020-02-12 11:47:51 +01:00
```
2020-02-12 16:37:57 +01:00
3. Be sure that you are up to date:
2020-02-12 11:09:53 +01:00
```bash
$ rustup update stable
2020-01-26 22:36:36 +01:00
```
2020-02-12 11:09:53 +01:00
2020-02-12 16:37:57 +01:00
4. Execute `cargo new my_bot` , enter the directory and put these lines into your `Cargo.toml` :
2020-01-26 22:36:36 +01:00
```toml
2020-02-12 11:09:53 +01:00
[dependencies]
2020-01-26 22:36:36 +01:00
teloxide = "0.1.0"
2020-02-12 17:00:54 +01:00
log = "0.4.8"
2020-02-12 18:21:29 +01:00
tokio = "0.2.11"
2020-02-13 18:08:50 +01:00
pretty_env_logger = "0.4.0"
2020-01-26 22:36:36 +01:00
```
2020-02-12 11:35:51 +01:00
## The ping-pong bot
2020-02-13 23:00:37 +01:00
This bot has a single message handler, which answers "pong" to each incoming message:
2020-02-13 18:07:28 +01:00
2020-02-13 21:19:08 +01:00
([Full](https://github.com/teloxide/teloxide/blob/dev/examples/ping_pong_bot/src/main.rs))
2020-01-26 22:36:36 +01:00
```rust
2020-02-12 11:17:20 +01:00
use teloxide::prelude::*;
2020-01-26 22:36:36 +01:00
#[tokio::main]
async fn main() {
2020-02-13 18:07:28 +01:00
teloxide::enable_logging!();
2020-02-13 18:27:07 +01:00
log::info!("Starting the ping-pong bot!");
2020-02-12 11:17:20 +01:00
2020-02-13 18:07:28 +01:00
let bot = Bot::from_env();
2020-02-12 11:47:51 +01:00
Dispatcher::< RequestError > ::new(bot)
2020-02-12 11:17:20 +01:00
.message_handler(& |ctx: DispatcherHandlerCtx< Message > | async move {
ctx.answer("pong").send().await?;
Ok(())
})
.dispatch()
.await;
2020-01-26 22:36:36 +01:00
}
```
2020-02-13 21:19:08 +01:00
2020-02-13 22:49:49 +01:00
## Commands
2020-02-13 23:40:17 +01:00
Commands are defined similar to how we define CLI using [structopt ](https://docs.rs/structopt/0.3.9/structopt/ ). This bot says "I am a cat! Meow!" on `/meow` , generates a random number within [0; 1) on `/generate` , and shows the usage guide on `/help` :
2020-02-13 22:49:49 +01:00
([Full](https://github.com/teloxide/teloxide/blob/dev/examples/simple_commands_bot/src/main.rs))
```rust
// Imports are omitted...
#[derive(BotCommand)]
#[command(rename = "lowercase", description = "These commands are supported:")]
enum Command {
#[command(description = "display this text.")]
Help,
#[command(description = "be a cat.")]
Meow,
2020-02-13 23:36:57 +01:00
#[command(description = "generate a random number within [0; 1).")]
Generate,
2020-02-13 22:49:49 +01:00
}
async fn handle_command(
ctx: DispatcherHandlerCtx< Message > ,
) -> Result< (), RequestError> {
let text = match ctx.update.text() {
Some(text) => text,
None => {
log::info!("Received a message, but not text.");
2020-02-13 23:36:57 +01:00
return Ok(());
2020-02-13 22:49:49 +01:00
}
};
let command = match Command::parse(text) {
Some((command, _)) => command,
None => {
log::info!("Received a text message, but not a command.");
2020-02-13 23:36:57 +01:00
return Ok(());
2020-02-13 22:49:49 +01:00
}
};
match command {
Command::Help => ctx.answer(Command::descriptions()).send().await?,
2020-02-13 23:36:57 +01:00
Command::Generate => {
ctx.answer(thread_rng().gen_range(0.0, 1.0).to_string())
.send()
.await?
}
2020-02-13 22:49:49 +01:00
Command::Meow => ctx.answer("I am a cat! Meow!").send().await?,
};
Ok(())
}
#[tokio::main]
async fn main() {
// Setup is omitted...
}
```
2020-02-13 21:19:08 +01:00
## Guess a number
Wanna see more? This is a bot, which starts a game on each incoming message. You must guess a number from 1 to 10 (inclusively):
([Full](https://github.com/teloxide/teloxide/blob/dev/examples/guess_a_number_bot/src/main.rs))
```rust
// Imports are omitted...
#[derive(SmartDefault)]
enum Dialogue {
#[default]
Start,
ReceiveAttempt(u8),
}
2020-02-14 10:57:14 +01:00
2020-02-13 21:19:08 +01:00
async fn handle_message(
ctx: DialogueHandlerCtx< Message , Dialogue > ,
) -> Result< DialogueStage < Dialogue > , RequestError> {
match ctx.dialogue {
Dialogue::Start => {
ctx.answer(
"Let's play a game! Guess a number from 1 to 10 (inclusively).",
)
.send()
.await?;
next(Dialogue::ReceiveAttempt(thread_rng().gen_range(1, 11)))
}
Dialogue::ReceiveAttempt(secret) => match ctx.update.text() {
None => {
ctx.answer("Oh, please, send me a text message!")
.send()
.await?;
next(ctx.dialogue)
}
Some(text) => match text.parse::< u8 > () {
Ok(attempt) => match attempt {
x if !(1..=10).contains(& x) => {
ctx.answer(
"Oh, please, send me a number in the range [1; \
10]!",
)
.send()
.await?;
next(ctx.dialogue)
}
x if x == secret => {
ctx.answer("Congratulations! You won!").send().await?;
exit()
}
_ => {
ctx.answer("No.").send().await?;
next(ctx.dialogue)
}
},
Err(_) => {
2020-02-13 23:11:29 +01:00
ctx.answer(
"Oh, please, send me a number in the range [1; 10]!",
)
.send()
.await?;
2020-02-13 21:19:08 +01:00
next(ctx.dialogue)
}
},
},
}
}
#[tokio::main]
async fn main() {
2020-02-14 08:31:42 +01:00
// Setup is omitted...
2020-02-13 21:19:08 +01:00
Dispatcher::new(bot)
.message_handler(& DialogueDispatcher::new(|ctx| async move {
handle_message(ctx)
.await
.expect("Something wrong with the bot!")
}))
.dispatch()
.await;
}
```
2020-02-13 21:22:04 +01:00
Our [finite automaton ](https://en.wikipedia.org/wiki/Finite-state_machine ), designating a user dialogue, cannot be in an invalid state. See [examples/dialogue_bot ](https://github.com/teloxide/teloxide/blob/dev/examples/dialogue_bot/src/main.rs ) to see a bit more complicated bot with dialogues.
2020-02-13 21:38:58 +01:00
2020-02-14 00:01:40 +01:00
[See more examples ](https://github.com/teloxide/teloxide/tree/dev/examples ).
2020-02-13 21:38:58 +01:00
## Recommendations
- Use this pattern:
```rust
#[tokio::main]
async fn main() {
run().await;
}
async fn run() {
// Your logic here...
}
```
Instead of this:
```rust
#[tokio::main]
async fn main() {
// Your logic here...
}
```
2020-02-14 10:53:14 +01:00
The second one produces very strange compiler messages because of the `#[tokio::main]` macro. However, the examples in this README use the second variant for brevity.
2020-02-14 09:45:12 +01:00
## Contributing
See [CONRIBUTING.md ](https://github.com/teloxide/teloxide/blob/dev/CONTRIBUTING.md ).