Skip to main content

Crate oxidebot

Crate oxidebot 

Source
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:

  • ConversationRef addresses 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.
  • MessageRef and MessageTarget are portable message addresses. They are used consistently by send, edit, delete, bulk delete, reply, forward, copy, pin, poll, checklist and reaction APIs.
  • OutgoingMessage separates 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.
  • RichText uses 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).
  • RichLayout represents 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.
  • MessageOptions carries 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:

  • MessageOptions attaches an inline keyboard, reply keyboard, forced reply, or platform-native component tree to a message;
  • InteractionEvent carries button callbacks, selections, commands, and form submissions;
  • InteractionResponse acknowledges an event, shows a notification, opens a URL or modal, sends a response, or updates the original message;
  • BotCommandSet and ChatMenu configure command discovery and persistent menu entry points;
  • InteractionCapabilities lets an adapter report which high-level surfaces it implements, and unsupported operations return a downcastable UnsupportedInteractionError.

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::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 interaction::ViewNavigation;
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