diff --git a/docs/guides/commands.md b/docs/guides/commands.md index 181b93bd4..04d027f69 100644 --- a/docs/guides/commands.md +++ b/docs/guides/commands.md @@ -10,6 +10,23 @@ Included below is a very bare-bones Command Handler. You can extend your Command [!code-csharp[Barebones Command Handler](samples/command_handler.cs)] +## Commands + +In 1.0, Commands are no longer implemented at runtime with a builder pattern. +While a builder pattern may be provided later, commands are created primarily with +attributes. + +### Basic Structure + +All commands belong to a Module. (See the below section for creating modules.) + +All commands in a module must be defined as an `Task`, with at least one argument, +being the @Discord.IMessage representing the context of the command. + +To add parameters to your command, add additional arguments to the `Task` of the command. +You are _not_ required to accept all arguments as `String`, they will be automatically parsed +into the type you specify for the arument. See the Example Module for an example of command parameters. + ## Modules Modules serve as a host for commands you create. @@ -38,6 +55,12 @@ When automatically loading modules, you are limited in your constructor. Using a Alternatively, you can use an @Discord.Commands.IDependencyMap, as shown below. +### Command Groups + +Command groups function similarly to Modules, but they must be contained inside a module. Simply create a **public** class inside a module, and flag it with the @Discord.Commands.GroupAttribute + +[!code-csharp[Groups Sample](samples/groups.cs)] + ## Dependency Injection The Commands Service includes a very basic implementation of Dependency Injection that allows you to have completely custom constructors, within certain limitations. @@ -61,7 +84,56 @@ In the constructor of your module, any parameters will be filled in by the @Disc [!code-csharp[DependencyMap in Modules](samples/dependency_module.cs)] -## Type Readers +# 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. + +>[!NOTE] +>Preconditions can be applied to Modules, Groups, or Commands. + +## Bundled Preconditions + +@Discord.Commands ships with two built-in preconditions, @Discord.Commands.RequireContextAttribute +and @Discord.Commands.RequirePermissionAttribute. + +### RequireContext + +@Discord.Commands.RequireContextAttribute is a precondition that requires your command to be +executed in the specified context. + +You may require three different types of context: +* Guild +* DM +* Group + +Since these are `Flags`, you may OR them together. + +[!code-csharp[RequireContext](samples/require_context.cs)] + +### RequirePermission + +@Discord.Commands.RequirePermissionAttribute is a precondition that allows you to quickly +specfiy that a user must poesess a permission to execute a command. + +You may require either a @Discord.GuildPermission or @Discord.ChannelPermission + +[!code-csharp[RequireContext](samples/require_permission.cs)] + +## Custom Preconditions + +To write your own preconditions, create a new class that inherits from @Discord.Commands.PreconditionAttribute + +In order for your precondition to function, you will need to override `CheckPermissions`, +which is a `Task`. +Your IDE should provide an option to fill this in for you. + +Return `PreconditionResult.FromSuccess()` if the context met the required parameters, otherwise +return `PreconditionResult.FromError()`, optionally including an error message. + +[!code-csharp[Custom Precondition](samples/require_owner.cs)] + +# Type Readers Type Readers allow you to parse different types of arguments in your commands. @@ -86,7 +158,7 @@ To create a TypeReader, create a new class that imports @Discord and @Discord.Co Next, satisfy the `TypeReader` class by overriding `Task Read(IMessage context, string input)`. >[!NOTE] ->In many cases, Visual Stuido can fill this in for you, using the "Implement Abstract Class" IntelliSense hint. +>In many cases, Visual Studio can fill this in for you, using the "Implement Abstract Class" IntelliSense hint. Inside this task, add whatever logic you need to parse the input string. diff --git a/docs/guides/samples/groups.cs b/docs/guides/samples/groups.cs new file mode 100644 index 000000000..4bcedf82b --- /dev/null +++ b/docs/guides/samples/groups.cs @@ -0,0 +1,15 @@ +[Module("admin")] +public class AdminModule +{ + [Group("mod")] + public class ModerationGroup + { + // ~admin mod ban foxbot#0282 + [Command("ban")] + public async Task Ban(IMessage msg, IGuildUser user) { } + } + + // ~admin clean 100 + [Command("clean")] + public async Task Clean(IMessage msg, int count = 100) { } +} \ No newline at end of file diff --git a/docs/guides/samples/module.cs b/docs/guides/samples/module.cs index 81dd963be..1330c201c 100644 --- a/docs/guides/samples/module.cs +++ b/docs/guides/samples/module.cs @@ -31,7 +31,9 @@ public class Sample // ~sample userinfo Khionu#8708 --> Khionu#8708 // ~sample userinfo Khionu --> Khionu#8708 // ~sample userinfo 96642168176807936 --> Khionu#8708 + // ~sample whois 96642168176807936 --> Khionu#8708 [Command("userinfo"), Description("Returns info about the current user, or the user parameter, if one passed.")] + [Alias("user", "whois")] public async Task UserInfo(IMessage msg, [Description("The (optional) user to get info for")] IUser user = null) { diff --git a/docs/guides/samples/require_context.cs b/docs/guides/samples/require_context.cs new file mode 100644 index 000000000..c9d5e5672 --- /dev/null +++ b/docs/guides/samples/require_context.cs @@ -0,0 +1,11 @@ +[Module] +public class InfoModule +{ + // Constrain this command to Guilds + [RequireContext(ContextType.Guild)] + public async Task Whois(IMessage msg, IGuildUser user) { } + + // Constrain this command to either Guilds or DMs + [RequireContext(ContextType.Guild | ContextType.DM)] + public async Task Info(IMessage msg) { } +} \ No newline at end of file diff --git a/docs/guides/samples/require_owner.cs b/docs/guides/samples/require_owner.cs new file mode 100644 index 000000000..90f07819f --- /dev/null +++ b/docs/guides/samples/require_owner.cs @@ -0,0 +1,16 @@ +// Defining the Precondition + +// Specify that this precondition can target a Class (Module/Group) or Method (Command) +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] +// Inherit from PreconditionAttribute +public class RequireOwnerAttribute : PreconditionAttribute +{ + public readonly ulong OwnerId = 66078337084162048; + + // Override the CheckPermissions method + public override Task CheckPermissions(IMessage context, Command executingCommand, object moduleInstance) + { + // If the author of the message is '66078337084162048', return success; otherwise fail. + return Task.FromResult(context.Author.Id == OwnerId ? PreconditionResult.FromSuccess() : PreconditionResult.FromError("You must be the owner of the bot.")); + } +} \ No newline at end of file diff --git a/docs/guides/samples/require_permission.cs b/docs/guides/samples/require_permission.cs new file mode 100644 index 000000000..77666d470 --- /dev/null +++ b/docs/guides/samples/require_permission.cs @@ -0,0 +1,7 @@ +[Module] +public class AdminModule +{ + [Command("ban")] + [RequirePermission(GuildPermission.BanMembers)] + public async Task Ban(IMessage msg) { } +} \ No newline at end of file