More fixes to the new config. Module loader reworked
This commit is contained in:
@@ -6,12 +6,12 @@ using DiscordBotCore.Plugin;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using DiscordBotCore.Others.Exceptions;
|
||||
using DiscordBotCore.Interfaces.Logger;
|
||||
using DiscordBotCore.Modules;
|
||||
using System.Diagnostics;
|
||||
using DiscordBotCore.Interfaces.Modules;
|
||||
using DiscordBotCore.Online.Helpers;
|
||||
using DiscordBotCore.Others.Settings;
|
||||
|
||||
@@ -39,32 +39,6 @@ namespace DiscordBotCore
|
||||
|
||||
public CustomSettingsDictionary ApplicationEnvironmentVariables { get; private set; }
|
||||
public InternalActionManager InternalActionManager { get; private set; }
|
||||
|
||||
public ILogger Logger
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
return _ModuleManager.GetModule<ILogger>();
|
||||
}
|
||||
catch (ModuleNotFoundException<ILogger> ex)
|
||||
{
|
||||
Console.WriteLine("No logger found");
|
||||
Console.WriteLine("Not having a valid logger is NOT an option. The default module will be downloaded from the official repo.");
|
||||
Console.WriteLine("Install the default one ? [y/n]");
|
||||
ConsoleKey response = Console.ReadKey().Key;
|
||||
if (response is ConsoleKey.Y)
|
||||
{
|
||||
Process.Start("DiscordBot", "--module-install LoggerModule");
|
||||
}
|
||||
|
||||
Environment.Exit(0);
|
||||
return null!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IPluginManager PluginManager { get; private set; }
|
||||
public Bot.App DiscordBotClient { get; internal set; }
|
||||
|
||||
@@ -88,9 +62,9 @@ namespace DiscordBotCore
|
||||
|
||||
CurrentApplication.ApplicationEnvironmentVariables = await CustomSettingsDictionary.CreateFromFile(_ConfigFile, true);
|
||||
|
||||
CurrentApplication.ApplicationEnvironmentVariables.Set("PluginFolder", _PluginsFolder);
|
||||
CurrentApplication.ApplicationEnvironmentVariables.Set("ResourceFolder", _ResourcesFolder);
|
||||
CurrentApplication.ApplicationEnvironmentVariables.Set("LogsFolder", _LogsFolder);
|
||||
CurrentApplication.ApplicationEnvironmentVariables.Add("PluginFolder", _PluginsFolder);
|
||||
CurrentApplication.ApplicationEnvironmentVariables.Add("ResourceFolder", _ResourcesFolder);
|
||||
CurrentApplication.ApplicationEnvironmentVariables.Add("LogsFolder", _LogsFolder);
|
||||
|
||||
|
||||
CurrentApplication._ModuleManager = new ModuleManager();
|
||||
@@ -117,7 +91,52 @@ namespace DiscordBotCore
|
||||
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<Type, List<object>> GetLoadedCoreModules() => _ModuleManager.LoadedModules.AsReadOnly();
|
||||
public static class Logger
|
||||
{
|
||||
public static async void LogException(Exception ex, object sender, bool fullStackTrace = false)
|
||||
{
|
||||
var loggerModule = CurrentApplication._ModuleManager.GetModule(ModuleType.Logger);
|
||||
await CurrentApplication._ModuleManager.InvokeMethod(loggerModule.Value, loggerModule.Key.MethodMapping["BaseLogException"], [ex, sender, fullStackTrace]);
|
||||
}
|
||||
|
||||
public static async void Log(string message)
|
||||
{
|
||||
var loggerModule = CurrentApplication._ModuleManager.GetModule(ModuleType.Logger);
|
||||
await CurrentApplication._ModuleManager.InvokeMethod(loggerModule.Value, loggerModule.Key.MethodMapping["BaseLog"], [message]);
|
||||
}
|
||||
|
||||
public static async void Log(string message, LogType logType, string format)
|
||||
{
|
||||
var loggerModule = CurrentApplication._ModuleManager.GetModule(ModuleType.Logger);
|
||||
await CurrentApplication._ModuleManager.InvokeMethod(loggerModule.Value, loggerModule.Key.MethodMapping["LogWithTypeAndFormat"], [message, logType, format]);
|
||||
}
|
||||
|
||||
public static async void Log(string message, LogType logType)
|
||||
{
|
||||
var loggerModule = CurrentApplication._ModuleManager.GetModule(ModuleType.Logger);
|
||||
await CurrentApplication._ModuleManager.InvokeMethod(loggerModule.Value, loggerModule.Key.MethodMapping["LogWithType"], [message, logType]);
|
||||
}
|
||||
|
||||
public static async void Log(string message, object sender)
|
||||
{
|
||||
var loggerModule = CurrentApplication._ModuleManager.GetModule(ModuleType.Logger);
|
||||
await CurrentApplication._ModuleManager.InvokeMethod(loggerModule.Value, loggerModule.Key.MethodMapping["LogWithSender"], [message, sender]);
|
||||
}
|
||||
|
||||
public static async void Log(string message, object sender, LogType type)
|
||||
{
|
||||
var loggerModule = CurrentApplication._ModuleManager.GetModule(ModuleType.Logger);
|
||||
await CurrentApplication._ModuleManager.InvokeMethod(loggerModule.Value, loggerModule.Key.MethodMapping["LogWithTypeAndSender"], [message, sender, type]);
|
||||
}
|
||||
|
||||
public static async void SetOutFunction(Action<string> outFunction)
|
||||
{
|
||||
var loggerModule = CurrentApplication._ModuleManager.GetModule(ModuleType.Logger);
|
||||
await CurrentApplication._ModuleManager.InvokeMethod(loggerModule.Value, loggerModule.Key.MethodMapping["SetPrintFunction"], [outFunction]);
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlyDictionary<ModuleData, IModule> GetLoadedCoreModules() => _ModuleManager.Modules.AsReadOnly();
|
||||
|
||||
public static string GetResourceFullPath(string path)
|
||||
{
|
||||
|
||||
@@ -107,7 +107,7 @@ public class App
|
||||
if (arg.Message.Contains("401"))
|
||||
{
|
||||
Application.CurrentApplication.ApplicationEnvironmentVariables.Remove("token");
|
||||
Application.CurrentApplication.Logger.Log("The token is invalid.", this, LogType.Critical);
|
||||
Application.Logger.Log("The token is invalid.", this, LogType.Critical);
|
||||
await Application.CurrentApplication.ApplicationEnvironmentVariables.SaveToFile();
|
||||
await Task.Delay(3000);
|
||||
|
||||
@@ -124,7 +124,7 @@ public class App
|
||||
|
||||
private Task LoggedIn()
|
||||
{
|
||||
Application.CurrentApplication.Logger.Log("Successfully Logged In", this);
|
||||
Application.Logger.Log("Successfully Logged In", this);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -134,12 +134,12 @@ public class App
|
||||
{
|
||||
case LogSeverity.Error:
|
||||
case LogSeverity.Critical:
|
||||
Application.CurrentApplication.Logger.Log(message.Message, this, LogType.Error);
|
||||
Application.Logger.Log(message.Message, this, LogType.Error);
|
||||
break;
|
||||
|
||||
case LogSeverity.Info:
|
||||
case LogSeverity.Debug:
|
||||
Application.CurrentApplication.Logger.Log(message.Message, this, LogType.Info);
|
||||
Application.Logger.Log(message.Message, this, LogType.Info);
|
||||
|
||||
|
||||
break;
|
||||
|
||||
@@ -51,12 +51,12 @@ internal class CommandHandler
|
||||
throw new Exception("Failed to run command !");
|
||||
|
||||
if (arg.Channel is SocketDMChannel)
|
||||
plugin.ExecuteDM(arg);
|
||||
plugin.ExecuteDm(arg);
|
||||
else plugin.ExecuteServer(arg);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Application.CurrentApplication.Logger.LogException(ex, this);
|
||||
Application.Logger.LogException(ex, this);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
@@ -91,7 +91,7 @@ internal class CommandHandler
|
||||
|
||||
await _commandService.ExecuteAsync(context, argPos, null);
|
||||
|
||||
DBCommand? plugin;
|
||||
IDbCommand? plugin;
|
||||
var cleanMessage = "";
|
||||
|
||||
if (message.HasMentionPrefix(_client.CurrentUser, ref argPos))
|
||||
@@ -129,7 +129,7 @@ internal class CommandHandler
|
||||
if (plugin is null)
|
||||
return;
|
||||
|
||||
if (plugin.requireAdmin && !context.Message.Author.IsAdmin())
|
||||
if (plugin.RequireAdmin && !context.Message.Author.IsAdmin())
|
||||
return;
|
||||
|
||||
var split = cleanMessage.Split(' ');
|
||||
@@ -140,19 +140,19 @@ internal class CommandHandler
|
||||
|
||||
DbCommandExecutingArguments cmd = new(context, cleanMessage, split[0], argsClean);
|
||||
|
||||
Application.CurrentApplication.Logger.Log(
|
||||
Application.Logger.Log(
|
||||
$"User ({context.User.Username}) from Guild \"{context.Guild.Name}\" executed command \"{cmd.CleanContent}\"",
|
||||
this,
|
||||
LogType.Info
|
||||
);
|
||||
|
||||
if (context.Channel is SocketDMChannel)
|
||||
plugin.ExecuteDM(cmd);
|
||||
plugin.ExecuteDm(cmd);
|
||||
else plugin.ExecuteServer(cmd);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Application.CurrentApplication.Logger.LogException(ex, this);
|
||||
Application.Logger.LogException(ex, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using DiscordBotCore.Others;
|
||||
|
||||
namespace DiscordBotCore.Interfaces;
|
||||
|
||||
public interface DBCommand
|
||||
public interface IDbCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Command to be executed
|
||||
@@ -30,7 +30,7 @@ public interface DBCommand
|
||||
/// <summary>
|
||||
/// true if the command requre admin, otherwise false
|
||||
/// </summary>
|
||||
bool requireAdmin { get; }
|
||||
bool RequireAdmin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The main body of the command. This is what is executed when user calls the command in Server
|
||||
@@ -44,7 +44,7 @@ public interface DBCommand
|
||||
/// The main body of the command. This is what is executed when user calls the command in DM
|
||||
/// </summary>
|
||||
/// <param name="args">The disocrd Context</param>
|
||||
void ExecuteDM(DbCommandExecutingArguments args)
|
||||
void ExecuteDm(DbCommandExecutingArguments args)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace DiscordBotCore.Interfaces;
|
||||
|
||||
public interface DBEvent
|
||||
public interface IDbEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the event
|
||||
@@ -6,11 +6,11 @@ using Discord.WebSocket;
|
||||
|
||||
namespace DiscordBotCore.Interfaces;
|
||||
|
||||
public interface DBSlashCommand
|
||||
public interface IDbSlashCommand
|
||||
{
|
||||
string Name { get; }
|
||||
string Description { get; }
|
||||
bool canUseDM { get; }
|
||||
bool CanUseDm { get; }
|
||||
bool HasInteraction { get; }
|
||||
|
||||
List<SlashCommandOptionBuilder> Options { get; }
|
||||
@@ -18,7 +18,7 @@ public interface DBSlashCommand
|
||||
void ExecuteServer(SocketSlashCommand context)
|
||||
{ }
|
||||
|
||||
void ExecuteDM(SocketSlashCommand context) { }
|
||||
void ExecuteDm(SocketSlashCommand context) { }
|
||||
|
||||
Task ExecuteInteraction(SocketInteraction interaction) => Task.CompletedTask;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using DiscordBotCore.Others;
|
||||
|
||||
using System;
|
||||
|
||||
namespace DiscordBotCore.Interfaces.Logger
|
||||
{
|
||||
public interface ILogMessage
|
||||
{
|
||||
public string Message { get; protected set; }
|
||||
public DateTime ThrowTime { get; protected set; }
|
||||
public string SenderName { get; protected set; }
|
||||
public LogType LogMessageType { get; protected set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using DiscordBotCore.Interfaces.Modules;
|
||||
using DiscordBotCore.Others;
|
||||
|
||||
using System;
|
||||
|
||||
namespace DiscordBotCore.Interfaces.Logger
|
||||
{
|
||||
public interface ILogger : IBaseModule
|
||||
{
|
||||
public struct FormattedMessage {
|
||||
public string Message;
|
||||
public LogType Type;
|
||||
}
|
||||
|
||||
string LogMessageFormat { get; set; }
|
||||
|
||||
void Log(string message);
|
||||
void Log(string message, LogType logType);
|
||||
void Log(string message, LogType logType, string format);
|
||||
void Log(string message, object Sender);
|
||||
void Log(string message, object Sender, LogType type);
|
||||
void LogException(Exception exception, object Sender, bool logFullStack = false);
|
||||
|
||||
void SetOutFunction(Action<string> outFunction);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace DiscordBotCore.Interfaces.Modules
|
||||
{
|
||||
public interface IBaseModule
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,20 @@
|
||||
|
||||
namespace DiscordBotCore.Interfaces.Modules
|
||||
{
|
||||
public interface IModule<T> where T : IBaseModule
|
||||
|
||||
public enum ModuleType
|
||||
{
|
||||
Logger,
|
||||
Other
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Define a module.
|
||||
/// </summary>
|
||||
public interface IModule
|
||||
{
|
||||
public ModuleType ModuleType { get; }
|
||||
public string Name { get; }
|
||||
public T Module { get; }
|
||||
public Task Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ namespace DiscordBotCore.Interfaces.PluginManager
|
||||
{
|
||||
public string BaseUrl { get; set; }
|
||||
public string Branch { get; set; }
|
||||
|
||||
Task AppendPluginToDatabase(PluginInfo pluginData);
|
||||
Task CheckForUpdates();
|
||||
Task ExecutePluginInstallScripts(List<OnlineScriptDependencyInfo> listOfDependencies);
|
||||
@@ -24,7 +23,6 @@ namespace DiscordBotCore.Interfaces.PluginManager
|
||||
Task<bool> MarkPluginToUninstall(string pluginName);
|
||||
Task RemovePluginFromDatabase(string pluginName);
|
||||
Task UninstallMarkedPlugins();
|
||||
|
||||
Task SetEnabledStatus(string pluginName, bool status);
|
||||
}
|
||||
}
|
||||
@@ -35,9 +35,9 @@ internal class Loader
|
||||
}
|
||||
}
|
||||
|
||||
await LoadEverythingOfType<DBEvent>();
|
||||
await LoadEverythingOfType<DBCommand>();
|
||||
await LoadEverythingOfType<DBSlashCommand>();
|
||||
await LoadEverythingOfType<IDbEvent>();
|
||||
await LoadEverythingOfType<IDbCommand>();
|
||||
await LoadEverythingOfType<IDbSlashCommand>();
|
||||
await LoadEverythingOfType<ICommandAction>();
|
||||
}
|
||||
|
||||
@@ -60,9 +60,9 @@ internal class Loader
|
||||
|
||||
var pluginType = plugin switch
|
||||
{
|
||||
DBEvent => PluginType.EVENT,
|
||||
DBCommand => PluginType.COMMAND,
|
||||
DBSlashCommand => PluginType.SLASH_COMMAND,
|
||||
IDbEvent => PluginType.EVENT,
|
||||
IDbCommand => PluginType.COMMAND,
|
||||
IDbSlashCommand => PluginType.SLASH_COMMAND,
|
||||
ICommandAction => PluginType.ACTION,
|
||||
_ => PluginType.UNKNOWN
|
||||
};
|
||||
|
||||
@@ -6,23 +6,24 @@ using System.Threading.Tasks;
|
||||
|
||||
using DiscordBotCore.Interfaces.Modules;
|
||||
using System.Reflection;
|
||||
using DiscordBotCore.Modules;
|
||||
|
||||
namespace DiscordBotCore.Loaders
|
||||
{
|
||||
internal class ModuleLoader
|
||||
{
|
||||
private readonly string _ModuleFolder;
|
||||
private readonly List<ModuleData> _ModuleData;
|
||||
|
||||
public ModuleLoader(string moduleFolder)
|
||||
public ModuleLoader(List<ModuleData> moduleFolder)
|
||||
{
|
||||
_ModuleFolder = moduleFolder;
|
||||
Directory.CreateDirectory(moduleFolder);
|
||||
_ModuleData = moduleFolder;
|
||||
}
|
||||
|
||||
public Task LoadFileModules()
|
||||
{
|
||||
var files = Directory.GetFiles(_ModuleFolder, "*.dll");
|
||||
foreach (var file in files)
|
||||
var paths = _ModuleData.Select(module => module.ModulePath);
|
||||
|
||||
foreach (var file in paths)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -37,20 +38,20 @@ namespace DiscordBotCore.Loaders
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<List<IModule<T>>> LoadModules<T>() where T : IBaseModule
|
||||
public Task<List<IModule>> LoadModules()
|
||||
{
|
||||
var moduleType = typeof(IModule<T>);
|
||||
var moduleType = typeof(IModule);
|
||||
var moduleTypes = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(s => s.GetTypes())
|
||||
.Where(p => moduleType.IsAssignableFrom(p) && !p.IsInterface);
|
||||
|
||||
var modules = new List<IModule<T>>();
|
||||
var modules = new List<IModule>();
|
||||
foreach (var module in moduleTypes)
|
||||
{
|
||||
try
|
||||
{
|
||||
var instance = (IModule<T>?)Activator.CreateInstance(module);
|
||||
if (instance == null)
|
||||
var instance = (IModule?)Activator.CreateInstance(module);
|
||||
if (instance is null)
|
||||
{
|
||||
Console.WriteLine($"Error loading module {module.Name}: Could not create instance");
|
||||
continue;
|
||||
|
||||
@@ -26,9 +26,9 @@ public class PluginLoader
|
||||
public SlashCommandLoaded? OnSlashCommandLoaded;
|
||||
public ActionLoaded? OnActionLoaded;
|
||||
|
||||
public static List<DBCommand> Commands { get; private set; } = new List<DBCommand>();
|
||||
public static List<DBEvent> Events { get; private set; } = new List<DBEvent>();
|
||||
public static List<DBSlashCommand> SlashCommands { get; private set; } = new List<DBSlashCommand>();
|
||||
public static List<IDbCommand> Commands { get; private set; } = new List<IDbCommand>();
|
||||
public static List<IDbEvent> Events { get; private set; } = new List<IDbEvent>();
|
||||
public static List<IDbSlashCommand> SlashCommands { get; private set; } = new List<IDbSlashCommand>();
|
||||
public static List<ICommandAction> Actions { get; private set; } = new List<ICommandAction>();
|
||||
|
||||
public PluginLoader(DiscordSocketClient discordSocketClient)
|
||||
@@ -41,7 +41,7 @@ public class PluginLoader
|
||||
|
||||
if (_Client == null)
|
||||
{
|
||||
Application.CurrentApplication.Logger.Log("Discord client is null", this, LogType.Error);
|
||||
Application.Logger.Log("Discord client is null", this, LogType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public class PluginLoader
|
||||
SlashCommands.Clear();
|
||||
Actions.Clear();
|
||||
|
||||
Application.CurrentApplication.Logger.Log("Loading plugins...", this);
|
||||
Application.Logger.Log("Loading plugins...", this);
|
||||
|
||||
var loader = new Loader();
|
||||
|
||||
@@ -62,7 +62,7 @@ public class PluginLoader
|
||||
|
||||
private void FileLoadedException(FileLoaderResult result)
|
||||
{
|
||||
Application.CurrentApplication.Logger.Log(result.ErrorMessage, this, LogType.Error);
|
||||
Application.Logger.Log(result.ErrorMessage, this, LogType.Error);
|
||||
}
|
||||
|
||||
private async void OnPluginLoaded(PluginLoadResultData result)
|
||||
@@ -81,31 +81,31 @@ public class PluginLoader
|
||||
|
||||
break;
|
||||
case PluginType.COMMAND:
|
||||
Commands.Add((DBCommand)result.Plugin);
|
||||
Commands.Add((IDbCommand)result.Plugin);
|
||||
OnCommandLoaded?.Invoke(result);
|
||||
break;
|
||||
case PluginType.EVENT:
|
||||
if (this.TryStartEvent((DBEvent)result.Plugin))
|
||||
if (this.TryStartEvent((IDbEvent)result.Plugin))
|
||||
{
|
||||
Events.Add((DBEvent)result.Plugin);
|
||||
Events.Add((IDbEvent)result.Plugin);
|
||||
OnEventLoaded?.Invoke(result);
|
||||
}
|
||||
|
||||
break;
|
||||
case PluginType.SLASH_COMMAND:
|
||||
if (await this.TryStartSlashCommand((DBSlashCommand)result.Plugin))
|
||||
if (await this.TryStartSlashCommand((IDbSlashCommand)result.Plugin))
|
||||
{
|
||||
if(((DBSlashCommand)result.Plugin).HasInteraction)
|
||||
_Client.InteractionCreated += ((DBSlashCommand)result.Plugin).ExecuteInteraction;
|
||||
SlashCommands.Add((DBSlashCommand)result.Plugin);
|
||||
if(((IDbSlashCommand)result.Plugin).HasInteraction)
|
||||
_Client.InteractionCreated += ((IDbSlashCommand)result.Plugin).ExecuteInteraction;
|
||||
SlashCommands.Add((IDbSlashCommand)result.Plugin);
|
||||
OnSlashCommandLoaded?.Invoke(result);
|
||||
}
|
||||
else
|
||||
Application.CurrentApplication.Logger.Log($"Failed to start slash command {result.PluginName}", this, LogType.Error);
|
||||
Application.Logger.Log($"Failed to start slash command {result.PluginName}", this, LogType.Error);
|
||||
break;
|
||||
case PluginType.UNKNOWN:
|
||||
default:
|
||||
Application.CurrentApplication.Logger.Log("Unknown plugin type", this, LogType.Error);
|
||||
Application.Logger.Log("Unknown plugin type", this, LogType.Error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace DiscordBotCore.Loaders;
|
||||
|
||||
internal static class PluginLoaderExtensions
|
||||
{
|
||||
internal static bool TryStartEvent(this PluginLoader pluginLoader, DBEvent? dbEvent)
|
||||
internal static bool TryStartEvent(this PluginLoader pluginLoader, IDbEvent? dbEvent)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -28,13 +28,13 @@ internal static class PluginLoaderExtensions
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Application.CurrentApplication.Logger.Log($"Error starting event {dbEvent.Name}: {e.Message}", typeof(PluginLoader), LogType.Error);
|
||||
Application.CurrentApplication.Logger.LogException(e, typeof(PluginLoader));
|
||||
Application.Logger.Log($"Error starting event {dbEvent.Name}: {e.Message}", typeof(PluginLoader), LogType.Error);
|
||||
Application.Logger.LogException(e, typeof(PluginLoader));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal static async Task<bool> TryStartSlashCommand(this PluginLoader pluginLoader, DBSlashCommand? dbSlashCommand)
|
||||
internal static async Task<bool> TryStartSlashCommand(this PluginLoader pluginLoader, IDbSlashCommand? dbSlashCommand)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -53,7 +53,7 @@ internal static class PluginLoaderExtensions
|
||||
builder.WithDescription(dbSlashCommand.Description);
|
||||
builder.Options = dbSlashCommand.Options;
|
||||
|
||||
if (dbSlashCommand.canUseDM)
|
||||
if (dbSlashCommand.CanUseDm)
|
||||
builder.WithContextTypes(InteractionContextType.BotDm, InteractionContextType.Guild);
|
||||
else
|
||||
builder.WithContextTypes(InteractionContextType.Guild);
|
||||
@@ -64,7 +64,7 @@ internal static class PluginLoaderExtensions
|
||||
|
||||
if (!result)
|
||||
{
|
||||
Application.CurrentApplication.Logger.Log($"Failed to enable slash command {dbSlashCommand.Name} for guild {guildId}", typeof(PluginLoader), LogType.Error);
|
||||
Application.Logger.Log($"Failed to enable slash command {dbSlashCommand.Name} for guild {guildId}", typeof(PluginLoader), LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ internal static class PluginLoaderExtensions
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Application.CurrentApplication.Logger.Log($"Error starting slash command {dbSlashCommand.Name}: {e.Message}", typeof(PluginLoader), LogType.Error);
|
||||
Application.Logger.Log($"Error starting slash command {dbSlashCommand.Name}: {e.Message}", typeof(PluginLoader), LogType.Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,7 @@ internal static class PluginLoaderExtensions
|
||||
SocketGuild? guild = pluginLoader._Client.GetGuild(guildId);
|
||||
if (guild is null)
|
||||
{
|
||||
Application.CurrentApplication.Logger.Log("Failed to get guild with ID " + guildId, typeof(PluginLoader), LogType.Error);
|
||||
Application.Logger.Log("Failed to get guild with ID " + guildId, typeof(PluginLoader), LogType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
19
DiscordBotCore/Modules/ModuleData.cs
Normal file
19
DiscordBotCore/Modules/ModuleData.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DiscordBotCore.Modules;
|
||||
|
||||
public class ModuleData
|
||||
{
|
||||
public string ModuleName { get; set; }
|
||||
public string ModulePath { get; set; }
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
public IDictionary<string, string> MethodMapping { get; set; }
|
||||
|
||||
public ModuleData(string moduleName, string modulePath, IDictionary<string, string> methodMapping, bool isEnabled)
|
||||
{
|
||||
ModuleName = moduleName;
|
||||
ModulePath = modulePath;
|
||||
MethodMapping = methodMapping;
|
||||
IsEnabled = isEnabled;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using DiscordBotCore.Online;
|
||||
@@ -11,20 +8,24 @@ namespace DiscordBotCore.Modules
|
||||
{
|
||||
public class ModuleDownloader
|
||||
{
|
||||
private string _moduleName;
|
||||
private readonly string _baseUrl = "https://raw.githubusercontent.com/andreitdr/SethPlugins/tests/Modules/";
|
||||
private readonly string _moduleFolder = "./Data/Modules";
|
||||
private readonly string _ModuleName;
|
||||
private const string _BaseUrl = "https://raw.githubusercontent.com/andreitdr/SethPlugins/tests/Modules/";
|
||||
|
||||
public ModuleDownloader(string moduleName)
|
||||
{
|
||||
_moduleName = moduleName;
|
||||
_ModuleName = moduleName;
|
||||
}
|
||||
|
||||
public async Task DownloadModule(IProgress<float> progressToWrite)
|
||||
{
|
||||
Directory.CreateDirectory(_moduleFolder);
|
||||
string url = _baseUrl + _moduleName + ".dll";
|
||||
await ServerCom.DownloadFileAsync(url, _moduleFolder + "/" + _moduleName + ".dll", progressToWrite);
|
||||
string? moduleFolder = Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("ModuleFolder");
|
||||
|
||||
if(moduleFolder is null)
|
||||
throw new DirectoryNotFoundException("Module folder not found"); // Should never happen
|
||||
|
||||
Directory.CreateDirectory(moduleFolder);
|
||||
string url = _BaseUrl + _ModuleName + ".dll";
|
||||
await ServerCom.DownloadFileAsync(url, moduleFolder + "/" + _ModuleName + ".dll", progressToWrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using DiscordBotCore.Interfaces.Logger;
|
||||
using DiscordBotCore.Interfaces.Modules;
|
||||
using DiscordBotCore.Loaders;
|
||||
using DiscordBotCore.Others.Exceptions;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace DiscordBotCore.Modules
|
||||
{
|
||||
internal class ModuleManager
|
||||
{
|
||||
private static readonly string _BaseModuleFolder = "./Data/Modules";
|
||||
|
||||
private readonly string _ModuleFolder;
|
||||
internal Dictionary<Type, List<object>> LoadedModules { get; }
|
||||
|
||||
public ModuleManager(string moduleFolder)
|
||||
{
|
||||
_ModuleFolder = moduleFolder;
|
||||
LoadedModules = new Dictionary<Type, List<object>>();
|
||||
}
|
||||
private static readonly string _BaseModuleConfig = "./Data/Resources/modules.json";
|
||||
internal Dictionary<ModuleData, IModule> Modules { get; set; }
|
||||
|
||||
public ModuleManager()
|
||||
{
|
||||
_ModuleFolder = Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("ModuleFolder", _BaseModuleFolder);
|
||||
LoadedModules = new Dictionary<Type, List<object>>();
|
||||
Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("ModuleFolder", _BaseModuleFolder);
|
||||
Modules = new Dictionary<ModuleData, IModule>();
|
||||
}
|
||||
|
||||
public T GetModule<T>() where T : IBaseModule
|
||||
public KeyValuePair<ModuleData, IModule> GetModule(string moduleName)
|
||||
{
|
||||
if(!LoadedModules.ContainsKey(typeof(T)))
|
||||
throw new ModuleNotFoundException<T>();
|
||||
|
||||
if (!LoadedModules[typeof(T)].Any())
|
||||
throw new ModuleNotFoundException<T>();
|
||||
|
||||
IModule<T> module = (IModule<T>)LoadedModules[typeof(T)][0];
|
||||
return module.Module;
|
||||
return Modules.FirstOrDefault(module => module.Key.ModuleName == moduleName);
|
||||
}
|
||||
|
||||
public KeyValuePair<ModuleData, IModule> GetModule(ModuleType moduleType)
|
||||
{
|
||||
return Modules.First(module => module.Value.ModuleType == moduleType);
|
||||
}
|
||||
|
||||
public async Task LoadModules()
|
||||
{
|
||||
ModuleLoader loader = new ModuleLoader(_ModuleFolder);
|
||||
await loader.LoadFileModules();
|
||||
|
||||
string moduleConfigPath = Application.CurrentApplication.ApplicationEnvironmentVariables
|
||||
.Get<string>("ModuleConfig", _BaseModuleConfig);
|
||||
|
||||
var loggers = await loader.LoadModules<ILogger>();
|
||||
foreach (var logger in loggers)
|
||||
string moduleConfigFile = await File.ReadAllTextAsync(moduleConfigPath);
|
||||
List<ModuleData>? listOfModuleData = JsonConvert.DeserializeObject<List<ModuleData>>(moduleConfigFile);
|
||||
|
||||
if(listOfModuleData is null)
|
||||
return;
|
||||
|
||||
if (!listOfModuleData.Any())
|
||||
{
|
||||
await logger.Initialize();
|
||||
Console.WriteLine("Module Loaded: " + logger.Name);
|
||||
return;
|
||||
}
|
||||
|
||||
ModuleLoader moduleLoader = new ModuleLoader(listOfModuleData);
|
||||
await moduleLoader.LoadFileModules();
|
||||
var modules = await moduleLoader.LoadModules();
|
||||
|
||||
foreach (var module in modules)
|
||||
{
|
||||
|
||||
ModuleData? moduleData = listOfModuleData.FirstOrDefault(data => data.ModuleName == module.Name);
|
||||
|
||||
if (moduleData is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (moduleData.IsEnabled)
|
||||
{
|
||||
await module.Initialize(); // TODO: Add error handling
|
||||
Modules.Add(moduleData, module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InvokeMethod(string moduleName, string methodName, object[] parameters)
|
||||
{
|
||||
IModule module = GetModule(moduleName).Value;
|
||||
var method = module.GetType().GetMethod(methodName);
|
||||
|
||||
if (method is null)
|
||||
{
|
||||
throw new Exception("Method not found"); // TODO: Add custom exception
|
||||
}
|
||||
|
||||
LoadedModules.Add(typeof(ILogger), loggers.Cast<object>().ToList());
|
||||
await Task.Run(() => method.Invoke(module, parameters));
|
||||
}
|
||||
|
||||
public async Task InvokeMethod(IModule module, string methodName, object[] parameters)
|
||||
{
|
||||
var method = module.GetType().GetMethod(methodName);
|
||||
|
||||
if (method is null)
|
||||
{
|
||||
throw new Exception($"Method not found {methodName}");
|
||||
}
|
||||
|
||||
await Task.Run(() => method.Invoke(module, parameters));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ public class PluginManager : IPluginManager
|
||||
{
|
||||
if (await pluginUpdater.HasUpdate(plugin.PluginName))
|
||||
{
|
||||
Application.CurrentApplication.Logger.Log("Updating plugin: " + plugin.PluginName, this, LogType.Info);
|
||||
Application.Logger.Log("Updating plugin: " + plugin.PluginName, this, LogType.Info);
|
||||
await pluginUpdater.UpdatePlugin(plugin.PluginName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public class InternalActionManager
|
||||
if (this.Actions.ContainsKey(action.ActionName))
|
||||
{
|
||||
// This should never happen. If it does, log it and return
|
||||
Application.CurrentApplication.Logger.Log($"Action {action.ActionName} already exists", this, LogType.Error);
|
||||
Application.Logger.Log($"Action {action.ActionName} already exists", this, LogType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public class InternalActionManager
|
||||
{
|
||||
if (!Actions.ContainsKey(actionName))
|
||||
{
|
||||
Application.CurrentApplication.Logger.Log($"Action {actionName} not found", this, LogType.Error);
|
||||
Application.Logger.Log($"Action {actionName} not found", this, LogType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public class InternalActionManager
|
||||
{
|
||||
if (Actions[actionName].RunType == InternalActionRunType.OnStartup)
|
||||
{
|
||||
Application.CurrentApplication.Logger.Log($"Action {actionName} is not executable", this, LogType.Error);
|
||||
Application.Logger.Log($"Action {actionName} is not executable", this, LogType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public class InternalActionManager
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Application.CurrentApplication.Logger.Log(e.Message, type: LogType.Error, Sender: this);
|
||||
Application.Logger.Log(e.Message, type: LogType.Error, sender: this);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ public static class ArchiveManager
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Application.CurrentApplication.Logger.Log(ex.Message, typeof(ArchiveManager), LogType.Error); // Write the error to a file
|
||||
Application.Logger.Log(ex.Message, typeof(ArchiveManager), LogType.Error); // Write the error to a file
|
||||
await Task.Delay(100);
|
||||
return await ReadFromPakAsync(fileName, archFile);
|
||||
}
|
||||
@@ -141,7 +141,7 @@ public static class ArchiveManager
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Application.CurrentApplication.Logger.Log(ex.Message, typeof(ArchiveManager), LogType.Error);
|
||||
Application.Logger.Log(ex.Message, typeof(ArchiveManager), LogType.Error);
|
||||
}
|
||||
|
||||
currentZipFile++;
|
||||
@@ -176,7 +176,7 @@ public static class ArchiveManager
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Application.CurrentApplication.Logger.Log(ex.Message, typeof(ArchiveManager), LogType.Error);
|
||||
Application.Logger.Log(ex.Message, typeof(ArchiveManager), LogType.Error);
|
||||
}
|
||||
|
||||
await Task.Delay(10);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -35,14 +37,14 @@ public class CustomSettingsDictionary : CustomSettingsDictionaryBase<string, obj
|
||||
|
||||
public override List<T> GetList<T>(string key, List<T> defaultValue)
|
||||
{
|
||||
List<T> result = base.GetList(key, defaultValue);
|
||||
|
||||
if (_EnableAutoAddOnGetWithDefault && defaultValue.All(result.Contains))
|
||||
List<T> value = base.GetList(key, defaultValue);
|
||||
|
||||
if (_EnableAutoAddOnGetWithDefault && value.All(defaultValue.Contains))
|
||||
{
|
||||
Add(key,defaultValue);
|
||||
Add(key, defaultValue);
|
||||
}
|
||||
|
||||
return result;
|
||||
return value;
|
||||
}
|
||||
|
||||
public override async Task LoadFromFile()
|
||||
@@ -71,11 +73,21 @@ public class CustomSettingsDictionary : CustomSettingsDictionaryBase<string, obj
|
||||
else if (kvp.Value is JArray nestedJArray)
|
||||
{
|
||||
dict[kvp.Key] = nestedJArray.ToObject<List<object>>();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
dict[kvp.Key] = kvp.Value;
|
||||
if (kvp.Value.Type == JTokenType.Integer)
|
||||
dict[kvp.Key] = kvp.Value.Value<int>();
|
||||
else if (kvp.Value.Type == JTokenType.Float)
|
||||
dict[kvp.Key] = kvp.Value.Value<float>();
|
||||
else if (kvp.Value.Type == JTokenType.Boolean)
|
||||
dict[kvp.Key] = kvp.Value.Value<bool>();
|
||||
else if (kvp.Value.Type == JTokenType.String)
|
||||
dict[kvp.Key] = kvp.Value.Value<string>();
|
||||
else if (kvp.Value.Type == JTokenType.Date)
|
||||
dict[kvp.Key] = kvp.Value.Value<DateTime>();
|
||||
else
|
||||
dict[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,27 @@ public abstract class CustomSettingsDictionaryBase<TKey,TValue> : ICustomSetting
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public virtual IDictionary<TSubKey, TSubValue> GetDictionary<TSubKey, TSubValue>(TKey key)
|
||||
{
|
||||
if (_InternalDictionary.TryGetValue(key, out var value))
|
||||
{
|
||||
if (value is not IDictionary)
|
||||
{
|
||||
throw new Exception("The value is not a dictionary");
|
||||
}
|
||||
|
||||
var dictionary = new Dictionary<TSubKey, TSubValue>();
|
||||
foreach (DictionaryEntry item in (IDictionary)value)
|
||||
{
|
||||
dictionary.Add((TSubKey)Convert.ChangeType(item.Key, typeof(TSubKey)), (TSubValue)Convert.ChangeType(item.Value, typeof(TSubValue)));
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
return new Dictionary<TSubKey, TSubValue>();
|
||||
}
|
||||
|
||||
public virtual List<T> GetList<T>(TKey key, List<T> defaultValue)
|
||||
{
|
||||
@@ -67,9 +88,9 @@ public abstract class CustomSettingsDictionaryBase<TKey,TValue> : ICustomSetting
|
||||
}
|
||||
|
||||
var list = new List<T>();
|
||||
foreach (var item in (IList)value)
|
||||
foreach (object? item in (IList)value)
|
||||
{
|
||||
list.Add(ConvertValue<T>(item));
|
||||
list.Add((T)Convert.ChangeType(item, typeof(T)));
|
||||
}
|
||||
|
||||
return list;
|
||||
@@ -141,20 +162,4 @@ public abstract class CustomSettingsDictionaryBase<TKey,TValue> : ICustomSetting
|
||||
public abstract Task SaveToFile();
|
||||
|
||||
public abstract Task LoadFromFile();
|
||||
|
||||
protected virtual T? ConvertValue<T>(object value)
|
||||
{
|
||||
|
||||
if (typeof(T) == typeof(ulong) && value is long longValue)
|
||||
{
|
||||
return (T)(object)Convert.ToUInt64(longValue);
|
||||
}
|
||||
|
||||
if (typeof(T).IsEnum && value is string stringValue)
|
||||
{
|
||||
return (T)Enum.Parse(typeof(T), stringValue);
|
||||
}
|
||||
|
||||
return (T)Convert.ChangeType(value, typeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user