composure_adapter_tokio 0.2.1

Serve Composure Discord bots from a hosted server on tokio and hyper, with graceful shutdown and health checks
Documentation

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.