Updated Module engine

This commit is contained in:
2024-08-10 20:27:59 +03:00
parent 18a059af0e
commit 9c98d2e219
16 changed files with 355 additions and 124 deletions

View File

@@ -7,13 +7,11 @@ 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)
public ModuleData(string moduleName, string modulePath, bool isEnabled)
{
ModuleName = moduleName;
ModulePath = modulePath;
MethodMapping = methodMapping;
IsEnabled = isEnabled;
}
}

View File

@@ -1,31 +0,0 @@
using System;
using System.IO;
using System.Threading.Tasks;
using DiscordBotCore.Online;
namespace DiscordBotCore.Modules
{
public class ModuleDownloader
{
private readonly string _ModuleName;
private const string _BaseUrl = "https://raw.githubusercontent.com/andreitdr/SethPlugins/tests/Modules/";
public ModuleDownloader(string moduleName)
{
_ModuleName = moduleName;
}
public async Task DownloadModule(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);
string url = _BaseUrl + _ModuleName + ".dll";
await ServerCom.DownloadFileAsync(url, moduleFolder + "/" + _ModuleName + ".dll", progressToWrite);
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -6,6 +6,9 @@ using System.Threading.Tasks;
using DiscordBotCore.Interfaces.Modules;
using DiscordBotCore.Loaders;
using DiscordBotCore.Online;
using DiscordBotCore.Others;
using DiscordBotCore.Others.Exceptions;
using Newtonsoft.Json;
namespace DiscordBotCore.Modules
@@ -14,22 +17,149 @@ namespace DiscordBotCore.Modules
{
private static readonly string _BaseModuleFolder = "./Data/Modules";
private static readonly string _BaseModuleConfig = "./Data/Resources/modules.json";
internal Dictionary<ModuleData, IModule> Modules { get; set; }
private const string _ModuleDatabase = "https://raw.githubusercontent.com/andreitdr/SethPlugins/tests/modules.json";
internal Dictionary<ModuleData, IModule> Modules { get; set; }
public ModuleManager()
{
Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("ModuleFolder", _BaseModuleFolder);
Modules = new Dictionary<ModuleData, IModule>();
}
public KeyValuePair<ModuleData, IModule> GetModule(string moduleName)
public async Task<List<ModuleOnlineData>> GetAllModules(ModuleType? moduleTypeFilter = null)
{
return Modules.FirstOrDefault(module => module.Key.ModuleName == moduleName);
string jsonDatabaseRemote = await ServerCom.GetAllTextFromUrl(_ModuleDatabase);
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 GetAllModules();
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 KeyValuePair<ModuleData, IModule> GetModule(string moduleName)
{
var result = Modules.FirstOrDefault(module => module.Key.ModuleName == moduleName);
if (result.Value is null || result.Key is null)
{
throw new ModuleNotFound(moduleName);
}
return result;
}
public KeyValuePair<ModuleData, IModule> GetModule(ModuleType moduleType)
{
return Modules.First(module => module.Value.ModuleType == moduleType);
var result = Modules.FirstOrDefault(module => module.Value.ModuleType == moduleType);
if (result.Value is null || result.Key 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);
}
public Task<RequireInstallModule> CheckRequiredModules()
{
RequireInstallModule requireInstallModule = new RequireInstallModule();
if (!Modules.Any(module => module.Value.ModuleType == ModuleType.Logger))
{
requireInstallModule.AddType(ModuleType.Logger);
}
return Task.FromResult(requireInstallModule);
}
public async Task SolveRequirementIssues(RequireInstallModule requirements)
{
if (!requirements.RequireAny)
{
return;
}
foreach (var module in requirements.RequiredModules)
{
var availableModules = await GetAllModules(module);
Console.WriteLine("Please select a module of type " + module);
for (int i = 0; i < availableModules.Count; i++)
{
Console.WriteLine(i + " - " + 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);
}
public async Task LoadModules()
@@ -37,47 +167,84 @@ namespace DiscordBotCore.Modules
string moduleConfigPath = Application.CurrentApplication.ApplicationEnvironmentVariables
.Get<string>("ModuleConfig", _BaseModuleConfig);
string moduleConfigFile = await File.ReadAllTextAsync(moduleConfigPath);
List<ModuleData>? listOfModuleData = JsonConvert.DeserializeObject<List<ModuleData>>(moduleConfigFile);
if(listOfModuleData is null)
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)
{
await module.Initialize(); // TODO: Add error handling
Modules.Add(moduleData, module);
try{
await module.Initialize();
Modules.Add(moduleData, module);
}catch(Exception e){
Console.WriteLine($"Error loading module {moduleData.ModuleName}: {e.Message}");
}
}
}
}
public 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;
}
public 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;
}
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
throw new ModuleMethodNotFound(module, methodName);
}
await Task.Run(() => method.Invoke(module, parameters));
@@ -86,15 +253,13 @@ namespace DiscordBotCore.Modules
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}");
throw new ModuleMethodNotFound(module, methodName);
}
await Task.Run(() => method.Invoke(module, parameters));
}
}
}

View File

@@ -0,0 +1,21 @@
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;
}
}