discord

    An experimental Discord library for MoonBit: typed APIs, a native gateway with voice (DAVE E2EE), and JS/serverless HTTP interactions.

    discord
    bot
    gateway
    api
    voice
    interactions
    Download zip
    Author
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    15 days ago
    Downloads
    8

    Dependencies

    #discord.mbt

    CI Ask DeepWiki

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

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

    Status: experimental, pre-1.0. APIs may change between releases.

    Long-form guides live in src/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.

    Every native build compiles the Gateway package's zlib_stream.c, which always includes <zlib.h> even when compress=false. The zlib development headers are therefore required for all native builds: install zlib1g-dev on Debian/Ubuntu or zlib-devel on Fedora/openSUSE. The zlib shared library is additionally required at runtime when Gateway zlib-stream compression is used.

    #Install

    moon update moon add gaato/discord

    The interaction App, REST client, and data packages support native and JavaScript. The gateway Bot executor is native-only. Both are built on moonbitlang/async.

    #Quickstart

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

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

    ///|
    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_int(
    name="times",
    description="How many times (1-5)",
    min=1,
    max=5,
    ).with_default(1L),
    (text, times) => { text, times: times.to_int(), },
    )
    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.App()
    app.command(echo)
    let bot = @discord.Bot(app, token~)
    bot.on(@discord.Events::ready(), (_ctx, ready) => {
    println("ready as \{ready.user.username}")
    })
    bot.run()
    }

    #Packages

    PackageWhat it isNativeJS
    gaato/discordFacade: aliases for the types a typical application names directlyYesYes*
    gaato/discord/modelPure data: ~24 entity domains, gateway payloads, zero IOYesYes
    gaato/discord/telemetryStructured REST, gateway, and dispatch observability valuesYesYes
    gaato/discord/httpREST Client, routes, rate limiting, multipart uploadsYesYes
    gaato/discord/gatewayShard: connection state machine, heartbeat, resumeYesNo
    gaato/discord/voiceExperimental native voice gateway v8, DAVE, RTP, and Opus send/receiveYesNo
    gaato/discord/interactionCommand/component builders, typed args and autocomplete dataYesYes
    gaato/discord/frameworkInteraction routing, response gates, low-level response contextsYesYes
    gaato/discord/appGateway-free typed commands/components/modals, HTTP endpoint, sync and policyYesYes
    gaato/discord/botNative gateway executor and typed gateway event descriptorsYesNo
    gaato/discord/endpoint_httpNative signed-interactions HTTP server (serve_interactions)YesNo
    gaato/discord/cacheOpt-in, gateway-driven in-memory cacheYesYes
    gaato/discord/utilPure helpers: permissions, mentions, timestamps, CDN URLsYesYes
    gaato/discord/verifyPure MoonBit Ed25519 request verificationYesYes
    gaato/discord/ratelimitRate limiter trait + in-memory implementationYesYes
    gaato/discord/queueIdentify queue trait + in-memory implementationYesYes
    gaato/discord/coordinatorNative TCP coordinator for multi-process Identify and REST limitsYesNo

    * The facade's gateway and HTTP-server exports exist only on native. WebAssembly is not currently enabled or validated as an application target in this repository.

    Packages remain usable on their own. A REST-only tool needs http and model. The dependency graph, generated from the moon.pkg declarations by tools/package_graph and transitively reduced:

    Package dependency graph

    Amber nodes are native-only; every other package also runs on JS.

    #Examples

    Runnable programs live under src/examples/:

    • 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.
    • interactions_http: native signed-interactions HTTP server.
    • plugin_demo: a stateful feedback feature installed as a separate package.
    • 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

    App owns the interaction declaration: commands, components, modals, autocomplete routes, command synchronization, and the error policy. It has no gateway dependency. After building an App, choose an executor:

    • Bot(app, token~) connects to the gateway and routes InteractionCreate events through the App.
    • app.serve(group, 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, App, and Bot 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

    Bot 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 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.Bot(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)

    Native builds can join voice gateway v8 calls, play 20 ms Opus frames, and receive per-user Opus streams. Discord requires DAVE encryption, so voice applications use the native-only gaato/dave binding to Discord's official libdave for MLS and media encryption. The separate Rust library in voice-shim/ handles RTP transport AEAD only. gaato/dave currently pins upstream v1.2.0/cpp; gaato/discord pins transport component release voice-shim-v0.1.0. A native voice build can bootstrap and verify both prebuilt runtimes without installing Cargo. See the voice guide for installation and runtime limits, the dave_probe, voice_player, and voice_recorder examples, and the accepted design in src/voice/DESIGN.md.

    #Synchronization and failures

    CommandSync belongs to App and defaults to Global. The gateway executor synchronizes after its first READY; App::serve synchronizes during startup only when you pass sync=true. Guild and multi-guild targets are available.

    Synchronization uses Discord's bulk overwrite endpoints. A required PUT deletes commands registered outside this App from the selected scope. Use Disabled when another process owns registration.

    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, 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, token~).handle(body)
    }

    Serverless targets such as Cloudflare Workers are first class: adapters verify the raw request bytes with the reusable pure MoonBit @discord.InteractionVerifier before parsing. The workers_echo example 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, addr~, public_key~, token~) is a complete signed-interactions HTTP server. See the HTTP interactions guide and the interactions_http and workers_echo examples.

    The JavaScript REST client supports null-body 204 responses through moonbitlang/async@0.21.2; the workers_echo workerd suite covers a deferred interaction-response deletion end to end. Gateway and Voice transports remain native-only.

    #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 429 retry, 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

    moon check --target native --deny-warn moon check --target js --deny-warn moon test --target native --release # debug native builds need a working tcc setup moon test --target js --release moon fmt moon -C template fmt --check moon -C tools/package_graph fmt --check moon info --target native # regenerate pkg.generated.mbti (API review signal)

    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

    #discord.mbt

    CI Ask DeepWiki

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

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

    Status: experimental, pre-1.0. APIs may change between releases.

    Long-form guides live in src/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.

    Every native build compiles the Gateway package's zlib_stream.c, which always includes <zlib.h> even when compress=false. The zlib development headers are therefore required for all native builds: install zlib1g-dev on Debian/Ubuntu or zlib-devel on Fedora/openSUSE. The zlib shared library is additionally required at runtime when Gateway zlib-stream compression is used.

    #Install

    moon update moon add gaato/discord

    The interaction App, REST client, and data packages support native and JavaScript. The gateway Bot executor is native-only. Both are built on moonbitlang/async.

    #Quickstart

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

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

    ///|
    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_int(
    name="times",
    description="How many times (1-5)",
    min=1,
    max=5,
    ).with_default(1L),
    (text, times) => { text, times: times.to_int(), },
    )
    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.App()
    app.command(echo)
    let bot = @discord.Bot(app, token~)
    bot.on(@discord.Events::ready(), (_ctx, ready) => {
    println("ready as \{ready.user.username}")
    })
    bot.run()
    }

    #Packages

    PackageWhat it isNativeJS
    gaato/discordFacade: aliases for the types a typical application names directlyYesYes*
    gaato/discord/modelPure data: ~24 entity domains, gateway payloads, zero IOYesYes
    gaato/discord/telemetryStructured REST, gateway, and dispatch observability valuesYesYes
    gaato/discord/httpREST Client, routes, rate limiting, multipart uploadsYesYes
    gaato/discord/gatewayShard: connection state machine, heartbeat, resumeYesNo
    gaato/discord/voiceExperimental native voice gateway v8, DAVE, RTP, and Opus send/receiveYesNo
    gaato/discord/interactionCommand/component builders, typed args and autocomplete dataYesYes
    gaato/discord/frameworkInteraction routing, response gates, low-level response contextsYesYes
    gaato/discord/appGateway-free typed commands/components/modals, HTTP endpoint, sync and policyYesYes
    gaato/discord/botNative gateway executor and typed gateway event descriptorsYesNo
    gaato/discord/endpoint_httpNative signed-interactions HTTP server (serve_interactions)YesNo
    gaato/discord/cacheOpt-in, gateway-driven in-memory cacheYesYes
    gaato/discord/utilPure helpers: permissions, mentions, timestamps, CDN URLsYesYes
    gaato/discord/verifyPure MoonBit Ed25519 request verificationYesYes
    gaato/discord/ratelimitRate limiter trait + in-memory implementationYesYes
    gaato/discord/queueIdentify queue trait + in-memory implementationYesYes
    gaato/discord/coordinatorNative TCP coordinator for multi-process Identify and REST limitsYesNo

    * The facade's gateway and HTTP-server exports exist only on native. WebAssembly is not currently enabled or validated as an application target in this repository.

    Packages remain usable on their own. A REST-only tool needs http and model. The dependency graph, generated from the moon.pkg declarations by tools/package_graph and transitively reduced:

    Package dependency graph

    Amber nodes are native-only; every other package also runs on JS.

    #Examples

    Runnable programs live under src/examples/:

    • 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.
    • interactions_http: native signed-interactions HTTP server.
    • plugin_demo: a stateful feedback feature installed as a separate package.
    • 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

    App owns the interaction declaration: commands, components, modals, autocomplete routes, command synchronization, and the error policy. It has no gateway dependency. After building an App, choose an executor:

    • Bot(app, token~) connects to the gateway and routes InteractionCreate events through the App.
    • app.serve(group, 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, App, and Bot 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

    Bot 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 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.Bot(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)

    Native builds can join voice gateway v8 calls, play 20 ms Opus frames, and receive per-user Opus streams. Discord requires DAVE encryption, so voice applications use the native-only gaato/dave binding to Discord's official libdave for MLS and media encryption. The separate Rust library in voice-shim/ handles RTP transport AEAD only. gaato/dave currently pins upstream v1.2.0/cpp; gaato/discord pins transport component release voice-shim-v0.1.0. A native voice build can bootstrap and verify both prebuilt runtimes without installing Cargo. See the voice guide for installation and runtime limits, the dave_probe, voice_player, and voice_recorder examples, and the accepted design in src/voice/DESIGN.md.

    #Synchronization and failures

    CommandSync belongs to App and defaults to Global. The gateway executor synchronizes after its first READY; App::serve synchronizes during startup only when you pass sync=true. Guild and multi-guild targets are available.

    Synchronization uses Discord's bulk overwrite endpoints. A required PUT deletes commands registered outside this App from the selected scope. Use Disabled when another process owns registration.

    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, 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, token~).handle(body)
    }

    Serverless targets such as Cloudflare Workers are first class: adapters verify the raw request bytes with the reusable pure MoonBit @discord.InteractionVerifier before parsing. The workers_echo example 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, addr~, public_key~, token~) is a complete signed-interactions HTTP server. See the HTTP interactions guide and the interactions_http and workers_echo examples.

    The JavaScript REST client supports null-body 204 responses through moonbitlang/async@0.21.2; the workers_echo workerd suite covers a deferred interaction-response deletion end to end. Gateway and Voice transports remain native-only.

    #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 429 retry, 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

    moon check --target native --deny-warn moon check --target js --deny-warn moon test --target native --release # debug native builds need a working tcc setup moon test --target js --release moon fmt moon -C template fmt --check moon -C tools/package_graph fmt --check moon info --target native # regenerate pkg.generated.mbti (API review signal)

    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, typed interaction handlers, command synchronization, and error policy contexts.

    AppConfigError

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    AppCtx

    using @gaato/discord/app { type AppCtx }

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    ApplicationEmojiRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    ApplicationRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    Arg

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

    Args

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

    ArgsDecodeError

    Errors introduced by typed argument combinators.

    AudioSource

    Experimental native voice connection, playback, and receive APIs.

    AutoModerationRuleRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    AutocompleteCtx

    Context handed to an autocomplete handler.

    Bot

    using @gaato/discord/bot { type Bot }

    Native gateway executor and typed event descriptors.

    BotError

    Native gateway executor and typed event descriptors.

    ChannelRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    CheckCtx

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    Client

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    Command

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    CommandCheck

    A command guard. Returning false produces HandlerError::CheckFailed; expected denials can instead raise a more specific HandlerError.

    CommandCtx

    Context handed to a slash / context-menu command handler.

    CommandModel

    A typed view of one command invocation, decoded from its submitted options.

    Implement this by hand per command (MoonBit has no derive macro for it):

    struct Echo {
    text : String
    times : Int64?
    }

    impl @interaction.CommandModel for Echo with fn from_options(options) {
    { text: options.string("text"), times: options.int_opt("times"), }
    }

    Commands with subcommands are naturally modeled as an enum whose from_options matches on options.path().

    CommandOptions

    Typed reader over the options submitted with an application command.

    Subcommand and subcommand-group levels are flattened away on construction: path() reports the traversed names and the value accessors see only the leaf options. Required accessors raise OptionError; _opt variants return None when the option is absent but still raise if a present option has an unexpected shape.

    CommandReply

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    CommandSpec

    A declarative description of one application command, used both to register the command (its ToJson is the registration payload) and to route incoming interactions by command type and name.

    CommandSync

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    ComponentCtx

    Context handed to a message-component (button / select) handler.

    ComponentDeferredCtx

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    ComponentHandler

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    ComponentImmediateCtx

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    ComponentReply

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    ComponentWaiter

    type ComponentWaiter = async (String, Int?) ->
    ComponentCtx
    ?

    Executor-provided lookup behind wait_for_component: resolves the next component interaction matching a custom id, with an optional timeout in milliseconds.

    CooldownBucket

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    Coordinator

    Native cross-process Identify and REST rate-limit coordination.

    CoordinatorError

    Native cross-process Identify and REST rate-limit coordination.

    DeferredCtx

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    EmojiRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    EntitlementRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    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.

    EventType

    Native gateway executor and typed event descriptors.

    Events

    using @gaato/discord/bot { type Events }

    Native gateway executor and typed event descriptors.

    FailureCtx

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    FailureOrigin

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    FailureRaw

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    FileUpload

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    Framework

    Routes incoming interactions to declared command / component / modal handlers, and can register the declared commands with Discord.

    Registration methods return self for chaining. Feed every InteractionCreate gateway event to process.

    GatewayCtx

    Native gateway executor and typed event descriptors.

    GlobalCommandRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    GuildCommandRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    GuildInvocation

    Proof that an interaction was invoked in a guild: the guild id and the invoking member, both validated when the context was built.

    GuildRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    HandlerError

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    ImmediateCtx

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    InitialResponse

    Gateway-free application declarations, 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()))
    }

    InteractionContextError

    A malformed interaction that cannot satisfy the guarantees exposed by a typed handler context.

    InteractionEndpoint

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    InteractionHandler

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    InteractionOutcome

    Gateway-free application declarations, 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

    Native Discord HTTP-interactions server.

    InviteRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    InvocationScope

    The context in which an interaction was invoked.

    MemberRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    MessageRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.
    using @gaato/discord/app { type Modal }

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    ModalCtx

    Context handed to a modal-submit handler.

    ModalDeferredCtx

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    ModalField

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    ModalFields

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    ModalHandle

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    ModalImmediateCtx

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    ModalOrigin

    The interaction that opened a submitted modal.

    ModalSubmitHandler

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    OggOpusError

    Experimental native voice connection, playback, and receive APIs.

    OggOpusSource

    Experimental native voice connection, playback, and receive APIs.

    OggOpusWriter

    Experimental native voice connection, playback, and receive APIs.

    Paginator

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    RegisteredCommand

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    RemoteIdentifyQueue

    Native cross-process Identify and REST rate-limit coordination.

    RemoteRateLimiter

    Native cross-process Identify and REST rate-limit coordination.

    ResponsePhase

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    RoleRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    ScheduledEventRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    Shard

    Native Discord Gateway transport primitives.

    ShardConfig

    Native gateway executor and typed event descriptors.

    ShardEvent

    Native Discord Gateway transport primitives.

    SkuRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    SlashChild

    Gateway-free application declarations, typed interaction handlers, command synchronization, and error policy contexts.

    SoundboardSoundRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    Spawner

    type Spawner = async (async () -> Unit) -> Unit

    Executor-provided function that runs a handler task concurrently on the surrounding async runtime.

    StickerRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    SuggestCtx

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

    TargetUser

    The resolved target of a USER context-menu command.

    TelemetryEvent

    Structured observability events shared by REST and gateway runtimes.

    TemplateRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    UserRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    VoiceConnection

    Experimental native voice connection, playback, and receive APIs.

    VoiceConnectionState

    Experimental native voice connection, playback, and receive APIs.

    VoiceError

    Experimental native voice connection, playback, and receive APIs.

    VoiceEvent

    Experimental native voice connection, playback, and receive APIs.

    VoiceReceiveStream

    Experimental native voice connection, playback, and receive APIs.

    VoiceTelemetry

    Experimental native voice connection, playback, and receive APIs.

    WebhookMessageRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    WebhookRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    WebhookTokenRef

    Convenience aliases for the types a typical bot names directly, so the core loop can be written against @discord alone. Everything else — entity models, REST wrappers, gateway internals, option builders — lives in the focused packages: @discord/model, @discord/http, @discord/gateway, @discord/interaction, @discord/framework.

    API_VERSION

    let API_VERSION : Int

    Discord API version this library targets.

    SILENCE_FRAME

    let SILENCE_FRAME : Bytes

    Discord's canonical Opus silence frame.

    action_row

    Build a message component row.

    arg_attachment

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

    Define a required attachment option resolved to its attachment object.

    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_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.

    attachment_option

    fn attachment_option(name : String, description : String, required? : Bool, name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    CommandOption

    Describe an ATTACHMENT option.

    boolean_option

    fn boolean_option(name : String, description : String, required? : Bool, name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    CommandOption

    Describe a BOOLEAN option.

    button

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

    Build an interactive button.

    channel_option

    fn channel_option(name : String, description : String, required? : Bool, channel_types? : Array[
    ChannelType
    ], name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    CommandOption

    Describe a CHANNEL option.

    channel_select

    fn channel_select(custom_id~ : String, placeholder? : String, min_values? : Int, max_values? : Int, disabled? : 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.

    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

    Require a direct-message invocation.

    guild_only

    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.

    integer_option

    fn integer_option(name : String, description : String, required? : Bool, choices? : Array[
    CommandOptionChoice
    ], min_value? : Int64, max_value? : Int64, autocomplete? : Bool, name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    CommandOption

    Describe an INTEGER option.

    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_option

    fn mentionable_option(name : String, description : String, required? : Bool, name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    CommandOption

    Describe a MENTIONABLE option.

    mentionable_select

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

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

    Define a typed modal.

    test "construct and validate a typed modal" {
    let feedback = @app.modal(
    custom_id="feedback",
    title="Feedback",
    fields=@app.ModalFields::of(
    @app.text_field(custom_id="details", label="Details").optional(),
    ),
    )
    let app = @app.App(sync=Disabled)
    app.on_modal(feedback, Raw(_ => ()))
    app.validate()
    let _ : @app.ModalHandle = feedback.show(state="draft")
    }

    number_choice

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

    Describe a number-valued option choice.

    number_option

    fn number_option(name : String, description : String, required? : Bool, choices? : Array[
    CommandOptionChoice
    ], min_value? : Double, max_value? : Double, autocomplete? : Bool, name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    CommandOption

    Describe a NUMBER option.

    required_permissions

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

    role_option

    fn role_option(name : String, description : String, required? : Bool, name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    CommandOption

    Describe a ROLE option.

    role_select

    fn role_select(custom_id~ : String, placeholder? : String, min_values? : Int, max_values? : Int, disabled? : 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, client? :
    Client
    , token? : String, application_id? :
    Id
    [
    ApplicationMarker
    ], sync? : Bool, path? : String, deadline_ms? : Int) ->
    InteractionsServer

    Start a native Discord HTTP-interactions server in group.

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

    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_option

    fn string_option(name : String, description : String, required? : Bool, choices? : Array[
    CommandOptionChoice
    ], min_length? : Int, max_length? : Int, autocomplete? : Bool, name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    CommandOption

    Describe a STRING option.

    string_select

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

    Build a string select menu.

    sub_command

    fn sub_command(name : String, description : String, options? : Array[
    CommandOption
    ], name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    CommandOption

    Describe a SUB_COMMAND option.

    sub_command_group

    fn sub_command_group(name : String, description : String, sub_commands : Array[
    CommandOption
    ], name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    CommandOption

    Describe a SUB_COMMAND_GROUP option.

    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 App::validate, 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_option

    fn user_option(name : String, description : String, required? : Bool, name_localizations? : Map[String, String], description_localizations? : Map[String, String]) ->
    CommandOption

    Describe a USER option.

    user_select

    fn user_select(custom_id~ : String, placeholder? : String, min_values? : Int, max_values? : Int, disabled? : 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.