Skip to content

Commands

Henry Xu edited this page Dec 13, 2020 · 16 revisions

This Wiki page details the process of how a command is registered, parsed, and dispatched. It also details how a developer should go about implementing a new command.

TL;DRs

Commands

When a developer is implementing a command, they should only have to deal with implementing the feature of the command, and not dealing with help methods, validation of arguments, etc. Once the command has been implemented, it should ideally be able to function without having to modify any other code.

VoiceCommands

When developers are implementing a command that requires receiving user voice / sending bot voice they need some more context than a regular command. Developers can indicate whether or not they want to have the bot joining the channel or moving from channel to channel based on the executing command. They can also indicate whether or not users who are not in a voice channel are allowed to execute the command.

Registry

The registry registers all the commands during runtime on startup. It holds a reference to all command keywords and their matching commands. It also resolves aliased command keywords to their hardcoded keyword.

Dispatcher

The dispatcher handles everything from when a user wishes to start a command (e.g sending a message), to parsing the message for the keyword and arguments, checking permissions / privileges of the user, running the pre execution of the command, and finally executing the command.

Parser

The parser handles the parsing and validation of the command message.

Commands

Commands are implemented by extending the base Command class.

Commands that require bot voice or user voice should extend the VoiceCommand class instead for handling of VoiceConnection situations.

The following are a list of features that are automatically done for you by the command architecture on runtime:

  • Registration of the command on startup
  • Execution of the command with context, argument values, executing user, and message source (e.g text message)
  • Description of command details using the help command. This includes command description, arguments, argument descriptions, examples, etc.
  • Validation of executing users permissions and privileges (see Privileges for details)
  • Throttling (optional) of the command if user has executed too many times within a set time span
  • Validation of argument values and types
  • Validation that arguments labelled as required are available
  • Execution of the optionally implemented preExecute method
  • Handling of thrown errors during command execution
  • Acknowledgement of command via returned promise from execution
  • Ability to be aliased through the alias command
  • Ability to have a macro created through the macro command

The structure of the command execution is based on the assumption that a command will do the following:

  1. Receive a keyword and some arguments
  2. Do something with the provided arguments
  3. Reply with an acknowledgement or message with the status of the execution

If an error occurs while executing the command (i.e no songs found for a search, etc.), a CommandExecutionError should be thrown. The CommandExecutionError has an optional message and / or emoji for acknowledgement purposes.

VoiceCommands

VoiceCommands are an extension of the Command class. The VoiceCommand class simply implements a set of checks required during the preExecute stage. A VoiceCommand can require that a certain state be fulfilled before the command is executed in order to prevent unnecessary or undesired joining/leaving/movement of the bot to/from/among VoiceChannels.

These states are implemented as abstract method that return a boolean:

  • botMustBeInTheSameVoiceChannel

The bot must be in the same voice channel as the user

  • botMustAlreadyBeInVoiceChannel

The bot must have already been in a voice channel

  • userMustBeInVoiceChannel

The user must be in a voice channel

Before the command is executed, all these checks will be done, if they all pass, the bot will then prepare to join / move to the correct voice channel in order to execute the command.

To determine if a bot will join a channel to execute a command, it will check the following protected method:

  • botShouldNotJoinVoiceChannelIfNotReady

Returns false by default (i.e will join channel)

Thus, if the command requests that the bot not join the correct voice channel if it was not already, the executed command will reject the promise during the preExecute stage. If the bot requests that the bot do join the voice channel, the executed command will be executed after the join voice channel promise is resolved.

Registry

The Registry handles the registration of commands on startup. This is done by recursively requiring all modules of the provided path and storing them into memory. Modules are validated to ensure they are commands by verifying the object type. The register assumes that the modules export the command by default (i.e export default class ExampleCommand). The register will also validate certain things about the command. These checks include:

  • Ensuring no two commands share the same keyword
  • Ensuring keyword is lowercase (to ensure case-insensitivity)
  • Ensuring all argument flags are unique
  • Ensuring required values do not have a default value
  • and more

Dispatcher

The Dispatcher handles the dispatch of commands. It is important to note that the dispatcher only handles voice commands after they have been converted into a command string. The dispatch handles the following:

  • Listening to messages to channels
  • Reducing the keyword if it is an alias or macro
  • Checking privileges / permissions of executing user
  • Checking throttle status of executing user
  • Pre execution of command and subsequent error handling
  • Execution of command

The dispatcher is the central routine that handles the execution of commands, and it will call on the parser and registry for handling of the parsing and retrieval of commands.

Parser

The Parser handles the parsing of the command string. This includes:

  • Parsing the keyword using the command prefix
  • Parsing the arguments
  • Validating that required arguments are present
  • Validating argument values and types
  • Processing arguments into pre-determined types (if required) (i.e User)
  • Validating file attachments

The parser returns the arguments as a map relating the argument name (not the argument flag) to the argument value.

Clone this wiki locally