diff --git a/DiscordBotCore.Configuration/Configuration.cs b/DiscordBotCore.Configuration/Configuration.cs index 437b779..4af9078 100644 --- a/DiscordBotCore.Configuration/Configuration.cs +++ b/DiscordBotCore.Configuration/Configuration.cs @@ -42,20 +42,20 @@ public class Configuration : ConfigurationBase return value; } - public override async void LoadFromFile() + public override void LoadFromFile() { if (!File.Exists(_DiskLocation)) { - await SaveToFile(); + SaveToFile().Wait(); return; } - string jsonContent = await File.ReadAllTextAsync(_DiskLocation); + string jsonContent = File.ReadAllText(_DiskLocation); var jObject = JsonConvert.DeserializeObject(jsonContent); if (jObject is null) { - await SaveToFile(); + SaveToFile().Wait(); return; } diff --git a/DiscordBotCore.Logging/Logger.cs b/DiscordBotCore.Logging/Logger.cs index ad50be0..13e8b7e 100644 --- a/DiscordBotCore.Logging/Logger.cs +++ b/DiscordBotCore.Logging/Logger.cs @@ -91,7 +91,7 @@ public sealed class Logger : ILogger { string fileName = _logFileStream.Name; - _logFileStream.Flush(); + //_logFileStream.Flush(); _logFileStream.Close(); string[] logs = File.ReadAllLines(fileName); diff --git a/DiscordBotCore.PluginCore/Interfaces/IDbEvent.cs b/DiscordBotCore.PluginCore/Interfaces/IDbEvent.cs index 7b5d1c4..d6d9703 100644 --- a/DiscordBotCore.PluginCore/Interfaces/IDbEvent.cs +++ b/DiscordBotCore.PluginCore/Interfaces/IDbEvent.cs @@ -19,5 +19,5 @@ public interface IDbEvent /// The method that is invoked when the event is loaded into memory /// /// The arguments for the start method - Task Start(IDbEventExecutingArgument args); + void Start(IDbEventExecutingArgument args); } diff --git a/DiscordBotCore.PluginManagement.Loading/PluginLoader.cs b/DiscordBotCore.PluginManagement.Loading/PluginLoader.cs index 3d1ac25..d65ed52 100644 --- a/DiscordBotCore.PluginManagement.Loading/PluginLoader.cs +++ b/DiscordBotCore.PluginManagement.Loading/PluginLoader.cs @@ -116,28 +116,48 @@ public sealed class PluginLoader : IPluginLoader ); } - private bool TryStartEvent(IDbEvent? dbEvent) + private bool TryStartEvent(IDbEvent dbEvent) { - try + string? botPrefix = _Configuration.Get("prefix"); + if (string.IsNullOrEmpty(botPrefix)) { - if (dbEvent is null) - { - throw new ArgumentNullException(nameof(dbEvent)); - } - IDbEventExecutingArgument args = new DbEventExecutingArgument( - _Logger, - _discordClient, - _Configuration.Get("prefix"), - new DirectoryInfo(Path.Combine(_Configuration.Get("ResourcesPath"), dbEvent.Name))); - dbEvent.Start(args); - return true; - } - catch (Exception e) - { - _Logger.Log($"Error starting event {dbEvent.Name}: {e.Message}", typeof(PluginLoader), LogType.Error); - _Logger.LogException(e, typeof(PluginLoader)); + _Logger.Log("Bot prefix is not set. Please set the bot prefix in the configuration.", this, LogType.Error); return false; } + + if (_discordClient is null) + { + _Logger.Log("Discord client is not set. Please set the discord client before starting events.", this, LogType.Error); + return false; + } + + string? resourcesFolder = _Configuration.Get("ResourcesFolder"); + if (string.IsNullOrEmpty(resourcesFolder)) + { + _Logger.Log("Resources folder is not set. Please set the resources folder in the configuration.", this, LogType.Error); + return false; + } + + if (!Directory.Exists(resourcesFolder)) + { + _Logger.Log("Resources folder does not exist. Please create the resources folder.", this, LogType.Error); + return false; + } + + string? eventConfigDirectory = Path.Combine(resourcesFolder, dbEvent.GetType().Assembly.GetName().Name); + + Directory.CreateDirectory(eventConfigDirectory); + + IDbEventExecutingArgument args = new DbEventExecutingArgument( + _Logger, + _discordClient, + botPrefix, + new DirectoryInfo(eventConfigDirectory)); + + dbEvent.Start(args); + + _Logger.Log("Event started: " + dbEvent.Name, this); + return true; } private async Task TryStartSlashCommand(IDbSlashCommand? dbSlashCommand) diff --git a/DiscordBotCore.PluginManagement/Helpers/PluginRepository.cs b/DiscordBotCore.PluginManagement/Helpers/PluginRepository.cs index b0c89d2..b78da57 100644 --- a/DiscordBotCore.PluginManagement/Helpers/PluginRepository.cs +++ b/DiscordBotCore.PluginManagement/Helpers/PluginRepository.cs @@ -45,7 +45,7 @@ public class PluginRepository : IPluginRepository public async Task GetPluginById(int pluginId) { string url = CreateUrlWithQueryParams(_pluginRepositoryConfiguration.PluginRepositoryLocation, - "get-plugin", new Dictionary + "get-by-id", new Dictionary { { "pluginId", pluginId.ToString() }, { "includeNotApproved", "false" } @@ -66,7 +66,7 @@ public class PluginRepository : IPluginRepository public async Task GetPluginByName(string pluginName, int operatingSystem, bool includeNotApproved) { string url = CreateUrlWithQueryParams(_pluginRepositoryConfiguration.PluginRepositoryLocation, - "get-plugin-by-name", new Dictionary + "get-by-name", new Dictionary { { "pluginName", pluginName }, { "operatingSystem", operatingSystem.ToString() }, @@ -89,7 +89,7 @@ public class PluginRepository : IPluginRepository public async Task> GetDependenciesForPlugin(int pluginId) { string url = CreateUrlWithQueryParams(_pluginRepositoryConfiguration.DependenciesRepositoryLocation, - "get-dependencies-for-plugin", new Dictionary + "get-by-plugin-id", new Dictionary { { "pluginId", pluginId.ToString() } }); diff --git a/DiscordBotCore.PluginManagement/IPluginManager.cs b/DiscordBotCore.PluginManagement/IPluginManager.cs index 42e115a..72502e4 100644 --- a/DiscordBotCore.PluginManagement/IPluginManager.cs +++ b/DiscordBotCore.PluginManagement/IPluginManager.cs @@ -6,6 +6,7 @@ public interface IPluginManager { Task> GetPluginsList(); Task GetPluginDataByName(string pluginName); + Task GetPluginDataById(int pluginId); Task AppendPluginToDatabase(LocalPlugin pluginData); Task> GetInstalledPlugins(); Task IsPluginInstalled(string pluginName); @@ -14,7 +15,7 @@ public interface IPluginManager Task GetDependencyLocation(string dependencyName); Task GetDependencyLocation(string dependencyName, string pluginName); string GenerateDependencyRelativePath(string pluginName, string dependencyPath); - Task InstallPlugin(OnlinePlugin plugin, IProgress progress); + Task InstallPlugin(OnlinePlugin plugin, IProgress progress); Task, List>> GatherInstallDataForPlugin(OnlinePlugin plugin); Task SetEnabledStatus(string pluginName, bool status); } \ No newline at end of file diff --git a/DiscordBotCore.PluginManagement/Models/InstallationProgressIndicator.cs b/DiscordBotCore.PluginManagement/Models/InstallationProgressIndicator.cs deleted file mode 100644 index 2cc8173..0000000 --- a/DiscordBotCore.PluginManagement/Models/InstallationProgressIndicator.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace DiscordBotCore.PluginManagement.Models; - -public class InstallationProgressIndicator -{ - private readonly Dictionary _DownloadProgress; - - public InstallationProgressIndicator() - { - _DownloadProgress = new Dictionary(); - } - - public void SetProgress(string fileName, float progress) - { - _DownloadProgress[fileName] = progress; - } -} \ No newline at end of file diff --git a/DiscordBotCore.PluginManagement/PluginManager.cs b/DiscordBotCore.PluginManagement/PluginManager.cs index 262abf5..c54a165 100644 --- a/DiscordBotCore.PluginManagement/PluginManager.cs +++ b/DiscordBotCore.PluginManagement/PluginManager.cs @@ -50,6 +50,18 @@ public sealed class PluginManager : IPluginManager return plugin; } + public async Task GetPluginDataById(int pluginId) + { + var plugin = await _PluginRepository.GetPluginById(pluginId); + if (plugin is null) + { + _Logger.Log($"Plugin {pluginId} not found in the repository.", this, LogType.Warning); + return null; + } + + return plugin; + } + private async Task RemovePluginFromDatabase(string pluginName) { string? pluginDatabaseFile = _Configuration.Get("PluginDatabase"); @@ -72,8 +84,9 @@ public sealed class PluginManager : IPluginManager { throw new Exception("Plugin database file not found"); } + + List installedPlugins = await GetInstalledPlugins(); - List installedPlugins = await JsonManager.ConvertFromJson>(await File.ReadAllTextAsync(pluginDatabaseFile)); foreach (var dependency in pluginData.ListOfExecutableDependencies) { pluginData.ListOfExecutableDependencies[dependency.Key] = dependency.Value; @@ -194,7 +207,7 @@ public sealed class PluginManager : IPluginManager return relative; } - public async Task InstallPlugin(OnlinePlugin plugin, IProgress progress) + public async Task InstallPlugin(OnlinePlugin plugin, IProgress progress) { List dependencies = await _PluginRepository.GetDependenciesForPlugin(plugin.Id); string? pluginsFolder = _Configuration.Get("PluginFolder"); @@ -205,13 +218,7 @@ public sealed class PluginManager : IPluginManager string downloadLocation = $"{pluginsFolder}/{plugin.Name}.dll"; - InstallationProgressIndicator installationProgressIndicator = new InstallationProgressIndicator(); - - IProgress downloadProgress = new Progress(fileProgress => - { - installationProgressIndicator.SetProgress(plugin.Name, fileProgress); - progress.Report(installationProgressIndicator); - }); + IProgress downloadProgress = new Progress(progress.Report); FileDownloader fileDownloader = new FileDownloader(plugin.DownloadLink, downloadLocation); await fileDownloader.DownloadFile(downloadProgress.Report); @@ -221,16 +228,14 @@ public sealed class PluginManager : IPluginManager foreach (var dependency in dependencies) { string dependencyLocation = GenerateDependencyRelativePath(plugin.Name, dependency.DownloadLocation); - Action dependencyProgress = new Action(fileProgress => - { - installationProgressIndicator.SetProgress(dependency.DependencyName, fileProgress); - progress.Report(installationProgressIndicator); - }); - executor.AddTask(dependency.DownloadLink, dependencyLocation, dependencyProgress); + executor.AddTask(dependency.DownloadLink, dependencyLocation, progress.Report); } await executor.ExecuteAllTasks(); + + LocalPlugin localPlugin = LocalPlugin.FromOnlineInfo(plugin, dependencies, downloadLocation); + await AppendPluginToDatabase(localPlugin); } public async Task, List>> GatherInstallDataForPlugin(OnlinePlugin plugin) diff --git a/Plugins/LevelingSystem/LevelEvent.cs b/Plugins/LevelingSystem/LevelEvent.cs index f91712d..22e0a1e 100644 --- a/Plugins/LevelingSystem/LevelEvent.cs +++ b/Plugins/LevelingSystem/LevelEvent.cs @@ -15,9 +15,9 @@ internal class LevelEvent : IDbEvent public string Name => "Leveling System Event Handler"; public string Description => "The Leveling System Event Handler"; - public async Task Start(IDbEventExecutingArgument args) + public async void Start(IDbEventExecutingArgument args) { - + args.Logger.Log("Starting Leveling System Event Handler", this); Directory.CreateDirectory(DataFolder); await Task.Delay(200); Database = new SqlDatabase(DataFolder + "Users.db"); diff --git a/WebUI/Controllers/HomeController.cs b/WebUI/Controllers/HomeController.cs index 0b6312a..962a742 100644 --- a/WebUI/Controllers/HomeController.cs +++ b/WebUI/Controllers/HomeController.cs @@ -65,7 +65,7 @@ public class HomeController : Controller { _logger.Log("Loading plugins", this); await _pluginLoader.LoadPlugins(); - _logger.Log("Plugins loaded", this); + //_logger.Log("Plugins loaded", this); return RedirectToAction("Index"); } } \ No newline at end of file diff --git a/WebUI/Controllers/PluginsController.cs b/WebUI/Controllers/PluginsController.cs index 6b042f8..2ec5a58 100644 --- a/WebUI/Controllers/PluginsController.cs +++ b/WebUI/Controllers/PluginsController.cs @@ -1,4 +1,5 @@ using DiscordBotCore.PluginManagement; +using DiscordBotCore.PluginManagement.Models; using Microsoft.AspNetCore.Mvc; using WebUI.Models; using ILogger = DiscordBotCore.Logging.ILogger; @@ -32,6 +33,7 @@ public class PluginsController : Controller pluginViewModel.Author = plugin.Author; pluginViewModel.Version = plugin.Version; pluginViewModel.DownloadUrl = plugin.DownloadLink; + pluginViewModel.Id = plugin.Id; pluginViewModels.Add(pluginViewModel); } @@ -66,4 +68,22 @@ public class PluginsController : Controller //TODO: Implement delete plugin return RedirectToAction("InstalledPlugins"); } + + [HttpPost] + public async Task InstallPlugin(int pluginId) + { + var pluginData = await _pluginManager.GetPluginDataById(pluginId); + if (pluginData is null) + { + _logger.Log($"Plugin with ID {pluginId} not found", this); + return RedirectToAction("OnlinePlugins"); + } + + IProgress progress = new Progress(f => _logger.Log($"Installing: {f}")); + + await _pluginManager.InstallPlugin(pluginData, progress); + + _logger.Log($"Plugin {pluginData.Name} installed", this); + return RedirectToAction("OnlinePlugins"); + } } \ No newline at end of file diff --git a/WebUI/Models/OnlinePluginViewModel.cs b/WebUI/Models/OnlinePluginViewModel.cs index 539b74e..16b919f 100644 --- a/WebUI/Models/OnlinePluginViewModel.cs +++ b/WebUI/Models/OnlinePluginViewModel.cs @@ -2,10 +2,10 @@ namespace WebUI.Models; public class OnlinePluginViewModel { + public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public string Author { get; set; } public string Version { get; set; } - public string DownloadUrl { get; set; } } \ No newline at end of file diff --git a/WebUI/Program.cs b/WebUI/Program.cs index 6db2b94..21cb11c 100644 --- a/WebUI/Program.cs +++ b/WebUI/Program.cs @@ -73,6 +73,7 @@ var builder = WebApplication.CreateBuilder(args); string defaultLogFormat = "{ThrowTime} {SenderName} {Message}"; string defaultLogFolder = "./Data/Logs"; +string defaultResourcesFolder = "./Data/Resources"; string defaultConfigFile = "./Data/Resources/config.json"; string defaultPluginFolder = "./Data/Plugins"; string defaultPluginDatabaseFile = "./Data/Resources/plugins.json"; @@ -135,7 +136,12 @@ builder.Services.AddSingleton(sp => ILogger logger = sp.GetRequiredService(); IConfiguration configuration = sp.GetRequiredService(); - Directory.CreateDirectory(configuration.Get("PluginFolder", defaultPluginFolder)); + string pluginFolder = configuration.Get("PluginFolder", defaultPluginFolder); + Directory.CreateDirectory(pluginFolder); + + string resourcesFolder = configuration.Get("ResourcesFolder", defaultResourcesFolder); + Directory.CreateDirectory(resourcesFolder); + string pluginDatabaseFile = configuration.Get("PluginDatabase", defaultPluginDatabaseFile); Directory.CreateDirectory(new FileInfo(pluginDatabaseFile).DirectoryName); @@ -159,8 +165,6 @@ builder.Services.AddSingleton(sp => return new DiscordBotApplication(logger, configuration, pluginLoader); }); - - var app = builder.Build(); // Configure the HTTP request pipeline. @@ -182,4 +186,23 @@ app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); +// Force eager creation of all required services +using (var scope = app.Services.CreateScope()) +{ + var provider = scope.ServiceProvider; + + // Manually resolve all your singletons here + provider.GetRequiredService(); + IConfiguration config = provider.GetRequiredService(); + provider.GetRequiredService(); + provider.GetRequiredService(); + provider.GetRequiredService(); + provider.GetRequiredService(); + provider.GetRequiredService(); + + // Optional: Log that all services were initialized + provider.GetRequiredService().Log("All core services have been initialized at startup."); +} + + app.Run(); \ No newline at end of file diff --git a/WebUI/Views/Plugins/OnlinePlugins.cshtml b/WebUI/Views/Plugins/OnlinePlugins.cshtml index cd7881f..0c8a039 100644 --- a/WebUI/Views/Plugins/OnlinePlugins.cshtml +++ b/WebUI/Views/Plugins/OnlinePlugins.cshtml @@ -25,7 +25,9 @@ @plugin.Author @plugin.Version - Download +
+ +
}