63 symbols from @neontechspace/fluxerly
Variables#
commands#
Route prefixed messages such as !ping to registered handlers
The simplest route is run's commands option. For an existing client, create a router, register commands and attach it
const commands: CommandsNothing listens until attach, which uses one bounded message subscription and does not connect the client.
Command handlers send replies and decide access, with optional guards, cooldowns and middleware.
The router does not retry failed handlers, and a handler that returns an Err result is reported like a throw
Optional groups organize names and help text, but a protected command still needs its own guard.
A guard decides whether a matched command may run, and the built-in guards cover common checks
Optional argument schemas convert raw args into frozen typed values after the guards. Conversion rejection gives rejection feedback without consuming a cooldown. The original args remain available
Help pages are strings the application can send. Hiding a command in help is not an access check
Creation, registration and help throw Configuration for misuse, so a misconfigured router fails at startup
import { commands } from "@neontechspace/fluxerly"
export function commandGroupExample(client) {
return commands
.create({ prefix: "!" })
.registerGroup({ name: "tools", aliases: ["t"] })
.register({ name: "ping", execute: ({ reply }) => reply("Pong") }, { group: ["tools"] })
.attach(client)
}
import { commands, type Client } from "@neontechspace/fluxerly"
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)
}
import { commands } from "@neontechspace/fluxerly"
export function installPing(client) {
return commands
.create({
prefix: "!",
parse: commands.parseQuoted,
onUnmatched: ({ reply }, unmatched) => {
if (unmatched._tag !== "CommandUnknownName")
return undefined
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)
}
import { commands, type Client } from "@neontechspace/fluxerly"
export function installPing(client: Client) {
return commands
.create({
prefix: "!",
parse: commands.parseQuoted,
onUnmatched: ({ reply }, unmatched) => {
if (unmatched._tag !== "CommandUnknownName") return undefined
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)
}
import { commands } from "@neontechspace/fluxerly"
export function typedCommandExample(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)
}
import { commands, type Client } from "@neontechspace/fluxerly"
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)
}
import { orThrow } from "@neontechspace/fluxerly"
export async function commandHelpExample(client, router, channelId) {
const pages = router.help({ prefix: "!", maxLength: 1_000, include: (command) => command.name !== "admin" })
// Visibility is not authorization. Each command still needs its own policy
for (const content of pages)
orThrow(await client.messages.send(channelId, { content, allowedMentions: {} }))
// Earlier pages remain sent if a later send fails. This example never retries
}
import { orThrow, type Client, type PrefixCommandRouter } from "@neontechspace/fluxerly"
export async 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
for (const content of pages) orThrow(await client.messages.send(channelId, { content, allowedMentions: {} }))
// Earlier pages remain sent if a later send fails. This example never retries
}
guards#
Built-in command guards, namely guild, dm, owner and require
const guards: GuardsInterfaces#
BotCommandsOptions #
Prefix commands for run: The router options of commands, the commands themselves, an optional failure hook and the delivery settings of the router's attach
The router is attached before the gateway starts and closed when the bot stops. The delivery settings concurrency, partition, partition, overflow, max and max are passed to attach unchanged and checked before any client exists. Omitted settings keep the attach defaults: Eight commands at a time and overflow drop, so a burst drops the oldest waiting message. With a partition, it follows the rule of the overflow option, with a Warn record. With overflow "stop", a full queue stops the bot with Critical. Unlike commands, run replies to a rejected command by default, such as one with a missing argument, as on does. Set on to send no reply, or a function to give custom feedback. An unset ignore follows the run ignore setting
interface BotCommandsOptions<
M extends MessageCore = Message,
S extends Readonly<Record<string, unknown>>
= Readonly<
Record<string, CommandArgumentSchema | undefined>
>
>
extends PrefixCommandsOptions<M>, HandlerOptions<M>interface BotCommandsOptions<
M extends MessageCore = Message,
S extends Readonly<
Record<string, unknown>
>
= Readonly<
Record<
string,
| CommandArgumentSchema
| undefined
>
>
>
extends PrefixCommandsOptions<
M
>, HandlerOptions<M>commands#-
Commands keyed by name, such as
{ ping: { execute: (, or a callback that receives the empty router and returns it with registrations, for groups or commands built elsewhere{ reply }) => reply( "Pong") } } The callback runs synchronously inside
run. Invalid registrations and a return value other than the registered router throwBot Configuration. Any other throw from the callback creates no client, andError runreturns an ErrBot Applicationwhose source is "Error runcommands" and whose cause is the thrown valueBot | PrefixCommandBatch<M, S> | ((router: PrefixCommandRouter<M>) => PrefixCommandRouter<M>)| PrefixCommandBatch<M, S> | ((router: PrefixCommandRouter<M>) => PrefixCommandRouter<M>) onoptional#Error -
Receive command failures, and the reports of the router's own subscription, instead of the client-level
on, as the router attach option doesError Those are a failing
partitionhook, a throwing partition function and an overflow that stops the router (overflow "stop"). With overflowReady drop, a full queue only writes a Warn record and produces no reportOldest (report: FailureReport) => unknown partitionoptional#Ready -
Hold the commands of a partition key until its own setup is done, as the
clientoption.on partitiondoes for event handlersReady Valid only together with partition, otherwise
runthrowsBot Configurationbefore any client exists. Before the first waiting message of a key starts, the SDK calls the hook with the key and a signal that aborts when the router's subscription stops. The key's messages stay in the queue without using one of the concurrent command slots until the returned promise settles, while other keys keep running, and the held messages count towardError maxandPending Messages max. A hook that throws, rejects or returns an Err result reports every waiting message of that key to thePending Bytes onoption of the commands, like a throwing partition function does, without a command name, and those messages run no commandError (key: string, signal: AbortSignal) => unknown(key: string, signal: AbortSignal) => unknown
Inherited from Prefix: case, cooldowns, ignore, mention, on, on, parse, prefix, use
Inherited from Handler: concurrency, max, max, overflow, partition
CommandArgumentBoolean #
Convert only the exact lowercase strings true and false to booleans, rejecting other spellings and numeric substitutes
interface CommandArgumentBooleanCommandArgumentChannel #
Channel data that a channel argument can match
Registration copies only id and name, and conversion returns that frozen copy. The router does not fetch channels or observe later changes to the supplied data
interface CommandArgumentChannelCommandArgumentChannelSelection #
Select a channel from a fixed list of candidates by decimal ID, complete channel mention or exact name
For any channel, use { type: "id", mention: "channel" } instead. Decimal text falls back to a name only when no ID matches, while a complete mention selects IDs only. Returns the one matching frozen id and name copy, rejecting absent or ambiguous matches. Channel selection does not fetch current state or check permissions
interface CommandArgumentChannelSelection<
C extends readonly CommandArgumentChannel[]
= readonly CommandArgumentChannel[]
>interface CommandArgumentChannelSelection<
C extends readonly CommandArgumentChannel[]
= readonly CommandArgumentChannel[]
>CommandArgumentChoice #
Match one of the configured nonempty strings exactly, returning that string rather than its list position
interface CommandArgumentChoice<
C extends readonly string[] = readonly string[]
>interface CommandArgumentChoice<
C extends readonly string[]
= readonly string[]
>choices#-
One to 100 unique nonempty strings, copied at registration
A literal tuple preserves the allowed-string union in
valuesC optionaloptional#-
Set to true to allow omission and produce undefined
An unlisted supplied token is invalid
true type#-
Compare the token to the configured strings without case folding
"choice"
CommandArgumentCustom #
Convert one token, or the remaining tokens with rest, through an application parse function
Return the converted value, or undefined to reject the token as Invalid. A parse function that throws or returns an Err result fails the command without running it, and that error is reported with the command name like a failed handler. Parsing is synchronous in both APIs. Returning a Promise or Effect is misuse: The command does not run, and a Configuration with field command is reported the same way. The outcome of a returned Promise is not awaited or reported
interface CommandArgumentCustom<T = unknown>interface CommandArgumentCustom<
T = unknown
>expectedoptional#-
Short description of the expected value, such as
a hex color, used in rejection repliesstring optionaloptional#-
Set to true to produce undefined when omitted
true parse#-
Convert the token synchronously, returning undefined for input that should be rejected
Throw or return an Err to fail the command
(token: string) => T | undefined restoptional#-
Set to true to pass the remaining tokens joined with single spaces
This descriptor must be last
true type#-
Convert with the supplied parse function
"custom"
CommandArgumentDuration #
Convert a duration such as 90s, 5m, 1h30m or 2d to whole milliseconds
Units are ms, s, m, h, d and w, each after a whole number, combined from largest to smallest without spaces. A bare number is not accepted, so 10 is rejected rather than guessed as seconds or minutes
interface CommandArgumentDurationdefaultoptional#-
Milliseconds used when the argument is omitted, which makes it optional without adding undefined to its value
number maxoptional#-
Longest accepted duration in milliseconds, inclusive
number minoptional#-
Shortest accepted duration in milliseconds, inclusive
number optionaloptional#-
Set to true to produce undefined when omitted
true type#-
Produce whole milliseconds, a safe integer, from unit-suffixed duration text
"duration" wholeoptional#Seconds -
Set to true to reject a duration with a millisecond part, such as
1m500ms, for a destination that takes whole seconds, such as a ban'sdurationMs true
CommandArgumentId #
Keep a decimal resource ID as a string without losing digits through JavaScript number conversion
Accepts digits beginning with a nonzero digit, with no sign or leading zeroes, up to 9223372036854775807, the largest 64-bit ID. Rejects 0 and larger values as Invalid. Optionally accepts one selected complete mention form and returns only its ID. Does not fetch a resource, check whether it exists or authorize an action against it
interface CommandArgumentIdmentionoptional#-
Also accept this mention kind as a complete token
Omitted means accept decimal ID strings only
CommandArgumentMention
CommandArgumentInteger #
Convert a decimal token such as 3 or -2 to a JavaScript safe integer
Accepts an optional minus sign and digits without leading zeroes, except 0 itself. Rejects fractions, exponent notation, a leading plus sign and numbers outside the safe-integer range
interface CommandArgumentIntegerdefaultoptional#-
Safe integer used when the argument is omitted, which makes it optional without adding undefined to its value
number maxoptional#-
Largest accepted value, inclusive
A larger value is rejected as Invalid
number minoptional#-
Smallest accepted value, inclusive
A smaller value is rejected as Invalid
number optionaloptional#-
Set to true to return undefined when omitted
Otherwise this argument is required and must precede optional arguments
true type#-
Require decimal integer syntax and a safe integer result
"integer"
CommandArgumentMember #
Select a member of the message's community by complete user mention or decimal user ID, without a roster lookup
The ID must be nonzero, without leading zeroes and no larger than 9223372036854775807, the largest 64-bit ID. Other tokens are rejected as Invalid. The value is a frozen { guild reference that member operations such as members accept directly. Whether that user is still a member is not checked. A message outside a community rejects this argument as Invalid
interface CommandArgumentMemberoptionaloptional#-
Set to true to produce undefined when omitted
true
CommandArgumentMetadata #
Frozen description of an argument for help generation, without resource candidate data
interface CommandArgumentMetadatachoicesoptional#-
Frozen accepted strings for a
choicedescriptorAbsent for other types
readonly string[] defaultoptional#-
Value used when the argument is omitted, for descriptors that declare one
number expectedoptional#-
Short description of the expected value for a
customdescriptor, used in rejection repliesAbsent for other types
string maxoptional#-
Inclusive upper bound of an
integer,numberordurationdescriptor, in milliseconds for a durationAbsent without a bound
number mentionoptional#-
Additional mention kind accepted by an
iddescriptor, if configuredGenerated usage still shows the argument name
CommandArgumentMention minoptional#-
Inclusive lower bound of an
integer,numberordurationdescriptor, in milliseconds for a durationAbsent without a bound
number name#-
Schema property name, which also names its converted value
string optional#-
True when the argument can be omitted, producing undefined or its default instead of an error
boolean rest#-
True when this final text or custom argument takes all remaining tokens, joined with spaces
boolean type#-
Conversion applied to this positional token, such as
integerormemberCommandArgumentType wholeoptional#Seconds -
True for a
durationdescriptor that rejects a millisecond partAbsent otherwise
true
CommandArgumentNumber #
Convert decimal text such as 1.25, -2 or 3e2 to a finite JavaScript number
Requires an integer part without leading zeroes, with optional minus sign, fraction and exponent. Rejects .5, 1., a leading plus sign, Infinity, NaN and values that overflow to infinity. Normal JavaScript number rounding still applies, unlike the safe-integer check for integer
interface CommandArgumentNumberdefaultoptional#-
Finite number used when the argument is omitted, which makes it optional without adding undefined to its value
number maxoptional#-
Largest accepted value, inclusive
A larger value is rejected as Invalid
number minoptional#-
Smallest accepted value, inclusive
A smaller value is rejected as Invalid
number optionaloptional#-
Set to true to produce undefined when omitted
Leaving this unset requires a token
true type#-
Accept decimal and exponent syntax when conversion produces a finite number
"number"
CommandArgumentParseOptions #
Settings for commands that a message would otherwise supply
interface CommandArgumentParseOptionsCommandArgumentRole #
Role data that a role argument can match
Registration copies only id and name, and conversion returns that frozen copy. Selection does not fetch roles or verify the invoking user's permissions or role hierarchy
interface CommandArgumentRoleCommandArgumentRoleSelection #
Select a role from a fixed list of candidates by decimal ID, complete role mention or exact name
For any role, use { type: "id", mention: "role" } instead. Decimal text tries IDs first and falls back to names only when no ID matches. Complete role mentions select IDs only, with no name fallback. Returns the one matching frozen id and name copy, rejecting absent or ambiguous matches
interface CommandArgumentRoleSelection<
C extends readonly CommandArgumentRole[]
= readonly CommandArgumentRole[]
>interface CommandArgumentRoleSelection<
C extends readonly CommandArgumentRole[]
= readonly CommandArgumentRole[]
>CommandArgumentText #
Keep one nonempty parsed token as a string, or join the remaining tokens when rest is true
For example, a final rest argument converts ["hello", "world"] to "hello world". This uses parsed tokens, not raw, so original quotes and separator whitespace are not retained. A supplied empty token is invalid even when the argument is optional
interface CommandArgumentTextoptionaloptional#-
Set to true to produce undefined when no token remains
Omitted means required, and false is not a supported option
true restoptional#-
Set to true to join remaining tokens with single spaces
This descriptor must be last. Omitted means consume one token
true type#-
Keep parsed text without numeric or resource conversion
"text"
CommandArgumentUser #
User data that a user argument can match
Registration copies only id and username, and conversion returns that frozen copy. No cache or network search occurs, and later changes to the original candidate do not affect selection
interface CommandArgumentUserCommandArgumentUserSelection #
Select a user from a fixed list of candidates by decimal ID, complete user mention or exact username
For any user, use { type: "id", mention: "user" } or { type: "member" } instead. Decimal text tries IDs before usernames, while a complete mention never falls back to a username. Exactly one match returns a frozen id and username copy. No match is invalid, and multiple matches are ambiguous, even when candidate IDs are duplicated
interface CommandArgumentUserSelection<
C extends readonly CommandArgumentUser[]
= readonly CommandArgumentUser[]
>interface CommandArgumentUserSelection<
C extends readonly CommandArgumentUser[]
= readonly CommandArgumentUser[]
>CommandContextHelpOptions #
Choose help pages for the help function of a command context
Every setting is optional: The prefix defaults to the one that matched the invoking message and the page length defaults to 2,000 UTF-16 code units, which fits one message
interface CommandContextHelpOptionsgroupoptional#-
Registered names of the group to describe, such as
["admin"]Omit for root entries, as in
router.help readonly string[] includeoptional#-
Return false to leave an entry out, as in
router.help Hidden entries never appear
(command: PrefixCommandMetadata) => boolean(command: PrefixCommandMetadata) => boolean maxoptional#Length -
Maximum page length in UTF-16 code units, a positive safe integer, default 2,000
number prefixoptional#-
Prefix to display, default the prefix that matched the invoking message, such as
!string
CommandCooldownRequest #
Request to reserve a cooldown after successful guard and argument conversion, before command execution
interface CommandCooldownRequestCommandHelpOptions #
Choose the text prefix, page length and visible entries for router
Help reads registered metadata only, without connecting a client, executing commands or sending messages. The caller decides which returned pages to send and how to handle mentions
interface CommandHelpOptionsgroupoptional#-
Choose a group by its registered names, not aliases, such as
["admin"]Omit or use
[]for root entries. A selected group shows itself and its immediate children, not deeper descendants. Missing groups fail withConfiguration, and so do hidden groups and groups inside them, so help does not reveal themError readonly string[] includeoptional#-
Return true to show an entry or false to exclude it, for example
(entry) => entry.name !== "internal" Omitted means show each considered entry. Receives frozen metadata, with
kind: "group"identifying groups. Entries registered withhidden: true, and everything inside a hidden group, are left out before this callback runs(command: PrefixCommandMetadata) => boolean(command: PrefixCommandMetadata) => booleanRoot help checks immediate entries once each in sibling registration order. A group selection checks ancestors first, then the selected group and its immediate children, at most once per entry. An ancestor or selected group that include rejects returns
[]without inspecting later entries. An included empty group remains visible, even if none of its children appearExcluding an entry from help does not prevent the command from running. Help does not run guards or check cooldowns
A throw fails with
Configurationwhose cause is the thrown value. A non-boolean or asynchronous return fails withError Configuration. Promises are not supported or awaited. Keep this synchronous callback short because it cannot be interrupted while runningError max#Length -
Required maximum page length, measured in UTF-16 code units
Use a positive safe integer. There is no default or provider-limit lookup
number prefix#-
Nonempty well-formed prefix to display, such as
!This can differ from dispatch prefixes and does not call the prefix resolver
string
Commands#
Recognize message commands such as !repeat hello with the default API's optional commands tools
Create a router, keep the new router returned by each registration and attach the final router to a client, or pass the commands to run, which does all of that. Construction is local. No connection or subscription starts until attachment
interface Commandscreate#
Create an empty router, for example commands
Defaults to full Message typing. Supply the client's Message or Selected type as M when it selects fewer fields. Prefix resolvers, parsers, command callbacks and their context client keep that same message type. Invalid options throw Configuration, without including rejected prefix or parser values. A throwing getter throws Sdk for commands with code application, while another unexpected fault uses sdk. No client connects and no feedback callback runs during creation
create<
M extends MessageCore = Message
>(options: PrefixCommandsOptions<M>): PrefixCommandRouter<M>create<
M extends MessageCore = Message
>(
options: PrefixCommandsOptions<M>
): PrefixCommandRouter<M>Parameters
options-
PrefixCommandsOptions<M>
- Returns
PrefixCommandRouter<M>
memoryCooldowns #
Create an empty in-memory cooldown store with a default limit of 10,000 keys, to share one store between routers
An optional positive safe integer max changes that limit. 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 Configuration. A throwing getter throws Sdk for commands with code application, while another unexpected fault uses sdk. The store has no timer or persistence. Reuse it in command cooldown definitions and clear it explicitly to forget claims
memoryCooldowns(
options?: MemoryCooldownOptions
): MemoryCooldownStorememoryCooldowns(
options?: MemoryCooldownOptions
): MemoryCooldownStoreParameters
optionsoptional-
MemoryCooldownOptions
- Returns
MemoryCooldownStore
parseArgument #
Convert one token with a built-in argument descriptor, such as commands, 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 Member 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, 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 Configuration. A throwing getter or proxy trap in the descriptor or options throws Sdk for commands with code application, while another unexpected fault uses sdk
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>
| undefinedParameters
token-
string | undefined descriptor-
D optionsoptional-
CommandArgumentParseOptions
- Returns
CommandArgumentValue<D> | undefined
parseQuoted #
Split a suffix using single or double quotes and backslash escapes, retaining original argument text in raw
Use as create( to opt in instead of the whitespace-only default. Returns undefined for empty input or invalid names, and a Prefix with a fixed reason and the command name for an unclosed quote or a trailing escape, which on 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. Runs synchronously without client or network work
parseQuoted<M extends MessageCore = Message>(
input: PrefixCommandParseInput<M>
): PrefixCommandParse | PrefixCommandParseRejection | undefinedparseQuoted<
M extends MessageCore = Message
>(
input: PrefixCommandParseInput<M>
):
| PrefixCommandParse
| PrefixCommandParseRejection
| undefinedParameters
input-
PrefixCommandParseInput<M>
- Returns
PrefixCommandParse | PrefixCommandParseRejection | undefined| PrefixCommandParse | PrefixCommandParseRejection | undefined
suggest#
Name the candidate closest to a misspelled name, such as commands, which returns "ping"
This is the rule behind the suggestion of Command. 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 case 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 Configuration. Candidates whose iterator lookup or iterator throws make it throw Sdk for commands with code application, while another unexpected fault uses sdk
suggest(
name: string,
candidates: Iterable<string>
): string | undefinedsuggest(
name: string,
candidates: Iterable<string>
): string | undefinedParameters
name-
string candidates-
Iterable<string>
- Returns
string | undefined
CooldownStore #
Storage that reserves a command cooldown and reports whether this invocation may execute
A custom store must handle saving claims and sharing them across processes if needed. Checking and reserving a full key must be atomic, so two concurrent calls cannot both claim the same key
interface CooldownStoreclaim#
Reserve input for input and return a claim immediately or through a promise
Only Cooldown permits the handler to run. Other claims give optional rejection feedback, without waiting or retrying. Malformed claims, throws, rejected promises and returned Err results use subscription error reporting
claim(
input: CommandCooldownRequest
): CommandCooldownClaim | PromiseLike<CommandCooldownClaim>claim(
input: CommandCooldownRequest
):
| CommandCooldownClaim
| PromiseLike<
CommandCooldownClaim
>Parameters
input-
CommandCooldownRequest
- Returns
CommandCooldownClaim | PromiseLike<CommandCooldownClaim>| CommandCooldownClaim | PromiseLike<CommandCooldownClaim>
Guards#
Built-in 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 on sends to the user. Invalid guard settings throw Configuration when the guard is created
interface Guardsimport { guards, runBot } from "@neontechspace/fluxerly"
export function moderationBot(token) {
return runBot({
token,
commands: {
prefix: "!",
commands: {
purge: {
guard: [guards.guildOnly(), guards.requirePermissions(["ManageMessages"])],
execute: ({ reply }) => reply("Purging"),
},
},
},
})
}
import { guards, runBot } from "@neontechspace/fluxerly"
export function moderationBot(token: string | undefined) {
return runBot({
token,
commands: {
prefix: "!",
commands: {
purge: {
guard: [guards.guildOnly(), guards.requirePermissions(["ManageMessages"])],
execute: ({ reply }) => reply("Purging"),
},
},
},
})
}
dmOnly #
Allow the command only in a confirmed one-to-one or group conversation, and deny every other message
A message with a guild 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 direct, which also fills an enabled direct-message cache. Enable cache 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 command, which is reported with the command name
dmOnly<M extends MessageCore = Message>(): PrefixCommandGuard<M>dmOnly<
M extends MessageCore = Message
>(): PrefixCommandGuard<M>- Returns
PrefixCommandGuard<M>
guildOnly #
Allow the command only for a message that carries its community's guild, and deny every other message without a request
Fluxer's gateway supplies the guild 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<M>guildOnly<
M extends MessageCore = Message
>(): PrefixCommandGuard<M>- Returns
PrefixCommandGuard<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, 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 command, which is reported with the command name, and the next invocation reads again
ownerOnly<M extends MessageCore = Message>(): PrefixCommandGuard<M>ownerOnly<
M extends MessageCore = Message
>(): PrefixCommandGuard<M>- Returns
PrefixCommandGuard<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 Configuration rather than falling back to the application owner
ownerOnly<
M extends MessageCore = Message
>(ownerIds: string | readonly string[]): PrefixCommandGuard<M>ownerOnly<
M extends MessageCore = Message
>(
ownerIds:
| string
| readonly string[]
): PrefixCommandGuard<M>Parameters
ownerIds -
string | readonly string[]
- Returns
PrefixCommandGuard<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 and prefer: 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. The reads share one deadline, rest, 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 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 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<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<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")[]
- Returns
PrefixCommandGuard<M>
MemoryCooldownOptions #
Limit the cooldown keys held by one memory store in one process
Keys are not saved or shared with other processes
interface MemoryCooldownOptionsmaxoptional#Entries -
Positive safe integer key limit, defaulting to 10,000
A claim for a new key never fails for lack of space: A full store removes expired keys first, then the reservation that expires soonest, whose key can then claim again early
number
MemoryCooldownStore #
Cooldown reservations kept in one memory store, up to that store's configured key limit
Share this instance across commands that should use the same store. Uses Date to set expiry. It does not save claims, run a background timer or share claims with other processes
interface MemoryCooldownStoremax#Entries -
Maximum keys this store retains, fixed at creation
number size#-
Current stored-key count
May include expired keys until a full store needs room or an explicit sweep removes them
number
claim#
Atomically reserve the requested key, or report its active cooldown
Acquired expiry is Date. 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 throw Configuration. A throwing claim getter throws Sdk for commands with code application and the thrown value as its cause
claim(input: CommandCooldownRequest): CommandCooldownClaimclaim(
input: CommandCooldownRequest
): CommandCooldownClaimParameters
input-
CommandCooldownRequest
- Returns
CommandCooldownClaim
clear#
Forget this store's reservations immediately, allowing new claims
Does not cancel handlers or clear other store instances
clear(): voidsweep#
Remove keys expired according to Date and return the number removed, without changing active reservations
sweep(): number- Returns
number
PrefixCommand #
A command name and the application callback that handles it
Dispatch runs router middleware and the command's guards, converts arguments, claims any cooldown and then calls execute. Registration copies metadata and arguments but retains callback and cooldown-store references. No automatic message or retry is provided, and successful return values are ignored
interface PrefixCommand<
S extends CommandArgumentSchema = { … },
M extends MessageCore = Message
>
extends PrefixCommandDefinitioninterface PrefixCommand<
S extends CommandArgumentSchema
= { … },
M extends MessageCore = Message
>
extends PrefixCommandDefinitionargumentsoptional#-
Named positional conversions, read in property order
Omitted leaves
argsunrestricted andvaluesemptyS cooldownoptional#-
Optional reservation required after the guards and arguments succeed
A denied claim skips execution
PrefixCommandCooldown<S, M> execute#-
Perform application work after the command is allowed and all arguments convert
Return normally or a promise completing that work. A returned or resolved Err result is reported like a throw, so
({ reply }) => reply(reports a failed reply. Other successful values are ignored. Throws, rejected promises and Err results use attachment"Pong") onreporting, with no automatic retry or cooldown rollback. PassError contextto cancellable operations when application work should stop with the subscription.signal (context: PrefixCommandExecutionContext<S, M>) => unknown( context: PrefixCommandExecutionContext< S, M > ) => unknown guardoptional#-
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 permission from help visibility
PrefixCommandGuard<M> | readonly PrefixCommandGuard<M>[]| PrefixCommandGuard<M> | readonly PrefixCommandGuard<M>[] onoptional#Reject -
Feedback after a denied guard, rejected arguments or a denied cooldown claim, overriding the router's
onReject A function receives raw context and a safe rejection classification, not partially converted values. Throws, rejected promises and returned Err results use attachment error reporting, without retry
PrefixCommandRejectionFeedback<M>
Inherited from Prefix: aliases, description, , name, usage
PrefixCommandContext #
Frozen command information copied for a matched message and passed to its guards, middleware and rejection callback
Includes the incoming message and parsed strings, but not converted argument values. Execution and cooldown-key callbacks receive the extended execution context
interface PrefixCommandContext<M extends MessageCore = Message>interface PrefixCommandContext<
M extends MessageCore = Message
>args#-
Parsed string tokens copied to a frozen array, unchanged by subsequent argument conversion
readonly string[] client#-
Client supplied to
attach, available for explicit operationsThe application remains responsible for connecting and shutting it down
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, 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.help ConfigurationasError routerdoes.help (options?: CommandContextHelpOptions) => readonly string[](options?: CommandContextHelpOptions) => readonly string[] message#-
Incoming frozen message with this client's selected fields
The router does not fetch excluded fields
M name#-
Registered command name, even when the message used an alias or different letter case
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
clientwith users defaulting to this message's author and this handler's cancellation signal applied, combined with any supplied signal. Pass users to let other people flip, such as.messages .paginate () => truefor anyone. The other options, defaults, permissions, cleanup and failures are those ofclient. The result settles when listening ends, so the handler stays active until then. Returning it from a command callback reports an Err like a thrown error.messages .paginate (pages: readonly PageInput[], options?: PaginateOptions) => ResultAsync< PaginateResult<M>, CancelledError | PaginateFailure >( pages: readonly PageInput[], options?: PaginateOptions ) => ResultAsync< PaginateResult<M>, CancelledError | PaginateFailure > pathoptional#-
Frozen registered names through this command, such as
["admin", "inspect"]Absent for root commands and does not imply permission
readonly string[] prefix#-
Prefix that matched this message, such as
!,!!or the bot mention whenmentionis enabledPrefix string raw#Args -
Argument text from the parser
The default retains everything after command-name separator whitespace, including trailing whitespace
string reply#-
Reply to the incoming message through the attached client, with this handler's cancellation signal already applied
A string is sent as the reply's content, such as
reply(. Delegates to"Pong") client, including its validation, deadline, nonce, retry, cache and defect behavior. Cancellation or a lost response can leave the reply posted. An Err does not always mean nothing was sent, and uncertain sends are not replayed. Returning the result from a command callback reports an Err like a thrown error, so.messages .reply ({ reply }) => reply(needs no further checks"Pong") ( input: string | ReplyInput, options?: Omit<RequestOptions, "signal"> ) => ResultAsync<M, ConfigurationError | SendError | CancelledError>( input: string | ReplyInput, options?: Omit<RequestOptions, "signal"> ) => ResultAsync< M, | ConfigurationError | SendError | CancelledError > send#Help -
Send this router's help in this message's channel, such as
({ sendHelp }) => send Help() Takes the same optional settings as
help. One page is sent as a plain message, and several pages usepaginatewith its defaults, so only the command's author can flip them. Invalid settings, and help with no visible entries, returnConfigurationwithout sending anythingError (options?: CommandContextHelpOptions) => ResultAsync< PaginateResult<M>, CancelledError | PaginateFailure >(options?: CommandContextHelpOptions) => ResultAsync< PaginateResult<M>, CancelledError | PaginateFailure > signal#-
Subscription cancellation signal for operations that support it
Cancellation prevents later router callbacks but cannot forcibly stop pending application promises
AbortSignal
PrefixCommandCooldown #
Limit how often a known command executes, per user by default
Claimed after its guards and argument conversion succeed, immediately before execution. The router does not release an acquired claim after handler failure or cancellation
interface PrefixCommandCooldown<
S extends CommandArgumentSchema = { … },
M extends MessageCore = Message
>interface PrefixCommandCooldown<
S extends CommandArgumentSchema
= { … },
M extends MessageCore = Message
>duration#Ms -
Whole milliseconds per reservation, from 1 through 2,147,483,647
number keyoptional#-
Return a nonempty key suffix from the converted execution context, replacing the one selected by
perThe router adds the command's canonical name or full group path, so aliases share a reservation and different groups remain separate
(context: PrefixCommandExecutionContext<S, M>) => string( context: PrefixCommandExecutionContext< S, M > ) => string peroptional#-
Whose invocations share the cooldown:
user(default),channelorguildCommandCooldownPer storeoptional#-
Store whose
claimperforms the reservationOmit it to use the router's own memory store in this process, sized by the router's
cooldownsoption and shared by every router derived from the same createCooldownStore
PrefixCommandDefinition #
Names, help text and positional arguments for a command such as !repeat hello 3
Each API's command type adds the callback that runs when this name or an alias matches. Registration copies this data, so later edits do not change an existing router. Individual and batch command registration read recognized fields once, including inherited and non-enumerable fields
interface PrefixCommandDefinitionaliasesoptional#-
Other names that run the same command, such as
rNames and aliases must not collide with a command or group in the same parent group
readonly string[] argumentsoptional#-
Named argument descriptors read in property order after the guard allows execution
Omit this to leave
argsunconverted and expose emptyvaluesReadonly<Record<string, CommandArgumentDescriptor>>Readonly< Record< string, CommandArgumentDescriptor > > descriptionoptional#-
Text placed below the invocation in generated help
The router neither escapes markup nor sends the text
string name#-
Name without the prefix, such as
repeat, beginning with an ASCII letter or digit and using only ASCII letters, digits,_or-string usageoptional#-
Help text after the command name, such as
<text> <count>Overrides argument-derived syntax, including when set to an empty string
string
PrefixCommandExecutionContext #
Matched-command context plus fully converted argument values, supplied to execution and cooldown-key callbacks
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 Empty when no schema is supplied. Guards and rejection callbacks never receive partial values
CommandArgumentValues<S>
Inherited from Prefix: args, client, help, message, name, paginate, path, prefix, raw, reply, send, signal
PrefixCommandGroupDefinition #
A named parent for commands, such as admin in !admin inspect
Register the group before adding children to its canonical path. Groups organize lookup and help only, with no handler, argument conversion, guard or cooldown. Each protected command needs its own guard
interface PrefixCommandGroupDefinitionaliasesoptional#-
Alternative names for entering this group, matched with the router's
casesettingSensitive readonly string[] descriptionoptional#-
Description shown below this group's help entry, without markup escaping or automatic delivery
string name#-
Group name using command-name syntax
It must not collide with sibling command or group names and aliases
string
PrefixCommandGroupMetadata #
Frozen group information copied at registration, including its registered-name path for adding children or selecting help
interface PrefixCommandGroupMetadata
extends PrefixCommandGroupDefinitioninterface PrefixCommandGroupMetadata
extends PrefixCommandGroupDefinitionkind#-
Distinguishes this non-executable group from a command in help visibility callbacks
"group" path#-
Registered names from the outermost group to this one, such as
["admin", "users"], even if the message used aliasesreadonly string[]
Inherited from Prefix: aliases, description, , name
PrefixCommandMetadata #
Frozen command information for inspection and help generation
Contains no callbacks, cooldown store or resource candidates. Help visibility callbacks also receive group entries, identified by kind: "group"
interface PrefixCommandMetadata
extends Omit<PrefixCommandDefinition, "arguments">interface PrefixCommandMetadata
extends Omit<
PrefixCommandDefinition,
"arguments"
>argumentsoptional#-
Frozen argument descriptions used to generate usage text, without candidate objects
readonly CommandArgumentMetadata[] kindoptional#-
Present for groups supplied to help visibility callbacks
Executable commands omit this property
"group" pathoptional#-
Registered names from the outer group through this entry, such as
["admin", "inspect"]Root commands omit this property
readonly string[]
Inherited from Prefix: aliases, description, , name, usage
PrefixCommandParse #
Parser result copied and frozen by the router before command callbacks run
interface PrefixCommandParseargs#-
Positional string tokens in input order, without array holes
A custom parser defines how these tokens are split
readonly string[] name#-
Command name or alias using command-name syntax in the selected parent
A returned group name does not enter that group
string raw#Args -
Argument text retained for callbacks
The default parser removes whitespace between name and arguments but preserves the remaining text
string
PrefixCommandParseInput #
Frozen input given to a custom parser after prefix matching and any group lookup
interface PrefixCommandParseInput<M extends MessageCore = Message>interface PrefixCommandParseInput<
M extends MessageCore = Message
>message#-
Incoming message with the attached client's selected fields
The router does not fetch omitted fields
M pathoptional#-
Registered parent group names already selected
Absent at root. Returning another name cannot change this parent
readonly string[] prefix#-
Matched prefix as written, such as
!!when both!and!!matchstring source#-
Text after the prefix at root, or after consumed group names and their separator whitespace
Remaining text is not rewritten
string
PrefixCommandParseRejection #
A parser result that reports a syntax mistake in text that was meant as a command, such as a quote that never closes
Return it instead of undefined so on receives the explanation as reason of Command. The router sends nothing by itself, so the application decides whether to turn the reason into a reply. The router passes the reason on unchanged and logs it at Debug, so keep it fixed text that is safe to show in a channel and leaves the sender's input out. Return undefined instead when the text is probably not a command, such as ordinary chat that begins with the prefix
interface PrefixCommandParseRejectionnameoptional#-
The command name the parser read before the mistake, in command-name syntax, which
onreceives asUnmatched nameso a reply can say which command the mistake was forOmit it when no name was read. Any other value is reported as a parser failure
string reason#-
Nonempty explanation, such as
A double quote was opened but never closedAny other value is reported as a parser failure
string
PrefixCommandParsing #
Parsing and cooldown settings shared by both API styles, apart from the prefix, which each API resolves in its own way
interface PrefixCommandParsing<M extends MessageCore = Message>interface PrefixCommandParsing<
M extends MessageCore = Message
>caseoptional#Sensitive -
Require matching letter case for command and group names and aliases
Defaults to false. This does not change prefix or argument matching
boolean cooldownsoptional#-
Size the router's own memory cooldown store, used by commands whose cooldown supplies no
store, such as{ maxEntries: 50_000 } Defaults to 10,000 keys, shared by every router derived from the same create. A full store never denies a new key: It removes expired keys first, then the reservation that expires soonest, which lets that key run again early. The same limit bounds the rejections
onremembers so that it does not repeat itself. Invalid options throwReject: "reply" Configurationwhen the router is createdError MemoryCooldownOptions ignoreoptional#Bots -
Skip messages whose author has
isset, before prefix resolutionBot Defaults to true
boolean mentionoptional#Prefix -
Also accept a mention of the bot, such as
@bot ping, as a prefixDefaults to false. Both mention forms,
<@id>and<@!id>, are accepted. The bot's user ID comes from the user in the gateway's READY, so normally no request is needed. When READY did not supply a valid ID, the first message that starts with a mention triggers oneusersread, which concurrent messages share. A failed read is logged at Warn in the commands category, and mentions are then treated as ordinary text without a new read for a backoff that starts at 5 seconds and doubles per consecutive failure up to 5 minutes, unless READY supplies the ID first.fetch Self boolean parseoptional#-
Split the command name and arguments, optionally using
commandsfor quoted words.parse Quoted By default, leading whitespace is trimmed, the name must use command-name syntax and trimmed arguments are split on whitespace. The default preserves
rawafter removing the command-name separator whitespace. Groups are consumed first using fixed whitespace separators, then this parser runs once on the remaining command text. A custom parser cannot enter another group by returning its name. Return undefined to decline parsing and call optionalArgs onfeedback. Return aUnmatched Prefixinstead to decline with aCommand Parse Rejection reasonthatonreceives, asUnmatched commandsdoes for an unclosed quote. A group with no following name reports.parse Quoted Commandwithout calling the parser. Throws and malformed results use the attached subscription's error reporting, with no automatic response or retryMissing Subcommand (input: PrefixCommandParseInput<M>) => PrefixCommandParse | PrefixCommandParseRejection | undefined(input: PrefixCommandParseInput<M>) => | PrefixCommandParse | PrefixCommandParseRejection | undefined
PrefixCommandRegistrationOptions #
Choose the existing parent group for a new command or child group
interface PrefixCommandRegistrationOptionsgroupoptional#-
Registered parent names, not aliases, such as
["admin", "users"]Omit or use
[]for root. Missing parents are not createdreadonly string[]
PrefixCommandRouter #
Immutable registered commands and groups that can be attached to a client
The register and register methods return new routers, so use the returned value for subsequent registrations and attachment. Older routers and their active attachments keep the definitions they already had
interface PrefixCommandRouter<M extends MessageCore = Message>interface PrefixCommandRouter<
M extends MessageCore = Message
>commands#-
Frozen command descriptions in registration order across groups, without callbacks or resource candidates
Grouped entries include paths of registered names
readonly PrefixCommandMetadata[] groups#-
Frozen groups in registration order, including empty groups
These names organize lookup and help, not permission checks
readonly PrefixCommandGroupMetadata[]
attach#
Subscribe this router to message through the client's existing bounded event system
Returns a Subscription immediately, without connecting the client or creating another queue. Invalid options throw Configuration, and a throwing on getter throws Sdk for commands with code application. On a closing or closed client, attach returns an already-closed Subscription whose wait succeeds, and writes a Warn log record with code events. Subscription options control scheduling, overflow and on reports for callback failures. With partition, the option partition holds a key's messages until that key's own setup is done, as for client. The held messages wait in the queue without using a concurrent command slot, and a hook that throws, rejects or returns an Err result reports each waiting message to the router's on, without a command name, and runs no command for it. A throwing partition function and an overflow that stops the router are reported to on the same way, in the order the reports arrived. Without an on they go to the client-level on or the log. The option without partition throws Configuration. Omitted or undefined settings default to eight concurrent commands and overflow drop, so a burst never stops the router. A failed command is reported with its command name, original error and message IDs to on, or logged at Error without one. A failed on callback, prefix resolver or custom parser is reported the same way without a command name. The router owns one bounded queue for its on hook, which receives reports one at a time in order, as for client. 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, cancellation 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 ignore 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 attaching twice can run the same command twice. Call subscription to detach it and signal cancellation, without shutting down the client. The returned subscription observes SDK cleanup, not completion of arbitrary application promises. Cancellation prevents later dispatch stages but cannot forcibly stop application promises already running. The client must have this router's message type M. A full-message router cannot attach to a client with omitted fields
attach(
client: Client<M>,
options?: EventHandlerOptions<unknown>
): Subscriptionattach(
client: Client<M>,
options?:
EventHandlerOptions<unknown>
): SubscriptionParameters
client-
Client<M> optionsoptional-
EventHandlerOptions<unknown>
- Returns
Subscription
help#
Build frozen help-text pages locally, for example router
help(options: CommandHelpOptions): readonly string[]help(
options: CommandHelpOptions
): readonly string[]Root help lists immediate commands and groups in sibling registration order.
Selecting a group by its registered names shows that group and its immediate children, including empty groups, unless include rejects an ancestor.
Entries show full registered-name 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 Configuration as for a missing one
Arguments generate <required>, [optional] and <rest...> or [rest...] syntax unless explicit usage overrides it.
No schema means no inferred argument syntax, and an explicit empty usage suppresses it.
A selection that is empty, or whose group or ancestor include rejects, returns []. Nothing is sent, and no prefix resolver, guard, cooldown or handler runs
Pages respect the UTF-16 length limit and end between visible characters, so a character represented by two code units, a flag or a base character with combining marks is 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 is not a lossless reconstruction. The caller selects pages to send and handles mentions
Malformed options, ill-formed text, too-small limits and invalid visibility callbacks throw Configuration.
A throwing option getter throws Sdk for commands with code application and the thrown value as its cause, while another unexpected fault uses sdk
Parameters
options-
CommandHelpOptions
- Returns
readonly string[]
register#
Validate and add one executable command to a new router, at root unless options selects an existing parent by registered names
Copies metadata and arguments while retaining callbacks and the cooldown store. 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 Configuration. A throwing getter throws Sdk for commands with code application, while another unexpected fault uses sdk. The original router and its attachments are unchanged
register<
S extends Readonly<Record<string, CommandArgumentDescriptor>>
= { … }
>(
command: PrefixCommand<S, M>,
options?: PrefixCommandRegistrationOptions
): PrefixCommandRouter<M>register<
S extends Readonly<
Record<
string,
CommandArgumentDescriptor
>
>
= { … }
>(
command: PrefixCommand<S, M>,
options?: PrefixCommandRegistrationOptions
): PrefixCommandRouter<M>Parameters
command-
PrefixCommand<S, M> optionsoptional-
PrefixCommandRegistrationOptions
- Returns
PrefixCommandRouter<M>
registerGroup #
Add a named group to a new router, at root or beneath an existing options path of registered names
Register parents before children. Groups accept identity and description only, with no handler, schema, guard or cooldown. At dispatch, group names and aliases are consumed using whitespace separators before the command parser runs. Invalid metadata, missing parents or sibling name collisions throw Configuration. A throwing getter throws Sdk for commands with code application, while another unexpected fault uses sdk. No callback runs and older routers remain unchanged
registerGroup(
group: PrefixCommandGroupDefinition,
options?: PrefixCommandRegistrationOptions
): PrefixCommandRouter<M>registerGroup(
group:
PrefixCommandGroupDefinition,
options?: PrefixCommandRegistrationOptions
): PrefixCommandRouter<M>Parameters
optionsoptional-
PrefixCommandRegistrationOptions
- Returns
PrefixCommandRouter<M>
registerMany #
Validate and add a nonempty object of commands to one new router, in the order of its own enumerable string keys and under the same optional parent group
Each object key supplies its command name. Omit arguments to leave raw args unrestricted, or set arguments: {} to reject positional arguments. The router copies every definition before registration. Keys inherited by the batch object 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 whole call throws Configuration, without a partially registered router or changes to this router and its attachments. The optional parent is validated and snapshotted once for the whole batch. A throwing getter throws Sdk for commands with code application, while another unexpected fault uses sdk. Each entry retains its own inferred argument-value type
registerMany<S extends Readonly<Record<string, unknown>>>(
commands: PrefixCommandBatch<M, S>,
options?: PrefixCommandRegistrationOptions
): PrefixCommandRouter<M>registerMany<
S extends Readonly<
Record<string, unknown>
>
>(
commands:
PrefixCommandBatch<M, S>,
options?: PrefixCommandRegistrationOptions
): PrefixCommandRouter<M>Parameters
commands-
PrefixCommandBatch<M, S> optionsoptional-
PrefixCommandRegistrationOptions
- Returns
PrefixCommandRouter<M>
PrefixCommandUnmatchedContext #
Frozen message and matched prefix for on, when parsing or lookup did not select an executable command
interface PrefixCommandUnmatchedContext<
M extends MessageCore = Message
>interface PrefixCommandUnmatchedContext<
M extends MessageCore = Message
>client#-
Attached client available for explicit feedback
The router does not take ownership of its lifetime
Client<M> message#-
Incoming frozen message with the attached 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, with this callback's cancellation signal already applied
A string is sent as the reply's content. Delegates to
client, including its validation, deadline, nonce, retry, cache and defect behavior. Cancellation or a lost response can leave the reply posted. An Err does not always mean nothing was sent, and uncertain sends are not replayed. Returning the result from the callback reports an Err like a thrown error.messages .reply ( input: string | ReplyInput, options?: Omit<RequestOptions, "signal"> ) => ResultAsync<M, ConfigurationError | SendError | CancelledError>( input: string | ReplyInput, options?: Omit<RequestOptions, "signal"> ) => ResultAsync< M, | ConfigurationError | SendError | CancelledError > signal#-
Cancellation signal for cooperative feedback work
A promise that ignores it may keep running after the subscription closes
AbortSignal
PrefixCommandsOptions #
Configure how an attached router recognizes command messages, without connecting or subscribing during construction
Prefix and parsing options, plus router-wide middleware, rejection feedback and optional feedback when a command-like message has no executable match
interface PrefixCommandsOptions<M extends MessageCore = Message>
extends PrefixCommandParsing<M>interface PrefixCommandsOptions<
M extends MessageCore = Message
>
extends PrefixCommandParsing<M>onoptional#Reject -
Rejection feedback for commands that do not set their own
onReject Omit it to give no feedback, except in
run, which replies by default. Guards returning false never send an automatic replyBot PrefixCommandRejectionFeedback<M> onoptional#Unmatched -
Handle parser declines, unknown names and groups without a subcommand, optionally sending feedback
An unknown name carries a
suggestionwhen a registered name is close, and a parser decline carries areasonand the commandnamewhen the parser explained a syntax mistake, such as an unclosed quote fromcommands. Return normally or a promise that completes the work. The router does not interpret a successful return value or send a response. The callback 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. Throws, rejected promises and returned Err results use the attachment's.parse Quoted onreporting, without retry. Not called for ignored bots or messages without a matching prefixError ( context: PrefixCommandUnmatchedContext<M>, unmatched: PrefixCommandUnmatched ) => unknown( context: PrefixCommandUnmatchedContext<M>, unmatched: PrefixCommandUnmatched ) => unknown prefix#-
Prefix text, accepted-prefix list or per-message resolver, which may return a promise
If several prefixes match, the longest wins
PrefixCommandPrefix<M> useoptional#-
Middleware run around every matched command, in order
readonly PrefixCommandMiddleware<M>[]
Inherited from Prefix: case, cooldowns, ignore, mention, parse
Type Aliases#
CommandArgumentDescriptor #
One argument's conversion settings, selected by its type field and validated when the command is registered
type CommandArgumentDescriptor =
| CommandArgumentText | CommandArgumentInteger
| CommandArgumentNumber | CommandArgumentBoolean
| CommandArgumentId | CommandArgumentChoice
| CommandArgumentUserSelection | CommandArgumentChannelSelection
| CommandArgumentRoleSelection | CommandArgumentMember
| CommandArgumentDuration | CommandArgumentCustomtype CommandArgumentDescriptor =
| CommandArgumentText
| CommandArgumentInteger
| CommandArgumentNumber
| CommandArgumentBoolean
| CommandArgumentId
| CommandArgumentChoice
| CommandArgumentUserSelection
| CommandArgumentChannelSelection
| CommandArgumentRoleSelection
| CommandArgumentMember
| CommandArgumentDuration
| CommandArgumentCustomCommandArgumentMention #
Additional complete-token mention form accepted by an id descriptor
User mentions are <@123> or <@!123>, channel mentions are <#123> and role mentions are <@&123>. Mention IDs follow the same rules as plain id tokens: Nonzero, without leading zeroes and no larger than 9223372036854775807. Surrounding text is not accepted
type CommandArgumentMention = "user" | "channel" | "role"type CommandArgumentMention =
"user" | "channel" | "role"CommandArgumentOptionalValue #
The converted value type V, with undefined added when descriptor D sets optional: true
type CommandArgumentOptionalValue<
D extends CommandArgumentDescriptor,
V
> = D extends { readonly default: number }
? V
: D extends { readonly optional: true } ? V | undefined : Vtype CommandArgumentOptionalValue<
D extends CommandArgumentDescriptor,
V
> = D extends {
readonly default: number
}
? V
: D extends {
readonly optional: true
}
? V | undefined
: VCommandArgumentRejectionReason #
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
type CommandArgumentRejectionReason =
"Missing" | "Invalid" | "Ambiguous" | "Unexpected"type CommandArgumentRejectionReason =
| "Missing" | "Invalid"
| "Ambiguous" | "Unexpected"CommandArgumentSchema #
Describe positional arguments by name, such as { text: { type: "text" }, count: { type: "integer" } }
The router reads the schema's own enumerable properties in order, ignoring inherited and non-enumerable properties. Converted values keep those property names. Names must begin with an ASCII letter and use only ASCII letters, digits or _. Required arguments must precede optional arguments, including arguments with a default, and only the final text or custom argument may use rest. Registration validates and freezes a copy, including choices and resource candidates. Omit the schema to accept any unconverted arguments. Use an empty schema to reject all supplied arguments
type CommandArgumentSchema =
Readonly<Record<string, CommandArgumentDescriptor>>type CommandArgumentSchema =
Readonly<
Record<
string,
CommandArgumentDescriptor
>
>CommandArgumentType #
Supported positional conversions, performed locally after the guard allows the command
The text, id and choice types produce strings, numeric types and duration produce numbers and boolean produces a boolean. The member type produces a member reference in the message's community, and custom produces whatever its synchronous parse function returns. The user, channel and role types select a frozen object from a fixed list of candidates supplied at registration, without a lookup request. For any user, channel or role, use id with a mention form, or member for a member of the message's community
type CommandArgumentType =
| "text" | "integer" | "number" | "boolean" | "id"
| "userChoice" | "channelChoice" | "roleChoice" | "choice"
| "member" | "duration" | "custom"type CommandArgumentType =
| "text" | "integer" | "number"
| "boolean" | "id"
| "userChoice"
| "channelChoice"
| "roleChoice" | "choice"
| "member" | "duration"
| "custom"CommandArgumentValue #
TypeScript result type for one descriptor, including undefined when optional: true is selected without a default
Text, IDs and choices return strings, numeric and duration descriptors return numbers and boolean descriptors return booleans. Member descriptors return a Member and custom descriptors return their parse function's value type. Resource descriptors return only the candidate's documented fields, not extra fields from the original object
type CommandArgumentValue<D extends CommandArgumentDescriptor> =
D extends CommandArgumentText
? CommandArgumentOptionalValue<D, string>
: D extends CommandArgumentInteger | CommandArgumentNumber
? CommandArgumentOptionalValue<D, number>
: D extends CommandArgumentBoolean
? CommandArgumentOptionalValue<D, boolean>
: D extends CommandArgumentId
? CommandArgumentOptionalValue<D, string>
: D extends CommandArgumentChoice<infer C>
? CommandArgumentOptionalValue<D, C[number]>
: D extends CommandArgumentUserSelection
? CommandArgumentOptionalValue<D, CommandArgumentUser>
: D extends CommandArgumentChannelSelection
? CommandArgumentOptionalValue<D, CommandArgumentChannel>
: D extends CommandArgumentRoleSelection
? CommandArgumentOptionalValue<D, CommandArgumentRole>
: D extends CommandArgumentMember
? CommandArgumentOptionalValue<D, MemberReference>
: D extends CommandArgumentDuration
? CommandArgumentOptionalValue<..., ...>
: ... extends ... ? ... : ...type CommandArgumentValue<
D extends CommandArgumentDescriptor
> = D extends CommandArgumentText
? CommandArgumentOptionalValue<
D, string
>
: D extends
| CommandArgumentInteger
| CommandArgumentNumber
? CommandArgumentOptionalValue<
D, number
>
: D extends CommandArgumentBoolean
? CommandArgumentOptionalValue<
D, boolean
>
: D extends CommandArgumentId
? CommandArgumentOptionalValue<
D, string
>
: D extends CommandArgumentChoice<
infer C
>
? CommandArgumentOptionalValue<
D, C[number]
>
: D extends CommandArgumentUserSelection
? CommandArgumentOptionalValue<
D, CommandArgumentUser
>
: D extends CommandArgumentChannelSelection
? CommandArgumentOptionalValue<
D,
CommandArgumentChannel
>
: D extends CommandArgumentRoleSelection
? CommandArgumentOptionalValue<
D, CommandArgumentRole
>
: D extends CommandArgumentMember
? CommandArgumentOptionalValue<
D, MemberReference
>
: D extends CommandArgumentDuration
? CommandArgumentOptionalValue<
..., ...
>
: ... extends ... ? ... : ...CommandArgumentValues #
Frozen converted values keyed by the command's argument names, such as values
Available to execution and cooldown-key callbacks only after the entire schema converts successfully. Optional descriptors add undefined to their value type. Guards and rejection callbacks receive raw context instead
type CommandArgumentValues<
S extends CommandArgumentSchema
> = { readonly [K in keyof S]: CommandArgumentValue<S[K]> }type CommandArgumentValues<
S extends CommandArgumentSchema
> = {
readonly [K in keyof S]:
CommandArgumentValue<S[K]>
}CommandCooldownClaim #
Outcome of reserving a cooldown, selected by _tag
Only Cooldown permits execution. A denied claim may call on, but the router does not wait or retry. Retry times are millisecond timestamps since 1970-01-01 UTC, not wait durations
type CommandCooldownClaim =
| {
readonly _tag: "CooldownAcquired"
readonly retryAtMs: number
}
| {
readonly _tag: "CooldownActive"
readonly retryAtMs: number
}type CommandCooldownClaim =
| {
readonly _tag:
"CooldownAcquired"
readonly retryAtMs: number
}
| {
readonly _tag:
"CooldownActive"
readonly retryAtMs: number
}CommandCooldownPer #
Whose invocations share one cooldown: The invoking user, the channel, or the guild
A guild cooldown in a direct message applies to that conversation
type CommandCooldownPer = "user" | "channel" | "guild"type CommandCooldownPer =
"user" | "channel" | "guild"PrefixCommandBatch #
Commands keyed by name, as register and the run 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<
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<
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
Return true to allow it, false to deny it silently, or { deny: "reason" } to deny it with text that on sends. A promise of those values is awaited. A false verdict is still counted, logged at Debug and passed to a custom on callback, but never sends an automatic reply. The built-in guards cover common checks. Throws, rejected promises, Err results and other values use subscription error reporting
type PrefixCommandGuard<M extends MessageCore = Message> =
(context: PrefixCommandContext<M>)
=>
| PrefixCommandGuardResult
| PromiseLike<PrefixCommandGuardResult>type PrefixCommandGuard<
M extends MessageCore = Message
> = (
context: PrefixCommandContext<M>
) =>
| PrefixCommandGuardResult
| PromiseLike<
PrefixCommandGuardResult
>PrefixCommandGuardResult #
A guard's decision: true allows the command, false denies it without an automatic reply, and { deny } denies it with a reason that on sends to the user
Both denials are counted and logged at Debug. A custom on callback receives either denial
type PrefixCommandGuardResult = boolean | { readonly deny: string }type PrefixCommandGuardResult =
| boolean
| { readonly deny: string }PrefixCommandMiddleware #
Run around every matched command of a router, in registration order, before its guards
Call next() to continue and await it to observe completion. Not calling it stops the command, which is logged at Debug. The rest of the chain runs at most once: Calling next() again, even concurrently, returns the first call's promise. When the command fails, next() rejects with the command's own error, the same value its execute callback threw, rejected with or returned in an Err
type PrefixCommandMiddleware<M extends MessageCore = Message> =
(context: PrefixCommandContext<M>, next: () => Promise<void>)
=> unknowntype PrefixCommandMiddleware<
M extends MessageCore = Message
> = (
context: PrefixCommandContext<M>,
next: () => Promise<void>
) => unknownA failure of the command is reported once with its command name, whatever the middleware does with the rejection.
Rethrowing that same error adds no second report. A different throw, rejection or returned Err from 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() was not called yet.
The command's failure is still reported when the subscription closes while the middleware is running
PrefixCommandPrefix #
Text that starts a command invocation, such as !, or a list of accepted prefixes
A resolver can choose prefixes from each message, for example from per-community settings, and may return a promise. Return undefined to ignore the message. Prefixes must be nonempty strings, and arrays must be nonempty and contain no holes. Matching is exact at the start of message content, with the longest matching prefix selected
type PrefixCommandPrefix<M extends MessageCore = Message> =
| string
| readonly string[]
| ((message: M)
=>
| PrefixCommandPrefixValue
| PromiseLike<PrefixCommandPrefixValue>)type PrefixCommandPrefix<
M extends MessageCore = Message
> =
| string
| readonly string[]
| ((message: M)
=>
| PrefixCommandPrefixValue
| PromiseLike<
PrefixCommandPrefixValue
>)PrefixCommandPrefixValue #
Prefix text selected for one message: One prefix, a list of accepted prefixes, or undefined to ignore the message
type PrefixCommandPrefixValue =
string | readonly string[] | undefinedtype PrefixCommandPrefixValue =
| string
| readonly string[]
| undefinedPrefixCommandRejection #
Why a known command did not execute, passed as a frozen value to its optional on callback
Inspect _tag to distinguish a denied guard, cooldown or invalid arguments. The router replies only when on is selected, and it schedules no retry. That reply is limited: An active cooldown key is answered once until its retry, and a guard denial once per user and command every 5 seconds. Argument rejections are always answered
type PrefixCommandRejection =
| {
readonly _tag: "CommandGuardRejected"
readonly reason?: string
}
| {
readonly _tag: "CommandCooldownActive"
readonly retryAtMs: number
}
| {
readonly _tag: "CommandArgumentRejected"
readonly argument: string
readonly reason: CommandArgumentRejectionReason
readonly retryAtMs?: never
}type PrefixCommandRejection =
| {
readonly _tag:
"CommandGuardRejected"
readonly reason?: string
}
| {
readonly _tag:
"CommandCooldownActive"
readonly retryAtMs: number
}
| {
readonly _tag:
"CommandArgumentRejected"
readonly argument: string
readonly reason: CommandArgumentRejectionReason
readonly retryAtMs?: never
}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 receives the context and rejection instead, including false guard denials
type PrefixCommandRejectionFeedback<
M extends MessageCore = Message
> =
| "reply"
| "silent"
| ((
context: PrefixCommandContext<M>,
rejection: PrefixCommandRejection
) => unknown)type PrefixCommandRejectionFeedback<
M extends MessageCore = Message
> =
| "reply"
| "silent"
| ((
context:
PrefixCommandContext<M>,
rejection:
PrefixCommandRejection
) => unknown)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. 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 retry
PrefixCommandUnmatched #
A prefix matched but no executable command was selected
Passed as a frozen value to the router's optional on callback, without an automatic response. Ignored bot messages and messages without a matching prefix do not produce this callback
type PrefixCommandUnmatched =
| {
readonly _tag: "CommandUnknownName"
readonly name: string
readonly path?: readonly string[]
readonly suggestion?: string
}
| {
readonly _tag: "CommandParserRejected"
readonly name?: string
readonly path?: readonly string[]
readonly reason?: string
}
| {
readonly _tag: "CommandMissingSubcommand"
readonly path: readonly string[]
}type PrefixCommandUnmatched =
| {
readonly _tag:
"CommandUnknownName"
readonly name: string
readonly path?:
readonly string[]
readonly suggestion?:
string
}
| {
readonly _tag:
"CommandParserRejected"
readonly name?: string
readonly path?:
readonly string[]
readonly reason?: string
}
| {
readonly _tag: "CommandMissingSubcommand"
readonly path:
readonly string[]
}