一个功能齐全的框架,让你能使用Rust中的
亮点
-
函数式响应编程 teloxide遵循函数式响应设计模式,可以让您使用filters, maps, folds, zips和许多其他适配器声明式地操作来自电报的更新流。
-
对话管理子系统 我们的对话管理子系统易于使用,且对话的存储方式/位置是不可知的。例如,你可以只替换一行来实现永久保留。开箱即用的存储方式包括Redis和Sqlite。
-
强类型的机器人命令 你可以将机器人命令以枚举形式描述,然后它们将由字符串自动生成——像serve-json中的JSON结构和structopt中的命令行参数一样。
环境配置
-
下载Rust。
-
用@Botfather 创建一个新的机器人,得到一个格式为
123456789:blablabla 的认证凭证。 -
将环境变量:
TELOXIDE_TOKEN 数值初始化为你得到的认证凭证的哈希。
1 2 3 4 5 | # Unix-like $ export TELOXIDE_TOKEN=<Your token here> # Windows $ set TELOXIDE_TOKEN=<Your token here> |
-
确保你的 Rust 编译器是最新版:
1 2 3 4 5 6 7 | # 如果你在用稳定分支 $ rustup update stable $ rustup override set stable # 如果你在用每日构建分支 $ rustup update nightly $ rustup override set nightly |
-
运行
cargo new my_bot , 进入文件目录将以下内容放进Cargo.toml :
1 2 3 4 5 6 7 8 | [dependencies] teloxide = "0.3" teloxide-macros = "0.3" log = "0.4.8" pretty_env_logger = "0.4.0" tokio = { version = "0.2.11", features = ["rt-threaded", "macros"] } |
API 总览
骰子机器人
这个机器人会在每次收到消息时掷出随机骰子:
(完整)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | use teloxide::prelude::*; #[tokio::main] async fn main() { teloxide::enable_logging!(); log::info!("机器人启动中..."); let bot = Bot::from_env(); teloxide::repl(bot, |message| async move { message.answer_dice().send().await?; ResponseResult::<()>::Ok(()) }) .await; } |
命令
命令是强类型的声明式, 和我们在serde-json中使用 structopt 和JSON结构定义CLI类似,下列机器人接受以下命令
-
/username <你的用户名> -
/usernameandage <你的用户名> <你的年龄> -
/help
(完整)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | use teloxide::{utils::command::BotCommand, prelude::*}; #[derive(BotCommand)] #[command(rename = "lowercase", description = "These commands are supported:")] enum Command { #[command(description = "帮助文本内容")] Help, #[command(description = "处理用户名")] Username(String), #[command(description = "处理用户名和年龄", parse_with = "split")] UsernameAndAge { username: String, age: u8 }, } async fn answer(cx: UpdateWithCx<Message>, command: Command) -> ResponseResult<()> { match command { Command::Help => cx.answer(Command::descriptions()).send().await?, Command::Username(username) => { cx.answer_str(format!("你的用户名是 @{}.", username)).await? } Command::UsernameAndAge { username, age } => { cx.answer_str(format!("你的用户名是 @{} 年龄是 {}.", username, age)).await? } }; Ok(()) } #[tokio::main] async fn main() { teloxide::enable_logging!(); log::info!("命令机器人启动中..."); let bot = Bot::from_env(); let bot_name: String = panic!("机器人名称"); teloxide::commands_repl(bot, bot_name, answer).await; } |
对话管理
对话是由一个枚举来描述的,其中每个变体都是可能的对话状态之一。还有子过渡函数,将对话从一个状态转到另一个状态,从而形成一个有限状态机。
下面是一个机器人,它会问你三个问题,然后把答案反馈给你。首先,让我们从一个枚举(我们对话的状态集合)开始。
(dialogue_bot/src/dialogue/mod.rs)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 略去Imports... #[derive(Transition, From)] pub enum Dialogue { Start(StartState), ReceiveFullName(ReceiveFullNameState), ReceiveAge(ReceiveAgeState), ReceiveLocation(ReceiveLocationState), } impl Default for Dialogue { fn default() -> Self { Self::Start(StartState) } } |
当用户向我们的机器人发送消息,而这样的对话还不存在时,
Dialogue::Start
Dialogue::ReceiveFullName
Dialogue::ReceiveAge
Dialogue::ReceiveLocation
所有这些子过渡函数都接受一个相应的状态("对话 "的许多变体之一)、上下文或文本消息。它们返回
最后,
(dialogue_bot/src/main.rs)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // 略去Imports... #[tokio::main] async fn main() { teloxide::enable_logging!(); log::info!("对话机器人启动中"); let bot = Bot::from_env(); teloxide::dialogues_repl(bot, |message, dialogue| async move { handle_message(message, dialogue).await.expect("机器人出错!") }) .await; } async fn handle_message(cx: UpdateWithCx<Message>, dialogue: Dialogue) -> TransitionOut<Dialogue> { match cx.update.text_owned() { None => { cx.answer_str("Send me a text message.").await?; next(dialogue) } Some(ans) => dialogue.react(cx, ans).await, } } |