From 4f4e16d674daabee148f599f5f78a8975dec0c6a Mon Sep 17 00:00:00 2001 From: Daan Boerlage Date: Tue, 9 Nov 2021 00:51:37 +0100 Subject: [PATCH] Add a way to lookup all bot commands in the bot assembly without using the commandService provided by discord.net --- src/Core/BotCommandLookup/CommandInfo.cs | 11 +++ src/Core/BotCommandLookup/CommandLookup.cs | 87 ++++++++++++++++++++++ src/Core/BotCommandLookup/ParameterInfo.cs | 8 ++ 3 files changed, 106 insertions(+) create mode 100644 src/Core/BotCommandLookup/CommandInfo.cs create mode 100644 src/Core/BotCommandLookup/CommandLookup.cs create mode 100644 src/Core/BotCommandLookup/ParameterInfo.cs diff --git a/src/Core/BotCommandLookup/CommandInfo.cs b/src/Core/BotCommandLookup/CommandInfo.cs new file mode 100644 index 0000000..c5793ac --- /dev/null +++ b/src/Core/BotCommandLookup/CommandInfo.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Geekbot.Core.BotCommandLookup; + +public struct CommandInfo +{ + public string Name { get; set; } + public Dictionary Parameters { get; set; } + public List Aliases { get; set; } + public string Summary { get; set; } +} \ No newline at end of file diff --git a/src/Core/BotCommandLookup/CommandLookup.cs b/src/Core/BotCommandLookup/CommandLookup.cs new file mode 100644 index 0000000..09fadf2 --- /dev/null +++ b/src/Core/BotCommandLookup/CommandLookup.cs @@ -0,0 +1,87 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Discord.Commands; + +namespace Geekbot.Core.BotCommandLookup; + +public class CommandLookup +{ + private readonly Assembly _assembly; + + public CommandLookup(Assembly assembly) + { + _assembly = assembly; + } + + public List GetCommands() + { + var commands = SearchCommands(_assembly); + var result = new List(); + commands.ForEach(x => GetCommandDefinition(ref result, x)); + + return result; + } + + private List SearchCommands(Assembly assembly) + { + bool IsLoadableModule(TypeInfo info) => info.DeclaredMethods.Any(x => x.GetCustomAttribute() != null || x.GetCustomAttribute() != null); + return assembly + .DefinedTypes + .Where(typeInfo => typeInfo.IsPublic || typeInfo.IsNestedPublic) + .Where(IsLoadableModule) + .ToList(); + } + + private void GetCommandDefinition(ref List commandInfos, TypeInfo commandType) + { + var methods = commandType + .GetMethods() + .Where(x => x.GetCustomAttribute() != null) + .ToList(); + + var commandGroup = (commandType.GetCustomAttributes().FirstOrDefault(attr => attr is GroupAttribute) as GroupAttribute)?.Prefix; + + foreach (var command in methods) + { + var commandInfo = new CommandInfo() + { + Parameters = new Dictionary(), + }; + + foreach (var attr in command.GetCustomAttributes()) + { + + switch (attr) + { + case SummaryAttribute name: + commandInfo.Summary = name.Text; + break; + case CommandAttribute name: + commandInfo.Name = string.IsNullOrEmpty(commandGroup) ? name.Text : $"{commandGroup} {name.Text}"; + break; + case AliasAttribute name: + commandInfo.Aliases = name.Aliases.ToList() ?? new List(); + break; + } + } + + foreach (var param in command.GetParameters()) + { + var paramName = param.Name ?? string.Empty; + var paramInfo = new ParameterInfo() + { + Summary = param.GetCustomAttribute()?.Text ?? string.Empty, + Type = param.ParameterType.Name, + DefaultValue = param.DefaultValue?.ToString() ?? string.Empty + }; + commandInfo.Parameters.Add(paramName, paramInfo); + } + + if (!string.IsNullOrEmpty(commandInfo.Name)) + { + commandInfos.Add(commandInfo); + } + } + } +} \ No newline at end of file diff --git a/src/Core/BotCommandLookup/ParameterInfo.cs b/src/Core/BotCommandLookup/ParameterInfo.cs new file mode 100644 index 0000000..afdfd50 --- /dev/null +++ b/src/Core/BotCommandLookup/ParameterInfo.cs @@ -0,0 +1,8 @@ +namespace Geekbot.Core.BotCommandLookup; + +public struct ParameterInfo +{ + public string Summary { get; set; } + public string Type { get; set; } + public string DefaultValue { get; set; } +} \ No newline at end of file