Sign in

    discord

    An experimental Discord library for MoonBit: native/JS/Wasm REST, interactions, and gateway bots. Voice is in gaato/discord_voice.

    discord
    bot
    gateway
    api
    interactions
    Download zip
    Author
    Version
    0.11.0
    License
    Apache-2.0
    Last updated
    1 hour ago
    Downloads
    158

    #discord.mbt

    CI Voice shim Release mooncakes Ask DeepWiki License

    A Discord application library for MoonBit: typed interaction declarations, API models, a rate-limited REST client, native/JS/moonrun Wasm HTTP interactions and WebSocket gateway shards.

    The design follows twilight: loosely coupled packages that model the Discord API, plus an App layer for typed interaction declarations.

    Status: experimental. Minor releases may contain breaking changes; see the changelog for migration notes.

    Long-form guides live in the repository's guide/; their code blocks compile and run as part of the test suite, so the examples cannot drift from the library. Task-focused recipes are docstring examples on the relevant symbols — look anything up with moon ide doc (see Development).

    #Prerequisites

    On a clean machine, install git and run moon update before resolving the module dependencies. Moon uses git to clone the package registry.

    The ghcr.io/gaato/moonbit toolchain image bundles the pinned toolchain with Node.js 24, gcc, and git; the repository's .devcontainer starts from it, CI runs in it (except the Rust voice shim job), and template/Dockerfile uses it as the builder stage for a distroless bot image.

    #Install

    moon update moon add gaato/discord

    The interaction App, REST client, data packages, and gateway Bot executor support native, JavaScript, and linear-memory Wasm on moonrun. Voice is native-only. These executors use moonbitlang/async.

    #Quickstart

    Build the interaction App, then pass it to the gateway Bot executor. This example registers and answers a /echo command. See examples/slash_echo for the runnable version.

    ///|
    struct QuickstartEchoArgs {
    text : String
    times : Int32
    }

    ///|
    async fn run_echo_bot(token : String) -> Unit {
    let args : @discord.Args[QuickstartEchoArgs] = @discord.Args::map2(
    @discord.arg_string(name="text", description="What to echo"),
    @discord.arg_int32(
    name="times",
    description="How many times (1-5)",
    min=1,
    max=5,
    ).with_default(1),
    (text, times) => { text, times, },
    )
    let echo = @discord.slash(
    name="echo",
    description="Echo your text back",
    args~,
    handler=Immediate((_ctx, value) => {
    @discord.CommandReply::message(
    content=Array::make(value.times, value.text).join("\n"),
    )
    }),
    )
    let app = @discord.AppBuilder()
    app.command(echo)
    let bot = @discord.BotBuilder(app.build(), token~, sync=Global)
    bot.on(@discord.Events::ready(), (_ctx, ready) => {
    println("ready as \{ready.user.username}")
    })
    bot.build().run()
    }

    #Packages

    PackageWhat it isNativeJSWasm¹
    gaato/discordFacade: golden-path names a typical application uses directlyYesYes²Yes²
    gaato/discord/modelPure data: ~24 entity domains, gateway payloads, zero IOYesYesYes
    gaato/discord/telemetryStructured REST, gateway, and dispatch observability valuesYesYesYes
    gaato/discord/httpREST Client, routes, rate limiting, multipart uploadsYesYesYes
    gaato/discord/gatewayShard: connection state machine, heartbeat, resumeYesYesYes
    gaato/discord/interactionCommand/component builders, typed args and autocomplete dataYesYesYes
    gaato/discord/frameworkInteraction routing, response gates, low-level response contextsYesYesYes
    gaato/discord/appGateway-free typed commands/components/modals, HTTP endpoint, sync and policyYesYesYes
    gaato/discord/botGateway executor and typed gateway event descriptorsYesYesYes
    gaato/discord/endpoint_httpSigned-interactions HTTP server (serve_interactions)YesNoYes
    gaato/discord/cacheOpt-in, gateway-driven in-memory cacheYesYesYes
    gaato/discord/utilPure helpers: permissions, mentions, timestamps, CDN URLsYesYesYes
    gaato/discord/verifyPure MoonBit Ed25519 request verificationYesYesYes
    gaato/discord/ratelimitIn-memory Discord rate limiter for the gaato/sdk-runtime RateLimiter traitYesYesYes
    gaato/discord/cooldownCommand cooldown store trait + in-memory fixed windowsYesYesYes
    gaato/discord/queueIdentify queue trait + in-memory implementationYesYesYes
    gaato/discord/coordinatorExperimental TCP coordinator for multi-process Identify, REST limits, and cooldownsYesNoYes
    gaato/discord/testkitDeterministic interaction and model fixturesYesYesYes

    ¹ Linear-memory Wasm run by moonrun. The host imports come from moonbitlang/async, so generic WASI runtimes, browsers, and wasm-gc are not targets. See Wasm runtime and permissions.

    ² The HTTP-server exports exist on native and Wasm. Packages that are empty on a backend do not provide that feature, even if Moon accepts them as dependencies. Voice (gaato/discord_voice) is native-only.

    #Wasm runtime and permissions

    Use --target wasm with the pinned MoonBit toolchain and its matching moonrun. This backend uses MoonBit host APIs for sockets, TLS, timers, and environment variables; it is not a browser, generic WASI, or wasm-gc application target.

    The interactions_http example also builds for Wasm:

    moon build --target wasm --release examples/interactions_http moonrun --policy discord-policy.json _build/wasm/release/build/local/discord_examples/interactions_http/interactions_http.wasm

    An example discord-policy.json for that server:

    { "env": { "required_from_host": ["DISCORD_TOKEN", "PUBLIC_KEY"], "from_host": ["GUILD_ID"], "set": {"PORT": "8080"} }, "net": { "connect": ["discord.com:443"], "bind": ["0.0.0.0:8080"] } }

    Supply credentials through the environment, never in the policy file. This example synchronizes commands at startup; run it only when you intend to update your application's commands. A REST-only program needs the token and outbound connection permission but no bind permission. The coordinator needs bind permission on its server address and connect permission on each client. Add other destinations only for features that actually need them. In policy mode, omitted environment, filesystem, network, and process permissions are denied.

    Packages remain usable on their own. Import only what the program needs:

    The facade follows the typical bot's golden path. Import the focused http, framework, gateway, coordinator, telemetry, or interaction package for resource refs, pagination, low-level routing and raw contexts, shard transport, coordination, observability events, or raw option declarations.

    • REST-only tool: http (brings model).
    • Serverless HTTP interactions (Cloudflare Workers, Deno, Bun, and Node.js Functions, JS): app and verify.
    • Native HTTP interactions server: endpoint_http.
    • Gateway bot: bot, or the gaato/discord facade for everything.

    #Examples

    Runnable programs live in the repository's examples/ workspace module; run them from the repository root, for example moon run --target native examples/slash_echo:

    • slash_echo: minimal typed Gateway bot.
    • kitchen_sink: subcommands, autocomplete, context menus, components, and modals.
    • ping_gateway: low-level Gateway and REST use.
    • low_level: manual Framework and Shard wiring.
    • workers_echo: Cloudflare Workers adapter.
    • deno_echo: Deno adapter for the same MoonBit interaction App.
    • bun_echo: Bun HTTP server adapter for the same interaction App.
    • vercel_echo: Vercel Node.js Function adapter for the same interaction App.
    • fastly_echo: Fastly FetchEvent entry point experiment.
    • lambda_url_echo: Lambda Function URL payload v2 adapter experiment.
    • workers_gateway: Cloudflare Durable Object running a gateway bot (no voice).
    • interactions_http: native signed-interactions HTTP server.
    • experiments/spin_interactions: WASIp2 Spin component for signed PING and immediate echo; this experiment sits outside examples/.
    • plugin_demo: a stateful feedback feature installed as a separate package.
    • gate_probe: live probe for error-policy recovery and user-restricted component waits (needs a Discord client; guild commands only).
    • voice_player: join a voice channel and play an Ogg/Opus file.
    • voice_recorder: record a user's voice to an Ogg/Opus file.

    #App core and executors

    An AppBuilder collects the interaction declaration: commands, components, modals, autocomplete routes, middleware, and the error policy. build() validates it into an immutable App, which has no gateway dependency. Hand the App to an executor:

    • BotBuilder(app, token~) collects gateway event handlers and services; build() validates it into a Bot, whose run() connects to the gateway and routes InteractionCreate events through the App.
    • app.serve(group, access=Token(token)) creates an InteractionEndpoint for an HTTP adapter.

    Both executors use the same handlers, so an application can move between a persistent gateway process and an HTTP or serverless deployment without rewriting its interaction declarations.

    Command[A] pairs one handler with Args[A]; the argument value drives both Discord's registration payload and interaction decoding (build records with Args::map1 through Args::map8). Choose a handler mode against Discord's three-second initial-response deadline: Immediate computes and returns a CommandReply before it, Deferred acknowledges first and continues on a DeferredCtx, and Raw receives the underlying CommandCtx for imperative flows. Subcommands carry their own typed Args, autocomplete attaches directly to the focused argument, and context-menu commands, components, and typed modals route through the same App — see the commands and components and modals guides.

    Client, AppBuilder, and BotBuilder each accept onion-style middleware — around one logical REST call, around routed interaction dispatch, and around gateway event fan-out — plus structured telemetry callbacks. See the middleware guide.

    #Gateway executor

    BotBuilder adds typed gateway event subscriptions and services:

    bot.on(@discord.Events::message_create(), (ctx, event) => {
    let message = event.message
    println("\{message.author.username}: \{message.content}")
    })

    If intents is omitted, Bot derives non-privileged intents from typed subscriptions; pass privileged intents (members, presences, message content) explicitly, and pass the full set when raw event handlers are used. ctx.wait_for(...) makes one-shot typed event observations inside a handler. The events and intents guide covers descriptors, intent derivation, decode-error observers, opt-in zstd-stream and zlib-stream compression, and the gateway-driven in-memory cache.

    Several shards can run in one process behind the same dispatch path, so handlers, the cache, collectors, and telemetry observe every shard:

    ///|
    let bot = @discord.BotBuilder(app, token~, shards=Auto)

    Identify calls honor the max_concurrency bucket rules from GET /gateway/bot, and multi-process deployments can share Identify and REST limits through the bundled TCP coordinator. See the scaling guide.

    #Voice (experimental)

    Voice is a separate module, gaato/discord_voice, for native bots built with this one. It joins voice gateway v8 calls, plays 20 ms Opus frames, and receives per-user Opus streams with Discord's DAVE encryption. See the voice guide for installation and runtime limits, and the dave_probe, voice_player, and voice_recorder examples.

    #Synchronization and failures

    CommandScope selects global, guild, or multi-guild registration. Pass sync=scope to BotBuilder to synchronize once after the first READY; when omitted, the bot makes no command request. HTTP executors never synchronize commands. For those deployments, call app.build().sync_commands(client, application_id,scope~) from a one-shot registration program. It returns an @app.SyncReport whose scopes list each scope's created, updated, deleted, unchanged, and preserved command names, plus whether an overwrite was sent.

    Synchronization fetches the remote catalog and only sends a bulk-overwrite PUT when it differs. By default (unowned=Delete), it removes undeclared commands from the selected scope. The Entry Point command is always preserved, including its id and fields unknown to this library. Use unowned=Keep on App::sync_commands or Framework::sync_*, or sync_unowned=Keep on Bot, when another process owns commands in the same scope. Bot emits one warning per scope with deletions through app.on_warn. Use exactly one process for synchronization in a multi-process deployment.

    ///|
    async test "sync preserves entry points and skips an unchanged catalog" {
    let spec = @interaction.CommandSpec::slash("ping", "Ping")
    let (payload, report) = @framework.plan_command_sync(
    [spec],
    [spec.to_json(), { "id": "10", "type": 4, "name": "Launch", "handler": 2 }],
    unowned=Delete,
    )
    assert_true(payload is None)
    assert_false(report.overwritten)
    assert_eq(report.preserved, ["4:Launch"])
    }

    Install app.error_policy(...) to map failures to logs or interaction responses; handlers raise HandlerError variants such as UserMessage or MissingPermission for expected failures, and commands accept ordered pre-execution checks and fixed-window cooldowns. See the structuring bots guide.

    #HTTP interactions (experimental)

    App::serve(group, access=Token(token)) starts the HTTP interaction executor and returns an InteractionEndpoint that uses the App's declarations and error policy without opening a gateway. handle accepts decoded interaction JSON and returns callback JSON for Reply; the caller owns the task group, so handlers that defer keep running on it after handle returns:

    ///|
    async fn handle_http_interaction(
    app : @discord.App,
    group : @async.TaskGroup[Unit],
    token : String,
    body : Json,
    ) -> Json? {
    app.serve(group, access=Token(token)).handle(body)
    }

    The Cloudflare Worker, Deno, Bun, and Vercel Function HTTP adapters verify the raw request bytes with the reusable pure MoonBit @discord.InteractionVerifier before parsing. The dispatch bridge is App::start_signed_http in the library. The examples/interactions_js example is an exported wrapper plus a small handler.js that maps a Web Request to InteractionHttpRequest and back; the per-host adapters (Workers, Deno, Bun, Vercel, Fastly, Lambda) only wire their entry point and lifetime API. The shared handler is tested on Node.js and Bun, while the Vercel lifecycle adapter is tested with an injected waitUntil collector. These local tests do not establish hosted Vercel deployment behavior. workers_echo is tested inside Cloudflare's local workerd runtime and supports streamed multipart callbacks for in-memory FileUpload values. On native, @discord.serve_interactions(group, app, access=Token(token), addr~, public_key~) is a complete signed-interactions HTTP server. See the HTTP interactions guide and the interactions_http, workers_echo, deno_echo, bun_echo, and vercel_echo examples. The separate Spin experiment uses WASIp2 and Spin's variables import for an immediate handler. It does not yet run the async App on a general Wasm host.

    The JavaScript host checks cover different boundaries:

    HostChecked hereRemaining host integration
    Cloudflare WorkersSigned requests and deferred REST in local workerdProduction deployment
    DenoDeno.serve adapter and signed requestsHosted lifecycle
    BunShared MoonBit handler and Bun.serve loopback endpointHosted lifecycle
    Node.jsShared MoonBit handler and Web APIsHTTP hosting and process lifecycle
    Vercel FunctionsNode.js fetch adapter and waitUntil handoffVercel packaging and hosted lifecycle
    Fastly ComputeFetchEvent and synchronous lifetime registration with a fake eventFastly compilation and hosted execution
    Lambda Function URLPayload v2 body and response conversion with local eventsAWS invocation and deployment

    Other JavaScript function platforms can reuse the handler if they provide Web Request, Response, crypto.subtle, and fetch. Their entry point, background-work API, deployment bundle, and execution limits still need platform-specific checks. The Spin experiment instead tests the WASIp2 component boundary. The host entry point survey records the other host conventions and the remaining API decisions, including multipart responses and background-work lifetime.

    The JavaScript REST client supports null-body 204 responses through moonbitlang/async@0.22.1; the workers_echo workerd suite covers a deferred interaction-response deletion end to end. Voice remains native-only; the gateway also runs on JavaScript and moonrun Wasm.

    #Cloudflare Workers gateway

    The workers_gateway example runs one gateway shard in a Durable Object with an outbound WebSocket. A bot accepts saved BotSession values through resume~; bot.sessions() returns snapshots containing both the Gateway session and its original READY metadata. The example stores them on a 30-second alarm so a restarted bot can process replayed events, including interactions arriving before RESUMED. Call bot.sessions() while bot.run() is active to capture live sessions:

    ///|
    fn restored_bot(
    app : @discord.App,
    token : String,
    saved : Map[Int32, @discord.BotSession],
    ) -> @discord.BotBuilder {
    @discord.BotBuilder(app, token~, resume=saved)
    }

    ///|
    fn current_bot_sessions(bot : @discord.Bot) -> Map[Int32, @discord.BotSession] {
    bot.sessions()
    }

    An alarm restarts a failed bot, and a five-minute cron reconciles the object's stored enabled state after eviction or deployment. /stop disables those restarts and clears the saved session because a graceful close invalidates it; the public controls require a separate bearer secret. Snapshots are periodic: they do not guarantee exactly-once event handling or restore application state or optional cache contents. Cloudflare can evict an object after an outbound socket has protected it for up to 15 minutes. Received WebSocket messages are limited to 32 MiB, CPU to 30 seconds per event by default, and a connected object incurs duration charges. See the example README for deployment and local test details.

    #Typed models

    Every entity decodes from real API payloads, and unknown enum values and unknown JSON keys round-trip through Unknown(...) variants, so a new Discord feature does not break your application. IDs are phantom-typed snowflakes — a UserId cannot be passed where a ChannelId is expected — that keep full precision above 2^53:

    let id : @model.MessageId = @model.Id::parse("175928847299117063")
    println(id.timestamp_ms()) // 1462015105796

    Wire models use T? for optional fields and Nullable[T] for nullable fields; PATCH methods expose plain optional arguments with clear_<field>=true flags for clearing. See the models and utilities guide.

    #REST without the gateway

    http.Client stands alone: typed wrappers over the routes, per-bucket rate limiting with a global window, automatic retry of 429s and of attempts that never reached Discord, multipart file uploads, and a raw client.request(route) escape hatch for anything not wrapped yet. Ordinary channel messages suppress @everyone and @here by default while still parsing user and role mentions.

    let client = @dhttp.Client(token)
    let message = client.create_message(channel_id, content="hello")
    client.create_message(channel_id, content="with reply", reply_to=message.id)
    |> ignore

    Every endpoint with a resource anchor is also available through client-bound refs, which chain from parent to child resource before applying a verb:

    client.guild_ref(guild_id).emoji_ref(emoji_id).edit(name="renamed") |> ignore

    Validation failures raise before any I/O, request failures use DiscordHttpError, and list endpoints expose stateful Paginator[T] values. The REST guide covers the full ref surface, allowed mentions, file uploads, pagination, and custom routes.

    Also in the box, each with its guide chapter:

    #Development

    For handler tests without a Discord connection, Client::offline answers REST calls from a function you write, and the native/JS/Wasm gaato/discord/testkit provides deterministic interaction and model fixtures. Import the testkit only in your package's for "test" block.

    The repository is a MoonBit workspace: this module is in discord/, next to gaato/discord_voice, gaato/dave, the examples, the guide, and the bot template. Run these from the repository root; they cover every member and build each against the in-tree versions of the others. A native build in the workspace runs every member's prebuild hook, so working in the repository needs Node.js on PATH:

    moon check --target native --deny-warn moon check --target js --deny-warn moon check --target wasm --deny-warn moon test --target native --release # debug native builds need a working tcc setup moon test --target js --release moon test --target wasm --release moon fmt moon info --target native # regenerate pkg.generated.mbti (API review signal) scripts/gen_extends.py # regenerate extends.mbt after adding or removing a ToJson type

    To build this module alone against its published dependencies, as a consumer does, run MOON_WORK=off moon -C discord check --target native.

    Look up any package, type, or symbol — including its docstring examples — from the terminal:

    moon ide doc "@discord" moon ide doc "@http.Client::create_message" moon ide doc "@model.Message" moon ide doc "@util.channel_permissions"

    Each package's pkg.generated.mbti is a concise public-interface snapshot; review its diff when checking API drift.

    Every MoonBit code block in this README has a compiled twin in src/readme_native_test.mbt, and code blocks marked mbt check in the guides and in docstrings compile and run as part of the test suite.

    #License

    Apache-2.0

    AllowedMentionType

    A mention category Discord may derive directly from message content.

    AllowedMentions

    Controls which mentions Discord resolves and notifies for an outgoing message.

    Use AllowedMentions(...) so parse categories and explicit ID lists cannot conflict. The stored arrays are copied during construction to preserve that invariant if callers later mutate their input arrays.

    AllowedMentionsError

    An invalid allowed-mentions configuration rejected during construction.

    App

    using @gaato/discord/app { type App }

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    AppBuilder

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    AppConfigError

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    AppCtx

    using @gaato/discord/app { type AppCtx }

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    AppWarning

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    ApplicationRef

    Golden-path names that a typical bot uses directly: application declarations and handler contexts, typed arguments and components, common models, the REST client, executors, and verification. Every type these names mention is exported here too, unless it belongs to one of the focused packages below; scripts/api_surface_audit.mbtx keeps it so.

    Not re-exported — import the focused package instead:
    • gaato/discord/http for resource refs and pagination.
    • gaato/discord/framework for the low-level router and the raw contexts used by Raw handlers.
    • gaato/discord/gateway for shard transport primitives.
    • gaato/discord/coordinator for multi-process coordination.
    • gaato/discord/telemetry for observability events.
    • gaato/discord/interaction for raw option builders, CommandSpec, and low-level option decoding.
    • gaato/discord/app for implementation-facing registration, spawning, waiting, and raw failure details.

    Arg

    One typed command option, combining its registration definition and reader.

    Args

    A typed collection of command options used for registration and decoding.

    Bot

    using @gaato/discord/bot { type Bot }

    Gateway executor and typed event descriptors (native, JavaScript, Wasm).

    BotBuilder

    Gateway executor and typed event descriptors (native, JavaScript, Wasm).

    BotConfigError

    Gateway executor and typed event descriptors (native, JavaScript, Wasm).

    BotError

    Gateway executor and typed event descriptors (native, JavaScript, Wasm).

    BotSession

    Gateway executor and typed event descriptors (native, JavaScript, Wasm).

    CacheLimit

    Bounds one cached resource by entry count, lifetime, or both.

    Negative limits clamp to zero. A max_entries of zero retains nothing, while a ttl_ms of zero makes entries expire immediately. TTL deadlines are fixed when an entry is written and are never extended by reads; expiry is enforced lazily, without background timers: every read and event first releases whatever has expired, with what it owns or holds.

    CacheLimits

    Per-resource bounds for an in-memory cache.

    Guild, channel, DM-channel, user, and recent-user limits are global. Role, member, voice-state, and presence limits apply independently per guild; message limits apply independently per channel. Everything keyed by a guild or channel leaves with it: when a cached guild or channel is deleted, evicted by its limit, or expires, its roles, members, voice states, presences, channels, threads, and messages go too, and per-guild and per-channel maps are dropped once empty. State for a guild or channel the cache does not hold (its retention is off, or the event arrived after it left) still obeys its per-guild or per-channel limit and goes when the guild or channel is deleted.

    Under the defaults every structure is bounded:
    • guilds and unavailable-guild flags by the guilds the bot is in (a new session's READY also drops the guilds it no longer lists);
    • a guild's channels, roles, members, voice states, and presences by what Discord holds per guild, times those guilds (a fresh GUILD_CREATE drops the channels and threads it no longer lists);
    • threads by the active threads of those guilds: a thread leaves when it is deleted, archived, replaced by a thread list sync, or (private) when the bot is removed from it;
    • channels without a guild (DMs and group DMs) by dm_channels, 128, evicting the least recently written with its messages;
    • messages by messages, 100 per channel, times the channels above;
    • users by the entries that hold them (members, the authors of cached messages, and the current user), plus recent_users, the 128 most recently seen users nothing holds.

    Passing CacheLimit() for dm_channels, recent_users, or messages lifts that bound. TTLs use fixed, non-sliding deadlines set at write time (re-storing an entry sets a new one) and are enforced lazily without background timers.

    CacheResources

    Selects which gateway resources an in-memory cache retains.

    Guilds, channels, roles, members, users, and voice states are enabled by default. Presences and messages are opt-in because they are high-churn and can dominate memory use on larger bots.

    ChannelRef

    Golden-path names that a typical bot uses directly: application declarations and handler contexts, typed arguments and components, common models, the REST client, executors, and verification. Every type these names mention is exported here too, unless it belongs to one of the focused packages below; scripts/api_surface_audit.mbtx keeps it so.

    Not re-exported — import the focused package instead:
    • gaato/discord/http for resource refs and pagination.
    • gaato/discord/framework for the low-level router and the raw contexts used by Raw handlers.
    • gaato/discord/gateway for shard transport primitives.
    • gaato/discord/coordinator for multi-process coordination.
    • gaato/discord/telemetry for observability events.
    • gaato/discord/interaction for raw option builders, CommandSpec, and low-level option decoding.
    • gaato/discord/app for implementation-facing registration, spawning, waiting, and raw failure details.

    CheckCtx

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    Client

    Golden-path names that a typical bot uses directly: application declarations and handler contexts, typed arguments and components, common models, the REST client, executors, and verification. Every type these names mention is exported here too, unless it belongs to one of the focused packages below; scripts/api_surface_audit.mbtx keeps it so.

    Not re-exported — import the focused package instead:
    • gaato/discord/http for resource refs and pagination.
    • gaato/discord/framework for the low-level router and the raw contexts used by Raw handlers.
    • gaato/discord/gateway for shard transport primitives.
    • gaato/discord/coordinator for multi-process coordination.
    • gaato/discord/telemetry for observability events.
    • gaato/discord/interaction for raw option builders, CommandSpec, and low-level option decoding.
    • gaato/discord/app for implementation-facing registration, spawning, waiting, and raw failure details.

    Command

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    CommandCheck

    type CommandCheck = async (
    CheckCtx
    ) -> Bool

    A command guard. Returning false produces HandlerError::CheckFailed; raise a more specific HandlerError for an expected denial. Any other error reaches the error policy unchanged.

    CommandPlan

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    CommandReply

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    CommandScope

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    ComponentCtx

    Invocation and response types every handler context hands out, and the unanswered ComponentCtx a component wait returns.

    ComponentDeferredCtx

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    ComponentHandler

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    ComponentImmediateCtx

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    ComponentReply

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    ComponentRoute

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    CooldownBucket

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    CooldownOutage

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    CustomIdCodec

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    CustomIdError

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    DeferredCtx

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    DiscordHttpError

    Golden-path names that a typical bot uses directly: application declarations and handler contexts, typed arguments and components, common models, the REST client, executors, and verification. Every type these names mention is exported here too, unless it belongs to one of the focused packages below; scripts/api_surface_audit.mbtx keeps it so.

    Not re-exported — import the focused package instead:
    • gaato/discord/http for resource refs and pagination.
    • gaato/discord/framework for the low-level router and the raw contexts used by Raw handlers.
    • gaato/discord/gateway for shard transport primitives.
    • gaato/discord/coordinator for multi-process coordination.
    • gaato/discord/telemetry for observability events.
    • gaato/discord/interaction for raw option builders, CommandSpec, and low-level option decoding.
    • gaato/discord/app for implementation-facing registration, spawning, waiting, and raw failure details.

    ErrorPolicy

    type ErrorPolicy = async (
    FailureCtx
    , Error) -> Unit

    Policy invoked for interaction, event, and service failures.

    Event

    A gateway dispatch event decoded by its wire event name.

    Unknown event names retain both the name and payload for forward compatibility.

    EventMiddleware

    Runs once per decoded gateway event, around the handler fan-out. next(event) spawns the typed and raw handlers for (a possibly transformed) event and returns once they are spawned, not completed. Not calling next drops the event for handlers only: cache updates, decode-error observation, collectors, and interaction routing are wire-level machinery that runs before this chain.

    EventType

    Gateway executor and typed event descriptors (native, JavaScript, Wasm).

    Events

    using @gaato/discord/bot { type Events }

    Gateway executor and typed event descriptors (native, JavaScript, Wasm).

    FailureCtx

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    FailureOrigin

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    FileUpload

    Golden-path names that a typical bot uses directly: application declarations and handler contexts, typed arguments and components, common models, the REST client, executors, and verification. Every type these names mention is exported here too, unless it belongs to one of the focused packages below; scripts/api_surface_audit.mbtx keeps it so.

    Not re-exported — import the focused package instead:
    • gaato/discord/http for resource refs and pagination.
    • gaato/discord/framework for the low-level router and the raw contexts used by Raw handlers.
    • gaato/discord/gateway for shard transport primitives.
    • gaato/discord/coordinator for multi-process coordination.
    • gaato/discord/telemetry for observability events.
    • gaato/discord/interaction for raw option builders, CommandSpec, and low-level option decoding.
    • gaato/discord/app for implementation-facing registration, spawning, waiting, and raw failure details.

    GatewayCapabilities

    Gateway capabilities: the Identify bitfield that opts a client into gateway behaviors (as opposed to Intents, which select events). Unknown bits are preserved. Wire format: JSON number.

    test "opt into channel obfuscation ahead of general availability" {
    let capabilities = @model.GatewayCapabilities::channel_obfuscation()
    inspect(capabilities.bits(), content="32768")
    }

    GatewayCompression

    Transport compression for BotBuilder(compression=...).

    GatewayCtx

    Gateway executor and typed event descriptors (native, JavaScript, Wasm).

    GuildInvocation

    Invocation and response types every handler context hands out, and the unanswered ComponentCtx a component wait returns.

    GuildRef

    Golden-path names that a typical bot uses directly: application declarations and handler contexts, typed arguments and components, common models, the REST client, executors, and verification. Every type these names mention is exported here too, unless it belongs to one of the focused packages below; scripts/api_surface_audit.mbtx keeps it so.

    Not re-exported — import the focused package instead:
    • gaato/discord/http for resource refs and pagination.
    • gaato/discord/framework for the low-level router and the raw contexts used by Raw handlers.
    • gaato/discord/gateway for shard transport primitives.
    • gaato/discord/coordinator for multi-process coordination.
    • gaato/discord/telemetry for observability events.
    • gaato/discord/interaction for raw option builders, CommandSpec, and low-level option decoding.
    • gaato/discord/app for implementation-facing registration, spawning, waiting, and raw failure details.

    HandlerError

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    HandlerIntake

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    ImmediateCtx

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    InMemoryCache

    A gateway-driven, resource-selective in-memory cache.

    update is the only mutation entry point. Entity getters return the model value retained by the cache; callers should treat it as immutable. List getters always allocate a new outer array so callers cannot mutate the cache's collections. Archived threads are not served: Discord stops sending their events, so the cache drops them when they are archived; fetch them over REST.

    InitialResponse

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    Intents

    Gateway intents: the server-side event subscription bitfield sent in Identify. Unknown bits are preserved. Wire format: JSON number.

    test "compose the gateway event subscriptions a bot needs" {
    let intents = @model.Intents::guilds() |
    @model.Intents::guild_messages() |
    @model.Intents::message_content()
    inspect(
    intents.bits(),
    content=(
    #|33281
    ),
    )
    assert_true(intents.contains(@model.Intents::message_content()))
    }

    InteractionCtx

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    InteractionEndpoint

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    InteractionHandler

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    InteractionHttpBody

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    InteractionHttpRequest

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    InteractionHttpResponse

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    InteractionOutcome

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    InteractionTarget

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    InteractionVerifier

    Pure MoonBit verification for Discord HTTP interaction signatures.

    InteractionVerifierError

    Pure MoonBit verification for Discord HTTP interaction signatures.

    InteractionsServer

    Discord HTTP-interactions server for native and moonrun Wasm.

    InvocationScope

    Invocation and response types every handler context hands out, and the unanswered ComponentCtx a component wait returns.

    MemberRef

    Golden-path names that a typical bot uses directly: application declarations and handler contexts, typed arguments and components, common models, the REST client, executors, and verification. Every type these names mention is exported here too, unless it belongs to one of the focused packages below; scripts/api_surface_audit.mbtx keeps it so.

    Not re-exported — import the focused package instead:
    • gaato/discord/http for resource refs and pagination.
    • gaato/discord/framework for the low-level router and the raw contexts used by Raw handlers.
    • gaato/discord/gateway for shard transport primitives.
    • gaato/discord/coordinator for multi-process coordination.
    • gaato/discord/telemetry for observability events.
    • gaato/discord/interaction for raw option builders, CommandSpec, and low-level option decoding.
    • gaato/discord/app for implementation-facing registration, spawning, waiting, and raw failure details.

    MessageRef

    Golden-path names that a typical bot uses directly: application declarations and handler contexts, typed arguments and components, common models, the REST client, executors, and verification. Every type these names mention is exported here too, unless it belongs to one of the focused packages below; scripts/api_surface_audit.mbtx keeps it so.

    Not re-exported — import the focused package instead:
    • gaato/discord/http for resource refs and pagination.
    • gaato/discord/framework for the low-level router and the raw contexts used by Raw handlers.
    • gaato/discord/gateway for shard transport primitives.
    • gaato/discord/coordinator for multi-process coordination.
    • gaato/discord/telemetry for observability events.
    • gaato/discord/interaction for raw option builders, CommandSpec, and low-level option decoding.
    • gaato/discord/app for implementation-facing registration, spawning, waiting, and raw failure details.

    MessageUpdate

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.
    using @gaato/discord/app { type Modal }

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    ModalDeferredCtx

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    ModalField

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    ModalFields

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    ModalHandle

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    ModalImmediateCtx

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    ModalOrigin

    Invocation and response types every handler context hands out, and the unanswered ComponentCtx a component wait returns.

    ModalReply

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    ModalShowError

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    ModalSubmitHandler

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    PendingEvent

    Gateway executor and typed event descriptors (native, JavaScript, Wasm).

    ResponseState

    Invocation and response types every handler context hands out, and the unanswered ComponentCtx a component wait returns.

    RestAccess

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    ServerConfigError

    Discord HTTP-interactions server for native and moonrun Wasm.

    ShardConfig

    Gateway executor and typed event descriptors (native, JavaScript, Wasm).

    SlashChild

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    SuggestCtx

    Data available while producing autocomplete choices. This context is transport-neutral and intentionally does not own an HTTP client.

    SyncReport

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    TargetUser

    Invocation and response types every handler context hands out, and the unanswered ComponentCtx a component wait returns.

    UserRef

    Golden-path names that a typical bot uses directly: application declarations and handler contexts, typed arguments and components, common models, the REST client, executors, and verification. Every type these names mention is exported here too, unless it belongs to one of the focused packages below; scripts/api_surface_audit.mbtx keeps it so.

    Not re-exported — import the focused package instead:
    • gaato/discord/http for resource refs and pagination.
    • gaato/discord/framework for the low-level router and the raw contexts used by Raw handlers.
    • gaato/discord/gateway for shard transport primitives.
    • gaato/discord/coordinator for multi-process coordination.
    • gaato/discord/telemetry for observability events.
    • gaato/discord/interaction for raw option builders, CommandSpec, and low-level option decoding.
    • gaato/discord/app for implementation-facing registration, spawning, waiting, and raw failure details.

    WaitFrom

    Gateway-free application declarations (built with AppBuilder), typed interaction handlers, command synchronization, and error policy contexts.

    API_VERSION

    let API_VERSION : Int

    Discord API version this library targets.

    action_row

    Build a message component row.

    arg_attachment

    fn arg_attachment(name~ : String, description~ : String, file_types? : Array[
    FileTypeFilter
    ], name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    Arg
    [
    Attachment
    ]

    Define a required attachment option resolved to its attachment object. file_types restricts which files the client lets the user pick (by extension only; still validate the contents).

    arg_bool

    fn arg_bool(name~ : String, description~ : String, name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    Arg
    [Bool]

    Define a required boolean option.

    arg_channel

    fn arg_channel(name~ : String, description~ : String, channel_types? : Array[
    ChannelType
    ], name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    Arg
    [
    Id
    [
    ChannelMarker
    ]]

    Define a required channel option.

    arg_int

    fn arg_int(name~ : String, description~ : String, min? : Int64, max? : Int64, choices? : Array[
    CommandOptionChoice
    ], suggest? : async (
    SuggestCtx
    , Int64) -> Array[
    CommandOptionChoice
    ], name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    Arg
    [Int64]

    Define a required integer option.

    arg_int32

    fn arg_int32(name~ : String, description~ : String, min? : Int, max? : Int, choices? : Array[
    CommandOptionChoice
    ], suggest? : async (
    SuggestCtx
    , Int) -> Array[
    CommandOptionChoice
    ], name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    Arg
    [Int]

    Define a required integer option that decodes to Int.

    Discord integers span the 53-bit safe range, so arg_int yields Int64. This variant keeps the narrowing safe instead of leaving a wrapping to_int() to the caller: a missing min or max is registered as the Int bound, so Discord rejects wider input before it is sent, and a received value outside the range fails decoding with ArgsDecodeError::Validation. With choices, only explicit bounds are registered, because the choices already close the value set.

    arg_mentionable

    fn arg_mentionable(name~ : String, description~ : String, name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    Arg
    [
    Id
    [
    GenericMarker
    ]]

    Define a required mentionable option.

    arg_number

    fn arg_number(name~ : String, description~ : String, min? : Double, max? : Double, choices? : Array[
    CommandOptionChoice
    ], suggest? : async (
    SuggestCtx
    , Double) -> Array[
    CommandOptionChoice
    ], name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    Arg
    [Double]

    Define a required floating-point number option.

    arg_role

    fn arg_role(name~ : String, description~ : String, name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    Arg
    [
    Id
    [
    RoleMarker
    ]]

    Define a required role option.

    arg_string

    fn arg_string(name~ : String, description~ : String, choices? : Array[
    CommandOptionChoice
    ], min_length? : Int, max_length? : Int, suggest? : async (
    SuggestCtx
    , String) -> Array[
    CommandOptionChoice
    ], name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    Arg
    [String]

    Define a required string option.

    arg_user

    fn arg_user(name~ : String, description~ : String, name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    Arg
    [
    Id
    [
    UserMarker
    ]]

    Define a required user option.

    button

    fn button(custom_id~ : String, label? : String, style? :
    ButtonStyle
    , emoji? :
    Emoji
    , disabled? : Bool) ->
    Component

    Build an interactive button.

    channel_select

    fn channel_select(custom_id~ : String, placeholder? : String, min_values? : Int, max_values? : Int, disabled? : Bool, required? : Bool, channel_types? : Array[
    ChannelType
    ]) ->
    Component

    Build a channel select menu, optionally restricted to channel_types; the chosen channels arrive via the component context's selected_channels.

    component_route

    Define a component route matching id or id:state. The id may itself contain :, so ids that are already in use elsewhere (bot:rolemenu) keep working. AppBuilder::build rejects empty ids, ids over 100 characters, and an id that extends another typed route's id at a : (ticket beside ticket:close), because the shorter route's state could then produce a custom id the longer route receives.

    test "build a component from its route" {
    let route = @app.component_route(id="page", state=@app.CustomIdCodec::int())
    let app = @app.AppBuilder()
    app.on_component(
    route,
    Immediate((_, page) => @app.ComponentReply::message(content="Page \{page}")),
    )
    let _ = @interaction.button(custom_id=route.custom_id(2), label="Next")
    assert_eq(route.custom_id(2), "page:2")
    app.build() |> ignore
    }

    container

    fn container(components~ : Array[
    Component
    ], accent_color? : Int, spoiler? : Bool) ->
    Component

    Build a container (components v2): groups child components in a rounded box with an optional accent_color (0xRRGGBB) stripe, similar to an embed. Reply and send layers set the required IS_COMPONENTS_V2 message flag automatically.

    dm_only

    fn dm_only() -> (async (
    CheckCtx
    ) -> Bool)

    Require a direct-message invocation.

    file_field

    fn file_field(custom_id~ : String, label~ : String, description? : String, min_values? : Int, max_values? : Int, file_types? : Array[
    FileTypeFilter
    ]) ->
    ModalField
    [Array[
    Attachment
    ]]

    Define a modern label-wrapped file upload. Decodes to the uploaded files' attachment objects (resolved by Discord alongside the submission). file_types filters selectable files by extension only — validate the contents yourself. min_values/max_values bound the file count.

    file_upload

    fn file_upload(custom_id~ : String, min_values? : Int, max_values? : Int, required? : Bool, file_types? : Array[
    FileTypeFilter
    ]) ->
    Component

    Build a file upload input for a modal (wrap it in label). file_types restricts selectable files by extension; min_values/max_values bound the file count (0-10 / 1-10).

    guild_only

    fn guild_only() -> (async (
    CheckCtx
    ) -> Bool)

    Require a guild invocation.

    int_choice

    fn int_choice(name~ : String, value~ : Int64, name_localizations? : Map[String, String]) ->
    CommandOptionChoice

    Describe an integer-valued option choice.

    label

    fn label(label~ : String, component~ :
    Component
    , description? : String) ->
    Component

    Wrap a modal component with a label and optional description. Modals require every input to be wrapped in a label; the typed ModalField builders do this automatically.
    fn link_button(url~ : String, label? : String, emoji? :
    Emoji
    , disabled? : Bool) ->
    Component

    Build a link button.

    Build a media gallery (components v2): a grid of 1–10 media items. Reply and send layers set the required IS_COMPONENTS_V2 message flag automatically.

    media_item

    fn media_item(url~ : String, description? : String, spoiler? : Bool) ->
    MediaGalleryItem

    Build one media gallery entry from an image or video url (https://... or attachment://<filename>), for use with media_gallery.

    mentionable_select

    fn mentionable_select(custom_id~ : String, placeholder? : String, min_values? : Int, max_values? : Int, disabled? : Bool, required? : Bool) ->
    Component

    Build a mentionable select menu accepting both users and roles.
    fn[S, A] modal(custom_id~ : String, title~ : String, state~ :
    CustomIdCodec
    [S], fields~ :
    ModalFields
    [A]) ->
    Modal
    [S, A]

    Define a typed modal. Like component_route, state encodes the value carried in the custom id after custom_id:; pass CustomIdCodec::unit() for a modal without state. Handlers receive the decoded state and form.

    test "construct and validate a typed modal" {
    let feedback = @app.modal(
    custom_id="feedback",
    title="Feedback",
    state=@app.CustomIdCodec::int(),
    fields=@app.ModalFields::of(
    @app.text_field(custom_id="details", label="Details").optional(),
    ),
    )
    let app = @app.AppBuilder()
    app.on_modal(feedback, Raw(_ => ()))
    app.build() |> ignore
    let _ : @app.ModalHandle = feedback.show(42)
    }

    number_choice

    fn number_choice(name~ : String, value~ : Double, name_localizations? : Map[String, String]) ->
    CommandOptionChoice

    Describe a number-valued option choice.

    required_permissions

    fn required_permissions(required :
    Permissions
    ) -> (async (
    CheckCtx
    ) -> Bool)

    Require effective guild permissions supplied on the interaction member. Discord does not provide member permissions for DM invocations.

    role_select

    fn role_select(custom_id~ : String, placeholder? : String, min_values? : Int, max_values? : Int, disabled? : Bool, required? : Bool) ->
    Component

    Build a role select menu; the chosen roles arrive via the component context's selected_roles.

    section

    Build a section (components v2): up to three text displays laid out next to an accessory (a thumbnail or button). Reply and send layers set the required IS_COMPONENTS_V2 message flag automatically.

    select_field

    fn select_field(custom_id~ : String, label~ : String, options~ : Array[
    SelectOption
    ], description? : String, placeholder? : String, min_values? : Int, max_values? : Int) ->
    ModalField
    [Array[String]]

    Define a modern label-wrapped string select.

    select_option

    fn select_option(label~ : String, value~ : String, description? : String, emoji? :
    Emoji
    , default? : Bool) ->
    SelectOption

    Build an option for a string select menu.

    separator

    fn separator(divider? : Bool, spacing? : Int) ->
    Component

    Build a separator (components v2): vertical padding between components, with an optional visible divider line and spacing size (1 = small, 2 = large). Reply and send layers set the required IS_COMPONENTS_V2 message flag automatically.

    serve_interactions

    async fn[X] serve_interactions(group :
    TaskGroup
    [X], app :
    App
    , addr~ : String, public_key~ : String, access~ :
    RestAccess
    , application_id? :
    Id
    [
    ApplicationMarker
    ], path? : String, deadline_ms? : Int, max_body_bytes? : Int, body_timeout_ms? : Int, header_timeout_ms? : Int, max_connections? : Int) ->
    InteractionsServer

    Start a Discord HTTP-interactions server in group on native or moonrun Wasm.

    Use port zero in addr to let the OS choose a free port, then read the resolved address from InteractionsServer::addr.

    A request body larger than max_body_bytes gets 413, and one not received within body_timeout_ms gets 408; both close the connection. Discord's interaction payloads are far below the 1 MiB default. A connection whose next request head does not arrive within header_timeout_ms, idle keep-alive time included, is closed. At most max_connections clients are served at once; more wait to be accepted. Non-positive limits raise ServerConfigError before the server listens.

    slash

    fn[A] slash(name~ : String, description~ : String, args~ :
    Args
    [A], handler~ :
    InteractionHandler
    [A], default_member_permissions? :
    Permissions
    , nsfw? : Bool, integration_types? : Array[
    ApplicationIntegrationType
    ], contexts? : Array[
    InteractionContextType
    ], name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    Command
    [A]

    Define a typed slash command.

    test "inspect a slash-command registration spec" {
    let command = @app.slash(
    name="echo",
    description="Echo text",
    args=@interaction.Args::of(
    @interaction.arg_string(name="text", description="Text to echo"),
    ),
    handler=Raw(_ => ()),
    )
    json_inspect(command.spec().to_json(), content={
    "name": "echo",
    "description": "Echo text",
    "options": [
    {
    "type": 3,
    "name": "text",
    "description": "Text to echo",
    "required": true,
    },
    ],
    })
    }

    slash_group

    fn slash_group(name~ : String, description~ : String, children~ : Array[
    SlashChild
    ], default_member_permissions? :
    Permissions
    , nsfw? : Bool, integration_types? : Array[
    ApplicationIntegrationType
    ], contexts? : Array[
    InteractionContextType
    ], name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    Command
    [Unit]

    Define a slash command whose children dispatch by submitted option path.

    string_choice

    fn string_choice(name~ : String, value~ : String, name_localizations? : Map[String, String]) ->
    CommandOptionChoice

    Describe a string-valued option choice.

    string_select

    fn string_select(custom_id~ : String, options~ : Array[
    SelectOption
    ], placeholder? : String, min_values? : Int, max_values? : Int, disabled? : Bool, required? : Bool) ->
    Component

    Build a string select menu.

    subcommand

    fn[A] subcommand(name~ : String, description~ : String, args~ :
    Args
    [A], handler~ :
    InteractionHandler
    [A], name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    SlashChild

    Define a typed slash subcommand.

    subcommand_group

    fn subcommand_group(name~ : String, description~ : String, children~ : Array[
    SlashChild
    ], name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    SlashChild

    Define a slash subcommand group. Nested groups are retained for validation and rejected by AppBuilder::build, never by panic or abort here.

    text_display

    fn text_display(content : String) ->
    Component

    Build a text display block (components v2): markdown content rendered in the message body. Reply and send layers set the required IS_COMPONENTS_V2 message flag automatically.

    text_field

    fn text_field(custom_id~ : String, label~ : String, style? :
    TextInputStyle
    , description? : String, placeholder? : String, min_length? : Int, max_length? : Int, value? : String) ->
    ModalField
    [String]

    Define a modern label-wrapped text input. value is its static prefill; Modal::show(values=...) can override it for one response.

    text_input

    fn text_input(custom_id~ : String, style? :
    TextInputStyle
    , placeholder? : String, min_length? : Int, max_length? : Int, required? : Bool, value? : String) ->
    Component

    Build a modal text input. style picks single-line Short (default) or multi-line Paragraph; value prefills the field. Wrap it with label before placing it in a modal (the typed ModalField builders handle this).

    thumbnail

    fn thumbnail(url~ : String, description? : String, spoiler? : Bool) ->
    Component

    Build a thumbnail accessory (components v2) from an image url (https://... or attachment://<filename>).

    user_command

    Define a USER context-menu command.

    test "inspect context-menu command kinds" {
    let user = @app.user_command(name="Inspect user", handler=Raw(_ => ()))
    let message = @app.message_command(
    name="Inspect message",
    handler=Raw(_ => ()),
    )
    json_inspect(user.spec().to_json(), content={
    "name": "Inspect user",
    "description": "",
    "type": 2,
    })
    json_inspect(message.spec().to_json(), content={
    "name": "Inspect message",
    "description": "",
    "type": 3,
    })
    }

    user_select

    fn user_select(custom_id~ : String, placeholder? : String, min_values? : Int, max_values? : Int, disabled? : Bool, required? : Bool) ->
    Component

    Build a user select menu; the chosen users arrive in resolved and via the component context's selected_users.

    verify_signature

    fn verify_signature(public_key~ : String, signature~ : String, timestamp~ : String, body~ : BytesView) -> Bool

    Verify a Discord HTTP interaction signature without retaining the key.

    This convenience function validates and expands public_key on every call. Long-lived adapters should construct and reuse an InteractionVerifier instead. Malformed keys and signatures return false.