| @@ -0,0 +1,72 @@ | |||||
| using Discord; | |||||
| using Discord.Commands; | |||||
| using Discord.SlashCommands; | |||||
| using Discord.WebSocket; | |||||
| using Microsoft.Extensions.DependencyInjection; | |||||
| using System; | |||||
| using System.Reflection; | |||||
| using System.Threading.Tasks; | |||||
| namespace SlashCommandsExample | |||||
| { | |||||
| class DiscordClient | |||||
| { | |||||
| public static DiscordSocketClient socketClient { get; set; } = new DiscordSocketClient(); | |||||
| public static SlashCommandService _commands { get; set; } | |||||
| public static IServiceProvider _services { get; set; } | |||||
| private string botToken = "<YOUR TOKEN HERE>"; | |||||
| public DiscordClient() | |||||
| { | |||||
| _commands = new SlashCommandService(); | |||||
| _services = new ServiceCollection() | |||||
| .AddSingleton(socketClient) | |||||
| .AddSingleton(_commands) | |||||
| .BuildServiceProvider(); | |||||
| socketClient.Log += SocketClient_Log; | |||||
| _commands.Log += SocketClient_Log; | |||||
| socketClient.InteractionCreated += InteractionHandler; | |||||
| // This is for dev purposes. | |||||
| // To avoid the situation in which you accidentally push your bot token to upstream, you can use | |||||
| // EnviromentVariables to store your key. | |||||
| botToken = Environment.GetEnvironmentVariable("DiscordSlashCommandsBotToken", EnvironmentVariableTarget.User); | |||||
| // Uncomment the next line of code to set the environment variable. | |||||
| // ------------------------------------------------------------------ | |||||
| // | WARNING! | | |||||
| // | | | |||||
| // | MAKE SURE TO DELETE YOUR TOKEN AFTER YOU HAVE SET THE VARIABLE | | |||||
| // | | | |||||
| // ------------------------------------------------------------------ | |||||
| //Environment.SetEnvironmentVariable("DiscordSlashCommandsBotToken", | |||||
| // "[YOUR TOKEN GOES HERE DELETE & COMMENT AFTER USE]", | |||||
| // EnvironmentVariableTarget.User); | |||||
| } | |||||
| public async Task RunAsync() | |||||
| { | |||||
| await socketClient.LoginAsync(TokenType.Bot, botToken); | |||||
| await socketClient.StartAsync(); | |||||
| await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services); | |||||
| await Task.Delay(-1); | |||||
| } | |||||
| private async Task InteractionHandler(SocketInteraction arg) | |||||
| { | |||||
| if(arg.Type == InteractionType.ApplicationCommand) | |||||
| { | |||||
| await _commands.ExecuteAsync(arg); | |||||
| } | |||||
| } | |||||
| private Task SocketClient_Log(LogMessage arg) | |||||
| { | |||||
| Console.WriteLine("[Discord] " + arg.ToString()); | |||||
| return Task.CompletedTask; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,20 @@ | |||||
| using Discord.SlashCommands; | |||||
| using Discord.WebSocket; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Text; | |||||
| namespace SlashCommandsExample.Modules | |||||
| { | |||||
| // Doesn't inherit from SlashCommandModule | |||||
| public class InvalidDefinition : Object | |||||
| { | |||||
| // commands | |||||
| } | |||||
| // Isn't public | |||||
| class PrivateDefinition : SlashCommandModule<SocketInteraction> | |||||
| { | |||||
| // commands | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,33 @@ | |||||
| using Discord.Commands; | |||||
| using Discord.Commands.SlashCommands.Types; | |||||
| using Discord.SlashCommands; | |||||
| using Discord.WebSocket; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace SlashCommandsExample.Modules | |||||
| { | |||||
| public class PingCommand : SlashCommandModule<SocketInteraction> | |||||
| { | |||||
| [SlashCommand("johnny-test", "Ping the bot to see if it is alive!")] | |||||
| public async Task PingAsync() | |||||
| { | |||||
| await Interaction.FollowupAsync(":white_check_mark: **Bot Online**"); | |||||
| } | |||||
| } | |||||
| } | |||||
| /* | |||||
| The base way of defining a command using the regular command service: | |||||
| public class PingModule : ModuleBase<SocketCommandContext> | |||||
| { | |||||
| [Command("ping")] | |||||
| [Summary("Pong! Check if the bot is alive.")] | |||||
| public async Task PingAsync() | |||||
| { | |||||
| await ReplyAsync(":white_check_mark: **Bot Online**"); | |||||
| } | |||||
| } | |||||
| */ | |||||
| @@ -4,14 +4,23 @@ | |||||
| * this project should be re-made into one that could be used as an example usage of the new Slash Command Service. | * this project should be re-made into one that could be used as an example usage of the new Slash Command Service. | ||||
| */ | */ | ||||
| using System; | using System; | ||||
| using System.Threading.Tasks; | |||||
| using Discord; | |||||
| using Discord.Commands; | |||||
| using Discord.SlashCommands; | |||||
| using Discord.WebSocket; | |||||
| namespace SlashCommandsExample | namespace SlashCommandsExample | ||||
| { | { | ||||
| class Program | class Program | ||||
| { | { | ||||
| static void Main(string[] args) | |||||
| static void Main(string[] args) | |||||
| { | { | ||||
| Console.WriteLine("Hello World!"); | Console.WriteLine("Hello World!"); | ||||
| } | |||||
| } | |||||
| DiscordClient discordClient = new DiscordClient(); | |||||
| // This could instead be handled in another thread, if for whatever reason you want to continue execution in the main Thread. | |||||
| discordClient.RunAsync().GetAwaiter().GetResult(); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -5,4 +5,18 @@ | |||||
| <TargetFramework>netcoreapp3.1</TargetFramework> | <TargetFramework>netcoreapp3.1</TargetFramework> | ||||
| </PropertyGroup> | </PropertyGroup> | ||||
| <ItemGroup> | |||||
| <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.0" /> | |||||
| </ItemGroup> | |||||
| <ItemGroup> | |||||
| <ProjectReference Include="..\src\Discord.Net.Analyzers\Discord.Net.Analyzers.csproj" /> | |||||
| <ProjectReference Include="..\src\Discord.Net.Commands\Discord.Net.Commands.csproj" /> | |||||
| <ProjectReference Include="..\src\Discord.Net.Core\Discord.Net.Core.csproj" /> | |||||
| <ProjectReference Include="..\src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" /> | |||||
| <ProjectReference Include="..\src\Discord.Net.Rest\Discord.Net.Rest.csproj" /> | |||||
| <ProjectReference Include="..\src\Discord.Net.Webhook\Discord.Net.Webhook.csproj" /> | |||||
| <ProjectReference Include="..\src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" /> | |||||
| </ItemGroup> | |||||
| </Project> | </Project> | ||||
| @@ -10,6 +10,7 @@ | |||||
| </PropertyGroup> | </PropertyGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" /> | <ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" /> | ||||
| <ProjectReference Include="..\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" /> | |||||
| </ItemGroup> | </ItemGroup> | ||||
| </Project> | </Project> | ||||
| @@ -15,15 +15,21 @@ namespace Discord.SlashCommands | |||||
| /// <summary> | /// <summary> | ||||
| /// The name of this slash command. | /// The name of this slash command. | ||||
| /// </summary> | /// </summary> | ||||
| public string CommandName; | |||||
| public string commandName; | |||||
| /// <summary> | |||||
| /// The description of this slash command. | |||||
| /// </summary> | |||||
| public string description; | |||||
| /// <summary> | /// <summary> | ||||
| /// Tells the <see cref="SlashCommandService"/> that this class/function is a slash command. | /// Tells the <see cref="SlashCommandService"/> that this class/function is a slash command. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="CommandName">The name of this slash command.</param> | |||||
| public SlashCommand(string CommandName) | |||||
| /// <param name="commandName">The name of this slash command.</param> | |||||
| public SlashCommand(string commandName, string description = "No description.") | |||||
| { | { | ||||
| this.CommandName = CommandName; | |||||
| this.commandName = commandName; | |||||
| this.description = description; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,64 @@ | |||||
| using Discord.Commands; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.SlashCommands | |||||
| { | |||||
| public class SlashCommandInfo | |||||
| { | |||||
| /// <summary> | |||||
| /// Gets the module that the command belongs in. | |||||
| /// </summary> | |||||
| public SlashModuleInfo Module { get; } | |||||
| /// <summary> | |||||
| /// Gets the name of the command. | |||||
| /// </summary> | |||||
| public string Name { get; } | |||||
| /// <summary> | |||||
| /// Gets the name of the command. | |||||
| /// </summary> | |||||
| public string Description { get; } | |||||
| /// <summary> | |||||
| /// The user method as a delegate. We need to use Delegate because there is an unknown number of parameters | |||||
| /// </summary> | |||||
| public Delegate userMethod; | |||||
| /// <summary> | |||||
| /// The callback that we call to start the delegate. | |||||
| /// </summary> | |||||
| public Func<object[], Task<IResult>> callback; | |||||
| public SlashCommandInfo(SlashModuleInfo module, string name, string description, Delegate userMethod) | |||||
| { | |||||
| Module = module; | |||||
| Name = name; | |||||
| Description = description; | |||||
| this.userMethod = userMethod; | |||||
| this.callback = new Func<object[], Task<IResult>>(async (args) => | |||||
| { | |||||
| // Try-catch it and see what we get - error or success | |||||
| try | |||||
| { | |||||
| await Task.Run(() => | |||||
| { | |||||
| userMethod.DynamicInvoke(args); | |||||
| }).ConfigureAwait(false); | |||||
| } | |||||
| catch(Exception e) | |||||
| { | |||||
| return ExecuteResult.FromError(e); | |||||
| } | |||||
| return ExecuteResult.FromSuccess(); | |||||
| }); | |||||
| } | |||||
| public async Task<IResult> ExecuteAsync(object[] args) | |||||
| { | |||||
| return await callback.Invoke(args).ConfigureAwait(false); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,47 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.SlashCommands | |||||
| { | |||||
| public class SlashModuleInfo | |||||
| { | |||||
| public SlashModuleInfo(SlashCommandService service) | |||||
| { | |||||
| Service = service; | |||||
| } | |||||
| /// <summary> | |||||
| /// Gets the command service associated with this module. | |||||
| /// </summary> | |||||
| public SlashCommandService Service { get; } | |||||
| /// <summary> | |||||
| /// Gets a read-only list of commands associated with this module. | |||||
| /// </summary> | |||||
| public List<SlashCommandInfo> Commands { get; private set; } | |||||
| /// <summary> | |||||
| /// The user command module defined as the interface ISlashCommandModule | |||||
| /// Used to set context. | |||||
| /// </summary> | |||||
| public ISlashCommandModule userCommandModule; | |||||
| public void SetCommands(List<SlashCommandInfo> commands) | |||||
| { | |||||
| if (this.Commands == null) | |||||
| { | |||||
| this.Commands = commands; | |||||
| } | |||||
| } | |||||
| public void SetCommandModule(object userCommandModule) | |||||
| { | |||||
| if (this.userCommandModule == null) | |||||
| { | |||||
| this.userCommandModule = userCommandModule as ISlashCommandModule; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,19 +1,37 @@ | |||||
| using Discord.Commands; | using Discord.Commands; | ||||
| using Discord.Logging; | |||||
| using Discord.WebSocket; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Reflection; | |||||
| using System.Threading; | |||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| namespace Discord.SlashCommands | namespace Discord.SlashCommands | ||||
| { | { | ||||
| public class SlashCommandService | public class SlashCommandService | ||||
| { | { | ||||
| private List<SlashCommandModule> _modules; | |||||
| // This semaphore is used to prevent race conditions. | |||||
| private readonly SemaphoreSlim _moduleLock; | |||||
| // This contains a dictionary of all definde SlashCommands, based on it's name | |||||
| public Dictionary<string, SlashCommandInfo> commandDefs; | |||||
| // This contains a list of all slash command modules defined by their user in their assembly. | |||||
| public Dictionary<Type, SlashModuleInfo> moduleDefs; | |||||
| // This is such a complicated method to log stuff... | |||||
| public event Func<LogMessage, Task> Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } } | |||||
| internal readonly AsyncEvent<Func<LogMessage, Task>> _logEvent = new AsyncEvent<Func<LogMessage, Task>>(); | |||||
| internal Logger _logger; | |||||
| internal LogManager _logManager; | |||||
| public SlashCommandService() // TODO: possible config? | public SlashCommandService() // TODO: possible config? | ||||
| { | { | ||||
| // max one thread | |||||
| _moduleLock = new SemaphoreSlim(1, 1); | |||||
| _logManager = new LogManager(LogSeverity.Info); | |||||
| _logManager.Message += async msg => await _logEvent.InvokeAsync(msg).ConfigureAwait(false); | |||||
| _logger = new Logger(_logManager, "SlshCommand"); | |||||
| } | } | ||||
| public void AddAssembly() | public void AddAssembly() | ||||
| @@ -21,10 +39,50 @@ namespace Discord.SlashCommands | |||||
| } | } | ||||
| public async Task<IResult> ExecuteAsync() | |||||
| /// <summary> | |||||
| /// Execute a slash command. | |||||
| /// </summary> | |||||
| /// <param name="interaction">Interaction data recieved from discord.</param> | |||||
| /// <returns></returns> | |||||
| public async Task<IResult> ExecuteAsync(SocketInteraction interaction) | |||||
| { | |||||
| // First, get the info about this command, if it exists | |||||
| SlashCommandInfo commandInfo; | |||||
| if (commandDefs.TryGetValue(interaction.Data.Name, out commandInfo)) | |||||
| { | |||||
| // TODO: implement everything that has to do with parameters :) | |||||
| // Then, set the context in which the command will be executed | |||||
| commandInfo.Module.userCommandModule.SetContext(interaction); | |||||
| // Then run the command (with no parameters) | |||||
| return await commandInfo.ExecuteAsync(new object[] { }).ConfigureAwait(false); | |||||
| } | |||||
| else | |||||
| { | |||||
| return SearchResult.FromError(CommandError.UnknownCommand, $"There is no registered slash command with the name {interaction.Data.Name}"); | |||||
| } | |||||
| } | |||||
| public async Task AddModulesAsync(Assembly assembly, IServiceProvider services) | |||||
| { | { | ||||
| // TODO: handle execution | |||||
| return null; | |||||
| // First take a hold of the module lock, as to make sure we aren't editing stuff while we do our business | |||||
| await _moduleLock.WaitAsync().ConfigureAwait(false); | |||||
| try | |||||
| { | |||||
| // Get all of the modules that were properly defined by the user. | |||||
| IReadOnlyList<TypeInfo> types = await SlashCommandServiceHelper.GetValidModuleClasses(assembly, this).ConfigureAwait(false); | |||||
| // Then, based on that, make an instance out of each of them, and get the resulting SlashModuleInfo s | |||||
| moduleDefs = await SlashCommandServiceHelper.InstantiateModules(types, this).ConfigureAwait(false); | |||||
| // After that, internally register all of the commands into SlashCommandInfo | |||||
| commandDefs = await SlashCommandServiceHelper.PrepareAsync(types,moduleDefs,this).ConfigureAwait(false); | |||||
| // TODO: And finally, register the commands with discord. | |||||
| await SlashCommandServiceHelper.RegisterCommands(commandDefs, this, services).ConfigureAwait(false); | |||||
| } | |||||
| finally | |||||
| { | |||||
| _moduleLock.Release(); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,151 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Linq.Expressions; | |||||
| using System.Reflection; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.SlashCommands | |||||
| { | |||||
| internal static class SlashCommandServiceHelper | |||||
| { | |||||
| /// <summary> | |||||
| /// Get all of the valid user-defined slash command modules | |||||
| /// </summary> | |||||
| public static async Task<IReadOnlyList<TypeInfo>> GetValidModuleClasses(Assembly assembly, SlashCommandService service) | |||||
| { | |||||
| var result = new List<TypeInfo>(); | |||||
| foreach (TypeInfo typeInfo in assembly.DefinedTypes) | |||||
| { | |||||
| if (IsValidModuleDefinition(typeInfo)) | |||||
| { | |||||
| // To simplify our lives, we need the modules to be public. | |||||
| if (typeInfo.IsPublic || typeInfo.IsNestedPublic) | |||||
| { | |||||
| result.Add(typeInfo); | |||||
| } | |||||
| else | |||||
| { | |||||
| await service._logger.WarningAsync($"Found class {typeInfo.FullName} as a valid SlashCommand Module, but it's not public!"); | |||||
| } | |||||
| } | |||||
| } | |||||
| return result; | |||||
| } | |||||
| private static bool IsValidModuleDefinition(TypeInfo typeInfo) | |||||
| { | |||||
| // See if the base type (SlashCommandInfo<T>) implements interface ISlashCommandModule | |||||
| return typeInfo.BaseType.GetInterfaces() | |||||
| .Any(n => n == typeof(ISlashCommandModule)); | |||||
| } | |||||
| /// <summary> | |||||
| /// Create an instance of each user-defined module | |||||
| /// </summary> | |||||
| public static async Task<Dictionary<Type, SlashModuleInfo>> InstantiateModules(IReadOnlyList<TypeInfo> types, SlashCommandService slashCommandService) | |||||
| { | |||||
| var result = new Dictionary<Type, SlashModuleInfo>(); | |||||
| foreach (Type userModuleType in types) | |||||
| { | |||||
| SlashModuleInfo moduleInfo = new SlashModuleInfo(slashCommandService); | |||||
| // If they want a constructor with different parameters, this is the place to add them. | |||||
| object instance = userModuleType.GetConstructor(Type.EmptyTypes).Invoke(null); | |||||
| moduleInfo.SetCommandModule(instance); | |||||
| result.Add(userModuleType, moduleInfo); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| /// <summary> | |||||
| /// Prepare all of the commands and register them internally. | |||||
| /// </summary> | |||||
| public static async Task<Dictionary<string, SlashCommandInfo>> PrepareAsync(IReadOnlyList<TypeInfo> types, Dictionary<Type, SlashModuleInfo> moduleDefs, SlashCommandService slashCommandService) | |||||
| { | |||||
| var result = new Dictionary<string, SlashCommandInfo>(); | |||||
| // fore each user-defined module | |||||
| foreach (var userModule in types) | |||||
| { | |||||
| // Get its associated information | |||||
| SlashModuleInfo moduleInfo; | |||||
| if (moduleDefs.TryGetValue(userModule, out moduleInfo)) | |||||
| { | |||||
| // and get all of its method, and check if they are valid, and if so create a new SlashCommandInfo for them. | |||||
| var commandMethods = userModule.GetMethods(); | |||||
| List<SlashCommandInfo> commandInfos = new List<SlashCommandInfo>(); | |||||
| foreach (var commandMethod in commandMethods) | |||||
| { | |||||
| SlashCommand slashCommand; | |||||
| if (IsValidSlashCommand(commandMethod, out slashCommand)) | |||||
| { | |||||
| Delegate delegateMethod = CreateDelegate(commandMethod, moduleInfo.userCommandModule); | |||||
| SlashCommandInfo commandInfo = new SlashCommandInfo( | |||||
| module: moduleInfo, | |||||
| name: slashCommand.commandName, | |||||
| description: slashCommand.description, | |||||
| userMethod: delegateMethod | |||||
| ); | |||||
| result.Add(slashCommand.commandName, commandInfo); | |||||
| commandInfos.Add(commandInfo); | |||||
| } | |||||
| } | |||||
| moduleInfo.SetCommands(commandInfos); | |||||
| } | |||||
| } | |||||
| return result; | |||||
| } | |||||
| private static bool IsValidSlashCommand(MethodInfo method, out SlashCommand slashCommand) | |||||
| { | |||||
| // Verify that we only have one [SlashCommand(...)] attribute | |||||
| IEnumerable<Attribute> slashCommandAttributes = method.GetCustomAttributes(typeof(SlashCommand)); | |||||
| if (slashCommandAttributes.Count() > 1) | |||||
| { | |||||
| throw new Exception("Too many SlashCommand attributes on a single method. It can only contain one!"); | |||||
| } | |||||
| // And at least one | |||||
| if (slashCommandAttributes.Count() == 0) | |||||
| { | |||||
| slashCommand = null; | |||||
| return false; | |||||
| } | |||||
| // And return the first (and only) attribute | |||||
| slashCommand = slashCommandAttributes.First() as SlashCommand; | |||||
| return true; | |||||
| } | |||||
| /// <summary> | |||||
| /// Creae a delegate from methodInfo. Taken from | |||||
| /// https://stackoverflow.com/a/40579063/8455128 | |||||
| /// </summary> | |||||
| public static Delegate CreateDelegate(MethodInfo methodInfo, object target) | |||||
| { | |||||
| Func<Type[], Type> getType; | |||||
| var isAction = methodInfo.ReturnType.Equals((typeof(void))); | |||||
| var types = methodInfo.GetParameters().Select(p => p.ParameterType); | |||||
| if (isAction) | |||||
| { | |||||
| getType = Expression.GetActionType; | |||||
| } | |||||
| else | |||||
| { | |||||
| getType = Expression.GetFuncType; | |||||
| types = types.Concat(new[] { methodInfo.ReturnType }); | |||||
| } | |||||
| if (methodInfo.IsStatic) | |||||
| { | |||||
| return Delegate.CreateDelegate(getType(types.ToArray()), methodInfo); | |||||
| } | |||||
| return Delegate.CreateDelegate(getType(types.ToArray()), target, methodInfo.Name); | |||||
| } | |||||
| public static async Task RegisterCommands(Dictionary<string, SlashCommandInfo> commandDefs, SlashCommandService slashCommandService, IServiceProvider services) | |||||
| { | |||||
| return; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,21 @@ | |||||
| using Discord.Commands.Builders; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.SlashCommands | |||||
| { | |||||
| public interface ISlashCommandModule | |||||
| { | |||||
| void SetContext(IDiscordInteraction interaction); | |||||
| //void BeforeExecute(CommandInfo command); | |||||
| //void AfterExecute(CommandInfo command); | |||||
| //void OnModuleBuilding(CommandService commandService, ModuleBuilder builder); | |||||
| } | |||||
| } | |||||
| @@ -1,14 +1,21 @@ | |||||
| using Discord.Commands.SlashCommands.Types; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Reflection; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.SlashCommands | namespace Discord.SlashCommands | ||||
| { | { | ||||
| internal class SlashCommandModule | |||||
| public class SlashCommandModule<T> : ISlashCommandModule where T : class, IDiscordInteraction | |||||
| { | { | ||||
| /// <summary> | |||||
| /// The underlying interaction of the command. | |||||
| /// </summary> | |||||
| /// <seealso cref="T:Discord.IDiscordInteraction"/> | |||||
| /// <seealso cref="T:Discord.WebSocket.SocketInteraction" /> | |||||
| public T Interaction { get; private set; } | |||||
| void ISlashCommandModule.SetContext(IDiscordInteraction interaction) | |||||
| { | |||||
| var newValue = interaction as T; | |||||
| Interaction = newValue ?? throw new InvalidOperationException($"Invalid interaction type. Expected {typeof(T).Name}, got {interaction.GetType().Name}."); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||