| @@ -5,6 +5,8 @@ VisualStudioVersion = 14.0.25123.0 | |||||
| MinimumVisualStudioVersion = 10.0.40219.1 | MinimumVisualStudioVersion = 10.0.40219.1 | ||||
| Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net", "src\Discord.Net\Discord.Net.xproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net", "src\Discord.Net\Discord.Net.xproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" | ||||
| EndProject | EndProject | ||||
| Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Commands", "src\Discord.Net.Commands\Discord.Net.Commands.xproj", "{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}" | |||||
| EndProject | |||||
| Global | Global | ||||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
| Debug|Any CPU = Debug|Any CPU | Debug|Any CPU = Debug|Any CPU | ||||
| @@ -15,6 +17,10 @@ Global | |||||
| {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU | ||||
| {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.Build.0 = Release|Any CPU | {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.Build.0 = Release|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 | |||||
| EndGlobalSection | EndGlobalSection | ||||
| GlobalSection(SolutionProperties) = preSolution | GlobalSection(SolutionProperties) = preSolution | ||||
| HideSolutionNode = FALSE | HideSolutionNode = FALSE | ||||
| @@ -0,0 +1,14 @@ | |||||
| using System; | |||||
| namespace Discord.Commands | |||||
| { | |||||
| [AttributeUsage(AttributeTargets.Method)] | |||||
| public class CommandAttribute : Attribute | |||||
| { | |||||
| public string Name { get; } | |||||
| public CommandAttribute(string name) | |||||
| { | |||||
| Name = name; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,14 @@ | |||||
| using System; | |||||
| namespace Discord.Commands | |||||
| { | |||||
| [AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter)] | |||||
| public class DescriptionAttribute : Attribute | |||||
| { | |||||
| public string Text { get; } | |||||
| public DescriptionAttribute(string text) | |||||
| { | |||||
| Text = text; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,14 @@ | |||||
| using System; | |||||
| namespace Discord.Commands | |||||
| { | |||||
| [AttributeUsage(AttributeTargets.Class)] | |||||
| public class GroupAttribute : Attribute | |||||
| { | |||||
| public string Name { get; } | |||||
| public GroupAttribute(string name) | |||||
| { | |||||
| Name = name; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| using System; | |||||
| namespace Discord.Commands | |||||
| { | |||||
| [AttributeUsage(AttributeTargets.Class)] | |||||
| public class ModuleAttribute : Attribute | |||||
| { | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| using System; | |||||
| namespace Discord.Commands | |||||
| { | |||||
| [AttributeUsage(AttributeTargets.Parameter)] | |||||
| public class UnparsedAttribute : Attribute | |||||
| { | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,106 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Reflection; | |||||
| using System.Threading; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.Commands | |||||
| { | |||||
| public class Module | |||||
| { | |||||
| public string Name { get; } | |||||
| public IEnumerable<Command> Commands { get; } | |||||
| internal Module(object module, TypeInfo typeInfo) | |||||
| { | |||||
| List<Command> commands = new List<Command>(); | |||||
| SearchClass(commands); | |||||
| Commands = commands; | |||||
| } | |||||
| private void SearchClass(List<Command> commands) | |||||
| { | |||||
| //TODO: Implement | |||||
| } | |||||
| } | |||||
| public class Command | |||||
| { | |||||
| public string SourceName { get; } | |||||
| } | |||||
| public class CommandParser | |||||
| { | |||||
| private readonly SemaphoreSlim _moduleLock; | |||||
| private readonly Dictionary<object, Module> _modules; | |||||
| public CommandParser() | |||||
| { | |||||
| _modules = new Dictionary<object, Module>(); | |||||
| _moduleLock = new SemaphoreSlim(1, 1); | |||||
| } | |||||
| public async Task<Module> Load(object module) | |||||
| { | |||||
| await _moduleLock.WaitAsync().ConfigureAwait(false); | |||||
| try | |||||
| { | |||||
| if (_modules.ContainsKey(module)) | |||||
| throw new ArgumentException($"This module has already been loaded."); | |||||
| return LoadInternal(module, module.GetType().GetTypeInfo()); | |||||
| } | |||||
| finally | |||||
| { | |||||
| _moduleLock.Release(); | |||||
| } | |||||
| } | |||||
| private Module LoadInternal(object module, TypeInfo typeInfo) | |||||
| { | |||||
| var loadedModule = new Module(module, typeInfo); | |||||
| _modules[module] = loadedModule; | |||||
| return loadedModule; | |||||
| } | |||||
| public async Task<IEnumerable<Module>> LoadAssembly(Assembly assembly) | |||||
| { | |||||
| List<Module> modules = new List<Module>(); | |||||
| await _moduleLock.WaitAsync().ConfigureAwait(false); | |||||
| try | |||||
| { | |||||
| foreach (var type in assembly.ExportedTypes) | |||||
| { | |||||
| var typeInfo = type.GetTypeInfo(); | |||||
| if (typeInfo.GetCustomAttribute<ModuleAttribute>() != null) | |||||
| { | |||||
| var constructor = typeInfo.DeclaredConstructors.Where(x => x.GetParameters().Length == 0).FirstOrDefault(); | |||||
| if (constructor == null) | |||||
| throw new InvalidOperationException($"Failed to find a valid constructor for \"{typeInfo.FullName}\""); | |||||
| object module; | |||||
| try { module = constructor.Invoke(null); } | |||||
| catch (Exception ex) | |||||
| { | |||||
| throw new InvalidOperationException($"Failed to create \"{typeInfo.FullName}\"", ex); | |||||
| } | |||||
| modules.Add(LoadInternal(module, typeInfo)); | |||||
| } | |||||
| } | |||||
| return modules; | |||||
| } | |||||
| finally | |||||
| { | |||||
| _moduleLock.Release(); | |||||
| } | |||||
| } | |||||
| public async Task<bool> Unload(object module) | |||||
| { | |||||
| await _moduleLock.WaitAsync().ConfigureAwait(false); | |||||
| try | |||||
| { | |||||
| return _modules.Remove(module); | |||||
| } | |||||
| finally | |||||
| { | |||||
| _moduleLock.Release(); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,19 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||||
| <PropertyGroup> | |||||
| <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion> | |||||
| <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> | |||||
| </PropertyGroup> | |||||
| <Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" /> | |||||
| <PropertyGroup Label="Globals"> | |||||
| <ProjectGuid>078dd7e6-943d-4d09-afc2-d2ba58b76c9c</ProjectGuid> | |||||
| <RootNamespace>Discord.Commands</RootNamespace> | |||||
| <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath> | |||||
| <OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath> | |||||
| <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion> | |||||
| </PropertyGroup> | |||||
| <PropertyGroup> | |||||
| <SchemaVersion>2.0</SchemaVersion> | |||||
| </PropertyGroup> | |||||
| <Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" /> | |||||
| </Project> | |||||
| @@ -0,0 +1,34 @@ | |||||
| { | |||||
| "version": "1.0.0-dev", | |||||
| "description": "A Discord.Net extension adding command support.", | |||||
| "authors": [ "RogueException" ], | |||||
| "packOptions": { | |||||
| "tags": [ "discord", "discordapp" ], | |||||
| "licenseUrl": "http://opensource.org/licenses/MIT", | |||||
| "projectUrl": "https://github.com/RogueException/Discord.Net", | |||||
| "repository": { | |||||
| "type": "git", | |||||
| "url": "git://github.com/RogueException/Discord.Net" | |||||
| } | |||||
| }, | |||||
| "buildOptions": { | |||||
| "allowUnsafe": true, | |||||
| "warningsAsErrors": false | |||||
| }, | |||||
| "dependencies": { | |||||
| "Discord.Net": "1.0.0-dev" | |||||
| }, | |||||
| "frameworks": { | |||||
| "netstandard1.3": { | |||||
| "imports": [ | |||||
| "dotnet5.4", | |||||
| "dnxcore50", | |||||
| "portable-net45+win8" | |||||
| ] | |||||
| } | |||||
| } | |||||
| } | |||||