| @@ -6,8 +6,7 @@ remarks: *content | |||
| The attribute can be applied to a public settable property inside a | |||
| @Discord.Commands.ModuleBase based class. By applying this attribute, | |||
| the marked property will not be automatically injected of the | |||
| dependency. See [Dependency Injection](xref:Guides.Commands.DI) | |||
| to learn more. | |||
| dependency. See @Guides.Commands.DI to learn more. | |||
| --- | |||
| uid: Discord.Commands.DontInjectAttribute | |||
| @@ -16,6 +16,8 @@ DI when writing your modules. | |||
| to use in the modules. | |||
| 3. Pass the service collection into `AddModulesAsync`. | |||
| ### Example - Setting up Injection | |||
| [!code-csharp[IServiceProvider Setup](samples/dependency_map_setup.cs)] | |||
| ## Usage in Modules | |||
| @@ -34,8 +36,10 @@ manner. | |||
| > 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. | |||
| > the `IServiceProvider` that is passed into it respectively. | |||
| ### Example - Injection in Modules | |||
| [!code-csharp[ServiceProvider in Modules](samples/dependency_module.cs)] | |||
| [!code-csharp[IServiceProvider in Modules](samples/dependency_module.cs)] | |||
| [DontInjectAttribute]: xref:Discord.Commands.DontInjectAttribute | |||
| @@ -99,19 +99,20 @@ For example: | |||
| * ...etc. | |||
| Starting from 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](#type-readers). | |||
| a full list of types that are parsed by default can | |||
| be found in @Guides.Commands.TypeReaders. | |||
| [CommandAttribute]: xref:Discord.Commands.CommandAttribute | |||
| #### Optional Parameters | |||
| Parameters, by default, are always required. To make a parameter | |||
| optional, give it a default value (i.e. `int num = 0`). To accept a comma-separated list, | |||
| set the parameter to `params Type[]`. | |||
| optional, give it a default value (i.e. `int num = 0`). | |||
| #### Parameters with Spaces | |||
| To accept a comma-separated list, set the parameter to `params Type[]`. | |||
| Should a parameter include spaces, the parameter **must** be | |||
| wrapped in quotes. For example, for a command with a parameter | |||
| `string food`, you would execute it with | |||
| @@ -198,7 +199,8 @@ that are placed in the Module's constructor must be injected into an | |||
| ### Module Properties | |||
| Modules with `public` settable properties will have the dependencies | |||
| injected after the construction of the Module. | |||
| injected after the construction of the module. See @Guides.Commands.DI | |||
| to learn more. | |||
| ### Module Groups | |||
| @@ -216,112 +218,4 @@ Submodules are "modules" that reside within another one. Typically, | |||
| submodules are used to create nested groups (although not required to | |||
| create nested groups). | |||
| [!code-csharp[Groups and Submodules](samples/groups.cs)] | |||
| # Preconditions | |||
| 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] | |||
| > 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 ship with four bundled Preconditions; you may view their | |||
| usages on their respective API pages. | |||
| * @Discord.Commands.RequireContextAttribute | |||
| * @Discord.Commands.RequireOwnerAttribute | |||
| * @Discord.Commands.RequireBotPermissionAttribute | |||
| * @Discord.Commands.RequireUserPermissionAttribute | |||
| * @Discord.Commands.RequireNsfwAttribute | |||
| ## Custom Preconditions | |||
| 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 | |||
| the [CheckPermissionsAsync] method. | |||
| Your IDE should provide an option to fill this in for you. | |||
| 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)] | |||
| [CheckPermissionsAsync]: xref:Discord.Commands.PreconditionAttribute.CheckPermissionsAsync* | |||
| [PreconditionResult.FromSuccess]: xref:Discord.Commands.PreconditionResult.FromSuccess* | |||
| [PreconditionResult.FromError]: xref:Discord.Commands.PreconditionResult.FromError* | |||
| # Type Readers | |||
| Type Readers allow you to parse different types of arguments in | |||
| your commands. | |||
| By default, the following Types are supported arguments: | |||
| * `bool` | |||
| * `char` | |||
| * `sbyte`/`byte` | |||
| * `ushort`/`short` | |||
| * `uint`/`int` | |||
| * `ulong`/`long` | |||
| * `float`, `double`, `decimal` | |||
| * `string` | |||
| * `DateTime`/`DateTimeOffset`/`TimeSpan` | |||
| * `Nullable<T>` where applicible | |||
| * Any implementation of `IChannel`/`IMessage`/`IUser`/`IRole` | |||
| ## Creating a Type Readers | |||
| 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 the [ReadAsync] 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. | |||
| 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. | |||
| [TypeReaderResult]: xref:Discord.Commands.TypeReaderResult | |||
| [TypeReaderResult.FromSuccess]: xref:Discord.Commands.TypeReaderResult.FromSuccess* | |||
| [TypeReaderResult.FromError]: xref:Discord.Commands.TypeReaderResult.FromError* | |||
| [ReadAsync]: xref:Discord.Commands.TypeReader.ReadAsync* | |||
| ## Registering TypeReaders | |||
| TypeReaders are not automatically discovered by the Command Service | |||
| and must be explicitly added. | |||
| To register a TypeReader, invoke [CommandService.AddTypeReader]. | |||
| > [!WARNING] | |||
| > TypeReaders must be added prior to module discovery, otherwise your | |||
| > TypeReaders may not work! | |||
| [CommandService.AddTypeReader]: xref:Discord.Commands.CommandService.AddTypeReader* | |||
| ### Sample | |||
| [!code-csharp[TypeReaders](samples/typereader.cs)] | |||
| [!code-csharp[Groups and Submodules](samples/groups.cs)] | |||
| @@ -0,0 +1,53 @@ | |||
| --- | |||
| uid: Guides.Commands.Preconditions | |||
| title: Preconditions | |||
| --- | |||
| # Preconditions | |||
| 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. | |||
| There are two types of Preconditions you can use: | |||
| * [PreconditionAttribute] can be applied to Modules, Groups, or Commands. | |||
| * [ParameterPreconditionAttribute] can be applied to Parameters. | |||
| You may visit their respective API documentation to find out more. | |||
| [PreconditionAttribute]: xref:Discord.Commands.PreconditionAttribute | |||
| [ParameterPreconditionAttribute]: xref:Discord.Commands.ParameterPreconditionAttribute | |||
| ## Bundled Preconditions | |||
| @Discord.Commands ship with several bundled Preconditions; you may | |||
| view their usages on their respective API pages. | |||
| * @Discord.Commands.RequireContextAttribute | |||
| * @Discord.Commands.RequireOwnerAttribute | |||
| * @Discord.Commands.RequireBotPermissionAttribute | |||
| * @Discord.Commands.RequireUserPermissionAttribute | |||
| * @Discord.Commands.RequireNsfwAttribute | |||
| ## Custom Preconditions | |||
| 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 | |||
| the [CheckPermissionsAsync] method. | |||
| Your IDE should provide an option to fill this in for you. | |||
| 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)] | |||
| [CheckPermissionsAsync]: xref:Discord.Commands.PreconditionAttribute.CheckPermissionsAsync* | |||
| [PreconditionResult.FromSuccess]: xref:Discord.Commands.PreconditionResult.FromSuccess* | |||
| [PreconditionResult.FromError]: xref:Discord.Commands.PreconditionResult.FromError* | |||
| @@ -1,40 +1,30 @@ | |||
| using Discord; | |||
| using Discord.Commands; | |||
| using Discord.WebSocket; | |||
| public class ModuleA : ModuleBase<SocketCommandContext> | |||
| public class DatabaseModule : ModuleBase<SocketCommandContext> | |||
| { | |||
| private readonly DatabaseService _database; | |||
| // Dependencies can be injected via the constructor | |||
| public ModuleA(DatabaseService database) | |||
| public DatabaseModule(DatabaseService database) | |||
| { | |||
| _database = database; | |||
| } | |||
| public async Task ReadFromDb() | |||
| [Command("read")] | |||
| public async Task ReadFromDbAsync() | |||
| { | |||
| var x = _database.getX(); | |||
| await ReplyAsync(x); | |||
| await ReplyAsync(_database.GetData()); | |||
| } | |||
| } | |||
| public class ModuleB : ModuleBase<SocketCommandContext> | |||
| public class MixModule : ModuleBase<SocketCommandContext> | |||
| { | |||
| // Public settable properties will be injected. | |||
| public AnnounceService Announce { get; set; } | |||
| // Public settable properties will be injected | |||
| public AnnounceService AnnounceService { get; set; } | |||
| // Public properties without setters will not be injected. | |||
| public CommandService Commands { get; } | |||
| // Public properties without setters will not be injected | |||
| public ImageService ImageService { get; } | |||
| // Public properties annotated with [DontInject] will not | |||
| // be injected. | |||
| // be injected | |||
| [DontInject] | |||
| public NotificationService NotificationService { get; set; } | |||
| public ModuleB(CommandService commands) | |||
| { | |||
| Commands = commands; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,5 +1,7 @@ | |||
| using Discord.Commands; | |||
| // Keep in mind your module **must** be public and inherit ModuleBase. | |||
| // If it isn't, it will not be discovered by AddModulesAsync! | |||
| public class InfoModule : ModuleBase<SocketCommandContext> | |||
| { | |||
| @@ -0,0 +1,29 @@ | |||
| public class CommandHandler | |||
| { | |||
| private readonly CommandService _commands; | |||
| private readonly DiscordSocketClient _client; | |||
| private readonly IServiceProvider _services; | |||
| public CommandHandler(CommandService commands, DiscordSocketClient client, IServiceProvider services) | |||
| { | |||
| _commands = commands; | |||
| _client = client; | |||
| _services = services; | |||
| } | |||
| public async Task SetupAsync() | |||
| { | |||
| _client.MessageReceived += CommandHandleAsync; | |||
| // Add BooleanTypeReader to type read for the type "bool" | |||
| _commands.AddTypeReader(typeof(bool), new BooleanTypeReader()); | |||
| // Then register the modules | |||
| await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services); | |||
| } | |||
| public async Task CommandHandleAsync(SocketMessage msg) | |||
| { | |||
| // ... | |||
| } | |||
| } | |||
| @@ -0,0 +1,69 @@ | |||
| --- | |||
| uid: Guides.Commands.TypeReaders | |||
| title: Type Readers | |||
| --- | |||
| # Type Readers | |||
| Type Readers allow you to parse different types of arguments in | |||
| your commands. | |||
| By default, the following Types are supported arguments: | |||
| * `bool` | |||
| * `char` | |||
| * `sbyte`/`byte` | |||
| * `ushort`/`short` | |||
| * `uint`/`int` | |||
| * `ulong`/`long` | |||
| * `float`, `double`, `decimal` | |||
| * `string` | |||
| * `DateTime`/`DateTimeOffset`/`TimeSpan` | |||
| * `Nullable<T>` where applicible | |||
| * Any implementation of `IChannel`/`IMessage`/`IUser`/`IRole` | |||
| ## Creating a Type Reader | |||
| 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 the [ReadAsync] method. | |||
| Inside this Task, add whatever logic you need to parse the input | |||
| string. | |||
| 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. | |||
| > [!NOTE] | |||
| > Visual Studio can help you implement missing members | |||
| > from the abstract class by using the "Implement Abstract Class" | |||
| > IntelliSense hint. | |||
| [TypeReaderResult]: xref:Discord.Commands.TypeReaderResult | |||
| [TypeReaderResult.FromSuccess]: xref:Discord.Commands.TypeReaderResult.FromSuccess* | |||
| [TypeReaderResult.FromError]: xref:Discord.Commands.TypeReaderResult.FromError* | |||
| [ReadAsync]: xref:Discord.Commands.TypeReader.ReadAsync* | |||
| ### Example - Creating a Type Reader | |||
| [!code-csharp[TypeReaders](samples/typereader.cs)] | |||
| ## Registering a Type Reader | |||
| TypeReaders are not automatically discovered by the Command Service | |||
| and must be explicitly added. | |||
| To register a TypeReader, invoke [CommandService.AddTypeReader]. | |||
| > [!IMPORTANT] | |||
| > TypeReaders must be added prior to module discovery, otherwise your | |||
| > TypeReaders may not work! | |||
| [CommandService.AddTypeReader]: xref:Discord.Commands.CommandService.AddTypeReader* | |||
| ### Example - Adding a Type Reader | |||
| [!code-csharp[Adding TypeReaders](samples/typereader-register.cs)] | |||
| @@ -1,6 +1,6 @@ | |||
| --- | |||
| uid: Guides.Concepts.Logging | |||
| title: Logging | |||
| title: Logging Events/Data | |||
| --- | |||
| # Logging in Discord.Net | |||
| @@ -5,14 +5,15 @@ title: Start making a bot | |||
| # Making a Ping-Pong bot | |||
| One of the first steps to getting started with the Discord API is to | |||
| write a basic ping-pong bot. We will expand on this to create more | |||
| diverse commands later, but for now, it is a good starting point. | |||
| One of ways to get started with the Discord API is to write a basic | |||
| ping-pong bot. This bot will respond to a simple command "ping." | |||
| We will expand on this to create more diverse commands later, but for | |||
| now, it is a good starting point. | |||
| ## Creating a Discord Bot | |||
| Before you can begin writing your bot, it is necessary to create a bot | |||
| account on Discord. | |||
| Before writing your bot, it is necessary to create a bot account via the | |||
| Discord Applications Portal first. | |||
| 1. Visit the [Discord Applications Portal]. | |||
| 2. Create a New Application. | |||
| @@ -37,12 +38,14 @@ 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]. | |||
| 2. Retrieve the app's **Client ID**. | |||
| 2. Retrieve the application's **Client ID**. | |||
|  | |||
| 3. Create an OAuth2 authorization URL | |||
| `https://discordapp.com/oauth2/authorize?client_id=<CLIENT ID>&scope=bot` | |||
| - `https://discordapp.com/oauth2/authorize?client_id=<CLIENT ID>&scope=bot` | |||
| 4. Open the authorization URL in your browser. | |||
| 5. Select a server. | |||
| 6. Click on authorize. | |||
| @@ -56,36 +59,38 @@ through the OAuth2 flow. | |||
| ## Connecting to Discord | |||
| If you have not already created a project and installed Discord.Net, | |||
| do that now. (see the [Installing](xref:Guides.GettingStarted.Installation) section) | |||
| do that now. | |||
| For more information, see @Guides.GettingStarted.Installation. | |||
| ### Async | |||
| Discord.Net uses .NET's [Task-based Asynchronous Pattern (TAP)] | |||
| extensively - nearly every operation is asynchronous. | |||
| extensively - nearly every operation is asynchronous. It is highly | |||
| recommended that these operations are awaited in a | |||
| properly established async context whenever possible. | |||
| 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. | |||
| To establish an async context, we will be creating an async main method | |||
| in your console application, and rewriting the static main method to | |||
| invoke the new async main. | |||
| To do so, we will be creating an async main in your console | |||
| application, and rewriting the static main method to invoke the new | |||
| async main. | |||
| [!code-csharp[Async Context](samples/intro/async-context.cs)] | |||
| [!code-csharp[Async Context](samples/first-bot/async-context.cs)] | |||
| As a result of this, your program will now start and immediately | |||
| 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 | |||
| to Discord later on without having to worry about setting up the | |||
| correct async implementation. | |||
| > [!TIP] | |||
| > [!WARNING] | |||
| > 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 | |||
| > 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. | |||
| > 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 | |||
| @@ -100,57 +105,61 @@ parameter. See the [API Documentation] for this event. | |||
| If you are using your own logging framework, this is where you would | |||
| invoke it. For the sake of simplicity, we will only be logging to | |||
| the Console. | |||
| the console. | |||
| You may learn more about this concept in @Guides.Concepts.Logging. | |||
| [!code-csharp[Async Context](samples/intro/logging.cs)] | |||
| [!code-csharp[Async Context](samples/first-bot/logging.cs)] | |||
| [API Documentation]: xref: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 | |||
| entities. See the [terminology](xref:Guides.GettingStarted.Terminology) if you're unsure of | |||
| the differences. | |||
| Finally, we can create a new connection to Discord. | |||
| Since we are writing a bot, we will be using a [DiscordSocketClient] | |||
| along with socket entities. See @Guides.GettingStarted.Terminology | |||
| if you are unsure of the differences. | |||
| To do so, create an instance of [DiscordSocketClient] in your async | |||
| main, passing in a configuration object only if necessary. For most | |||
| To establish a new connection, we will create an instance of | |||
| [DiscordSocketClient] in the new async main. You may pass in an | |||
| optional @Discord.WebSocket.DiscordSocketConfig if necessary. For most | |||
| users, the default will work fine. | |||
| 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. | |||
| log handler that we had just created. Events in Discord.Net work | |||
| similarly to any other events in C#. | |||
| Next, you will need to "login to Discord" with the `LoginAsync` | |||
| method. | |||
| Next, you will need to "login to Discord" with the [LoginAsync] | |||
| method with the application's "token." | |||
| 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]). | |||
| > [!NOTE] | |||
| > Pay attention to what you are copying from the developer portal! | |||
| > A token is not the same as the application's "client secret." | |||
|  | |||
| > [!IMPORTANT] | |||
| > Your bot's token can be used to gain total access to your bot, so | |||
| > **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 | |||
| > to store this token in an external source if you plan on distributing | |||
| > the source code for your bot. | |||
| We may now invoke the client's `StartAsync` method, which will | |||
| We may now invoke the client's [StartAsync] method, which will | |||
| start connection/reconnection logic. It is important to note that | |||
| **this method returns as soon as connection logic has been started!** | |||
| **this method will return as soon as connection logic has been started!** | |||
| Any methods that rely on the client's state should go in an event | |||
| handler. | |||
| handler. This means that you should **not** directly be interacting with | |||
| the client before it is fully ready. | |||
| 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 | |||
| the console. | |||
| when running the application. To do this, we can await an infinite delay | |||
| or any other blocking method, such as reading from the console. | |||
| The following lines can now be added: | |||
| [!code-csharp[Create client](samples/intro/client.cs)] | |||
| [!code-csharp[Create client](samples/first-bot/client.cs)] | |||
| At this point, feel free to start your program and see your bot come | |||
| online in Discord. | |||
| @@ -162,6 +171,8 @@ online in Discord. | |||
| > for how to fix this. | |||
| [DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient | |||
| [LoginAsync]: xref:Discord.Rest.BaseDiscordClient.LoginAsync* | |||
| [StartAsync]: xref:Discord.WebSocket.DiscordSocketClient.StartAsync* | |||
| [installation guide]: xref:Guides.GettingStarted.Installation#installing-on-net-standard-11 | |||
| ### Handling a 'ping' | |||
| @@ -169,18 +180,18 @@ online in Discord. | |||
| > [!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. | |||
| > in the [Command Guide](xref:Guides.Commands.Intro) 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!". | |||
| Now that we have learned to open a connection to Discord, we can | |||
| begin handling messages that the users are sending. To start out, our | |||
| bot will listen for any message whose content is equal to `!ping` and | |||
| will respond back with "Pong!". | |||
| 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 | |||
| `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`. | |||
| @@ -189,45 +200,49 @@ 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 comes from - "Pong!". To | |||
| find the channel, look for the `Channel` property on the message | |||
| Inside the branch of this condition, we will want to send a message, | |||
| `Pong!`, back to the channel from which the message comes from. 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!". | |||
| channel object is of type [ISocketMessageChannel], we can invoke the | |||
| [SendMessageAsync] instance method. For the message content, send back | |||
| a string, "Pong!". | |||
| You should have now added the following lines, | |||
| [!code-csharp[Message](samples/intro/message.cs)] | |||
| [!code-csharp[Message](samples/first-bot/message.cs)] | |||
| Now your first bot is complete. You may continue to add on to this | |||
| Now that 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]. | |||
| > [!NOTE] | |||
| > For your reference, you may view the [completed program]. | |||
| [MessageReceived]: xref:Discord.WebSocket.BaseSocketClient.MessageReceived | |||
| [SocketMessage]: xref:Discord.WebSocket.SocketMessage | |||
| [SocketMessageChannel]: xref:Discord.WebSocket.ISocketMessageChannel | |||
| [completed program]: samples/intro/complete.cs | |||
| [Command Guide]: xref:Guides.Commands.Intro | |||
| [SendMessageAsync]: xref:Discord.WebSocket.ISocketMessageChannel.SendMessageAsync* | |||
| [completed program]: samples/first-bot/complete.cs | |||
| # Building a bot with commands | |||
| This section will show you how to write a program that is ready for | |||
| [Commands](xref:Guides.Commands.Intro). Note that we will not be | |||
| explaining _how_ to write Commands or Services, it will only be | |||
| covering the general structure. | |||
| @Guides.Commands.Intro will guide you through how to setup a program | |||
| that is ready for [CommandService], a service that is ready for | |||
| advanced command usage. | |||
| For reference, view an [annotated example] of this structure. | |||
| [annotated example]: samples/intro/structure.cs | |||
| [annotated example]: samples/first-bot/structure.cs | |||
| It is important to know that the recommended design pattern of bots | |||
| should be to separate the program (initialization and command handler), | |||
| the modules (handle commands), and the services (persistent storage, | |||
| pure functions, data manipulation). | |||
| should be to separate... | |||
| 1. the program (initialization and command handler) | |||
| 2. the modules (handle commands) | |||
| 3. the services (persistent storage, pure functions, data manipulation) | |||
| [CommandService]: xref:Discord.Commands.CommandService | |||
| @@ -3,19 +3,22 @@ uid: Guides.GettingStarted.Installation | |||
| 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 Installation | |||
| Optionally, you may compile from source and install yourself. | |||
| Discord.Net is distributed through the NuGet package manager, so it is | |||
| recommended for you to install the library that way. | |||
| # Supported Platforms | |||
| Alternatively, you may compile from the source and install the library | |||
| 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]. | |||
| Since Discord.Net is built on the .NET Standard, it is also | |||
| recommended to create applications using [.NET Core], though not | |||
| Since Discord.Net is built on top of .NET Standard, it is also | |||
| recommended to create applications using [.NET Core], although it is not | |||
| required. When using .NET Framework, it is suggested to target | |||
| `.NET Framework 4.6.1` or higher. | |||
| @@ -23,13 +26,13 @@ required. When using .NET Framework, it is suggested to target | |||
| [.NET Core]: https://docs.microsoft.com/en-us/dotnet/articles/core/ | |||
| [additional steps]: #installing-on-net-standard-11 | |||
| # Installing with NuGet | |||
| ## Installing with NuGet | |||
| Release builds of Discord.Net will be published to the | |||
| [official NuGet feed]. | |||
| Development builds of Discord.Net, as well as add-ons are published to | |||
| our development [MyGet feed]. | |||
| Development builds of Discord.Net, as well as add-ons, will be | |||
| published to our [MyGet feed]. | |||
| Direct feed link: `https://www.myget.org/F/discord-net/api/v3/index.json` | |||
| @@ -41,7 +44,7 @@ Not sure how to add a direct feed? See how [with Visual Studio] or | |||
| [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](#tab/vs-install) | |||
| ### [Using Visual Studio](#tab/vs-install) | |||
| > [!TIP] | |||
| > Don't forget to change your package source if you're installing from | |||
| @@ -49,8 +52,8 @@ Not sure how to add a direct feed? See how [with Visual Studio] or | |||
| > 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 | |||
| 1. Create a new solution for your bot. | |||
| 2. In the Solution Explorer, find the "Dependencies" element under your | |||
| bot's project. | |||
| 3. Right click on "Dependencies", and select "Manage NuGet packages." | |||
|  | |||
| @@ -58,7 +61,7 @@ Not sure how to add a direct feed? See how [with Visual Studio] or | |||
| 5. Install the `Discord.Net` package. | |||
|  | |||
| ## [Using JetBrains Rider](#tab/rider-install) | |||
| ### [Using JetBrains Rider](#tab/rider-install) | |||
| > [!TIP] | |||
| > Make sure to check the "Prerelease" box if installing a dev build! | |||
| @@ -72,7 +75,7 @@ Not sure how to add a direct feed? See how [with Visual Studio] or | |||
| 4. Install by adding the package to your project. | |||
|  | |||
| ## [Using Visual Studio Code](#tab/vs-code) | |||
| ### [Using Visual Studio Code](#tab/vs-code) | |||
| > [!TIP] | |||
| > Don't forget to add the package source to a [NuGet.Config file] if | |||
| @@ -81,11 +84,11 @@ Not sure how to add a direct feed? See how [with Visual Studio] or | |||
| 1. Create a new project for your bot. | |||
| 2. Add `Discord.Net` to your .csproj. | |||
| [!code[Sample .csproj](samples/project.csproj)] | |||
| [!code[Sample .csproj](samples/project.xml)] | |||
| [NuGet.Config file]: #configuring-nuget-without-visual-studio | |||
| ## [Using dotnet CLI](#tab/dotnet-cli) | |||
| ### [Using dotnet CLI](#tab/dotnet-cli) | |||
| > [!TIP] | |||
| > Don't forget to add the package source to a [NuGet.Config file] if | |||
| @@ -98,11 +101,11 @@ Not sure how to add a direct feed? See how [with Visual Studio] or | |||
| *** | |||
| # Compiling from Source | |||
| ## Compiling from Source | |||
| In order to compile Discord.Net, you require the following: | |||
| In order to compile Discord.Net, you will need the following: | |||
| ## Using Visual Studio | |||
| ### Using Visual Studio | |||
| - [Visual Studio 2017](https://www.visualstudio.com/) | |||
| - [.NET Core SDK] | |||
| @@ -110,31 +113,32 @@ In order to compile Discord.Net, you require the following: | |||
| The .NET Core and Docker (Preview) workload is required during Visual | |||
| Studio installation. | |||
| ## Using Command Line | |||
| ### Using Command Line | |||
| - [.NET Core SDK] | |||
| [.NET Core SDK]: https://www.microsoft.com/net/download/ | |||
| # Additional Information | |||
| ## Additional Information | |||
| ## Installing on .NET Standard 1.1 | |||
| ### 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 | |||
| 1.1 or 1.2, the built-in WebSocket and UDP provider will not work. For | |||
| applications which utilize a WebSocket connection to Discord, such as | |||
| WebSocket or RPC, third-party provider packages will need to be | |||
| installed and configured. | |||
| First, install the following packages through NuGet, or compile | |||
| > [!NOTE] | |||
| > `Discord.Net.Providers.UDPClient` is _only_ required if your | |||
| > bot will be utilizing voice chat. | |||
| First, install the following packages through NuGet, or compile them | |||
| 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. | |||
| Next, you will need to configure your [DiscordSocketClient] to use | |||
| these custom providers over the default ones. | |||
| @@ -147,16 +151,16 @@ are passing into your client. | |||
| [DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient | |||
| [DiscordSocketConfig]: xref:Discord.WebSocket.DiscordSocketConfig | |||
| ## Configuring NuGet without Visual Studio | |||
| ### 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. | |||
| 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 is located. | |||
| Paste the following snippets into this configuration file, adding any | |||
| additional feeds as necessary. | |||
| additional feeds if necessary. | |||
| [!code[NuGet Configuration](samples/nuget.config)] | |||
| @@ -0,0 +1,9 @@ | |||
| public class Program | |||
| { | |||
| public static void Main(string[] args) | |||
| => new Program().MainAsync().GetAwaiter().GetResult(); | |||
| public async Task MainAsync() | |||
| { | |||
| } | |||
| } | |||
| @@ -1,17 +1,17 @@ | |||
| // Program.cs | |||
| using Discord.WebSocket; | |||
| // ... | |||
| private DiscordSocketClient _client; | |||
| public async Task MainAsync() | |||
| { | |||
| _client = new DiscordSocketClient(); | |||
| _client.Log += Log; | |||
| string token = "abcdefg..."; // Remember to keep this private! | |||
| // Remember to keep this private or to read this | |||
| // from an external source! | |||
| string token = "abcdefg..."; | |||
| await _client.LoginAsync(TokenType.Bot, token); | |||
| await _client.StartAsync(); | |||
| // Block this task until the program is closed. | |||
| await Task.Delay(-1); | |||
| } | |||
| } | |||
| @@ -0,0 +1,38 @@ | |||
| public class Program | |||
| { | |||
| private DiscordSocketClient _client; | |||
| public static void Main(string[] args) | |||
| => new Program().MainAsync().GetAwaiter().GetResult(); | |||
| public async Task MainAsync() | |||
| { | |||
| _client = new DiscordSocketClient(); | |||
| _client.Log += Log; | |||
| _client.MessageReceived += MessageReceivedAsync; | |||
| // Remember to keep this private or to read this | |||
| // from an external source! | |||
| string token = "abcdefg..."; | |||
| await _client.LoginAsync(TokenType.Bot, token); | |||
| await _client.StartAsync(); | |||
| // Block this task until the program is closed. | |||
| await Task.Delay(-1); | |||
| } | |||
| private async Task MessageReceivedAsync(SocketMessage message) | |||
| { | |||
| if (message.Content == "!ping") | |||
| { | |||
| await message.Channel.SendMessageAsync("Pong!"); | |||
| } | |||
| } | |||
| private Task Log(LogMessage msg) | |||
| { | |||
| Console.WriteLine(msg.ToString()); | |||
| return Task.CompletedTask; | |||
| } | |||
| } | |||
| @@ -0,0 +1,5 @@ | |||
| private Task Log(LogMessage msg) | |||
| { | |||
| Console.WriteLine(msg.ToString()); | |||
| return Task.CompletedTask; | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| public async Task MainAsync() | |||
| { | |||
| // client.Log ... | |||
| // ... | |||
| _client.MessageReceived += MessageReceived; | |||
| // ... | |||
| } | |||
| @@ -1,15 +0,0 @@ | |||
| using System; | |||
| using System.Threading.Tasks; | |||
| namespace MyBot | |||
| { | |||
| public class Program | |||
| { | |||
| public static void Main(string[] args) | |||
| => new Program().MainAsync().GetAwaiter().GetResult(); | |||
| public async Task MainAsync() | |||
| { | |||
| } | |||
| } | |||
| } | |||
| @@ -1,44 +0,0 @@ | |||
| using Discord; | |||
| using Discord.WebSocket; | |||
| using System; | |||
| using System.Threading.Tasks; | |||
| namespace MyBot | |||
| { | |||
| public class Program | |||
| { | |||
| private DiscordSocketClient _client; | |||
| public static void Main(string[] args) | |||
| => new Program().MainAsync().GetAwaiter().GetResult(); | |||
| public async Task MainAsync() | |||
| { | |||
| _client = new DiscordSocketClient(); | |||
| _client.Log += Log; | |||
| _client.MessageReceived += MessageReceived; | |||
| string token = "abcdefg..."; // Remember to keep this private! | |||
| await _client.LoginAsync(TokenType.Bot, token); | |||
| await _client.StartAsync(); | |||
| // Block this task until the program is closed. | |||
| await Task.Delay(-1); | |||
| } | |||
| private async Task MessageReceived(SocketMessage message) | |||
| { | |||
| if (message.Content == "!ping") | |||
| { | |||
| await message.Channel.SendMessageAsync("Pong!"); | |||
| } | |||
| } | |||
| private Task Log(LogMessage msg) | |||
| { | |||
| Console.WriteLine(msg.ToString()); | |||
| return Task.CompletedTask; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,22 +0,0 @@ | |||
| using Discord; | |||
| using System; | |||
| using System.Threading.Tasks; | |||
| namespace MyBot | |||
| { | |||
| public class Program | |||
| { | |||
| public static void Main(string[] args) | |||
| => new Program().MainAsync().GetAwaiter().GetResult(); | |||
| public async Task MainAsync() | |||
| { | |||
| } | |||
| private Task Log(LogMessage msg) | |||
| { | |||
| Console.WriteLine(msg.ToString()); | |||
| return Task.CompletedTask; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,4 +1,5 @@ | |||
| <Project Sdk="Microsoft.NET.Sdk"> | |||
| <!-- | |||
| The following may differ depending on the latest version of | |||
| .NET Core Framework or Discord.Net. | |||
| @@ -26,7 +26,7 @@ Here are some examples: | |||
| > It is not meant to be something that will work out of the box. | |||
| [Official template]: https://github.com/foxbot/DiscordBotBase/tree/csharp/src/DiscordBot | |||
| [Official quick start guide]: https://github.com/RogueException/Discord.Net/blob/dev/docs/guides/getting_started/samples/intro/structure.cs | |||
| [Official quick start guide]: https://github.com/RogueException/Discord.Net/blob/dev/docs/guides/getting_started/samples/first-bot/structure.cs | |||
| [Task-based Asynchronous Pattern]: https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap | |||
| [polymorphism]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/polymorphism | |||
| [interface]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/ | |||
| @@ -20,8 +20,12 @@ | |||
| topicUid: Guides.Concepts.Entities | |||
| - name: The Command Service | |||
| items: | |||
| - name: Introduction to Command Service | |||
| - name: Introduction | |||
| topicUid: Guides.Commands.Intro | |||
| - name: TypeReaders | |||
| topicUid: Guides.Commands.TypeReaders | |||
| - name: Preconditions | |||
| topicUid: Guides.Commands.Preconditions | |||
| - name: Dependency Injection | |||
| topicUid: Guides.Commands.DI | |||
| - name: Post-execution Handling | |||