Expand description
§telegram_bot_oxidebot
telegram_bot_oxidebot 是 oxidebot 的 Telegram Bot API 适配器。
当前版本按 Telegram Bot API 10.2(2026-07-14) 核对。适配器只实现 oxidebot 通用接口所需的 Telegram API 子集,但接收更新时采用向前兼容设计:Telegram 新增字段会被保留,尚未映射为 oxidebot 标准事件的更新会作为原始事件交给上层,而不会导致整个 getUpdates 响应反序列化失败。
§主要能力
- 纯 Rustls HTTPS,不依赖 OpenSSL。
- 长轮询支持 offset 提交、失败指数退避、Telegram
retry_after限流等待和自定义allowed_updates。 - 支持 v2
OutgoingMessage:纯文本、UTF-16 实体富文本、图片、视频、音频、动画、语音、视频便笺、文件、相册、贴纸、自定义 emoji、位置、联系人、Poll/Quiz、Checklist 和 Bot API 10.2 Rich Message;支持 file_id、HTTP(S)、本地文件和 base64 上传。 - 自动遵守文本 4096、caption 1024、相册 2–10 项等 Telegram 约束;单个媒体不会再错误调用
sendMediaGroup。 - 支持消息发送/编辑/删除/批量删除、转发/复制、Reaction、Pin、Activity、Business Read Receipt、Poll 停止、Checklist 编辑、Forum Topic、邀请/订阅邀请、成员权限/标签/封禁、入群审批、群资料、用户资料和文件信息。
- 支持结构化命令的 Telegram 菜单降级、Inline Query suggestion、Chat Menu surface、Mini App query、localized bot profile、Invoice、Shipping、Checkout、Star refund。
- 将普通/频道/Business/Guest 消息、编辑、删除、成员、反应计数、Poll/Vote、Checklist、Topic、Pin、Inline Query、Mini App、支付和订阅映射为 v2
LifecycleEvent;同时保留旧MessageEvent/NoticeEvent。 - 完整映射 Telegram inline keyboard、reply keyboard、Callback Query、Bot Commands 和 Chat Menu Button;包括 Bot API 10.2 的按钮颜色、自定义 emoji 图标、Managed Bot 请求和 ephemeral command。
callback_query会成为Event::InteractionEvent;users_shared、chat_shared、managed_bot_created和web_app_data也会产生类型化的选择/表单交互事件。- 入站消息同时生成
MessageEnvelope,保留 rich content、forward/reply origin、topic、时间、reaction、pin 和完整 Telegram JSON;无法诚实映射的字段继续通过PlatformNativeData/TelegramRawEvent保留。 BotCapabilities逐项报告 Native、Emulated、Unsupported 和 Telegram 硬限制,不把“能发一条消息”误报成“支持全部富消息语义”。- 通过 oxidebot 的
PlatformApiRequest暴露全部 185 个 Bot API 10.2 方法;其他适配器不支持时会返回明确错误。 - 通过
TelegramBot::client()继续暴露更低层的TelegramClient::call,新发布且尚未进入方法目录的 Telegram API 也能立即调用。
§v2 公共模型映射
| oxidebot 能力 | Telegram Bot API 10.2 映射 | 边界 |
|---|---|---|
ConversationRef | private/group/channel、forum topic、direct-messages topic | list_threads 无官方 API |
RichText | MessageEntity / RichText,自动转换 UTF-8 byte range 与 UTF-16 offset | Regular Message 与 Rich Message 支持的 style 集合不同 |
RichLayout | sendRichMessage / editMessageText.rich_message | 不可表示的 layout style 明确报错 |
MessageOptions | reply/quote、silent、protect、link preview、ephemeral receiver、draft、keyboard、business/native fields | Telegram 不提供 portable mention allow-list、scheduled message 和 idempotency key |
InteractionResponseHandle | Callback Query ID + chat/message/inline 上下文 | Inline callback 没有 chat_id 时不能发送 follow-up |
| Reaction | setMessageReaction | 非 Premium bot 每条消息最多设置一个 reaction;不能列出 reaction users |
| Poll / Checklist | sendPoll、stopPoll、sendChecklist、editMessageChecklist | Checklist 仅 Connected Business 可发送/编辑 |
| Thread | Forum Topic create/edit/close/reopen/delete | Telegram 不支持枚举所有 topic |
| History | getUserPersonalChatMessages | 仅用户 Personal Chat,1–20 条;普通聊天历史不可查询 |
| Commands / suggestions | setMyCommands、answerInlineQuery | Telegram 命令菜单不注册 typed options;复杂 schema 返回错误 |
| Surface / Mini App | Chat Menu、answerWebAppQuery、init-data HMAC verification | 无 App Home / Sidebar / Rich Menu |
| Commerce | Invoice、Shipping、Pre-checkout、Star refund、Subscription update | Bot API 只能直接退款 Telegram Stars |
所有 MessageRef、ConversationRef、MessageTarget 和 option 类型都可以携带 PlatformNativeData::new("telegram", ...)。适配器会校验 platform,重复或错误平台字段会报错,不会覆盖 portable 字段。
§安装
cargo add telegram_bot_oxidebot§使用
use anyhow::Result;
use oxidebot::OxideBotManager;
use telegram_bot_oxidebot::{GetUpdatesConfig, TelegramBot};
async fn run_bot() -> Result<()> {
let config = GetUpdatesConfig {
// chat_member 和 message_reaction 默认不会由 Telegram 推送,
// 需要使用 allowed_updates 显式订阅;None 会沿用 Telegram 端设置。
allowed_updates: None,
..Default::default()
};
let bot = TelegramBot::try_new(std::env::var("TELEGRAM_BOT_TOKEN")?, config).await?;
OxideBotManager::new().bot(bot).await.run_block().await
}TelegramBot::new 为兼容 0.1 版本仍然保留,但连接失败时会 panic。新代码应使用返回 Result 的 TelegramBot::try_new。本地 Bot API 服务器或测试服务可使用 TelegramBot::try_with_api_base。
§按钮和交互
组件通过 MessageOptions 附在整条消息上,不属于 MessageSegment。这样相册拆分、长文本拆分和媒体上传不会把同一组按钮错误复制到每个子消息。
use anyhow::Result;
use oxidebot::{
api::payload::SendMessageTarget, ActionRow, Button, ButtonStyle, CallApiTrait,
InlineKeyboard, MessageComponents, MessageOptions,
};
use oxidebot::source::message::MessageSegment;
use telegram_bot_oxidebot::TelegramBot;
async fn send_actions(bot: &TelegramBot) -> Result<()> {
let keyboard = InlineKeyboard::new([ActionRow::buttons([
Button::callback("确认", "confirm").style(ButtonStyle::Success),
Button::url("文档", "https://core.telegram.org/bots/api"),
Button::copy_text("复制编号", "order-42"),
])]);
bot.send_message_with_options(
vec![MessageSegment::text("请选择操作")],
SendMessageTarget::Private("123456".to_owned()),
MessageOptions::default().components(MessageComponents::InlineKeyboard(keyboard)),
)
.await?;
Ok(())
}Inline keyboard 已适配以下动作:
- Callback、HTTP/HTTPS/
tg://URL、HTTPS Web App 和 Login URL; - 切换 inline query(选择聊天、当前聊天和按聊天类型选择);
- Copy Text、Game 和 Pay;
- Bot API 10.2 的
primary、success、danger样式和icon_custom_emoji_id; PlatformNativeData::new("telegram", json!(...))提供完整原生字段逃生口。
Telegram 不支持 disabled inline button 和 inline select menu,调用时会返回 UnsupportedInteractionError,不会静默降级。callback_data 会在发送前检查 1–64 bytes,Copy Text 检查 1–256 个字符,Game/Pay 必须位于第一行第一列,Web App/Login URL 必须是 HTTPS。
Callback Query 会直接成为 Event::InteractionEvent,可以用统一响应 API 完成 ACK、toast、alert 或打开 URL:
use anyhow::Result;
use oxidebot::{event::Event, InteractionResponse, Matcher};
async fn handle_interaction(matcher: &Matcher) -> Result<()> {
if let Event::InteractionEvent(interaction) = matcher.event.as_ref() {
interaction
.respond(matcher.bot.clone(), InteractionResponse::toast("已处理"))
.await?;
}
Ok(())
}Telegram 的 answerCallbackQuery 本身只能 ACK、toast/alert 或打开 URL。适配器把 InteractionResponse::Message 模拟为 ACK 后向来源 chat 发送 follow-up,把 UpdateMessage 映射为编辑 callback 来源消息;公开/ephemeral visibility 会映射到 Bot API 10.2 的 receiver/callback 字段。Inline callback 若没有 chat_id 不能发送 follow-up,会返回明确错误。Telegram 没有 modal callback response,所以 OpenModal 仍明确报不支持。传 None 给 edit_message_components 会移除 inline keyboard。
§Reply Keyboard
Reply keyboard 支持普通文字、选择用户、选择聊天、创建 Managed Bot、请求联系人、位置、投票和 Web App,并支持 persistent、resize、one-time、selective、输入框 placeholder、Bot API 10.2 样式及自定义 emoji 图标。
use anyhow::Result;
use oxidebot::{
api::payload::SendMessageTarget, CallApiTrait, MessageComponents, MessageOptions, ReplyButton,
ReplyButtonRow, ReplyKeyboard, RequestUsers,
};
use oxidebot::source::message::MessageSegment;
use telegram_bot_oxidebot::TelegramBot;
async fn ask_for_user(bot: &TelegramBot) -> Result<()> {
let keyboard = ReplyKeyboard::new([ReplyButtonRow::new([
ReplyButton::request_users("选择用户", RequestUsers::new(1)),
ReplyButton::request_location("发送位置"),
])])
.resize(true)
.one_time(true)
.placeholder("请选择");
bot.send_message_with_options(
vec![MessageSegment::text("需要一些信息")],
SendMessageTarget::Private("123456".to_owned()),
MessageOptions::default().components(MessageComponents::ReplyKeyboard(keyboard)),
)
.await?;
Ok(())
}Telegram 只允许 reply keyboard、Force Reply 和 Remove Keyboard 附在单条消息上。对于相册,inline keyboard 会在相册发送成功后编辑到第一条媒体消息;reply keyboard 必须改为单独发送一条消息。
§Rich Message、Poll、Checklist 和 Topic
新代码可以直接使用 v2 API,避免把 reply、caption 或 topic 编码进旧的 segment/string ID:
use anyhow::Result;
use oxidebot::{
CallApiTrait, ConversationKind, ConversationRef, MessageContent, MessageTarget,
OutgoingMessage, RichText, TextStyle,
};
use telegram_bot_oxidebot::TelegramBot;
async fn send_to_topic(bot: &TelegramBot) -> Result<()> {
let topic = ConversationRef::new("17", ConversationKind::Topic)
.child_of(ConversationRef::group("-100123456"));
let text = RichText::plain("Important update")
.span(0..9, TextStyle::Bold);
bot.send_outgoing_message(
MessageTarget::new(topic),
OutgoingMessage::new([MessageContent::RichText(text)]),
)
.await?;
Ok(())
}MediaGallery 会检查 2–10 项以及 Telegram 的 album class 规则:Photo/Video 可混合,Audio 只能与 Audio、Document 只能与 Document 组合。本地附件在 multipart 的 attach:// 名称下发送。相册不能原子携带 markup,所以 inline keyboard 会在成功后编辑到第一条消息;reply keyboard 会提前报错。
RichLayout 映射 Telegram InputRichBlock 的 paragraph、table、list、quotation、preformatted、divider、collage、details、map、photo/video/audio/animation 等 block。表格会补齐 Telegram 必需的 align/valign,ordered list 使用 Telegram 规定的 type: "1"。需要 Telegram 独有 block 时可用 LayoutNode::PlatformNative。
Checklist 会验证 1–30 个 task、正数且不重复的 task ID、title/task 长度和不可直接设置的 completion state。Poll 会验证 2–12 个 option、open period、country code、correct option 和 Bot API 字符限制。
§Mini App 数据校验
TelegramClient::verify_web_app_init_data 按 Telegram 官方两层 HMAC-SHA256 算法校验 initData,使用常量时间 MAC 比较,并可用 max_age 阻止重放。返回值会把 user、receiver、chat 解码为 JSON,把 auth_date 解码为整数。
use std::time::Duration;
use anyhow::Result;
use telegram_bot_oxidebot::TelegramClient;
fn verify_init_data(client: &TelegramClient, init_data: &str) -> Result<i64> {
let data = client.verify_web_app_init_data(init_data, Some(Duration::from_secs(300)))?;
Ok(data["user"]["id"]
.as_i64()
.ok_or_else(|| anyhow::anyhow!("Mini App user has no id"))?)
}§Bot Commands 和聊天菜单
use anyhow::Result;
use oxidebot::{
BotCommand, BotCommandSet, CallApiTrait, ChatMenu, CommandScope,
};
use telegram_bot_oxidebot::TelegramBot;
async fn configure_ui(bot: &TelegramBot) -> Result<()> {
bot.set_bot_commands(
BotCommandSet::new([
BotCommand::new("start", "开始"),
BotCommand::new("account", "查看账户").ephemeral(true),
])
.scope(CommandScope::AllPrivateChats)
.language_code("zh"),
)
.await?;
bot.set_chat_menu(
None,
ChatMenu::WebApp {
text: "打开应用".to_owned(),
url: "https://example.com/app".to_owned(),
},
)
.await?;
Ok(())
}命令 API 覆盖 Telegram 的 7 种 scope、语言代码,以及 Bot API 10.2 的 is_ephemeral。聊天菜单覆盖 Default、Commands 和 Web App;Telegram 无法表示 oxidebot 的多行动作持久菜单,所以 ChatMenu::Actions 会返回明确错误。
§原始 Telegram 事件
无法直接表示为 oxidebot 标准事件的更新会成为 Event::AnyEvent:
AnyEvent.r#type是 Telegram 原始 update 字段名;当前或未来出现且还没有 portable semantic mapping 的 update 会走这个路径;AnyEvent.data可向下转换为telegram_bot_oxidebot::event::TelegramRawEvent;TelegramRawEvent.data保留 Telegram 返回的完整 JSON。
对于已经映射成 oxidebot 标准事件的 update,Matcher.event_object 仍保存完整的 UpdateEvent,Matcher::event_envelopes 还会给出 update ID、时间、conversation 和 raw JSON;也可通过 EventTrait::as_any 向下转换后读取原始 Update。因此标准事件映射不会阻断业务层访问 Telegram 的完整数据。这种设计也意味着 Telegram 后续增加 update 类型时,机器人不会因为依赖库缺少枚举变体而卡死在同一个 offset。
§调用全部 Telegram 原生 API
oxidebot 的平台 API 扩展层可以直接通过 BotObject 使用,因此不需要向下转换到 TelegramBot。JSON 参数和返回值不会丢失 Telegram 专属字段:
use anyhow::Result;
use oxidebot::{BotTrait, PlatformApiRequest};
use serde_json::json;
async fn get_star_balance(bot: &dyn BotTrait) -> Result<serde_json::Value> {
let response = bot
.call_platform_api(PlatformApiRequest::new("getMyStarBalance").parameters(json!({})))
.await?;
Ok(response.result)
}需要上传文件时,使用 PlatformApiFile::bytes 或异步的 PlatformApiFile::from_path,再通过 PlatformApiRequest::file 添加 multipart 字段。嵌套 Telegram 对象可以按官方语法用 attach://字段名 引用附件。
TELEGRAM_BOT_API_METHODS 完整列出 Bot API 10.2 的 185 个方法,TELEGRAM_BOT_API_UPDATE_TYPES 完整列出 26 个 update 类型。原生调用不会被目录硬性限制,因此 Telegram 发布第 186 个方法后也仍然可以直接调用。
Telegram 返回 API 错误时,可以从 anyhow::Error 向下转换为 TelegramApiError。错误会保留 HTTP 状态、Telegram error_code、描述,以及 migrate_to_chat_id、retry_after 两个完整的 ResponseParameters 字段。
§兼容性边界
oxidebot 的通用模型不能表达 Telegram 的全部概念,因此以下行为是有意的:
- Telegram 不提供按 message_id 查询普通历史消息、好友列表、机器人所在群列表或群文件目录;仅
getUserPersonalChatMessages可读取用户 Personal Chat 的最后 1–20 条消息。 get_group_member_list只能返回 Telegram Bot API 允许查询的管理员列表。- 临时“全群禁言”没有对应 Telegram API;永久修改群默认权限可用,临时禁言应逐成员执行。
getChat只能返回最近一条 pinned message,所以list_pins最多返回一条;Bot API 也不能枚举全部 forum topics。- Telegram 非 Premium bot 每条消息最多设置一个 reaction,且 Bot API 不提供 reaction-user 列表。
MessageVisibility::Ephemeral需要 receiver 或 Callback Query 上下文;Inline callback 没有 chat 地址时不能模拟 follow-up。- Rich Message、Guest/Business/Ephemeral Message、支付和 Inline Query 已映射公共语义;仍无法表达的 Telegram 字段保留在各对象的
PlatformNativeData,完整未知 update 才使用TelegramRawEvent。 get_file_info返回的 Telegram 下载 URL 含 bot token。不要记录、公开或持久化完整 URL;应把它视为敏感凭据。
§从 0.1.2 升级到 0.1.3
0.1.3 移除了停留在 Bot API 7.9 的 telegram_bot_api_rs 依赖,并将配置类型改为本 crate 的 GetUpdatesConfig。常见迁移只需:
- 把
telegram_bot_api_rs::getting_updates::GetUpdateConfig改为telegram_bot_oxidebot::GetUpdatesConfig; - 把
TelegramBot::new(...).await改为TelegramBot::try_new(...).await?; - 如果处理过适配器内部的 Telegram 类型,改为使用
telegram_bot_oxidebot::telegram中的精简类型或TelegramRawEvent的 JSON。
§从 0.1.3 升级到 0.1.4
0.1.4 使用 oxidebot 0.1.8 新增的统一交互层。原有 send_message 和事件处理代码保持可用;需要按钮时改用 send_message_with_options。callback_query 不再以 TelegramRawEvent 交付,而是 Event::InteractionEvent,完整 Telegram JSON 仍保存在 InteractionEvent.data 和 Matcher.event_object 中。
Re-exports§
pub use bot::TelegramBot;pub use methods::TELEGRAM_BOT_API_METHODS;pub use methods::TELEGRAM_BOT_API_UPDATE_TYPES;pub use telegram::GetUpdatesConfig;pub use telegram::ResponseParameters;pub use telegram::TelegramApiError;pub use telegram::TelegramClient;pub use telegram::BOT_API_VERSION;
Modules§
- api
- bot
- event
- methods
- Telegram Bot API 10.2 method and update catalog.
- segment
- telegram
- A small, forward-compatible Telegram Bot API client.
- utils