Expand description
§Oxidebot
Oxidebot is a lightweight yet powerful chatbot framework based on Rust and the Tokio runtime. It aims to provide developers with a flexible and extensible environment for bot development through modular design.
§Available Bots
§Available Handlers
§Example Usage
§Core Concepts
§Bot
Bot is the core component of the framework, responsible for providing Events and offering basic API methods for developers to call. It serves as the bridge between the framework and external platforms (such as QQ, Telegram, etc.).
The common API is complemented by PlatformApiRequest. Every adapter can expose its complete native API through the same lossless JSON request, response, and multipart-file abstraction. This second layer deliberately retains platform-only capabilities even when no honest cross-platform semantic model exists. Unsupported adapters return a downcastable UnsupportedPlatformApiError by default, while callers can inspect platform_api_methods or supports_platform_api_method before calling a platform-only feature.
§Portable chat-platform model
The v2 common model covers the capabilities shared by Telegram, Discord, Slack, Microsoft Teams, Lark/Feishu, LINE, QQ and similar bot platforms while retaining the original 0.1 API:
ConversationRefaddresses direct chats, groups, channels, forums, threads and topics. A nested thread or topic carries its parent conversation, so an adapter never has to encode a hierarchy into an opaque string.MessageRefandMessageTargetare portable message addresses. They are used consistently by send, edit, delete, bulk delete, reply, forward, copy, pin, poll, checklist and reaction APIs.OutgoingMessageseparates ordered content from message-wide options. Content includes plain/rich text, all common media forms, galleries, location, contacts, stickers, custom emoji, polls, quizzes, checklists and rich layouts.RichTextuses byte-indexed spans with styles such as links, mentions, custom emoji, code, quotes, date/time, references and mathematical expressions. Adapters validate and convert offsets to their platform’s unit (Telegram and Discord commonly use UTF-16).RichLayoutrepresents paragraphs, sections, containers, columns, grids, lists, tables, quotations, code, maps, details, media and action rows. It can map to Telegram Rich Message, Slack Block Kit, Teams Adaptive Cards, LINE Flex Message, Discord embeds/components or a platform-native fallback.MessageOptionscarries reply/quote data, components, notification policy, visibility, link-preview and mention policy, protected content, immediate / scheduled / draft delivery, idempotency keys, client IDs and metadata.
The message lifecycle is exposed through CallApiTrait: send/edit/delete,
bulk delete, reactions, pins, activity indicators, read state, polls,
checklists, topics/threads, profiles, members, permissions, member tags,
moderation, join requests, invite links, history, forwarding/copying and batch
delivery. Application APIs cover localized structured commands, suggestions /
autocomplete, persistent surfaces, Mini Apps and bot profiles. Commerce APIs
cover invoices, shipping, checkout and refunds.
use anyhow::Result;
use oxidebot::{
BotTrait, ConversationRef, MessageContent, MessageOptions, MessageTarget,
OutgoingMessage, RichText, TextStyle,
};
async fn send_portable_message(bot: &dyn BotTrait) -> Result<()> {
let text = RichText::plain("Read the docs")
.span(0..13, TextStyle::Bold)
.span(5..13, TextStyle::Link {
url: "https://example.com/docs".to_owned(),
});
let message = OutgoingMessage::new([MessageContent::RichText(text)])
.options(MessageOptions::default().protect_content(true));
bot.send_outgoing_message(
MessageTarget::new(ConversationRef::direct("user-id")),
message,
)
.await?;
Ok(())
}BotCapabilities reports support as Native, Emulated or Unsupported
for individual content, delivery, component, interaction, conversation,
collaboration and application features. It also reports hard limits such as
text length, caption length, media-group size, command count and poll-option
count. Callers should use capabilities for user-interface decisions and still
handle the structured UnsupportedFeatureError: capabilities can depend on
chat type, bot permissions or account configuration.
Legacy adapters remain valid because every new trait method has a default.
Basic v2 messages are converted to MessageSegment when lossless conversion
is possible. Rich content, nested targets or advanced options return an
explicit error instead of being silently discarded.
§Interactive messages and menus
Oxidebot models the complete interaction lifecycle instead of treating a button as message content:
MessageOptionsattaches an inline keyboard, reply keyboard, forced reply, or platform-native component tree to a message;InteractionEventcarries button callbacks, selections, commands, and form submissions;InteractionResponseacknowledges an event, shows a notification, opens a URL or modal, sends a response, or updates the original message;BotCommandSetandChatMenuconfigure command discovery and persistent menu entry points;InteractionCapabilitieslets an adapter report which high-level surfaces it implements, and unsupported operations return a downcastableUnsupportedInteractionError.
Components intentionally live in MessageOptions, not MessageSegment. A segment is ordered message content that can be split or grouped during delivery, while a component tree belongs to the final platform message as a whole.
use anyhow::Result;
use oxidebot::{
api::payload::SendMessageTarget, ActionRow, BotTrait, Button, ButtonStyle, CallApiTrait,
InlineKeyboard, MessageComponents, MessageOptions,
};
use oxidebot::source::message::MessageSegment;
async fn send_confirmation(bot: &dyn BotTrait) -> Result<()> {
let keyboard = InlineKeyboard::new([ActionRow::buttons([
Button::callback("Confirm", "confirm").style(ButtonStyle::Success),
Button::url("Documentation", "https://example.com/docs"),
])]);
bot.send_message_with_options(
vec![MessageSegment::text("Continue?")],
SendMessageTarget::Private("user-id".to_owned()),
MessageOptions::default().components(MessageComponents::InlineKeyboard(keyboard)),
)
.await?;
Ok(())
}The common component model includes callback, send-text, URL, Web App, login, inline-query, copy, game and payment buttons; text/user/role/channel selects; rich reply keyboards; modals; commands and menus. An adapter maps only semantics its platform can represent. PlatformNativeData is the lossless escape hatch for a platform field or component that has no portable meaning.
An interaction may carry an InteractionResponseHandle. The handle records
whether acknowledgement is required, its deadline, follow-up support and the
platform context needed to edit or delete the original response.
InteractionEvent::respond routes acknowledge/defer, initial messages,
message updates and simple callback responses through the appropriate
lifecycle method. Message-like reply-keyboard events intentionally have no
response handle, so they cannot accidentally be answered as callback queries.
§Event
Event is the object that the framework processes, representing the various events received by the bot. Event types include:
- MessageEvent: Message events
- NoticeEvent: Notification events
- RequestEvent: Request events
- InteractionEvent: Button, selection, command, and form interactions
- LifecycleEvent: v2 messages, reactions, pins, polls, checklists, threads, members, permissions, suggestions, Mini Apps, checkout, payment and subscription lifecycle events
- MetaEvent: Meta events
- AnyEvent: Generalized events
§Matcher
Matcher is an abstraction over Bot and Event, simplifying event handling and API calls. It provides convenient methods to extract key information from events (such as users, messages, groups) and easily call related APIs.
For v2 code, try_get_conversation, try_send_outgoing_message and
try_reply_outgoing_message preserve thread/topic addressing and rich message
options. event_envelopes exposes delivery metadata and raw payloads without
changing the public 0.1 Matcher layout. EventEnvelope includes a stable
event ID, platform, timestamps, conversation, delivery attempt, metadata and
the original platform JSON when an adapter supplies it.
§Native data and unsupported features
PlatformNativeData can be attached to content, components, targets,
conversations, replies, events and most option objects. An adapter must verify
the platform identifier and reject duplicate fields; it must never silently
interpret native data intended for another platform.
Use native data for a small platform-only field and PlatformApiRequest for a
complete platform-only workflow. Portable fields are intentionally strict:
when a platform cannot represent a requested semantic, the adapter returns a
structured error rather than dropping it. This makes the same business logic
safe to run against adapters with very different feature sets.
§Handler
Handler is the core component for event processing, divided into two types:
- EventHandler: Handles incoming
Events and is triggered only when an event occurs. - ActiveHandler: Suitable for proactive processing scenarios, it can run continuously, execute scheduled tasks, or perform other background operations.
A Handler can include either an EventHandler or an ActiveHandler, or both.
§Filter
Filter is a global event filter used to process and intercept events before they reach the Handler. The Filter has a higher priority than the Handler.
§OxideBotManager
OxideBotManager is the manager of the framework, the entry point for starting and running the bot. Developers should call its run_block method at the end of the main function to launch the entire framework along with all registered Bots, Filters, and Handlers.
§Auxiliary Tools for Handler Writer
§Wait
Include a restricted BroadcastSender that can only use subscribe fn in your handler
use oxidebot::manager::BroadcastSender;
pub struct WaitHandler {
pub broadcast_sender: BroadcastSender,
}Then use wait in your HandlerTrait implementation.
You can find the provided wait methods in utils::wait or define your own.
use std::time::Duration;
use anyhow::Result;
use oxidebot::{manager::BroadcastSender, matcher::Matcher, wait_user_text_generic};
async fn wait_for_number(
matcher: &Matcher,
broadcast_sender: &BroadcastSender,
) -> Result<()> {
let (number, matcher) = wait_user_text_generic::<u8>(
matcher,
broadcast_sender,
Duration::from_secs(30),
3,
Some("Please send an unsigned 8-bit integer".to_string()),
)
.await?;
Ok(())
}§License
MIT OR Apache-2.0
Re-exports§
pub use api::platform::PlatformApiFile;pub use api::platform::PlatformApiFileSource;pub use api::platform::PlatformApiRequest;pub use api::platform::PlatformApiResponse;pub use api::platform::UnsupportedPlatformApiError;pub use api::CallApiTrait;pub use application::AppSurface;pub use application::AppSurfaceKind;pub use application::BotProfile;pub use application::CommandChoice;pub use application::CommandContext;pub use application::CommandDefinition;pub use application::CommandInvocation;pub use application::CommandKind;pub use application::CommandOption;pub use application::CommandOptionType;pub use application::Localized;pub use application::MiniAppEvent;pub use application::MiniAppLaunch;pub use application::MiniAppMode;pub use application::Suggestion;pub use application::SuggestionRequest;pub use application::SuggestionSelection;pub use application::SurfaceContent;pub use application::VerificationState;pub use bot::get_bot;pub use bot::BotTrait;pub use capability::ApplicationCapabilities;pub use capability::BotCapabilities;pub use capability::ButtonCapabilities;pub use capability::CollaborationCapabilities;pub use capability::ComponentCapabilities;pub use capability::ContentCapabilities;pub use capability::ConversationCapabilities;pub use capability::DeliveryCapabilities;pub use capability::InteractionLifecycleCapabilities;pub use capability::PlatformLimits;pub use capability::SupportLevel;pub use capability::UnsupportedFeatureError;pub use collaboration::ActivityState;pub use collaboration::CallKind;pub use collaboration::CallOptions;pub use collaboration::CallSession;pub use collaboration::CallState;pub use collaboration::ChatActivity;pub use collaboration::PinOptions;pub use collaboration::PinnedMessage;pub use collaboration::Reaction;pub use collaboration::ReactionChange;pub use collaboration::ReactionOptions;pub use collaboration::ReactionSummary;pub use collaboration::ReadReceipt;pub use commerce::CheckoutRequest;pub use commerce::CustomerDetails;pub use commerce::Invoice;pub use commerce::InvoiceOptions;pub use commerce::LineItem;pub use commerce::Money;pub use commerce::Payment;pub use commerce::PaymentStatus;pub use commerce::ShippingOption;pub use commerce::ShippingRequest;pub use commerce::Subscription;pub use content::BatchItemResult;pub use content::BatchMessage;pub use content::BatchSendResult;pub use content::Checklist;pub use content::ChecklistChange;pub use content::ChecklistTask;pub use content::ContactCard;pub use content::ContentConversionError;pub use content::CustomEmoji;pub use content::DeliveryTime;pub use content::FormValue;pub use content::ForwardContext;pub use content::ForwardOptions;pub use content::LayoutColumn;pub use content::LayoutNode;pub use content::LayoutStyle;pub use content::LinkPreviewOptions;pub use content::LocationContent;pub use content::Media;pub use content::MediaGalleryItem;pub use content::MediaType;pub use content::MentionAllowance;pub use content::MentionPolicy;pub use content::MessageContent;pub use content::MessageEnvelope;pub use content::MessageOrigin;pub use content::MessageQuery;pub use content::MessageVisibility;pub use content::NotificationPolicy;pub use content::OutgoingMessage;pub use content::PhoneNumber;pub use content::Poll;pub use content::PollOption;pub use content::PollType;pub use content::ReplyContext;pub use content::ReplyOptions;pub use content::RichLayout;pub use content::RichText;pub use content::Sticker;pub use content::TableCell;pub use content::TextSpan;pub use content::TextStyle;pub use conversation::ConversationKind;pub use conversation::ConversationMember;pub use conversation::ConversationPermission;pub use conversation::ConversationProfile;pub use conversation::ConversationRef;pub use conversation::InviteLink;pub use conversation::InviteLinkOptions;pub use conversation::JoinRequest;pub use conversation::MessageRef;pub use conversation::MessageTarget;pub use conversation::Page;pub use conversation::PageRequest;pub use conversation::PermissionSet;pub use conversation::RoleRef;pub use conversation::Thread;pub use conversation::ThreadOptions;pub use conversation::ThreadState;pub use event::EventTrait;pub use filter::FilterTrait;pub use handler::ActiveHandlerTrait;pub use handler::EventHandlerTrait;pub use handler::Handler;pub use interaction::ActionRow;pub use interaction::BotCommand;pub use interaction::BotCommandQuery;pub use interaction::BotCommandSet;pub use interaction::Button;pub use interaction::ButtonAction;pub use interaction::ButtonIcon;pub use interaction::ButtonStyle;pub use interaction::ChatMenu;pub use interaction::ChoiceOption;pub use interaction::ChosenChatCriteria;pub use interaction::CommandScope;pub use interaction::InlineKeyboard;pub use interaction::InlineQueryTarget;pub use interaction::InputComponent;pub use interaction::InteractionCapabilities;pub use interaction::InteractionComponent;pub use interaction::InteractionEvent;pub use interaction::InteractionKind;pub use interaction::InteractionNotificationStyle;pub use interaction::InteractionResponse;pub use interaction::InteractionResponseHandle;pub use interaction::InteractionVisibility;pub use interaction::LoginAction;pub use interaction::MessageComponents;pub use interaction::MessageOptions;pub use interaction::Modal;pub use interaction::ModalField;pub use interaction::PlatformNativeData;pub use interaction::PollKind;pub use interaction::ReplyButton;pub use interaction::ReplyButtonAction;pub use interaction::ReplyButtonRow;pub use interaction::ReplyKeyboard;pub use interaction::RequestChat;pub use interaction::RequestManagedBot;pub use interaction::RequestUsers;pub use interaction::SelectKind;pub use interaction::SelectMenu;pub use interaction::SelectOption;pub use interaction::UnsupportedInteractionError;pub use manager::OxideBotManager;pub use matcher::Matcher;pub use utils::wait::wait;pub use utils::wait::wait_text_generic;pub use utils::wait::wait_user;pub use utils::wait::wait_user_message;pub use utils::wait::wait_user_text_generic;pub use utils::wait::EasyBool;
Modules§
- api
- application
- Application commands, dynamic suggestions, mini apps, persistent surfaces, and localized bot profiles.
- bot
- capability
- Granular platform capability and constraint reporting.
- collaboration
- Reactions, pins, transient chat activity, and read state.
- commerce
- Portable payment, invoice, checkout, and subscription data.
- content
- Portable message content, rich text, rich layout, media, poll, checklist, contact, and delivery models.
- conversation
- Cross-platform conversation, thread, membership, permission, and paging models.
- event
- filter
- handler
- interaction
- Cross-platform interactive message, command, menu, and response types.
- manager
- matcher
- source
- utils