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/Discord.Net.targets b/Discord.Net.targets
index 95eccd790..3f623c619 100644
--- a/Discord.Net.targets
+++ b/Discord.Net.targets
@@ -1,7 +1,7 @@
- 2.0.0-alpha
-
+ 2.0.0
+ beta
RogueException
discord;discordapp
https://github.com/RogueException/Discord.Net
diff --git a/README.md b/README.md
index 2b58d4579..bd0ef20c7 100644
--- a/README.md
+++ b/README.md
@@ -2,11 +2,11 @@
[](https://www.nuget.org/packages/Discord.Net)
[](https://www.myget.org/feed/Packages/discord-net)
[](https://ci.appveyor.com/project/RogueException/discord-net/branch/dev)
-[](https://discord.gg/0SBTUU1wZTVjAMPx)
+[](https://discord.gg/jkrBmQR)
An unofficial .NET API Wrapper for the Discord client (http://discordapp.com).
-Check out the [documentation](https://discord.foxbot.me/docs/) or join the [Discord API Chat](https://discord.gg/0SBTUU1wZTVjAMPx).
+Check out the [documentation](https://discord.foxbot.me/docs/) or join the [Discord API Chat](https://discord.gg/jkrBmQR).
## Installation
### Stable (NuGet)
diff --git a/appveyor.yml b/appveyor.yml
index d94e2ad68..3bf70c09c 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -34,7 +34,7 @@ after_build:
if ($Env:APPVEYOR_REPO_TAG -eq "true") {
nuget pack src\Discord.Net\Discord.Net.nuspec -OutputDirectory "artifacts" -properties suffix=""
} else {
- nuget pack src\Discord.Net\Discord.Net.nuspec -OutputDirectory "artifacts" -properties suffix="-build-$Env:BUILD"
+ nuget pack src\Discord.Net\Discord.Net.nuspec -OutputDirectory "artifacts" -properties suffix="-$Env:BUILD"
}
- ps: Get-ChildItem artifacts\*.nupkg | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
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'
-
-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."
+
+4. In the "Browse" tab, search for `Discord.Net`.
+5. Install the `Discord.Net` package.

## Using JetBrains Rider
-1. Create a new solution for your bot
-2. Open the NuGet window (Tools > NuGet > Manage NuGet packages for Solution)
-
-3. In the 'Packages' tab, search for 'Discord.Net'
-
-
> [!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).
+
+3. In the "Packages" tab, search for `Discord.Net`.
+
+4. Install by adding the package to your project.

## 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..db086df21 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
-
-5. In the application review page, click **Create a Bot User**
-
-6. Confirm the popup
-7. If this bot will be public, check 'Public Bot'.
-**Do not tick any other options!**
+4. Create the Application.
+
+ 
+
+5. In the application review page, click **Create a Bot User**.
+
+ 
+
+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**.
-
-
-
+
+ 
+
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.
+
+ 
-6. Click authorize
-
-
## 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 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
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]).
+

>[!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/samples/intro/message.cs b/docs/guides/getting_started/samples/intro/message.cs
index d3cda46e5..d6fd90778 100644
--- a/docs/guides/getting_started/samples/intro/message.cs
+++ b/docs/guides/getting_started/samples/intro/message.cs
@@ -1,7 +1,7 @@
public async Task MainAsync()
{
// client.Log ...
- client.MessageReceived += MessageReceived;
+ _client.MessageReceived += MessageReceived;
// ...
}
@@ -11,4 +11,4 @@ private async Task MessageReceived(SocketMessage message)
{
await message.Channel.SendMessageAsync("Pong!");
}
-}
\ No newline at end of file
+}
diff --git a/docs/guides/getting_started/samples/intro/structure.cs b/docs/guides/getting_started/samples/intro/structure.cs
index 00ce7a6c9..bdfc12b67 100644
--- a/docs/guides/getting_started/samples/intro/structure.cs
+++ b/docs/guides/getting_started/samples/intro/structure.cs
@@ -1,5 +1,6 @@
using System;
using System.Reflection;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Discord;
@@ -8,12 +9,6 @@ using Discord.WebSocket;
class Program
{
- private readonly DiscordSocketClient _client;
-
- // Keep the CommandService and IServiceCollection around for use with commands.
- private readonly IServiceCollection _map = new ServiceCollection();
- private readonly CommandService _commands = new CommandService();
-
// Program entry point
static void Main(string[] args)
{
@@ -22,6 +17,13 @@ class Program
new Program().MainAsync().GetAwaiter().GetResult();
}
+ private readonly DiscordSocketClient _client;
+
+ // Keep the CommandService and IServiceCollection around for use with commands.
+ // These two types require you install the Discord.Net.Commands package.
+ private readonly IServiceCollection _map = new ServiceCollection();
+ private readonly CommandService _commands = new CommandService();
+
private Program()
{
_client = new DiscordSocketClient(new DiscordSocketConfig
@@ -48,7 +50,6 @@ class Program
// that ask for a Func.
private static Task Logger(LogMessage message)
{
- var cc = Console.ForegroundColor;
switch (message.Severity)
{
case LogSeverity.Critical:
@@ -66,8 +67,8 @@ class Program
Console.ForegroundColor = ConsoleColor.DarkGray;
break;
}
- Console.WriteLine($"{DateTime.Now,-19} [{message.Severity,8}] {message.Source}: {message.Message}");
- Console.ForegroundColor = cc;
+ Console.WriteLine($"{DateTime.Now,-19} [{message.Severity,8}] {message.Source}: {message.Message} {message.Exception}");
+ Console.ResetColor();
// If you get an error saying 'CompletedTask' doesn't exist,
// your project is targeting .NET 4.5.2 or lower. You'll need
@@ -80,7 +81,7 @@ class Program
private async Task MainAsync()
{
- // Centralize the logic for commands into a seperate method.
+ // Centralize the logic for commands into a separate method.
await InitCommands();
// Login and connect.
@@ -88,7 +89,7 @@ class Program
await _client.StartAsync();
// Wait infinitely so your bot actually stays connected.
- await Task.Delay(-1);
+ await Task.Delay(Timeout.Infinite);
}
private IServiceProvider _services;
@@ -105,10 +106,11 @@ class Program
_services = _map.BuildServiceProvider();
// Either search the program and add all Module classes that can be found.
- // Module classes *must* be marked 'public' or they will be ignored.
+ // Module classes MUST be marked 'public' or they will be ignored.
await _commands.AddModulesAsync(Assembly.GetEntryAssembly());
// Or add Modules manually if you prefer to be a little more explicit:
await _commands.AddModuleAsync();
+ // Note that the first one is 'Modules' (plural) and the second is 'Module' (singular).
// Subscribe a handler to see if a message invokes a command.
_client.MessageReceived += HandleCommandAsync;
@@ -120,6 +122,11 @@ class Program
var msg = arg as SocketUserMessage;
if (msg == null) return;
+ // We don't want the bot to respond to itself or other bots.
+ // NOTE: Selfbots should invert this first check and remove the second
+ // as they should ONLY be allowed to respond to messages from the same account.
+ if (msg.Author.Id == _client.CurrentUser.Id || msg.Author.IsBot) return;
+
// Create a number to track where the prefix ends and the command begins
int pos = 0;
// Replace the '!' with whatever character
@@ -132,7 +139,7 @@ class Program
var context = new SocketCommandContext(_client, msg);
// Execute the command. (result does not indicate a return value,
- // rather an object stating if the command executed succesfully).
+ // rather an object stating if the command executed successfully).
var result = await _commands.ExecuteAsync(context, pos, _services);
// Uncomment the following lines if you want the bot
diff --git a/docs/guides/getting_started/samples/project.csproj b/docs/guides/getting_started/samples/project.csproj
index 8daf71877..feb0b0c40 100644
--- a/docs/guides/getting_started/samples/project.csproj
+++ b/docs/guides/getting_started/samples/project.csproj
@@ -7,7 +7,7 @@
-
+
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/samples/audio_create_ffmpeg.cs b/docs/guides/voice/samples/audio_create_ffmpeg.cs
index e24af088b..dda560efe 100644
--- a/docs/guides/voice/samples/audio_create_ffmpeg.cs
+++ b/docs/guides/voice/samples/audio_create_ffmpeg.cs
@@ -1,11 +1,10 @@
private Process CreateStream(string path)
{
- var ffmpeg = new ProcessStartInfo
+ return Process.Start(new ProcessStartInfo
{
FileName = "ffmpeg",
- Arguments = $"-i {path} -ac 2 -f s16le -ar 48000 pipe:1",
+ Arguments = $"-hide_banner -loglevel panic -i \"{path}\" -ac 2 -f s16le -ar 48000 pipe:1",
UseShellExecute = false,
RedirectStandardOutput = true,
- };
- return Process.Start(ffmpeg);
-}
\ No newline at end of file
+ });
+}
diff --git a/docs/guides/voice/samples/audio_ffmpeg.cs b/docs/guides/voice/samples/audio_ffmpeg.cs
index b9430ac11..d36fbbc20 100644
--- a/docs/guides/voice/samples/audio_ffmpeg.cs
+++ b/docs/guides/voice/samples/audio_ffmpeg.cs
@@ -1,9 +1,11 @@
private async Task SendAsync(IAudioClient client, string path)
{
// Create FFmpeg using the previous example
- var ffmpeg = CreateStream(path);
- var output = ffmpeg.StandardOutput.BaseStream;
- var discord = client.CreatePCMStream(AudioApplication.Mixed);
- await output.CopyToAsync(discord);
- await discord.FlushAsync();
+ using (var ffmpeg = CreateStream(path))
+ using (var output = ffmpeg.StandardOutput.BaseStream)
+ using (var discord = client.CreatePCMStream(AudioApplication.Mixed))
+ {
+ try { await output.CopyToAsync(discord); }
+ finally { await discord.FlushAsync(); }
+ }
}
diff --git a/docs/guides/voice/samples/joining_audio.cs b/docs/guides/voice/samples/joining_audio.cs
index 0cc36978a..4cec67540 100644
--- a/docs/guides/voice/samples/joining_audio.cs
+++ b/docs/guides/voice/samples/joining_audio.cs
@@ -7,4 +7,4 @@ public async Task JoinChannel(IVoiceChannel channel = null)
// For the next step with transmitting audio, you would want to pass this Audio Client in to a service.
var audioClient = await channel.ConnectAsync();
-}
\ 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
diff --git a/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs b/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs
index 49dae6080..209822583 100644
--- a/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs
+++ b/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs
@@ -7,6 +7,6 @@ namespace Discord.Commands
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true, Inherited = true)]
public abstract class ParameterPreconditionAttribute : Attribute
{
- public abstract Task CheckPermissions(ICommandContext context, ParameterInfo parameter, object value, IServiceProvider services);
+ public abstract Task CheckPermissionsAsync(ICommandContext context, ParameterInfo parameter, object value, IServiceProvider services);
}
}
\ No newline at end of file
diff --git a/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs b/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs
index 3727510d9..367adebf0 100644
--- a/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs
+++ b/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs
@@ -8,11 +8,11 @@ namespace Discord.Commands
{
///
/// Specify a group that this precondition belongs to. Preconditions of the same group require only one
- /// of the preconditions to pass in order to be successful (A || B). Specifying =
+ /// of the preconditions to pass in order to be successful (A || B). Specifying =
/// or not at all will require *all* preconditions to pass, just like normal (A && B).
///
public string Group { get; set; } = null;
- public abstract Task CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services);
+ public abstract Task CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services);
}
}
diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs
index b2cd3811c..104252799 100644
--- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs
+++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs
@@ -41,7 +41,7 @@ namespace Discord.Commands
GuildPermission = null;
}
- public override async Task CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services)
+ public override async Task CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
IGuildUser guildUser = null;
if (context.Guild != null)
@@ -57,13 +57,11 @@ namespace Discord.Commands
if (ChannelPermission.HasValue)
{
- var guildChannel = context.Channel as IGuildChannel;
-
ChannelPermissions perms;
- if (guildChannel != null)
+ if (context.Channel is IGuildChannel guildChannel)
perms = guildUser.GetPermissions(guildChannel);
else
- perms = ChannelPermissions.All(guildChannel);
+ perms = ChannelPermissions.All(context.Channel);
if (!perms.Has(ChannelPermission.Value))
return PreconditionResult.FromError($"Bot requires channel permission {ChannelPermission.Value}");
diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs
index a221eb4a9..5fa0fb1b9 100644
--- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs
+++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs
@@ -38,7 +38,7 @@ namespace Discord.Commands
Contexts = contexts;
}
- public override Task CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services)
+ public override Task CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
bool isValid = false;
diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireNsfwAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireNsfwAttribute.cs
index b3cf25365..c8e3bfa82 100644
--- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireNsfwAttribute.cs
+++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireNsfwAttribute.cs
@@ -9,7 +9,7 @@ namespace Discord.Commands
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class RequireNsfwAttribute : PreconditionAttribute
{
- public override Task CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services)
+ public override Task CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
if (context.Channel is ITextChannel text && text.IsNsfw)
return Task.FromResult(PreconditionResult.FromSuccess());
diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs
index 0852ce39c..e370aeec4 100644
--- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs
+++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs
@@ -1,4 +1,5 @@
-using System;
+#pragma warning disable CS0618
+using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
@@ -11,7 +12,7 @@ namespace Discord.Commands
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class RequireOwnerAttribute : PreconditionAttribute
{
- public override async Task CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services)
+ public override async Task CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
switch (context.Client.TokenType)
{
diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs
index f5e3a9fc5..14121f35b 100644
--- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs
+++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs
@@ -42,7 +42,7 @@ namespace Discord.Commands
GuildPermission = null;
}
- public override Task CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services)
+ public override Task CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
var guildUser = context.User as IGuildUser;
@@ -56,13 +56,11 @@ namespace Discord.Commands
if (ChannelPermission.HasValue)
{
- var guildChannel = context.Channel as IGuildChannel;
-
ChannelPermissions perms;
- if (guildChannel != null)
+ if (context.Channel is IGuildChannel guildChannel)
perms = guildUser.GetPermissions(guildChannel);
else
- perms = ChannelPermissions.All(guildChannel);
+ perms = ChannelPermissions.All(context.Channel);
if (!perms.Has(ChannelPermission.Value))
return Task.FromResult(PreconditionResult.FromError($"User requires channel permission {ChannelPermission.Value}"));
diff --git a/src/Discord.Net.Commands/CommandParser.cs b/src/Discord.Net.Commands/CommandParser.cs
index 394f8589d..28e36d54d 100644
--- a/src/Discord.Net.Commands/CommandParser.cs
+++ b/src/Discord.Net.Commands/CommandParser.cs
@@ -14,7 +14,7 @@ namespace Discord.Commands
QuotedParameter
}
- public static async Task ParseArgs(CommandInfo command, ICommandContext context, IServiceProvider services, string input, int startPos)
+ public static async Task ParseArgsAsync(CommandInfo command, ICommandContext context, IServiceProvider services, string input, int startPos)
{
ParameterInfo curParam = null;
StringBuilder argBuilder = new StringBuilder(input.Length);
@@ -111,7 +111,7 @@ namespace Discord.Commands
if (curParam == null)
return ParseResult.FromError(CommandError.BadArgCount, "The input text has too many parameters.");
- var typeReaderResult = await curParam.Parse(context, argString, services).ConfigureAwait(false);
+ var typeReaderResult = await curParam.ParseAsync(context, argString, services).ConfigureAwait(false);
if (!typeReaderResult.IsSuccess && typeReaderResult.Error != CommandError.MultipleMatches)
return ParseResult.FromError(typeReaderResult);
@@ -134,7 +134,7 @@ namespace Discord.Commands
if (curParam != null && curParam.IsRemainder)
{
- var typeReaderResult = await curParam.Parse(context, argBuilder.ToString(), services).ConfigureAwait(false);
+ var typeReaderResult = await curParam.ParseAsync(context, argBuilder.ToString(), services).ConfigureAwait(false);
if (!typeReaderResult.IsSuccess)
return ParseResult.FromError(typeReaderResult);
argList.Add(typeReaderResult);
diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs
index 5dcd50cd8..b53b0248c 100644
--- a/src/Discord.Net.Commands/CommandServiceConfig.cs
+++ b/src/Discord.Net.Commands/CommandServiceConfig.cs
@@ -2,17 +2,18 @@
{
public class CommandServiceConfig
{
- /// The default RunMode commands should have, if one is not specified on the Command attribute or builder.
+ /// Gets or sets the default RunMode commands should have, if one is not specified on the Command attribute or builder.
public RunMode DefaultRunMode { get; set; } = RunMode.Sync;
public char SeparatorChar { get; set; } = ' ';
- /// Should commands be case-sensitive?
+
+ /// Determines whether commands should be case-sensitive.
public bool CaseSensitiveCommands { get; set; } = false;
/// Gets or sets the minimum log level severity that will be sent to the Log event.
public LogSeverity LogLevel { get; set; } = LogSeverity.Info;
- /// Gets or sets whether RunMode.Sync commands should push exceptions up to the caller.
+ /// Determines whether RunMode.Sync commands should push exceptions up to the caller.
public bool ThrowOnError { get; set; } = true;
}
-}
\ No newline at end of file
+}
diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs
index c94be525f..6bb621f94 100644
--- a/src/Discord.Net.Commands/Info/CommandInfo.cs
+++ b/src/Discord.Net.Commands/Info/CommandInfo.cs
@@ -78,7 +78,7 @@ namespace Discord.Commands
{
foreach (PreconditionAttribute precondition in preconditionGroup)
{
- var result = await precondition.CheckPermissions(context, this, services).ConfigureAwait(false);
+ var result = await precondition.CheckPermissionsAsync(context, this, services).ConfigureAwait(false);
if (!result.IsSuccess)
return result;
}
@@ -87,7 +87,7 @@ namespace Discord.Commands
{
var results = new List();
foreach (PreconditionAttribute precondition in preconditionGroup)
- results.Add(await precondition.CheckPermissions(context, this, services).ConfigureAwait(false));
+ results.Add(await precondition.CheckPermissionsAsync(context, this, services).ConfigureAwait(false));
if (!results.Any(p => p.IsSuccess))
return PreconditionGroupResult.FromError($"{type} precondition group {preconditionGroup.Key} failed.", results);
@@ -117,7 +117,7 @@ namespace Discord.Commands
return ParseResult.FromError(preconditionResult);
string input = searchResult.Text.Substring(startIndex);
- return await CommandParser.ParseArgs(this, context, services, input, 0).ConfigureAwait(false);
+ return await CommandParser.ParseArgsAsync(this, context, services, input, 0).ConfigureAwait(false);
}
public Task ExecuteAsync(ICommandContext context, ParseResult parseResult, IServiceProvider services)
@@ -163,11 +163,11 @@ namespace Discord.Commands
switch (RunMode)
{
case RunMode.Sync: //Always sync
- return await ExecuteAsyncInternal(context, args, services).ConfigureAwait(false);
+ return await ExecuteAsyncInternalAsync(context, args, services).ConfigureAwait(false);
case RunMode.Async: //Always async
var t2 = Task.Run(async () =>
{
- await ExecuteAsyncInternal(context, args, services).ConfigureAwait(false);
+ await ExecuteAsyncInternalAsync(context, args, services).ConfigureAwait(false);
});
break;
}
@@ -179,7 +179,7 @@ namespace Discord.Commands
}
}
- private async Task ExecuteAsyncInternal(ICommandContext context, object[] args, IServiceProvider services)
+ private async Task ExecuteAsyncInternalAsync(ICommandContext context, object[] args, IServiceProvider services)
{
await Module.Service._cmdLogger.DebugAsync($"Executing {GetLogText(context)}").ConfigureAwait(false);
try
@@ -199,10 +199,13 @@ namespace Discord.Commands
return result;
}
else
+ {
await task.ConfigureAwait(false);
+ var result = ExecuteResult.FromSuccess();
+ await Module.Service._commandExecutedEvent.InvokeAsync(this, context, result).ConfigureAwait(false);
+ }
var executeResult = ExecuteResult.FromSuccess();
- await Module.Service._commandExecutedEvent.InvokeAsync(this, context, executeResult).ConfigureAwait(false);
return executeResult;
}
catch (Exception ex)
diff --git a/src/Discord.Net.Commands/Info/ParameterInfo.cs b/src/Discord.Net.Commands/Info/ParameterInfo.cs
index e417b1ab6..4a56415e5 100644
--- a/src/Discord.Net.Commands/Info/ParameterInfo.cs
+++ b/src/Discord.Net.Commands/Info/ParameterInfo.cs
@@ -48,7 +48,7 @@ namespace Discord.Commands
foreach (var precondition in Preconditions)
{
- var result = await precondition.CheckPermissions(context, this, arg, services).ConfigureAwait(false);
+ var result = await precondition.CheckPermissionsAsync(context, this, arg, services).ConfigureAwait(false);
if (!result.IsSuccess)
return result;
}
@@ -56,10 +56,10 @@ namespace Discord.Commands
return PreconditionResult.FromSuccess();
}
- public async Task Parse(ICommandContext context, string input, IServiceProvider services = null)
+ public async Task ParseAsync(ICommandContext context, string input, IServiceProvider services = null)
{
services = services ?? EmptyServiceProvider.Instance;
- return await _reader.Read(context, input, services).ConfigureAwait(false);
+ return await _reader.ReadAsync(context, input, services).ConfigureAwait(false);
}
public override string ToString() => Name;
diff --git a/src/Discord.Net.Commands/Readers/ChannelTypeReader.cs b/src/Discord.Net.Commands/Readers/ChannelTypeReader.cs
index 72c62282e..3136eb2cb 100644
--- a/src/Discord.Net.Commands/Readers/ChannelTypeReader.cs
+++ b/src/Discord.Net.Commands/Readers/ChannelTypeReader.cs
@@ -9,7 +9,7 @@ namespace Discord.Commands
internal class ChannelTypeReader : TypeReader
where T : class, IChannel
{
- public override async Task Read(ICommandContext context, string input, IServiceProvider services)
+ public override async Task ReadAsync(ICommandContext context, string input, IServiceProvider services)
{
if (context.Guild != null)
{
diff --git a/src/Discord.Net.Commands/Readers/EnumTypeReader.cs b/src/Discord.Net.Commands/Readers/EnumTypeReader.cs
index 383b8e63c..c097e6189 100644
--- a/src/Discord.Net.Commands/Readers/EnumTypeReader.cs
+++ b/src/Discord.Net.Commands/Readers/EnumTypeReader.cs
@@ -44,7 +44,7 @@ namespace Discord.Commands
_enumsByValue = byValueBuilder.ToImmutable();
}
- public override Task Read(ICommandContext context, string input, IServiceProvider services)
+ public override Task ReadAsync(ICommandContext context, string input, IServiceProvider services)
{
object enumValue;
diff --git a/src/Discord.Net.Commands/Readers/MessageTypeReader.cs b/src/Discord.Net.Commands/Readers/MessageTypeReader.cs
index 895713e4f..fe576c3c6 100644
--- a/src/Discord.Net.Commands/Readers/MessageTypeReader.cs
+++ b/src/Discord.Net.Commands/Readers/MessageTypeReader.cs
@@ -7,7 +7,7 @@ namespace Discord.Commands
internal class MessageTypeReader : TypeReader
where T : class, IMessage
{
- public override async Task Read(ICommandContext context, string input, IServiceProvider services)
+ public override async Task ReadAsync(ICommandContext context, string input, IServiceProvider services)
{
ulong id;
diff --git a/src/Discord.Net.Commands/Readers/NullableTypeReader.cs b/src/Discord.Net.Commands/Readers/NullableTypeReader.cs
index 07976fb69..109689e15 100644
--- a/src/Discord.Net.Commands/Readers/NullableTypeReader.cs
+++ b/src/Discord.Net.Commands/Readers/NullableTypeReader.cs
@@ -24,11 +24,11 @@ namespace Discord.Commands
_baseTypeReader = baseTypeReader;
}
- public override async Task Read(ICommandContext context, string input, IServiceProvider services)
+ public override async Task ReadAsync(ICommandContext context, string input, IServiceProvider services)
{
if (string.Equals(input, "null", StringComparison.OrdinalIgnoreCase) || string.Equals(input, "nothing", StringComparison.OrdinalIgnoreCase))
return TypeReaderResult.FromSuccess(new T?());
- return await _baseTypeReader.Read(context, input, services); ;
+ return await _baseTypeReader.ReadAsync(context, input, services);
}
}
}
diff --git a/src/Discord.Net.Commands/Readers/PrimitiveTypeReader.cs b/src/Discord.Net.Commands/Readers/PrimitiveTypeReader.cs
index 2656741f0..b19a6bd69 100644
--- a/src/Discord.Net.Commands/Readers/PrimitiveTypeReader.cs
+++ b/src/Discord.Net.Commands/Readers/PrimitiveTypeReader.cs
@@ -30,7 +30,7 @@ namespace Discord.Commands
_score = score;
}
- public override Task Read(ICommandContext context, string input, IServiceProvider services)
+ public override Task ReadAsync(ICommandContext context, string input, IServiceProvider services)
{
if (_tryParse(input, out T value))
return Task.FromResult(TypeReaderResult.FromSuccess(new TypeReaderValue(value, _score)));
diff --git a/src/Discord.Net.Commands/Readers/RoleTypeReader.cs b/src/Discord.Net.Commands/Readers/RoleTypeReader.cs
index 17786e6f0..703374c05 100644
--- a/src/Discord.Net.Commands/Readers/RoleTypeReader.cs
+++ b/src/Discord.Net.Commands/Readers/RoleTypeReader.cs
@@ -9,7 +9,7 @@ namespace Discord.Commands
internal class RoleTypeReader : TypeReader
where T : class, IRole
{
- public override Task Read(ICommandContext context, string input, IServiceProvider services)
+ public override Task ReadAsync(ICommandContext context, string input, IServiceProvider services)
{
ulong id;
diff --git a/src/Discord.Net.Commands/Readers/TypeReader.cs b/src/Discord.Net.Commands/Readers/TypeReader.cs
index 2c4644376..af45a0aac 100644
--- a/src/Discord.Net.Commands/Readers/TypeReader.cs
+++ b/src/Discord.Net.Commands/Readers/TypeReader.cs
@@ -5,6 +5,6 @@ namespace Discord.Commands
{
public abstract class TypeReader
{
- public abstract Task Read(ICommandContext context, string input, IServiceProvider services);
+ public abstract Task ReadAsync(ICommandContext context, string input, IServiceProvider services);
}
}
diff --git a/src/Discord.Net.Commands/Readers/UserTypeReader.cs b/src/Discord.Net.Commands/Readers/UserTypeReader.cs
index c71dac2d2..ca337aaf6 100644
--- a/src/Discord.Net.Commands/Readers/UserTypeReader.cs
+++ b/src/Discord.Net.Commands/Readers/UserTypeReader.cs
@@ -10,7 +10,7 @@ namespace Discord.Commands
internal class UserTypeReader : TypeReader
where T : class, IUser
{
- public override async Task Read(ICommandContext context, string input, IServiceProvider services)
+ public override async Task ReadAsync(ICommandContext context, string input, IServiceProvider services)
{
var results = new Dictionary();
IReadOnlyCollection channelUsers = (await context.Channel.GetUsersAsync(CacheMode.CacheOnly).Flatten().ConfigureAwait(false)).ToArray(); //TODO: must be a better way?
diff --git a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs
index 90d1175d4..c9841cb15 100644
--- a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs
+++ b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs
@@ -24,7 +24,7 @@ namespace Discord
/// The time (in seconds) until the invite expires. Set to null to never expire.
/// The max amount of times this invite may be used. Set to null to have unlimited uses.
/// If true, a user accepting this invite will be kicked from the guild after closing their client.
- Task CreateInviteAsync(int? maxAge = 1800, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null);
+ Task CreateInviteAsync(int? maxAge = 86400, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null);
/// Returns a collection of all invites to this channel.
Task> GetInvitesAsync(RequestOptions options = null);
diff --git a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs
index a465b3ad8..a1df5b4c7 100644
--- a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs
+++ b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs
@@ -29,10 +29,6 @@ namespace Discord
CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
/// Gets a collection of pinned messages in this channel.
Task> GetPinnedMessagesAsync(RequestOptions options = null);
- /// Bulk deletes multiple messages.
- Task DeleteMessagesAsync(IEnumerable messages, RequestOptions options = null);
- /// Bulk deletes multiple messages.
- Task DeleteMessagesAsync(IEnumerable messageIds, RequestOptions options = null);
/// Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds.
Task TriggerTypingAsync(RequestOptions options = null);
diff --git a/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs b/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs
index b2b7e491f..be4dd0260 100644
--- a/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs
+++ b/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Threading.Tasks;
namespace Discord
@@ -11,6 +12,11 @@ namespace Discord
/// Gets the current topic for this text channel.
string Topic { get; }
+ /// Bulk deletes multiple messages.
+ Task DeleteMessagesAsync(IEnumerable messages, RequestOptions options = null);
+ /// Bulk deletes multiple messages.
+ Task DeleteMessagesAsync(IEnumerable messageIds, RequestOptions options = null);
+
/// Modifies this text channel.
Task ModifyAsync(Action func, RequestOptions options = null);
}
diff --git a/src/Discord.Net.Core/Entities/Emotes/EmoteProperties.cs b/src/Discord.Net.Core/Entities/Emotes/EmoteProperties.cs
new file mode 100644
index 000000000..be24d306c
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Emotes/EmoteProperties.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+
+namespace Discord
+{
+ public class EmoteProperties
+ {
+ public Optional Name { get; set; }
+ public Optional> Roles { get; set; }
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
index 94eb67b61..3882bcf96 100644
--- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
+++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
@@ -120,5 +120,14 @@ namespace Discord
Task DownloadUsersAsync();
/// Removes all users from this guild if they have not logged on in a provided number of days or, if simulate is true, returns the number of users that would be removed.
Task PruneUsersAsync(int days = 30, bool simulate = false, RequestOptions options = null);
+
+ /// Gets a specific emote from this guild.
+ Task GetEmoteAsync(ulong id, RequestOptions options = null);
+ /// Creates a new emote in this guild.
+ Task CreateEmoteAsync(string name, Image image, Optional> roles = default(Optional>), RequestOptions options = null);
+ /// Modifies an existing emote in this guild.
+ Task ModifyEmoteAsync(GuildEmote emote, Action func, RequestOptions options = null);
+ /// Deletes an existing emote from this guild.
+ Task DeleteEmoteAsync(GuildEmote emote, RequestOptions options = null);
}
}
\ No newline at end of file
diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs
index a93f02497..3e438f43f 100644
--- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs
+++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs
@@ -1,40 +1,36 @@
-namespace Discord
+using System;
+
+namespace Discord
{
- public enum ChannelPermission : byte
+ [FlagsAttribute]
+ public enum ChannelPermission : ulong
{
- //General
- CreateInstantInvite = 0,
- //KickMembers = 1,
- //BanMembers = 2,
- //Administrator = 3,
- ManageChannel = 4,
- //ManageGuild = 5,
+ // General
+ CreateInstantInvite = 0x00_00_00_01,
+ ManageChannels = 0x00_00_00_10,
- //Text
- AddReactions = 6,
- ReadMessages = 10,
- SendMessages = 11,
- SendTTSMessages = 12,
- ManageMessages = 13,
- EmbedLinks = 14,
- AttachFiles = 15,
- ReadMessageHistory = 16,
- MentionEveryone = 17,
- UseExternalEmojis = 18,
+ // Text
+ AddReactions = 0x00_00_00_40,
+ ReadMessages = 0x00_00_04_00,
+ SendMessages = 0x00_00_08_00,
+ SendTTSMessages = 0x00_00_10_00,
+ ManageMessages = 0x00_00_20_00,
+ EmbedLinks = 0x00_00_40_00,
+ AttachFiles = 0x00_00_80_00,
+ ReadMessageHistory = 0x00_01_00_00,
+ MentionEveryone = 0x00_02_00_00,
+ UseExternalEmojis = 0x00_04_00_00,
- //Voice
- Connect = 20,
- Speak = 21,
- MuteMembers = 22,
- DeafenMembers = 23,
- MoveMembers = 24,
- UseVAD = 25,
+ // Voice
+ Connect = 0x00_10_00_00,
+ Speak = 0x00_20_00_00,
+ MuteMembers = 0x00_40_00_00,
+ DeafenMembers = 0x00_80_00_00,
+ MoveMembers = 0x01_00_00_00,
+ UseVAD = 0x02_00_00_00,
- //General2
- //ChangeNickname = 26,
- //ManageNicknames = 27,
- ManagePermissions = 28,
- ManageWebhooks = 29,
- //ManageEmojis = 30
+ // More General
+ ManageRoles = 0x10_00_00_00,
+ ManageWebhooks = 0x20_00_00_00,
}
}
diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs
index 94596e0e6..4c11d0db0 100644
--- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs
+++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs
@@ -10,7 +10,7 @@ namespace Discord
/// Gets a blank ChannelPermissions that grants no permissions.
public static readonly ChannelPermissions None = new ChannelPermissions();
/// Gets a ChannelPermissions that grants all permissions for text channels.
- public static readonly ChannelPermissions Text = new ChannelPermissions(0b00100_0000000_1111111110001_010001);
+ public static readonly ChannelPermissions Text = new ChannelPermissions(0b01100_0000000_1111111110001_010001);
/// Gets a ChannelPermissions that grants all permissions for voice channels.
public static readonly ChannelPermissions Voice = new ChannelPermissions(0b00100_1111110_0000000000000_010001);
/// Gets a ChannelPermissions that grants all permissions for direct message channels.
@@ -36,7 +36,7 @@ namespace Discord
/// If True, a user may create invites.
public bool CreateInstantInvite => Permissions.GetValue(RawValue, ChannelPermission.CreateInstantInvite);
/// If True, a user may create, delete and modify this channel.
- public bool ManageChannel => Permissions.GetValue(RawValue, ChannelPermission.ManageChannel);
+ public bool ManageChannel => Permissions.GetValue(RawValue, ChannelPermission.ManageChannels);
/// If true, a user may add reactions.
public bool AddReactions => Permissions.GetValue(RawValue, ChannelPermission.AddReactions);
@@ -72,8 +72,8 @@ namespace Discord
/// If True, a user may use voice-activity-detection rather than push-to-talk.
public bool UseVAD => Permissions.GetValue(RawValue, ChannelPermission.UseVAD);
- /// If True, a user may adjust permissions. This also implictly grants all other permissions.
- public bool ManagePermissions => Permissions.GetValue(RawValue, ChannelPermission.ManagePermissions);
+ /// If True, a user may adjust role permissions. This also implictly grants all other permissions.
+ public bool ManageRoles => Permissions.GetValue(RawValue, ChannelPermission.ManageRoles);
/// If True, a user may edit the webhooks for this channel.
public bool ManageWebhooks => Permissions.GetValue(RawValue, ChannelPermission.ManageWebhooks);
@@ -85,12 +85,12 @@ namespace Discord
bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null,
bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null,
bool? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null,
- bool? moveMembers = null, bool? useVoiceActivation = null, bool? managePermissions = null, bool? manageWebhooks = null)
+ bool? moveMembers = null, bool? useVoiceActivation = null, bool? manageRoles = null, bool? manageWebhooks = null)
{
ulong value = initialValue;
Permissions.SetValue(ref value, createInstantInvite, ChannelPermission.CreateInstantInvite);
- Permissions.SetValue(ref value, manageChannel, ChannelPermission.ManageChannel);
+ Permissions.SetValue(ref value, manageChannel, ChannelPermission.ManageChannels);
Permissions.SetValue(ref value, addReactions, ChannelPermission.AddReactions);
Permissions.SetValue(ref value, readMessages, ChannelPermission.ReadMessages);
Permissions.SetValue(ref value, sendMessages, ChannelPermission.SendMessages);
@@ -107,7 +107,7 @@ namespace Discord
Permissions.SetValue(ref value, deafenMembers, ChannelPermission.DeafenMembers);
Permissions.SetValue(ref value, moveMembers, ChannelPermission.MoveMembers);
Permissions.SetValue(ref value, useVoiceActivation, ChannelPermission.UseVAD);
- Permissions.SetValue(ref value, managePermissions, ChannelPermission.ManagePermissions);
+ Permissions.SetValue(ref value, manageRoles, ChannelPermission.ManageRoles);
Permissions.SetValue(ref value, manageWebhooks, ChannelPermission.ManageWebhooks);
RawValue = value;
@@ -119,10 +119,10 @@ namespace Discord
bool readMessages = false, bool sendMessages = false, bool sendTTSMessages = false, bool manageMessages = false,
bool embedLinks = false, bool attachFiles = false, bool readMessageHistory = false, bool mentionEveryone = false,
bool useExternalEmojis = false, bool connect = false, bool speak = false, bool muteMembers = false, bool deafenMembers = false,
- bool moveMembers = false, bool useVoiceActivation = false, bool managePermissions = false, bool manageWebhooks = false)
+ bool moveMembers = false, bool useVoiceActivation = false, bool manageRoles = false, bool manageWebhooks = false)
: this(0, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages,
embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect,
- speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, managePermissions, manageWebhooks)
+ speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, manageRoles, manageWebhooks)
{ }
/// Creates a new ChannelPermissions from this one, changing the provided non-null permissions.
@@ -131,21 +131,21 @@ namespace Discord
bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null,
bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null,
bool useExternalEmojis = false, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null,
- bool? moveMembers = null, bool? useVoiceActivation = null, bool? managePermissions = null, bool? manageWebhooks = null)
+ bool? moveMembers = null, bool? useVoiceActivation = null, bool? manageRoles = null, bool? manageWebhooks = null)
=> new ChannelPermissions(RawValue, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages,
embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect,
- speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, managePermissions, manageWebhooks);
+ speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, manageRoles, manageWebhooks);
public bool Has(ChannelPermission permission) => Permissions.GetValue(RawValue, permission);
public List ToList()
{
var perms = new List();
- ulong x = 1;
- for (byte i = 0; i < Permissions.MaxBits; i++, x <<= 1)
+ for (byte i = 0; i < Permissions.MaxBits; i++)
{
- if ((RawValue & x) != 0)
- perms.Add((ChannelPermission)i);
+ ulong flag = ((ulong)1 << i);
+ if ((RawValue & flag) != 0)
+ perms.Add((ChannelPermission)flag);
}
return perms;
}
diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs
index 3975c1b8b..8469fd304 100644
--- a/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs
+++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs
@@ -1,40 +1,44 @@
-namespace Discord
+using System;
+
+namespace Discord
{
- public enum GuildPermission : byte
+ [FlagsAttribute]
+ public enum GuildPermission : ulong
{
- //General
- CreateInstantInvite = 0,
- KickMembers = 1,
- BanMembers = 2,
- Administrator = 3,
- ManageChannels = 4,
- ManageGuild = 5,
+ // General
+ CreateInstantInvite = 0x00_00_00_01,
+ KickMembers = 0x00_00_00_02,
+ BanMembers = 0x00_00_00_04,
+ Administrator = 0x00_00_00_08,
+ ManageChannels = 0x00_00_00_10,
+ ManageGuild = 0x00_00_00_20,
- //Text
- AddReactions = 6,
- ReadMessages = 10,
- SendMessages = 11,
- SendTTSMessages = 12,
- ManageMessages = 13,
- EmbedLinks = 14,
- AttachFiles = 15,
- ReadMessageHistory = 16,
- MentionEveryone = 17,
- UseExternalEmojis = 18,
+ // Text
+ AddReactions = 0x00_00_00_40,
+ ViewAuditLog = 0x00_00_00_80,
+ ReadMessages = 0x00_00_04_00,
+ SendMessages = 0x00_00_08_00,
+ SendTTSMessages = 0x00_00_10_00,
+ ManageMessages = 0x00_00_20_00,
+ EmbedLinks = 0x00_00_40_00,
+ AttachFiles = 0x00_00_80_00,
+ ReadMessageHistory = 0x00_01_00_00,
+ MentionEveryone = 0x00_02_00_00,
+ UseExternalEmojis = 0x00_04_00_00,
- //Voice
- Connect = 20,
- Speak = 21,
- MuteMembers = 22,
- DeafenMembers = 23,
- MoveMembers = 24,
- UseVAD = 25,
+ // Voice
+ Connect = 0x00_10_00_00,
+ Speak = 0x00_20_00_00,
+ MuteMembers = 0x00_40_00_00,
+ DeafenMembers = 0x00_80_00_00,
+ MoveMembers = 0x01_00_00_00,
+ UseVAD = 0x02_00_00_00,
- //General2
- ChangeNickname = 26,
- ManageNicknames = 27,
- ManageRoles = 28,
- ManageWebhooks = 29,
- ManageEmojis = 30
+ // General 2
+ ChangeNickname = 0x04_00_00_00,
+ ManageNicknames = 0x08_00_00_00,
+ ManageRoles = 0x10_00_00_00,
+ ManageWebhooks = 0x20_00_00_00,
+ ManageEmojis = 0x40_00_00_00
}
}
diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs
index c5f1efab0..a880e62ca 100644
--- a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs
+++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs
@@ -11,7 +11,7 @@ namespace Discord
/// Gets a GuildPermissions that grants all guild permissions for webhook users.
public static readonly GuildPermissions Webhook = new GuildPermissions(0b00000_0000000_0001101100000_000000);
/// Gets a GuildPermissions that grants all guild permissions.
- public static readonly GuildPermissions All = new GuildPermissions(0b11111_1111110_0111111110001_111111);
+ public static readonly GuildPermissions All = new GuildPermissions(0b11111_1111110_1111111110011_111111);
/// Gets a packed value representing all the permissions in this GuildPermissions.
public ulong RawValue { get; }
@@ -28,9 +28,12 @@ namespace Discord
public bool ManageChannels => Permissions.GetValue(RawValue, GuildPermission.ManageChannels);
/// If True, a user may adjust guild properties.
public bool ManageGuild => Permissions.GetValue(RawValue, GuildPermission.ManageGuild);
-
+
/// If true, a user may add reactions.
public bool AddReactions => Permissions.GetValue(RawValue, GuildPermission.AddReactions);
+ /// If true, a user may view the audit log.
+ public bool ViewAuditLog => Permissions.GetValue(RawValue, GuildPermission.ViewAuditLog);
+
/// If True, a user may join channels.
public bool ReadMessages => Permissions.GetValue(RawValue, GuildPermission.ReadMessages);
/// If True, a user may send messages.
@@ -77,13 +80,13 @@ namespace Discord
/// Creates a new GuildPermissions with the provided packed value.
public GuildPermissions(ulong rawValue) { RawValue = rawValue; }
- private GuildPermissions(ulong initialValue, bool? createInstantInvite = null, bool? kickMembers = null,
- bool? banMembers = null, bool? administrator = null, bool? manageChannel = null, bool? manageGuild = null,
- bool? addReactions = null,
- bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null,
- bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null,
- bool? userExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null,
- bool? moveMembers = null, bool? useVoiceActivation = null, bool? changeNickname = null, bool? manageNicknames = null,
+ private GuildPermissions(ulong initialValue, bool? createInstantInvite = null, bool? kickMembers = null,
+ bool? banMembers = null, bool? administrator = null, bool? manageChannels = null, bool? manageGuild = null,
+ bool? addReactions = null, bool? viewAuditLog = null,
+ bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null,
+ bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null,
+ bool? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null,
+ bool? moveMembers = null, bool? useVoiceActivation = null, bool? changeNickname = null, bool? manageNicknames = null,
bool? manageRoles = null, bool? manageWebhooks = null, bool? manageEmojis = null)
{
ulong value = initialValue;
@@ -92,9 +95,10 @@ namespace Discord
Permissions.SetValue(ref value, banMembers, GuildPermission.BanMembers);
Permissions.SetValue(ref value, kickMembers, GuildPermission.KickMembers);
Permissions.SetValue(ref value, administrator, GuildPermission.Administrator);
- Permissions.SetValue(ref value, manageChannel, GuildPermission.ManageChannels);
+ Permissions.SetValue(ref value, manageChannels, GuildPermission.ManageChannels);
Permissions.SetValue(ref value, manageGuild, GuildPermission.ManageGuild);
Permissions.SetValue(ref value, addReactions, GuildPermission.AddReactions);
+ Permissions.SetValue(ref value, viewAuditLog, GuildPermission.ViewAuditLog);
Permissions.SetValue(ref value, readMessages, GuildPermission.ReadMessages);
Permissions.SetValue(ref value, sendMessages, GuildPermission.SendMessages);
Permissions.SetValue(ref value, sendTTSMessages, GuildPermission.SendTTSMessages);
@@ -103,7 +107,7 @@ namespace Discord
Permissions.SetValue(ref value, attachFiles, GuildPermission.AttachFiles);
Permissions.SetValue(ref value, readMessageHistory, GuildPermission.ReadMessageHistory);
Permissions.SetValue(ref value, mentionEveryone, GuildPermission.MentionEveryone);
- Permissions.SetValue(ref value, userExternalEmojis, GuildPermission.UseExternalEmojis);
+ Permissions.SetValue(ref value, useExternalEmojis, GuildPermission.UseExternalEmojis);
Permissions.SetValue(ref value, connect, GuildPermission.Connect);
Permissions.SetValue(ref value, speak, GuildPermission.Speak);
Permissions.SetValue(ref value, muteMembers, GuildPermission.MuteMembers);
@@ -120,42 +124,50 @@ namespace Discord
}
/// Creates a new GuildPermissions with the provided permissions.
- public GuildPermissions(bool createInstantInvite = false, bool kickMembers = false,
+ public GuildPermissions(bool createInstantInvite = false, bool kickMembers = false,
bool banMembers = false, bool administrator = false, bool manageChannels = false, bool manageGuild = false,
- bool addReactions = false,
+ bool addReactions = false, bool viewAuditLog = false,
bool readMessages = false, bool sendMessages = false, bool sendTTSMessages = false, bool manageMessages = false,
bool embedLinks = false, bool attachFiles = false, bool readMessageHistory = false, bool mentionEveryone = false,
bool useExternalEmojis = false, bool connect = false, bool speak = false, bool muteMembers = false, bool deafenMembers = false,
- bool moveMembers = false, bool useVoiceActivation = false, bool? changeNickname = false, bool? manageNicknames = false,
+ bool moveMembers = false, bool useVoiceActivation = false, bool? changeNickname = false, bool? manageNicknames = false,
bool manageRoles = false, bool manageWebhooks = false, bool manageEmojis = false)
- : this(0, createInstantInvite, manageRoles, kickMembers, banMembers, manageChannels, manageGuild, addReactions,
- readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, mentionEveryone, useExternalEmojis, connect,
- manageWebhooks, manageEmojis) { }
+ : this(0, createInstantInvite: createInstantInvite, manageRoles: manageRoles, kickMembers: kickMembers, banMembers: banMembers,
+ administrator: administrator, manageChannels: manageChannels, manageGuild: manageGuild, addReactions: addReactions,
+ viewAuditLog: viewAuditLog, readMessages: readMessages, sendMessages: sendMessages, sendTTSMessages: sendTTSMessages,
+ manageMessages: manageMessages, embedLinks: embedLinks, attachFiles: attachFiles, readMessageHistory: readMessageHistory,
+ mentionEveryone: mentionEveryone, useExternalEmojis: useExternalEmojis, connect: connect, speak: speak, muteMembers: muteMembers,
+ deafenMembers: deafenMembers, moveMembers: moveMembers, useVoiceActivation: useVoiceActivation, changeNickname: changeNickname,
+ manageNicknames: manageNicknames, manageWebhooks: manageWebhooks, manageEmojis: manageEmojis)
+ { }
/// Creates a new GuildPermissions from this one, changing the provided non-null permissions.
- public GuildPermissions Modify(bool? createInstantInvite = null, bool? kickMembers = null,
+ public GuildPermissions Modify(bool? createInstantInvite = null, bool? kickMembers = null,
bool? banMembers = null, bool? administrator = null, bool? manageChannels = null, bool? manageGuild = null,
- bool? addReactions = null,
+ bool? addReactions = null, bool? viewAuditLog = null,
bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null,
bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null,
bool? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null,
- bool? moveMembers = null, bool? useVoiceActivation = null, bool? changeNickname = null, bool? manageNicknames = null,
+ bool? moveMembers = null, bool? useVoiceActivation = null, bool? changeNickname = null, bool? manageNicknames = null,
bool? manageRoles = null, bool? manageWebhooks = null, bool? manageEmojis = null)
- => new GuildPermissions(RawValue, createInstantInvite, manageRoles, kickMembers, banMembers, manageChannels, manageGuild, addReactions,
- readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, mentionEveryone, useExternalEmojis, connect,
- speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, changeNickname, manageNicknames, manageRoles,
- manageWebhooks, manageEmojis);
+ => new GuildPermissions(RawValue, createInstantInvite, kickMembers, banMembers, administrator, manageChannels, manageGuild, addReactions,
+ viewAuditLog, readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles,
+ readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers,
+ useVoiceActivation, changeNickname, manageNicknames, manageRoles, manageWebhooks, manageEmojis);
public bool Has(GuildPermission permission) => Permissions.GetValue(RawValue, permission);
public List ToList()
{
var perms = new List();
- ulong x = 1;
- for (byte i = 0; i < Permissions.MaxBits; i++, x <<= 1)
+
+ // bitwise operations on raw value
+ // each of the GuildPermissions increments by 2^i from 0 to MaxBits
+ for (byte i = 0; i < Permissions.MaxBits; i++)
{
- if ((RawValue & x) != 0)
- perms.Add((GuildPermission)i);
+ ulong flag = ((ulong)1 << i);
+ if ((RawValue & flag) != 0)
+ perms.Add((GuildPermission)flag);
}
return perms;
}
diff --git a/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs b/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs
index c3f8b2bab..c3e296e2c 100644
--- a/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs
+++ b/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs
@@ -23,7 +23,7 @@ namespace Discord
/// If Allowed, a user may create invites.
public PermValue CreateInstantInvite => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.CreateInstantInvite);
/// If Allowed, a user may create, delete and modify this channel.
- public PermValue ManageChannel => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.ManageChannel);
+ public PermValue ManageChannel => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.ManageChannels);
/// If Allowed, a user may add reactions.
public PermValue AddReactions => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.AddReactions);
/// If Allowed, a user may join channels.
@@ -58,8 +58,8 @@ namespace Discord
/// If Allowed, a user may use voice-activity-detection rather than push-to-talk.
public PermValue UseVAD => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.UseVAD);
- /// If Allowed, a user may adjust permissions. This also implictly grants all other permissions.
- public PermValue ManagePermissions => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.ManagePermissions);
+ /// If Allowed, a user may adjust role permissions. This also implictly grants all other permissions.
+ public PermValue ManageRoles => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.ManageRoles);
/// If True, a user may edit the webhooks for this channel.
public PermValue ManageWebhooks => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.ManageWebhooks);
@@ -75,11 +75,11 @@ namespace Discord
PermValue? readMessages = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, PermValue? manageMessages = null,
PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, PermValue? mentionEveryone = null,
PermValue? useExternalEmojis = null, PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null,
- PermValue? deafenMembers = null, PermValue? moveMembers = null, PermValue? useVoiceActivation = null, PermValue? managePermissions = null,
+ PermValue? deafenMembers = null, PermValue? moveMembers = null, PermValue? useVoiceActivation = null, PermValue? manageRoles = null,
PermValue? manageWebhooks = null)
{
Permissions.SetValue(ref allowValue, ref denyValue, createInstantInvite, ChannelPermission.CreateInstantInvite);
- Permissions.SetValue(ref allowValue, ref denyValue, manageChannel, ChannelPermission.ManageChannel);
+ Permissions.SetValue(ref allowValue, ref denyValue, manageChannel, ChannelPermission.ManageChannels);
Permissions.SetValue(ref allowValue, ref denyValue, addReactions, ChannelPermission.AddReactions);
Permissions.SetValue(ref allowValue, ref denyValue, readMessages, ChannelPermission.ReadMessages);
Permissions.SetValue(ref allowValue, ref denyValue, sendMessages, ChannelPermission.SendMessages);
@@ -96,7 +96,7 @@ namespace Discord
Permissions.SetValue(ref allowValue, ref denyValue, deafenMembers, ChannelPermission.DeafenMembers);
Permissions.SetValue(ref allowValue, ref denyValue, moveMembers, ChannelPermission.MoveMembers);
Permissions.SetValue(ref allowValue, ref denyValue, useVoiceActivation, ChannelPermission.UseVAD);
- Permissions.SetValue(ref allowValue, ref denyValue, managePermissions, ChannelPermission.ManagePermissions);
+ Permissions.SetValue(ref allowValue, ref denyValue, manageRoles, ChannelPermission.ManageRoles);
Permissions.SetValue(ref allowValue, ref denyValue, manageWebhooks, ChannelPermission.ManageWebhooks);
AllowValue = allowValue;
@@ -109,10 +109,10 @@ namespace Discord
PermValue readMessages = PermValue.Inherit, PermValue sendMessages = PermValue.Inherit, PermValue sendTTSMessages = PermValue.Inherit, PermValue manageMessages = PermValue.Inherit,
PermValue embedLinks = PermValue.Inherit, PermValue attachFiles = PermValue.Inherit, PermValue readMessageHistory = PermValue.Inherit, PermValue mentionEveryone = PermValue.Inherit,
PermValue useExternalEmojis = PermValue.Inherit, PermValue connect = PermValue.Inherit, PermValue speak = PermValue.Inherit, PermValue muteMembers = PermValue.Inherit, PermValue deafenMembers = PermValue.Inherit,
- PermValue moveMembers = PermValue.Inherit, PermValue useVoiceActivation = PermValue.Inherit, PermValue managePermissions = PermValue.Inherit, PermValue manageWebhooks = PermValue.Inherit)
+ PermValue moveMembers = PermValue.Inherit, PermValue useVoiceActivation = PermValue.Inherit, PermValue manageRoles = PermValue.Inherit, PermValue manageWebhooks = PermValue.Inherit)
: this(0, 0, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages,
embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers,
- moveMembers, useVoiceActivation, managePermissions, manageWebhooks) { }
+ moveMembers, useVoiceActivation, manageRoles, manageWebhooks) { }
/// Creates a new OverwritePermissions from this one, changing the provided non-null permissions.
public OverwritePermissions Modify(PermValue? createInstantInvite = null, PermValue? manageChannel = null,
@@ -120,30 +120,31 @@ namespace Discord
PermValue? readMessages = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, PermValue? manageMessages = null,
PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, PermValue? mentionEveryone = null,
PermValue? useExternalEmojis = null, PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null, PermValue? deafenMembers = null,
- PermValue? moveMembers = null, PermValue? useVoiceActivation = null, PermValue? managePermissions = null, PermValue? manageWebhooks = null)
+ PermValue? moveMembers = null, PermValue? useVoiceActivation = null, PermValue? manageRoles = null, PermValue? manageWebhooks = null)
=> new OverwritePermissions(AllowValue, DenyValue, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages,
embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers,
- moveMembers, useVoiceActivation, managePermissions, manageWebhooks);
+ moveMembers, useVoiceActivation, manageRoles, manageWebhooks);
public List ToAllowList()
{
var perms = new List();
- ulong x = 1;
- for (byte i = 0; i < Permissions.MaxBits; i++, x <<= 1)
+ for (byte i = 0; i < Permissions.MaxBits; i++)
{
- if ((AllowValue & x) != 0)
- perms.Add((ChannelPermission)i);
+ // first operand must be long or ulong to shift >31 bits
+ ulong flag = ((ulong)1 << i);
+ if ((AllowValue & flag) != 0)
+ perms.Add((ChannelPermission)flag);
}
return perms;
}
public List ToDenyList()
{
var perms = new List();
- ulong x = 1;
- for (byte i = 0; i < Permissions.MaxBits; i++, x <<= 1)
+ for (byte i = 0; i < Permissions.MaxBits; i++)
{
- if ((DenyValue & x) != 0)
- perms.Add((ChannelPermission)i);
+ ulong flag = ((ulong)1 << i);
+ if ((DenyValue & flag) != 0)
+ perms.Add((ChannelPermission)flag);
}
return perms;
}
diff --git a/src/Discord.Net.Core/TokenType.cs b/src/Discord.Net.Core/TokenType.cs
index e19197cd6..c351b1c19 100644
--- a/src/Discord.Net.Core/TokenType.cs
+++ b/src/Discord.Net.Core/TokenType.cs
@@ -1,7 +1,10 @@
-namespace Discord
+using System;
+
+namespace Discord
{
public enum TokenType
{
+ [Obsolete("User logins are being deprecated and may result in a ToS strike against your account - please see https://github.com/RogueException/Discord.Net/issues/827")]
User,
Bearer,
Bot,
diff --git a/src/Discord.Net.Core/Utils/Optional.cs b/src/Discord.Net.Core/Utils/Optional.cs
index df927b7ea..eb3cbdca2 100644
--- a/src/Discord.Net.Core/Utils/Optional.cs
+++ b/src/Discord.Net.Core/Utils/Optional.cs
@@ -10,7 +10,7 @@ namespace Discord
public static Optional Unspecified => default(Optional);
private readonly T _value;
- /// Gets the value for this paramter.
+ /// Gets the value for this parameter.
public T Value
{
get
diff --git a/src/Discord.Net.Core/Utils/Permissions.cs b/src/Discord.Net.Core/Utils/Permissions.cs
index c2b7e83ea..a7de90623 100644
--- a/src/Discord.Net.Core/Utils/Permissions.cs
+++ b/src/Discord.Net.Core/Utils/Permissions.cs
@@ -7,84 +7,84 @@ namespace Discord
public const int MaxBits = 53;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static PermValue GetValue(ulong allow, ulong deny, ChannelPermission bit)
- => GetValue(allow, deny, (byte)bit);
+ public static PermValue GetValue(ulong allow, ulong deny, ChannelPermission flag)
+ => GetValue(allow, deny, (ulong)flag);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static PermValue GetValue(ulong allow, ulong deny, GuildPermission bit)
- => GetValue(allow, deny, (byte)bit);
+ public static PermValue GetValue(ulong allow, ulong deny, GuildPermission flag)
+ => GetValue(allow, deny, (ulong)flag);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static PermValue GetValue(ulong allow, ulong deny, byte bit)
+ public static PermValue GetValue(ulong allow, ulong deny, ulong flag)
{
- if (HasBit(allow, bit))
+ if (HasFlag(allow, flag))
return PermValue.Allow;
- else if (HasBit(deny, bit))
+ else if (HasFlag(deny, flag))
return PermValue.Deny;
else
return PermValue.Inherit;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool GetValue(ulong value, ChannelPermission bit)
- => GetValue(value, (byte)bit);
+ public static bool GetValue(ulong value, ChannelPermission flag)
+ => GetValue(value, (ulong)flag);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool GetValue(ulong value, GuildPermission bit)
- => GetValue(value, (byte)bit);
+ public static bool GetValue(ulong value, GuildPermission flag)
+ => GetValue(value, (ulong)flag);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool GetValue(ulong value, byte bit) => HasBit(value, bit);
+ public static bool GetValue(ulong value, ulong flag) => HasFlag(value, flag);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void SetValue(ref ulong rawValue, bool? value, ChannelPermission bit)
- => SetValue(ref rawValue, value, (byte)bit);
+ public static void SetValue(ref ulong rawValue, bool? value, ChannelPermission flag)
+ => SetValue(ref rawValue, value, (ulong)flag);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void SetValue(ref ulong rawValue, bool? value, GuildPermission bit)
- => SetValue(ref rawValue, value, (byte)bit);
+ public static void SetValue(ref ulong rawValue, bool? value, GuildPermission flag)
+ => SetValue(ref rawValue, value, (ulong)flag);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void SetValue(ref ulong rawValue, bool? value, byte bit)
+ public static void SetValue(ref ulong rawValue, bool? value, ulong flag)
{
if (value.HasValue)
{
if (value == true)
- SetBit(ref rawValue, bit);
+ SetFlag(ref rawValue, flag);
else
- UnsetBit(ref rawValue, bit);
+ UnsetFlag(ref rawValue, flag);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, ChannelPermission bit)
- => SetValue(ref allow, ref deny, value, (byte)bit);
+ public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, ChannelPermission flag)
+ => SetValue(ref allow, ref deny, value, (ulong)flag);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, GuildPermission bit)
- => SetValue(ref allow, ref deny, value, (byte)bit);
+ public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, GuildPermission flag)
+ => SetValue(ref allow, ref deny, value, (ulong)flag);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, byte bit)
+ public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, ulong flag)
{
if (value.HasValue)
{
switch (value)
{
case PermValue.Allow:
- SetBit(ref allow, bit);
- UnsetBit(ref deny, bit);
+ SetFlag(ref allow, flag);
+ UnsetFlag(ref deny, flag);
break;
case PermValue.Deny:
- UnsetBit(ref allow, bit);
- SetBit(ref deny, bit);
+ UnsetFlag(ref allow, flag);
+ SetFlag(ref deny, flag);
break;
default:
- UnsetBit(ref allow, bit);
- UnsetBit(ref deny, bit);
+ UnsetFlag(ref allow, flag);
+ UnsetFlag(ref deny, flag);
break;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static bool HasBit(ulong value, byte bit) => (value & (1U << bit)) != 0;
+ private static bool HasFlag(ulong value, ulong flag) => (value & flag) != 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void SetBit(ref ulong value, byte bit) => value |= (1U << bit);
+ public static void SetFlag(ref ulong value, ulong flag) => value |= flag;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void UnsetBit(ref ulong value, byte bit) => value &= ~(1U << bit);
+ public static void UnsetFlag(ref ulong value, ulong flag) => value &= ~flag;
public static ChannelPermissions ToChannelPerms(IGuildChannel channel, ulong guildPermissions)
=> new ChannelPermissions(guildPermissions & ChannelPermissions.All(channel).RawValue);
diff --git a/src/Discord.Net.Core/Utils/Preconditions.cs b/src/Discord.Net.Core/Utils/Preconditions.cs
index bec8de9dc..300f584e4 100644
--- a/src/Discord.Net.Core/Utils/Preconditions.cs
+++ b/src/Discord.Net.Core/Utils/Preconditions.cs
@@ -188,7 +188,8 @@ namespace Discord
var minimum = SnowflakeUtils.ToSnowflake(DateTimeOffset.UtcNow.Subtract(TimeSpan.FromDays(14)));
for (var i = 0; i < collection.Length; i++)
{
- if (collection[i] <= minimum)
+ if (collection[i] == 0) continue;
+ if (collection[i] <= minimum)
throw new ArgumentOutOfRangeException(name, "Messages must be younger than two weeks old.");
}
}
diff --git a/src/Discord.Net.Rest/API/Rest/CreateGuildEmoteParams.cs b/src/Discord.Net.Rest/API/Rest/CreateGuildEmoteParams.cs
new file mode 100644
index 000000000..308199820
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Rest/CreateGuildEmoteParams.cs
@@ -0,0 +1,16 @@
+#pragma warning disable CS1591
+using Newtonsoft.Json;
+
+namespace Discord.API.Rest
+{
+ [JsonObject(MemberSerialization = MemberSerialization.OptIn)]
+ internal class CreateGuildEmoteParams
+ {
+ [JsonProperty("name")]
+ public string Name { get; set; }
+ [JsonProperty("image")]
+ public Image Image { get; set; }
+ [JsonProperty("roles")]
+ public Optional RoleIds { get; set; }
+ }
+}
diff --git a/src/Discord.Net.Rest/API/Rest/ModifyGuildEmoteParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyGuildEmoteParams.cs
new file mode 100644
index 000000000..a2295dd5d
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Rest/ModifyGuildEmoteParams.cs
@@ -0,0 +1,14 @@
+#pragma warning disable CS1591
+using Newtonsoft.Json;
+
+namespace Discord.API.Rest
+{
+ [JsonObject(MemberSerialization = MemberSerialization.OptIn)]
+ internal class ModifyGuildEmoteParams
+ {
+ [JsonProperty("name")]
+ public Optional Name { get; set; }
+ [JsonProperty("roles")]
+ public Optional RoleIds { get; set; }
+ }
+}
diff --git a/src/Discord.Net.Rest/BaseDiscordClient.cs b/src/Discord.Net.Rest/BaseDiscordClient.cs
index 87f974dc2..47a946f20 100644
--- a/src/Discord.Net.Rest/BaseDiscordClient.cs
+++ b/src/Discord.Net.Rest/BaseDiscordClient.cs
@@ -85,7 +85,8 @@ namespace Discord.Rest
await _loggedInEvent.InvokeAsync().ConfigureAwait(false);
}
- internal virtual Task OnLoginAsync(TokenType tokenType, string token) { return Task.Delay(0); }
+ internal virtual Task OnLoginAsync(TokenType tokenType, string token)
+ => Task.Delay(0);
///
public async Task LogoutAsync()
@@ -110,7 +111,8 @@ namespace Discord.Rest
await _loggedOutEvent.InvokeAsync().ConfigureAwait(false);
}
- internal virtual Task OnLogoutAsync() { return Task.Delay(0); }
+ internal virtual Task OnLogoutAsync()
+ => Task.Delay(0);
internal virtual void Dispose(bool disposing)
{
@@ -127,7 +129,8 @@ namespace Discord.Rest
ConnectionState IDiscordClient.ConnectionState => ConnectionState.Disconnected;
ISelfUser IDiscordClient.CurrentUser => CurrentUser;
- Task IDiscordClient.GetApplicationInfoAsync(RequestOptions options) { throw new NotSupportedException(); }
+ Task IDiscordClient.GetApplicationInfoAsync(RequestOptions options)
+ => throw new NotSupportedException();
Task IDiscordClient.GetChannelAsync(ulong id, CacheMode mode, RequestOptions options)
=> Task.FromResult(null);
@@ -148,7 +151,8 @@ namespace Discord.Rest
=> Task.FromResult(null);
Task> IDiscordClient.GetGuildsAsync(CacheMode mode, RequestOptions options)
=> Task.FromResult>(ImmutableArray.Create());
- Task IDiscordClient.CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon, RequestOptions options) { throw new NotSupportedException(); }
+ Task IDiscordClient.CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon, RequestOptions options)
+ => throw new NotSupportedException();
Task IDiscordClient.GetUserAsync(ulong id, CacheMode mode, RequestOptions options)
=> Task.FromResult(null);
diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs
index a6c42782a..4e65b19d2 100644
--- a/src/Discord.Net.Rest/DiscordRestApiClient.cs
+++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable CS0618
using Discord.API.Rest;
using Discord.Net;
using Discord.Net.Converters;
@@ -1065,6 +1066,50 @@ namespace Discord.API
return await SendJsonAsync>("PATCH", () => $"guilds/{guildId}/roles", args, ids, options: options).ConfigureAwait(false);
}
+ //Guild emoji
+ public async Task GetGuildEmoteAsync(ulong guildId, ulong emoteId, RequestOptions options = null)
+ {
+ Preconditions.NotEqual(guildId, 0, nameof(guildId));
+ Preconditions.NotEqual(emoteId, 0, nameof(emoteId));
+ options = RequestOptions.CreateOrClone(options);
+
+ var ids = new BucketIds(guildId: guildId);
+ return await SendAsync("GET", () => $"guilds/{guildId}/emojis/{emoteId}", ids, options: options);
+ }
+
+ public async Task CreateGuildEmoteAsync(ulong guildId, Rest.CreateGuildEmoteParams args, RequestOptions options = null)
+ {
+ Preconditions.NotEqual(guildId, 0, nameof(guildId));
+ Preconditions.NotNull(args, nameof(args));
+ Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name));
+ Preconditions.NotNull(args.Image.Stream, nameof(args.Image));
+ options = RequestOptions.CreateOrClone(options);
+
+ var ids = new BucketIds(guildId: guildId);
+ return await SendJsonAsync("POST", () => $"guilds/{guildId}/emojis", args, ids, options: options);
+ }
+
+ public async Task ModifyGuildEmoteAsync(ulong guildId, ulong emoteId, ModifyGuildEmoteParams args, RequestOptions options = null)
+ {
+ Preconditions.NotEqual(guildId, 0, nameof(guildId));
+ Preconditions.NotEqual(emoteId, 0, nameof(emoteId));
+ Preconditions.NotNull(args, nameof(args));
+ options = RequestOptions.CreateOrClone(options);
+
+ var ids = new BucketIds(guildId: guildId);
+ return await SendJsonAsync("PATCH", () => $"guilds/{guildId}/emojis/{emoteId}", args, ids, options: options);
+ }
+
+ public async Task DeleteGuildEmoteAsync(ulong guildId, ulong emoteId, RequestOptions options = null)
+ {
+ Preconditions.NotEqual(guildId, 0, nameof(guildId));
+ Preconditions.NotEqual(emoteId, 0, nameof(emoteId));
+ options = RequestOptions.CreateOrClone(options);
+
+ var ids = new BucketIds(guildId: guildId);
+ await SendAsync("DELETE", () => $"guilds/{guildId}/emojis/{emoteId}", ids, options: options);
+ }
+
//Users
public async Task GetUserAsync(ulong userId, RequestOptions options = null)
{
diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
index 4c265f3aa..0d272a004 100644
--- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
@@ -184,7 +184,7 @@ namespace Discord.Rest
return RestUserMessage.Create(client, channel, client.CurrentUser, model);
}
- public static async Task DeleteMessagesAsync(IMessageChannel channel, BaseDiscordClient client,
+ public static async Task DeleteMessagesAsync(ITextChannel channel, BaseDiscordClient client,
IEnumerable messageIds, RequestOptions options)
{
var msgs = messageIds.ToArray();
diff --git a/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs
index 342e57717..04cc5a937 100644
--- a/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs
@@ -48,8 +48,8 @@ namespace Discord.Rest
string IChannel.Name => null;
Task IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options)
- => Task.FromResult(null); //Overriden
+ => Task.FromResult(null); //Overridden
IAsyncEnumerable> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options)
- => AsyncEnumerable.Empty>(); //Overriden
+ => AsyncEnumerable.Empty>(); //Overridden
}
}
diff --git a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs
index 8a31da3f1..d41441967 100644
--- a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs
@@ -72,11 +72,6 @@ namespace Discord.Rest
public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options);
- public Task DeleteMessagesAsync(IEnumerable messages, RequestOptions options = null)
- => ChannelHelper.DeleteMessagesAsync(this, Discord, messages.Select(x => x.Id), options);
- public Task DeleteMessagesAsync(IEnumerable messageIds, RequestOptions options = null)
- => ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options);
-
public Task TriggerTypingAsync(RequestOptions options = null)
=> ChannelHelper.TriggerTypingAsync(this, Discord, options);
public IDisposable EnterTypingState(RequestOptions options = null)
diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs
index 44c118fee..aa5c0f7dc 100644
--- a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs
@@ -85,11 +85,6 @@ namespace Discord.Rest
public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options);
- public Task DeleteMessagesAsync(IEnumerable messages, RequestOptions options = null)
- => ChannelHelper.DeleteMessagesAsync(this, Discord, messages.Select(x => x.Id), options);
- public Task DeleteMessagesAsync(IEnumerable messageIds, RequestOptions options = null)
- => ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options);
-
public Task TriggerTypingAsync(RequestOptions options = null)
=> ChannelHelper.TriggerTypingAsync(this, Discord, options);
public IDisposable EnterTypingState(RequestOptions options = null)
diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs
index 2f6b6a174..7656063e1 100644
--- a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs
@@ -123,7 +123,7 @@ namespace Discord.Rest
public async Task> GetInvitesAsync(RequestOptions options = null)
=> await ChannelHelper.GetInvitesAsync(this, Discord, options).ConfigureAwait(false);
- public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
+ public async Task CreateInviteAsync(int? maxAge = 86400, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, options).ConfigureAwait(false);
public override string ToString() => Name;
@@ -158,14 +158,14 @@ namespace Discord.Rest
=> await RemovePermissionOverwriteAsync(user, options).ConfigureAwait(false);
IAsyncEnumerable> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options)
- => AsyncEnumerable.Empty>(); //Overriden //Overriden in Text/Voice
+ => AsyncEnumerable.Empty>(); //Overridden //Overridden in Text/Voice
Task IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options)
- => Task.FromResult(null); //Overriden in Text/Voice
+ => Task.FromResult(null); //Overridden in Text/Voice
//IChannel
IAsyncEnumerable> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options)
- => AsyncEnumerable.Empty>(); //Overriden in Text/Voice
+ => AsyncEnumerable.Empty>(); //Overridden in Text/Voice
Task IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options)
- => Task.FromResult(null); //Overriden in Text/Voice
+ => Task.FromResult(null); //Overridden in Text/Voice
}
}
diff --git a/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs
index 7043c8c76..eb807423f 100644
--- a/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs
@@ -42,11 +42,6 @@ namespace Discord.Rest
public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options);
- public Task DeleteMessagesAsync(IEnumerable messages, RequestOptions options = null)
- => ChannelHelper.DeleteMessagesAsync(this, Discord, messages.Select(x => x.Id), options);
- public Task DeleteMessagesAsync(IEnumerable messageIds, RequestOptions options = null)
- => ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options);
-
public Task TriggerTypingAsync(RequestOptions options = null)
=> ChannelHelper.TriggerTypingAsync(this, Discord, options);
public IDisposable EnterTypingState(RequestOptions options = null)
diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
index a33f27243..6248b7941 100644
--- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
@@ -262,5 +262,46 @@ namespace Discord.Rest
model = await client.ApiClient.BeginGuildPruneAsync(guild.Id, args, options).ConfigureAwait(false);
return model.Pruned;
}
+
+ //Emotes
+ public static async Task GetEmoteAsync(IGuild guild, BaseDiscordClient client, ulong id, RequestOptions options)
+ {
+ var emote = await client.ApiClient.GetGuildEmoteAsync(guild.Id, id, options);
+ return emote.ToEntity();
+ }
+ public static async Task CreateEmoteAsync(IGuild guild, BaseDiscordClient client, string name, Image image, Optional> roles,
+ RequestOptions options)
+ {
+ var apiargs = new CreateGuildEmoteParams
+ {
+ Name = name,
+ Image = image.ToModel()
+ };
+ if (roles.IsSpecified)
+ apiargs.RoleIds = roles.Value?.Select(xr => xr.Id)?.ToArray();
+
+ var emote = await client.ApiClient.CreateGuildEmoteAsync(guild.Id, apiargs, options);
+ return emote.ToEntity();
+ }
+ public static async Task ModifyEmoteAsync(IGuild guild, BaseDiscordClient client, ulong id, Action func,
+ RequestOptions options)
+ {
+ if (func == null) throw new ArgumentNullException(nameof(func));
+
+ var props = new EmoteProperties();
+ func(props);
+
+ var apiargs = new ModifyGuildEmoteParams
+ {
+ Name = props.Name
+ };
+ if (props.Roles.IsSpecified)
+ apiargs.RoleIds = props.Roles.Value?.Select(xr => xr.Id)?.ToArray();
+
+ var emote = await client.ApiClient.ModifyGuildEmoteAsync(guild.Id, id, apiargs, options);
+ return emote.ToEntity();
+ }
+ public static Task DeleteEmoteAsync(IGuild guild, BaseDiscordClient client, ulong id, RequestOptions options)
+ => client.ApiClient.DeleteGuildEmoteAsync(guild.Id, id, options);
}
}
diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
index f0e95a9a6..423c2119d 100644
--- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
+++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
@@ -267,6 +267,16 @@ namespace Discord.Rest
public override string ToString() => Name;
private string DebuggerDisplay => $"{Name} ({Id})";
+ //Emotes
+ public Task GetEmoteAsync(ulong id, RequestOptions options = null)
+ => GuildHelper.GetEmoteAsync(this, Discord, id, options);
+ public Task CreateEmoteAsync(string name, Image image, Optional> roles = default(Optional>), RequestOptions options = null)
+ => GuildHelper.CreateEmoteAsync(this, Discord, name, image, roles, options);
+ public Task ModifyEmoteAsync(GuildEmote emote, Action func, RequestOptions options = null)
+ => GuildHelper.ModifyEmoteAsync(this, Discord, emote.Id, func, options);
+ public Task DeleteEmoteAsync(GuildEmote emote, RequestOptions options = null)
+ => GuildHelper.DeleteEmoteAsync(this, Discord, emote.Id, options);
+
//IGuild
bool IGuild.Available => Available;
IAudioClient IGuild.AudioClient => null;
diff --git a/src/Discord.Net.Rest/Extensions/EmbedBuilderExtensions.cs b/src/Discord.Net.Rest/Extensions/EmbedBuilderExtensions.cs
index cee9a136e..2eb4ed473 100644
--- a/src/Discord.Net.Rest/Extensions/EmbedBuilderExtensions.cs
+++ b/src/Discord.Net.Rest/Extensions/EmbedBuilderExtensions.cs
@@ -1,3 +1,5 @@
+using System;
+
namespace Discord
{
public static class EmbedBuilderExtensions
@@ -19,5 +21,38 @@ namespace Discord
public static EmbedBuilder WithAuthor(this EmbedBuilder builder, IGuildUser user) =>
builder.WithAuthor($"{user.Nickname ?? user.Username}#{user.Discriminator}", user.GetAvatarUrl());
+
+ public static EmbedBuilder ToEmbedBuilder(this IEmbed embed)
+ {
+ if (embed.Type != EmbedType.Rich)
+ throw new InvalidOperationException($"Only {nameof(EmbedType.Rich)} embeds may be built.");
+
+ var builder = new EmbedBuilder
+ {
+ Author = new EmbedAuthorBuilder
+ {
+ Name = embed.Author?.Name,
+ IconUrl = embed.Author?.IconUrl,
+ Url = embed.Author?.Url
+ },
+ Color = embed.Color ?? Color.Default,
+ Description = embed.Description,
+ Footer = new EmbedFooterBuilder
+ {
+ Text = embed.Footer?.Text,
+ IconUrl = embed.Footer?.IconUrl
+ },
+ ImageUrl = embed.Image?.Url,
+ ThumbnailUrl = embed.Thumbnail?.Url,
+ Timestamp = embed.Timestamp,
+ Title = embed.Title,
+ Url = embed.Url
+ };
+
+ foreach (var field in embed.Fields)
+ builder.AddField(field.Name, field.Value, field.Inline);
+
+ return builder;
+ }
}
}
diff --git a/src/Discord.Net.Rest/Net/DefaultRestClient.cs b/src/Discord.Net.Rest/Net/DefaultRestClient.cs
index a54107829..637099fd6 100644
--- a/src/Discord.Net.Rest/Net/DefaultRestClient.cs
+++ b/src/Discord.Net.Rest/Net/DefaultRestClient.cs
@@ -22,7 +22,7 @@ namespace Discord.Net.Rest
private CancellationToken _cancelToken;
private bool _isDisposed;
- public DefaultRestClient(string baseUrl)
+ public DefaultRestClient(string baseUrl, bool useProxy = false)
{
_baseUrl = baseUrl;
@@ -30,7 +30,7 @@ namespace Discord.Net.Rest
{
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
UseCookies = false,
- UseProxy = false
+ UseProxy = useProxy,
});
SetHeader("accept-encoding", "gzip, deflate");
diff --git a/src/Discord.Net.Rest/Net/DefaultRestClientProvider.cs b/src/Discord.Net.Rest/Net/DefaultRestClientProvider.cs
index 311a53562..e0e776549 100644
--- a/src/Discord.Net.Rest/Net/DefaultRestClientProvider.cs
+++ b/src/Discord.Net.Rest/Net/DefaultRestClientProvider.cs
@@ -4,16 +4,21 @@ namespace Discord.Net.Rest
{
public static class DefaultRestClientProvider
{
- public static readonly RestClientProvider Instance = url =>
+ public static readonly RestClientProvider Instance = Create();
+
+ public static RestClientProvider Create(bool useProxy = false)
{
- try
- {
- return new DefaultRestClient(url);
- }
- catch (PlatformNotSupportedException ex)
+ return url =>
{
- throw new PlatformNotSupportedException("The default RestClientProvider is not supported on this platform.", ex);
- }
- };
+ try
+ {
+ return new DefaultRestClient(url, useProxy);
+ }
+ catch (PlatformNotSupportedException ex)
+ {
+ throw new PlatformNotSupportedException("The default RestClientProvider is not supported on this platform.", ex);
+ }
+ };
+ }
}
}
diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs
index da9bce700..42e590aca 100644
--- a/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs
+++ b/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs
@@ -53,11 +53,6 @@ namespace Discord.Rpc
public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options);
- public Task DeleteMessagesAsync(IEnumerable messages, RequestOptions options = null)
- => ChannelHelper.DeleteMessagesAsync(this, Discord, messages.Select(x => x.Id), options);
- public Task DeleteMessagesAsync(IEnumerable messageIds, RequestOptions options = null)
- => ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options);
-
public Task TriggerTypingAsync(RequestOptions options = null)
=> ChannelHelper.TriggerTypingAsync(this, Discord, options);
public IDisposable EnterTypingState(RequestOptions options = null)
diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs
index d449688a4..1c9355047 100644
--- a/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs
+++ b/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs
@@ -56,11 +56,6 @@ namespace Discord.Rpc
public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options);
- public Task DeleteMessagesAsync(IEnumerable messages, RequestOptions options = null)
- => ChannelHelper.DeleteMessagesAsync(this, Discord, messages.Select(x => x.Id), options);
- public Task DeleteMessagesAsync(IEnumerable messageIds, RequestOptions options = null)
- => ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options);
-
public Task TriggerTypingAsync(RequestOptions options = null)
=> ChannelHelper.TriggerTypingAsync(this, Discord, options);
public IDisposable EnterTypingState(RequestOptions options = null)
diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs
index 983f30106..576a0489c 100644
--- a/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs
+++ b/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs
@@ -52,7 +52,7 @@ namespace Discord.Rpc
public async Task> GetInvitesAsync(RequestOptions options = null)
=> await ChannelHelper.GetInvitesAsync(this, Discord, options).ConfigureAwait(false);
- public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
+ public async Task CreateInviteAsync(int? maxAge = 86400, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null)
=> await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, options).ConfigureAwait(false);
public override string ToString() => Name;
diff --git a/src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs b/src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs
index e87c58221..af16f22f5 100644
--- a/src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs
+++ b/src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs
@@ -13,8 +13,6 @@ namespace Discord.API.Gateway
public IDictionary Properties { get; set; }
[JsonProperty("large_threshold")]
public int LargeThreshold { get; set; }
- [JsonProperty("compress")]
- public bool UseCompression { get; set; }
[JsonProperty("shard")]
public Optional ShardingParams { get; set; }
}
diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
new file mode 100644
index 000000000..e881a7855
--- /dev/null
+++ b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
@@ -0,0 +1,193 @@
+using System;
+using System.Threading.Tasks;
+
+namespace Discord.WebSocket
+{
+ public partial class BaseSocketClient
+ {
+ //Channels
+ /// Fired when a channel is created.
+ public event Func ChannelCreated
+ {
+ add { _channelCreatedEvent.Add(value); }
+ remove { _channelCreatedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _channelCreatedEvent = new AsyncEvent>();
+ /// Fired when a channel is destroyed.
+ public event Func ChannelDestroyed {
+ add { _channelDestroyedEvent.Add(value); }
+ remove { _channelDestroyedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _channelDestroyedEvent = new AsyncEvent>();
+ /// Fired when a channel is updated.
+ public event Func ChannelUpdated {
+ add { _channelUpdatedEvent.Add(value); }
+ remove { _channelUpdatedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _channelUpdatedEvent = new AsyncEvent>();
+
+ //Messages
+ /// Fired when a message is received.
+ public event Func MessageReceived {
+ add { _messageReceivedEvent.Add(value); }
+ remove { _messageReceivedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _messageReceivedEvent = new AsyncEvent>();
+ /// Fired when a message is deleted.
+ public event Func, ISocketMessageChannel, Task> MessageDeleted {
+ add { _messageDeletedEvent.Add(value); }
+ remove { _messageDeletedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent, ISocketMessageChannel, Task>> _messageDeletedEvent = new AsyncEvent, ISocketMessageChannel, Task>>();
+ /// Fired when a message is updated.
+ public event Func, SocketMessage, ISocketMessageChannel, Task> MessageUpdated {
+ add { _messageUpdatedEvent.Add(value); }
+ remove { _messageUpdatedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent, SocketMessage, ISocketMessageChannel, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, ISocketMessageChannel, Task>>();
+ /// Fired when a reaction is added to a message.
+ public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionAdded {
+ add { _reactionAddedEvent.Add(value); }
+ remove { _reactionAddedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>();
+ /// Fired when a reaction is removed from a message.
+ public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionRemoved {
+ add { _reactionRemovedEvent.Add(value); }
+ remove { _reactionRemovedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>();
+ /// Fired when all reactions to a message are cleared.
+ public event Func, ISocketMessageChannel, Task> ReactionsCleared {
+ add { _reactionsClearedEvent.Add(value); }
+ remove { _reactionsClearedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent, ISocketMessageChannel, Task>> _reactionsClearedEvent = new AsyncEvent, ISocketMessageChannel, Task>>();
+
+ //Roles
+ /// Fired when a role is created.
+ public event Func RoleCreated {
+ add { _roleCreatedEvent.Add(value); }
+ remove { _roleCreatedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _roleCreatedEvent = new AsyncEvent>();
+ /// Fired when a role is deleted.
+ public event Func RoleDeleted {
+ add { _roleDeletedEvent.Add(value); }
+ remove { _roleDeletedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _roleDeletedEvent = new AsyncEvent>();
+ /// Fired when a role is updated.
+ public event Func RoleUpdated {
+ add { _roleUpdatedEvent.Add(value); }
+ remove { _roleUpdatedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _roleUpdatedEvent = new AsyncEvent>();
+
+ //Guilds
+ /// Fired when the connected account joins a guild.
+ public event Func JoinedGuild {
+ add { _joinedGuildEvent.Add(value); }
+ remove { _joinedGuildEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _joinedGuildEvent = new AsyncEvent>();
+ /// Fired when the connected account leaves a guild.
+ public event Func LeftGuild {
+ add { _leftGuildEvent.Add(value); }
+ remove { _leftGuildEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _leftGuildEvent = new AsyncEvent>();
+ /// Fired when a guild becomes available.
+ public event Func GuildAvailable {
+ add { _guildAvailableEvent.Add(value); }
+ remove { _guildAvailableEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _guildAvailableEvent = new AsyncEvent>();
+ /// Fired when a guild becomes unavailable.
+ public event Func GuildUnavailable {
+ add { _guildUnavailableEvent.Add(value); }
+ remove { _guildUnavailableEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _guildUnavailableEvent = new AsyncEvent>();
+ /// Fired when offline guild members are downloaded.
+ public event Func GuildMembersDownloaded {
+ add { _guildMembersDownloadedEvent.Add(value); }
+ remove { _guildMembersDownloadedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _guildMembersDownloadedEvent = new AsyncEvent>();
+ /// Fired when a guild is updated.
+ public event Func GuildUpdated {
+ add { _guildUpdatedEvent.Add(value); }
+ remove { _guildUpdatedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _guildUpdatedEvent = new AsyncEvent>();
+
+ //Users
+ /// Fired when a user joins a guild.
+ public event Func UserJoined {
+ add { _userJoinedEvent.Add(value); }
+ remove { _userJoinedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _userJoinedEvent = new AsyncEvent>();
+ /// Fired when a user leaves a guild.
+ public event Func UserLeft {
+ add { _userLeftEvent.Add(value); }
+ remove { _userLeftEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _userLeftEvent = new AsyncEvent>();
+ /// Fired when a user is banned from a guild.
+ public event Func UserBanned {
+ add { _userBannedEvent.Add(value); }
+ remove { _userBannedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _userBannedEvent = new AsyncEvent>();
+ /// Fired when a user is unbanned from a guild.
+ public event Func UserUnbanned {
+ add { _userUnbannedEvent.Add(value); }
+ remove { _userUnbannedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _userUnbannedEvent = new AsyncEvent>();
+ /// Fired when a user is updated.
+ public event Func UserUpdated {
+ add { _userUpdatedEvent.Add(value); }
+ remove { _userUpdatedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _userUpdatedEvent = new AsyncEvent>();
+ /// Fired when a guild member is updated, or a member presence is updated.
+ public event Func GuildMemberUpdated {
+ add { _guildMemberUpdatedEvent.Add(value); }
+ remove { _guildMemberUpdatedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _guildMemberUpdatedEvent = new AsyncEvent>();
+ /// Fired when a user joins, leaves, or moves voice channels.
+ public event Func UserVoiceStateUpdated {
+ add { _userVoiceStateUpdatedEvent.Add(value); }
+ remove { _userVoiceStateUpdatedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _userVoiceStateUpdatedEvent = new AsyncEvent>();
+ /// Fired when the connected account is updated.
+ public event Func CurrentUserUpdated {
+ add { _selfUpdatedEvent.Add(value); }
+ remove { _selfUpdatedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _selfUpdatedEvent = new AsyncEvent>();
+ /// Fired when a user starts typing.
+ public event Func UserIsTyping {
+ add { _userIsTypingEvent.Add(value); }
+ remove { _userIsTypingEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _userIsTypingEvent = new AsyncEvent>();
+ /// Fired when a user joins a group channel.
+ public event Func RecipientAdded {
+ add { _recipientAddedEvent.Add(value); }
+ remove { _recipientAddedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _recipientAddedEvent = new AsyncEvent>();
+ /// Fired when a user is removed from a group channel.
+ public event Func RecipientRemoved {
+ add { _recipientRemovedEvent.Add(value); }
+ remove { _recipientRemovedEvent.Remove(value); }
+ }
+ internal readonly AsyncEvent> _recipientRemovedEvent = new AsyncEvent>();
+ }
+}
diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.cs b/src/Discord.Net.WebSocket/BaseSocketClient.cs
new file mode 100644
index 000000000..d248285cd
--- /dev/null
+++ b/src/Discord.Net.WebSocket/BaseSocketClient.cs
@@ -0,0 +1,93 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using Discord.API;
+using Discord.Rest;
+
+namespace Discord.WebSocket
+{
+ public abstract partial class BaseSocketClient : BaseDiscordClient, IDiscordClient
+ {
+ protected readonly DiscordSocketConfig _baseconfig;
+
+ /// Gets the estimated round-trip latency, in milliseconds, to the gateway server.
+ public abstract int Latency { get; protected set; }
+ public abstract UserStatus Status { get; protected set; }
+ public abstract Game? Game { get; protected set; }
+
+ internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient;
+
+ public new SocketSelfUser CurrentUser { get => base.CurrentUser as SocketSelfUser; protected set => base.CurrentUser = value; }
+ public abstract IReadOnlyCollection Guilds { get; }
+ public abstract IReadOnlyCollection PrivateChannels { get; }
+ public abstract IReadOnlyCollection VoiceRegions { get; }
+
+ internal BaseSocketClient(DiscordSocketConfig config, DiscordRestApiClient client)
+ : base(config, client) => _baseconfig = config;
+ private static DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config)
+ => new DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent);
+
+ ///
+ public abstract Task GetApplicationInfoAsync(RequestOptions options = null);
+ ///
+ public abstract SocketUser GetUser(ulong id);
+ ///
+ public abstract SocketUser GetUser(string username, string discriminator);
+ ///
+ public abstract SocketChannel GetChannel(ulong id);
+ ///
+ public abstract SocketGuild GetGuild(ulong id);
+ ///
+ public abstract RestVoiceRegion GetVoiceRegion(string id);
+ ///
+ public abstract Task StartAsync();
+ ///
+ public abstract Task StopAsync();
+ public abstract Task SetStatusAsync(UserStatus status);
+ public abstract Task SetGameAsync(string name, string streamUrl = null, StreamType streamType = StreamType.NotStreaming);
+ public abstract Task DownloadUsersAsync(IEnumerable guilds);
+
+ ///
+ public Task CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon = null, RequestOptions options = null)
+ => ClientHelper.CreateGuildAsync(this, name, region, jpegIcon, options ?? RequestOptions.Default);
+ ///
+ public Task> GetConnectionsAsync(RequestOptions options = null)
+ => ClientHelper.GetConnectionsAsync(this, options ?? RequestOptions.Default);
+ ///
+ public Task GetInviteAsync(string inviteId, RequestOptions options = null)
+ => ClientHelper.GetInviteAsync(this, inviteId, options ?? RequestOptions.Default);
+
+ // IDiscordClient
+ async Task IDiscordClient.GetApplicationInfoAsync(RequestOptions options)
+ => await GetApplicationInfoAsync(options).ConfigureAwait(false);
+
+ Task IDiscordClient.GetChannelAsync(ulong id, CacheMode mode, RequestOptions options)
+ => Task.FromResult(GetChannel(id));
+ Task> IDiscordClient.GetPrivateChannelsAsync(CacheMode mode, RequestOptions options)
+ => Task.FromResult>(PrivateChannels);
+
+ async Task> IDiscordClient.GetConnectionsAsync(RequestOptions options)
+ => await GetConnectionsAsync(options).ConfigureAwait(false);
+
+ async Task IDiscordClient.GetInviteAsync(string inviteId, RequestOptions options)
+ => await GetInviteAsync(inviteId, options).ConfigureAwait(false);
+
+ Task IDiscordClient.GetGuildAsync(ulong id, CacheMode mode, RequestOptions options)
+ => Task.FromResult(GetGuild(id));
+ Task> IDiscordClient.GetGuildsAsync(CacheMode mode, RequestOptions options)
+ => Task.FromResult>(Guilds);
+
+ async Task