Fundamentals
Commands
Since version 8 of the websocket, Discord have announced their willingness to migrate traditional commands designed with the MessageCreate event to dedicated components that we call slash commands.
See more about version 8
Basic usage
To simplify the creation of command files, the CLI provides you with an initialisation command.
dart run mineral make:command <MyCommand>
A question/answer game then appears, asking you to select an event from among those available in the database.
The slash commands
The mineral framework provide you with dedicated classes, please consider the examples below.
import 'package:mineral/framework.dart';
import 'package:mineral/core/api.dart';
class HelloCommand extends MineralCommand<GuildCommandInteraction> {
HelloCommand() : super(
label: Display("hello"),
description: Display("Say Hello !")
);
Future<void> handle(CommandInteraction interaction) async {
await interaction.reply(content: "Hello world !", private: true);
}
}
A command can also contain options, which are parameters that the user can enter when calling the command. As shown below with the word
option, which is a string
type.
import 'package:mineral/framework.dart';
import 'package:mineral/core/api.dart';
class HelloCommand extends MineralCommand<GuildCommandInteraction> {
HelloCommand() : super(
label: Display("hello"),
description: Display("Say Hello !"),
options: [
CommandOption.string(Display("word"), Display("Say what you want !"), required: true)
]);
Future<void> handle(CommandInteraction interaction) async {
String word = interaction.getValue<String>("word"); // Get the value of the option "word"
await interaction.reply(content: "Hello $word !", private: true);
}
}
The sub-commands
In the same update, Discord also announced the ability to create subcommands, which are commands nested within a main command. To create a subcommand, you need to create a class that extends MineralSubCommand<T extends CommandInteraction>
. We've simplified this to avoid having all sub commands in the same file. So you can create a file for each sub command.
You cannot use a subcommand and a command, the command will be ignored.
import 'package:mineral/framework.dart';
import 'package:mineral/core/api.dart';
class WorldCommand extends MineralSubcommand<GuildCommandInteraction> {
WorldCommand(): super(
label: Display("world"),
description: Display("Say hello world")
);
Future<void> handle(CommandInteraction interaction) async {
await interaction.reply(content: "Hello world !", private: true);
}
}
Register the sub-command
In our previous HelloCommand
class, we'll define our subcommand in our constructor:
import 'package:mineral/framework.dart';
import 'package:mineral/core/api.dart';
import 'subcommands/world.dart';
class HelloCommand extends MineralCommand<GuildCommandInteraction> {
HelloCommand() : super(
label: Display("hello"),
description: Display("Say Hello !"),
subcommands: [
WorldCommand()
]);
// This is useless, that will be ignored because we have a subcommand
/*Future<void> handle(CommandInteraction interaction) async {
String word = interaction.getValue<String>("word"); // Get the value of the option "word"
await interaction.reply(content: "Hello $word !", private: true);
}*/
}
The sub-command groups
Subcommand groups are subcommands that can contain subcommands, so here's how to register them:
// subcommands/world_subcommand.dart
import 'package:mineral/framework.dart';
import 'package:mineral/core/api.dart';
class WorldSubCommand extends MineralSubcommand<GuildCommandInteraction> {
WorldSubCommand(): super(
label: Display("world"),
description: Display("Say hello world")
);
Future<void> handle(CommandInteraction interaction) async {
await interaction.reply(content: "Hello world !", private: true);
}
}
// hello_command.dart
import 'package:mineral/framework.dart';
import 'package:mineral/core/api.dart';
import 'subcommands/world_subcommand.dart';
class HelloCommand extends MineralCommand<GuildCommandInteraction> {
HelloCommand() : super(
label: Display("hello"),
description: Display("Say Hello !"),
groups: [
MineralCommandGroup(
label: Display("world"),
description: Display("Say hello to world"),
subcommands: [WorldSubCommand()]
)
]);
}
Here you go, now you can use groups commands and sub commands !
Accessing the options
The commands allow you to easily define various parameters that the Mineral framework makes available to you through the interaction received as parameters.
import 'package:mineral/framework.dart';
import 'package:mineral/core/api.dart';
class WorldSubCommand extends MineralSubcommand<GuildCommandInteraction> {
WorldSubCommand(): super(label: Display("world"), description: Display("Say hello world"), options: [
CommandOption.string(Display("string"), Display("String description"), required: true),
CommandOption.double(Display("double"), Display("Number description"), required: true),
CommandOption.bool(Display("boolean"), Display("Boolean description"), required: false), // this can be null
CommandOption.user(Display("user"), Display("User description"), required: false), // this can be used for guildMember too and can be null
CommandOption.role(Display("role"), Display("Role description"), required: true),
CommandOption.channel(Display("channel"), Display("Channel description"), required: true),
CommandOption.mentionable(Display("mentionable"), Display("Mentionable description"), required: true),
]);
Future<void> handle(CommandInteraction interaction) async {
final string = interaction.getValue<String>("string");
final doubleValue = interaction.getValue<double>("double");
final boolean = interaction.getValue<bool?>("boolean"); // this can be null
final GuildMember? member = interaction.getMember("user");
final User? user = interaction.getUser("user"); // this can be used for guildMember too
final role = interaction.getRole("role");
final channel = interaction.getChannel("channel");
final mentionable = interaction.getMentionable("mentionable");
await interaction.reply(content: "Hello world !", private: true);
}
}
You'll find that Mineral can't guess whether an option is null or not, so you need to use the ?
for options that may be null.