| @@ -3,4 +3,10 @@ | |||||
| This is where you will find documentation for all members and objects in Discord.Net | This is where you will find documentation for all members and objects in Discord.Net | ||||
| **TODO:** Think of something useful to put on this page. | |||||
| __Commonly Used Entities__ | |||||
| @Discord.WebSocket | |||||
| @Discord.WebSocket.DiscordSocketClient | |||||
| @Discord.WebSocket.SocketGuildChannel | |||||
| @Discord.WebSocket.SocketGuildUser | |||||
| @Discord.WebSocket.SocketMessage | |||||
| @Discord.WebSocket.SocketRole | |||||
| @@ -1,37 +1,60 @@ | |||||
| # The Command Service | # The Command Service | ||||
| [Discord.Commands](xref:Discord.Commands) provides an Attribute-based Command Parser. | |||||
| [Discord.Commands](xref:Discord.Commands) provides an Attribute-based | |||||
| Command Parser. | |||||
| ### Setup | ### Setup | ||||
| To use Commands, you must create a [Commands Service](xref:Discord.Commands.CommandService) and a Command Handler. | |||||
| 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. | |||||
| 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)] | [!code-csharp[Barebones Command Handler](samples/command_handler.cs)] | ||||
| ## Commands | ## 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. | |||||
| 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 | ### Basic Structure | ||||
| All commands belong to a Module. (See the below section for creating modules.) | |||||
| 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.IUserMessage representing the context of the command. | |||||
| All commands in a module must be defined as a `Task`. | |||||
| 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. | |||||
| To add parameters to your command, you simply need to add parameters | |||||
| to the Task that represents 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 | ||||
| Modules serve as a host for commands you create. | |||||
| Modules are an organizational pattern that allow you to write your | |||||
| commands in different classes, and have them automatically loaded. | |||||
| 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. | |||||
| Discord.Net's implementation of Modules is influenced heavily from | |||||
| ASP.Net Core's Controller pattern. This means that the lifetime of a | |||||
| module instance is only as long as the command being ran in it. | |||||
| **Avoid using long-running code** in your modules whereever possible. | |||||
| You should **not** be implementing very much logic into your modules; | |||||
| outsource to a service for that. | |||||
| If you are unfamiliar with Inversion of Control, it is recommended to | |||||
| read the MSDN article on [IoC] and [Dependency Injection]. | |||||
| To create a module, create a class that inherits from | |||||
| @Discord.Commands.ModuleBase. | |||||
| [IoC]: https://msdn.microsoft.com/en-us/library/ff921087.aspx | |||||
| [Dependency Injection]: https://msdn.microsoft.com/en-us/library/ff921152.aspx | |||||
| ### Example Module | ### Example Module | ||||
| @@ -39,68 +62,95 @@ To create a module, create a class that you will place commands in. Flag this cl | |||||
| #### Loading Modules Automatically | #### Loading Modules Automatically | ||||
| The Command Service can automatically discover all classes in an Assembly that are flagged with the `Module` attribute, and load them. | |||||
| The Command Service can automatically discover all classes in an | |||||
| Assembly that inherit @Discord.Commands.ModuleBase, and load them. | |||||
| To have a module opt-out of auto-loading, pass `autoload: false` in the Module attribute. | |||||
| To have a module opt-out of auto-loading, pass `autoload: false` in | |||||
| the Module attribute. | |||||
| Invoke [CommandService.LoadAssembly](xref:Discord.Commands.CommandService#Discord_Commands_CommandService_LoadAssembly) to discover modules and install them. | |||||
| Invoke [CommandService.AddModules] to discover modules and install them. | |||||
| [CommandService.AddModules]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddModules | |||||
| #### Loading Modules Manually | #### Loading Modules Manually | ||||
| To manually load a module, invoke [CommandService.Load](xref:Discord.Commands.CommandService#Discord_Commands_CommandService_Load), and pass in an instance of your module. | |||||
| To manually load a module, invoke [CommandService.AddModule], | |||||
| by passing in the generic type of your module, and optionally | |||||
| a dependency map. | |||||
| ### Module Constructors | |||||
| [CommandService.AddModule]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddModule__1_Discord_Commands_IDependencyMap_ | |||||
| 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. | |||||
| ### Module Constructors | |||||
| Alternatively, you can use an @Discord.Commands.IDependencyMap, as shown below. | |||||
| Modules are constructed using Dependency Injection. Any parameters | |||||
| that are placed in the constructor must be injected into an | |||||
| @Discord.Commands.IDependencyMap. Alternatively, you may accept an | |||||
| IDependencyMap as an argument and extract services yourself. | |||||
| ### Command Groups | ### 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 | |||||
| Command Groups allow you to create a module where commands are prefixed. | |||||
| To create a group, create a new module and flag it with the | |||||
| @Discord.Commands.GroupAttribute. | |||||
| >![NOTE] | |||||
| >Groups do not _need_ to be modules. Only classes with commands should | |||||
| >inherit from ModuleBase. If you plan on using a group for strictly | |||||
| >organizational purposes, there is no reason to make it a module. | |||||
| [!code-csharp[Groups Sample](samples/groups.cs)] | [!code-csharp[Groups Sample](samples/groups.cs)] | ||||
| ## Dependency Injection | ## Dependency Injection | ||||
| The Commands Service includes a very basic implementation of Dependency Injection that allows you to have completely custom constructors, within certain limitations. | |||||
| The commands service is bundled with a very barebones Dependency | |||||
| Injection service for your convienence. It is recommended that | |||||
| you use DI when writing your modules. | |||||
| ### Setup | ### 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. | |||||
| 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. | 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. | |||||
| 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)] | [!code-csharp[DependencyMap Setup](samples/dependency_map_setup.cs)] | ||||
| ### Usage in Modules | ### Usage in Modules | ||||
| In the constructor of your module, any parameters will be filled in by the @Discord.Commands.IDependencyMap you pass into `LoadAssembly`. | |||||
| In the constructor of your module, any parameters will be filled in by | |||||
| the @Discord.Commands.IDependencyMap you pass into `LoadAssembly`. | |||||
| >[!NOTE] | >[!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. | |||||
| >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)] | [!code-csharp[DependencyMap in Modules](samples/dependency_module.cs)] | ||||
| # Preconditions | # 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. | |||||
| 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] | >[!NOTE] | ||||
| >Preconditions can be applied to Modules, Groups, or Commands. | >Preconditions can be applied to Modules, Groups, or Commands. | ||||
| ## Bundled Preconditions | ## Bundled Preconditions | ||||
| @Discord.Commands ships with two built-in preconditions, @Discord.Commands.RequireContextAttribute | |||||
| and @Discord.Commands.RequirePermissionAttribute. | |||||
| @Discord.Commands ships with two built-in preconditions, | |||||
| @Discord.Commands.RequireContextAttribute and | |||||
| @Discord.Commands.RequirePermissionAttribute. | |||||
| ### RequireContext | ### RequireContext | ||||
| @Discord.Commands.RequireContextAttribute is a precondition that requires your command to be | |||||
| executed in the specified context. | |||||
| @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: | You may require three different types of context: | ||||
| * Guild | * Guild | ||||
| @@ -113,29 +163,34 @@ Since these are `Flags`, you may OR them together. | |||||
| ### RequirePermission | ### RequirePermission | ||||
| @Discord.Commands.RequirePermissionAttribute is a precondition that allows you to quickly | |||||
| specfiy that a user must poesess a permission to execute a command. | |||||
| @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 | |||||
| You may require either a @Discord.GuildPermission or | |||||
| @Discord.ChannelPermission | |||||
| [!code-csharp[RequireContext](samples/require_permission.cs)] | [!code-csharp[RequireContext](samples/require_permission.cs)] | ||||
| ## Custom Preconditions | ## Custom Preconditions | ||||
| To write your own preconditions, create a new class that inherits from @Discord.Commands.PreconditionAttribute | |||||
| 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<PreconditionResult>`. | |||||
| In order for your precondition to function, you will need to override | |||||
| `CheckPermissions`, which is a `Task<PreconditionResult>`. | |||||
| Your IDE should provide an option to fill this in for you. | 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. | |||||
| 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)] | [!code-csharp[Custom Precondition](samples/require_owner.cs)] | ||||
| # Type Readers | # Type Readers | ||||
| Type Readers allow you to parse different types of arguments in your commands. | |||||
| Type Readers allow you to parse different types of arguments in | |||||
| your commands. | |||||
| By default, the following Types are supported arguments: | By default, the following Types are supported arguments: | ||||
| @@ -153,16 +208,20 @@ By default, the following Types are supported arguments: | |||||
| ### Creating a Type Readers | ### Creating a Type Readers | ||||
| To create a TypeReader, create a new class that imports @Discord and @Discord.Commands . Ensure your class inherits from @Discord.Commands.TypeReader | |||||
| To create a TypeReader, create a new class that imports @Discord and | |||||
| @Discord.Commands. Ensure your class inherits from @Discord.Commands.TypeReader | |||||
| Next, satisfy the `TypeReader` class by overriding `Task<TypeReaderResult> Read(IUserMessage context, string input)`. | |||||
| Next, satisfy the `TypeReader` class by overriding `Task<TypeReaderResult> Read(CommandContext context, string input)`. | |||||
| >[!NOTE] | >[!NOTE] | ||||
| >In many cases, Visual Studio 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. | Inside this task, add whatever logic you need to parse the input string. | ||||
| Finally, return a `TypeReaderResult`. If you were able to successfully parse the input, return `TypeReaderResult.FromSuccess(parsedInput)`. Otherwise, return `TypeReaderResult.FromError`. | |||||
| Finally, return a `TypeReaderResult`. If you were able to successfully | |||||
| parse the input, return `TypeReaderResult.FromSuccess(parsedInput)`. | |||||
| Otherwise, return `TypeReaderResult.FromError`. | |||||
| #### Sample | #### Sample | ||||
| @@ -170,4 +229,5 @@ Finally, return a `TypeReaderResult`. If you were able to successfully parse the | |||||
| ### Installing TypeReaders | ### Installing TypeReaders | ||||
| TypeReaders are not automatically discovered by the Command Service, and must be explicitly added. To install a TypeReader, invoke [CommandService.AddTypeReader](xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddTypeReader__1_Discord_Commands_TypeReader_). | |||||
| TypeReaders are not automatically discovered by the Command Service, | |||||
| and must be explicitly added. To install a TypeReader, invoke [CommandService.AddTypeReader](xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddTypeReader__1_Discord_Commands_TypeReader_). | |||||
| @@ -16,6 +16,8 @@ Bot accounts must be added to a server, you must use the [OAuth 2 Flow](https:// | |||||
| You can install Discord.Net 1.0 from our [MyGet Feed](https://www.myget.org/feed/Packages/discord-net). | You can install Discord.Net 1.0 from our [MyGet Feed](https://www.myget.org/feed/Packages/discord-net). | ||||
| **For most users writing bots, install only `Discord.Net.WebSocket`.** | |||||
| You may add the MyGet feed to Visual Studio directly from `https://www.myget.org/F/discord-net/api/v3/index.json`. | You may add the MyGet feed to Visual Studio directly from `https://www.myget.org/F/discord-net/api/v3/index.json`. | ||||
| You can also pull the latest source from [GitHub](https://github.com/RogueException/Discord.Net). | You can also pull the latest source from [GitHub](https://github.com/RogueException/Discord.Net). | ||||
| @@ -17,8 +17,4 @@ title: Samples | |||||
| #### Sending a message to a channel | #### Sending a message to a channel | ||||
| [!code-csharp[Message to Channel](samples/faq/send_message.cs)] | |||||
| #### Retrieving an IGuild from an IUserMessage | |||||
| [!code-csharp[Message to Guild](samples/faq/guild_from_message.cs)] | |||||
| [!code-csharp[Message to Channel](samples/faq/send_message.cs)] | |||||
| @@ -33,21 +33,21 @@ public class Program | |||||
| // Discover all of the commands in this assembly and load them. | // Discover all of the commands in this assembly and load them. | ||||
| await commands.LoadAssembly(Assembly.GetEntryAssembly()); | await commands.LoadAssembly(Assembly.GetEntryAssembly()); | ||||
| } | } | ||||
| public async Task HandleCommand(IMessage paramMessage) | |||||
| public async Task HandleCommand(SocketMessage messageParam) | |||||
| { | { | ||||
| // Cast paramMessage to an IUserMessage, return if the message was a System message. | |||||
| var msg = paramMessage as IUserMessage; | |||||
| if (msg == null) return; | |||||
| // Internal integer, marks where the command begins | |||||
| // Don't process the command if it was a System Message | |||||
| var message = messageParam as SocketUserMessage; | |||||
| if (message == null) return; | |||||
| // Create a number to track where the prefix ends and the command begins | |||||
| int argPos = 0; | 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 | // 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)) | |||||
| if (message.HasCharPrefix('!', ref argPos) || message.HasMentionPrefix(client.CurrentUser, ref argPos)) | |||||
| { | { | ||||
| // Execute the command. (result does not indicate a return value, | |||||
| // Create a Command Context | |||||
| var context = new CommandContext(client, message); | |||||
| // Execute the command. (result does not indicate a return value, | |||||
| // rather an object stating if the command executed succesfully) | // rather an object stating if the command executed succesfully) | ||||
| var result = await _commands.Execute(msg, argPos); | |||||
| var result = await _commands.Execute(context, argPos); | |||||
| if (!result.IsSuccess) | if (!result.IsSuccess) | ||||
| await msg.Channel.SendMessageAsync(result.ErrorReason); | await msg.Channel.SendMessageAsync(result.ErrorReason); | ||||
| } | } | ||||
| @@ -1,6 +1,7 @@ | |||||
| using Discord; | using Discord; | ||||
| using Discord.Commands; | using Discord.Commands; | ||||
| using Discord.WebSocket; | using Discord.WebSocket; | ||||
| using foxboat.Services; | |||||
| public class Commands | public class Commands | ||||
| { | { | ||||
| @@ -8,10 +9,16 @@ public class Commands | |||||
| { | { | ||||
| var commands = new CommandService(); | var commands = new CommandService(); | ||||
| var map = new DependencyMap(); | var map = new DependencyMap(); | ||||
| map.Add<IDiscordClient>(client); | |||||
| var self = await client.GetCurrentUserAsync(); | |||||
| map.Add<ISelfUser>(self); | |||||
| map.Add(client); | |||||
| map.Add(commands); | |||||
| await commands.LoadAssembly(Assembly.GetCurrentAssembly(), map); | await commands.LoadAssembly(Assembly.GetCurrentAssembly(), map); | ||||
| } | } | ||||
| // In ConfigureServices, we will inject the Dependency Map with | |||||
| // all of the services our client will use. | |||||
| public Task ConfigureServices(IDependencyMap map) | |||||
| { | |||||
| map.Add(new NotificationService(map)); | |||||
| map.Add(new DatabaseService(map)); | |||||
| } | |||||
| // ... | // ... | ||||
| } | } | ||||
| @@ -1,5 +1,5 @@ | |||||
| public async Task ChangeAvatar() | public async Task ChangeAvatar() | ||||
| { | { | ||||
| var fileStream = new FileStream("./newAvatar.png", FileMode.Open); | var fileStream = new FileStream("./newAvatar.png", FileMode.Open); | ||||
| await (await _client.GetCurrentUserAsync()).ModifyAsync(x => x.Avatar = fileStream); | |||||
| await _client.CurrentUser.ModifyAsync(x => x.Avatar = fileStream); | |||||
| } | } | ||||
| @@ -1,4 +0,0 @@ | |||||
| public async Task MessageReceived(IMessage msg) | |||||
| { | |||||
| var guild = (msg.Channel as IGuildChannel)?.Guild; | |||||
| } | |||||
| @@ -1,6 +1,6 @@ | |||||
| public async Task SendMessageToChannel(ulong ChannelId) | public async Task SendMessageToChannel(ulong ChannelId) | ||||
| { | { | ||||
| var channel = await _client.GetChannelAsync(ChannelId) as IMessageChannel; | |||||
| var channel = _client.GetChannel(ChannelId) as ISocketMessageChannel; | |||||
| await channel?.SendMessageAsync("aaaaaaaaahhh!!!") | await channel?.SendMessageAsync("aaaaaaaaahhh!!!") | ||||
| /* ^ This question mark is used to indicate that 'channel' may sometimes be null, and in cases that it is null, we will do nothing here. */ | /* ^ This question mark is used to indicate that 'channel' may sometimes be null, and in cases that it is null, we will do nothing here. */ | ||||
| } | } | ||||
| @@ -1,15 +1,15 @@ | |||||
| [Module("admin")] | |||||
| public class AdminModule | |||||
| [Group("admin")] | |||||
| public class AdminModule : ModuleBase | |||||
| { | { | ||||
| [Group("mod")] | [Group("mod")] | ||||
| public class ModerationGroup | |||||
| public class ModerationGroup : ModuleBase | |||||
| { | { | ||||
| // ~admin mod ban foxbot#0282 | // ~admin mod ban foxbot#0282 | ||||
| [Command("ban")] | [Command("ban")] | ||||
| public async Task Ban(IUserMessage msg, IGuildUser user) { } | |||||
| public async Task Ban(IGuildUser user) { } | |||||
| } | } | ||||
| // ~admin clean 100 | // ~admin clean 100 | ||||
| [Command("clean")] | [Command("clean")] | ||||
| public async Task Clean(IUserMessage msg, int count = 100) { } | |||||
| public async Task Clean(int count = 100) { } | |||||
| } | } | ||||
| @@ -1,29 +1,29 @@ | |||||
| using Discord; | |||||
| using Discord.Commands; | using Discord.Commands; | ||||
| using Discord.WebSocket; | using Discord.WebSocket; | ||||
| // Create a module with no prefix | // Create a module with no prefix | ||||
| [Module] | |||||
| public class Info | |||||
| public class Info : ModuleBase | |||||
| { | { | ||||
| // ~say hello -> hello | // ~say hello -> hello | ||||
| [Command("say"), Summary("Echos a message.")] | [Command("say"), Summary("Echos a message.")] | ||||
| public async Task Say(IUserMessage msg, | |||||
| [Unparsed, Summary("The text to echo")] string echo) | |||||
| public async Task Say([Unparsed, Summary("The text to echo")] string echo) | |||||
| { | { | ||||
| await msg.Channel.SendMessageAsync(echo); | |||||
| // ReplyAsync is a method on ModuleBase | |||||
| await ReplyAsync(echo); | |||||
| } | } | ||||
| } | } | ||||
| // Create a module with the 'sample' prefix | // Create a module with the 'sample' prefix | ||||
| [Module("sample")] | |||||
| public class Sample | |||||
| [Group("sample")] | |||||
| public class Sample : ModuleBase | |||||
| { | { | ||||
| // ~sample square 20 -> | |||||
| // ~sample square 20 -> 400 | |||||
| [Command("square"), Summary("Squares a number.")] | [Command("square"), Summary("Squares a number.")] | ||||
| public async Task Square(IUserMessage msg, | |||||
| [Summary("The number to square.")] int num) | |||||
| public async Task Square([Summary("The number to square.")] int num) | |||||
| { | { | ||||
| await msg.Channel.SendMessageAsync($"{num}^2 = {Math.Pow(num, 2)}"); | |||||
| // We can also access the channel from the Command Context. | |||||
| await Context.Channel.SendMessageAsync($"{num}^2 = {Math.Pow(num, 2)}"); | |||||
| } | } | ||||
| // ~sample userinfo --> foxbot#0282 | // ~sample userinfo --> foxbot#0282 | ||||
| @@ -34,10 +34,9 @@ public class Sample | |||||
| // ~sample whois 96642168176807936 --> Khionu#8708 | // ~sample whois 96642168176807936 --> Khionu#8708 | ||||
| [Command("userinfo"), Summary("Returns info about the current user, or the user parameter, if one passed.")] | [Command("userinfo"), Summary("Returns info about the current user, or the user parameter, if one passed.")] | ||||
| [Alias("user", "whois")] | [Alias("user", "whois")] | ||||
| public async Task UserInfo(IUserMessage msg, | |||||
| [Summary("The (optional) user to get info for")] IUser user = null) | |||||
| public async Task UserInfo([Summary("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}"); | |||||
| var userInfo = user ?? Context.Client.CurrentUser; | |||||
| await ReplyAsync($"{userInfo.Username}#{userInfo.Discriminator}"); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,11 +1,10 @@ | |||||
| [Module] | |||||
| public class InfoModule | |||||
| public class InfoModule : ModuleBase | |||||
| { | { | ||||
| // Constrain this command to Guilds | // Constrain this command to Guilds | ||||
| [RequireContext(ContextType.Guild)] | [RequireContext(ContextType.Guild)] | ||||
| public async Task Whois(IUserMessage msg, IGuildUser user) { } | |||||
| public async Task Whois(IGuildUser user) { } | |||||
| // Constrain this command to either Guilds or DMs | // Constrain this command to either Guilds or DMs | ||||
| [RequireContext(ContextType.Guild | ContextType.DM)] | [RequireContext(ContextType.Guild | ContextType.DM)] | ||||
| public async Task Info(IUserMessage msg) { } | |||||
| public async Task Info() { } | |||||
| } | } | ||||
| @@ -1,16 +1,18 @@ | |||||
| // Defining the Precondition | // 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 | // Inherit from PreconditionAttribute | ||||
| public class RequireOwnerAttribute : PreconditionAttribute | public class RequireOwnerAttribute : PreconditionAttribute | ||||
| { | { | ||||
| public readonly ulong OwnerId = 66078337084162048; | |||||
| // Override the CheckPermissions method | // Override the CheckPermissions method | ||||
| public override Task<PreconditionResult> CheckPermissions(IUserMessage context, Command executingCommand, object moduleInstance) | |||||
| public override Task<PreconditionResult> CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map) | |||||
| { | { | ||||
| // 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.")); | |||||
| // Get the ID of the bot's owner | |||||
| var ownerId = (await map.Get<DiscordSocketClient>().GetApplicationInfoAsync()).Owner.Id; | |||||
| // If this command was executed by that user, return a success | |||||
| if (context.User.Id == ownerId) | |||||
| return PreconditionResult.FromSuccess(); | |||||
| // Since it wasn't, fail | |||||
| else | |||||
| return PreconditionResult.FromError("You must be the owner of the bot to run this command."); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,7 +1,6 @@ | |||||
| [Module] | |||||
| public class AdminModule | |||||
| public class AdminModule : ModuleBase | |||||
| { | { | ||||
| [Command("ban")] | [Command("ban")] | ||||
| [RequirePermission(GuildPermission.BanMembers)] | [RequirePermission(GuildPermission.BanMembers)] | ||||
| public async Task Ban(IUserMessage msg) { } | |||||
| public async Task Ban(IGuildUser target) { } | |||||
| } | } | ||||
| @@ -3,7 +3,7 @@ using Discord.Commands; | |||||
| public class BooleanTypeReader : TypeReader | public class BooleanTypeReader : TypeReader | ||||
| { | { | ||||
| public override Task<TypeReaderResult> Read(IUserMessage context, string input) | |||||
| public override Task<TypeReaderResult> Read(CommandContext context, string input) | |||||
| { | { | ||||
| bool result; | bool result; | ||||
| if (bool.TryParse(input, out result)) | if (bool.TryParse(input, out result)) | ||||
| @@ -9,17 +9,30 @@ title: Terminology | |||||
| Most terms for objects remain the same between 0.9 and 1.0. The major difference is that the ``Server`` is now called ``Guild``, to stay in line with Discord internally | Most terms for objects remain the same between 0.9 and 1.0. The major difference is that the ``Server`` is now called ``Guild``, to stay in line with Discord internally | ||||
| ## Introduction to Interfaces | |||||
| ## Implementation Specific Entities | |||||
| Discord.Net 1.0 is built strictly around Interfaces. There are no methods that return a concrete object, only an interface. | |||||
| Discord.Net 1.0 is split into a core library, and three different | |||||
| implementations - Discord.Net.Core, Discord.Net.Rest, Discord.Net.Rpc, | |||||
| and Discord.Net.WebSockets. | |||||
| Many of the interfaces in Discord.Net are linked through inheritance. For example, @Discord.IChannel represents any channel in Discord. @Discord.IGuildChannel inherits from IChannel, and represents all channels belonging to a Guild. As a result, @Discord.IChannel can sometimes be cast to @Discord.IGuildChannel, and you may find yourself doing this frequently in order to properly utilize the library. | |||||
| As a bot developer, you will only need to use Discord.Net.WebSockets, | |||||
| but you should be aware of the differences between them. | |||||
| ### The Inheritance Tree | |||||
| `Discord.Net.Core` provides a set of interfaces that model Discord's | |||||
| API. These interfaces are consistent throughout all implementations of | |||||
| Discord.Net, and if you are writing an implementation-agnostic library | |||||
| or addon, you can rely on the core interfaces to ensure that your | |||||
| addon will run on all platforms. | |||||
| You may want to familiarize yourself with the inheritance in Discord.Net. An inheritance tree is provided below. | |||||
| `Discord.Net.Rest` provides a set of concrete classes to be used | |||||
| **strictly** with the REST portion of Discord's API. Entities in | |||||
| this implementation are prefixed with `Rest`, e.g. `RestChannel`. | |||||
|  | |||||
|  | |||||
|  | |||||
|  | |||||
| `Discord.Net.Rpc` provides a set of concrete classes that are used with | |||||
| Discord's RPC API. Entities in this implementation are prefixed with | |||||
| `Rpc`, e.g. `RpcChannel`. | |||||
| `Discord.Net.WebSocket` provides a set of concrete classes that are used | |||||
| primarily with Discord's WebSocket API, or entities that are kept in | |||||
| cache. When developing bots, you will be using this implementation. All | |||||
| entities are prefixed with `Socket`, e.g. `SocketChannel`. | |||||
| @@ -5,11 +5,6 @@ changes in the design of the library.** | |||||
| >A medium to advanced understanding is recommended when working with this library. | >A medium to advanced understanding is recommended when working with this library. | ||||
| One of the biggest major changes from `0.9.x` is the exclusive use of interfaces. | |||||
| For the most part, your usability will be very similar to the 0.9 approach of concrete | |||||
| classes. You **will** be required to cast some entities; this is outlined in a later | |||||
| section. | |||||
| It is recommended to familiarize yourself with the entities in 1.0 before continuing. | It is recommended to familiarize yourself with the entities in 1.0 before continuing. | ||||
| Feel free to look through the library's source directly, look through IntelliSense, or | Feel free to look through the library's source directly, look through IntelliSense, or | ||||
| look through our hosted [API Documentation](xref:Discord). | look through our hosted [API Documentation](xref:Discord). | ||||
| @@ -19,8 +14,7 @@ look through our hosted [API Documentation](xref:Discord). | |||||
| Most API models function _similarly_ to 0.9, however their names have been changed. | Most API models function _similarly_ to 0.9, however their names have been changed. | ||||
| You should also keep in mind that we now separate different types of Channels and Users. | You should also keep in mind that we now separate different types of Channels and Users. | ||||
| Take a look at inheritance section of @Terminology for an example of how inheritance and interfaces | |||||
| work in 1.0 | |||||
| Before proceeding, please read over @Terminology to understand the naming behind some objects. | |||||
| Below is a table that compares most common 0.9 entities to their 1.0 counterparts. | Below is a table that compares most common 0.9 entities to their 1.0 counterparts. | ||||
| @@ -29,18 +23,16 @@ Below is a table that compares most common 0.9 entities to their 1.0 counterpart | |||||
| | 0.9 | 1.0 | Notice | | | 0.9 | 1.0 | Notice | | ||||
| | --- | --- | ------ | | | --- | --- | ------ | | ||||
| | Server | @Discord.IGuild | | |||||
| | Channel | @Discord.IGuildChannel | Applies only to channels that are members of a Guild | | |||||
| | Channel.IsPrivate | @Discord.IDMChannel | |||||
| | ChannelType.Text | @Discord.ITextChannel | This applies only to Text Channels in Guilds | |||||
| | ChannelType.Voice | @Discord.IVoiceChannel | This applies only to Voice Channels in Guilds | |||||
| | User | @Discord.IGuildUser | This applies only to users belonging to a Guild* | |||||
| | Profile | @Discord.ISelfUser | |||||
| | Message | @Discord.IUserMessage | |||||
| \* To retrieve an @Discord.IGuildUser, you must retrieve the user from an @Discord.IGuild. | |||||
| [IDiscordClient.GetUserAsync](xref:Discord.IDiscordClient#Discord_IDiscordClient_GetUserAsync_System_UInt64_) | |||||
| returns a @Discord.IUser, which only contains the information that Discord exposes for public users. | |||||
| | Server | @Discord.WebSocket.SocketGuild | | |||||
| | Channel | @Discord.WebSocket.SocketGuildChannel | Applies only to channels that are members of a Guild | | |||||
| | Channel.IsPrivate | @Discord.WebSocket.SocketDMChannel | |||||
| | ChannelType.Text | @Discord.WebSocket.SocketTextChannel | This applies only to Text Channels in Guilds | |||||
| | ChannelType.Voice | @Discord.WebSocket.SocketVoiceChannel | This applies only to Voice Channels in Guilds | |||||
| | User | @Discord.WebSocket.SocketGuildUser | This applies only to users belonging to a Guild* | |||||
| | Profile | @Discord.WebSocket.SocketGuildUser | |||||
| | Message | @Discord.WebSocket.SocketUserMessage | |||||
| \* To retrieve an @Discord.WebSocket.SocketGuildUser, you must retrieve the user from an @Discord.WebSocket.SocketGuild. | |||||
| ## Event Registration | ## Event Registration | ||||
| @@ -67,15 +59,3 @@ API docs before implementing it. | |||||
| ## Async | ## Async | ||||
| Nearly everything in 1.0 is an async Task. You should always await any tasks you invoke. | Nearly everything in 1.0 is an async Task. You should always await any tasks you invoke. | ||||
| However, when using WebSockets, you may find this both inconvienent, and unnecessary, as many of the | |||||
| WebSocket implementations of the interfaces keep their own local cache of objects, rendering the use | |||||
| of async redundant. | |||||
| **As of right now,** there are extension methods you can use, located in `Discord.WebSocket` that will | |||||
| provide java-esque, synchronus `GetXXX` methods to replace the asynchronus methods on WebSocket entities. | |||||
| This functionality may be changed at a later date, we are currently reviewing this implementation and | |||||
| alternative methods. | |||||
| For your reference, you may want to look through the extension classes located in @Discord.WebSocket | |||||