From 22d79c100449f0f1e9380bdffc627e1097a82ff3 Mon Sep 17 00:00:00 2001 From: Hsu Still <341464@gmail.com> Date: Wed, 4 Oct 2017 04:48:05 +0800 Subject: [PATCH] Improve library documentation (#826) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Improve the Command Service documentation The following changes have been added to this PR: • Fix minor grammatical errors. • Capitalize terms such as Commands, Modules and such, as the context is specific to the lib. • Wrap methods and properties in code blocks. The docs page currently has several issues that remains to be fixed. 1. ```md >[!WARNING] >This article is out of date and has not been rewritten yet. Information is not guaranteed to be accurate. ``` The docs doesn't necessarily seem "out of date" as the warning claims. The basics seem pretty relevant to the latest version of the lib. 2. >“To manually load a module, invoke [CommandService.AddModuleAsync], by passing in the generic type of your module and optionally, a dependency map.” The latter part of the sentence seems off. Where should the user pass the dependency map to? It seems to suggest that `AddModuleAsync` has an argument to pass the dependency to. If it is referring to `AddModuleAsync(Type type)`, then I feel like it should be clarified here - or perhaps change the wording of the sentence. 3. >“First, you need to create an @System.IServiceProvider You may create your own IServiceProvider if you wish.” Any mention of @System.IServiceProvider is currently broken on the docs. 4. >“Submodules are Modules that reside within another one. Typically, submodules are used to create nested groups (although not required to create nested groups).” Clarification on the part after "although?" 5. >“Finally, pass the map into the LoadAssembly method. Your modules will automatically be loaded with this dependency map.” Where is this `LoadAssembly` method? 6. ```md >[!NOTE] >Preconditions can be applied to Modules, Groups, or Commands. ``` The docs should mention `ParameterPreconditionAttribute`'s existence. * Update line breaks to comply with docs standard * Change "you should..." to "instead, ..." * Trim trailing spaces * Change "inherits" to "inherit" * Fix Context warning note and add ReplyAsync xref * Fix broken xrefs * Fix [Command Service] xref * Fix consistency between TypeReaders and Preconditions returns * Add missing semi-colons in ServiceProvider sample * Change CommandContext to SocketCommandContext & change variable naming * Cleanup TypeReader section * Wrap [DontInject] in code block * Fix commands docs linking in intro * Improve Getting Started - Installation - Fix character misalignment to comply with docs standard. - Fix image numbering issues by moving the tooltips above some of the steps. - Add codeblocks to search terms like `Discord.Net`. - Remove broken `addons` reference. - Specify `.NET 4.6.1` as `.NET Framework 4.6.1`. - Minor cross-reference cleanup. * Fix Getting Started - Intro - Minor grammartical fixes. - Wrap mentions of the methods, properties, and events in code block. - Replace `Discord.Net` to `Discord.NET`. - Fix steps numbering under `Creating a Discord Bot` and `Adding your bot to a server`. - Change `Task-based Asynchronous Pattern ([TAP])` linking to mark the entire term instead. - Change code block of `Pong!` to quotation mark instead. * Fix cross references in Sending Voice * Mention parameter precondition attribute * Change `Discord.NET` to `Discord.Net` for consistency * Wrap project names in code blocks & minor fixes in Terminology * Change `add-ons` to `addons` for consistency * Fix cross references in Logging * Fix minor grammatical issues in "Working with Events" * Missed a tilda * Remove out-of-date warning in Commands * Minor grammatical fixes for Entities * Fix broken xref in Logging * Adjust service collection sample ...according to https://github.com/RogueException/Discord.Net/pull/826/files/f89aecb7bfa8fe82a541d3bba44d0e37c125d235#r141530227 * Update Command Handler sample - Update Main for C# 7.1. - Inject CommandService and DiscordSocketClient into the service collection. - Add Async suffix to asynchronous methods. * Minor grammatical fixes in Events * Revert 2 incorrect grammar corrections * Revert async Main sample * Add hardcode token notice in sample * Fix missing method for Command Handler * Modify module samples to use SocketCommandContext instead * Emphasize CommandContext and SocketCommandContext * Fix formatting for module sample * Add SocketCommandContext for Groups sample * Remove comma * Fix DepMap sample formatting * Replace [DontInject] with DontInjectAttribute with cross reference * Remove connection logic note There is no reason that this note should still be here since Ready event exists. * Add a new warning message informing the users the existence of CommandService * Make command handler private excellent change --- Discord.Net.sln | 8 +- docs/guides/commands/commands.md | 248 ++++++++++-------- .../commands/samples/command_handler.cs | 43 +-- .../commands/samples/dependency_map_setup.cs | 32 +-- docs/guides/commands/samples/empty-module.cs | 2 +- docs/guides/commands/samples/groups.cs | 4 +- docs/guides/commands/samples/module.cs | 59 ++--- docs/guides/concepts/entities.md | 11 +- docs/guides/concepts/events.md | 20 +- docs/guides/concepts/logging.md | 9 +- docs/guides/getting_started/installing.md | 125 ++++----- docs/guides/getting_started/intro.md | 134 +++++----- docs/guides/getting_started/terminology.md | 32 +-- docs/guides/voice/sending-voice.md | 8 +- 14 files changed, 394 insertions(+), 341 deletions(-) diff --git a/Discord.Net.sln b/Discord.Net.sln index a63606787..58bfcad86 100644 --- a/Discord.Net.sln +++ b/Discord.Net.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 +VisualStudioVersion = 15.0.26730.12 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Core", "src\Discord.Net.Core\Discord.Net.Core.csproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" EndProject @@ -142,4 +142,10 @@ Global {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7} = {B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012} {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D2404771-EEC8-45F2-9D71-F3373F6C1495} + EndGlobalSection + GlobalSection(CodealikeProperties) = postSolution + SolutionGuid = a45217b4-a401-4dbf-8654-34d2ec034cd9 + EndGlobalSection EndGlobal diff --git a/docs/guides/commands/commands.md b/docs/guides/commands/commands.md index e021b1eb3..6781764c9 100644 --- a/docs/guides/commands/commands.md +++ b/docs/guides/commands/commands.md @@ -1,24 +1,20 @@ # The Command Service ->[!WARNING] ->This article is out of date, and has not been rewritten yet. -Information is not guaranteed to be accurate. - [Discord.Commands](xref:Discord.Commands) provides an Attribute-based - Command Parser. +command parser. ## Setup -To use Commands, you must create a [Commands Service] and a -Command Handler. +To use Commands, you must create a [Command Service] 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 barebone Command Handler. You can extend your +Command Handler as much as you like; however, the below is the bare +minimum. -The CommandService optionally will accept a [CommandServiceConfig], +The `CommandService` will optionally accept a [CommandServiceConfig], which _does_ set a few default values for you. It is recommended to -look over the properties in [CommandServiceConfig], and their default +look over the properties in [CommandServiceConfig] and their default values. [!code-csharp[Command Handler](samples/command_handler.cs)] @@ -28,32 +24,32 @@ values. ## With Attributes -In 1.0, Commands can be defined ahead of time, with attributes, or -at runtime, with builders. +In 1.0, Commands can be defined ahead of time with attributes, or at +runtime with builders. -For most bots, ahead-of-time commands should be all you need, and this -is the recommended method of defining commands. +For most bots, ahead-of-time Commands should be all you need, and this +is the recommended method of defining Commands. ### Modules -The first step to creating commands is to create a _module_. +The first step to creating Commands is to create a _module_. -Modules are an organizational pattern that allow you to write your -commands in different classes, and have them automatically loaded. +A Module is an organizational pattern that allows you to write your +Commands in different classes and have them automatically loaded. 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 invoked. +ASP.NET Core's Controller pattern. This means that the lifetime of a +module instance is only as long as the Command is being invoked. **Avoid using long-running code** in your modules wherever possible. -You should **not** be implementing very much logic into your modules; -outsource to a service for that. +You should **not** be implementing very much logic into your modules, +instead, 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 begin, create a new class somewhere in your project, and -inherit the class from [ModuleBase]. This class **must** be `public`. +To begin, create a new class somewhere in your project and inherit the +class from [ModuleBase]. This class **must** be `public`. >[!NOTE] >[ModuleBase] is an _abstract_ class, meaning that you may extend it @@ -61,6 +57,7 @@ inherit the class from [ModuleBase]. This class **must** be `public`. >extension of ModuleBase. By now, your module should look like this: + [!code-csharp[Empty Module](samples/empty-module.cs)] [IoC]: https://msdn.microsoft.com/en-us/library/ff921087.aspx @@ -69,72 +66,75 @@ By now, your module should look like this: ### Adding Commands -The next step to creating commands, is actually creating commands. +The next step to creating Commands is actually creating the Commands. -To create a command, add a method to your module of type `Task`. -Typically, you will want to mark this method as `async`, although it is -not required. +To create a Command, add a method to your module of type `Task`. +Typically, you will want to mark this method as `async`, although it +is not required. -Adding parameters to a command is done by adding parameters to the +Adding parameters to a Command is done by adding parameters to the parent Task. -For example, to take an integer as an argument, add `int arg`. To take -a user as an argument, add `IUser user`. In 1.0, a command can accept -nearly any type of argument; a full list of types that are parsed by -default can be found in the below section on _Type Readers_. +For example, to take an integer as an argument from the user, add `int +arg`; to take a user as an argument from the user, add `IUser user`. +In 1.0, a Command can accept nearly any type of argument; a full list +of types that are parsed by default can be found in the below section +on _Type Readers_. Parameters, by default, are always required. To make a parameter optional, give it a default value. To accept a comma-separated list, set the parameter to `params Type[]`. Should a parameter include spaces, it **must** be wrapped in quotes. -For example, for a command with a parameter `string food`, you would +For example, for a Command with a parameter `string food`, you would execute it with `!favoritefood "Key Lime Pie"`. -If you would like a parameter to parse until the end of a command, +If you would like a parameter to parse until the end of a Command, flag the parameter with the [RemainderAttribute]. This will allow a -user to invoke a command without wrapping a parameter in quotes. +user to invoke a Command without wrapping a parameter in quotes. -Finally, flag your command with the [CommandAttribute]. (You must -specify a name for this command, except for when it is part of a -module group - see below). +Finally, flag your Command with the [CommandAttribute] (you must +specify a name for this Command, except for when it is part of a +Module Group - see below). [RemainderAttribute]: xref:Discord.Commands.RemainderAttribute [CommandAttribute]: xref:Discord.Commands.CommandAttribute ### Command Overloads -You may add overloads of your commands, and the command parser will +You may add overloads to your Commands, and the Command parser will automatically pick up on it. -If, for whatever reason, you have too commands which are ambiguous to +If for whatever reason, you have two Commands which are ambiguous to each other, you may use the @Discord.Commands.PriorityAttribute to specify which should be tested before the other. -Priority's are sorted in ascending order; the higher priority will be -called first. +The `Priority` attributes are sorted in ascending order; the higher +priority will be called first. -### CommandContext +### Command Context -Every command can access the execution context through the [Context] -property on [ModuleBase]. CommandContext allows you to access the -message, channel, guild, and user that the command was invoked from, -as well as the underlying discord client the command was invoked from. +Every Command can access the execution context through the [Context] +property on [ModuleBase]. `ICommandContext` allows you to access the +message, channel, guild, and user that the Command was invoked from, +as well as the underlying Discord client that the Command was invoked +from. Different types of Contexts may be specified using the generic variant -of [ModuleBase]. When using a [SocketCommandContext], for example, -the properties on this context will already be Socket entities. You +of [ModuleBase]. When using a [SocketCommandContext], for example, the +properties on this context will already be Socket entities, so you will not need to cast them. To reply to messages, you may also invoke [ReplyAsync], instead of accessing the channel through the [Context] and sending a message. +> [!WARNING] +>Contexts should **NOT** be mixed! You cannot have one module that +>uses `CommandContext` and another that uses `SocketCommandContext`. + [Context]: xref:Discord.Commands.ModuleBase`1#Discord_Commands_ModuleBase_1_Context [SocketCommandContext]: xref:Discord.Commands.SocketCommandContext - ->![WARNING] ->Contexts should **NOT** be mixed! You cannot have one module that ->uses CommandContext, and another that uses SocketCommandContext. +[ReplyAsync]: xref:Discord.Commands.ModuleBase`1#Discord_Commands_ModuleBase_1_ReplyAsync_System_String_System_Boolean_Discord_Embed_Discord_RequestOptions_ ### Example Module @@ -144,50 +144,50 @@ At this point, your module should look comparable to this example: #### Loading Modules Automatically The Command Service can automatically discover all classes in an -Assembly that inherit [ModuleBase], and load them. +Assembly that inherit [ModuleBase] and load them. To opt a module out of auto-loading, flag it with -[DontAutoLoadAttribute] +[DontAutoLoadAttribute]. Invoke [CommandService.AddModulesAsync] to discover modules and install them. [DontAutoLoadAttribute]: xref:Discord.Commands.DontAutoLoadAttribute -[CommandService.AddModulesAsync]: xref:Discord_Commands_CommandService#Discord_Commands_CommandService_AddModulesAsync_Assembly_ +[CommandService.AddModulesAsync]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddModulesAsync_Assembly_ #### Loading Modules Manually -To manually load a module, invoke [CommandService.AddModuleAsync], -by passing in the generic type of your module, and optionally -a dependency map. +To manually load a module, invoke [CommandService.AddModuleAsync] by +passing in the generic type of your module and optionally, a +dependency map. [CommandService.AddModuleAsync]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddModuleAsync__1 ### Module Constructors Modules are constructed using Dependency Injection. Any parameters -that are placed in the constructor must be injected into an -@System.IServiceProvider. Alternatively, you may accept an -IServiceProvider as an argument and extract services yourself. +that are placed in the Module's constructor must be injected into an +@System.IServiceProvider first. Alternatively, you may accept an +`IServiceProvider` as an argument and extract services yourself. ### Module Properties -Modules with public settable properties will have them injected after module -construction. +Modules with `public` settable properties will have the dependencies +injected after the construction of the Module. ### Module Groups -Module Groups allow you to create a module where commands are prefixed. -To create a group, flag a module with the -@Discord.Commands.GroupAttribute +Module Groups allow you to create a module where Commands are +prefixed. To create a group, flag a module with the +@Discord.Commands.GroupAttribute. -Module groups also allow you to create **nameless commands**, where the -[CommandAttribute] is configured with no name. In this case, the -command will inherit the name of the group it belongs to. +Module groups also allow you to create **nameless Commands**, where +the [CommandAttribute] is configured with no name. In this case, the +Command will inherit the name of the group it belongs to. ### Submodules -Submodules are modules that reside within another module. Typically, +Submodules are Modules that reside within another one. Typically, submodules are used to create nested groups (although not required to create nested groups). @@ -199,54 +199,62 @@ create nested groups). ## Dependency Injection -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. +The Command Service is bundled with a very barebone Dependency +Injection service for your convenience. It is recommended that you use +DI when writing your modules. ### Setup -First, you need to create an @System.IServiceProvider -You may create your own IServiceProvider if you wish. +First, you need to create an @System.IServiceProvider; you may create +your own one if you wish. -Next, add the dependencies your modules will use to the map. +Next, add the dependencies that 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 be automatically loaded with this dependency map. [!code-csharp[IServiceProvider Setup](samples/dependency_map_setup.cs)] ### Usage in Modules -In the constructor of your module, any parameters will be filled in by -the @System.IServiceProvider you pass into `LoadAssembly`. +In the constructor of your Module, any parameters will be filled in by +the @System.IServiceProvider that you've passed into `LoadAssembly`. -Any publicly settable properties will also be filled in the same manner. +Any publicly settable properties will also be filled in the same +manner. >[!NOTE] -> Annotating a property with the [DontInject] attribute will prevent it from -being injected. +> Annotating a property with a [DontInjectAttribute] attribute will prevent the +property from being injected. >[!NOTE] ->If you accept `CommandService` or `IServiceProvider` as a parameter in -your constructor or as an injectable property, these entries will be filled -by the CommandService the module was loaded from, and the ServiceProvider passed -into it, respectively. +>If you accept `CommandService` or `IServiceProvider` as a parameter +in your constructor or as an injectable property, these entries will +be filled by the `CommandService` that the Module is loaded from and +the `ServiceProvider` that is passed into it respectively. [!code-csharp[ServiceProvider in Modules](samples/dependency_module.cs)] +[DontInjectAttribute]: xref:Discord.Commands.DontInjectAttribute + # 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. +Precondition 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. +>There are two types of Preconditions. +[PreconditionAttribute] can be applied to Modules, Groups, or Commands; +[ParameterPreconditionAttribute] can be applied to Parameters. + +[PreconditionAttribute]: xref:Discord.Commands.PreconditionAttribute +[ParameterPreconditionAttribute]: xref:Discord.Commands.ParameterPreconditionAttribute ## Bundled Preconditions -Commands ships with four bundled preconditions; you may view their -usages on their API page. +Commands ship with four bundled Preconditions; you may view their +usages on their respective API pages. - @Discord.Commands.RequireContextAttribute - @Discord.Commands.RequireOwnerAttribute @@ -255,21 +263,23 @@ usages on their API page. ## Custom Preconditions -To write your own preconditions, create a new class that inherits from - @Discord.Commands.PreconditionAttribute +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 -[CheckPermissions]. +In order for your Precondition to function, you will need to override +the [CheckPermissions] method. 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. +If the context meets the required parameters, return +[PreconditionResult.FromSuccess], otherwise return +[PreconditionResult.FromError] and include an error message if +necessary. [!code-csharp[Custom Precondition](samples/require_owner.cs)] -[CheckPermissions]: xref:Discord.Commands.PreconditionAttribute#Discord_Commands_PreconditionAttribute_CheckPermissions_Discord_Commands_CommandContext_Discord_Commands_CommandInfo_Discord_Commands_IDependencyMap_ +[CheckPermissions]: xref:Discord.Commands.PreconditionAttribute#Discord_Commands_PreconditionAttribute_CheckPermissions_Discord_Commands_ICommandContext_Discord_Commands_CommandInfo_IServiceProvider_ [PreconditionResult.FromSuccess]: xref:Discord.Commands.PreconditionResult#Discord_Commands_PreconditionResult_FromSuccess [PreconditionResult.FromError]: xref:Discord.Commands.PreconditionResult#Discord_Commands_PreconditionResult_FromError_System_String_ @@ -296,22 +306,28 @@ By default, the following Types are supported arguments: ### 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 and ensure the class inherits from +@Discord.Commands.TypeReader. -Next, satisfy the `TypeReader` class by overriding [Read]. +Next, satisfy the `TypeReader` class by overriding the [Read] method. >[!NOTE] >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`. +If you are able to successfully parse the input, return +[TypeReaderResult.FromSuccess] with the parsed input, otherwise return +[TypeReaderResult.FromError] and include an error message if +necessary. -[Read]: xref:Discord.Commands.TypeReader#Discord_Commands_TypeReader_Read_Discord_Commands_CommandContext_System_String_ +[TypeReaderResult]: xref:Discord.Commands.TypeReaderResult +[TypeReaderResult.FromSuccess]: xref:Discord.Commands.TypeReaderResult#Discord_Commands_TypeReaderResult_FromSuccess_Discord_Commands_TypeReaderValue_ +[TypeReaderResult.FromError]: xref:Discord.Commands.TypeReaderResult#Discord_Commands_TypeReaderResult_FromError_Discord_Commands_CommandError_System_String_ +[Read]: xref:Discord.Commands.TypeReader#Discord_Commands_TypeReader_Read_Discord_Commands_ICommandContext_System_String_IServiceProvider_ #### Sample @@ -319,5 +335,9 @@ Otherwise, return `TypeReaderResult.FromError`. ### 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]. + +[CommandService.AddTypeReader]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddTypeReader__1_Discord_Commands_TypeReader_ \ No newline at end of file diff --git a/docs/guides/commands/samples/command_handler.cs b/docs/guides/commands/samples/command_handler.cs index 6b5d4ad2b..da2453aa8 100644 --- a/docs/guides/commands/samples/command_handler.cs +++ b/docs/guides/commands/samples/command_handler.cs @@ -8,39 +8,42 @@ using Microsoft.Extensions.DependencyInjection; public class Program { - private CommandService commands; - private DiscordSocketClient client; - private IServiceProvider services; + private CommandService _commands; + private DiscordSocketClient _client; + private IServiceProvider _services; - static void Main(string[] args) => new Program().Start().GetAwaiter().GetResult(); + private static void Main(string[] args) => new Program().StartAsync().GetAwaiter().GetResult(); - public async Task Start() + public async Task StartAsync() { - client = new DiscordSocketClient(); - commands = new CommandService(); + _client = new DiscordSocketClient(); + _commands = new CommandService(); + // Avoid hard coding your token. Use an external source instead in your code. string token = "bot token here"; - services = new ServiceCollection() - .BuildServiceProvider(); + _services = new ServiceCollection() + .AddSingleton(_client) + .AddSingleton(_commands) + .BuildServiceProvider(); - await InstallCommands(); + await InstallCommandsAsync(); - await client.LoginAsync(TokenType.Bot, token); - await client.StartAsync(); + await _client.LoginAsync(TokenType.Bot, token); + await _client.StartAsync(); await Task.Delay(-1); } - public async Task InstallCommands() + public async Task InstallCommandsAsync() { // Hook the MessageReceived Event into our Command Handler - client.MessageReceived += HandleCommand; + _client.MessageReceived += HandleCommandAsync; // Discover all of the commands in this assembly and load them. - await commands.AddModulesAsync(Assembly.GetEntryAssembly()); + await _commands.AddModulesAsync(Assembly.GetEntryAssembly()); } - public async Task HandleCommand(SocketMessage messageParam) + private async Task HandleCommandAsync(SocketMessage messageParam) { // Don't process the command if it was a System Message var message = messageParam as SocketUserMessage; @@ -48,13 +51,13 @@ public class Program // Create a number to track where the prefix ends and the command begins int argPos = 0; // Determine if the message is a command, based on if it starts with '!' or a mention prefix - if (!(message.HasCharPrefix('!', ref argPos) || message.HasMentionPrefix(client.CurrentUser, ref argPos))) return; + if (!(message.HasCharPrefix('!', ref argPos) || message.HasMentionPrefix(_client.CurrentUser, ref argPos))) return; // Create a Command Context - var context = new CommandContext(client, message); + var context = new SocketCommandContext(_client, message); // Execute the command. (result does not indicate a return value, // rather an object stating if the command executed successfully) - var result = await commands.ExecuteAsync(context, argPos, service); + var result = await _commands.ExecuteAsync(context, argPos, _services); if (!result.IsSuccess) await context.Channel.SendMessageAsync(result.ErrorReason); } -} +} \ No newline at end of file diff --git a/docs/guides/commands/samples/dependency_map_setup.cs b/docs/guides/commands/samples/dependency_map_setup.cs index e205d891d..a36925904 100644 --- a/docs/guides/commands/samples/dependency_map_setup.cs +++ b/docs/guides/commands/samples/dependency_map_setup.cs @@ -1,18 +1,18 @@ -using Discord; -using Discord.Commands; -using Discord.WebSocket; -using foxboat.Services; +private IServiceProvider _services; +private CommandService _commands; -public class Commands +public async Task InstallAsync(DiscordSocketClient client) { - public async Task Install(DiscordSocketClient client) - { - // Here, we will inject the ServiceProvider with - // all of the services our client will use. - _serviceCollection.AddSingleton(client) - _serviceCollection.AddSingleton(new NotificationService()) - _serviceCollection.AddSingleton(new DatabaseService()) - // ... - await _commands.AddModulesAsync(Assembly.GetEntryAssembly()); - } -} + // Here, we will inject the ServiceProvider with + // all of the services our client will use. + _services = new ServiceCollection() + .AddSingleton(client) + .AddSingleton(_commands) + // You can pass in an instance of the desired type + .AddSingleton(new NotificationService()) + // ...or by using the generic method. + .AddSingleton() + .BuildServiceProvider(); + // ... + await _commands.AddModulesAsync(Assembly.GetEntryAssembly()); +} \ No newline at end of file diff --git a/docs/guides/commands/samples/empty-module.cs b/docs/guides/commands/samples/empty-module.cs index cac9922b5..6483c7cd2 100644 --- a/docs/guides/commands/samples/empty-module.cs +++ b/docs/guides/commands/samples/empty-module.cs @@ -1,6 +1,6 @@ using Discord.Commands; -public class InfoModule : ModuleBase +public class InfoModule : ModuleBase { } \ No newline at end of file diff --git a/docs/guides/commands/samples/groups.cs b/docs/guides/commands/samples/groups.cs index db6456c87..5f96c34e8 100644 --- a/docs/guides/commands/samples/groups.cs +++ b/docs/guides/commands/samples/groups.cs @@ -1,8 +1,8 @@ [Group("admin")] -public class AdminModule : ModuleBase +public class AdminModule : ModuleBase { [Group("clean")] - public class CleanModule : ModuleBase + public class CleanModule : ModuleBase { // ~admin clean 15 [Command] diff --git a/docs/guides/commands/samples/module.cs b/docs/guides/commands/samples/module.cs index 403acba06..5014619da 100644 --- a/docs/guides/commands/samples/module.cs +++ b/docs/guides/commands/samples/module.cs @@ -1,42 +1,41 @@ -using Discord; -using Discord.Commands; -using Discord.WebSocket; - // Create a module with no prefix -public class Info : ModuleBase +public class Info : ModuleBase { - // ~say hello -> hello - [Command("say"), Summary("Echos a message.")] - public async Task Say([Remainder, Summary("The text to echo")] string echo) - { - // ReplyAsync is a method on ModuleBase - await ReplyAsync(echo); - } + // ~say hello -> hello + [Command("say")] + [Summary("Echos a message.")] + public async Task SayAsync([Remainder] [Summary("The text to echo")] string echo) + { + // ReplyAsync is a method on ModuleBase + await ReplyAsync(echo); + } } // Create a module with the 'sample' prefix [Group("sample")] -public class Sample : ModuleBase +public class Sample : ModuleBase { - // ~sample square 20 -> 400 - [Command("square"), Summary("Squares a number.")] - public async Task Square([Summary("The number to square.")] int num) - { - // We can also access the channel from the Command Context. - await Context.Channel.SendMessageAsync($"{num}^2 = {Math.Pow(num, 2)}"); - } + // ~sample square 20 -> 400 + [Command("square")] + [Summary("Squares a number.")] + public async Task SquareAsync([Summary("The number to square.")] int num) + { + // 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 // ~sample userinfo @Khionu --> Khionu#8708 // ~sample userinfo Khionu#8708 --> Khionu#8708 // ~sample userinfo Khionu --> Khionu#8708 // ~sample userinfo 96642168176807936 --> Khionu#8708 - // ~sample whois 96642168176807936 --> Khionu#8708 - [Command("userinfo"), Summary("Returns info about the current user, or the user parameter, if one passed.")] - [Alias("user", "whois")] - public async Task UserInfo([Summary("The (optional) user to get info for")] IUser user = null) - { - var userInfo = user ?? Context.Client.CurrentUser; - await ReplyAsync($"{userInfo.Username}#{userInfo.Discriminator}"); - } -} + // ~sample whois 96642168176807936 --> Khionu#8708 + [Command("userinfo")] + [Summary("Returns info about the current user, or the user parameter, if one passed.")] + [Alias("user", "whois")] + public async Task UserInfoAsync([Summary("The (optional) user to get info for")] SocketUser user = null) + { + var userInfo = user ?? Context.Client.CurrentUser; + await ReplyAsync($"{userInfo.Username}#{userInfo.Discriminator}"); + } +} \ No newline at end of file diff --git a/docs/guides/concepts/entities.md b/docs/guides/concepts/entities.md index a38651829..3a5d5496b 100644 --- a/docs/guides/concepts/entities.md +++ b/docs/guides/concepts/entities.md @@ -12,7 +12,7 @@ Discord API. ### Inheritance Due to the nature of the Discord API, some entities are designed with -multiple variants, for example, `SocketUser` and `SocketGuildUser`. +multiple variants; for example, `SocketUser` and `SocketGuildUser`. All models will contain the most detailed version of an entity possible, even if the type is less detailed. @@ -61,8 +61,11 @@ a variant of the type that you need. ### Tips Avoid using boxing-casts to coerce entities into a variant, use the -`as` keyword, and a null-conditional operator. +[`as`] keyword, and a null-conditional operator instead. -This allows you to write safer code, and avoid InvalidCastExceptions. +This allows you to write safer code and avoid [InvalidCastExceptions]. -For example, `(message.Author as SocketGuildUser)?.Nickname`. \ No newline at end of file +For example, `(message.Author as SocketGuildUser)?.Nickname`. + +[`as`]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/as +[InvalidCastExceptions]: https://msdn.microsoft.com/en-us/library/system.invalidcastexception(v=vs.110).aspx \ No newline at end of file diff --git a/docs/guides/concepts/events.md b/docs/guides/concepts/events.md index f2dfb00f0..47db49aa8 100644 --- a/docs/guides/concepts/events.md +++ b/docs/guides/concepts/events.md @@ -4,27 +4,27 @@ title: Working with Events Events in Discord.Net are consumed in a similar manner to the standard convention, with the exception that every event must be of the type -`System.Threading.Tasks.Task`, and instead of using EventArgs, the +`System.Threading.Tasks.Task` and instead of using `EventArgs`, the event's parameters are passed directly into the handler. -This allows for events to be handled in an async context directly, -instead of relying on async void. +This allows for events to be handled in an async context directly +instead of relying on `async void`. ### Usage To receive data from an event, hook into it using C#'s delegate event pattern. -You may opt either to hook an event to an anonymous function (lambda) +You may either opt to hook an event to an anonymous function (lambda) or a named function. ### Safety -All events are designed to be thread-safe, in that events are executed -synchronously off the gateway task, in the same context as the gateway +All events are designed to be thread-safe; events are executed +synchronously off the gateway task in the same context as the gateway task. -As a side effect, this makes it possible to deadlock the gateway task, +As a side effect, this makes it possible to deadlock the gateway task and kill a connection. As a general rule of thumb, any task that takes longer than three seconds should **not** be awaited directly in the context of an event, but should be wrapped in a `Task.Run` or @@ -62,7 +62,7 @@ This pattern is typically only found on `EntityUpdated` events. An event handler with a signature of `Func` means that the `before` state of the entity was not provided by the -API, so it can either be pulled from the client's cache, or +API, so it can either be pulled from the client's cache or downloaded from the API. See the documentation for [Cacheable] for more information on this @@ -76,8 +76,8 @@ object. ### Tips -Many events relating to a Message entity, e.g. `MessageUpdated` -and `ReactionAdded` rely on the client's message cache, which is +Many events relating to a Message entity (i.e. `MessageUpdated` and +`ReactionAdded`) rely on the client's message cache, which is **not** enabled by default. Set the `MessageCacheSize` flag in [DiscordSocketConfig] to enable it. diff --git a/docs/guides/concepts/logging.md b/docs/guides/concepts/logging.md index 1592dfc72..50d2e9546 100644 --- a/docs/guides/concepts/logging.md +++ b/docs/guides/concepts/logging.md @@ -14,12 +14,14 @@ section. ### Usage To receive log events, simply hook the discord client's log method -to a Task with a single parameter of type [LogMessage] +to a `Task` with a single parameter of type [LogMessage]. It is recommended that you use an established function instead of a -lambda for handling logs, because most [addons] accept a reference +lambda for handling logs, because most addons accept a reference to a logging function to write their own messages. +[LogMessage]: xref:Discord.LogMessage + ### Usage in Commands Discord.Net's [CommandService] also provides a log event, identical @@ -29,6 +31,9 @@ Data logged through this event is typically coupled with a [CommandException], where information about the command's context and error can be found and handled. +[CommandService]: xref:Discord.Commands.CommandService +[CommandException]: xref:Discord.Commands.CommandException + #### Samples [!code-csharp[Logging Sample](samples/logging.cs)] diff --git a/docs/guides/getting_started/installing.md b/docs/guides/getting_started/installing.md index 82d242647..5d4c85d81 100644 --- a/docs/guides/getting_started/installing.md +++ b/docs/guides/getting_started/installing.md @@ -2,84 +2,87 @@ title: Installing Discord.Net --- -Discord.Net is distributed through the NuGet package manager, and it is -recommended to use NuGet to get started. +Discord.Net is distributed through the NuGet package manager, and it +is recommended to use NuGet to get started. Optionally, you may compile from source and install yourself. # Supported Platforms -Currently, Discord.Net targets [.NET Standard] 1.3, and offers support for -.NET Standard 1.1. If your application will be targeting .NET Standard 1.1, -please see the [additional steps](#installing-on-net-standard-11). +Currently, Discord.Net targets [.NET Standard] 1.3 and offers support +for .NET Standard 1.1. If your application will be targeting .NET +Standard 1.1, please see the [additional steps]. -Since Discord.Net is built on the .NET Standard, it is also recommended to -create applications using [.NET Core], though you are not required to. When -using .NET Framework, it is suggested to target `.NET 4.6.1` or higher. +Since Discord.Net is built on the .NET Standard, it is also +recommended to create applications using [.NET Core], though not +required. When using .NET Framework, it is suggested to target +`.NET Framework 4.6.1` or higher. [.NET Standard]: https://docs.microsoft.com/en-us/dotnet/articles/standard/library [.NET Core]: https://docs.microsoft.com/en-us/dotnet/articles/core/ +[additional steps]: #installing-on-net-standard-11 # Installing with NuGet Release builds of Discord.Net 1.0 will be published to the [official NuGet feed]. -Development builds of Discord.Net 1.0, as well as [addons](TODO) are published -to our development [MyGet feed]. +Development builds of Discord.Net 1.0, as well as addons *(TODO)* are +published to our development [MyGet feed]. Direct feed link: `https://www.myget.org/F/discord-net/api/v3/index.json` -Not sure how to add a direct feed? See how [with Visual Studio] -or [without Visual Studio](#configuring-nuget-without-visual-studio) +Not sure how to add a direct feed? See how [with Visual Studio] or +[without Visual Studio]. [official NuGet feed]: https://nuget.org [MyGet feed]: https://www.myget.org/feed/Packages/discord-net [with Visual Studio]: https://docs.microsoft.com/en-us/nuget/tools/package-manager-ui#package-sources - +[without Visual Studio]: #configuring-nuget-without-visual-studio ## Using Visual Studio -1. Create a solution for your bot -2. In Solution Explorer, find the 'Dependencies' element under your bot's -project -3. Right click on 'Dependencies', and select 'Manage NuGet packages' -![Step 3](images/install-vs-deps.png) -4. In the 'browse' tab, search for 'Discord.Net' - > [!TIP] -Don't forget to change your package source if you're installing from the -developer feed. -Also make sure to check 'Enable Prereleases' if installing a dev build! - -5. Install the 'Discord.Net' package - +>Don't forget to change your package source if you're installing from +the developer feed. +>Also make sure to check "Enable Prereleases" if installing a dev +build! + +1. Create a solution for your bot. +2. In Solution Explorer, find the "Dependencies" element under your +bot's project. +3. Right click on "Dependencies", and select "Manage NuGet packages." +![Step 3](images/install-vs-deps.png) +4. In the "Browse" tab, search for `Discord.Net`. +5. Install the `Discord.Net` package. ![Step 5](images/install-vs-nuget.png) ## Using JetBrains Rider -1. Create a new solution for your bot -2. Open the NuGet window (Tools > NuGet > Manage NuGet packages for Solution) -![Step 2](images/install-rider-nuget-manager.png) -3. In the 'Packages' tab, search for 'Discord.Net' -![Step 3](images/install-rider-search.png) - > [!TIP] -Make sure to check the 'Prerelease' box if installing a dev build! +Make sure to check the "Prerelease" box if installing a dev build! -4. Install by adding the package to your project +1. Create a new solution for your bot. +2. Open the NuGet window (Tools > NuGet > Manage NuGet packages for +Solution). +![Step 2](images/install-rider-nuget-manager.png) +3. In the "Packages" tab, search for `Discord.Net`. +![Step 3](images/install-rider-search.png) +4. Install by adding the package to your project. ![Step 4](images/install-rider-add.png) ## Using Visual Studio Code -1. Create a new project for your bot -2. Add Discord.Net to your .csproj +> [!TIP] +Don't forget to add the package source to a [NuGet.Config file] if +you're installing from the developer feed. + +1. Create a new project for your bot. +2. Add `Discord.Net` to your .csproj. [!code-xml[Sample .csproj](samples/project.csproj)] -> [!TIP] -Don't forget to add the package source to a [NuGet.Config file](#configuring-nuget-without-visual-studio) if you're installing from the -developer feed. +[NuGet.Config file]: #configuring-nuget-without-visual-studio # Compiling from Source @@ -90,8 +93,8 @@ In order to compile Discord.Net, you require the following: - [Visual Studio 2017](https://www.visualstudio.com/) - [.NET Core SDK 1.0](https://www.microsoft.com/net/download/core#/sdk) -The .NET Core and Docker (Preview) workload is required during Visual Studio -installation. +The .NET Core and Docker (Preview) workload is required during Visual +Studio installation. ### Using Command Line @@ -101,26 +104,27 @@ installation. ## Installing on .NET Standard 1.1 -For applications targeting a runtime corresponding with .NET Standard 1.1 or 1.2, -the builtin WebSocket and UDP provider will not work. For applications which -utilize a WebSocket connection to Discord (WebSocket or RPC), third-party -provider packages will need to be installed and configured. +For applications targeting a runtime corresponding with .NET Standard +1.1 or 1.2, the builtin WebSocket and UDP provider will not work. For +applications which utilize a WebSocket connection to Discord +(WebSocket or RPC), third-party provider packages will need to be +installed and configured. -First, install the following packages through NuGet, or compile yourself, if -you prefer: +First, install the following packages through NuGet, or compile +yourself, if you prefer: - Discord.Net.Providers.WS4Net - Discord.Net.Providers.UDPClient -Note that `Discord.Net.Providers.UDPClient` is _only_ required if your bot will -be utilizing voice chat. +Note that `Discord.Net.Providers.UDPClient` is _only_ required if your +bot will be utilizing voice chat. -Next, you will need to configure your [DiscordSocketClient] to use these custom -providers over the default ones. +Next, you will need to configure your [DiscordSocketClient] to use +these custom providers over the default ones. -To do this, set the `WebSocketProvider` and optionally `UdpSocketProvider` -properties on the [DiscordSocketConfig] that you are passing into your -client. +To do this, set the `WebSocketProvider` and the optional +`UdpSocketProvider` properties on the [DiscordSocketConfig] that you +are passing into your client. [!code-csharp[NET Standard 1.1 Example](samples/netstd11.cs)] @@ -129,13 +133,14 @@ client. ## Configuring NuGet without Visual Studio -If you plan on deploying your bot or developing outside of Visual Studio, you -will need to create a local NuGet configuration file for your project. +If you plan on deploying your bot or developing outside of Visual +Studio, you will need to create a local NuGet configuration file for +your project. -To do this, create a file named `nuget.config` alongside the root of your -application, where the project solution is located. +To do this, create a file named `nuget.config` alongside the root of +your application, where the project solution is located. -Paste the following snippets into this configuration file, adding any additional -feeds as necessary. +Paste the following snippets into this configuration file, adding any +additional feeds as necessary. [!code-xml[NuGet Configuration](samples/nuget.config)] diff --git a/docs/guides/getting_started/intro.md b/docs/guides/getting_started/intro.md index 837814511..02f04bec4 100644 --- a/docs/guides/getting_started/intro.md +++ b/docs/guides/getting_started/intro.md @@ -13,42 +13,46 @@ diverse commands later, but for now, it is a good starting point. Before you can begin writing your bot, it is necessary to create a bot account on Discord. -1. Visit the [Discord Applications Portal] -2. Create a New Application +1. Visit the [Discord Applications Portal]. +2. Create a New Application. 3. Give the application a name (this will be the bot's initial username). -4. Create the Application -![Step 4](images/intro-create-app.png) -5. In the application review page, click **Create a Bot User** -![Step 5](images/intro-create-bot.png) -6. Confirm the popup -7. If this bot will be public, check 'Public Bot'. -**Do not tick any other options!** +4. Create the Application. + + ![Step 4](images/intro-create-app.png) + +5. In the application review page, click **Create a Bot User**. + + ![Step 5](images/intro-create-bot.png) + +6. Confirm the popup. +7. If this bot will be public, check "Public Bot." **Do not tick any +other options!** [Discord Applications Portal]: https://discordapp.com/developers/applications/me ## Adding your bot to a server -Bots **can not** use invite links, they must be explicitly invited +Bots **cannot** use invite links, they must be explicitly invited through the OAuth2 flow. -1. Open your bot's application on the [Discord Applications Portal] +1. Open your bot's application on the [Discord Applications Portal]. 2. Retrieve the app's **Client ID**. - -![Step 2](images/intro-client-id.png) - + + ![Step 2](images/intro-client-id.png) + 3. Create an OAuth2 authorization URL `https://discordapp.com/oauth2/authorize?client_id=&scope=bot` -4. Open the authorization URL in your browser -5. Select a server - ->[!NOTE] -Only servers where you have the `MANAGE_SERVER` permission will be -present in this list. +4. Open the authorization URL in your browser. +5. Select a server. +6. Click on authorize. + + >[!NOTE] + Only servers where you have the `MANAGE_SERVER` permission will be + present in this list. + + ![Step 6](images/intro-add-bot.png) -6. Click authorize - -![Step 6](images/intro-add-bot.png) ## Connecting to Discord @@ -57,10 +61,10 @@ do that now. (see the [Installing](installing.md) section) ### Async -Discord.Net uses .NET's Task-based Asynchronous Pattern ([TAP]) +Discord.Net uses .NET's [Task-based Asynchronous Pattern (TAP)] extensively - nearly every operation is asynchronous. -It is highly recommended that these operations be awaited in a +It is highly recommended that these operations are awaited in a properly established async context whenever possible. Establishing an async context can be problematic, but not hard. @@ -70,27 +74,29 @@ async main. [!code-csharp[Async Context](samples/intro/async-context.cs)] -As a result of this, your program will now start, and immidiately -jump into an async context. This will allow us later on to create a -connection to Discord, without needing to worry about setting up the +As a result of this, your program will now start and immidiately +jump into an async context. This will allow us to create a connection +to Discord later on without needing to worry about setting up the correct async implementation. >[!TIP] If your application throws any exceptions within an async context, -they will be thrown all the way back up to the first non-async method. -Since our first non-async method is the program's Main method, this +they will be thrown all the way back up to the first non-async method; +since our first non-async method is the program's `Main` method, this means that **all** unhandled exceptions will be thrown up there, which will crash your application. Discord.Net will prevent exceptions in event handlers from crashing your program, but any exceptions in your async main **will** cause the application to crash. +[Task-based Asynchronous Pattern (TAP)]: https://docs.microsoft.com/en-us/dotnet/articles/csharp/async + ### Creating a logging method Before we create and configure a Discord client, we will add a method to handle Discord.Net's log events. To allow agnostic support of as many log providers as possible, we -log information through a Log event, with a proprietary LogMessage +log information through a `Log` event with a proprietary `LogMessage` parameter. See the [API Documentation] for this event. If you are using your own logging framework, this is where you would @@ -99,10 +105,12 @@ the Console. [!code-csharp[Async Context](samples/intro/logging.cs)] +[API Documentation]: xref:Discord.Rest.BaseDiscordClient#Discord_Rest_BaseDiscordClient_Log + ### Creating a Discord Client Finally, we can create a connection to Discord. Since we are writing -a bot, we will be using a [DiscordSocketClient], along with socket +a bot, we will be using a [DiscordSocketClient] along with socket entities. See the [terminology](terminology.md) if you're unsure of the differences. @@ -110,22 +118,24 @@ To do so, create an instance of [DiscordSocketClient] in your async main, passing in a configuration object only if necessary. For most users, the default will work fine. -Before connecting, we should hook the client's log event to the +Before connecting, we should hook the client's `Log` event to the log handler that was just created. Events in Discord.Net work similarly to other events in C#, so hook this event the way that you typically would. -Next, you will need to 'login to Discord' with the `LoginAsync` method. +Next, you will need to "login to Discord" with the `LoginAsync` +method. You may create a variable to hold your bot's token (this can be found on your bot's application page on the [Discord Applications Portal]). + ![Token](images/intro-token.png) >[!IMPORTANT] Your bot's token can be used to gain total access to your bot, so -**do __NOT__ share this token with anyone!** It may behoove you to -store this token in an external file if you plan on distributing the -source code for your bot. +**do __NOT__ share this token with anyone else!** It may behoove you +to store this token in an external file if you plan on distributing +the source code for your bot. We may now invoke the client's `StartAsync` method, which will start connection/reconnection logic. It is important to note that @@ -134,14 +144,9 @@ start connection/reconnection logic. It is important to note that Any methods that rely on the client's state should go in an event handler. ->[!NOTE] -Connection logic is incomplete as of the current build. Events will -soon be added to indicate when the client's state is ready for use; -(rewrite this section when possible) - Finally, we will want to block the async main method from returning until after the application is exited. To do this, we can await an -infinite delay, or any other blocking method, such as reading from +infinite delay or any other blocking method, such as reading from the console. The following lines can now be added: @@ -154,51 +159,55 @@ online in Discord. >[!TIP] Encountering a `PlatformNotSupportedException` when starting your bot? This means that you are targeting a platform where .NET's default -WebSocket client is not supported. Refer to the [installing guide] +WebSocket client is not supported. Refer to the [installation guide] for how to fix this. -[TAP]: https://docs.microsoft.com/en-us/dotnet/articles/csharp/async -[API Documentation]: xref:Discord.Rest.BaseDiscordClient#Discord_Rest_BaseDiscordClient_Log [DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient -[installing guide]: installing.md#installing-on-net-standard-11 +[installation guide]: installing.md#installing-on-net-standard-11 ### Handling a 'ping' +>[!WARNING] +Please note that this is *not* a proper way to create a command. +Use the `CommandService` provided by the library instead, as explained +in the [Command Guide] section. + Now that we have learned how to open a connection to Discord, we can begin handling messages that users are sending. To start out, our bot will listen for any message where the content -is equal to `!ping`, and respond back with `Pong!`. +is equal to `!ping` and respond back with "Pong!". -Since we want to listen for new messages, the event to hook in to +Since we want to listen for new messages, the event to hook into is [MessageReceived]. In your program, add a method that matches the signature of the -MessageReceived event - it must be a method (`Func`) that returns the -type `Task`, and takes a single parameter, a [SocketMessage]. Also, +`MessageReceived` event - it must be a method (`Func`) that returns +the type `Task` and takes a single parameter, a [SocketMessage]. Also, since we will be sending data to Discord in this method, we will flag it as `async`. -In this method, we will add an `if` block, to determine if the message +In this method, we will add an `if` block to determine if the message content fits the rules of our scenario - recall that it must be equal to `!ping`. Inside the branch of this condition, we will want to send a message -back to the channel from which the message came - `Pong!`. To find the -channel, look for the `Channel` property on the message parameter. +back to the channel from which the message comes from - "Pong!". To +find the channel, look for the `Channel` property on the message +parameter. Next, we will want to send a message to this channel. Since the channel object is of type [SocketMessageChannel], we can invoke the `SendMessageAsync` instance method. For the message content, send back -a string containing 'Pong!'. +a string containing "Pong!". You should have now added the following lines: [!code-csharp[Message](samples/intro/message.cs)] -Now, your first bot is complete. You may continue to add on to this -if you desire, but for any bot that will be carrying out multiple -commands, it is strongly encouraged to use the command framework, as +Now your first bot is complete. You may continue to add on to this +if you desire, but for any bots that will be carrying out multiple +commands, it is strongly recommended to use the command framework as shown below. For your reference, you may view the [completed program]. @@ -207,13 +216,14 @@ For your reference, you may view the [completed program]. [SocketMessage]: xref:Discord.WebSocket.SocketMessage [SocketMessageChannel]: xref:Discord.WebSocket.ISocketMessageChannel [completed program]: samples/intro/complete.cs +[Command Guide]: ../commands/commands.md # Building a bot with commands This section will show you how to write a program that is ready for -[commands](commands/commands.md). Note that this will not be explaining _how_ -to write commands or services, it will only be covering the general -structure. +[Commands](../commands/commands.md). Note that we will not be +explaining _how_ to write Commands or Services, it will only be +covering the general structure. For reference, view an [annotated example] of this structure. @@ -224,4 +234,4 @@ should be to separate the program (initialization and command handler), the modules (handle commands), and the services (persistent storage, pure functions, data manipulation). -**todo:** diagram of bot structure +**todo:** diagram of bot structure \ No newline at end of file diff --git a/docs/guides/getting_started/terminology.md b/docs/guides/getting_started/terminology.md index a51003e34..74f7a6259 100644 --- a/docs/guides/getting_started/terminology.md +++ b/docs/guides/getting_started/terminology.md @@ -7,32 +7,34 @@ title: Terminology ## Preface -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. ## Implementation Specific Entities -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. +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`. -As a bot developer, you will only need to use Discord.Net.WebSockets, +As a bot developer, you will only need to use `Discord.Net.WebSockets`, but you should be aware of the differences between them. -`Discord.Net.Core` provides a set of interfaces that model Discord's +`Discord.Net.Core` provides a set of interfaces that models 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. `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`. +**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.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`. \ No newline at end of file +`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`). \ No newline at end of file diff --git a/docs/guides/voice/sending-voice.md b/docs/guides/voice/sending-voice.md index c3ec8d9d7..024a98b95 100644 --- a/docs/guides/voice/sending-voice.md +++ b/docs/guides/voice/sending-voice.md @@ -17,7 +17,7 @@ when developing on .NET Core, this is where you execute `dotnet run` from; typically the same directory as your csproj). For Windows Users, precompiled binaries are available for your -convienence [here](https://discord.foxbot.me/binaries/) +convienence [here](https://discord.foxbot.me/binaries/). For Linux Users, you will need to compile [Sodium] and [Opus] from source, or install them from your package manager. @@ -31,7 +31,7 @@ Joining a channel is the first step to sending audio, and will return an [IAudioClient] to send data with. To join a channel, simply await [ConnectAsync] on any instance of an -@Discord.IVoiceChannel. +@Discord.IAudioChannel. [!code-csharp[Joining a Channel](samples/joining_audio.cs)] @@ -44,7 +44,7 @@ guild. To switch channels within a guild, invoke [ConnectAsync] on another voice channel in the guild. [IAudioClient]: xref:Discord.Audio.IAudioClient -[ConnectAsync]: xref:Discord.IVoiceChannel#Discord_IVoiceChannel_ConnectAsync +[ConnectAsync]: xref:Discord.IAudioChannel#Discord_IAudioChannel_ConnectAsync_Action_IAudioClient__ ## Transmitting Audio @@ -84,7 +84,7 @@ Channels should be left at `2`, unless you specified a different value for `-ac 2` when creating FFmpeg. [AudioOutStream]: xref:Discord.Audio.AudioOutStream -[IAudioClient.CreatePCMStream]: xref:Discord.Audio.IAudioClient#Discord_Audio_IAudioClient_CreatePCMStream_System_Int32_System_Int32_System_Nullable_System_Int32__System_Int32_ +[IAudioClient.CreatePCMStream]: xref:Discord.Audio.IAudioClient#Discord_Audio_IAudioClient_CreateDirectPCMStream_Discord_Audio_AudioApplication_System_Nullable_System_Int32__System_Int32_ Finally, audio will need to be piped from FFmpeg's stdout into your AudioOutStream. This step can be as complex as you'd like it to be, but