| @@ -0,0 +1,62 @@ | |||||
| # The Command Service | |||||
| [Discord.Commands](xref:Discord.Commands) provides an Attribute-based Command Parser. | |||||
| ### Setup | |||||
| To use Commands, you must create a [Commands Service](xref:Discord.Commands.CommandService) and a Command Handler. | |||||
| Included below is a very bare-bones Command Handler. You can extend your Command Handler as much as you like, however the below is the bare minimum. | |||||
| [!code-csharp[Barebones Command Handler](samples/command_handler.cs)] | |||||
| ## Modules | |||||
| Modules serve as a host for commands you create. | |||||
| To create a module, create a class that you will place commands in. Flag this class with the `[Module]` attribute. You may optionally pass in a string to the `Module` attribute to set a prefix for all of the commands inside the module. | |||||
| ### Example: | |||||
| [!code-csharp[Modules](samples/module.cs)] | |||||
| ### Loading Modules Automatically | |||||
| The Command Service can automatically discover all classes in an Assembly that are flagged with the `Module` attribute, and load them. | |||||
| To have a module opt-out of auto-loading, pass `autoload: false` in the Module attribute. | |||||
| Invoke [CommandService.LoadAssembly](Discord.Commands.CommandService#Discord_Commands_CommandService_LoadAssembly) to discover modules and install them. | |||||
| ### Loading Modules Manually | |||||
| To manually load a module, invoke [CommandService.Load](Discord.Commands.CommandService#Discord_Commands_CommandService_Load), and pass in an instance of your module. | |||||
| ### Module Constructors | |||||
| When automatically loading modules, you are limited in your constructor. Using a constructor that accepts _no arguments_, or a constructor that accepts a @Discord.Commands.CommandService will always work. | |||||
| Alternatively, you can use an @Discord.Commands.IDependencyMap, as shown below. | |||||
| ## Dependency Injection | |||||
| The Commands Service includes a very basic implementation of Dependency Injection that allows you to have completely custom constructors, within certain limitations. | |||||
| ## Setup | |||||
| First, you need to create an @Discord.Commands.IDependencyMap . The library includes @Discord.Commands.DependencyMap to help with this, however you may create your own IDependencyMap if you wish. | |||||
| Next, add the dependencies your modules will use to the map. | |||||
| Finally, pass the map into the `LoadAssembly` method. Your modules will automatically be loaded with this dependency map. | |||||
| [!code-csharp[DependencyMap Setup](samples/dependency_map_setup.cs)] | |||||
| ## Usage in Modules | |||||
| In the constructor of your module, any parameters will be filled in by the @Discord.Commands.IDependencyMap you pass into `LoadAssembly`. | |||||
| >[!NOTE] | |||||
| >If you accept `CommandService` or `IDependencyMap` as a parameter in your constructor, these parameters will be filled by the CommandService the module was loaded from, and the DependencyMap passed into it, respectively. | |||||
| [!code-csharp[DependencyMap in Modules](samples/dependency_module.cs)] | |||||
| @@ -0,0 +1,52 @@ | |||||
| using System.Threading.Tasks; | |||||
| using System.Reflection; | |||||
| using Discord; | |||||
| using Discord.Commands; | |||||
| public class Program | |||||
| { | |||||
| private CommandService commands; | |||||
| private DiscordSocketClient client; | |||||
| static void Main(string[] args) => new Program().Start().GetAwaiter().GetResult(); | |||||
| public async Task Start() | |||||
| { | |||||
| client = new DiscordSocketClient(); | |||||
| commands = new CommandService(); | |||||
| string token = "bot token here"; | |||||
| await InstallCommands(); | |||||
| await client.LoginAsync(TokenType.Bot, token); | |||||
| await client.ConnectAsync(); | |||||
| await Task.Delay(-1); | |||||
| } | |||||
| public async Task InstallCommands() | |||||
| { | |||||
| // Hook the MessageReceived Event into our Command Handler | |||||
| client.MessageReceived += HandleCommand; | |||||
| // Discover all of the commands in this assembly and load them. | |||||
| await commands.LoadAssembly(Assembly.GetEntryAssembly()); | |||||
| } | |||||
| public async Task HandleCommand(IMessage msg) | |||||
| { | |||||
| // Internal integer, marks where the command begins | |||||
| int argPos = 0; | |||||
| // Get the current user (used for Mention parsing) | |||||
| var currentUser = await client.GetCurrentUserAsync(); | |||||
| // Determine if the message is a command, based on if it starts with '!' or a mention prefix | |||||
| if (msg.HasCharPrefix('!', ref argPos) || msg.HasMentionPrefix(currentUser, ref argPos)) | |||||
| { | |||||
| // Execute the command. (result does not indicate a return value, | |||||
| // rather an object stating if the command executed succesfully) | |||||
| var result = await _commands.Execute(msg, argPos); | |||||
| if (!result.IsSuccess) | |||||
| await msg.Channel.SendMessageAsync(result.ErrorReason); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,16 @@ | |||||
| using Discord; | |||||
| using Discord.Commands; | |||||
| public class Commands | |||||
| { | |||||
| public async Task Install(DiscordSocketClient client) | |||||
| { | |||||
| var commands = new CommandService(); | |||||
| var map = new DependencyMap(); | |||||
| map.Add<IDiscordClient>(client); | |||||
| var self = await client.GetCurrentUserAsync(); | |||||
| map.Add<ISelfUser>(self); | |||||
| await commands.LoadAssembly(Assembly.GetCurrentAssembly(), map); | |||||
| } | |||||
| // ... | |||||
| } | |||||
| @@ -0,0 +1,28 @@ | |||||
| using Discord; | |||||
| using Discord.Commands; | |||||
| [Module] | |||||
| public class ModuleA | |||||
| { | |||||
| private DiscordSocketClient client; | |||||
| private ISelfUser self; | |||||
| public ModuleA(IDiscordClient c, ISelfUser s) | |||||
| { | |||||
| if (!(c is DiscordSocketClient)) throw new InvalidOperationException("This module requires a DiscordSocketClient"); | |||||
| client = c as DiscordSocketClient; | |||||
| self = s; | |||||
| } | |||||
| } | |||||
| public class ModuleB | |||||
| { | |||||
| private IDiscordClient client; | |||||
| private CommandService commands; | |||||
| public ModuleB(CommandService c, IDependencyMap m) | |||||
| { | |||||
| commands = c; | |||||
| client = m.Get<IDiscordClient>(); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,40 @@ | |||||
| using Discord.Commands; | |||||
| // Create a module with no prefix | |||||
| [Module] | |||||
| public class Info | |||||
| { | |||||
| // ~say hello -> hello | |||||
| [Command("say"), Description("Echos a message.")] | |||||
| public async Task Say(IMessage msg, | |||||
| [Unparsed, Description("The text to echo")] string echo) | |||||
| { | |||||
| await msg.Channel.SendMessageAsync(echo); | |||||
| } | |||||
| } | |||||
| // Create a module with the 'sample' prefix | |||||
| [Module("sample")] | |||||
| public class Sample | |||||
| { | |||||
| // ~sample square 20 -> | |||||
| [Command("square"), Description("Squares a number.")] | |||||
| public async Task Square(IMessage msg, | |||||
| [Description("The number to square.")] int num) | |||||
| { | |||||
| await msg.Channel.SendMessageAsync($"{num}^2 = {Math.Pow(num, 2)}"); | |||||
| } | |||||
| // ~sample userinfo --> foxbot#0282 | |||||
| // ~sample userinfo @Khionu --> Khionu#8708 | |||||
| // ~sample userinfo Khionu#8708 --> Khionu#8708 | |||||
| // ~sample userinfo Khionu --> Khionu#8708 | |||||
| // ~sample userinfo 96642168176807936 --> Khionu#8708 | |||||
| [Command("userinfo"), Description("Returns info about the current user, or the user parameter, if one passed.")] | |||||
| public async Task UserInfo(IMessage msg, | |||||
| [Description("The (optional) user to get info for")] IUser user = null) | |||||
| { | |||||
| var userInfo = user ?? await Program.Client.GetCurrentUserAsync(); | |||||
| await msg.Channel.SendMessageAsync($"{userInfo.Username}#{userInfo.Discriminator}"); | |||||
| } | |||||
| } | |||||
| @@ -4,4 +4,6 @@ | |||||
| - name: Terminology | - name: Terminology | ||||
| href: terminology.md | href: terminology.md | ||||
| - name: Logging | - name: Logging | ||||
| href: logging.md | |||||
| href: logging.md | |||||
| - name: Commands | |||||
| href: commands.md | |||||