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 Newtonsoft.Json; namespace DiscordBotCore.Modules { internal 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"; internal Dictionary Modules { get; set; } public ModuleManager() { Application.CurrentApplication.ApplicationEnvironmentVariables.Get("ModuleFolder", _BaseModuleFolder); Modules = new Dictionary(); } public async Task> GetAllModules(ModuleType? moduleTypeFilter = null) { string jsonDatabaseRemote = await ServerCom.GetAllTextFromUrl(_ModuleDatabase); var modules = await JsonManager.ConvertFromJson>(jsonDatabaseRemote); if(moduleTypeFilter is not null) modules = modules.FindAll(m => m.ModuleType == moduleTypeFilter); return modules; } public async Task InstallModule(string moduleName, IProgress progressToWrite) { string? moduleFolder = Application.CurrentApplication.ApplicationEnvironmentVariables.Get("ModuleFolder"); if(moduleFolder is null) throw new DirectoryNotFoundException("Module folder not found"); // Should never happen Directory.CreateDirectory(moduleFolder); List 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 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 GetModule(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("ModuleConfig", _BaseModuleConfig); List? listOfModuleData = null; if (File.Exists(moduleConfigPath)) { string moduleConfigFile = await File.ReadAllTextAsync(moduleConfigPath); listOfModuleData = JsonConvert.DeserializeObject>(moduleConfigFile); } if (listOfModuleData is null) { listOfModuleData = new List(); } listOfModuleData.Add(moduleData); string json = JsonConvert.SerializeObject(listOfModuleData, Formatting.Indented); await File.WriteAllTextAsync(moduleConfigPath, json); } public Task 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 (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 progress = new Progress(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() { string moduleConfigPath = Application.CurrentApplication.ApplicationEnvironmentVariables .Get("ModuleConfig", _BaseModuleConfig); if(!File.Exists(moduleConfigPath)) return; string moduleConfigFile = await File.ReadAllTextAsync(moduleConfigPath); List? listOfModuleData = JsonConvert.DeserializeObject>(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(moduleData, module); }catch(Exception e){ Console.WriteLine($"Error loading module {moduleData.ModuleName}: {e.Message}"); } } } } public async Task 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 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 ModuleMethodNotFound(module, methodName); } 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 ModuleMethodNotFound(module, methodName); } await Task.Run(() => method.Invoke(module, parameters)); } } }