diff --git a/Discord.Net.sln b/Discord.Net.sln
index 48d0dbf93..206c5b54a 100644
--- a/Discord.Net.sln
+++ b/Discord.Net.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.25420.1
+# Visual Studio 15
+VisualStudioVersion = 15.0.25914.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F7F3E124-93C7-4846-AE87-9CE12BD82859}"
ProjectSection(SolutionItems) = preProject
@@ -9,57 +9,109 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
README.md = README.md
EndProjectSection
EndProject
-Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net", "src\Discord.Net\Discord.Net.xproj", "{496DB20A-A455-4D01-B6BC-90FE6D7C6B81}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net", "src\Discord.Net\Discord.Net.csproj", "{496DB20A-A455-4D01-B6BC-90FE6D7C6B81}"
EndProject
-Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Core", "src\Discord.Net.Core\Discord.Net.Core.xproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Core", "src\Discord.Net.Core\Discord.Net.Core.csproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Impls", "Impls", "{288C363D-A636-4EAE-9AC1-4698B641B26E}"
EndProject
-Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Rest", "src\Discord.Net.Rest\Discord.Net.Rest.xproj", "{BFC6DC28-0351-4573-926A-D4124244C04F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Rest", "src\Discord.Net.Rest\Discord.Net.Rest.csproj", "{BFC6DC28-0351-4573-926A-D4124244C04F}"
EndProject
-Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.WebSocket", "src\Discord.Net.WebSocket\Discord.Net.WebSocket.xproj", "{22AB6C66-536C-4AC2-BBDB-A8BC4EB6B14D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Rpc", "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj", "{5688A353-121E-40A1-8BFA-B17B91FB48FB}"
EndProject
-Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Rpc", "src\Discord.Net.Rpc\Discord.Net.Rpc.xproj", "{5688A353-121E-40A1-8BFA-B17B91FB48FB}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Commands", "src\Discord.Net.Commands\Discord.Net.Commands.csproj", "{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}"
EndProject
-Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Commands", "src\Discord.Net.Commands\Discord.Net.Commands.xproj", "{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.WebSocket", "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj", "{688FD1D8-7F01-4539-B2E9-F473C5D699C7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|Any CPU.Build.0 = Release|Any CPU
+ {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|x64.Build.0 = Debug|Any CPU
+ {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|x86.Build.0 = Debug|Any CPU
+ {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|Any CPU.ActiveCfg = Debug|Any CPU
+ {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|Any CPU.Build.0 = Debug|Any CPU
+ {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|x64.ActiveCfg = Debug|Any CPU
+ {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|x64.Build.0 = Debug|Any CPU
+ {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|x86.ActiveCfg = Debug|Any CPU
+ {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|x86.Build.0 = Debug|Any CPU
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|x64.ActiveCfg = Debug|x64
+ {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|x64.Build.0 = Debug|x64
+ {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|x86.ActiveCfg = Debug|x86
+ {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|x86.Build.0 = Debug|x86
+ {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.ActiveCfg = Debug|Any CPU
+ {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.Build.0 = Debug|Any CPU
+ {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|x64.ActiveCfg = Release|x64
+ {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|x64.Build.0 = Release|x64
+ {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|x86.ActiveCfg = Release|x86
+ {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|x86.Build.0 = Release|x86
{BFC6DC28-0351-4573-926A-D4124244C04F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BFC6DC28-0351-4573-926A-D4124244C04F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|Any CPU.Build.0 = Release|Any CPU
- {22AB6C66-536C-4AC2-BBDB-A8BC4EB6B14D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {22AB6C66-536C-4AC2-BBDB-A8BC4EB6B14D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {22AB6C66-536C-4AC2-BBDB-A8BC4EB6B14D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {22AB6C66-536C-4AC2-BBDB-A8BC4EB6B14D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BFC6DC28-0351-4573-926A-D4124244C04F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {BFC6DC28-0351-4573-926A-D4124244C04F}.Debug|x64.Build.0 = Debug|Any CPU
+ {BFC6DC28-0351-4573-926A-D4124244C04F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {BFC6DC28-0351-4573-926A-D4124244C04F}.Debug|x86.Build.0 = Debug|Any CPU
+ {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|Any CPU.ActiveCfg = Debug|Any CPU
+ {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|Any CPU.Build.0 = Debug|Any CPU
+ {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|x64.ActiveCfg = Debug|Any CPU
+ {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|x64.Build.0 = Debug|Any CPU
+ {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|x86.ActiveCfg = Debug|Any CPU
+ {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|x86.Build.0 = Debug|Any CPU
{5688A353-121E-40A1-8BFA-B17B91FB48FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5688A353-121E-40A1-8BFA-B17B91FB48FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Debug|x64.Build.0 = Debug|Any CPU
+ {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Debug|x86.Build.0 = Debug|Any CPU
+ {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|Any CPU.ActiveCfg = Debug|Any CPU
+ {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|Any CPU.Build.0 = Debug|Any CPU
+ {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|x64.ActiveCfg = Debug|Any CPU
+ {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|x64.Build.0 = Debug|Any CPU
+ {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|x86.ActiveCfg = Debug|Any CPU
+ {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|x86.Build.0 = Debug|Any CPU
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|x64.Build.0 = Debug|Any CPU
+ {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|x86.Build.0 = Debug|Any CPU
+ {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.ActiveCfg = Debug|Any CPU
+ {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.Build.0 = Debug|Any CPU
+ {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|x64.ActiveCfg = Debug|Any CPU
+ {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|x64.Build.0 = Debug|Any CPU
+ {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|x86.ActiveCfg = Debug|Any CPU
+ {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|x86.Build.0 = Debug|Any CPU
+ {688FD1D8-7F01-4539-B2E9-F473C5D699C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {688FD1D8-7F01-4539-B2E9-F473C5D699C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {688FD1D8-7F01-4539-B2E9-F473C5D699C7}.Debug|x64.ActiveCfg = Debug|x64
+ {688FD1D8-7F01-4539-B2E9-F473C5D699C7}.Debug|x64.Build.0 = Debug|x64
+ {688FD1D8-7F01-4539-B2E9-F473C5D699C7}.Debug|x86.ActiveCfg = Debug|x86
+ {688FD1D8-7F01-4539-B2E9-F473C5D699C7}.Debug|x86.Build.0 = Debug|x86
+ {688FD1D8-7F01-4539-B2E9-F473C5D699C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {688FD1D8-7F01-4539-B2E9-F473C5D699C7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {688FD1D8-7F01-4539-B2E9-F473C5D699C7}.Release|x64.ActiveCfg = Release|x64
+ {688FD1D8-7F01-4539-B2E9-F473C5D699C7}.Release|x64.Build.0 = Release|x64
+ {688FD1D8-7F01-4539-B2E9-F473C5D699C7}.Release|x86.ActiveCfg = Release|x86
+ {688FD1D8-7F01-4539-B2E9-F473C5D699C7}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{BFC6DC28-0351-4573-926A-D4124244C04F} = {288C363D-A636-4EAE-9AC1-4698B641B26E}
- {22AB6C66-536C-4AC2-BBDB-A8BC4EB6B14D} = {288C363D-A636-4EAE-9AC1-4698B641B26E}
{5688A353-121E-40A1-8BFA-B17B91FB48FB} = {288C363D-A636-4EAE-9AC1-4698B641B26E}
+ {688FD1D8-7F01-4539-B2E9-F473C5D699C7} = {288C363D-A636-4EAE-9AC1-4698B641B26E}
EndGlobalSection
EndGlobal
diff --git a/README.md b/README.md
index 8bc4f4394..268a6d81d 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Discord.Net v1.0.0-beta
+# Discord.Net v1.0.0-beta2
[](https://www.myget.org/feed/Packages/discord-net)
[](https://www.myget.org/)
[](https://discord.gg/0SBTUU1wZTYLhAAW)
@@ -19,20 +19,15 @@ Bleeding edge builds are available using our MyGet feed (`https://www.myget.org/
## Compiling
In order to compile Discord.Net, you require the following:
-### Using Visual Studio 2015
-- [VS2015 Update 3](https://www.microsoft.com/net/core#windows)
-- [.Net Core 1.0 VS Plugin](https://www.microsoft.com/net/core#windows)
+### Using Visual Studio
+- [Visual Studio 2017 RC](https://www.microsoft.com/net/core#windowsvs2017)
-### Using CLI
-- [.Net Core 1.0 SDK](https://www.microsoft.com/net/core)
+The .NET Core and Docker (Preview) workload is required during Visual Studio installation.
-## Known Issues
-
-### WebSockets
-The current stable .Net Core websocket package does not support Linux, or pre-Win8.
+### Using Command Line
+- [.Net Core 1.1 SDK](https://www.microsoft.com/net/download/core)
-#### Linux
-Add the latest version of `System.Net.WebSockets.Client` from the .Net Core MyGet feed (`https://dotnet.myget.org/F/dotnet-core/api/v3/index.json`) to your project.
+## Known Issues
-#### Windows 7 and earlier
-There is currently no workaround, track the issue [here](https://github.com/dotnet/corefx/issues/9503).
+### WebSockets (Win7 and earlier)
+.Net Core 1.1 does not support WebSockets on Win7 and earlier. Track the issue [here](https://github.com/dotnet/corefx/issues/9503).
diff --git a/build.bat b/build.bat
index 26df9c77a..95a7c5d9a 100644
--- a/build.bat
+++ b/build.bat
@@ -1,8 +1,15 @@
@echo Off
dotnet restore
-dotnet pack "src\Discord.Net" -c "%Configuration%" -o "artifacts"
-dotnet pack "src\Discord.Net.Core" -c "%Configuration%" -o "artifacts"
-dotnet pack "src\Discord.Net.Rest" -c "%Configuration%" -o "artifacts"
-dotnet pack "src\Discord.Net.WebSocket" -c "%Configuration%" -o "artifacts"
-dotnet pack "src\Discord.Net.Rpc" -c "%Configuration%" -o "artifacts"
-dotnet pack "src\Discord.Net.Commands" -c "%Configuration%" -o "artifacts"
\ No newline at end of file
+dotnet pack "src\Discord.Net" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%"
+dotnet pack "src\Discord.Net.Core" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%"
+dotnet pack "src\Discord.Net.Commands" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%"
+dotnet pack "src\Discord.Net.Rest" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%"
+dotnet pack "src\Discord.Net.WebSocket" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%"
+dotnet pack "src\Discord.Net.Rpc" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%"
+
+REM dotnet pack "src\Discord.Net\Discord.Net.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%"
+REM dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%"
+REM dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%"
+REM dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%"
+REM dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%"
+REM dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%"
\ No newline at end of file
diff --git a/docs/guides/samples.md b/docs/guides/samples.md
index ef85e4898..4406f2f1e 100644
--- a/docs/guides/samples.md
+++ b/docs/guides/samples.md
@@ -13,8 +13,8 @@ title: Samples
#### Changing the bot's status
-[!code-sharp[Bot Status](samples/faq/status.cs)]
+[!code-csharp[Bot Status](samples/faq/status.cs)]
#### Sending a message to a channel
-[!code-csharp[Message to Channel](samples/faq/send_message.cs)]
\ No newline at end of file
+[!code-csharp[Message to Channel](samples/faq/send_message.cs)]
diff --git a/docs/guides/samples/faq/status.cs b/docs/guides/samples/faq/status.cs
index 4d6a29924..8025dd7fd 100644
--- a/docs/guides/samples/faq/status.cs
+++ b/docs/guides/samples/faq/status.cs
@@ -1,8 +1,5 @@
public async Task ModifyStatus()
{
- await (await _client.GetCurrentUserAsync()).ModifyStatusAsync(x =>
- {
- x.Status = UserStatus.Idle;
- x.Game = new Game("Type !help for help");
- });
-}
\ No newline at end of file
+ await _client.SetStatus(UserStatus.Idle);
+ await _client.SetGame("Type !help for help");
+}
diff --git a/global.json b/global.json
deleted file mode 100644
index 9a66d5edc..000000000
--- a/global.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "projects": [ "src", "test" ]
-}
diff --git a/src/Discord.Net.Commands/Attributes/CommandAttribute.cs b/src/Discord.Net.Commands/Attributes/CommandAttribute.cs
index baac75ff9..5ae6092eb 100644
--- a/src/Discord.Net.Commands/Attributes/CommandAttribute.cs
+++ b/src/Discord.Net.Commands/Attributes/CommandAttribute.cs
@@ -6,7 +6,7 @@ namespace Discord.Commands
public class CommandAttribute : Attribute
{
public string Text { get; }
- public RunMode RunMode { get; set; } = RunMode.Sync;
+ public RunMode RunMode { get; set; } = RunMode.Default;
public CommandAttribute()
{
diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs
new file mode 100644
index 000000000..914978192
--- /dev/null
+++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Discord.Commands
+{
+ ///
+ /// This attribute requires that the bot has a speicifed permission in the channel a command is invoked in.
+ ///
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+ public class RequireBotPermissionAttribute : PreconditionAttribute
+ {
+ public GuildPermission? GuildPermission { get; }
+ public ChannelPermission? ChannelPermission { get; }
+
+ ///
+ /// Require that the bot account has a specified GuildPermission
+ ///
+ /// This precondition will always fail if the command is being invoked in a private channel.
+ /// The GuildPermission that the bot must have. Multiple permissions can be specified by ORing or ANDing the permissions together.
+ public RequireBotPermissionAttribute(GuildPermission permission)
+ {
+ GuildPermission = permission;
+ ChannelPermission = null;
+ }
+ ///
+ /// Require that the bot account has a specified ChannelPermission.
+ ///
+ /// The ChannelPermission that the bot must have. Multiple permissions can be specified by ORing or ANDing the permissions together.
+ ///
+ ///
+ /// [Command("permission")]
+ /// [RequireBotPermission(ChannelPermission.ManageMessages)]
+ /// public async Task Purge()
+ /// {
+ /// }
+ ///
+ ///
+ public RequireBotPermissionAttribute(ChannelPermission permission)
+ {
+ ChannelPermission = permission;
+ GuildPermission = null;
+ }
+
+ public override async Task CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map)
+ {
+ var guildUser = await context.Guild.GetCurrentUserAsync();
+
+ if (GuildPermission.HasValue)
+ {
+ if (guildUser == null)
+ return PreconditionResult.FromError("Command must be used in a guild channel");
+ if (!guildUser.GuildPermissions.Has(GuildPermission.Value))
+ return PreconditionResult.FromError($"Command requires guild permission {GuildPermission.Value}");
+ }
+
+ if (ChannelPermission.HasValue)
+ {
+ var guildChannel = context.Channel as IGuildChannel;
+
+ ChannelPermissions perms;
+ if (guildChannel != null)
+ perms = guildUser.GetPermissions(guildChannel);
+ else
+ perms = ChannelPermissions.All(guildChannel);
+
+ if (!perms.Has(ChannelPermission.Value))
+ return PreconditionResult.FromError($"Command requires channel permission {ChannelPermission.Value}");
+ }
+
+ return PreconditionResult.FromSuccess();
+ }
+ }
+}
diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs
index 1cd32e72e..beadbbc89 100644
--- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs
+++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs
@@ -11,11 +11,27 @@ namespace Discord.Commands
Group = 0x04
}
+ ///
+ /// Require that the command be invoked in a specified context.
+ ///
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class RequireContextAttribute : PreconditionAttribute
{
public ContextType Contexts { get; }
+ ///
+ /// Require that the command be invoked in a specified context.
+ ///
+ /// The type of context the command can be invoked in. Multiple contexts can be speicifed by ORing the contexts together.
+ ///
+ ///
+ /// [Command("private_only")]
+ /// [RequireContext(ContextType.DM | ContextType.Group)]
+ /// public async Task PrivateOnly()
+ /// {
+ /// }
+ ///
+ ///
public RequireContextAttribute(ContextType contexts)
{
Contexts = contexts;
diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs
new file mode 100644
index 000000000..8992cd115
--- /dev/null
+++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Discord;
+
+namespace Discord.Commands
+{
+ ///
+ /// Require that the command is invoked by the owner of the bot.
+ ///
+ /// This precondition will only work if the bot is a bot account.
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+ public class RequireOwnerAttribute : PreconditionAttribute
+ {
+ public override async Task CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map)
+ {
+ var application = await context.Client.GetApplicationInfoAsync();
+ if (context.User.Id == application.Owner.Id) return PreconditionResult.FromSuccess();
+ return PreconditionResult.FromError("Command can only be run by the owner of the bot");
+ }
+ }
+}
diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequirePermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs
similarity index 54%
rename from src/Discord.Net.Commands/Attributes/Preconditions/RequirePermissionAttribute.cs
rename to src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs
index 26aeac5ec..17cf234aa 100644
--- a/src/Discord.Net.Commands/Attributes/Preconditions/RequirePermissionAttribute.cs
+++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs
@@ -3,18 +3,40 @@ using System.Threading.Tasks;
namespace Discord.Commands
{
+ ///
+ /// This attribute requires that the user invoking the command has a specified permission.
+ ///
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
- public class RequirePermissionAttribute : PreconditionAttribute
+ public class RequireUserPermissionAttribute : PreconditionAttribute
{
public GuildPermission? GuildPermission { get; }
public ChannelPermission? ChannelPermission { get; }
- public RequirePermissionAttribute(GuildPermission permission)
+ ///
+ /// Require that the user invoking the command has a specified GuildPermission
+ ///
+ /// This precondition will always fail if the command is being invoked in a private channel.
+ /// The GuildPermission that the user must have. Multiple permissions can be specified by ORing or ANDing the permissions together.
+ public RequireUserPermissionAttribute(GuildPermission permission)
{
GuildPermission = permission;
ChannelPermission = null;
}
- public RequirePermissionAttribute(ChannelPermission permission)
+ ///
+ /// Require that the user invoking the command has a specified ChannelPermission.
+ ///
+ /// The ChannelPermission that the user must have. Multiple permissions can be specified by ORing or ANDing the permissions together.
+ ///
+ ///
+ /// [Command("permission")]
+ /// [RequireUserPermission(ChannelPermission.ReadMessageHistory & ChannelPermission.ReadMessages)]
+ /// public async Task HasPermission()
+ /// {
+ /// await ReplyAsync("You can read messages and the message history!");
+ /// }
+ ///
+ ///
+ public RequireUserPermissionAttribute(ChannelPermission permission)
{
ChannelPermission = permission;
GuildPermission = null;
diff --git a/src/Discord.Net.Commands/Builders/CommandBuilder.cs b/src/Discord.Net.Commands/Builders/CommandBuilder.cs
new file mode 100644
index 000000000..9b983fd1f
--- /dev/null
+++ b/src/Discord.Net.Commands/Builders/CommandBuilder.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+namespace Discord.Commands.Builders
+{
+ public class CommandBuilder
+ {
+ private readonly List _preconditions;
+ private readonly List _parameters;
+ private readonly List _aliases;
+
+ public ModuleBuilder Module { get; }
+ internal Func Callback { get; set; }
+
+ public string Name { get; set; }
+ public string Summary { get; set; }
+ public string Remarks { get; set; }
+ public RunMode RunMode { get; set; }
+ public int Priority { get; set; }
+
+ public IReadOnlyList Preconditions => _preconditions;
+ public IReadOnlyList Parameters => _parameters;
+ public IReadOnlyList Aliases => _aliases;
+
+ //Automatic
+ internal CommandBuilder(ModuleBuilder module)
+ {
+ Module = module;
+
+ _preconditions = new List();
+ _parameters = new List();
+ _aliases = new List();
+ }
+ //User-defined
+ internal CommandBuilder(ModuleBuilder module, string primaryAlias, Func callback)
+ : this(module)
+ {
+ Discord.Preconditions.NotNull(primaryAlias, nameof(primaryAlias));
+ Discord.Preconditions.NotNull(callback, nameof(callback));
+
+ Callback = callback;
+ _aliases.Add(primaryAlias);
+ }
+
+ public CommandBuilder WithName(string name)
+ {
+ Name = name;
+ return this;
+ }
+ public CommandBuilder WithSummary(string summary)
+ {
+ Summary = summary;
+ return this;
+ }
+ public CommandBuilder WithRemarks(string remarks)
+ {
+ Remarks = remarks;
+ return this;
+ }
+ public CommandBuilder WithRunMode(RunMode runMode)
+ {
+ RunMode = runMode;
+ return this;
+ }
+ public CommandBuilder WithPriority(int priority)
+ {
+ Priority = priority;
+ return this;
+ }
+
+ public CommandBuilder AddAliases(params string[] aliases)
+ {
+ _aliases.AddRange(aliases);
+ return this;
+ }
+ public CommandBuilder AddPrecondition(PreconditionAttribute precondition)
+ {
+ _preconditions.Add(precondition);
+ return this;
+ }
+ public CommandBuilder AddParameter(string name, Type type, Action createFunc)
+ {
+ var param = new ParameterBuilder(this, name, type);
+ createFunc(param);
+ _parameters.Add(param);
+ return this;
+ }
+ internal CommandBuilder AddParameter(Action createFunc)
+ {
+ var param = new ParameterBuilder(this);
+ createFunc(param);
+ _parameters.Add(param);
+ return this;
+ }
+
+ internal CommandInfo Build(ModuleInfo info, CommandService service)
+ {
+ //Default name to first alias
+ if (Name == null)
+ Name = _aliases[0];
+
+ if (_parameters.Count > 0)
+ {
+ var lastParam = _parameters[_parameters.Count - 1];
+
+ var firstMultipleParam = _parameters.FirstOrDefault(x => x.IsMultiple);
+ if ((firstMultipleParam != null) && (firstMultipleParam != lastParam))
+ throw new InvalidOperationException("Only the last parameter in a command may have the Multiple flag.");
+
+ var firstRemainderParam = _parameters.FirstOrDefault(x => x.IsRemainder);
+ if ((firstRemainderParam != null) && (firstRemainderParam != lastParam))
+ throw new InvalidOperationException("Only the last parameter in a command may have the Remainder flag.");
+ }
+
+ return new CommandInfo(this, info, service);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Discord.Net.Commands/Builders/ModuleBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleBuilder.cs
new file mode 100644
index 000000000..083de8e81
--- /dev/null
+++ b/src/Discord.Net.Commands/Builders/ModuleBuilder.cs
@@ -0,0 +1,109 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Discord.Commands.Builders
+{
+ public class ModuleBuilder
+ {
+ private readonly List _commands;
+ private readonly List _submodules;
+ private readonly List _preconditions;
+ private readonly List _aliases;
+
+ public CommandService Service { get; }
+ public ModuleBuilder Parent { get; }
+ public string Name { get; set; }
+ public string Summary { get; set; }
+ public string Remarks { get; set; }
+
+ public IReadOnlyList Commands => _commands;
+ public IReadOnlyList Modules => _submodules;
+ public IReadOnlyList Preconditions => _preconditions;
+ public IReadOnlyList Aliases => _aliases;
+
+ //Automatic
+ internal ModuleBuilder(CommandService service, ModuleBuilder parent)
+ {
+ Service = service;
+ Parent = parent;
+
+ _commands = new List();
+ _submodules = new List();
+ _preconditions = new List();
+ _aliases = new List();
+ }
+ //User-defined
+ internal ModuleBuilder(CommandService service, ModuleBuilder parent, string primaryAlias)
+ : this(service, parent)
+ {
+ Discord.Preconditions.NotNull(primaryAlias, nameof(primaryAlias));
+
+ _aliases = new List { primaryAlias };
+ }
+
+ public ModuleBuilder WithName(string name)
+ {
+ Name = name;
+ return this;
+ }
+ public ModuleBuilder WithSummary(string summary)
+ {
+ Summary = summary;
+ return this;
+ }
+ public ModuleBuilder WithRemarks(string remarks)
+ {
+ Remarks = remarks;
+ return this;
+ }
+
+ public ModuleBuilder AddAlias(params string[] newAliases)
+ {
+ _aliases.AddRange(newAliases);
+ return this;
+ }
+ public ModuleBuilder AddPrecondition(PreconditionAttribute precondition)
+ {
+ _preconditions.Add(precondition);
+ return this;
+ }
+ public ModuleBuilder AddCommand(string primaryAlias, Func callback, Action createFunc)
+ {
+ var builder = new CommandBuilder(this, primaryAlias, callback);
+ createFunc(builder);
+ _commands.Add(builder);
+ return this;
+ }
+ internal ModuleBuilder AddCommand(Action createFunc)
+ {
+ var builder = new CommandBuilder(this);
+ createFunc(builder);
+ _commands.Add(builder);
+ return this;
+ }
+ public ModuleBuilder AddModule(string primaryAlias, Action createFunc)
+ {
+ var builder = new ModuleBuilder(Service, this, primaryAlias);
+ createFunc(builder);
+ _submodules.Add(builder);
+ return this;
+ }
+ internal ModuleBuilder AddModule(Action createFunc)
+ {
+ var builder = new ModuleBuilder(Service, this);
+ createFunc(builder);
+ _submodules.Add(builder);
+ return this;
+ }
+
+ public ModuleInfo Build(CommandService service)
+ {
+ //Default name to first alias
+ if (Name == null)
+ Name = _aliases[0];
+
+ return new ModuleInfo(this, service);
+ }
+ }
+}
diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
new file mode 100644
index 000000000..aaec43161
--- /dev/null
+++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
@@ -0,0 +1,232 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Threading.Tasks;
+
+using Discord.Commands.Builders;
+
+namespace Discord.Commands
+{
+ internal static class ModuleClassBuilder
+ {
+ private static readonly TypeInfo _moduleTypeInfo = typeof(ModuleBase).GetTypeInfo();
+
+ public static IEnumerable Search(Assembly assembly)
+ {
+ foreach (var type in assembly.ExportedTypes)
+ {
+ var typeInfo = type.GetTypeInfo();
+ if (IsValidModuleDefinition(typeInfo) &&
+ !typeInfo.IsDefined(typeof(DontAutoLoadAttribute)))
+ {
+ yield return typeInfo;
+ }
+ }
+ }
+
+ public static Dictionary Build(CommandService service, params TypeInfo[] validTypes) => Build(validTypes, service);
+ public static Dictionary Build(IEnumerable validTypes, CommandService service)
+ {
+ if (!validTypes.Any())
+ throw new InvalidOperationException("Could not find any valid modules from the given selection");
+
+ var topLevelGroups = validTypes.Where(x => x.DeclaringType == null);
+ var subGroups = validTypes.Intersect(topLevelGroups);
+
+ var builtTypes = new List();
+
+ var result = new Dictionary();
+
+ foreach (var typeInfo in topLevelGroups)
+ {
+ // TODO: This shouldn't be the case; may be safe to remove?
+ if (result.ContainsKey(typeInfo.AsType()))
+ continue;
+
+ var module = new ModuleBuilder(service, null);
+
+ BuildModule(module, typeInfo, service);
+ BuildSubTypes(module, typeInfo.DeclaredNestedTypes, builtTypes, service);
+
+ result[typeInfo.AsType()] = module.Build(service);
+ }
+
+ return result;
+ }
+
+ private static void BuildSubTypes(ModuleBuilder builder, IEnumerable subTypes, List builtTypes, CommandService service)
+ {
+ foreach (var typeInfo in subTypes)
+ {
+ if (!IsValidModuleDefinition(typeInfo))
+ continue;
+
+ if (builtTypes.Contains(typeInfo))
+ continue;
+
+ builder.AddModule((module) => {
+ BuildModule(module, typeInfo, service);
+ BuildSubTypes(module, typeInfo.DeclaredNestedTypes, builtTypes, service);
+ });
+
+ builtTypes.Add(typeInfo);
+ }
+ }
+
+ private static void BuildModule(ModuleBuilder builder, TypeInfo typeInfo, CommandService service)
+ {
+ var attributes = typeInfo.GetCustomAttributes();
+
+ foreach (var attribute in attributes)
+ {
+ // TODO: C#7 type switch
+ if (attribute is NameAttribute)
+ builder.Name = (attribute as NameAttribute).Text;
+ else if (attribute is SummaryAttribute)
+ builder.Summary = (attribute as SummaryAttribute).Text;
+ else if (attribute is RemarksAttribute)
+ builder.Remarks = (attribute as RemarksAttribute).Text;
+ else if (attribute is AliasAttribute)
+ builder.AddAlias((attribute as AliasAttribute).Aliases);
+ else if (attribute is GroupAttribute)
+ {
+ var groupAttr = attribute as GroupAttribute;
+ builder.Name = builder.Name ?? groupAttr.Prefix;
+ builder.AddAlias(groupAttr.Prefix);
+ }
+ else if (attribute is PreconditionAttribute)
+ builder.AddPrecondition(attribute as PreconditionAttribute);
+ }
+
+ if (builder.Name == null)
+ builder.Name = typeInfo.Name;
+
+ var validCommands = typeInfo.DeclaredMethods.Where(x => IsValidCommandDefinition(x));
+
+ foreach (var method in validCommands)
+ {
+ builder.AddCommand((command) => {
+ BuildCommand(command, typeInfo, method, service);
+ });
+ }
+ }
+
+ private static void BuildCommand(CommandBuilder builder, TypeInfo typeInfo, MethodInfo method, CommandService service)
+ {
+ var attributes = method.GetCustomAttributes();
+
+ foreach (var attribute in attributes)
+ {
+ // TODO: C#7 type switch
+ if (attribute is CommandAttribute)
+ {
+ var cmdAttr = attribute as CommandAttribute;
+ builder.AddAliases(cmdAttr.Text);
+ builder.RunMode = cmdAttr.RunMode;
+ builder.Name = builder.Name ?? cmdAttr.Text;
+ }
+ else if (attribute is NameAttribute)
+ builder.Name = (attribute as NameAttribute).Text;
+ else if (attribute is PriorityAttribute)
+ builder.Priority = (attribute as PriorityAttribute).Priority;
+ else if (attribute is SummaryAttribute)
+ builder.Summary = (attribute as SummaryAttribute).Text;
+ else if (attribute is RemarksAttribute)
+ builder.Remarks = (attribute as RemarksAttribute).Text;
+ else if (attribute is AliasAttribute)
+ builder.AddAliases((attribute as AliasAttribute).Aliases);
+ else if (attribute is PreconditionAttribute)
+ builder.AddPrecondition(attribute as PreconditionAttribute);
+ }
+
+ if (builder.Name == null)
+ builder.Name = method.Name;
+
+ var parameters = method.GetParameters();
+ int pos = 0, count = parameters.Length;
+ foreach (var paramInfo in parameters)
+ {
+ builder.AddParameter((parameter) => {
+ BuildParameter(parameter, paramInfo, pos++, count, service);
+ });
+ }
+
+ var createInstance = ReflectionUtils.CreateBuilder(typeInfo, service);
+
+ builder.Callback = (ctx, args, map) => {
+ var instance = createInstance(map);
+ instance.Context = ctx;
+ try
+ {
+ return method.Invoke(instance, args) as Task ?? Task.CompletedTask;
+ }
+ finally{
+ (instance as IDisposable)?.Dispose();
+ }
+ };
+ }
+
+ private static void BuildParameter(ParameterBuilder builder, System.Reflection.ParameterInfo paramInfo, int position, int count, CommandService service)
+ {
+ var attributes = paramInfo.GetCustomAttributes();
+ var paramType = paramInfo.ParameterType;
+
+ builder.Name = paramInfo.Name;
+
+ builder.IsOptional = paramInfo.IsOptional;
+ builder.DefaultValue = paramInfo.HasDefaultValue ? paramInfo.DefaultValue : null;
+
+ foreach (var attribute in attributes)
+ {
+ // TODO: C#7 type switch
+ if (attribute is SummaryAttribute)
+ builder.Summary = (attribute as SummaryAttribute).Text;
+ else if (attribute is ParamArrayAttribute)
+ {
+ builder.IsMultiple = true;
+ paramType = paramType.GetElementType();
+ }
+ else if (attribute is RemainderAttribute)
+ {
+ if (position != count-1)
+ throw new InvalidOperationException("Remainder parameters must be the last parameter in a command.");
+
+ builder.IsRemainder = true;
+ }
+ }
+
+ var reader = service.GetTypeReader(paramType);
+ if (reader == null)
+ {
+ var paramTypeInfo = paramType.GetTypeInfo();
+ if (paramTypeInfo.IsEnum)
+ {
+ reader = EnumTypeReader.GetReader(paramType);
+ service.AddTypeReader(paramType, reader);
+ }
+ else
+ {
+ throw new InvalidOperationException($"{paramType.FullName} is not supported as a command parameter, are you missing a TypeReader?");
+ }
+ }
+
+ builder.ParameterType = paramType;
+ builder.TypeReader = reader;
+ }
+
+ private static bool IsValidModuleDefinition(TypeInfo typeInfo)
+ {
+ return _moduleTypeInfo.IsAssignableFrom(typeInfo) &&
+ !typeInfo.IsAbstract;
+ }
+
+ private static bool IsValidCommandDefinition(MethodInfo methodInfo)
+ {
+ return methodInfo.IsDefined(typeof(CommandAttribute)) &&
+ methodInfo.ReturnType == typeof(Task) &&
+ !methodInfo.IsStatic &&
+ !methodInfo.IsGenericMethod;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Discord.Net.Commands/Builders/ParameterBuilder.cs b/src/Discord.Net.Commands/Builders/ParameterBuilder.cs
new file mode 100644
index 000000000..801a10080
--- /dev/null
+++ b/src/Discord.Net.Commands/Builders/ParameterBuilder.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Reflection;
+
+namespace Discord.Commands.Builders
+{
+ public class ParameterBuilder
+ {
+ public CommandBuilder Command { get; }
+ public string Name { get; internal set; }
+ public Type ParameterType { get; internal set; }
+
+ public TypeReader TypeReader { get; set; }
+ public bool IsOptional { get; set; }
+ public bool IsRemainder { get; set; }
+ public bool IsMultiple { get; set; }
+ public object DefaultValue { get; set; }
+ public string Summary { get; set; }
+
+ //Automatic
+ internal ParameterBuilder(CommandBuilder command)
+ {
+ Command = command;
+ }
+ //User-defined
+ internal ParameterBuilder(CommandBuilder command, string name, Type type)
+ : this(command)
+ {
+ Preconditions.NotNull(name, nameof(name));
+
+ Name = name;
+ SetType(type);
+ }
+
+ internal void SetType(Type type)
+ {
+ TypeReader = Command.Module.Service.GetTypeReader(type);
+
+ if (type.GetTypeInfo().IsValueType)
+ DefaultValue = Activator.CreateInstance(type);
+ else if (type.IsArray)
+ type = ParameterType.GetElementType();
+ ParameterType = type;
+ }
+
+ public ParameterBuilder WithSummary(string summary)
+ {
+ Summary = summary;
+ return this;
+ }
+ public ParameterBuilder WithDefault(object defaultValue)
+ {
+ DefaultValue = defaultValue;
+ return this;
+ }
+ public ParameterBuilder WithIsOptional(bool isOptional)
+ {
+ IsOptional = isOptional;
+ return this;
+ }
+ public ParameterBuilder WithIsRemainder(bool isRemainder)
+ {
+ IsRemainder = isRemainder;
+ return this;
+ }
+ public ParameterBuilder WithIsMultiple(bool isMultiple)
+ {
+ IsMultiple = isMultiple;
+ return this;
+ }
+
+ internal ParameterInfo Build(CommandInfo info)
+ {
+ if (TypeReader == null)
+ throw new InvalidOperationException($"No default TypeReader found, one must be specified");
+
+ return new ParameterInfo(this, info, Command.Module.Service);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Discord.Net.Commands/CommandInfo.cs b/src/Discord.Net.Commands/CommandInfo.cs
deleted file mode 100644
index 47aae1ae2..000000000
--- a/src/Discord.Net.Commands/CommandInfo.cs
+++ /dev/null
@@ -1,284 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Collections.Immutable;
-using System.Diagnostics;
-using System.Linq;
-using System.Reflection;
-using System.Threading.Tasks;
-
-namespace Discord.Commands
-{
- [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
- public class CommandInfo
- {
- private static readonly MethodInfo _convertParamsMethod = typeof(CommandInfo).GetTypeInfo().GetDeclaredMethod(nameof(ConvertParamsList));
- private static readonly ConcurrentDictionary, object>> _arrayConverters = new ConcurrentDictionary, object>>();
-
- private readonly Func _action;
-
- public MethodInfo Source { get; }
- public ModuleInfo Module { get; }
- public string Name { get; }
- public string Summary { get; }
- public string Remarks { get; }
- public string Text { get; }
- public int Priority { get; }
- public bool HasVarArgs { get; }
- public RunMode RunMode { get; }
- public IReadOnlyList Aliases { get; }
- public IReadOnlyList Parameters { get; }
- public IReadOnlyList Preconditions { get; }
-
- internal CommandInfo(MethodInfo source, ModuleInfo module, CommandAttribute attribute, string groupPrefix)
- {
- try
- {
- Source = source;
- Module = module;
-
- Name = source.Name;
-
- if (attribute.Text == null)
- Text = groupPrefix;
- RunMode = attribute.RunMode;
-
- if (groupPrefix != "")
- groupPrefix += " ";
-
- if (attribute.Text != null)
- Text = groupPrefix + attribute.Text;
-
- var aliasesBuilder = ImmutableArray.CreateBuilder();
-
- aliasesBuilder.Add(Text);
-
- var aliasesAttr = source.GetCustomAttribute();
- if (aliasesAttr != null)
- aliasesBuilder.AddRange(aliasesAttr.Aliases.Select(x => groupPrefix + x));
-
- Aliases = aliasesBuilder.ToImmutable();
-
- var nameAttr = source.GetCustomAttribute();
- if (nameAttr != null)
- Name = nameAttr.Text;
-
- var summary = source.GetCustomAttribute();
- if (summary != null)
- Summary = summary.Text;
-
- var remarksAttr = source.GetCustomAttribute();
- if (remarksAttr != null)
- Remarks = remarksAttr.Text;
-
- var priorityAttr = source.GetCustomAttribute();
- Priority = priorityAttr?.Priority ?? 0;
-
- Parameters = BuildParameters(source);
- HasVarArgs = Parameters.Count > 0 ? Parameters[Parameters.Count - 1].IsMultiple : false;
- Preconditions = BuildPreconditions(source);
- _action = BuildAction(source);
- }
- catch (Exception ex)
- {
- throw new Exception($"Failed to build command {source.DeclaringType.FullName}.{source.Name}", ex);
- }
- }
-
- public async Task CheckPreconditions(CommandContext context, IDependencyMap map = null)
- {
- if (map == null)
- map = DependencyMap.Empty;
-
- foreach (PreconditionAttribute precondition in Module.Preconditions)
- {
- var result = await precondition.CheckPermissions(context, this, map).ConfigureAwait(false);
- if (!result.IsSuccess)
- return result;
- }
-
- foreach (PreconditionAttribute precondition in Preconditions)
- {
- var result = await precondition.CheckPermissions(context, this, map).ConfigureAwait(false);
- if (!result.IsSuccess)
- return result;
- }
-
- return PreconditionResult.FromSuccess();
- }
-
- public async Task Parse(CommandContext context, SearchResult searchResult, PreconditionResult? preconditionResult = null)
- {
- if (!searchResult.IsSuccess)
- return ParseResult.FromError(searchResult);
- if (preconditionResult != null && !preconditionResult.Value.IsSuccess)
- return ParseResult.FromError(preconditionResult.Value);
-
- string input = searchResult.Text;
- var matchingAliases = Aliases.Where(alias => input.StartsWith(alias));
-
- string matchingAlias = "";
- foreach (string alias in matchingAliases)
- {
- if (alias.Length > matchingAlias.Length)
- matchingAlias = alias;
- }
-
- input = input.Substring(matchingAlias.Length);
-
- return await CommandParser.ParseArgs(this, context, input, 0).ConfigureAwait(false);
- }
- public Task Execute(CommandContext context, ParseResult parseResult, IDependencyMap map)
- {
- if (!parseResult.IsSuccess)
- return Task.FromResult(ExecuteResult.FromError(parseResult));
-
- var argList = new object[parseResult.ArgValues.Count];
- for (int i = 0; i < parseResult.ArgValues.Count; i++)
- {
- if (!parseResult.ArgValues[i].IsSuccess)
- return Task.FromResult(ExecuteResult.FromError(parseResult.ArgValues[i]));
- argList[i] = parseResult.ArgValues[i].Values.First().Value;
- }
-
- var paramList = new object[parseResult.ParamValues.Count];
- for (int i = 0; i < parseResult.ParamValues.Count; i++)
- {
- if (!parseResult.ParamValues[i].IsSuccess)
- return Task.FromResult(ExecuteResult.FromError(parseResult.ParamValues[i]));
- paramList[i] = parseResult.ParamValues[i].Values.First().Value;
- }
-
- return Execute(context, argList, paramList, map);
- }
- public async Task Execute(CommandContext context, IEnumerable