Preconditions
Preconditions serve as a permissions system for your Commands. Keep in mind, however, that they are not limited to just permissions and can be as complex as you want them to be.
There are two types of Preconditions you can use:
- PreconditionAttribute can be applied to Modules, Groups, or Commands.
- ParameterPreconditionAttribute can be applied to Parameters.
You may visit their respective API documentation to find out more.
Bundled Preconditions
Discord.Commands ships with several bundled Preconditions for you to use.
- RequireContextAttribute
- RequireOwnerAttribute
- RequireBotPermissionAttribute
- RequireUserPermissionAttribute
- RequireNsfwAttribute
- DoHierarchyCheckAttribute
Using Preconditions
To use a precondition, simply apply any valid precondition candidate to a command method signature as an attribute.
Example - Using a Precondition
[RequireOwner]
[Command("echo")]
public Task EchoAsync(string input) => ReplyAsync(input);
ORing Preconditions
When writing commands, you may want to allow some of them to be executed when only some of the precondition checks are passed.
This is where the Group property of a precondition attribute comes in handy. By assigning two or more preconditions to a group, the command system will allow the command to be executed when one of the precondition passes.
Example - ORing Preconditions
// The following example only requires the user to either have the
// Administrator permission in this guild or own the bot application.
[RequireUserPermission(GuildPermission.Administrator, Group = "Permission")]
[RequireOwner(Group = "Permission")]
public class AdminModule : ModuleBase<SocketCommandContext>
{
[Command("ban")]
public Task BanAsync(IUser user) => Context.Guild.AddBanAsync(user);
}
Custom Preconditions
To write your own Precondition, create a new class that inherits from either PreconditionAttribute or ParameterPreconditionAttribute depending on your use.
In order for your Precondition to function, you will need to override the CheckPermissionsAsync method.
If the context meets the required parameters, return PreconditionResult.FromSuccess, otherwise return PreconditionResult.FromError and include an error message if necessary.
Note
Visual Studio can help you implement missing members from the abstract class by using the "Implement Abstract Class" IntelliSense hint.
Example - Creating a Custom Precondition
using System;
using System.Linq;
using System.Threading.Tasks;
using Discord.Commands;
using Discord.WebSocket;
// Inherit from PreconditionAttribute
public class RequireRoleAttribute : PreconditionAttribute
{
// Create a field to store the specified name
private readonly string _name;
// Create a constructor so the name can be specified
public RequireRoleAttribute(string name) => _name = name;
// Override the CheckPermissions method
public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
// Check if this user is a Guild User, which is the only context where roles exist
if (context.User is SocketGuildUser gUser)
{
// If this command was executed by a user with the appropriate role, return a success
if (gUser.Roles.Any(r => r.Name == _name))
// Since no async work is done, the result has to be wrapped with `Task.FromResult` to avoid compiler errors
return Task.FromResult(PreconditionResult.FromSuccess());
// Since it wasn't, fail
else
return Task.FromResult(PreconditionResult.FromError($"You must have a role named {_name} to run this command."));
}
else
return Task.FromResult(PreconditionResult.FromError("You must be in a guild to run this command."));
}
}