@@ -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 | |||
href: terminology.md | |||
- name: Logging | |||
href: logging.md | |||
href: logging.md | |||
- name: Commands | |||
href: commands.md |