Sign in

    gaato/discord/app does not have a README file

    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.

    ComponentWaiter

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

    ErrorPolicy

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

    Policy invoked for interaction, event, and service failures.

    InteractionMiddleware

    type InteractionMiddleware = async (InteractionCtx, async () -> Unit) -> Unit

    Middleware around interaction dispatch: receives the read-only context and a next continuation. Call next() to continue the chain; skip it (typically after InteractionCtx::respond) to short-circuit the handler.

    Spawner

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

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

    AppConfigError

    pub(all) suberror AppConfigError {
    EmptyToken
    DuplicateCommand(name~ : String, first~ : SourceLoc, second~ : SourceLoc)
    DuplicateCommandPath(command~ : String, path~ : String)
    NestedSubcommandGroup(command~ : String, path~ : String)
    ChoicesWithAutocomplete(command~ : String, path~ : String)
    RequiredOptionAfterOptional(command~ : String, path~ : String)
    DuplicateComponentRoute(custom_id~ : String, first~ : SourceLoc, second~ : SourceLoc)
    DuplicateModalRoute(custom_id~ : String, first~ : SourceLoc, second~ : SourceLoc)
    EmptyComponentRoute
    EmptyModalRoute
    InvalidRouteId(custom_id~ : String, reason~ : String)
    InvalidModalFieldCount(custom_id~ : String, count~ : Int)
    DuplicateModalTextInput(custom_id~ : String, field~ : String)
    ModalOutsideLimits(custom_id~ : String, violation~ :
    LimitViolation
    )
    InvalidFileTypes(owner~ : String, reason~ : String)
    CommandOutsideLimits(command~ : String, violation~ :
    LimitViolation
    )
    InvalidCooldown(command~ : String, seconds~ : Int)
    InvalidMaxInFlight(value~ : Int)
    InvalidMaxPending(value~ : Int)
    } derive(
    Debug
    )

    Configuration errors detected before starting an application executor.

    CustomIdError

    pub(all) suberror CustomIdError {
    TooLong(custom_id~ : String, length~ : Int)
    SeparatorInSegment(segment~ : String)
    } derive(
    Debug
    )

    A custom id that exceeds Discord's limit or cannot round-trip its segments.

    HandlerError

    pub(all) suberror HandlerError {
    UserMessage(message~ : String, ephemeral~ : Bool)
    GuildOnly
    DmOnly
    CheckFailed
    MissingPermission(
    Permissions
    )
    OnCooldown(retry_after_ms~ : Int64)
    InvalidInput(name~ : String?, message~ : String)
    Malformed(reason~ : String)
    UnsupportedResponse(reason~ : String)
    Busy
    } derive(
    Debug
    )

    Expected command failures that an error policy can present to the user.

    Raise these from handlers, checks, or app middleware; the application's ErrorPolicy turns them into user-facing responses. A permission guard computed with @util.channel_permissions typically raises MissingPermission:

    test "guard a handler with a permission requirement" {
    fn require_send_messages(permissions : @model.Permissions) -> Unit raise {
    let required = @model.Permissions::send_messages()
    if !permissions.contains(required) {
    raise @app.HandlerError::MissingPermission(required)
    }
    }

    try require_send_messages(@model.Permissions::none()) catch {
    @app.HandlerError::MissingPermission(_) => ()
    other => raise other
    } noraise {
    _ => fail("expected MissingPermission")
    }
    }

    ModalShowError

    pub(all) suberror ModalShowError {
    InvalidCustomId(CustomIdError)
    UnknownCustomId(custom_id~ : String)
    NonTextInput(custom_id~ : String)
    ValueTooLong(custom_id~ : String, length~ : Int, max~ : Int)
    } derive(
    Debug
    )

    Why Modal::show could not prepare a modal response.

    App

    pub struct App {
    // private fields
    }

    Validated, immutable application declarations shared by gateway and HTTP executors. Build one with AppBuilder::build; every App has passed validation, so executors and attach never see an invalid declaration.

    App::attach

    Wire every registered command, component, modal, and autocomplete route into framework and return the AppCtx shared by handlers. Called by the built-in executors (Bot, the HTTP endpoint); only custom executors need it directly. Each call gets its own handler budget of max_in_flight. Once intake closes, no interaction handler starts: one waiting for capacity, or arriving later, is dropped with AppWarning::InteractionDroppedWhileStopping.

    App::plan_commands

    Fetch each scope's registered commands and plan their synchronization with the declared ones, without changing anything: a dry run of sync_commands. Entry points are always preserved; Keep also preserves other owners, while the default Delete plans to remove them, so check report().scopes[..].deleted before applying in a shared application.

    App::report_failure

    async fn App::report_failure(self : App, origin : FailureOrigin, error : Error) -> Unit

    Run the error policy for a failure raised outside interaction dispatch. Executors use this for event and service handlers; the policy context has no response target in that case.

    App::serve

    Build a gateway-free dispatcher.

    With access=Token(token) the endpoint owns a client built from the token and closes it when group finishes; with access=Client(client) it uses the caller's client. When application_id is omitted, this method resolves it through GET /applications/@me. Supplying a client and the id avoids startup HTTP requests. clock times callback deadlines, handler admission and the App's default cooldown store, and drives the client built from a token. Raises AppConfigError::EmptyToken for an empty token.

    Command registration is a deploy-time step. Call app.sync_commands(...) from a one-shot program (see examples/workers_echo/register), never per request.

    App::spawner

    fn App::spawner(self : App, group :
    TaskGroup
    [Unit], intake? : HandlerIntake) -> (async (async () -> Unit) -> Unit)

    Create a bounded task spawner for an executor-owned task group. Executors use it for non-interaction work such as event handlers; interaction handlers get their own budget inside attach. Once intake closes, a call returns without spawning, a call waiting for capacity gives up, and a task spawned but not yet started does not run its body.

    App::sync_commands

    Synchronize the declared commands and report each scope's changes, planning and applying one scope at a time (see plan_commands for a dry run). Entry points are always preserved; Keep also preserves other owners.

    App::warn

    fn App::warn(self : App, warning : AppWarning) -> Unit

    Emit a structured warning through the app's warning hook.

    AppBuilder

    pub struct AppBuilder {
    // private fields
    }

    Collects application declarations: commands, component and modal routes, autocomplete handlers, middleware, and failure hooks. build validates them into an immutable App.

    AppBuilder::AppBuilder

    fn AppBuilder::AppBuilder(max_in_flight? : Int, max_pending? : Int, cooldown_store? : &
    CooldownStore
    , cooldown_outage? : CooldownOutage) -> AppBuilder

    Start collecting application declarations. max_in_flight caps concurrently running interaction handlers; routing and component waiters are not counted, so a handler waiting for a component never blocks the interaction that wakes it. A gateway executor gives its event handlers a separate budget of the same size. max_pending caps the deferred interactions waiting for a handler permit; one more is refused with HandlerError::Busy before it is acknowledged, and a deferred interaction still waiting when its token expires (15 minutes after the executor received it) is dropped with a warning. Cooldowns use a fresh in-memory store for each built App, timed by the clock of the executor it is first attached to, unless a shared cooldown_store is supplied; when that store cannot answer, cooldown_outage decides whether commands run, and the first failure of each outage is reported as AppWarning::CooldownUnavailable.

    AppBuilder::autocomplete

    fn AppBuilder::autocomplete(self : AppBuilder, name : String, handler : async (
    AutocompleteCtx
    ) -> Unit) -> Unit

    Register a raw autocomplete handler. A raw handler takes precedence over Arg-level suggest handlers registered for the same command name.

    AppBuilder::build

    fn AppBuilder::build(self : AppBuilder) -> App raise AppConfigError

    Validate the declarations and freeze them into an App. Later changes to this builder, or to the commands and specs registered on it, do not affect the returned App.

    AppBuilder::command

    #callsite(autofill(loc))
    fn[A] AppBuilder::command(self : AppBuilder, command : Command[A], loc~ : SourceLoc) -> Unit

    Register a typed slash or context-menu command built with the command / user_command / message_command builders. loc is filled with the caller's location, which duplicate-registration errors report.

    AppBuilder::error_policy

    fn AppBuilder::error_policy(self : AppBuilder, policy : async (FailureCtx, Error) -> Unit) -> Unit

    Replace the error policy invoked when a handler raises. The default policy maps HandlerError variants to ephemeral user-facing messages. For Malformed and any other error it warns with the details and shows the user only a generic message.

    AppBuilder::middleware

    fn AppBuilder::middleware(self : AppBuilder, middleware : async (InteractionCtx, async () -> Unit) -> Unit) -> Unit

    Install middleware around every command, component, and modal handler (not autocomplete). First installed is outermost. Runs inside the error policy: raising HandlerError behaves exactly like a failing check.

    AppBuilder::on_component

    #callsite(autofill(loc))
    fn[A] AppBuilder::on_component(self : AppBuilder, route : ComponentRoute[A], handler : ComponentHandler[A], loc~ : SourceLoc) -> Unit

    Route a component's exact id or id:state, decoding state before the handler. Use the same route's custom_id when building buttons and selects. loc is filled with the caller's location.

    AppBuilder::on_component_raw

    #callsite(autofill(loc))
    fn AppBuilder::on_component_raw(self : AppBuilder, prefix~ : String, handler : async (
    ComponentCtx
    ) -> Unit, loc~ : SourceLoc) -> Unit

    Register a fully raw literal-prefix component route. Longer prefixes win. AppBuilder::build rejects empty or duplicate effective prefixes. loc is filled with the caller's location.

    AppBuilder::on_modal

    #callsite(autofill(loc))
    fn[S, A] AppBuilder::on_modal(self : AppBuilder, modal : Modal[S, A], handler : ModalSubmitHandler[S, A], loc~ : SourceLoc) -> Unit

    Route submissions of the typed modal to handler; field values are decoded through the modal's ModalFields before the handler runs. Matches the exact modal id or that id followed by : and state. loc is filled with the caller's location.

    AppBuilder::on_modal_raw

    #callsite(autofill(loc))
    fn AppBuilder::on_modal_raw(self : AppBuilder, prefix~ : String, handler : async (
    ModalCtx
    ) -> Unit, loc~ : SourceLoc) -> Unit

    Register a fully raw literal-prefix modal route. Longer prefixes win. AppBuilder::build rejects empty or duplicate effective prefixes. loc is filled with the caller's location.

    AppBuilder::on_warn

    fn AppBuilder::on_warn(self : AppBuilder, hook : (AppWarning) -> Unit) -> Unit

    Replace the warning hook used for non-fatal diagnostics. The hook receives an AppWarning; by default its one-line to_string() form is printed.

    AppBuilder::register

    #callsite(autofill(loc))
    fn AppBuilder::register(self : AppBuilder, command : RegisteredCommand, loc~ : SourceLoc) -> Unit

    Register a type-erased command. App::command is the typed entry point; use this when composing pre-erased commands, e.g. from a plugin. loc is filled with the caller's location.

    AppCtx

    pub struct AppCtx {
    // private fields
    }

    Transport-neutral services available to interaction handlers.

    Executors construct these values; the public API exposes read-only accessors.

    AppCtx::application_id

    The application id learned during startup.

    AppCtx::application_ref

    The current application bound to its Discord REST client.

    AppCtx::http

    The Discord REST client used by this application.

    AppCtx::latency_ms

    fn AppCtx::latency_ms(self : AppCtx) -> Int64?

    The latest gateway heartbeat round-trip time in milliseconds. For a multi-shard bot, this is the arithmetic mean of shards that have measured latency. HTTP interaction executors and gateway connections awaiting their first heartbeat acknowledgement return None; use GatewayCtx::latency_ms() when the exact value for one shard is required.

    AppWarning

    pub(all) enum AppWarning {
    AutoDeferFailed(target~ : String, error~ : Error)
    InteractionDropped(target~ : String)
    DeferredInteractionExpired(target~ : String)
    InteractionDroppedWhileStopping(target~ : String)
    ErrorResponseWithoutInteraction(summary~ : String)
    ErrorResponseExpired(summary~ : String)
    MalformedInteraction(origin~ : FailureOrigin, reason~ : String)
    UnhandledFailure(origin~ : FailureOrigin, error~ : Error)
    ErrorPolicyFailed(error~ : Error)
    AutocompleteTruncated(command_path~ : String, option~ : String, count~ : Int)
    HandlerFailed(target~ : String, error~ : Error)
    WaiterUnavailable
    InteractionProcessingFailed(error~ : Error)
    Executor(source~ : String, message~ : String)
    DeferredInteractionsRefused(max_pending~ : Int)
    CooldownUnavailable(reason~ : String, outage~ : CooldownOutage)
    } derive(
    Debug
    )

    Something the App or its executor handled on its own but an operator should know about. Delivered to AppBuilder::on_warn; without a hook the App prints it. Show renders a one-line message for every variant, so a hook that only logs can call to_string().
    impl Show for AppWarning

    AppWarning::to_string

    fn AppWarning::to_string(self : AppWarning) -> String

    The one-line message, as the default hook prints it.

    CheckCtx

    pub struct CheckCtx {
    // private fields
    }

    Read-only context passed to command checks before argument decoding and handler execution.

    CheckCtx::app

    fn CheckCtx::app(self : CheckCtx) -> AppCtx

    The app-level services: the REST client and application id.

    CheckCtx::guild_id

    The guild the interaction was invoked in, or None outside guilds. Use guild_scope() for flows that require a guild; this accessor is for maybe-guild flows where DMs are valid.

    CheckCtx::guild_scope

    The validated guild invocation. In a DM this raises HandlerError::GuildOnly, which the error policy renders normally.

    CheckCtx::interaction

    The full interaction payload.

    CheckCtx::scope

    Guild or DM invocation scope, carrying the invoking member or user.

    CheckCtx::user

    The invoking user.

    Command

    pub struct Command[A] {
    // private fields
    }

    An application-command definition with a context-aware decoder.

    Command::check

    fn[A] Command::check(self : Command[A], check : async (CheckCtx) -> Bool) -> Command[A]

    Add a pre-execution check. Checks run in registration order after middleware and before cooldown, argument decoding, and handler dispatch, inside Discord's three-second initial-response window. A Deferred command is acknowledged automatically when 1000 ms of that budget remain; immediate and raw commands are not, so keep their checks fast or cache the results. Returning false is equivalent to HandlerError::CheckFailed. Returns a new command; self keeps its own checks.

    Command::cooldown

    fn[A] Command::cooldown(self : Command[A], seconds~ : Int, bucket? : CooldownBucket) -> Command[A]

    Apply a fixed-window cooldown to this command. The window starts when all checks pass, before argument decoding and handler execution. Returns a new command; self keeps its own cooldown.

    Command::erase

    fn[A] Command::erase(self : Command[A]) -> RegisteredCommand

    Erase the decoded argument type while retaining registration and dispatch.

    Command::spec

    The command registration specification.

    CommandPlan

    Planned synchronizations for each requested scope, in order. Inspect report() before apply.

    CommandPlan::apply

    Apply every scope's plan in order and report what changed.

    CommandPlan::report

    fn CommandPlan::report(self : CommandPlan) -> SyncReport

    What applying the plan would change, scope by scope.

    CommandReply

    pub(all) enum CommandReply {
    Message(InitialResponse)
    ShowModal(ModalHandle)
    }

    An initial response returned by an immediate command handler.

    CommandReply::message

    Construct an immediate message reply.

    CommandScope

    Which command scope a synchronization targets.

    ComponentDeferredCtx

    pub struct ComponentDeferredCtx {
    // private fields
    }

    Component context available after a deferred callback.

    ComponentDeferredCtx::app

    The app-level services: the REST client and application id.

    ComponentDeferredCtx::custom_id

    fn ComponentDeferredCtx::custom_id(self : ComponentDeferredCtx) -> String

    The full custom id of the component that fired.

    ComponentDeferredCtx::delete_followup

    Delete a followup message sent for this interaction.

    ComponentDeferredCtx::delete_original

    async fn ComponentDeferredCtx::delete_original(self : ComponentDeferredCtx) -> Unit

    Delete the original response.

    ComponentDeferredCtx::edit_followup

    Edit a followup message sent for this interaction. New files replace its attachments unless keep_attachments lists the ones to retain.

    ComponentDeferredCtx::edit_original

    Edit the original response after deferring. For DeferredUpdate handlers this edits the component's host message. New files replace the existing attachments unless keep_attachments lists the ones to retain; poll can be added only here, while editing the deferred response.

    ComponentDeferredCtx::followup

    Send a followup message after the initial deferred response.

    ComponentDeferredCtx::get_followup

    Fetch a followup message sent for this interaction.

    ComponentDeferredCtx::guild_id

    The guild the interaction was invoked in, or None outside guilds. Use guild_scope() for flows that require a guild; this accessor is for maybe-guild flows where DMs are valid.

    ComponentDeferredCtx::guild_scope

    The validated guild invocation. In a DM this raises HandlerError::GuildOnly, which the error policy renders normally.

    ComponentDeferredCtx::interaction

    The full interaction payload.

    ComponentDeferredCtx::message

    The message hosting the component.

    ComponentDeferredCtx::original

    Fetch the original response.

    ComponentDeferredCtx::raw

    Escape hatch for advanced inspection. Calling response methods on the raw value opts out of this wrapper's response discipline. Failure handling still follows the gate's real response state.

    ComponentDeferredCtx::scope

    Guild or DM invocation scope, carrying the invoking member or user.

    ComponentDeferredCtx::selected_channels

    Resolved partial channel objects for a channel select.

    ComponentDeferredCtx::selected_roles

    Resolved role objects for a role or mentionable select.

    ComponentDeferredCtx::selected_users

    Resolved user objects for a user or mentionable select.

    ComponentDeferredCtx::user

    The invoking user.

    ComponentDeferredCtx::values

    fn ComponentDeferredCtx::values(self : ComponentDeferredCtx) -> Array[String]

    The raw string values selected in a select menu (empty for buttons).

    ComponentDeferredCtx::wait_for_component

    async fn[A] ComponentDeferredCtx::wait_for_component(self : ComponentDeferredCtx, route : ComponentRoute[A], state : A, from? : WaitFrom, timeout_ms? : Int) ->
    ComponentCtx
    ?

    Wait for the next click on the component route.custom_id(state) identifies — build the button with the same route and state. By default only the invoking user may satisfy the wait; pass from=Anyone for a public flow or from=User(id) for someone else. Returns None on timeout, or at once without a gateway connection. Raises CustomIdError when the state does not encode.

    The returned context has not been answered, and the waited interaction does not pass through admission or the automatic defer: respond to it (for example update_message or defer_update) within three seconds.

    ComponentHandler

    pub(all) enum ComponentHandler[A] {
    Immediate(async (ComponentImmediateCtx, A) -> ComponentReply)
    DeferredUpdate(async (ComponentDeferredCtx, A) -> Unit)
    DeferredMessage(ephemeral~ : Bool, async (ComponentDeferredCtx, A) -> Unit)
    Raw(async (
    ComponentCtx
    ) -> Unit)
    }

    Execution strategy for a component route.

    ComponentImmediateCtx

    pub struct ComponentImmediateCtx {
    // private fields
    }

    Read-only component context for handlers returning their initial callback.

    ComponentImmediateCtx::app

    The app-level services: the REST client and application id.

    ComponentImmediateCtx::custom_id

    fn ComponentImmediateCtx::custom_id(self : ComponentImmediateCtx) -> String

    The full custom id of the component that fired.

    ComponentImmediateCtx::guild_id

    The guild the interaction was invoked in, or None outside guilds. Use guild_scope() for flows that require a guild; this accessor is for maybe-guild flows where DMs are valid.

    ComponentImmediateCtx::guild_scope

    The validated guild invocation. In a DM this raises HandlerError::GuildOnly, which the error policy renders normally.

    ComponentImmediateCtx::interaction

    The full interaction payload.

    ComponentImmediateCtx::message

    The message hosting the component.

    ComponentImmediateCtx::remaining_ms

    fn ComponentImmediateCtx::remaining_ms(self : ComponentImmediateCtx) -> Int?

    Milliseconds left to return the initial response, or None when the executor set no deadline.

    ComponentImmediateCtx::scope

    Guild or DM invocation scope, carrying the invoking member or user.

    ComponentImmediateCtx::selected_channels

    Resolved partial channel objects for a channel select.

    ComponentImmediateCtx::selected_roles

    Resolved role objects for a role or mentionable select.

    ComponentImmediateCtx::selected_users

    Resolved user objects for a user or mentionable select.

    ComponentImmediateCtx::unchecked_raw

    Escape hatch to the underlying context. Its response methods bypass this handler's discipline of returning its initial response: a callback sent through it makes the returned reply fail with ResponseGateError::AlreadyResponded, which reaches the error policy. Prefer the read-only accessors on this context. Each use raises an alert_unchecked warning; once the bypass is deliberate, silence it with warnings = "-alert_unchecked" in that package's moon.pkg.

    ComponentImmediateCtx::user

    The invoking user.

    ComponentImmediateCtx::values

    fn ComponentImmediateCtx::values(self : ComponentImmediateCtx) -> Array[String]

    The raw string values selected in a select menu (empty for buttons).

    ComponentReply

    pub(all) enum ComponentReply {
    UpdateMessage(MessageUpdate)
    Message(InitialResponse)
    ShowModal(ModalHandle)
    }

    Initial response returned by an immediate component handler.

    ComponentReply::message

    Reply with a new message; set ephemeral=true to show it only to the invoking user.

    ComponentReply::update_message

    Reply by editing the message that hosts the component. Omitted fields keep their current value; clear_content removes the text, and keep_attachments lists the existing attachments to retain alongside new files (see MessageUpdate).

    ComponentRoute

    pub struct ComponentRoute[A] {
    // private fields
    }

    A component's route identity and the codec shared by producer and handler.

    ComponentRoute::custom_id

    fn[A] ComponentRoute::custom_id(self : ComponentRoute[A], state : A) -> String raise CustomIdError

    Build the id or id:state, enforcing Discord's 100-character limit. An empty encoded state produces the bare id.

    ComponentRoute::decode

    fn[A] ComponentRoute::decode(self : ComponentRoute[A], custom_id : String) -> A raise HandlerError

    Decode the state from a custom id this route produced — the inverse of custom_id. Typed handlers receive the state already decoded; use this for ids that reach you undecoded, such as the ComponentCtx returned by wait_for_component, and in tests. An id that belongs to another route, or whose state does not decode, raises HandlerError::Malformed. The decoded state is untrusted input.

    test {
    let route = @app.component_route(
    id="ticket-close",
    state=@app.CustomIdCodec::int(),
    )
    assert_eq(route.decode(route.custom_id(42)), 42)
    }

    CooldownBucket

    pub(all) enum CooldownBucket {
    User
    Guild
    Global
    } derive(Eq,
    Debug
    )

    Identity used to share a fixed-window command cooldown.

    CooldownOutage

    pub(all) enum CooldownOutage {
    Allow
    Refuse
    } derive(Eq,
    Debug
    )

    Whether a command with a cooldown runs while the cooldown store cannot answer.

    CustomIdCodec

    pub struct CustomIdCodec[A] {
    // private fields
    }

    A reversible text encoding for the state carried in a custom id. Custom encode/decode functions and imap mappings must preserve round trips.

    CustomIdCodec::custom

    fn[A] CustomIdCodec::custom(encode~ : (A) -> String, decode~ : (String) -> A raise) -> CustomIdCodec[A]

    Define a reversible encoding. A decode failure that is not a HandlerError reaches the error policy as HandlerError::Malformed: component state is produced by the app, so state that does not decode is stale or tampered.

    CustomIdCodec::decode

    fn[A] CustomIdCodec::decode(self : CustomIdCodec[A], text : String) -> A raise HandlerError

    Decode untrusted state. The handler must still authorize the invoking user.

    CustomIdCodec::encode

    fn[A] CustomIdCodec::encode(self : CustomIdCodec[A], value : A) -> String raise CustomIdError

    Encode state, rejecting ambiguous zip segments and text over 100 characters. The complete route id is checked separately by ComponentRoute::custom_id.

    CustomIdCodec::id

    Encode and decode a snowflake without losing its phantom resource type.

    CustomIdCodec::imap

    fn[A, B] CustomIdCodec::imap(self : CustomIdCodec[A], to~ : (A) -> B raise, from~ : (B) -> A) -> CustomIdCodec[B]

    Map a codec to another type using a fallible decode and a reverse mapping.

    CustomIdCodec::int

    fn CustomIdCodec::int() -> CustomIdCodec[Int]

    Encode and decode a decimal integer.

    CustomIdCodec::string

    fn CustomIdCodec::string() -> CustomIdCodec[String]

    Preserve the state string, including separators in a final zip segment.

    CustomIdCodec::unit

    fn CustomIdCodec::unit() -> CustomIdCodec[Unit]

    Encode unit as no state at all, producing only the route id.

    CustomIdCodec::zip

    fn[A, B] CustomIdCodec::zip(self : CustomIdCodec[A], other : CustomIdCodec[B]) -> CustomIdCodec[(A, B)]

    Join two encodings with : and split at the first : on decode. Encoding rejects a separator in the non-final segment. Nest additional segments on the right, e.g. a.zip(b.zip(c)).

    DeferredCtx

    pub struct DeferredCtx {
    // private fields
    }

    Command context made available after the initial deferred response.

    DeferredCtx::app

    fn DeferredCtx::app(self : DeferredCtx) -> AppCtx

    The app-level services: the REST client and application id.

    DeferredCtx::delete_followup

    Delete a followup message sent for this interaction.

    DeferredCtx::delete_original

    async fn DeferredCtx::delete_original(self : DeferredCtx) -> Unit

    Delete the original response.

    DeferredCtx::edit_followup

    Edit a followup message sent for this interaction. New files replace its attachments unless keep_attachments lists the ones to retain.

    DeferredCtx::edit_original

    Edit the original response after deferring. New files replace the existing attachments unless keep_attachments lists the ones to retain; poll can be added only here, while editing the deferred response.

    DeferredCtx::followup

    Send a followup message after the initial deferred response.

    DeferredCtx::get_followup

    Fetch a followup message sent for this interaction.

    DeferredCtx::guild_id

    The guild the interaction was invoked in, or None outside guilds. Use guild_scope() for flows that require a guild; this accessor is for maybe-guild flows where DMs are valid.

    DeferredCtx::guild_scope

    The validated guild invocation. In a DM this raises HandlerError::GuildOnly, which the error policy renders normally.

    DeferredCtx::interaction

    The full interaction payload.

    DeferredCtx::original

    Fetch the original response.

    DeferredCtx::raw

    Escape hatch for advanced inspection. Calling response methods on the raw value opts out of this wrapper's response discipline. Failure handling still follows the gate's real response state.

    DeferredCtx::scope

    Guild or DM invocation scope, carrying the invoking member or user.

    DeferredCtx::user

    The invoking user.

    DeferredCtx::wait_for_component

    async fn[A] DeferredCtx::wait_for_component(self : DeferredCtx, route : ComponentRoute[A], state : A, from? : WaitFrom, timeout_ms? : Int) ->
    ComponentCtx
    ?

    Wait for the next click on the component route.custom_id(state) identifies — build the button with the same route and state. By default only the invoking user may satisfy the wait; pass from=Anyone for a public flow or from=User(id) for someone else. Returns None on timeout, or at once without a gateway connection. Raises CustomIdError when the state does not encode.

    The returned context has not been answered, and the waited interaction does not pass through admission or the automatic defer: respond to it (for example update_message or defer_update) within three seconds.

    FailureCtx

    pub struct FailureCtx {
    // private fields
    }

    Context handed to the error policy when a handler fails.

    FailureCtx::interaction

    The interaction payload associated with this failure. Event and service failures return None.

    FailureCtx::origin

    fn FailureCtx::origin(self : FailureCtx) -> FailureOrigin

    The operation whose handler failed.

    FailureCtx::raw

    fn FailureCtx::raw(self : FailureCtx) -> FailureRaw?

    Access the underlying interaction context. Event, service, and autocomplete failures return None. Direct responses through this escape hatch may fail unless the response state is checked first; normally use respond_error().

    FailureCtx::respond_error

    async fn FailureCtx::respond_error(self : FailureCtx, content? : String, embeds? : Array[
    Embed
    ], components? : Array[
    Component
    ], files? : Array[
    FileUpload
    ], allowed_mentions? :
    AllowedMentions
    , ephemeral? : Bool) -> Unit

    Send an error without violating Discord's one-initial-response rule. A pending callback gets an initial response; a deferred message whose loading placeholder is still showing is edited in place (keeping the visibility chosen when deferring); any other sent or unconfirmed callback gets a followup. Expired callbacks and failures without a raw context send a payload summary to the warning hook.

    FailureCtx::response_state

    Read the gate's current state. Event, service, and autocomplete failures have no raw context and return None.

    FailureCtx::user

    The user who invoked the failed interaction. Event and service failures return None.

    FailureOrigin

    pub(all) enum FailureOrigin {
    Command(name~ : String)
    Autocomplete(name~ : String)
    Component(custom_id~ : String)
    Modal(custom_id~ : String)
    Event(kind~ :
    EventKind
    )
    Service(name~ : String)
    } derive(
    Debug
    )

    The operation whose handler failed.

    FailureRaw

    The underlying interaction context for a failure.

    Event, service, and autocomplete failures have no raw context. Responding through these contexts without checking the response state may fail; prefer FailureCtx::respond_error() for ordinary error responses.

    HandlerIntake

    pub struct HandlerIntake {
    // private fields
    }

    Whether an executor still starts handlers. An executor that stops gracefully closes its intake: from then on no handler starts, and handlers waiting for capacity give up at once; handlers already running are not affected. Pass one to App::spawner and App::attach.

    HandlerIntake::HandlerIntake

    fn HandlerIntake::HandlerIntake() -> HandlerIntake

    An open intake.

    HandlerIntake::close

    fn HandlerIntake::close(self : HandlerIntake) -> Unit

    Stop starting handlers, and wake the ones waiting for capacity so they give up. Closing twice is harmless.

    HandlerIntake::is_open

    fn HandlerIntake::is_open(self : HandlerIntake) -> Bool

    Whether handlers may still start.

    ImmediateCtx

    pub struct ImmediateCtx {
    // private fields
    }

    Read-only command context for handlers that return their initial response.

    ImmediateCtx::app

    fn ImmediateCtx::app(self : ImmediateCtx) -> AppCtx

    The app-level services: the REST client and application id.

    ImmediateCtx::guild_id

    The guild the interaction was invoked in, or None outside guilds. Use guild_scope() for flows that require a guild; this accessor is for maybe-guild flows where DMs are valid.

    ImmediateCtx::guild_scope

    The validated guild invocation. In a DM this raises HandlerError::GuildOnly, which the error policy renders normally.

    ImmediateCtx::interaction

    The full interaction payload.

    ImmediateCtx::remaining_ms

    fn ImmediateCtx::remaining_ms(self : ImmediateCtx) -> Int?

    Milliseconds left to return the initial response, or None when the executor set no deadline.

    ImmediateCtx::scope

    Guild or DM invocation scope, carrying the invoking member or user.

    ImmediateCtx::unchecked_raw

    Escape hatch to the underlying context. Its response methods bypass this handler's discipline of returning its initial response: a callback sent through it makes the returned reply fail with ResponseGateError::AlreadyResponded, which reaches the error policy. Prefer the read-only accessors on this context. Each use raises an alert_unchecked warning; once the bypass is deliberate, silence it with warnings = "-alert_unchecked" in that package's moon.pkg.

    ImmediateCtx::user

    The invoking user.

    InitialResponse

    pub struct InitialResponse {
    // private fields
    }

    A declarative initial message response returned by an immediate handler.

    InteractionCtx

    pub struct InteractionCtx {
    // private fields
    }

    Read-only context handed to interaction middleware, before any initial response has been sent.

    InteractionCtx::guild_id

    The guild the interaction was invoked in, or None outside guilds. Use guild_scope() for flows that require a guild; this accessor is for maybe-guild flows where DMs are valid.

    InteractionCtx::guild_scope

    The validated guild invocation. In a DM this raises HandlerError::GuildOnly, which the error policy renders normally.

    InteractionCtx::interaction

    The full interaction payload.

    InteractionCtx::respond

    async fn InteractionCtx::respond(self : InteractionCtx, message : String, ephemeral? : Bool) -> Unit

    Answer from middleware, typically before short-circuiting the handler. While the callback is pending this is the initial response; if a deferring route's watchdog already acknowledged, it replaces the loading placeholder, whose visibility was fixed when deferring.

    InteractionCtx::scope

    Guild or DM invocation scope, carrying the invoking member or user.

    InteractionCtx::target

    The routed command, component, or modal being invoked.

    InteractionCtx::user

    The invoking user.

    InteractionEndpoint

    pub struct InteractionEndpoint {
    // private fields
    }

    A gateway-free interaction dispatcher backed by a caller-owned task group.

    InteractionEndpoint::handle

    async fn InteractionEndpoint::handle(self : InteractionEndpoint, body : Json, deadline_ms? : Int) -> Json?

    Decode and dispatch an interaction JSON body.

    Returns callback JSON only for Reply. Multipart files cannot be expressed by this thin JSON API; callers that accept uploads should use handle_interaction and inspect InteractionOutcome::Reply.files.

    InteractionEndpoint::handle_interaction

    async fn InteractionEndpoint::handle_interaction(self : InteractionEndpoint, interaction :
    Interaction
    , deadline_ms? : Int) -> InteractionOutcome

    Dispatch one interaction and wait only for its initial response deadline. deadline_ms is measured from this call: routing starts immediately, and time spent waiting for handler capacity counts against it. A timed-out handler remains attached to the app's task group and continues in the background.

    InteractionEndpoint::handle_signed_http

    Verify the raw HTTP body, decode one interaction, and dispatch it.

    The caller owns the task group supplied to App::serve. Keep that group alive after this method returns so deferred handlers can finish.

    InteractionHandler

    pub(all) enum InteractionHandler[A] {
    Immediate(async (ImmediateCtx, A) -> CommandReply)
    Deferred(ephemeral~ : Bool, async (DeferredCtx, A) -> Unit)
    Raw(async (
    CommandCtx
    ) -> Unit)
    }

    Execution strategy for a typed application command.

    InteractionHttpBody

    pub(all) enum InteractionHttpBody {
    Empty
    Bytes(Bytes)
    Chunks(Array[Bytes])
    }

    A callback body. Multipart chunks are written in order and retain file bytes without a second copy.

    InteractionHttpRequest

    pub(all) struct InteractionHttpRequest {
    http_method : String
    signature : String?
    timestamp : String?
    body : Bytes
    }

    The exact bytes received from an HTTP host. Never parse or reserialize body before verifying the Discord signature.

    InteractionHttpResponse

    pub(all) struct InteractionHttpResponse {
    status : Int
    content_type : String?
    body : InteractionHttpBody
    }

    A host-independent HTTP response for a Discord interaction.

    InteractionOutcome

    pub(all) enum InteractionOutcome {
    Reply(response~ :
    InteractionResponse
    , files~ : Array[
    FileUpload
    ]?)
    NoRoute
    NoResponse
    TimedOut
    } derive(
    Debug
    )

    The result of dispatching one HTTP-delivered interaction.

    InteractionTarget

    pub(all) enum InteractionTarget {
    Command(name~ : String)
    Component(custom_id~ : String)
    Modal(custom_id~ : String)
    } derive(
    Debug
    )

    The routed target of an interaction entering the middleware chain. Autocomplete, gateway events, and services never pass through it.

    MessageUpdate

    pub struct MessageUpdate {
    // private fields
    }

    An UPDATE_MESSAGE callback: edits the message that hosts the component (or, for a modal, the message whose component opened it). Omitted fields keep their current value.

    MessageUpdate::MessageUpdate

    Describe an update of the host message. clear_content removes the text (it cannot be combined with content). New files replace the existing attachments unless keep_attachments lists the ones to retain; an empty keep_attachments removes every existing attachment.
    pub struct Modal[S, A] {
    // private fields
    }

    A typed modal definition: a route id with typed state, and typed fields. The decoders are retained for the matching on_modal registration while show erases them into a ModalHandle.

    Modal::show

    fn[S, A] Modal::show(self : Modal[S, A], state : S, values? : Map[String, String]) -> ModalHandle raise ModalShowError

    Prepare a modal response carrying state, optionally overriding text-input defaults by custom_id for this response only. Raises ModalShowError::InvalidCustomId when the state does not encode or the complete id exceeds 100 characters, and the other variants when an override key is unknown, does not name a text input, or carries a value longer than the input's max_length (4000 when unset) in UTF-16 units.

    ModalDeferredCtx

    pub struct ModalDeferredCtx {
    // private fields
    }

    Modal-submit context available after a deferred response.

    ModalDeferredCtx::app

    The app-level services: the REST client and application id.

    ModalDeferredCtx::delete_followup

    Delete a followup message sent for this interaction.

    ModalDeferredCtx::delete_original

    async fn ModalDeferredCtx::delete_original(self : ModalDeferredCtx) -> Unit

    Delete the original response.

    ModalDeferredCtx::edit_followup

    Edit a followup message sent for this interaction. New files replace its attachments unless keep_attachments lists the ones to retain.

    ModalDeferredCtx::edit_original

    Edit the original response after deferring. New files replace the existing attachments unless keep_attachments lists the ones to retain; poll can be added only here, while editing the deferred response.

    ModalDeferredCtx::followup

    Send a followup message after the initial deferred response.

    ModalDeferredCtx::get_followup

    Fetch a followup message sent for this interaction.

    ModalDeferredCtx::guild_id

    The guild the interaction was invoked in, or None outside guilds. Use guild_scope() for flows that require a guild; this accessor is for maybe-guild flows where DMs are valid.

    ModalDeferredCtx::guild_scope

    The validated guild invocation. In a DM this raises HandlerError::GuildOnly, which the error policy renders normally.

    ModalDeferredCtx::interaction

    The full interaction payload.

    ModalDeferredCtx::origin

    What opened the modal: a component (with its host message) or a command.

    ModalDeferredCtx::original

    Fetch the original response.

    ModalDeferredCtx::raw

    Escape hatch for advanced inspection. Calling response methods on the raw value opts out of this wrapper's response discipline. Failure handling still follows the gate's real response state.

    ModalDeferredCtx::scope

    Guild or DM invocation scope, carrying the invoking member or user.

    ModalDeferredCtx::user

    The invoking user.

    ModalDeferredCtx::wait_for_component

    async fn[A] ModalDeferredCtx::wait_for_component(self : ModalDeferredCtx, route : ComponentRoute[A], state : A, from? : WaitFrom, timeout_ms? : Int) ->
    ComponentCtx
    ?

    Wait for the next click on the component route.custom_id(state) identifies — build the button with the same route and state. By default only the invoking user may satisfy the wait; pass from=Anyone for a public flow or from=User(id) for someone else. Returns None on timeout, or at once without a gateway connection. Raises CustomIdError when the state does not encode.

    The returned context has not been answered, and the waited interaction does not pass through admission or the automatic defer: respond to it (for example update_message or defer_update) within three seconds.

    ModalField

    pub struct ModalField[A] {
    // private fields
    }

    One typed input in a modal form.

    ModalField::map

    fn[A, B] ModalField::map(self : ModalField[A], f : (A) -> B) -> ModalField[B]

    Transform a decoded modal field without changing its emitted component.

    ModalField::optional

    fn[A] ModalField::optional(self : ModalField[A]) -> ModalField[A?]

    Make a modal field optional. Missing and empty submissions decode to None.

    ModalField::validate

    fn[A] ModalField::validate(self : ModalField[A], check : (A) -> String?) -> ModalField[A]

    Validate a decoded field. Returning a message rejects the submission.

    ModalField::with_default

    fn[A] ModalField::with_default(self : ModalField[A], value : A) -> ModalField[A]

    Use a default when a modal field is missing or empty.

    ModalFields

    pub struct ModalFields[A] {
    // private fields
    }

    A composable collection of typed modal inputs.

    ModalFields::map

    fn[A, B] ModalFields::map(self : ModalFields[A], f : (A) -> B) -> ModalFields[B]

    Transform the decoded value without changing the emitted components.

    ModalFields::map1

    fn[A, Z] ModalFields::map1(a : ModalField[A], f : (A) -> Z) -> ModalFields[Z]

    Build a modal's field set from one field, mapping its decoded value into the handler's argument type.

    ModalFields::map2

    fn[A, B, Z] ModalFields::map2(a : ModalField[A], b : ModalField[B], f : (A, B) -> Z) -> ModalFields[Z]

    Build a modal's field set from two fields, combining their decoded values with the final function argument.

    ModalFields::map3

    fn[A, B, C, Z] ModalFields::map3(a : ModalField[A], b : ModalField[B], c : ModalField[C], f : (A, B, C) -> Z) -> ModalFields[Z]

    Build a modal's field set from three fields, combining their decoded values with the final function argument.

    ModalFields::map4

    fn[A, B, C, D, Z] ModalFields::map4(a : ModalField[A], b : ModalField[B], c : ModalField[C], d : ModalField[D], f : (A, B, C, D) -> Z) -> ModalFields[Z]

    Build a modal's field set from four fields, combining their decoded values with the final function argument.

    ModalFields::map5

    fn[A, B, C, D, E, Z] ModalFields::map5(a : ModalField[A], b : ModalField[B], c : ModalField[C], d : ModalField[D], e : ModalField[E], f : (A, B, C, D, E) -> Z) -> ModalFields[Z]

    Build a modal's field set from five fields, combining their decoded values with the final function argument.

    ModalFields::of

    fn[A] ModalFields::of(field : ModalField[A]) -> ModalFields[A]

    Lift a single field into a ModalFields; combine further with zip and map, or use the mapN helpers directly.

    ModalFields::zip

    fn[A, B] ModalFields::zip(self : ModalFields[A], other : ModalFields[B]) -> ModalFields[(A, B)]

    Concatenate two field sets and pair their decoded values.

    ModalHandle

    pub struct ModalHandle {
    // private fields
    }

    A type-erased modal response ready to be returned by a command or component handler.

    ModalImmediateCtx

    pub struct ModalImmediateCtx {
    // private fields
    }

    Read-only modal-submit context for handlers returning an initial message.

    ModalImmediateCtx::app

    The app-level services: the REST client and application id.

    ModalImmediateCtx::guild_id

    The guild the interaction was invoked in, or None outside guilds. Use guild_scope() for flows that require a guild; this accessor is for maybe-guild flows where DMs are valid.

    ModalImmediateCtx::guild_scope

    The validated guild invocation. In a DM this raises HandlerError::GuildOnly, which the error policy renders normally.

    ModalImmediateCtx::interaction

    The full interaction payload.

    ModalImmediateCtx::origin

    What opened the modal: a component (with its host message) or a command.

    ModalImmediateCtx::remaining_ms

    fn ModalImmediateCtx::remaining_ms(self : ModalImmediateCtx) -> Int?

    Milliseconds left to return the initial response, or None when the executor set no deadline.

    ModalImmediateCtx::scope

    Guild or DM invocation scope, carrying the invoking member or user.

    ModalImmediateCtx::unchecked_raw

    Escape hatch to the underlying context. Its response methods bypass this handler's discipline of returning its initial response: a callback sent through it makes the returned reply fail with ResponseGateError::AlreadyResponded, which reaches the error policy. Prefer the read-only accessors on this context. Each use raises an alert_unchecked warning; once the bypass is deliberate, silence it with warnings = "-alert_unchecked" in that package's moon.pkg.

    ModalImmediateCtx::user

    The invoking user.

    ModalReply

    pub(all) enum ModalReply {
    UpdateMessage(MessageUpdate)
    Message(InitialResponse)
    }

    Initial response returned by an immediate modal handler.

    ModalReply::message

    Reply with a new message; set ephemeral=true to show it only to the submitting user.

    ModalReply::update_message

    Reply by editing the message whose component opened the modal. Omitted fields keep their current value (see MessageUpdate).

    ModalSubmitHandler

    pub(all) enum ModalSubmitHandler[S, A] {
    Immediate(async (ModalImmediateCtx, S, A) -> ModalReply)
    DeferredMessage(ephemeral~ : Bool, async (ModalDeferredCtx, S, A) -> Unit)
    DeferredUpdate(async (ModalDeferredCtx, S, A) -> Unit)
    Raw(async (
    ModalCtx
    ) -> Unit)
    }

    Execution strategy for a typed modal submission. Handlers receive the decoded state and form.

    RegisteredCommand

    pub struct RegisteredCommand {
    // private fields
    }

    A type-erased command consumed by an App executor.

    RegisteredCommand::name

    fn RegisteredCommand::name(self : RegisteredCommand) -> String

    The top-level command name used for routing.

    RestAccess

    pub(all) enum RestAccess {
    Token(String)
    Client(
    Client
    )
    }

    How an executor reaches Discord's REST API: with a bot token, from which it builds and owns a client, or through an existing client the caller owns.

    SlashChild

    pub struct SlashChild {
    // private fields
    }

    A type-erased slash subcommand or subcommand group.

    SyncReport

    Reports for each synchronized scope, in the requested order.

    WaitFrom

    Who may satisfy a component wait.

    component_route

    fn[A] component_route(id~ : String, state~ : CustomIdCodec[A]) -> ComponentRoute[A]

    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
    }

    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.

    guild_only

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

    Require a guild invocation.

    message_command

    Define a MESSAGE context-menu command.
    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)
    }

    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.

    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.

    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.

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

    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,
    })
    }