Skip to content
This repository has been archived by the owner on Aug 22, 2024. It is now read-only.

@Command

Adam Pine edited this page May 18, 2021 · 2 revisions

📟 Commands

discord.ts provides a decorator allowing the implementation of command systems very simply by essentially using only two decorators @Command(commandName?: string) and @CommandNotFound().

We will also use @Discord(prefix: string) to specify a prefix for our commands within the class.

You can specify a regex expression for your command names
For advanced usage use the @Rules decorator, you can also specify aliases using that

Notice that the first arguments do not use ArgsOf, the first payload is a CommandMessage.

import {
  Discord,
  On,
  Client,
  Command,
  CommandMessage,
  CommandNotFound
} from "@typeit/discord";

// Specify your prefix
@Discord("!") 
abstract class AppDiscord {
  // Reachable with the command: !hello
  @Command("hello")
  private hello(message: CommandMessage) {
  }

  // !bye
  // !yo
  @CommandNotFound()
  private notFound(message: CommandMessage) {
  }
}

The CommandMessage object

The CommandMessage is the first argument injected into a method using @Command or @CommandNotFound, it has exactly the same structure as the Message object in discord.js except that it includes useful information about the command that was executed such as:

  • prefix: string
    The prefix that is applied to your command.

  • commandName: string
    The command name

  • commandContent: string
    The message content without the prefix (-cmd hello there becomes hello there)

  • description: string
    The command description

  • infos: InfoType (any)
    The command infos

  • args: ArgsType (any)
    The command arguments

  • discord: DiscordInfos:
    The linked @Discord class infos

  • argsRules: (ArgsRulesFunction<Expression>[])
    The rules that are applied to execute the command (advanced)

Args parsing

You have the ability to specify arguments for your command, as express.js does in it's routing system. So by using ":" (or the value specified in variablesChar when your Client intializes) in the name of your @Command in front of the dynamic values, discord.ts will extract these informations when a command is executed and inject it into the args property of your CommandMessage with the correct name that you indicated in the command name.

If the argument value is a number the value will be casted automaticaly

@Discord("!")
abstract class AppDiscord {
  @Command("args :slug :number")
  private hello(message: CommandMessage) {
    const mySlug = message.args.slug;
    const myNumber = message.args.number;

    // Using js destructuring:
    const { slug, number } = message.args; 
  }
}

Dynamic Values

Okay but what if my prefix or my command name of my @Discord / @Command decorators depends on external datas? Well you can specify a function that will be executed when a command is executed to verify the origin and return the correct value.

You receive the Message as the first argument and the Client instance as the second one.

This is also applied to @ComputedRules

// If the message has been sent in the guild with
// the name MyGuildName the prefix "." will be considered
// otherwise the prefix "$" will trigger the action.
async function prefixBehaviour(message: Message, client: Client) {
  if (message.guild.name === "MyGuildName") {
    return ".";
  }
  return "$";
}

@Discord(prefixBehaviour)
abstract class AppDiscord {
  @Command("hello")
  private hello(message: CommandMessage) {
    // ...
  }
}

With a dynamic command name:

May be for a very specific use case

// If the message has been sent in the guild with
// the name MyGuildName the prefix "." will be considered
// otherwise the prefix "$" will trigger the action.
async function prefixBehaviour(message: Message, client: Client) {
  if (message.guild.name === "MyGuildName") {
    return ".";
  }
  return "$";
}

// The command name will be yo if the message is "hello"
async function commandName(message: Message, client: Client) {
  if (message.content === "hello") {
    return "yo";
  }
  return "hello";
}


@Discord(prefixBehaviour)
abstract class AppDiscord {
  @Command(commandName)
  private hello(message: CommandMessage) {
    // ...
  }
}

Command directory pattern

Example

If you have a directory pattern that looks like this:

Main.ts
DiscordApp.ts
commands
- Ping.ts
- Hello.ts
- Blabla.ts
events
- MessageDelete.ts

You should use the import parameter for the @Discord decorator. Here, all the elements will be injected into this Discord class instance.

import * as Path from "path";
import {
  Discord,
  CommandNotFound
} from "@typeit/discord";

// The prefix will be applied to the imported commands
@Discord("!", {
  import: [
    Path.join(__dirname,  "commands", "*.ts"),
    Path.join(__dirname,  "events", "*.ts")
    // You can also specify the class directly here if you don't want to use a glob
  ]
})
export abstract class DiscordApp {
  // This command not found is triggered
  @CommandNotFound()
  notFoundA(command: CommandMessage) {
    command.reply("Command not found");
  }
}

Here is an example of what your command file should look like: Bye.ts

import {
  Command,
  CommandMessage
} from "@typeit/discord";

// Do not have to decorate the class with @Discord
// It applied the parameters of the @Discord decorator that imported it
export abstract class Bye {
  @Command("bye")
  async bye(command: CommandMessage) {
    command.reply("Bye!");
  }

  @Command("ciao")
  async ciao(command: CommandMessage) {
    command.reply("Ciao!");
  }
}

MessageDelete.ts

import {
  On,
  ArgsOf
} from "@typeit/discord";

// Do not have to decorate the class with @Discord
// It applied the parameters of the @Discord decorator that imported it
export abstract class MessageDelete {
  @On("messageDelete")
  async onMessageDelete([message]: ArgsOf<"messageDelete">) {
    message.reply("Bye!");
  }
}