Removed the WebUI. Removed the Modules

This commit is contained in:
2024-10-30 23:10:04 +02:00
parent f8df0f0254
commit 9e8bfbbe16
2133 changed files with 120 additions and 15581 deletions

View File

@@ -4,18 +4,14 @@ using System.IO;
using System.Threading.Tasks;
using DiscordBotCore.Bot;
using DiscordBotCore.Interfaces.Logger;
using DiscordBotCore.Online;
using DiscordBotCore.Online.Helpers;
using DiscordBotCore.Others;
using DiscordBotCore.Others.Actions;
using DiscordBotCore.Others.Exceptions;
using DiscordBotCore.Others.Settings;
using DiscordBotCore.Modules;
using DiscordBotCore.Plugin;
using DiscordBotCore.Interfaces.Modules;
using DiscordBotCore.Logging;
using DiscordBotCore.Repository;
namespace DiscordBotCore
@@ -39,7 +35,8 @@ namespace DiscordBotCore
private static readonly string _PluginsFolder = "./Data/Plugins";
private static readonly string _LogsFolder = "./Data/Logs";
public ModuleManager ModuleManager = null!;
private static readonly string _LogFormat = "{ThrowTime} {SenderName} {Message}";
public DiscordBotApplication DiscordBotClient { get; set; } = null!;
public List<ulong> ServerIDs => ApplicationEnvironmentVariables.GetList("ServerID", new List<ulong>());
@@ -47,12 +44,12 @@ namespace DiscordBotCore
public CustomSettingsDictionary ApplicationEnvironmentVariables { get; private set; } = null!;
public InternalActionManager InternalActionManager { get; private set; } = null!;
public PluginManager PluginManager { get; private set; } = null!;
public ILogger Logger { get; private set; } = null!;
/// <summary>
/// Create the application. This method is used to initialize the application. Can not initialize multiple times.
/// </summary>
/// <param name="moduleRequirementsSolver">A function that will be called when a module is required to be installed. If set to default, will use the built in method(console)</param>
public static async Task CreateApplication(Func<ModuleRequirement, Task>? moduleRequirementsSolver)
public static async Task CreateApplication()
{
if (!await OnlineFunctions.IsInternetConnected())
{
@@ -62,7 +59,7 @@ namespace DiscordBotCore
if (CurrentApplication is not null)
{
Logger.Log("Application is already initialized. Reinitialization is not allowed", LogType.Error);
CurrentApplication.Logger.Log("Application is already initialized. Reinitialization is not allowed", LogType.Error);
return;
}
@@ -77,20 +74,10 @@ namespace DiscordBotCore
CurrentApplication.ApplicationEnvironmentVariables.Add("PluginFolder", _PluginsFolder);
CurrentApplication.ApplicationEnvironmentVariables.Add("ResourceFolder", _ResourcesFolder);
CurrentApplication.ApplicationEnvironmentVariables.Add("LogsFolder", _LogsFolder);
CurrentApplication.ModuleManager = new ModuleManager(ModuleRepository.SolveRepo());
await CurrentApplication.ModuleManager.LoadModules();
var requirements = await CurrentApplication.ModuleManager.CheckRequiredModules();
if(requirements.RequireAny)
{
moduleRequirementsSolver ??= requirement => CurrentApplication.ModuleManager.SolveRequirementIssues(requirement);
await moduleRequirementsSolver(requirements);
await CurrentApplication.ModuleManager.LoadModules();
}
Logger._LoggerModule = CurrentApplication.ModuleManager.GetLoadedModuleWithTag(ModuleType.Logger);
if (!File.Exists(_PluginsDatabaseFile))
CurrentApplication.Logger = new Logger(_LogsFolder, _LogFormat);
if (!File.Exists(_PluginsDatabaseFile))
{
List<PluginInfo> plugins = new();
await JsonManager.SaveToJsonFile(_PluginsDatabaseFile, plugins);
@@ -106,128 +93,6 @@ namespace DiscordBotCore
IsRunning = true;
}
/// <summary>
/// Invokes an external method from a module.
/// </summary>
/// <param name="moduleName">The module name</param>
/// <param name="methodFriendlyName">The method to be invoked. This should be in the documentation of the module</param>
/// <param name="parameters">The parameters of the invoked method</param>
public static async Task InvokeMethod(string moduleName, string methodFriendlyName, params object[] parameters)
{
var module = CurrentApplication.ModuleManager.GetModule(moduleName);
var methodName = module.Value.MethodMapping[methodFriendlyName];
await CurrentApplication.ModuleManager.InvokeMethod(module.Value, methodName, parameters);
}
/// <summary>
/// Invokes an external method from a module and returns the result.
/// </summary>
/// <param name="moduleName">The module name</param>
/// <param name="methodFriendlyName">The method to be invoked. This should be in the documentation of the module</param>
/// <param name="parameters">The parameters for the invoked function</param>
/// <returns>An object that has the expected result, otherwise method returns null</returns>
public static async Task<object?> InvokeMethodWithReturnValue(string moduleName, string methodFriendlyName, params object[] parameters)
{
var module = CurrentApplication.ModuleManager.GetModule(moduleName).Value;
var methodName = module.MethodMapping[methodFriendlyName];
var response = await CurrentApplication.ModuleManager.InvokeMethodWithReturnValue(module, methodName, parameters);
return response;
}
/// <summary>
/// A special class that is designed to log messages. It is a wrapper around the Logger module
/// The logger module is required to have this specific methods:
/// <br/><br/>
/// BaseLogException(Exception ex, object sender, bool fullStackTrace)<br/>
/// BaseLog(string message)<br/>
/// LogWithTypeAndFormat(string message, LogType logType, string format)<br/>
/// LogWithType(string message, LogType logType)<br/>
/// LogWithSender(string message, object sender)<br/>
/// LogWithTypeAndSender(string message, object sender, LogType type)<br/>
/// SetPrintFunction(Action[in string] outFunction)<br/><br/>
///
/// If your custom logger does not have the methods from above, the application might crash.
/// Please refer to the official logger documentation for more information.
/// </summary>
public static class Logger
{
internal static LoadedModule _LoggerModule = null!; // initial is null, will be populated when the application will load all modules !!
/// <summary>
/// Logs an exception with the default log type.
/// </summary>
/// <param name="ex">The exception to be logged</param>
/// <param name="sender">The type that sent this error</param>
/// <param name="fullStackTrace">True if it should keep all stack, otherwise false</param>
public static async void LogException(Exception ex, object sender, bool fullStackTrace = false)
{
await CurrentApplication.ModuleManager.InvokeMethod(_LoggerModule.Value, _LoggerModule.Value.MethodMapping["BaseLogException"], [ex, sender, fullStackTrace]);
}
/// <summary>
/// Logs a message with the default log type.
/// </summary>
/// <param name="message">The message in a normal string format</param>
public static async void Log(string message)
{
await CurrentApplication.ModuleManager.InvokeMethod(_LoggerModule.Value, _LoggerModule.Value.MethodMapping["BaseLog"], [message]);
}
/// <summary>
/// Logs a message with a specific type and format.
/// </summary>
/// <param name="message">The message in a normal string format</param>
/// <param name="logType">The log type</param>
/// <param name="format">The format in which the log should be written. Please refer to the documentation for placeholders</param>
public static async void Log(string message, LogType logType, string format)
{
await CurrentApplication.ModuleManager.InvokeMethod(_LoggerModule.Value, _LoggerModule.Value.MethodMapping["LogWithTypeAndFormat"], [message, logType, format]);
}
/// <summary>
/// Logs a message with a specific type.
/// </summary>
/// <param name="message">The message in a normal string format</param>
/// <param name="logType">The log type</param>
public static async void Log(string message, LogType logType)
{
await CurrentApplication.ModuleManager.InvokeMethod(_LoggerModule.Value, _LoggerModule.Value.MethodMapping["LogWithType"], [message, logType]);
}
/// <summary>
/// Logs a message with a specific sender.
/// </summary>
/// <param name="message">The message in a normal string format.</param>
/// <param name="sender">The sender of the log message.</param>
public static async void Log(string message, object sender)
{
await CurrentApplication.ModuleManager.InvokeMethod(_LoggerModule.Value, _LoggerModule.Value.MethodMapping["LogWithSender"], [message, sender]);
}
/// <summary>
/// Logs a message with a specific type and sender
/// </summary>
/// <param name="message">The message in a normal string format</param>
/// <param name="sender">The sender of the log message. It should be a type or a string</param>
/// <param name="type">The log type</param>
public static async void Log(string message, object sender, LogType type)
{
await CurrentApplication.ModuleManager.InvokeMethod(_LoggerModule.Value, _LoggerModule.Value.MethodMapping["LogWithTypeAndSender"], [message, sender, type]);
}
/// <summary>
/// Sets the output function. This function is called whenever a new log message was created.
/// </summary>
/// <param name="outFunction">The function to be used to process the log message.</param>
public static async void SetOutFunction(Action<string, LogType> outFunction)
{
await CurrentApplication.ModuleManager.InvokeMethod(_LoggerModule.Value, _LoggerModule.Value.MethodMapping["SetPrintFunction"], [outFunction]);
}
}
public static string GetResourceFullPath(string path)
{
var result = Path.Combine(_ResourcesFolder, path);

View File

@@ -56,7 +56,7 @@ internal class CommandHandler
}
catch (Exception ex)
{
Application.Logger.LogException(ex, this);
Application.CurrentApplication.Logger.LogException(ex, this);
}
return Task.CompletedTask;
@@ -140,7 +140,7 @@ internal class CommandHandler
DbCommandExecutingArguments cmd = new(context, cleanMessage, split[0], argsClean);
Application.Logger.Log(
Application.CurrentApplication.Logger.Log(
$"User ({context.User.Username}) from Guild \"{context.Guild.Name}\" executed command \"{cmd.CleanContent}\"",
this,
LogType.Info
@@ -152,7 +152,7 @@ internal class CommandHandler
}
catch (Exception ex)
{
Application.Logger.LogException(ex, this);
Application.CurrentApplication.Logger.LogException(ex, this);
}
}
}

View File

@@ -98,7 +98,7 @@ public class DiscordBotApplication
if (arg.Message.Contains("401"))
{
Application.CurrentApplication.ApplicationEnvironmentVariables.Remove("token");
Application.Logger.Log("The token is invalid.", this, LogType.Critical);
Application.CurrentApplication.Logger.Log("The token is invalid.", this, LogType.Critical);
await Application.CurrentApplication.ApplicationEnvironmentVariables.SaveToFile();
}
}
@@ -128,7 +128,7 @@ public class DiscordBotApplication
private Task LoggedIn()
{
Application.Logger.Log("Successfully Logged In", this);
Application.CurrentApplication.Logger.Log("Successfully Logged In", this);
return Task.CompletedTask;
}
@@ -138,12 +138,12 @@ public class DiscordBotApplication
{
case LogSeverity.Error:
case LogSeverity.Critical:
Application.Logger.Log(message.Message, this, LogType.Error);
Application.CurrentApplication.Logger.Log(message.Message, this, LogType.Error);
break;
case LogSeverity.Info:
case LogSeverity.Debug:
Application.Logger.Log(message.Message, this, LogType.Info);
Application.CurrentApplication.Logger.Log(message.Message, this, LogType.Info);
break;

View File

@@ -0,0 +1,13 @@
using System;
using DiscordBotCore.Others;
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; }
}

View File

@@ -0,0 +1,23 @@
using System;
using DiscordBotCore.Others;
namespace DiscordBotCore.Interfaces.Logger;
public interface ILogger
{
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,LogType> outFunction);
}

View File

@@ -1,25 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace DiscordBotCore.Interfaces.Modules
{
public enum ModuleType
{
Logger,
Compatibility,
Other
}
/// <summary>
/// Define a module.
/// </summary>
public interface IModule
{
public ModuleType ModuleType { get; }
public string Name { get; }
public IDictionary<string, string> MethodMapping { get; }
public Task Initialize();
}
}

View File

@@ -1,71 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading.Tasks;
using DiscordBotCore.Interfaces.Modules;
using System.Reflection;
using DiscordBotCore.Modules;
namespace DiscordBotCore.Loaders
{
internal class ModuleLoader
{
private readonly List<ModuleData> _ModuleData;
public ModuleLoader(List<ModuleData> moduleFolder)
{
_ModuleData = moduleFolder;
}
public Task LoadFileModules()
{
var paths = _ModuleData.Select(module => module.ModulePath);
foreach (var file in paths)
{
try
{
Assembly.LoadFrom(file);
}
catch (Exception e)
{
Console.WriteLine($"Error loading module {file}: {e.Message}");
}
}
return Task.CompletedTask;
}
public Task<List<IModule>> LoadModules()
{
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>();
foreach (var module in moduleTypes)
{
try
{
var instance = (IModule?)Activator.CreateInstance(module);
if (instance is null)
{
Console.WriteLine($"Error loading module {module.Name}: Could not create instance");
continue;
}
modules.Add(instance);
}
catch (Exception e)
{
Console.WriteLine($"Error loading module {module.Name}: {e.Message}");
}
}
return Task.FromResult(modules);
}
}
}

View File

@@ -39,7 +39,7 @@ public sealed class PluginLoader
SlashCommands.Clear();
Actions.Clear();
Application.Logger.Log("Loading plugins...", this);
Application.CurrentApplication.Logger.Log("Loading plugins...", this);
var loader = new Loader();
@@ -51,7 +51,7 @@ public sealed class PluginLoader
private void FileLoadedException(FileLoaderResult result)
{
Application.Logger.Log(result.ErrorMessage, this, LogType.Error);
Application.CurrentApplication.Logger.Log(result.ErrorMessage, this, LogType.Error);
}
private async void InitializeCommand(ICommandAction action)
@@ -99,7 +99,7 @@ public sealed class PluginLoader
private void HandleError(Exception exception)
{
Application.Logger.Log(exception.Message, this, LogType.Error);
Application.CurrentApplication.Logger.Log(exception.Message, this, LogType.Error);
}
private void OnPluginLoaded(PluginLoaderResult result)

View File

@@ -28,8 +28,8 @@ internal static class PluginLoaderExtensions
}
catch (Exception e)
{
Application.Logger.Log($"Error starting event {dbEvent.Name}: {e.Message}", typeof(PluginLoader), LogType.Error);
Application.Logger.LogException(e, typeof(PluginLoader));
Application.CurrentApplication.Logger.Log($"Error starting event {dbEvent.Name}: {e.Message}", typeof(PluginLoader), LogType.Error);
Application.CurrentApplication.Logger.LogException(e, typeof(PluginLoader));
return false;
}
}
@@ -83,7 +83,7 @@ internal static class PluginLoaderExtensions
SocketGuild? guild = Application.CurrentApplication.DiscordBotClient.Client.GetGuild(guildId);
if (guild is null)
{
Application.Logger.Log("Failed to get guild with ID " + guildId, typeof(PluginLoader), LogType.Error);
Application.CurrentApplication.Logger.Log("Failed to get guild with ID " + guildId, typeof(PluginLoader), LogType.Error);
return false;
}

View File

@@ -0,0 +1,80 @@
using System;
using DiscordBotCore.Interfaces.Logger;
using DiscordBotCore.Others;
namespace DiscordBotCore.Logging
{
internal sealed class LogMessage : ILogMessage
{
private static readonly string _DefaultLogMessageSender = "\b";
public string Message { get; set; }
public DateTime ThrowTime { get; set; }
public string SenderName { get; set; }
public LogType LogMessageType { get; set; }
public LogMessage(string message, LogType logMessageType)
{
Message = message;
LogMessageType = logMessageType;
ThrowTime = DateTime.Now;
SenderName = string.Empty;
}
public LogMessage(string message, object sender)
{
Message = message;
SenderName = sender is string && sender as string == string.Empty ? _DefaultLogMessageSender : sender.GetType().FullName ?? sender.GetType().Name;
ThrowTime = DateTime.Now;
LogMessageType = LogType.Info;
}
public LogMessage(string message, object sender, DateTime throwTime)
{
Message = message;
SenderName = sender is string && sender as string == string.Empty ? _DefaultLogMessageSender : sender.GetType().FullName ?? sender.GetType().Name;
ThrowTime = throwTime;
LogMessageType = LogType.Info;
}
public LogMessage(string message, object sender, LogType logMessageType)
{
Message = message;
SenderName = sender is string && sender as string == string.Empty ? _DefaultLogMessageSender : sender.GetType().FullName ?? sender.GetType().Name;
ThrowTime = DateTime.Now;
LogMessageType = logMessageType;
}
public LogMessage(string message, DateTime throwTime, object sender, LogType logMessageType)
{
Message = message;
ThrowTime = throwTime;
SenderName = sender is string && sender as string == string.Empty ? _DefaultLogMessageSender : sender.GetType().FullName ?? sender.GetType().Name;
LogMessageType = logMessageType;
}
public LogMessage WithMessage(string message)
{
this.Message = message;
return this;
}
public LogMessage WithCurrentThrowTime()
{
this.ThrowTime = DateTime.Now;
return this;
}
public LogMessage WithMessageType(LogType logType)
{
this.LogMessageType = logType;
return this;
}
public static LogMessage CreateFromException(Exception exception, object Sender, bool logFullStack)
{
LogMessage message = new LogMessage(logFullStack? exception.ToString() : exception.Message, Sender, LogType.Error);
return message;
}
}
}

View File

@@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using DiscordBotCore.Interfaces.Logger;
using DiscordBotCore.Others;
namespace DiscordBotCore.Logging;
public sealed class Logger : ILogger
{
private readonly FileStream _LogFileStream;
private readonly List<string> _LogMessageProperties = typeof(ILogMessage).GetProperties().Select(p => p.Name).ToList();
private Action<string, LogType>? _OutFunction;
public string LogMessageFormat { get ; set; }
public Logger(string logFolder, string logMessageFormat, Action<string, LogType>? outFunction = null)
{
this.LogMessageFormat = logMessageFormat;
var logFile = logFolder + DateTime.Now.ToString("yyyy-MM-dd") + ".log";
_LogFileStream = File.Open(logFile, FileMode.Append, FileAccess.Write, FileShare.Read);
this._OutFunction = outFunction ?? DefaultLogFunction;
}
private void DefaultLogFunction(string message, LogType logType)
{
Console.WriteLine($"[{logType}] {message}");
}
/// <summary>
/// Generate a formatted string based on the default parameters of the ILogMessage and a string defined as model
/// </summary>
/// <param name="message">The message</param>
/// <returns>A formatted string with the message values</returns>
private string GenerateLogMessage(ILogMessage message)
{
string messageAsString = new string(LogMessageFormat);
foreach (var prop in _LogMessageProperties)
{
Type messageType = typeof(ILogMessage);
messageAsString = messageAsString.Replace("{" + prop + "}", messageType?.GetProperty(prop)?.GetValue(message)?.ToString());
}
return messageAsString;
}
private async void LogToFile(string message)
{
byte[] messageAsBytes = System.Text.Encoding.ASCII.GetBytes(message);
await _LogFileStream.WriteAsync(messageAsBytes, 0, messageAsBytes.Length);
byte[] newLine = System.Text.Encoding.ASCII.GetBytes(Environment.NewLine);
await _LogFileStream.WriteAsync(newLine, 0, newLine.Length);
await _LogFileStream.FlushAsync();
}
private string GenerateLogMessage(ILogMessage message, string customFormat)
{
string messageAsString = customFormat;
foreach (var prop in _LogMessageProperties)
{
Type messageType = typeof(ILogMessage);
messageAsString = messageAsString.Replace("{" + prop + "}", messageType?.GetProperty(prop)?.GetValue(message)?.ToString());
}
return messageAsString;
}
public void Log(ILogMessage message, string format)
{
string messageAsString = GenerateLogMessage(message, format);
_OutFunction?.Invoke(messageAsString, message.LogMessageType);
LogToFile(messageAsString);
}
public void Log(ILogMessage message)
{
string messageAsString = GenerateLogMessage(message);
_OutFunction?.Invoke(messageAsString, message.LogMessageType);
LogToFile(messageAsString);
}
public void Log(string message) => Log(new LogMessage(message, string.Empty, LogType.Info));
public void Log(string message, LogType logType, string format) => Log(new LogMessage(message, logType), format);
public void Log(string message, LogType logType) => Log(new LogMessage(message, logType));
public void Log(string message, object Sender) => Log(new LogMessage(message, Sender));
public void Log(string message, object Sender, LogType type) => Log(new LogMessage(message, Sender, type));
public void LogException(Exception exception, object Sender, bool logFullStack = false) => Log(LogMessage.CreateFromException(exception, Sender, logFullStack));
public void SetOutFunction(Action<string, LogType> outFunction)
{
this._OutFunction = outFunction;
}
}

View File

@@ -1,17 +0,0 @@
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 ModuleData(string moduleName, string modulePath, bool isEnabled)
{
ModuleName = moduleName;
ModulePath = modulePath;
IsEnabled = isEnabled;
}
}

View File

@@ -1,292 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using DiscordBotCore.Interfaces.Modules;
using DiscordBotCore.Loaders;
using DiscordBotCore.Online;
using DiscordBotCore.Others;
using DiscordBotCore.Others.Exceptions;
using DiscordBotCore.Repository;
using Newtonsoft.Json;
namespace DiscordBotCore.Modules
{
public class LoadedModule
{
public IModule Value { get; init; }
public ModuleData ModuleData { get; init; }
public LoadedModule(IModule module, ModuleData moduleData)
{
Value = module;
ModuleData = moduleData;
}
}
public class ModuleManager
{
private static readonly string _BaseModuleFolder = "./Data/Modules";
private static readonly string _BaseModuleConfig = "./Data/Resources/modules.json";
// private const string _ModuleDatabase = "https://raw.githubusercontent.com/andreitdr/SethPlugins/tests/modules.json";
private ModuleRepository _ModuleRepository;
private List<LoadedModule> Modules { get; }
public IEnumerable<ModuleData> GetLocalModules()
{
return Modules.Select(module => module.ModuleData);
}
internal ModuleManager(ModuleRepository moduleRepository)
{
Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("ModuleFolder", _BaseModuleFolder);
Modules = new();
_ModuleRepository = moduleRepository;
}
public async Task<ModuleOnlineData?> ServerGetModuleWithName(string moduleName)
{
var modules = await ServerGetAllModules();
return modules.FirstOrDefault(module => module?.ModuleName == moduleName, null);
}
public async Task<List<ModuleOnlineData>> ServerGetAllModules(ModuleType? moduleTypeFilter = null)
{
var jsonDatabaseRemote = await _ModuleRepository.JsonGetAllModules();
var modules = await JsonManager.ConvertFromJson<List<ModuleOnlineData>>(jsonDatabaseRemote);
if(moduleTypeFilter is not null)
modules = modules.FindAll(m => m.ModuleType == moduleTypeFilter);
return modules;
}
public async Task InstallModule(string moduleName, IProgress<float> 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);
List<ModuleOnlineData> modules = await ServerGetAllModules();
string url = modules.Find(m => m.ModuleName == moduleName)?.ModuleDownloadUrl ?? string.Empty;
if(string.IsNullOrEmpty(url))
return;
string filePath = moduleFolder + "/" + moduleName + ".dll";
await ServerCom.DownloadFileAsync(url, filePath, progressToWrite);
ModuleData localModuleData = new ModuleData(moduleName, filePath, true);
await AddModuleToDatabase(localModuleData);
}
public LoadedModule GetModule(string moduleName)
{
var result = Modules.FirstOrDefault(module => module.ModuleData.ModuleName == moduleName);
if(result is null)
{
throw new ModuleNotFound(moduleName);
}
return result;
}
public LoadedModule GetLoadedModuleWithTag(ModuleType moduleType)
{
var result = Modules.FirstOrDefault(module => module.Value.ModuleType == moduleType);
if(result is null)
{
throw new ModuleNotFound(moduleType);
}
return result;
}
public async Task AddModuleToDatabase(ModuleData moduleData)
{
string moduleConfigPath = Application.CurrentApplication.ApplicationEnvironmentVariables
.Get<string>("ModuleConfig", _BaseModuleConfig);
List<ModuleData>? listOfModuleData = null;
if (File.Exists(moduleConfigPath))
{
string moduleConfigFile = await File.ReadAllTextAsync(moduleConfigPath);
listOfModuleData = JsonConvert.DeserializeObject<List<ModuleData>>(moduleConfigFile);
}
if (listOfModuleData is null)
{
listOfModuleData = new List<ModuleData>();
}
listOfModuleData.Add(moduleData);
string json = JsonConvert.SerializeObject(listOfModuleData, Formatting.Indented);
await File.WriteAllTextAsync(moduleConfigPath, json);
}
internal Task<ModuleRequirement> CheckRequiredModules()
{
ModuleRequirement moduleRequirement = new ModuleRequirement();
if (Modules.All(module => module.Value.ModuleType != ModuleType.Logger))
{
moduleRequirement.AddType(ModuleType.Logger);
}
return Task.FromResult(moduleRequirement);
}
internal async Task SolveRequirementIssues(ModuleRequirement requirements)
{
if (!requirements.RequireAny)
{
return;
}
foreach (var module in requirements.RequiredModulesWithTypes)
{
var availableModules = await ServerGetAllModules(module);
Console.WriteLine("Please select a module of type " + module);
for (var i = 0; i < availableModules.Count; i++)
{
Console.WriteLine((i+1) + " - " + availableModules[i].ModuleName);
Console.WriteLine("Author: " + availableModules[i].ModuleAuthor);
Console.WriteLine("Description: " + availableModules[i].ModuleDescription);
Console.WriteLine();
}
Console.WriteLine("Please select a module by typing the number:");
int selectedModule = int.Parse(Console.ReadLine() ?? string.Empty);
if (selectedModule < 1 || selectedModule > availableModules.Count)
{
Console.WriteLine("Invalid module selected");
Environment.Exit(-1);
}
IProgress<float> progress = new Progress<float>(f => Console.Write($"\b{f}"));
await InstallModule(availableModules[selectedModule - 1].ModuleName, progress);
}
Console.WriteLine("All required modules installed. Please restart the application");
System.Diagnostics.Process.Start(System.Diagnostics.Process.GetCurrentProcess().MainModule?.FileName);
}
internal async Task LoadModules()
{
Modules.Clear();
string moduleConfigPath = Application.CurrentApplication.ApplicationEnvironmentVariables
.Get<string>("ModuleConfig", _BaseModuleConfig);
if(!File.Exists(moduleConfigPath))
return;
string moduleConfigFile = await File.ReadAllTextAsync(moduleConfigPath);
List<ModuleData>? listOfModuleData = JsonConvert.DeserializeObject<List<ModuleData>>(moduleConfigFile);
if (listOfModuleData is null)
return;
if (!listOfModuleData.Any())
{
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)
{
try{
await module.Initialize();
Modules.Add(new LoadedModule(module, moduleData));
}catch(Exception e){
Console.WriteLine($"Error loading module {moduleData.ModuleName}: {e.Message}");
}
}
}
}
internal async Task<object?> InvokeMethodWithReturnValue(string moduleName, string methodName, object[] parameters)
{
IModule module = GetModule(moduleName).Value;
var method = module.GetType().GetMethod(methodName);
if (method is null)
{
throw new ModuleMethodNotFound(module, methodName);
}
object? result = await Task.Run(() => method.Invoke(module, parameters));
return result;
}
internal async Task<object?> InvokeMethodWithReturnValue(IModule module, string methodName, object[] parameters)
{
var method = module.GetType().GetMethod(methodName);
if (method is null)
{
throw new ModuleMethodNotFound(module, methodName);
}
object? result = await Task.Run(() => method.Invoke(module, parameters));
return result;
}
internal 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 ModuleMethodNotFound(module, methodName);
}
await Task.Run(() => method.Invoke(module, parameters));
}
internal async Task InvokeMethod(IModule module, string methodName, object[] parameters)
{
var method = module.GetType().GetMethod(methodName);
if (method is null)
{
throw new ModuleMethodNotFound(module, methodName);
}
await Task.Run(() => method.Invoke(module, parameters));
}
}
}

View File

@@ -1,21 +0,0 @@
using DiscordBotCore.Interfaces.Modules;
namespace DiscordBotCore.Modules;
public class ModuleOnlineData
{
public string ModuleName { get; set; }
public string ModuleDownloadUrl { get; set; }
public string ModuleDescription { get; set; }
public string ModuleAuthor { get; set; }
public ModuleType ModuleType { get; set; }
public ModuleOnlineData(string moduleName, string moduleDownloadUrl, ModuleType moduleType, string moduleDescription, string moduleAuthor)
{
ModuleName = moduleName;
ModuleDownloadUrl = moduleDownloadUrl;
ModuleType = moduleType;
ModuleDescription = moduleDescription;
ModuleAuthor = moduleAuthor;
}
}

View File

@@ -100,7 +100,7 @@ public class PluginManager
{
if (await pluginUpdater.HasUpdate(plugin.PluginName))
{
Application.Logger.Log("Updating plugin: " + plugin.PluginName, this, LogType.Info);
Application.CurrentApplication.Logger.Log("Updating plugin: " + plugin.PluginName, this, LogType.Info);
await pluginUpdater.UpdatePlugin(plugin.PluginName);
}
}

View File

@@ -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.Logger.Log($"Action {action.ActionName} already exists", this, LogType.Error);
Application.CurrentApplication.Logger.Log($"Action {action.ActionName} already exists", this, LogType.Error);
return;
}
@@ -50,7 +50,7 @@ public class InternalActionManager
{
if (!Actions.ContainsKey(actionName))
{
Application.Logger.Log($"Action {actionName} not found", this, LogType.Error);
Application.CurrentApplication.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.Logger.Log($"Action {actionName} is not executable", this, LogType.Error);
Application.CurrentApplication.Logger.Log($"Action {actionName} is not executable", this, LogType.Error);
return false;
}
@@ -67,7 +67,7 @@ public class InternalActionManager
}
catch (Exception e)
{
Application.Logger.Log(e.Message, type: LogType.Error, sender: this);
Application.CurrentApplication.Logger.LogException(e, this);
return false;
}
}

View File

@@ -105,7 +105,7 @@ public static class ArchiveManager
}
catch (Exception ex)
{
Application.Logger.Log(ex.Message, typeof(ArchiveManager), LogType.Error); // Write the error to a file
Application.CurrentApplication.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.Logger.Log(ex.Message, typeof(ArchiveManager), LogType.Error);
Application.CurrentApplication.Logger.Log(ex.Message, typeof(ArchiveManager), LogType.Error);
}
currentZipFile++;
@@ -176,7 +176,7 @@ public static class ArchiveManager
}
catch (Exception ex)
{
Application.Logger.Log(ex.Message, typeof(ArchiveManager), LogType.Error);
Application.CurrentApplication.Logger.Log(ex.Message, typeof(ArchiveManager), LogType.Error);
}
await Task.Delay(10);

View File

@@ -1,13 +0,0 @@
using System;
using DiscordBotCore.Interfaces.Modules;
namespace DiscordBotCore.Others.Exceptions;
public class ModuleMethodNotFound : Exception
{
private IModule _SearchedModule;
public ModuleMethodNotFound(IModule module, string methodName) : base($"Method not found {methodName} in module {module.Name}")
{
_SearchedModule = module;
}
}

View File

@@ -1,15 +0,0 @@
using System;
using DiscordBotCore.Interfaces.Modules;
namespace DiscordBotCore.Others.Exceptions;
public class ModuleNotFound : Exception
{
public ModuleNotFound(string moduleName) : base($"Module not found: {moduleName}")
{
}
public ModuleNotFound(ModuleType moduleType) : base($"No module with type {moduleType} found")
{
}
}

View File

@@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DiscordBotCore.Others.Exceptions
{
internal class ModuleNotFoundException<T> : Exception
{
private Type _type = typeof(T);
public ModuleNotFoundException() : base($"No module loaded with this signature: {typeof(T)}")
{
}
}
}

View File

@@ -1,31 +0,0 @@
using System.Collections.Generic;
using DiscordBotCore.Interfaces.Modules;
using DiscordBotCore.Modules;
namespace DiscordBotCore.Others.Exceptions;
public class ModuleRequirement
{
private List<ModuleType> RequiredModulesWithType { get; }
private List<string> RequiredModulesWithName { get; }
public ModuleRequirement()
{
RequiredModulesWithType = new List<ModuleType>();
RequiredModulesWithName = new List<string>();
}
public void AddType (ModuleType moduleType)
{
RequiredModulesWithType.Add(moduleType);
}
public void AddName (string moduleName)
{
RequiredModulesWithName.Add(moduleName);
}
public bool RequireAny => RequiredModulesWithType.Count > 0 || RequiredModulesWithName.Count > 0;
public IList<ModuleType> RequiredModulesWithTypes => RequiredModulesWithType ;
public IList<string> RequiredModulesWithNames => RequiredModulesWithName;
}

View File

@@ -1,49 +0,0 @@
using System;
using System.Threading.Tasks;
using DiscordBotCore.Online;
namespace DiscordBotCore.Repository;
public sealed class ModuleRepository : RepositoryBase
{
public static readonly ModuleRepository Default = new ModuleRepository("Testing", "https://wizzy-server.ro/SethDiscordBot/ModulesRepo", "modules.json");
private ModuleRepository(string repositoryName, string repositoryUrl, string databaseFile) : base(repositoryName, repositoryUrl, databaseFile)
{
}
public async Task<string> JsonGetAllModules()
{
var jsonResponse = await ServerCom.GetAllTextFromUrl(DatabasePath);
return jsonResponse;
}
private static ModuleRepository From(string repositoryName, string repositoryUrl, string databaseFile)
{
return new ModuleRepository(repositoryName, repositoryUrl, databaseFile);
}
internal static ModuleRepository SolveRepo()
{
if (!Application.CurrentApplication.ApplicationEnvironmentVariables.ContainsKey("ModuleRepository"))
{
return Default;
}
try
{
var moduleRepoDict = Application.CurrentApplication.ApplicationEnvironmentVariables.GetDictionary<string, string>("ModuleRepository");
var moduleRepo = From(
moduleRepoDict["Name"],
moduleRepoDict["Url"],
moduleRepoDict["DatabaseFile"]
);
return moduleRepo;
}
catch(Exception ex)
{
Application.Logger.LogException(ex, Application.CurrentApplication);
return Default;
}
}
}

View File

@@ -44,7 +44,7 @@ public sealed class PluginRepository : RepositoryBase
}
catch(Exception ex)
{
Application.Logger.LogException(ex, Application.CurrentApplication);
Application.CurrentApplication.Logger.LogException(ex, Application.CurrentApplication);
return Default;
}