Commands Introduction
DSharpPlus.Commands
is a new universal command framework designed with a "write-once, use-anywhere" mindset. Previously when developing bots with DSharpPlus, you'd have to choose between text commands through DSharpPlus.CommandsNext
, or slash commands through DSharpPlus.SlashCommands
. With DSharpPlus.Commands
, you can have both and more. This framework is designed to be easy to use, and easy to extend.
Hello World
First, we're going to setup our code.
public async Task Main(string[] args)
{
string discordToken = Environment.GetEnvironmentVariable("DISCORD_TOKEN");
if (string.IsNullOrWhiteSpace(discordToken))
{
Console.WriteLine("Error: No discord token found. Please provide a token via the DISCORD_TOKEN environment variable.");
Environment.Exit(1);
}
DiscordClientBuilder builder = DiscordClientBuilder.CreateDefault(discordToken, TextCommandProcessor.RequiredIntents | SlashCommandProcessor.RequiredIntents);
// Setup the commands extension
builder.UseCommands((IServiceProvider serviceProvider, CommandsExtension extension) =>
{
extension.AddCommands([typeof(MyCommand), typeof(MyOtherCommand)]);
TextCommandProcessor textCommandProcessor = new(new()
{
// The default behavior is that the bot reacts to direct
// mentions and to the "!" prefix. If you want to change
// it, you first set if the bot should react to mentions
// and then you can provide as many prefixes as you want.
PrefixResolver = new DefaultPrefixResolver(true, "?", "&").ResolvePrefixAsync,
});
// Add text commands with a custom prefix (?ping)
extension.AddProcessor(textCommandProcessor);
}, new CommandsConfiguration()
{
// The default value is true, however it's shown here for clarity
RegisterDefaultCommandProcessors = true,
DebugGuildId = Environment.GetEnvironmentVariable("DEBUG_GUILD_ID") ?? 0,
});
DiscordClient client = builder.Build();
// We can specify a status for our bot. Let's set it to "playing" and set the activity to "with fire".
DiscordActivity status = new("with fire", DiscordActivityType.Playing);
// Now we connect and log in.
await client.ConnectAsync(status, DiscordUserStatus.Online);
// And now we wait infinitely so that our bot actually stays connected.
await Task.Delay(-1);
}
Let's break this down a bit:
- We use each processor's required intents to ensure that the extension receives the necessary gateway events and data to function properly.
- We register commands explicitly through the collection-based overload. Note that this should be the preferred overload at this time, due to issues in other overloads.
- We register the
TextCommandProcessor
processor with a custom prefix resolver.
What in the world is a command processor? In order to execute a command, we need to be able to parse the input. Text commands are regular old Discord messages, while slash commands are a special type of event that Discord sends to your bot. The TextCommandProcessor
and SlashCommandProcessor
are responsible for parsing these inputs and determining which command to execute. By default, all processors are registered with the command framework. You can add/create your own processors if you need to.
Creating A Command
Now that we have the command framework registered and configured, we can create our first command:
public class PingCommand
{
[Command("ping")]
public static async ValueTask ExecuteAsync(CommandContext context) =>
await context.RespondAsync($"Pong! Latency is {context.Client.Ping}ms.");
}
There's multiple things to note here:
- There is no longer a
BaseCommandModule
class to inherit from. This is because the command framework is now attribute-based. - Your commands may now be
static
. - Your command return type may now be
ValueTask
orTask
, instead of only limiting toTask
. - By default, any response made via
CommandContext
will not mention any user or role - those mentions must be specified manually by using aDiscordMessageBuilder
.
Now start your Discord client, and type !ping
in a text channel. Your bot should respond with "Pong! Latency is {Number}ms."
Creating a Group Command
Creating a group command isn't much different:
[Command("math")]
public class MathCommands
{
[Command("add")]
public static async ValueTask AddAsync(CommandContext context, int a, int b) =>
await context.RespondAsync($"{a} + {b} = {a + b}");
[Command("subtract")]
public static async ValueTask SubtractAsync(CommandContext context, int a, int b) =>
await context.RespondAsync($"{a} - {b} = {a - b}");
}
You can invoke these commands by typing !math add 5 3
or !math subtract 5 3
in a text channel.