Skip to main content

Crate composure_adapter_tokio

Crate composure_adapter_tokio 

Source
Expand description
Composure logo

§composure_adapter_tokio

Your Discord bot as a plain HTTP server. Serve a Composure bot from any machine, container or PaaS with tokio and hyper: every Interaction on its own task across all cores, with the same answers as on Cloudflare Workers.

Crates.io Docs.rs CI License MSRV tokio

  • Server defaults: GET /health, a 1 MiB body limit, panics isolated to their request, graceful shutdown on Ctrl-C / SIGTERM
  • Configured from the environment: DISCORD_PUBLIC_KEY, DISCORD_TOKEN, PORT, and you’re serving; or load Credentials with config, figment, envy, …
  • Your data in every Command: .with_data(..) hands your Bot Data to handlers through Host<T>
  • Tracked Background Work: deferred work is awaited on shutdown
  • Testable: point the HTTP client at a stand-in Discord with HttpTransport::with_api_base

§Usage

ⓘ
use composure_adapter_tokio::TokioInteractionBot;

#[tokio::main]
async fn main() -> Result<(), composure_adapter_tokio::Error> {
    TokioInteractionBot::from_env()? // DISCORD_PUBLIC_KEY, DISCORD_TOKEN?, DISCORD_APPLICATION_ID?, PORT?
        .with_handler(Bot)            // an `#[app]`
        .serve()                      // 0.0.0.0:$PORT (8080), until Ctrl-C / SIGTERM
        .await
}
  • TokioInteractionBot::new(credentials) takes composure::Credentials loaded any way: settings.get::<Credentials>("discord")? with config, envy::prefixed("DISCORD_").from_env()?, or built in code. from_env() reads the conventional names plus PORT.
  • Handlers get a Host<T> State: your Bot Data from .with_data(..) (also via Deref, and HasData for platform-agnostic Commands), plus the bot token, an HttpTransport (async reqwest, rustls), and Background Work as tokio tasks. Ctx<impl Platform> commands, ctx.defer(..) and ctx.bot() work as on Workers.
  • Handler futures are Send (ADR 0012), so handlers can tokio::spawn work that owns a DeferredCtx or a clone of the Host.
  • HttpTransport::with_api_base(url) sends what would go to Discord’s API elsewhere: a proxy, or a stand-in Discord in tests.
  • REST outside Interactions: bot.host().bot() (needs a bot token and an application id in the Credentials), and host.spawn(..) for tasks shutdown should wait for.
  • Same answers as the Cloudflare Adapter (both go through composure::adapter): 401 on a bad signature, 400 on a bad body, ephemeral error messages for handler errors, files through the callback endpoint then 202, Webhook Events on /events ACKed with 204. On top: GET /health is 200, bodies over 1 MiB are 413, a panicking handler is 500.
  • Shutdown stops accepting and waits up to 30s (.with_grace(..)) for in-flight requests and Background Work. .with_shutdown(future) replaces the Ctrl-C / SIGTERM signal.
  • Plain HTTP only: Discord needs HTTPS, so run it behind a TLS-terminating proxy or tunnel.
  • Logs through tracing; install a subscriber (e.g. tracing_subscriber::fmt) to see them, Composure’s interaction / background_work / event / rest spans included.

See examples/tokio for the example bot served this way.

§License

Licensed under the Apache License, Version 2.0.

Structs§

Credentials
The application’s public key (required, verifies Interactions), and optionally the bot token (for Ctx::bot) and the application id (for a Bot Client outside Interactions).
HandlerError
Why an Interaction could not be answered normally. Adapters log it and answer with HandlerError::response instead.
Host
The State Commands get on a hosted server: your data T (also via Deref), plus what Platform needs: the bot token, an HttpTransport, and tokio tasks for Background Work, which graceful shutdown waits for. So ctx.client(), ctx.bot() (with a bot token) and ctx.defer(..) work.
HttpTransport
Transport over an async reqwest::Client (rustls), sleeping on tokio’s timer. Responses keep only the DISCORD_HEADERS (rate limits and content-type). Cloning shares the client’s connection pool.
NoHandler
Handles nothing: every Interaction gets the InteractionHandler defaults
TokioInteractionBot
Interaction bot on a hosted server: an HTTP server on tokio answering Discord’s signed requests, each Interaction on its own task (ADR 0012). The handler receives a Host (your data plus the bot token, Transport, and Background Work) as its State. Webhook Events on the events path (default /events) go to the same handler’s EventHandler implementation; GET /health answers 200. Plain HTTP: put it behind a TLS-terminating proxy or tunnel.

Enums§

CredentialsError
Why Credentials could not be built
Error
Why the bot could not be configured or served

Constants§

DEFAULT_EVENTS_PATH
Path Webhook Events are served on unless the Adapter is told otherwise; every other path serves Interactions
DEFAULT_GRACE
How long shutdown waits for in-flight requests and Background Work by default
DEFAULT_PORT
Port served on when PORT is not set
HEALTH_PATH
Path answering GET with 200, for load balancers and orchestrators
MAX_BODY
Requests with a bigger body are answered with 413; Discord’s are a few KB

Traits§

EventHandler
Answers Webhook Events. Every method defaults to ignoring the event; override the ones you care about, or handle to see every event. Registered on an App with #[app(.., events = MyEvents)].
InteractionHandler
Answers Interactions; the one contract every Adapter talks to. S is the Adapter’s State.