DiscordPHP-BridgeBot Documentation

SlashAdapter
in package

FinalYes

Registers actions that carry a {@see Slash} spec as Discord slash commands.

The surface that reuses the most: an action's handler is not touched at all. Discord's typed options are rendered back into exactly the text the prefix form would have produced — a channel picker becomes <#id> — so /twitch link target:x and !twitch link x run the same code down to the argument indices.

The shape of the tree

One command per qualifier, and the action's group decides how deep it sits:

/twitch link                    (no group)
/twitch channel title           (group: channel)

Discord caps a command at 25 options and allows exactly one level of sub-command group, which is the whole reason groups exist — a connector with thirty commands does not fit flat. What it does not do is let the qualifier be dropped; a chat surface does that, this one does not.

Slash commands matter beyond convenience. Message Content is a privileged intent; a server that has not granted it gets no prefix commands at all, but slash commands keep working, because Discord delivers the arguments rather than the bot reading them out of a message.

Tags
author

Valithor Obsidion valithor@discordphp.org

Table of Contents

Constants

MAX_DESCRIPTION  : mixed = 100
And on a description.
MAX_OPTIONS  : mixed = 25
Discord's cap on options — sub-commands and groups included.

Properties

$bot  : Bot
$registry  : ActionRegistry

Methods

__construct()  : mixed
register()  : void
Defines the commands with Discord, and starts listening for them.
argumentsFor()  : Arguments
Turns an interaction's typed options into the arguments a prefix handler expects.
build()  : CommandBuilder
Assembles one qualifier's actions into a single command definition.
builder()  : MessageBuilder
Every outbound message: clamped, and pinging nobody.
clampDescription()  : string
contextFor()  : PromiseInterface<string|int, Context>
Builds the invocation context, resolving which room this Discord channel acts on. Mirrors {@see DiscordAdapter::contextFor()}.
define()  : void
describe()  : string
Discord caps a description at 100 characters and rejects an empty one.
describeQualifier()  : string
fail()  : void
invoke()  : void
leafOptions()  : mixed
Walks the interaction down to the invoked leaf's own options.
logFailure()  : void
lowestAccess()  : Access
option()  : Option
publish()  : bool
Creates the command, updates it when this build defines something different, and leaves it alone otherwise.
rendered()  : string|MessageBuilder|null
reply()  : void
settle()  : PromiseInterface<string|int, string|MessageBuilder|null>
Normalises a handler's return into a promise of what to send.
subCommand()  : Option
tree()  : array<string, array<int, Action>>
Every slash-capable action, grouped by the command it belongs under.

Constants

MAX_DESCRIPTION

And on a description.

public mixed MAX_DESCRIPTION = 100

MAX_OPTIONS

Discord's cap on options — sub-commands and groups included.

public mixed MAX_OPTIONS = 25

Properties

Methods

register()

Defines the commands with Discord, and starts listening for them.

public register() : void

argumentsFor()

Turns an interaction's typed options into the arguments a prefix handler expects.

private argumentsFor(Action $action, Interaction $interaction) : Arguments

Each value is recorded twice: by name, and positionally in declaration order. That is what lets one handler read get(0) and another named('target') against the same invocation.

Positional recording stops at the first option the user omitted. Carrying on would shift every later value down an index, so a handler reading get(1) would silently receive what was meant for get(2) — the kind of bug that bans the wrong person.

Parameters
$action : Action
$interaction : Interaction
Return values
Arguments

build()

Assembles one qualifier's actions into a single command definition.

private build(string $qualifier, array<int, Action> $actions) : CommandBuilder
Parameters
$qualifier : string
$actions : array<int, Action>
Return values
CommandBuilder

builder()

Every outbound message: clamped, and pinging nobody.

private builder(string $text) : MessageBuilder
Parameters
$text : string
Return values
MessageBuilder

clampDescription()

private clampDescription(string $description) : string
Parameters
$description : string
Return values
string

contextFor()

Builds the invocation context, resolving which room this Discord channel acts on. Mirrors {@see DiscordAdapter::contextFor()}.

private contextFor(Action $action, Interaction $interaction, Access $access, Arguments $arguments, bool $isPublic) : PromiseInterface<string|int, Context>
Parameters
$action : Action
$interaction : Interaction
$access : Access
$arguments : Arguments
$isPublic : bool
Return values
PromiseInterface<string|int, Context>

define()

private define(array<string, array<int, Action>> $tree, GlobalCommandRepository $repo) : void
Parameters
$tree : array<string, array<int, Action>>
$repo : GlobalCommandRepository

describe()

Discord caps a description at 100 characters and rejects an empty one.

private describe(Action $action) : string
Parameters
$action : Action
Return values
string

describeQualifier()

private describeQualifier(string $qualifier, array<int, Action> $actions) : string
Parameters
$qualifier : string
$actions : array<int, Action>
Return values
string

fail()

private fail(Interaction $interaction, Action $action, Throwable $e) : void
Parameters
$interaction : Interaction
$action : Action
$e : Throwable

invoke()

private invoke(Action $action, Interaction $interaction) : void
Parameters
$action : Action
$interaction : Interaction

leafOptions()

Walks the interaction down to the invoked leaf's own options.

private leafOptions(Action $action, Interaction $interaction) : mixed

Discord nests these the same way the command is declared: a group holds a sub-command, which holds the values. Reading the top level instead would find the sub-command's name where a value was expected.

Parameters
$action : Action
$interaction : Interaction

publish()

Creates the command, updates it when this build defines something different, and leaves it alone otherwise.

private publish(string $qualifier, array<int, Action> $actions, GlobalCommandRepository $repo) : bool

Neither extreme works. Skipping whatever is already there means a command keeps the shape it had the first time it was published — add a sub-command, restart, and it is routed in code but never offered by Discord, so the handler cannot be reached. Upserting on every boot reaches Discord but costs one rate-limited write per command per restart to republish definitions that did not change, and a global command takes up to an hour to propagate, so rewriting it restarts that clock for nothing.

CommandSync tells the two apart, leniently enough that the fields Discord adds on the way back — id, version, defaults it fills in, key order — do not read as a change.

Parameters
$qualifier : string
$actions : array<int, Action>
$repo : GlobalCommandRepository
Return values
bool —

Whether a write was issued.

rendered()

private static rendered(mixed $value) : string|MessageBuilder|null
Parameters
$value : mixed
Return values
string|MessageBuilder|null

reply()

private reply(Interaction $interaction, string|MessageBuilder $reply) : void
Parameters
$interaction : Interaction
$reply : string|MessageBuilder

settle()

Normalises a handler's return into a promise of what to send.

private settle(mixed $result) : PromiseInterface<string|int, string|MessageBuilder|null>

A handler may answer with a string, or with a MessageBuilder when a sentence will not do — a Components v2 panel with buttons that stay live, most obviously. A builder only renders here, so an action that returns one must declare itself Discord-only; a chat has nothing to do with it.

Parameters
$result : mixed
Return values
PromiseInterface<string|int, string|MessageBuilder|null>

tree()

Every slash-capable action, grouped by the command it belongs under.

private tree() : array<string, array<int, Action>>
Return values
array<string, array<int, Action>>
On this page

Search results