Read command arguments¶
A command you declared has been run. This is how to find out which one, and read what it was given.
Declaring the surface is Declare a command surface.
1. Take interactions off the feed¶
for in := range interactive.Interactions() {
if in.Type != chatplatform.CommandInvoked {
continue
}
// ...
}
2. Work out which command ran¶
Command is the top-level name. Path is the verbs beneath it, and is empty
for a flat command:
switch {
case in.Command == "ask":
// /ask
case in.Command == "scout" && slices.Equal(in.Path, []string{"join"}):
// /scout join
case in.Command == "scout" && slices.Equal(in.Path, []string{"table", "new"}):
// /scout table new
}
Path is reported rather than reconstructed, so you never parse a verb back out
of a name you flattened.
3. Read each argument through its own accessor¶
channel, ok := in.Args.Channel("channel")
if !ok {
_ = actor.Respond(ctx, in.Token, "I need a channel to set that up.", true)
return
}
Every accessor answers (value, bool): String, Int, Number, Bool,
Channel, User, Role.
The second return is the whole point. It is false when the argument was not sent, and false when it arrived as a different kind. The obvious code (take the value, give up if there is none) is also the correct code.
A zero Args is safe to read from. A command invoked with nothing is ordinary,
not exceptional.
4. Know why String refuses a channel¶
id, ok := in.Args.Channel("channel") // ("123", true) — the platform resolved it
txt, ok := in.Args.String("channel") // ("", false) — deliberately
A channel the platform resolved is an identifier, not text. Handing its id
back through String would make it indistinguishable from a channel name
somebody typed by hand, which is the ambiguity typed options exist to remove.
That distinction is also how you detect a fallback. If you asked
SupportsOption before registering, found OptionChannel unsupported, and
declared OptionString instead, then the value arrives as text and String
answers true:
if id, ok := in.Args.Channel("channel"); ok {
use(id) // the platform resolved it
} else if name, ok := in.Args.String("channel"); ok {
resolveYourself(name) // you chose text, knowingly
}
A provider never makes that substitution on your behalf. Where a platform
cannot carry a type, RegisterCommands refuses rather than quietly downgrading
it, so the only way that second branch runs is that you asked for it.
5. Answer¶
An interaction expects a response, and the token is what addresses it:
The final argument keeps the reply private to the person who ran the command.
Deciding who may run what¶
Read in.By.Roles. CommandSpec.RequiredRoles asks the platform to gate the
command where it can, but platform-side gating is a convenience and never the
authority, because a provider that cannot enforce it still delivers the
interaction, so the check has to be yours regardless.
Never decide authorisation from By.Name, and never from anything in message
content.