域名 AXUM.RS 将于2025年10月到期。我们无意再对其进行续费,我们希望你能够接续这个域名,让更多 AXUM 开发者继续受益。
  • 方案1️⃣AXUM.RS 域名 = 3000
  • 方案2️⃣方案1️⃣ + 本站所有专题原始 Markdown 文档 = 5000
  • 方案3️⃣方案2️⃣ + 本站原始数据库 = 5500
如果你有意接续这份 AXUM 情怀,请与我们取得联系。
说明:
  1. 如果有人购买 AXUM.RS 域名(方案1️⃣),或者该域名到期,本站将启用新的免费域名继续提供服务。
  2. 如果有人购买了 AXUM.RS 域名,且同时购买了内容和/或数据库(方案2️⃣/方案3️⃣),本站将关闭。届时我们或许会以另一种方式与你再相遇。

处理指令

本章我们将学习如何处理 Telegram 的“指令”(command)。开始之前,我们对之前的代码进行必要的封装。

本章代码在03/处理指令分支。

封装错误类型

先从封装自定义错误开始:

// src/error.rs

#[derive(Debug)]
pub enum AppErrorType {
    HttpError,
    SerdeError,
}
#[derive(Debug)]
pub struct AppError {
    pub message: Option<String>,
    pub cause: Option<String>,
    pub error_type: AppErrorType,
}

// impl AppError ...

接着定义自己的Result

// src/main.rs

type Result<T> = std::result::Result<T, error::AppError>;

没什么需要多说的,我们每个专题都要这些操作。

封装 Telegram API 请求

现在我们要将发送请求到 Telegram API 的部分封装成一个函数:

// src/bot.rs

async fn invoke_api<T: Serialize>(data: &T, method: &str, token: &str) -> Result<Response> {
    let api_addr = format!("https://api.telegram.org/bot{}/{}", token, method);
    let res = reqwest::Client::new()
        .post(&api_addr)
        .form(data)
        .send()
        .await
        .map_err(AppError::from)?
        .text()
        .await
        .map_err(AppError::from)?;
    let res = serde_json::from_str(&res).map_err(AppError::from)?;
    Ok(res)
}

pub async fn send_text_message(token: &str, chat_id: u64, text: String) -> Result<Response> {
    let data = request::TextMessage { chat_id, text };
    invoke_api(&data, "sendMessage", token).await
}
#[derive(Deserialize, Debug)]
pub struct Response {
    pub ok: bool,
    pub result: Option<Message>,
}
#[derive(Deserialize, Debug)]
pub struct Message {
    pub message_id: u64,
    pub from: Option<User>,
    pub chat: Chat,
    pub date: u64,
    pub text: Option<String>,
}

#[derive(Deserialize, Debug)]
pub struct User {
    pub id: u64,
    pub is_bot: bool,
    pub first_name: Option<String>,
    pub username: Option<String>,
    pub language_code: Option<String>,
}
#[derive(Deserialize, Debug)]
pub struct Chat {
    pub id: u64,
    pub first_name: Option<String>,
    pub username: Option<String>,
    #[serde(rename(deserialize = "type"))]
    pub types: String,
}

Telegram API 中,每个数据结构中的必填项并不多,所以我们需要为那些可选字段加上Option<>,以避免发生无法解析的错误。

关于 Telegram API 中各数据结构是否必填,请参见官方文档。文档中,所有标有Optional的,均为可选的。

关于 Telegram API 中各数据结构是否必填,请参见官方文档。文档中,所有标有Optional的,均为可选的。

处理指令

指令的格式

在 Telegram 中,指令都是以/开头,后面是有效的标识符,比如:/start/help 等。如内容介绍部分所说,我们最终要实现三个指令:

  • /website:访问 axum 中文网官方

  • /logo:查看 axum 中文网 LOGO

  • /help:显示帮助信息

/website:访问 axum 中文网官方

/logo:查看 axum 中文网 LOGO

/help:显示帮助信息

/website 指令

指令其实也是用户发送过来的文本消息,所以我们只要判断用户发送的文本消息是不是/website即可。

识别出指令

handler::hook()中,我们需要对用户输入的内容进行处理:

通过对用户输入的内容进行判断,如果是 /website,则从 command::website()中获取要返回给用户的内容,否则,返回我们之前写的echo的内容。

然后将内容通过 Telegram API 发送给用户。

command::website()很简单,只是返回我们的网站的地址:

pub fn website() -> String {
    "https://axum.rs".to_string()
}
要查看完整内容,请先登录