On This Page

Commands

67 symbols from @neontechspace/fluxerly/effect

Variables#

commands#

Define text commands, parse their arguments and attach them to an existing client

The simplest route is runBot's commands option. Creation, registration and help return their result directly and throw ConfigurationError for misuse, so a misconfigured router fails at startup. Commands listen through one bounded messageCreate subscription in the caller's Scope when attach executes. The router does not connect the client or create a separate SDK runtime. Guards, cooldown keys, unmatched feedback and handler Effects remain application-owned, with built-in guards for common checks. The router does not retry failures, and it replies on its own only when onReject: "reply" is selected. Groups optionally organize command paths and help pages. Flat commands remain independent, and each protected command still needs its own guard

const commands: Commands
TypeScript
import { commands, type Client } from "@neontechspace/fluxerly/effect"
export function commandGroupExample(client: Client) {
    return commands
        .create({ prefix: "!" })
        .registerGroup({ name: "tools", aliases: ["t"] })
        .register({ name: "ping", execute: ({ reply }) => reply("Pong") }, { group: ["tools"] })
        .attach(client)
}
TypeScript
import { Effect } from "effect"
import { commands, type Client } from "@neontechspace/fluxerly/effect"

export const installPing = (client: Client) =>
    commands
        .create({
            prefix: "!",
            parse: commands.parseQuoted,
            onUnmatched: ({ reply }, unmatched) => {
                if (unmatched._tag !== "CommandUnknownName") return Effect.void
                const hint = unmatched.suggestion === undefined ? "" : `. Closest known command: ${unmatched.suggestion}`
                return reply(`Unknown command: ${unmatched.name}${hint}`)
            },
        })
        .register({ name: "ping", execute: ({ reply }) => reply("Pong") })
        .attach(client)

Argument schemas are optional definitions of the values a command expects. Guards see raw input first. Conversion failures give rejection feedback without consuming a cooldown. The execute callback receives inferred frozen values alongside unchanged args

TypeScript
import { commands, type Client } from "@neontechspace/fluxerly/effect"

export function typedCommandExample(client: Client) {
    return commands
        .create({ prefix: "!", parse: commands.parseQuoted, onReject: "reply" })
        .register({
            name: "repeat",
            arguments: {
                mode: { type: "choice", choices: ["fast", "slow"] },
                count: { type: "integer", min: 1, max: 5, default: 1 },
                note: { type: "text", optional: true, rest: true },
            },
            cooldown: { durationMs: 10_000, per: "user" },
            execute: ({ reply, values }) => reply(`${values.count} / ${values.mode} / ${values.note ?? "No note"}`),
        })
        .attach(client)
}
TypeScript
import { Effect } from "effect"
import { type Client, type PrefixCommandRouter } from "@neontechspace/fluxerly/effect"

export function commandHelpExample(client: Client, router: PrefixCommandRouter, channelId: string) {
    const pages = router.help({ prefix: "!", maxLength: 1_000, include: (command) => command.name !== "admin" })
    // Visibility is not authorization. Each command still needs its own policy
    return Effect.forEach(pages, (content) => client.messages.send(channelId, { content, allowedMentions: {} }), {
        discard: true,
    })
    // Earlier pages remain sent if a later send fails or is interrupted. No retries occur
}

guards#

Built-in native command guards, namely guildOnly, dmOnly, ownerOnly and requirePermissions

const guards: Guards
TypeScript
import { guards, runBot } from "@neontechspace/fluxerly/effect"

export const moderationBot = (token: string | undefined) =>
    runBot({
        token,
        commands: {
            prefix: "!",
            commands: {
                purge: {
                    guard: [guards.guildOnly(), guards.requirePermissions(["ManageMessages"])],
                    execute: ({ reply }) => reply("Purging"),
                },
            },
        },
    })

Interfaces#

BotCommandsOptions#

Prefix commands for the native runBot: The router options of commands.create, the commands themselves, an optional failure hook and the delivery settings of the router's attach

Router-level callbacks, such as onUnmatched and middleware, run without extra services, while each command's services are added to the bot's requirements. The router is attached before the gateway starts and closed when the bot stops. The delivery settings concurrency, partition, partitionReady, overflow, maxPendingMessages and maxPendingBytes are passed to attach unchanged and checked before any client exists. Omitted settings keep the attach defaults: Eight commands at a time and overflow dropOldest, so a burst drops the oldest waiting message with a Warn record. With overflow "stop", a full queue fails the bot with CriticalWorkerStoppedError. Unlike commands.create, runBot replies to a rejected command by default, such as one with a missing argument, as onReject: "reply" does. Set onReject: "silent" to send no reply, or a function to give custom feedback. An unset ignoreBots follows the runBot ignoreBots setting

interface BotCommandsOptions<
    M extends MessageCore = Message,
    S extends Readonly<Record<string, unknown>>
        = Readonly<Record<string, unknown>>,
    C extends BotCommandEntries<S> = BotCommandEntries<S>,
    RouterServices = never,
    ReadyServices = never
>
    extends PrefixCommandsOptions<
        unknown, never, M
    >, HandlerOptions<M>interface BotCommandsOptions<
  M extends MessageCore = Message,
  S extends Readonly<
    Record<string, unknown>
  >
    = Readonly<
      Record<string, unknown>
    >,
  C extends BotCommandEntries<S>
    = BotCommandEntries<S>,
  RouterServices = never,
  ReadyServices = never
>
  extends PrefixCommandsOptions<
    unknown, never, M
  >, HandlerOptions<M>
commands#

Commands keyed by name, such as { ping: { execute: ({ reply }) => reply("Pong") } }, or a callback that receives the empty router and returns it with registrations, for groups or commands built elsewhere

The callback runs synchronously when the runBot Effect runs. Invalid registrations and a return value other than the registered router are misuse and die with ConfigurationError. Any other throw from the callback creates no client, and runBot fails with ApplicationError whose source is "runBot commands" and whose cause is the thrown value

| C & PrefixCommandBatch<M, S>
| ((router: PrefixCommandRouter<never, M>)
      => PrefixCommandRouter<RouterServices, M>)| C & PrefixCommandBatch<M, S>
| ((
    router:
      PrefixCommandRouter<never, M>
  ) => PrefixCommandRouter<
    RouterServices, M
  >)
onError optional#

Receive command failures, and the reports of the router's own subscription, instead of the client-level onError, as the router attach option does

Those are a failing partitionReady hook, a throwing partition function and an overflow that stops the router (overflow "stop"). With overflow dropOldest, a full queue only writes a Warn record and produces no report

(report: FailureReport) => Effect<unknown, unknown, never>(report: FailureReport)
  => Effect<unknown, unknown, never>
partitionReady optional#

Hold the commands of a partition key until its own setup is done, as the client.on option partitionReady does for event handlers

Valid only together with partition, otherwise runBot dies with ConfigurationError before any client exists. Before the first waiting message of a key starts, the SDK calls the hook with the key and runs its Effect with the services available when runBot runs. The key's messages stay in the queue without using one of the concurrent command slots until the Effect completes, while other keys keep running, and the held messages count toward maxPendingMessages and maxPendingBytes. A hook that fails or dies reports every waiting message of that key to the onError option of the commands, like a throwing partition function does, without a command name, and those messages run no command. The services the hook requires are added to the bot's requirements

(key: string) => Effect<unknown, unknown, ReadyServices>(key: string)
  => Effect<
    unknown, unknown, ReadyServices
  >

Inherited from PrefixCommandsOptions: caseSensitive, cooldowns, ignoreBots, mentionPrefix, onReject, onUnmatched, parse, prefix, use

Inherited from HandlerOptions: concurrency, maxPendingBytes, maxPendingMessages, overflow, partition

Commands#

Recognize message commands such as !repeat hello with commands from the package's /effect entry point

Creation, registration and help are local and return their result directly, throwing ConfigurationError for misuse. Attachment and memory-store operations return Effects that do nothing until run

interface Commands

create#

Validate options and create an empty local router, without connecting or subscribing a client

Defaults to full Message typing. Supply MessageCore or the client's SelectedMessage type as M for fewer selected fields. Prefix resolvers, parsers, command callbacks and the context client keep the same message type. The router records services R required by optional unmatched feedback, but does not require them until attachment. Invalid options throw ConfigurationError. A throwing getter throws SdkDefect for commands with code application.defect, while another unexpected fault uses sdk.defect. No prefix resolver, parser or feedback callback runs during creation

create<E = never, R = never, M extends MessageCore = Message>(
    options: PrefixCommandsOptions<E, R, M>
): PrefixCommandRouter<R, M>create<
  E = never,
  R = never,
  M extends MessageCore = Message
>(
  options:
    PrefixCommandsOptions<E, R, M>
): PrefixCommandRouter<R, M>

Parameters

options
PrefixCommandsOptions<E, R, M>
Returns
PrefixCommandRouter<R, M>

memoryCooldowns#

Create a fresh in-memory cooldown store

Defaults to 10,000 retained keys, or the supplied positive safe integer maxEntries. A full store makes room rather than refusing a new key, removing expired keys first and then the reservation that expires soonest. Invalid options throw ConfigurationError. A throwing getter throws SdkDefect for commands with code application.defect, while another unexpected fault uses sdk.defect. Keep the store for reuse in command cooldowns. It has no persistence, scope finalizer or background timer

memoryCooldowns(
    options?: MemoryCooldownOptions
): MemoryCooldownStorememoryCooldowns(
  options?: MemoryCooldownOptions
): MemoryCooldownStore

Parameters

options optional
MemoryCooldownOptions

parseArgument#

Convert one token with a built-in argument descriptor, such as commands.parseArgument("30m", { type: "duration" }), which returns 1_800_000

The grammar is the router's own, so a token converts here exactly when it converts in a command with that descriptor. Accepts every descriptor type except custom, whose parse function can be called directly. The result has the descriptor's value type, such as a string for id and a MemberReference for member. A rejected token returns undefined, as does a missing token unless the descriptor has a default, which is returned instead. A member descriptor needs options.guildId, the community the token belongs to, and rejects every token without it. Runs synchronously without a cache, client or network work. A bad descriptor, a custom descriptor, a token that is neither a string nor undefined and bad options throw ConfigurationError. A throwing getter or proxy trap in the descriptor or options throws SdkDefect for commands with code application.defect, while another unexpected fault uses sdk.defect

parseArgument<
    D extends
        | CommandArgumentText
        | CommandArgumentInteger
        | CommandArgumentNumber
        | CommandArgumentBoolean
        | CommandArgumentId
        | CommandArgumentChoice<readonly string[]>
        | CommandArgumentUserSelection<
              readonly CommandArgumentUser[]
          >
        | CommandArgumentChannelSelection<
              readonly CommandArgumentChannel[]
          >
        | CommandArgumentRoleSelection<
              readonly CommandArgumentRole[]
          >
        | CommandArgumentMember
        | CommandArgumentDuration
>(
    token: string | undefined,
    descriptor: D,
    options?: CommandArgumentParseOptions
): CommandArgumentValue<D> | undefinedparseArgument<
  D extends
    | CommandArgumentText
    | CommandArgumentInteger
    | CommandArgumentNumber
    | CommandArgumentBoolean
    | CommandArgumentId
    | CommandArgumentChoice<
        readonly string[]
      >
    | CommandArgumentUserSelection<
        readonly CommandArgumentUser[]
      >
    | CommandArgumentChannelSelection<
        readonly CommandArgumentChannel[]
      >
    | CommandArgumentRoleSelection<
        readonly CommandArgumentRole[]
      >
    | CommandArgumentMember
    | CommandArgumentDuration
>(
  token: string | undefined,
  descriptor: D,
  options?:
    CommandArgumentParseOptions
):
  | CommandArgumentValue<D>
  | undefined

Parameters

token
string | undefined
descriptor
D
Returns
CommandArgumentValue<D> | undefined

parseQuoted#

Synchronously split a suffix with single or double quotes and backslash escapes, preserving original argument text in rawArgs

Select with create({ prefix: "!", parse: commands.parseQuoted }), since the default splits only on whitespace. Returns undefined for empty input or invalid names, and a PrefixCommandParseRejection with a fixed reason and the command name for an unclosed quote or a trailing escape, which onUnmatched receives. For 'a'b, the single-quote reason adds that a quote followed directly by a letter or digit does not close the span. A double quote opens a span anywhere. A single quote opens one only at the start of a word and closes unless a letter or digit follows, so don't stays literal and 'Yes', 'No' is Yes, and No. Empty quotes produce an empty token

parseQuoted<M extends MessageCore = Message>(
    input: PrefixCommandParseInput<M>
): PrefixCommandParse | PrefixCommandParseRejection | undefinedparseQuoted<
  M extends MessageCore = Message
>(
  input: PrefixCommandParseInput<M>
):
  | PrefixCommandParse
  | PrefixCommandParseRejection
  | undefined

Parameters

suggest#

Name the candidate closest to a misspelled name, such as commands.suggest("pnig", ["ping", "help"]), which returns "ping"

This is the rule behind the suggestion of CommandUnknownName. It agrees with the router's suggestion for its default case-insensitive matching when the candidates are the registered names of one level. The router also considers aliases and reports the command's own name for a near alias, whereas this helper returns the candidate that matched. A router created with caseSensitive: true compares case. Case is ignored. A candidate counts when it is within one edit of a name of up to four characters, or within two edits of a longer name, where an edit inserts, deletes or replaces one character, or swaps two adjacent characters. The closest candidate wins, and the earliest of equally close ones. Returns the winning candidate as written, or undefined when none is close enough or the name is empty. The helper applies no hidden-command filter, so pass only names that may be revealed. Runs synchronously without client or network work. A name that is not a string, candidates that are not an iterable of strings and a lone string as candidates throw ConfigurationError. Candidates whose iterator lookup or iterator throws make it throw SdkDefect for commands with code application.defect, while another unexpected fault uses sdk.defect

suggest(
    name: string,
    candidates: Iterable<string>
): string | undefinedsuggest(
  name: string,
  candidates: Iterable<string>
): string | undefined

Parameters

name
string
candidates
Iterable<string>
Returns
string | undefined

CooldownStore#

Storage that checks and reserves a command cooldown in one atomic claim

The application owns persistence and coordination of competing claims, including across processes. A claim can be immediate or return an Effect requiring the handler's services

interface CooldownStore<E = never, R = never>interface CooldownStore<
  E = never, R = never
>

claim#

Reserve input.key for input.durationMs, returning a claim directly or through an Effect

The returned Effect runs within command dispatch, preserving the attachment's services and interruption. Only CooldownAcquired permits execution. Other claims call optional rejection feedback without waiting or retrying. Malformed claims fail with ConfigurationError. Store failures and defects use subscription error reporting

claim(
    input: CommandCooldownRequest
): CommandCooldownClaim | Effect<CommandCooldownClaim, E, R>claim(
  input: CommandCooldownRequest
):
  | CommandCooldownClaim
  | Effect<
      CommandCooldownClaim, E, R
    >

Parameters

Guards#

Built-in native command guards for the most common access rules

Pass one or a list as a command's guard. Each denial carries a short reason that onReject: "reply" sends to the user. Invalid guard settings throw ConfigurationError when the guard is created

interface Guards

dmOnly#

Allow the command only in a confirmed one-to-one or group conversation, and deny every other message

A message with a guildId is denied without a request. For a message without one, the guard checks the direct-message cache, then the channel cache, and otherwise reads the channel once with directMessages.fetch, which also fills an enabled direct-message cache. Enable cache.directMessages to avoid a read for each invocation in the same conversation. A private conversation allows the command. A cached community channel, or a read that Fluxer answers with anything other than a private conversation, denies it. Any other read failure, such as a network failure, timeout or rejection, leaves the channel unconfirmed and fails the guard's Effect with that UserOperationFailure, which is reported with the command name

dmOnly<
    M extends MessageCore = Message
>(): PrefixCommandGuard<UserOperationFailure, never, M>dmOnly<
  M extends MessageCore = Message
>(): PrefixCommandGuard<
  UserOperationFailure, never, M
>

guildOnly#

Allow the command only for a message that carries its community's guildId, and deny every other message without a request

Fluxer's gateway supplies the guildId with every community message, so a message without one is denied even when its channel belongs to a community

guildOnly<
    M extends MessageCore = Message
>(): PrefixCommandGuard<never, never, M>guildOnly<
  M extends MessageCore = Message
>(): PrefixCommandGuard<
  never, never, M
>
Returns
PrefixCommandGuard<never, never, M>

ownerOnly#

Allow the command only for the owner of the bot's application, as Fluxer reports it

The first invocation reads the owner's user ID with application.fetch, and the guard keeps it for the rest of the client's lifetime, so later invocations decide without a request and an ownership transfer applies to a new client. Invocations that start before the first read completes each read it. A failed read, such as a network failure, timeout or rejection, keeps nothing and fails the guard's Effect with that BotApplicationOperationFailure, which is reported with the command name, and the next invocation reads again

ownerOnly<
    M extends MessageCore = Message
>(): PrefixCommandGuard<BotApplicationOperationFailure, never, M>ownerOnly<
  M extends MessageCore = Message
>(): PrefixCommandGuard<
  BotApplicationOperationFailure,
  never,
  M
>

Allow the command only for the given decimal user IDs, such as the bot owner's

An undefined argument, such as an unset environment variable, throws ConfigurationError rather than falling back to the application owner

ownerOnly<M extends MessageCore = Message>(
    ownerIds: string | readonly string[]
): PrefixCommandGuard<never, never, M>ownerOnly<
  M extends MessageCore = Message
>(
  ownerIds:
    | string
    | readonly string[]
): PrefixCommandGuard<
  never, never, M
>

Parameters

ownerIds
string | readonly string[]
Returns
PrefixCommandGuard<never, never, M>

requirePermissions#

Allow the command only when the invoking member has every named permission in the message's channel, and deny it in direct messages

The guard decides with permissions.fetch and preferCache: It reads the community, member, roles and channel from enabled caches first and fetches only what is missing, at most once each per invocation, then applies permissions.calculate. The reads share one deadline, rest.defaultTimeoutMs, and only fetches count toward it. Cached observations reflect the last applied gateway events, so a permission change that Fluxer has made but the gateway has not yet delivered is not seen. Where a stale grant matters, check again with permissions.fetch before acting. A cached channel without its overwrite list is read again instead of being treated as having no overwrites. In a thread, the guard also reads the thread's parent channel the same way, cache first, and decides as Fluxer does: From the parent channel's permissions, with SendMessages granted exactly when SendMessagesInThreads is. It does not check whether the member can see a private thread or whether the thread is archived or locked. Fluxer sends a channel's permission overwrite list, empty when there are none. When Fluxer sent the channel without its permission overwrite list, its permissions cannot be confirmed, so the guard denies the command rather than guessing. Every other failed read fails the command, which is reported with the command name. The decision does not guarantee that Fluxer allows a later action. A timed-out member keeps its permission bits, but Fluxer blocks sending, editing and publishing own messages, reacting, uploading, typing, changing the own nickname, writing in threads and joining voice, so check members.timeoutState as well. The denial names each missing permission by Fluxer's label, such as Manage community

requirePermissions<M extends MessageCore = Message>(
    names: readonly (
        | "CreateInstantInvite" | "KickMembers" | "BanMembers"
        | "Administrator" | "ManageChannels" | "ManageGuild"
        | "AddReactions" | "ViewAuditLog" | "PrioritySpeaker"
        | "Stream" | "ViewChannel" | "SendMessages"
        | "SendTtsMessages" | "ManageMessages" | "EmbedLinks"
        | "AttachFiles" | "ReadMessageHistory" | "MentionEveryone"
        | "UseExternalEmojis" | "Connect" | "Speak" | "MuteMembers"
        | "DeafenMembers" | "MoveMembers" | "UseVad"
        | "ChangeNickname" | "ManageNicknames" | "ManageRoles"
        | "ManageWebhooks" | "ManageExpressions" | "ManageThreads"
        | "CreatePublicThreads" | "CreatePrivateThreads"
        | "UseExternalStickers" | "SendMessagesInThreads"
        | "ModerateMembers" | "CreateExpressions" | "PinMessages"
        | "BypassSlowmode" | "UpdateRtcRegion"
        | "ViewChannelMembers")[]
): PrefixCommandGuard<
    ClientClosedError | ChannelOperationError | GuildOperationError,
    never,
    M
>requirePermissions<
  M extends MessageCore = Message
>(
  names: readonly (
    | "CreateInstantInvite"
    | "KickMembers" | "BanMembers"
    | "Administrator"
    | "ManageChannels"
    | "ManageGuild"
    | "AddReactions"
    | "ViewAuditLog"
    | "PrioritySpeaker" | "Stream"
    | "ViewChannel"
    | "SendMessages"
    | "SendTtsMessages"
    | "ManageMessages"
    | "EmbedLinks" | "AttachFiles"
    | "ReadMessageHistory"
    | "MentionEveryone"
    | "UseExternalEmojis"
    | "Connect" | "Speak"
    | "MuteMembers"
    | "DeafenMembers"
    | "MoveMembers" | "UseVad"
    | "ChangeNickname"
    | "ManageNicknames"
    | "ManageRoles"
    | "ManageWebhooks"
    | "ManageExpressions"
    | "ManageThreads"
    | "CreatePublicThreads"
    | "CreatePrivateThreads"
    | "UseExternalStickers"
    | "SendMessagesInThreads"
    | "ModerateMembers"
    | "CreateExpressions"
    | "PinMessages"
    | "BypassSlowmode"
    | "UpdateRtcRegion"
    | "ViewChannelMembers")[]
): PrefixCommandGuard<
  | ClientClosedError
  | ChannelOperationError
  | GuildOperationError,
  never,
  M
>

Parameters

names
readonly (
    | "CreateInstantInvite" | "KickMembers" | "BanMembers"
    | "Administrator" | "ManageChannels" | "ManageGuild"
    | "AddReactions" | "ViewAuditLog" | "PrioritySpeaker" | "Stream"
    | "ViewChannel" | "SendMessages" | "SendTtsMessages"
    | "ManageMessages" | "EmbedLinks" | "AttachFiles"
    | "ReadMessageHistory" | "MentionEveryone" | "UseExternalEmojis"
    | "Connect" | "Speak" | "MuteMembers" | "DeafenMembers"
    | "MoveMembers" | "UseVad" | "ChangeNickname"
    | "ManageNicknames" | "ManageRoles" | "ManageWebhooks"
    | "ManageExpressions" | "ManageThreads" | "CreatePublicThreads"
    | "CreatePrivateThreads" | "UseExternalStickers"
    | "SendMessagesInThreads" | "ModerateMembers"
    | "CreateExpressions" | "PinMessages" | "BypassSlowmode"
    | "UpdateRtcRegion" | "ViewChannelMembers")[]readonly (
  | "CreateInstantInvite"
  | "KickMembers" | "BanMembers"
  | "Administrator" | "ManageChannels"
  | "ManageGuild" | "AddReactions"
  | "ViewAuditLog" | "PrioritySpeaker"
  | "Stream" | "ViewChannel"
  | "SendMessages" | "SendTtsMessages"
  | "ManageMessages" | "EmbedLinks"
  | "AttachFiles"
  | "ReadMessageHistory"
  | "MentionEveryone"
  | "UseExternalEmojis" | "Connect"
  | "Speak" | "MuteMembers"
  | "DeafenMembers" | "MoveMembers"
  | "UseVad" | "ChangeNickname"
  | "ManageNicknames" | "ManageRoles"
  | "ManageWebhooks"
  | "ManageExpressions"
  | "ManageThreads"
  | "CreatePublicThreads"
  | "CreatePrivateThreads"
  | "UseExternalStickers"
  | "SendMessagesInThreads"
  | "ModerateMembers"
  | "CreateExpressions"
  | "PinMessages" | "BypassSlowmode"
  | "UpdateRtcRegion"
  | "ViewChannelMembers")[]

MemoryCooldownStore#

Bounded in-memory cooldown reservations for one process, using the caller's Effect Clock wall time

Claim, sweep and clear return lazy Effects, so storage changes occur only when those Effects run. No background timer, persistence or cross-process coordination is provided

interface MemoryCooldownStore
maxEntries#

Fixed key limit selected when the store is created

number
size#

Immediate stored-key count, including expired keys until a full store needs room or a sweep removes them

number

claim#

When run, atomically reserve the key or report its active cooldown

Acquired expiry is the caller Clock's current wall time plus durationMs. A new key is never refused for lack of space: A full store removes expired keys first, then the reservation that expires soonest, whose key can then claim again early. Malformed keys or durations are misuse and die with ConfigurationError. Unexpected input getter defects remain in the Effect cause

claim(input: CommandCooldownRequest): Effect<CommandCooldownClaim>claim(
  input: CommandCooldownRequest
): Effect<CommandCooldownClaim>

Parameters

clear#

Return an Effect that forgets this store's reservations, allowing new claims

Does not cancel handlers or clear other stores

clear(): Effect<void>
Returns
Effect<void>

sweep#

Return an Effect that removes keys expired according to the caller's Effect Clock and produces the number removed, preserving active claims

sweep(): Effect<number>
Returns
Effect<number>

PrefixCommand#

A prefix command whose application callbacks return Effects rather than promises

Dispatch evaluates its guard, converts arguments, claims any cooldown and runs the execution Effect. Callbacks use the attachment's captured services and interruption, without a detached runtime. Registration copies metadata and arguments while retaining callbacks and the cooldown store. E describes callback failures and R describes required services. Callback failures are reported by the subscription, not by a completed attach

interface PrefixCommand<
    E = never,
    R = never,
    S extends CommandArgumentSchema = { … },
    M extends MessageCore = Message
>
    extends PrefixCommandDefinitioninterface PrefixCommand<
  E = never,
  R = never,
  S extends CommandArgumentSchema
    = { … },
  M extends MessageCore = Message
>
  extends PrefixCommandDefinition
arguments optional#

Named positional conversions in property order

Omitted leaves parsed args unrestricted and converted values empty

S
cooldown optional#

Optional cooldown claim required after the guard and arguments succeed

Denied claims skip execution

PrefixCommandCooldown<E, R, S, M>
execute#

Return the Effect that performs work for this allowed command with fully converted arguments

The subscription runs it in the attachment's context and discards its success value, rather than sending it as a reply. Failures and defects use safe attachment onError reporting, without retry or cooldown rollback. Non-Effect returns fail with ConfigurationError. Cooperative Effect work is interrupted when the attachment closes. Synchronous work and promises that ignore cancellation cannot be forcibly stopped

(context: PrefixCommandExecutionContext<S, M>)
    => Effect<unknown, E, R>(
  context: PrefixCommandExecutionContext<
    S, M
  >
) => Effect<unknown, E, R>
guard optional#

One guard or a list of guards, run in order before argument conversion

The first that denies stops the command. Each command owns its policy, with no inherited group guard or authorization from help visibility. Non-Effect returns and invalid verdicts fail with ConfigurationError. Failures and defects use subscription error reporting rather than rejection feedback

PrefixCommandGuard<E, R, M> | readonly PrefixCommandGuard<E, R, M>[]| PrefixCommandGuard<E, R, M>
| readonly PrefixCommandGuard<E, R, M>[]
onReject optional#

Feedback after a denied guard, rejected arguments or a denied cooldown, overriding the router's onReject

A function receives raw context and a safe rejection classification, never partially converted values. Its success value is discarded. Failures and defects use attachment error reporting, without retry. Non-Effect returns fail with ConfigurationError

PrefixCommandRejectionFeedback<E, R, M>

Inherited from PrefixCommandDefinition: aliases, description, hidden, name, usage

PrefixCommandContext#

Message and parsed arguments for a native command's guard and rejection callback

These values cannot be changed. Execution and cooldown-key callbacks receive the extended context with converted values. Native callbacks run in the attachment's Effect context, with interruption rather than the default API's AbortSignal field

interface PrefixCommandContext<M extends MessageCore = Message>interface PrefixCommandContext<
  M extends MessageCore = Message
>
args#

Parsed string tokens copied to a frozen array, preserved alongside any converted values

readonly string[]
client#

Client supplied to attach, available for explicit Effect operations

The application still owns its connection and shutdown

Client<M>
help#

Build help pages from the router that matched this command, for example ({ help, reply }) => reply(help()[0] ?? "No commands")

Takes the same settings as router.help, all optional: The prefix defaults to the one this message used and the page length to 2,000 UTF-16 code units. Nothing is sent. Invalid settings throw ConfigurationError as router.help does

(options?: CommandContextHelpOptions) => readonly string[](options?: CommandContextHelpOptions)
  => readonly string[]
message#

Frozen incoming message with the attached client's selected fields

The router does not fetch omitted fields

M
name#

Registered command name, regardless of the alias or letter case used in the message

string
paginate#

Show several pages in this message's channel that the command's author flips with ◀ and ▶ reactions, such as ({ paginate }) => paginate(["First", "Second"])

Delegates to client.messages.paginate with users defaulting to this message's author. Pass users to let other people flip, such as () => true for anyone. The other options, defaults, permissions, cleanup and failures are those of client.messages.paginate. The Effect completes when listening ends, so the handler stays active until then

(pages: readonly PageInput[], options?: PaginateOptions)
    => Effect<PaginateResult<M>, PaginateFailure>(
  pages: readonly PageInput[],
  options?: PaginateOptions
) => Effect<
  PaginateResult<M>, PaginateFailure
>
path optional#

Frozen registered names through this command, such as ["admin", "inspect"]

Absent at root and does not imply authorization

readonly string[]
prefix#

Exact prefix selected for this invocation, such as ! or !!

string
rawArgs#

Argument text retained by the parser

The default removes command-name separator whitespace but preserves the remainder

string
reply#

Reply to the incoming message through the attached client

The returned Effect follows handler interruption and can fail with SendError. A string is sent as the reply's content, such as reply("Pong"). Delegates to client.messages.reply, including its validation, deadline, nonce, retry, cache and defect behavior. Interruption or a lost response can leave the reply posted. Failure does not always mean nothing was sent, and uncertain sends are not replayed. The router replies on its own only when onReject: "reply" is selected

(input: string | ReplyInput, options?: RequestOptions)
    => Effect<M, SendError>(
  input: string | ReplyInput,
  options?: RequestOptions
) => Effect<M, SendError>
sendHelp#

Send this router's help in this message's channel, such as ({ sendHelp }) => sendHelp()

Takes the same optional settings as help. One page is sent as a plain message, and several pages use paginate with its defaults, so only the command's author can flip them. Invalid settings, and help with no visible entries, fail with ConfigurationError without sending anything

(options?: CommandContextHelpOptions)
    => Effect<PaginateResult<M>, PaginateFailure>(options?: CommandContextHelpOptions)
  => Effect<
    PaginateResult<M>, PaginateFailure
  >

PrefixCommandCooldown#

Limit how often a command executes, per user by default

Claimed after the guards allow execution and all arguments convert, before executing the handler. A claimed cooldown remains claimed if the handler fails or is interrupted

interface PrefixCommandCooldown<
    E = never,
    R = never,
    S extends CommandArgumentSchema = { … },
    M extends MessageCore = Message
>interface PrefixCommandCooldown<
  E = never,
  R = never,
  S extends CommandArgumentSchema
    = { … },
  M extends MessageCore = Message
>
durationMs#

Whole milliseconds per claim, from 1 through 2,147,483,647

number
key optional#

Synchronously return a nonempty suffix from the converted context, replacing the one selected by per

The router adds the canonical command name or full group path, so aliases share a claim but different parent groups do not. Routers sharing a store also share claims when their command identities and suffixes match

(context: PrefixCommandExecutionContext<S, M>) => string(
  context: PrefixCommandExecutionContext<
    S, M
  >
) => string
per optional#

Whose invocations share the cooldown: user (default), channel or guild

CommandCooldownPer
store optional#

Reservation store retained by reference

Omit it to use the router's own memory store in this process, sized by the router's cooldowns option, using the handler's Effect Clock and shared by every router derived from the same create

CooldownStore<E, R>

PrefixCommandExecutionContext#

Command context supplied to execution and cooldown-key callbacks only after the complete argument schema succeeds

interface PrefixCommandExecutionContext<
    S extends CommandArgumentSchema = { … },
    M extends MessageCore = Message
>
    extends PrefixCommandContext<M>interface PrefixCommandExecutionContext<
  S extends CommandArgumentSchema
    = { … },
  M extends MessageCore = Message
>
  extends PrefixCommandContext<M>
values#

Frozen values keyed by schema names, such as values.count, or an empty object when no schema is supplied

Not available to guards or rejection callbacks

CommandArgumentValues<S>

Inherited from PrefixCommandContext: args, client, help, message, name, paginate, path, prefix, rawArgs, reply, sendHelp

PrefixCommandRouter#

Immutable registered commands and groups for attachment to a native client

Registration returns a new router synchronously, leaving earlier routers and attachments unchanged, and throws ConfigurationError for misuse. R records services required by registered callbacks, which must be provided when attaching the router. Creating or registering a router does not run its callbacks

interface PrefixCommandRouter<
    R = never,
    M extends MessageCore = Message
>interface PrefixCommandRouter<
  R = never,
  M extends MessageCore = Message
>
commands#

Frozen executable-command information in registration order across groups, without callbacks or resource candidates

Grouped commands include canonical paths

readonly PrefixCommandMetadata[]
groups#

Frozen group information in registration order, including empty groups

Groups organize lookup and help, not authorization

readonly PrefixCommandGroupMetadata[]

attach#

Return an Effect that registers this router as one bounded messageCreate subscription in the caller's scope

Provide registered callback services R and any error-callback or partitionReady services R2 when running this Effect

attach<E = never, R2 = never>(
    client: Client<M>,
    options?: EventHandlerOptions<E, R2, unknown>
): Effect<Subscription, never, Scope | R | R2>attach<E = never, R2 = never>(
  client: Client<M>,
  options?: EventHandlerOptions<
    E, R2, unknown
  >
): Effect<
  Subscription, never,
  Scope | R | R2
>

Produces a Subscription after registration, not after connecting the client or completing future command work. Callback failures and defects use subscription onError reporting with the command name and full Cause, rather than failing an already completed attachment Effect. A failed onUnmatched callback or custom parser is reported the same way without a command name. The router owns one bounded queue for its onError hook, which receives reports one at a time in order, as for client.on. Omitted or undefined settings default to eight concurrent commands and overflow dropOldest, so a burst never stops the router. With partition, the option partitionReady holds a key's messages until the Effect it returns completes, as for client.on. The held messages wait in the queue without using a concurrent command slot, and a hook that fails or dies reports each waiting message to the router's onError, without a command name, and runs no command for it. A throwing partition function and an overflow that stops the router are reported to onError the same way, in the order the reports arrived. Without an onError they go to the client-level onError or the log. The option without partition is misuse and dies with ConfigurationError. Rejected and unmatched commands are counted and logged at Debug in the commands category. The observe option receives one handler observation per router invocation, including event and command middleware. A matched command supplies its canonical command name, even when a guard denies it or middleware stops it. Reported failures produce outcome failure, interruption produces cancelled, and other completions produce success. A message with no command match still produces an observation without a command name. Only messages a person typed can run commands. Messages from bots are skipped unless ignoreBots is false, and so is every message whose type is neither Default nor Reply, such as the notice Fluxer posts with a new thread's name as its content. A message without a type, which Fluxer never sends, counts as typed. Each attachment dispatches independently, so duplicate attachments can execute a command twice

Run subscription.close() or close the registration scope to detach and interrupt handlers, without shutting down the client. The returned subscription.waitForClose() completes after handler finalizers. Work that disables interruption can delay closure. Synchronous work and caller-owned promises that ignore interruption cannot be forcibly stopped

The client must use message type M. A full-message router cannot attach to a client with omitted fields. Invalid options are misuse and die with ConfigurationError, and a throwing onError getter dies with its thrown value. On a closing or closed client, attach succeeds with an already-closed Subscription whose waitForClose succeeds, and writes a Warn log record with code events.registeredAfterShutdown

Parameters

client
Client<M>
options optional
EventHandlerOptions<E, R2, unknown>
Returns
Effect<Subscription, never, Scope | R | R2>Effect<
  Subscription, never, Scope | R | R2
>

help#

Build frozen help pages locally and synchronously, without sending messages or executing commands

It needs no scope or registered callback services

help(options: CommandHelpOptions): readonly string[]help(
  options: CommandHelpOptions
): readonly string[]

Root help lists immediate commands and groups in sibling registration order. A canonical group selection shows the group and its immediate children, including empty groups, unless include rejects an ancestor. Entries show full canonical paths, aliases and descriptions, with (Group) marking groups. Commands and groups registered with hidden: true never appear, nor does anything inside a hidden group, and selecting a hidden group throws ConfigurationError as for a missing one

Schemas generate <required>, [optional] and <rest...> or [rest...] syntax unless explicit usage overrides it. An empty usage suppresses inferred syntax, and an absent schema adds no argument syntax. A selection that is empty, or whose group or ancestor include rejects, produces []. No prefix resolver, guard, cooldown or handler is evaluated

The explicit UTF-16 page limit is respected and pages end between visible characters, so surrogate pairs, flags and base characters with combining marks are not split, but a page can be shorter than the limit and Markdown may still be split. Only a single character longer than the limit is cut, between code points. Page-edge whitespace is trimmed and empty pages are removed, so joining pages does not reconstruct the exact original text. The application chooses which pages to send and how to handle mentions

Malformed options, ill-formed text, too-small limits and invalid visibility callbacks throw ConfigurationError. A throwing option getter throws SdkDefect for commands with code application.defect and the thrown value as its cause, while another unexpected fault uses sdk.defect

Parameters

Returns
readonly string[]

register#

Validate and add a command to a new router, at root or an existing canonical options.group path

Copies metadata and arguments, retaining callbacks and the cooldown store without invoking them. The resulting router requires this command's services R2 in addition to existing R when attached. Names and aliases must not collide with sibling commands or groups under the router's case policy. Invalid definitions, collisions and missing or alias-only parent paths throw ConfigurationError. A throwing getter throws SdkDefect for commands with code application.defect, while another unexpected fault uses sdk.defect. Earlier routers and active attachments remain unchanged

register<
    E,
    R2,
    S extends Readonly<Record<string, CommandArgumentDescriptor>>
        = { … }
>(
    command: PrefixCommand<E, R2, S, M>,
    options?: PrefixCommandRegistrationOptions
): PrefixCommandRouter<R | R2, M>register<
  E,
  R2,
  S extends Readonly<
    Record<
      string,
      CommandArgumentDescriptor
    >
  >
    = { … }
>(
  command:
    PrefixCommand<E, R2, S, M>,
  options?: PrefixCommandRegistrationOptions
): PrefixCommandRouter<R | R2, M>

Parameters

command
PrefixCommand<E, R2, S, M>
Returns
PrefixCommandRouter<R | R2, M>

registerGroup#

Add a group to a new router, at root or beneath an existing canonical options.group path

Register parent groups before children. Groups accept identity and description only, without callbacks or argument schemas. Group names and aliases use fixed whitespace separators at dispatch, before the command parser runs. Invalid metadata, missing parents and sibling name collisions throw ConfigurationError. A throwing getter throws SdkDefect for commands with code application.defect, while another unexpected fault uses sdk.defect. Existing routers keep their data, and required services R do not change

registerGroup(
    group: PrefixCommandGroupDefinition,
    options?: PrefixCommandRegistrationOptions
): PrefixCommandRouter<R, M>registerGroup(
  group:
    PrefixCommandGroupDefinition,
  options?: PrefixCommandRegistrationOptions
): PrefixCommandRouter<R, M>

Parameters

Returns
PrefixCommandRouter<R, M>

registerMany#

Validate and add a nonempty keyed command object to one new router, in JavaScript own enumerable string-key order and under one optional parent

Each object key supplies its command name. Set arguments: {} to reject positional arguments, or arguments: undefined to leave raw args unrestricted. Every definition is snapshotted before registration. Inherited batch keys are ignored. Recognized fields inside each definition are read once, including inherited and non-enumerable fields. If any definition is invalid or any name collides, the call throws ConfigurationError and produces no partially registered router. Earlier routers and attachments remain unchanged. The optional parent is validated and snapshotted once. A throwing getter throws SdkDefect for commands with code application.defect, while another unexpected fault uses sdk.defect. Each entry retains its inferred argument values, and the returned router records every callback service requirement

registerMany<
    S extends Readonly<Record<string, unknown>>,
    C extends Readonly<
        Record<
            keyof S,
            {
                readonly arguments?: Readonly<
                    Record<string, CommandArgumentDescriptor>
                >
                readonly execute: unknown
            }
        >
    >
>(
    commands: C & PrefixCommandBatch<M, S>,
    options?: PrefixCommandRegistrationOptions
): PrefixCommandRouter<R | BatchRequirements<C>, M>registerMany<
  S extends Readonly<
    Record<string, unknown>
  >,
  C extends Readonly<
    Record<
      keyof S,
      {
        readonly arguments?: Readonly<
          Record<
            string,
            CommandArgumentDescriptor
          >
        >
        readonly execute: unknown
      }
    >
  >
>(
  commands:
    & C
    & PrefixCommandBatch<M, S>,
  options?: PrefixCommandRegistrationOptions
): PrefixCommandRouter<
  R | BatchRequirements<C>,
  M
>

Parameters

commands
C & PrefixCommandBatch<M, S>

PrefixCommandUnmatchedContext#

Frozen message and prefix for native onUnmatched feedback, without an assumed command or converted arguments

interface PrefixCommandUnmatchedContext<
    M extends MessageCore = Message
>interface PrefixCommandUnmatchedContext<
  M extends MessageCore = Message
>
client#

Attached client for explicit feedback operations, without transferring its lifetime to the router

Client<M>
message#

Incoming frozen message with the client's selected fields only

M
prefix#

Exact prefix selected before parsing declined, name lookup missed or a group needed a subcommand

string
reply#

Reply to the unmatched incoming message through the attached client, inheriting this callback's interruption

A string is sent as the reply's content. Delegates to client.messages.reply, including its validation, deadline, nonce, retry, cache and defect behavior. Interruption or a lost response can leave the reply posted. Failure does not always mean nothing was sent, and uncertain sends are not replayed

(input: string | ReplyInput, options?: RequestOptions)
    => Effect<M, SendError>(
  input: string | ReplyInput,
  options?: RequestOptions
) => Effect<M, SendError>

PrefixCommandsOptions#

Prefix and parser configuration with optional Effect feedback when no executable command is selected

interface PrefixCommandsOptions<
    E = never,
    R = never,
    M extends MessageCore = Message
>
    extends PrefixCommandParsing<M>interface PrefixCommandsOptions<
  E = never,
  R = never,
  M extends MessageCore = Message
>
  extends PrefixCommandParsing<M>
onReject optional#

Rejection feedback for commands that do not set their own onReject

Omit it to give no feedback, except in runBot, which replies by default. Guards returning false never send an automatic reply

PrefixCommandRejectionFeedback<E, R, M>
onUnmatched optional#

Return an Effect that handles a parser decline, unknown name or group without a subcommand

An unknown name carries a suggestion when a registered name is close, and a parser decline carries a reason and the command name when the parser explained a syntax mistake, such as an unclosed quote from commands.parseQuoted. The Effect runs for every such message and anyone can cause that, so a reply sent from it needs the application's own limit, and in a channel shared with other bots the prefix may belong to a command of another bot. Runs in the attachment's captured Effect context and follows subscription interruption, with its success value discarded. Failures and defects use attachment onError reporting, without retry or automatic replies. Not called for ignored bot messages or messages without a matching prefix. Returning anything other than an Effect fails dispatch with ConfigurationError

(
    context: PrefixCommandUnmatchedContext<M>,
    unmatched: PrefixCommandUnmatched
) => Effect<unknown, E, R>(
  context:
    PrefixCommandUnmatchedContext<M>,
  unmatched: PrefixCommandUnmatched
) => Effect<unknown, E, R>
prefix#

Prefix text, accepted-prefix list or per-message resolver, which may return an Effect

If several prefixes match, the longest wins. Return undefined to ignore the message. A resolver failure is reported without a command name

| string
| readonly string[]
| ((message: M)
      =>
          | PrefixCommandPrefixValue
          | Effect<PrefixCommandPrefixValue, unknown, never>)| string
| readonly string[]
| ((message: M)
    =>
      | PrefixCommandPrefixValue
      | Effect<
          PrefixCommandPrefixValue,
          unknown,
          never
        >)
use optional#

Middleware run around every matched command, in order

See PrefixCommandMiddleware

readonly PrefixCommandMiddleware<E, R, M>[]readonly PrefixCommandMiddleware<
  E, R, M
>[]

Inherited from PrefixCommandParsing: caseSensitive, cooldowns, ignoreBots, mentionPrefix, parse

Type Aliases#

BatchRequirements#

The services required by every command in a registerMany batch, recorded on the returned router

type BatchRequirements<C> =
    C extends Readonly<Record<string, unknown>>
        ? CommandRequirements<C[keyof C]>
        : nevertype BatchRequirements<C> =
    C extends Readonly<
      Record<string, unknown>
    >
      ? CommandRequirements<C[keyof C]>
      : never

BotCommandEntries#

The keyed-command constraint the native runBot infers command services from: Each entry's optional argument schema and callback

type BotCommandEntries<S> =
    Readonly<
        Record<
            keyof S,
            {
                readonly arguments?: CommandArgumentSchema
                readonly execute: unknown
            }
        >
    >type BotCommandEntries<S> =
    Readonly<
      Record<
        keyof S,
        {
          readonly arguments?:
            CommandArgumentSchema
          readonly execute: unknown
        }
      >
    >

CommandRequirements#

The services required by one command's execute, guard, onReject and cooldown claim callbacks

type CommandRequirements<C> =
    | (C extends { readonly execute: infer F }
          ? EffectRequirements<F>
          : never)
    | (C extends { readonly guard: infer F }
          ? F extends readonly infer G[]
                ? EffectRequirements<G>
                : EffectRequirements<F>
          : never)
    | (C extends { readonly onReject: infer F }
          ? EffectRequirements<F>
          : never)
    | (C extends {
          readonly cooldown: {
              readonly store: { readonly claim: infer F }
          }
      }
          ? F extends (...arguments_: any[]) => infer A
                ? A extends Effect.Effect<unknown, unknown, infer R>
                      ? R
                      : never
                : never
          : never)type CommandRequirements<C> =
    | (C extends {
        readonly execute: infer F
      }
        ? EffectRequirements<F>
        : never)
    | (C extends {
        readonly guard: infer F
      }
        ? F extends readonly infer G[]
            ? EffectRequirements<G>
            : EffectRequirements<F>
        : never)
    | (C extends {
        readonly onReject: infer F
      }
        ? EffectRequirements<F>
        : never)
    | (C extends {
        readonly cooldown: {
          readonly store: {
            readonly claim: infer F
          }
        }
      }
        ? F extends (
            ...arguments_: any[]
          ) => infer A
            ? A extends Effect.Effect<
                unknown, unknown,
                infer R
              >
                ? R
                : never
            : never
        : never)

EffectRequirements#

The services required by the Effect that callback F returns, or never when F returns no Effect

type EffectRequirements<F> =
    F extends (...arguments_: any[])
        => Effect.Effect<unknown, unknown, infer R>
        ? R
        : nevertype EffectRequirements<F> =
    F extends (
      ...arguments_: any[]
    ) => Effect.Effect<
      unknown, unknown, infer R
    >
      ? R
      : never

PrefixCommandBatch#

Commands keyed by name, as registerMany and the runBot commands option accept them

Each value defines one command without its name. Omit arguments to leave raw args unrestricted, or set arguments: {} to reject positional arguments

type PrefixCommandBatch<
    M extends MessageCore,
    S extends Readonly<Record<string, unknown>>
> = {
    readonly [K in keyof S]:
        & Omit<
              PrefixCommand<
                  unknown,
                  any,
                  S[K] extends CommandArgumentSchema ? S[K] : { … },
                  M
              >,
              "name" | "arguments"
          >
        & {
              readonly arguments?: {
                  readonly [A in keyof S[K]]:
                      S[K][A] extends CommandArgumentDescriptor
                          ? S[K][A]
                          : CommandArgumentDescriptor
              }
          }
}type PrefixCommandBatch<
  M extends MessageCore,
  S extends Readonly<
    Record<string, unknown>
  >
> = {
  readonly [K in keyof S]:
    & Omit<
        PrefixCommand<
          unknown,
          any,
          S[
            K
          ] extends CommandArgumentSchema
            ? S[K]
            : { … },
          M
        >,
        "name" | "arguments"
      >
    & {
        readonly arguments?: {
          readonly [A in keyof S[K]]:
            S[K][
              A
            ] extends CommandArgumentDescriptor
              ? S[K][A]
              : CommandArgumentDescriptor
        }
      }
}

PrefixCommandGuard#

Decide whether a matched command may run, from raw context before argument conversion

The Effect produces true to allow it, false to deny it silently, or { deny: "reason" } to deny it with text that onReject: "reply" sends. The built-in guards cover common checks. A false verdict is still counted, logged at Debug and passed to a custom onReject callback, but never sends an automatic reply. Failures, defects and other values use subscription error reporting

type PrefixCommandGuard<
    E = never,
    R = never,
    M extends MessageCore = Message
> = (context: PrefixCommandContext<M>)
    => Effect.Effect<PrefixCommandGuardResult, E, R>type PrefixCommandGuard<
  E = never,
  R = never,
  M extends MessageCore = Message
> = (
  context: PrefixCommandContext<M>
) => Effect.Effect<
  PrefixCommandGuardResult,
  E,
  R
>

PrefixCommandMiddleware#

Run around every matched command of a router, in registration order, before its guards

Include next in the returned Effect to continue. Leaving it out stops the command, which is logged at Debug. The rest of the chain runs at most once: Evaluating next again, even concurrently as in Effect.all([next, next], { concurrency: 2 }), waits for the first run and repeats its outcome. When the command fails, next fails with the command's own failure, the same error or defect its execute Effect produced

type PrefixCommandMiddleware<
    E = never,
    R = never,
    M extends MessageCore = Message
> = (
    context: PrefixCommandContext<M>,
    next: Effect.Effect<void, unknown>
) => Effect.Effect<unknown, E, R>type PrefixCommandMiddleware<
  E = never,
  R = never,
  M extends MessageCore = Message
> = (
  context: PrefixCommandContext<M>,
  next:
    Effect.Effect<void, unknown>
) => Effect.Effect<unknown, E, R>

A failure of the command is reported once with its command name, whatever the middleware does with it. Failing again with that same error adds no second report. A different failure or defect of the middleware is reported as a separate failure after the command's, also with the command name. Without a command failure, a middleware failure is reported like a failed command and stops the command if next has not run yet. The command's failure is still reported when the middleware is interrupted, for example because the subscription closes

PrefixCommandRejectionFeedback#

Rejection feedback for a command: "reply" sends a short explanation, such as the missing argument and the command's usage, the guard's deny reason or the cooldown's remaining time

"silent" sends nothing, and the rejection is still logged at Debug. A guard returning false never sends an automatic reply, even with "reply" selected. A function returns an Effect that gives feedback instead, including for false guard denials

type PrefixCommandRejectionFeedback<
    E = never,
    R = never,
    M extends MessageCore = Message
> =
    | "reply"
    | "silent"
    | ((
          context: PrefixCommandContext<M>,
          rejection: PrefixCommandRejection
      ) => Effect.Effect<unknown, E, R>)type PrefixCommandRejectionFeedback<
  E = never,
  R = never,
  M extends MessageCore = Message
> =
  | "reply"
  | "silent"
  | ((
      context:
        PrefixCommandContext<M>,
      rejection:
        PrefixCommandRejection
    ) => Effect.Effect<unknown, E, R>)

So that repeated attempts do not make the bot repeat itself, "reply" answers an active cooldown key once until its retry time, and a guard denial once per user and command every 5 seconds, measured with the handler's Effect Clock. Argument rejections are always answered. A skipped reply is counted with the rejection and logged at Debug in the commands category. A function is called for every rejection, so it can count attempts and apply its own limit, for example with retryAtMs

Re-Exports#

Declared in JavaScript & TypeScript and documented there. Import them from @neontechspace/fluxerly/effect as well

CommandArgumentBoolean#
Convert only the exact lowercase strings true and false to booleans, rejecting other spellings and numeric substitutes
CommandArgumentChannel#
Channel data that a channelChoice argument can match
CommandArgumentChannelSelection#
Select a channel from a fixed list of candidates by decimal ID, complete channel mention or exact name
CommandArgumentChoice#
Match one of the configured nonempty strings exactly, returning that string rather than its list position
CommandArgumentCustom#
Convert one token, or the remaining tokens with rest, through an application parse function
CommandArgumentDuration#
Convert a duration such as 90s, 5m, 1h30m or 2d to whole milliseconds
CommandArgumentId#
Keep a decimal resource ID as a string without losing digits through JavaScript number conversion
CommandArgumentInteger#
Convert a decimal token such as 3 or -2 to a JavaScript safe integer
CommandArgumentMember#
Select a member of the message's community by complete user mention or decimal user ID, without a roster lookup
CommandArgumentMetadata#
Frozen description of an argument for help generation, without resource candidate data
CommandArgumentNumber#
Convert decimal text such as 1.25, -2 or 3e2 to a finite JavaScript number
CommandArgumentParseOptions#
Settings for commands.parseArgument that a message would otherwise supply
CommandArgumentRole#
Role data that a roleChoice argument can match
CommandArgumentRoleSelection#
Select a role from a fixed list of candidates by decimal ID, complete role mention or exact name
CommandArgumentText#
Keep one nonempty parsed token as a string, or join the remaining tokens when rest is true
CommandArgumentUser#
User data that a userChoice argument can match
CommandArgumentUserSelection#
Select a user from a fixed list of candidates by decimal ID, complete user mention or exact username
CommandContextHelpOptions#
Choose help pages for the help function of a command context
CommandCooldownRequest#
Request to reserve a cooldown after successful guard and argument conversion, before command execution
CommandHelpOptions#
Choose the text prefix, page length and visible entries for router.help
MemoryCooldownOptions#
Limit the cooldown keys held by one memory store in one process
PrefixCommandDefinition#
Names, help text and positional arguments for a command such as !repeat hello 3
PrefixCommandGroupDefinition#
A named parent for commands, such as admin in !admin inspect
PrefixCommandGroupMetadata#
Frozen group information copied at registration, including its registered-name path for adding children or selecting help
PrefixCommandMetadata#
Frozen command information for inspection and help generation
PrefixCommandParse#
Parser result copied and frozen by the router before command callbacks run
PrefixCommandParseInput#
Frozen input given to a custom parser after prefix matching and any group lookup
PrefixCommandParseRejection#
A parser result that reports a syntax mistake in text that was meant as a command, such as a quote that never closes
PrefixCommandParsing#
Parsing and cooldown settings shared by both API styles, apart from the prefix, which each API resolves in its own way
PrefixCommandRegistrationOptions#
Choose the existing parent group for a new command or child group
CommandArgumentDescriptor#
One argument's conversion settings, selected by its type field and validated when the command is registered
CommandArgumentMention#
Additional complete-token mention form accepted by an id descriptor
CommandArgumentOptionalValue#
The converted value type V, with undefined added when descriptor D sets optional: true
CommandArgumentRejectionReason#
Argument rejection classification without the rejected token. Missing means a required token was absent, Invalid means conversion failed and Ambiguous means multiple candidates matched. Unexpected means positional tokens remained after the schema was consumed
CommandArgumentSchema#
Describe positional arguments by name, such as { text: { type: "text" }, count: { type: "integer" } }
CommandArgumentType#
Supported positional conversions, performed locally after the guard allows the command
CommandArgumentValue#
TypeScript result type for one descriptor, including undefined when optional: true is selected without a default
CommandArgumentValues#
Frozen converted values keyed by the command's argument names, such as values.count
CommandCooldownClaim#
Outcome of reserving a cooldown, selected by _tag
CommandCooldownPer#
Whose invocations share one cooldown: The invoking user, the channel, or the guild
PrefixCommandGuardResult#
A guard's decision: true allows the command, false denies it without an automatic reply, and { deny } denies it with a reason that onReject: "reply" sends to the user
PrefixCommandPrefix#
Text that starts a command invocation, such as !, or a list of accepted prefixes
PrefixCommandPrefixValue#
Prefix text selected for one message: One prefix, a list of accepted prefixes, or undefined to ignore the message
PrefixCommandRejection#
Why a known command did not execute, passed as a frozen value to its optional onReject callback
PrefixCommandUnmatched#
A prefix matched but no executable command was selected