From 84b19e20697d1102f04b3ac9db93782658ddc658 Mon Sep 17 00:00:00 2001 From: Andrei Tudor Date: Sun, 26 Jan 2025 20:34:34 +0200 Subject: [PATCH] Updated API for plugins to work with database from remote. Added PluginRepository and removed Installation Scripts --- DiscordBot/Bot/Actions/AddPlugin.cs | 2 +- DiscordBot/Bot/Actions/Extra/PluginMethods.cs | 159 ++---------- DiscordBot/Bot/Actions/Plugin.cs | 2 +- DiscordBot/DiscordBot.csproj | 2 +- DiscordBot/Program.cs | 10 - DiscordBot/Utilities/Console Utilities.cs | 23 +- .../PluginManagement/PluginInstallEndpoint.cs | 4 +- .../PluginManagement/PluginListEndpoint.cs | 6 +- DiscordBotCore/Application.cs | 30 +-- .../PluginManagement/IPluginRepository.cs | 16 ++ .../IPluginRepositoryConfiguration.cs | 11 + .../PluginManager/IPluginManager.cs | 28 --- .../Interfaces/Updater/AppVersion.cs | 81 ------ DiscordBotCore/Interfaces/Updater/IVersion.cs | 17 -- DiscordBotCore/Interfaces/Updater/Version.cs | 76 ------ DiscordBotCore/Online/FileDownloader.cs | 94 +++++++ .../Online/Helpers/OnlineFunctions.cs | 17 -- .../Online/Helpers/PluginRepository.cs | 89 +++++++ .../Helpers/PluginRepositoryConfiguration.cs | 19 ++ .../Online/Helpers/PluginVersion.cs | 20 -- DiscordBotCore/Online/PluginManager.cs | 233 +++++------------- DiscordBotCore/Online/ServerCom.cs | 67 +---- DiscordBotCore/Others/Enums.cs | 9 - DiscordBotCore/Others/JsonManager.cs | 7 +- DiscordBotCore/Others/OS.cs | 48 ++++ DiscordBotCore/Plugin/OnlineDependencyInfo.cs | 2 + DiscordBotCore/Plugin/OnlinePlugin.cs | 27 ++ .../Plugin/OnlineScriptDependencyInfo.cs | 14 -- DiscordBotCore/Plugin/PluginInfo.cs | 27 +- DiscordBotCore/Plugin/PluginOnlineInfo.cs | 56 ----- DiscordBotCore/Repository/PluginRepository.cs | 51 ---- DiscordBotCore/Repository/RepositoryBase.cs | 33 --- .../Updater/Application/AppUpdater.cs | 127 ---------- DiscordBotCore/Updater/Application/Update.cs | 19 -- .../Updater/Plugins/PluginUpdater.cs | 65 ----- 35 files changed, 435 insertions(+), 1056 deletions(-) create mode 100644 DiscordBotCore/Interfaces/PluginManagement/IPluginRepository.cs create mode 100644 DiscordBotCore/Interfaces/PluginManagement/IPluginRepositoryConfiguration.cs delete mode 100644 DiscordBotCore/Interfaces/PluginManager/IPluginManager.cs delete mode 100644 DiscordBotCore/Interfaces/Updater/AppVersion.cs delete mode 100644 DiscordBotCore/Interfaces/Updater/IVersion.cs delete mode 100644 DiscordBotCore/Interfaces/Updater/Version.cs create mode 100644 DiscordBotCore/Online/FileDownloader.cs create mode 100644 DiscordBotCore/Online/Helpers/PluginRepository.cs create mode 100644 DiscordBotCore/Online/Helpers/PluginRepositoryConfiguration.cs delete mode 100644 DiscordBotCore/Online/Helpers/PluginVersion.cs create mode 100644 DiscordBotCore/Others/OS.cs create mode 100644 DiscordBotCore/Plugin/OnlinePlugin.cs delete mode 100644 DiscordBotCore/Plugin/OnlineScriptDependencyInfo.cs delete mode 100644 DiscordBotCore/Plugin/PluginOnlineInfo.cs delete mode 100644 DiscordBotCore/Repository/PluginRepository.cs delete mode 100644 DiscordBotCore/Repository/RepositoryBase.cs delete mode 100644 DiscordBotCore/Updater/Application/AppUpdater.cs delete mode 100644 DiscordBotCore/Updater/Application/Update.cs delete mode 100644 DiscordBotCore/Updater/Plugins/PluginUpdater.cs diff --git a/DiscordBot/Bot/Actions/AddPlugin.cs b/DiscordBot/Bot/Actions/AddPlugin.cs index 1172833..bc46e4f 100644 --- a/DiscordBot/Bot/Actions/AddPlugin.cs +++ b/DiscordBot/Bot/Actions/AddPlugin.cs @@ -53,7 +53,7 @@ namespace DiscordBot.Bot.Actions Application.CurrentApplication.Logger.Log("The plugin name is invalid", LogType.Error); } - PluginInfo pluginInfo = new PluginInfo(args[^1], new(1, 0, 0), [], false, true, args.Contains("-enabled")); + PluginInfo pluginInfo = new PluginInfo(args[^1], "1.0.0", [], false, true, args.Contains("-enabled")); Application.CurrentApplication.Logger.Log("Adding plugin: " + args[^1]); await Application.CurrentApplication.PluginManager.AppendPluginToDatabase(pluginInfo); } diff --git a/DiscordBot/Bot/Actions/Extra/PluginMethods.cs b/DiscordBot/Bot/Actions/Extra/PluginMethods.cs index cb003c8..bf225da 100644 --- a/DiscordBot/Bot/Actions/Extra/PluginMethods.cs +++ b/DiscordBot/Bot/Actions/Extra/PluginMethods.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Threading.Tasks; using DiscordBot.Utilities; @@ -22,51 +23,20 @@ internal static class PluginMethods { Console.WriteLine($"Fetching plugin list ..."); - var data = await ConsoleUtilities.ExecuteWithProgressBar(Application.CurrentApplication.PluginManager.GetPluginsList(), "Reading remote database"); - - TableData tableData = new(["Name", "Description", "Version", "Installed", "Dependencies", "Enabled"]); - + var onlinePlugins = await ConsoleUtilities.ExecuteWithProgressBar(Application.CurrentApplication.PluginManager.GetPluginsList(), "Reading remote database"); var installedPlugins = await ConsoleUtilities.ExecuteWithProgressBar(Application.CurrentApplication.PluginManager.GetInstalledPlugins(), "Reading local database "); + TableData tableData = new(["Name", "Description", "Author", "Latest Version", "Installed Version"]); - foreach (var plugin in data) + foreach (var onlinePlugin in onlinePlugins) { - bool isInstalled = installedPlugins.Any(p => p.PluginName == plugin.Name); - - if (!plugin.HasFileDependencies) - { - tableData.AddRow([plugin.Name, plugin.Description, - plugin.Version.ToString(), isInstalled ? "[green]Yes[/]" : "[red]No[/]", "None", - isInstalled ? installedPlugins.First(p=>p.PluginName == plugin.Name).IsEnabled ? "[green]Enabled[/]" : "[red]Disabled[/]" : "[yellow]NOT INSTALLED[/]"]); - continue; - } - - TableData dependenciesTable; - - - if (isInstalled) - { - dependenciesTable = new(["Name", "Location", "Is Executable"]); - foreach (var dep in plugin.Dependencies) - { - dependenciesTable.AddRow([dep.DependencyName, dep.DownloadLocation, dep.IsExecutable ? "Yes" : "No"]); - } - - } - else - { - dependenciesTable = new(["Name", "Is Executable"]); - foreach (var dep in plugin.Dependencies) - { - dependenciesTable.AddRow([dep.DependencyName, dep.IsExecutable ? "Yes" : "No"]); - } - } - - dependenciesTable.DisplayLinesBetweenRows = true; - - Table spectreTable = dependenciesTable.AsTable(); - - tableData.AddRow([plugin.Name, plugin.Description, plugin.Version.ToString(), isInstalled ? "[green]Yes[/]" : "[red]No[/]", spectreTable, - isInstalled ? installedPlugins.First(p=>p.PluginName == plugin.Name).IsEnabled ? "Enabled" : "[red]Disabled[/]" : "[yellow]NOT INSTALLED[/]"]); + bool isInstalled = installedPlugins.Any(p => p.PluginName == onlinePlugin.PluginName); + tableData.AddRow([ + onlinePlugin.PluginName, + onlinePlugin.PluginDescription, + onlinePlugin.PluginAuthor, + onlinePlugin.LatestVersion, + isInstalled ? installedPlugins.First(p => p.PluginName == onlinePlugin.PluginName).PluginVersion : "Not Installed" + ]); } tableData.HasRoundBorders = false; @@ -100,113 +70,24 @@ internal static class PluginMethods await Application.CurrentApplication.PluginManager.SetEnabledStatus(pluginName, true); } - internal static async Task DownloadPlugin(string pluginName) + public static async Task DownloadPluginWithParallelDownloads(string pluginName) { - var pluginData = await Application.CurrentApplication.PluginManager.GetPluginDataByName(pluginName); + OnlinePlugin? pluginData = await Application.CurrentApplication.PluginManager.GetPluginDataByName(pluginName); + if (pluginData is null) { Console.WriteLine($"Plugin {pluginName} not found. Please check the spelling and try again."); return; } - // rename the plugin to the name of the plugin - pluginName = pluginData.Name; - - var pluginLink = pluginData.DownLoadLink; - - - await AnsiConsole.Progress() - .Columns(new ProgressColumn[] - { - new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn() - } - ) - .StartAsync(async ctx => - { - var downloadTask = ctx.AddTask("Downloading plugin..."); - - IProgress progress = new Progress(p => { downloadTask.Value = p; }); - - string baseFolder = Application.CurrentApplication.ApplicationEnvironmentVariables.Get("PluginFolder"); - - await ServerCom.DownloadFileAsync(pluginLink, $"{baseFolder}/{pluginData.Name}.dll", progress); - - downloadTask.Increment(100); - - ctx.Refresh(); - } - ); - - if (!pluginData.HasFileDependencies) - { - if (pluginData.HasScriptDependencies) - { - Console.WriteLine("Executing post install scripts ..."); - await Application.CurrentApplication.PluginManager.ExecutePluginInstallScripts(pluginData.ScriptDependencies); - } - - PluginInfo pluginInfo = new(pluginName, pluginData.Version, []); - Console.WriteLine("Finished installing " + pluginName + " successfully"); - await Application.CurrentApplication.PluginManager.AppendPluginToDatabase(pluginInfo); - await RefreshPlugins(false); - return; - } - - List, string, string>> downloadTasks = new(); - await AnsiConsole.Progress() - .Columns(new ProgressColumn[] - { - new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn() - } - ) - .StartAsync(async ctx => - { - - - foreach (var dependency in pluginData.Dependencies) - { - var task = ctx.AddTask($"Downloading {dependency.DownloadLocation} -> Executable: {dependency.IsExecutable}: "); - IProgress progress = new Progress(p => - { - task.Value = p; - } - ); - - task.IsIndeterminate = true; - downloadTasks.Add(new Tuple, string, string>(task, progress, dependency.DownloadLink, dependency.DownloadLocation)); - } - - int maxParallelDownloads = Application.CurrentApplication.ApplicationEnvironmentVariables.Get("MaxParallelDownloads", 5); - - var options = new ParallelOptions() - { - MaxDegreeOfParallelism = maxParallelDownloads, - TaskScheduler = TaskScheduler.Default - }; - - await Parallel.ForEachAsync(downloadTasks, options, async (tuple, token) => - { - tuple.Item1.IsIndeterminate = false; - string downloadLocation = Application.CurrentApplication.PluginManager.GenerateDependencyRelativePath(pluginName, tuple.Item4); - await ServerCom.DownloadFileAsync(tuple.Item3, downloadLocation, tuple.Item2); - } - ); - - - - } - ); + var result = await Application.CurrentApplication.PluginManager.GatherInstallDataForPlugin(pluginData); + List> downloadList = result.Item1.Select(kvp => new Tuple(kvp.Key, kvp.Value)).ToList(); + await ConsoleUtilities.ExecuteParallelDownload(FileDownloader.CreateDownloadTask, new HttpClient(), downloadList, "Downloading:"); - if(pluginData.HasScriptDependencies) - { - Console.WriteLine("Executing post install scripts ..."); - await Application.CurrentApplication.PluginManager.ExecutePluginInstallScripts(pluginData.ScriptDependencies); - } - - await Application.CurrentApplication.PluginManager.AppendPluginToDatabase(PluginInfo.FromOnlineInfo(pluginData)); - await RefreshPlugins(false); + await Application.CurrentApplication.PluginManager.AppendPluginToDatabase(PluginInfo.FromOnlineInfo(pluginData, result.Item2)); } + internal static async Task LoadPlugins(string[] args) { diff --git a/DiscordBot/Bot/Actions/Plugin.cs b/DiscordBot/Bot/Actions/Plugin.cs index 531badc..78f4438 100644 --- a/DiscordBot/Bot/Actions/Plugin.cs +++ b/DiscordBot/Bot/Actions/Plugin.cs @@ -118,7 +118,7 @@ public class Plugin: ICommandAction } } - await PluginMethods.DownloadPlugin(pluginName); + await PluginMethods.DownloadPluginWithParallelDownloads(pluginName); break; } diff --git a/DiscordBot/DiscordBot.csproj b/DiscordBot/DiscordBot.csproj index f4f4341..c87f489 100644 --- a/DiscordBot/DiscordBot.csproj +++ b/DiscordBot/DiscordBot.csproj @@ -2,7 +2,7 @@ Exe net8.0 - disable + enable False diff --git a/DiscordBot/Program.cs b/DiscordBot/Program.cs index 9f4edda..0644398 100644 --- a/DiscordBot/Program.cs +++ b/DiscordBot/Program.cs @@ -4,11 +4,9 @@ using System.Reflection; using System.Threading.Tasks; using DiscordBot.Bot.Actions.Extra; -using DiscordBot.Utilities; using DiscordBotCore; using DiscordBotCore.Bot; using DiscordBotCore.Others; -using DiscordBotCore.Updater.Application; using Spectre.Console; @@ -81,14 +79,6 @@ public class Program { await Application.CreateApplication(); - AppUpdater updater = new AppUpdater(); - Update? update = await updater.PrepareUpdate(); - if(update is not null) - { - await ConsoleUtilities.ExecuteTaskWithBuiltInProgress(updater.SelfUpdate, update, "Discord Bot Update"); - return; - } - void LogMessageFunction(string message, LogType logType) { string messageAsString = message; diff --git a/DiscordBot/Utilities/Console Utilities.cs b/DiscordBot/Utilities/Console Utilities.cs index ff72dce..c5ef1fc 100644 --- a/DiscordBot/Utilities/Console Utilities.cs +++ b/DiscordBot/Utilities/Console Utilities.cs @@ -1,6 +1,9 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; using System.Threading.Tasks; -using DiscordBotCore.Updater.Application; +using DiscordBotCore.Online; using Spectre.Console; namespace DiscordBot.Utilities; @@ -64,4 +67,22 @@ internal static class ConsoleUtilities } + public static async Task ExecuteParallelDownload(Func, Task> method, HttpClient client, + List> parameters, string taskMessage) + { + await AnsiConsole.Progress().AutoClear(false).HideCompleted(false) + .Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()) + .StartAsync(async ctx => + { + var tasks = new List(); + foreach (var (location, url) in parameters) + { + var task = ctx.AddTask(taskMessage + " " + url); + IProgress progress = new Progress(x => task.Value = x * 100); + tasks.Add(method(client, url, location, progress)); + } + + await Task.WhenAll(tasks); + }); + } } \ No newline at end of file diff --git a/DiscordBotCore/API/Endpoints/PluginManagement/PluginInstallEndpoint.cs b/DiscordBotCore/API/Endpoints/PluginManagement/PluginInstallEndpoint.cs index 8fce27e..9444f66 100644 --- a/DiscordBotCore/API/Endpoints/PluginManagement/PluginInstallEndpoint.cs +++ b/DiscordBotCore/API/Endpoints/PluginManagement/PluginInstallEndpoint.cs @@ -16,14 +16,14 @@ public class PluginInstallEndpoint : IEndpoint Dictionary jsonDict = await JsonManager.ConvertFromJson>(jsonRequest); string pluginName = jsonDict["pluginName"]; - PluginOnlineInfo? pluginInfo = await Application.CurrentApplication.PluginManager.GetPluginDataByName(pluginName); + OnlinePlugin? pluginInfo = await Application.CurrentApplication.PluginManager.GetPluginDataByName(pluginName); if (pluginInfo == null) { return ApiResponse.Fail("Plugin not found."); } - Application.CurrentApplication.PluginManager.InstallPluginWithNoProgress(pluginInfo); + Application.CurrentApplication.PluginManager.InstallPluginNoProgress(pluginInfo); return ApiResponse.Ok(); } } diff --git a/DiscordBotCore/API/Endpoints/PluginManagement/PluginListEndpoint.cs b/DiscordBotCore/API/Endpoints/PluginManagement/PluginListEndpoint.cs index 1dc49a3..15acc4b 100644 --- a/DiscordBotCore/API/Endpoints/PluginManagement/PluginListEndpoint.cs +++ b/DiscordBotCore/API/Endpoints/PluginManagement/PluginListEndpoint.cs @@ -14,9 +14,9 @@ public class PluginListEndpoint : IEndpoint var onlineInfos = await Application.CurrentApplication.PluginManager.GetPluginsList(); var response = await JsonManager.ConvertToJson(onlineInfos, [ - nameof(PluginOnlineInfo.Name), - nameof(PluginOnlineInfo.Author), - nameof(PluginOnlineInfo.Description) + nameof(OnlinePlugin.PluginName), + nameof(OnlinePlugin.PluginAuthor), + nameof(OnlinePlugin.PluginDescription) ]); return ApiResponse.From(response, true); diff --git a/DiscordBotCore/Application.cs b/DiscordBotCore/Application.cs index 78307c0..7636a94 100644 --- a/DiscordBotCore/Application.cs +++ b/DiscordBotCore/Application.cs @@ -14,7 +14,6 @@ using DiscordBotCore.Others.Actions; using DiscordBotCore.Others.Settings; using DiscordBotCore.Plugin; using DiscordBotCore.Logging; -using DiscordBotCore.Repository; namespace DiscordBotCore { @@ -55,12 +54,6 @@ namespace DiscordBotCore /// public static async Task CreateApplication() { - if (!await OnlineFunctions.IsInternetConnected()) - { - Console.WriteLine("The main repository server is not reachable. Please check your internet connection."); - Environment.Exit(0); - } - if (CurrentApplication is not null) { CurrentApplication.Logger.Log("Application is already initialized. Reinitialization is not allowed", LogType.Error); @@ -87,32 +80,13 @@ namespace DiscordBotCore await JsonManager.SaveToJsonFile(_PluginsDatabaseFile, plugins); } - CurrentApplication.PluginManager = new PluginManager(PluginRepository.SolveRepo()); + CurrentApplication.PluginManager = new PluginManager(new PluginRepository(PluginRepositoryConfiguration.Default)); await CurrentApplication.PluginManager.UninstallMarkedPlugins(); - await CurrentApplication.PluginManager.CheckForUpdates(); CurrentApplication.InternalActionManager = new InternalActionManager(); await CurrentApplication.InternalActionManager.Initialize(); - if (OperatingSystem.IsWindows()) - { - CurrentApplication.ApplicationEnvironmentVariables.Add("console.terminal", "cmd"); - CurrentApplication.ApplicationEnvironmentVariables.Add("console.cmd_prefix", "/c "); - } - - if(OperatingSystem.IsLinux()) - { - CurrentApplication.ApplicationEnvironmentVariables.Add("console.terminal", "bash"); - CurrentApplication.ApplicationEnvironmentVariables.Add("console.cmd_prefix", string.Empty); - } - - if(OperatingSystem.IsMacOS()) - { - CurrentApplication.ApplicationEnvironmentVariables.Add("console.terminal", "sh"); - CurrentApplication.ApplicationEnvironmentVariables.Add("console.cmd_prefix", string.Empty); - } - IsRunning = true; } @@ -165,7 +139,7 @@ namespace DiscordBotCore return result; } - public static void Log(string message, LogType? logType = LogType.Info) + public static void Log(string message, LogType logType = LogType.Info) { CurrentApplication.Logger.Log(message, logType); } diff --git a/DiscordBotCore/Interfaces/PluginManagement/IPluginRepository.cs b/DiscordBotCore/Interfaces/PluginManagement/IPluginRepository.cs new file mode 100644 index 0000000..52e4076 --- /dev/null +++ b/DiscordBotCore/Interfaces/PluginManagement/IPluginRepository.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using DiscordBotCore.Plugin; + +namespace DiscordBotCore.Interfaces.PluginManagement; + +public interface IPluginRepository +{ + public Task> GetAllPlugins(); + + public Task GetPluginById(int pluginId); + public Task GetPluginByName(string pluginName); + + public Task> GetDependenciesForPlugin(int pluginId); + +} \ No newline at end of file diff --git a/DiscordBotCore/Interfaces/PluginManagement/IPluginRepositoryConfiguration.cs b/DiscordBotCore/Interfaces/PluginManagement/IPluginRepositoryConfiguration.cs new file mode 100644 index 0000000..f234745 --- /dev/null +++ b/DiscordBotCore/Interfaces/PluginManagement/IPluginRepositoryConfiguration.cs @@ -0,0 +1,11 @@ +namespace DiscordBotCore.Interfaces.PluginManagement; + +public interface IPluginRepositoryConfiguration +{ + public string BaseUrl { get; } + + public string PluginRepositoryLocation { get; } + public string DependenciesRepositoryLocation { get; } + + +} \ No newline at end of file diff --git a/DiscordBotCore/Interfaces/PluginManager/IPluginManager.cs b/DiscordBotCore/Interfaces/PluginManager/IPluginManager.cs deleted file mode 100644 index 1b2fd2c..0000000 --- a/DiscordBotCore/Interfaces/PluginManager/IPluginManager.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using DiscordBotCore.Plugin; - -namespace DiscordBotCore.Interfaces.PluginManager -{ - public interface IPluginManager - { - public string BaseUrl { get; set; } - public string Branch { get; set; } - Task AppendPluginToDatabase(PluginInfo pluginData); - Task CheckForUpdates(); - Task ExecutePluginInstallScripts(List listOfDependencies); - string GenerateDependencyRelativePath(string pluginName, string dependencyPath); - Task GetDependencyLocation(string dependencyName); - Task GetDependencyLocation(string pluginName, string dependencyName); - Task> GetInstalledPlugins(); - Task GetPluginDataByName(string pluginName); - Task?> GetPluginsList(); - Task InstallPlugin(PluginOnlineInfo pluginData, IProgress? installProgress); - Task IsPluginInstalled(string pluginName); - Task MarkPluginToUninstall(string pluginName); - Task RemovePluginFromDatabase(string pluginName); - Task UninstallMarkedPlugins(); - Task SetEnabledStatus(string pluginName, bool status); - } -} \ No newline at end of file diff --git a/DiscordBotCore/Interfaces/Updater/AppVersion.cs b/DiscordBotCore/Interfaces/Updater/AppVersion.cs deleted file mode 100644 index 8c9732c..0000000 --- a/DiscordBotCore/Interfaces/Updater/AppVersion.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Reflection; - -namespace DiscordBotCore.Interfaces.Updater -{ - public class AppVersion : IVersion - { - public static readonly AppVersion CurrentAppVersion = new AppVersion(Assembly.GetEntryAssembly().GetName().Version.ToString()); - - public int Major { get; set; } - public int Minor { get; set; } - public int Patch { get; set; } - public int PatchVersion { get; set; } - - private readonly char _Separator = '.'; - - public AppVersion(string versionAsString) - { - string[] versionParts = versionAsString.Split(_Separator); - - if (versionParts.Length != 4) - { - throw new ArgumentException("Invalid version string"); - } - - Major = int.Parse(versionParts[0]); - Minor = int.Parse(versionParts[1]); - Patch = int.Parse(versionParts[2]); - PatchVersion = int.Parse(versionParts[3]); - } - - public bool IsNewerThan(IVersion version) - { - if (Major > version.Major) - return true; - - if (Major == version.Major && Minor > version.Minor) - return true; - - if (Major == version.Major && Minor == version.Minor && Patch > version.Patch) - return true; - - if (Major == version.Major && Minor == version.Minor && Patch == version.Patch && PatchVersion > version.PatchVersion) - return true; - - return false; - } - - public bool IsOlderThan(IVersion version) - { - if (Major < version.Major) - return true; - - if (Major == version.Major && Minor < version.Minor) - return true; - - if (Major == version.Major && Minor == version.Minor && Patch < version.Patch) - return true; - - if (Major == version.Major && Minor == version.Minor && Patch == version.Patch && PatchVersion < version.PatchVersion) - return true; - - return false; - } - - public bool IsEqualTo(IVersion version) - { - return Major == version.Major && Minor == version.Minor && Patch == version.Patch && PatchVersion == version.PatchVersion; - } - - public string ToShortString() - { - return $"{Major}.{Minor}.{Patch}.{PatchVersion}"; - } - - public override string ToString() - { - return ToShortString(); - } - } -} diff --git a/DiscordBotCore/Interfaces/Updater/IVersion.cs b/DiscordBotCore/Interfaces/Updater/IVersion.cs deleted file mode 100644 index 275482c..0000000 --- a/DiscordBotCore/Interfaces/Updater/IVersion.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace DiscordBotCore.Interfaces.Updater; - -public interface IVersion -{ - public int Major { get; } - public int Minor { get; } - public int Patch { get; } - public int PatchVersion => 0; - - public bool IsNewerThan(IVersion version); - - public bool IsOlderThan(IVersion version); - - public bool IsEqualTo(IVersion version); - - public string ToShortString(); -} diff --git a/DiscordBotCore/Interfaces/Updater/Version.cs b/DiscordBotCore/Interfaces/Updater/Version.cs deleted file mode 100644 index 67c3f76..0000000 --- a/DiscordBotCore/Interfaces/Updater/Version.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; - -namespace DiscordBotCore.Interfaces.Updater; - -public abstract class Version: IVersion -{ - public int Major { get; } - public int Minor { get; } - public int Patch { get; } - - protected readonly char _Separator = '.'; - - protected Version(int major, int minor, int patch) - { - Major = major; - Minor = minor; - Patch = patch; - } - - protected Version(string versionAsString) - { - string[] versionParts = versionAsString.Split(_Separator); - - if (versionParts.Length != 3) - { - throw new ArgumentException("Invalid version string"); - } - - Major = int.Parse(versionParts[0]); - Minor = int.Parse(versionParts[1]); - Patch = int.Parse(versionParts[2]); - } - - public bool IsNewerThan(IVersion version) - { - if (Major > version.Major) - return true; - - if (Major == version.Major && Minor > version.Minor) - return true; - - if (Major == version.Major && Minor == version.Minor && Patch > version.Patch) - return true; - - return false; - } - - public bool IsOlderThan(IVersion version) - { - if (Major < version.Major) - return true; - - if (Major == version.Major && Minor < version.Minor) - return true; - - if (Major == version.Major && Minor == version.Minor && Patch < version.Patch) - return true; - - return false; - } - - public bool IsEqualTo(IVersion version) - { - return Major == version.Major && Minor == version.Minor && Patch == version.Patch; - } - - public string ToShortString() - { - return $"{Major}.{Minor}.{Patch}"; - } - - public override string ToString() - { - return ToShortString(); - } -} diff --git a/DiscordBotCore/Online/FileDownloader.cs b/DiscordBotCore/Online/FileDownloader.cs new file mode 100644 index 0000000..64d7f9e --- /dev/null +++ b/DiscordBotCore/Online/FileDownloader.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; + +namespace DiscordBotCore.Online; + +public class FileDownloader +{ + private readonly HttpClient _httpClient; + public List ListOfDownloadTasks { get; private set; } + private bool IsParallelDownloader { get; set; } + + public Action FinishAction { get; private set; } + + public FileDownloader(bool isParallelDownloader) + { + _httpClient = new HttpClient(); + ListOfDownloadTasks = new List(); + + IsParallelDownloader = isParallelDownloader; + } + + public FileDownloader(bool isParallelDownloader, Action finishAction) + { + _httpClient = new HttpClient(); + ListOfDownloadTasks = new List(); + + IsParallelDownloader = isParallelDownloader; + FinishAction = finishAction; + } + + public async Task StartDownloadTasks() + { + if (IsParallelDownloader) + { + await Task.WhenAll(ListOfDownloadTasks); + } + else + { + foreach (var task in ListOfDownloadTasks) + { + await task; + } + } + + FinishAction?.Invoke(); + } + + public void AppendDownloadTask(string downloadLink, string downloadLocation, IProgress progress) + { + ListOfDownloadTasks.Add(CreateDownloadTask(_httpClient, downloadLink, downloadLocation, progress)); + } + + public void AppendDownloadTask(string downloadLink, string downloadLocation, Action progressCallback) + { + ListOfDownloadTasks.Add(CreateDownloadTask(_httpClient, downloadLink, downloadLocation, new Progress(progressCallback))); + } + + public static async Task CreateDownloadTask(HttpClient client, string url, string targetPath, IProgress progress) + { + using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); + response.EnsureSuccessStatusCode(); + + var totalBytes = response.Content.Headers.ContentLength ?? -1L; + var receivedBytes = 0L; + + var targetDirectory = Path.GetDirectoryName(targetPath); + if (!string.IsNullOrEmpty(targetDirectory)) + { + Directory.CreateDirectory(targetDirectory); + } + + using var contentStream = await response.Content.ReadAsStreamAsync(); + using var fileStream = new FileStream(targetPath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true); + var buffer = new byte[8192]; + int bytesRead; + + while ((bytesRead = await contentStream.ReadAsync(buffer)) > 0) + { + await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead)); + receivedBytes += bytesRead; + + if (totalBytes > 0) + { + float calculatedProgress = (float)receivedBytes / totalBytes; + progress.Report(calculatedProgress); + } + } + + progress.Report(1f); + } +} \ No newline at end of file diff --git a/DiscordBotCore/Online/Helpers/OnlineFunctions.cs b/DiscordBotCore/Online/Helpers/OnlineFunctions.cs index 820ca80..9134a1b 100644 --- a/DiscordBotCore/Online/Helpers/OnlineFunctions.cs +++ b/DiscordBotCore/Online/Helpers/OnlineFunctions.cs @@ -107,21 +107,4 @@ internal static class OnlineFunctions using var client = new HttpClient(); return await client.GetStringAsync(url, cancellation); } - - internal static async Task IsInternetConnected() - { - bool result = false; - try - { - using var client = new HttpClient(); - await client.GetStringAsync("files.wizzy-server.ro"); - result = true; - } - catch - { - result = false; - } - - return result; - } } diff --git a/DiscordBotCore/Online/Helpers/PluginRepository.cs b/DiscordBotCore/Online/Helpers/PluginRepository.cs new file mode 100644 index 0000000..6b2770c --- /dev/null +++ b/DiscordBotCore/Online/Helpers/PluginRepository.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Threading.Tasks; +using DiscordBotCore.Interfaces.PluginManagement; +using DiscordBotCore.Others; +using DiscordBotCore.Plugin; + +namespace DiscordBotCore.Online.Helpers; + +internal class PluginRepository : IPluginRepository +{ + private readonly IPluginRepositoryConfiguration _pluginRepositoryConfiguration; + private readonly HttpClient _httpClient; + internal PluginRepository(IPluginRepositoryConfiguration pluginRepositoryConfiguration) + { + _pluginRepositoryConfiguration = pluginRepositoryConfiguration; + _httpClient = new HttpClient(); + _httpClient.BaseAddress = new System.Uri(_pluginRepositoryConfiguration.BaseUrl); + } + + + public async Task> GetAllPlugins() + { + HttpResponseMessage response = + await _httpClient.GetAsync(_pluginRepositoryConfiguration.PluginRepositoryLocation + "get-all-plugins"); + + if (!response.IsSuccessStatusCode) + { + Application.Log("Failed to get all plugins from the repository", LogType.Warning); + return []; + } + + string content = await response.Content.ReadAsStringAsync(); + List plugins = await JsonManager.ConvertFromJson>(content); + + return plugins; + } + + public async Task GetPluginById(int pluginId) + { + HttpResponseMessage response = + await _httpClient.GetAsync(_pluginRepositoryConfiguration.PluginRepositoryLocation + + $"get-plugin/{pluginId}"); + + if (!response.IsSuccessStatusCode) + { + Application.Log("Failed to get plugin from the repository", LogType.Warning); + return null; + } + + string content = await response.Content.ReadAsStringAsync(); + OnlinePlugin plugin = await JsonManager.ConvertFromJson(content); + + return plugin; + } + + public async Task GetPluginByName(string pluginName) + { + HttpResponseMessage response = + await _httpClient.GetAsync(_pluginRepositoryConfiguration.PluginRepositoryLocation + + $"get-plugin-by-name/{pluginName}"); + + if (!response.IsSuccessStatusCode) + { + Application.Log("Failed to get plugin from the repository", LogType.Warning); + return null; + } + + string content = await response.Content.ReadAsStringAsync(); + OnlinePlugin plugin = await JsonManager.ConvertFromJson(content); + + return plugin; + } + + public async Task> GetDependenciesForPlugin(int pluginId) + { + HttpResponseMessage response = await _httpClient.GetAsync(_pluginRepositoryConfiguration.DependenciesRepositoryLocation + $"get-dependencies-for-plugin/{pluginId}"); + if(!response.IsSuccessStatusCode) + { + Application.Log("Failed to get dependencies for plugin from the repository", LogType.Warning); + return []; + } + + string content = await response.Content.ReadAsStringAsync(); + List dependencies = await JsonManager.ConvertFromJson>(content); + + return dependencies; + } +} \ No newline at end of file diff --git a/DiscordBotCore/Online/Helpers/PluginRepositoryConfiguration.cs b/DiscordBotCore/Online/Helpers/PluginRepositoryConfiguration.cs new file mode 100644 index 0000000..4b309dd --- /dev/null +++ b/DiscordBotCore/Online/Helpers/PluginRepositoryConfiguration.cs @@ -0,0 +1,19 @@ +using DiscordBotCore.Interfaces.PluginManagement; + +namespace DiscordBotCore.Online.Helpers; + +public class PluginRepositoryConfiguration : IPluginRepositoryConfiguration +{ + public static PluginRepositoryConfiguration Default => new ("http://localhost:5097/api/v1/", "plugins-repository/", "dependencies-repository/"); + + public string BaseUrl { get; } + public string PluginRepositoryLocation { get; } + public string DependenciesRepositoryLocation { get; } + + public PluginRepositoryConfiguration(string baseUrl, string pluginRepositoryLocation, string dependenciesRepositoryLocation) + { + BaseUrl = baseUrl; + PluginRepositoryLocation = pluginRepositoryLocation; + DependenciesRepositoryLocation = dependenciesRepositoryLocation; + } +} \ No newline at end of file diff --git a/DiscordBotCore/Online/Helpers/PluginVersion.cs b/DiscordBotCore/Online/Helpers/PluginVersion.cs deleted file mode 100644 index 409bdf2..0000000 --- a/DiscordBotCore/Online/Helpers/PluginVersion.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Text.Json.Serialization; -using DiscordBotCore.Interfaces.Updater; - -namespace DiscordBotCore.Online.Helpers; - -public class PluginVersion: Version -{ - [JsonConstructor] - public PluginVersion(int major, int minor, int patch): base(major, minor, patch) - { - } - public PluginVersion(string versionAsString): base(versionAsString) - { - } - - public override string ToString() - { - return ToShortString(); - } -} diff --git a/DiscordBotCore/Online/PluginManager.cs b/DiscordBotCore/Online/PluginManager.cs index db1e93c..312cf59 100644 --- a/DiscordBotCore/Online/PluginManager.cs +++ b/DiscordBotCore/Online/PluginManager.cs @@ -3,56 +3,54 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; - +using DiscordBotCore.Interfaces.PluginManagement; using DiscordBotCore.Others; using DiscordBotCore.Plugin; -using DiscordBotCore.Repository; -using DiscordBotCore.Updater.Plugins; namespace DiscordBotCore.Online; public sealed class PluginManager { private static readonly string _LibrariesBaseFolder = "Libraries"; - private readonly PluginRepository _PluginRepository; + private readonly IPluginRepository _PluginRepository; internal InstallingPluginInformation? InstallingPluginInformation { get; private set; } - public PluginManager(PluginRepository pluginRepository) + internal PluginManager(IPluginRepository pluginRepository) { _PluginRepository = pluginRepository; } - public async Task> GetPluginsList() + public async Task> GetPluginsList() { - var jsonText = await _PluginRepository.JsonGetAllPlugins(); - List result = await JsonManager.ConvertFromJson>(jsonText); - - var currentOs = OperatingSystem.IsWindows() ? OSType.WINDOWS : - OperatingSystem.IsLinux() ? OSType.LINUX : - OperatingSystem.IsMacOS() ? OSType.MACOSX : OSType.NONE; - - return result.FindAll(pl => (pl.SupportedOS & currentOs) != 0); - } - - public async Task GetPluginDataByName(string pluginName) - { - List? plugins = await GetPluginsList(); - - if (plugins is null) + var onlinePlugins = await _PluginRepository.GetAllPlugins(); + + if (!onlinePlugins.Any()) { - return null; + Application.Log("Failed to get all plugins from the repository", LogType.Warning); + return []; } - PluginOnlineInfo? result = plugins.Find(pl => pl.Name.Contains(pluginName, StringComparison.CurrentCultureIgnoreCase)); - if (result is null) + int os = OS.GetOperatingSystemInt(); + + var response = onlinePlugins.Where(plugin => plugin.OperatingSystem == os).ToList(); + + return response; + } + + public async Task GetPluginDataByName(string pluginName) + { + var plugin = await _PluginRepository.GetPluginByName(pluginName); + + if (plugin == null) { + Application.Log("Failed to get plugin from the repository", LogType.Warning); return null; } - return result; + return plugin; } - public async Task RemovePluginFromDatabase(string pluginName) + private async Task RemovePluginFromDatabase(string pluginName) { List installedPlugins = await JsonManager.ConvertFromJson>(await File.ReadAllTextAsync(Application.CurrentApplication.PluginDatabase)); @@ -60,24 +58,6 @@ public sealed class PluginManager await JsonManager.SaveToJsonFile(Application.CurrentApplication.PluginDatabase, installedPlugins); } - public async Task ExecutePluginInstallScripts(List listOfDependencies) - { - string? console = Application.CurrentApplication.ApplicationEnvironmentVariables.Get("console.terminal"); - if (string.IsNullOrEmpty(console)) - { - return; - } - - string? cmd_prefix = Application.CurrentApplication.ApplicationEnvironmentVariables.Get("console.cmd_prefix"); - if (string.IsNullOrEmpty(cmd_prefix)) - { - return; - } - - foreach (var script in listOfDependencies) - await ServerCom.RunConsoleCommand(console, $"{cmd_prefix}{script.ScriptContent}"); - } - public async Task AppendPluginToDatabase(PluginInfo pluginData) { List installedPlugins = await JsonManager.ConvertFromJson>(await File.ReadAllTextAsync(Application.CurrentApplication.PluginDatabase)); @@ -102,26 +82,10 @@ public sealed class PluginManager return installedPlugins.Any(plugin => plugin.PluginName == pluginName); } - public async Task CheckForUpdates() - { - var pluginUpdater = new PluginUpdater(this); - - List installedPlugins = await GetInstalledPlugins(); - - foreach (var plugin in installedPlugins) - { - if (await pluginUpdater.HasUpdate(plugin.PluginName)) - { - Application.CurrentApplication.Logger.Log("Updating plugin: " + plugin.PluginName, this, LogType.Info); - await pluginUpdater.UpdatePlugin(plugin.PluginName); - } - } - } - public async Task MarkPluginToUninstall(string pluginName) { - IEnumerable installedPlugins = await GetInstalledPlugins(); - IEnumerable info = installedPlugins.Where(info => info.PluginName == pluginName).AsEnumerable(); + List installedPlugins = await GetInstalledPlugins(); + List info = installedPlugins.Where(info => info.PluginName == pluginName).ToList(); if (!info.Any()) return false; @@ -132,10 +96,8 @@ public sealed class PluginManager item.IsMarkedToUninstall = true; await AppendPluginToDatabase(item); } - - + return true; - } public async Task UninstallMarkedPlugins() @@ -201,137 +163,54 @@ public sealed class PluginManager return relative; } - public async Task InstallPluginWithNoProgress(PluginOnlineInfo pluginData) + public async Task InstallPluginNoProgress(OnlinePlugin plugin) { - InstallingPluginInformation = new InstallingPluginInformation() { PluginName = pluginData.Name }; - - // Calculate the total number of steps: main file + dependencies - int totalSteps = pluginData.HasFileDependencies ? pluginData.Dependencies.Count + 1 : 1; - - // Each step contributes this percentage to the total progress + InstallingPluginInformation = new InstallingPluginInformation() { PluginName = plugin.PluginName }; + List dependencies = await _PluginRepository.GetDependenciesForPlugin(plugin.PluginId); + + int totalSteps = dependencies.Count + 1; float stepFraction = 100f / totalSteps; - - // Tracks the current cumulative progress in percentage float currentProgress = 0f; - + InstallingPluginInformation.IsInstalling = true; - - // Create a progress updater that maps the file's 0–100 progress to its portion of the total progress + var progress = currentProgress; IProgress downloadProgress = new Progress(fileProgress => { - // Map the file progress (0-100) to the total progress - InstallingPluginInformation.InstallationProgress = currentProgress + (fileProgress / 100f) * stepFraction; + InstallingPluginInformation.InstallationProgress = progress + (fileProgress / 100f) * stepFraction; }); - - // Download the main plugin file and map its progress - await ServerCom.DownloadFileAsync(pluginData.DownLoadLink, - $"{Application.CurrentApplication.ApplicationEnvironmentVariables.Get("PluginFolder")}/{pluginData.Name}.dll", - downloadProgress - ); - - // Update cumulative progress after the main file + + await ServerCom.DownloadFileAsync(plugin.PluginLink, + $"{Application.CurrentApplication.ApplicationEnvironmentVariables.Get("PluginFolder")}/{plugin.PluginName}.dll", + downloadProgress); + currentProgress += stepFraction; - // Download file dependencies if they exist - if (pluginData.HasFileDependencies) + if (dependencies.Count > 0) { - foreach (var dependency in pluginData.Dependencies) + foreach (var dependency in dependencies) { - string dependencyLocation = - GenerateDependencyRelativePath(pluginData.Name, dependency.DownloadLocation); - - // Download dependency and map its progress + string dependencyLocation = GenerateDependencyRelativePath(plugin.PluginName, dependency.DownloadLocation); await ServerCom.DownloadFileAsync(dependency.DownloadLink, dependencyLocation, downloadProgress); - - // Update cumulative progress after each dependency currentProgress += stepFraction; } } - - // Run script dependencies if any (doesn't affect download progress percentage) - if (pluginData.HasScriptDependencies) - { - foreach (var scriptDependency in pluginData.ScriptDependencies) - { - string? console = Application.CurrentApplication.ApplicationEnvironmentVariables.Get("console.terminal"); - if (string.IsNullOrEmpty(console)) - { - return; - } - - string? cmdPrefix = Application.CurrentApplication.ApplicationEnvironmentVariables.Get("console.cmd_prefix"); - if (string.IsNullOrEmpty(cmdPrefix)) - { - return; - } - - string arguments = $"{cmdPrefix}{scriptDependency.ScriptContent}"; - await ServerCom.RunConsoleCommand(console, arguments); - } - } - - // Register the plugin in the database - PluginInfo pluginInfo = PluginInfo.FromOnlineInfo(pluginData); - await AppendPluginToDatabase(pluginInfo); - - InstallingPluginInformation.IsInstalling = false; // Mark installation as complete - } - - - - - public async Task InstallPluginWithProgressBar(PluginOnlineInfo pluginData, IProgress? installProgress) - { - installProgress?.Report(0f); - - int totalSteps = pluginData.HasFileDependencies ? pluginData.Dependencies.Count + 1 : 1; - float stepProgress = 1f / totalSteps; - - float currentProgress = 0f; - - IProgress progress = new Progress((p) => + PluginInfo pluginInfo = PluginInfo.FromOnlineInfo(plugin, dependencies); + await AppendPluginToDatabase(pluginInfo); + InstallingPluginInformation.IsInstalling = false; + } + + public async Task, List>> GatherInstallDataForPlugin(OnlinePlugin plugin) + { + List dependencies = await _PluginRepository.GetDependenciesForPlugin(plugin.PluginId); + var downloads = new Dictionary { { $"{Application.CurrentApplication.ApplicationEnvironmentVariables.Get("PluginFolder")}/{plugin.PluginName}.dll", plugin.PluginLink } }; + foreach(var dependency in dependencies) { - installProgress?.Report(currentProgress + stepProgress * p); - }); - - await ServerCom.DownloadFileAsync(pluginData.DownLoadLink, $"{Application.CurrentApplication.ApplicationEnvironmentVariables.Get("PluginFolder")}/{pluginData.Name}.dll", progress); - - if (pluginData.HasFileDependencies) - foreach (var dependency in pluginData.Dependencies) - { - string dependencyLocation = GenerateDependencyRelativePath(pluginData.Name, dependency.DownloadLocation); - await ServerCom.DownloadFileAsync(dependency.DownloadLink, dependencyLocation, progress); - - currentProgress += stepProgress; - } - - if (pluginData.HasScriptDependencies) - { - foreach (var scriptDependency in pluginData.ScriptDependencies) - { - string? console = Application.CurrentApplication.ApplicationEnvironmentVariables.Get("console.terminal"); - if (string.IsNullOrEmpty(console)) - { - return; - } - - string? cmdPrefix = Application.CurrentApplication.ApplicationEnvironmentVariables.Get("console.cmd_prefix"); - if (string.IsNullOrEmpty(cmdPrefix)) - { - return; - } - - string arguments = $"{cmdPrefix}{scriptDependency.ScriptContent}"; - - await ServerCom.RunConsoleCommand(console, arguments); - } - + string dependencyLocation = GenerateDependencyRelativePath(plugin.PluginName, dependency.DownloadLocation); + downloads.Add(dependencyLocation, dependency.DownloadLink); } - PluginInfo pluginInfo = PluginInfo.FromOnlineInfo(pluginData); - - await AppendPluginToDatabase(pluginInfo); + return (downloads, dependencies).ToTuple(); } public async Task SetEnabledStatus(string pluginName, bool status) diff --git a/DiscordBotCore/Online/ServerCom.cs b/DiscordBotCore/Online/ServerCom.cs index 27ee343..5ab9fce 100644 --- a/DiscordBotCore/Online/ServerCom.cs +++ b/DiscordBotCore/Online/ServerCom.cs @@ -1,48 +1,17 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; -using System.Linq; + using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using DiscordBotCore.Online.Helpers; -using DiscordBotCore.Others; namespace DiscordBotCore.Online; public static class ServerCom { - /// - /// Read all lines from a file async - /// - /// The link of the file - /// - public static async Task> ReadTextFromURL(string link) - { - var response = await OnlineFunctions.DownloadStringAsync(link); - var lines = response.Split('\n'); - return lines.ToList(); - } - - /// - /// Get all text from a file async - /// - /// The link of the file - /// - public static async Task GetAllTextFromUrl(string link) - { - var response = await OnlineFunctions.DownloadStringAsync(link); - return response; - } - - /// - /// Download file from url - /// - /// The url to the file - /// The location where to store the downloaded data - /// The to track the download - /// - public static async Task DownloadFileAsync( + private static async Task DownloadFileAsync( string URL, string location, IProgress? progress, IProgress? downloadedBytes) { @@ -60,34 +29,8 @@ public static class ServerCom } } - public static async Task DownloadFileAsync(string URl, string location, IProgress progress) + public static async Task DownloadFileAsync(string url, string location, IProgress progress) { - await DownloadFileAsync(URl, location, progress, null); + await DownloadFileAsync(url, location, progress, null); } - - public static async Task DownloadFileAsync(string url, string location) - { - await DownloadFileAsync(url, location, null, null); - } - - public static Task CreateDownloadTask(string URl, string location) - { - return DownloadFileAsync(URl, location, null, null); - } - - public static Task CreateDownloadTask(string URl, string location, IProgress progress) - { - return DownloadFileAsync(URl, location, progress, null); - } - - public static async Task RunConsoleCommand(string console, string command) - { - Process process = new(); - process.StartInfo.FileName = console; - process.StartInfo.Arguments = command; - process.Start(); - await process.WaitForExitAsync(); - - } - } diff --git a/DiscordBotCore/Others/Enums.cs b/DiscordBotCore/Others/Enums.cs index 691eb1a..6942217 100644 --- a/DiscordBotCore/Others/Enums.cs +++ b/DiscordBotCore/Others/Enums.cs @@ -26,15 +26,6 @@ public enum InternalActionRunType OnStartupAndCall } -[Flags] -public enum OSType: byte -{ - NONE = 0, - WINDOWS = 1 << 0, - LINUX = 2 << 1, - MACOSX = 3 << 2 -} - public enum PluginType { UNKNOWN, diff --git a/DiscordBotCore/Others/JsonManager.cs b/DiscordBotCore/Others/JsonManager.cs index 335092c..46bdc10 100644 --- a/DiscordBotCore/Others/JsonManager.cs +++ b/DiscordBotCore/Others/JsonManager.cs @@ -93,7 +93,12 @@ public static class JsonManager text.Position = 0; - var obj = await JsonSerializer.DeserializeAsync(text); + JsonSerializerOptions options = new JsonSerializerOptions() + { + PropertyNameCaseInsensitive = true + }; + + var obj = await JsonSerializer.DeserializeAsync(text, options); await text.FlushAsync(); text.Close(); diff --git a/DiscordBotCore/Others/OS.cs b/DiscordBotCore/Others/OS.cs new file mode 100644 index 0000000..a344eb5 --- /dev/null +++ b/DiscordBotCore/Others/OS.cs @@ -0,0 +1,48 @@ +using System; + +namespace DiscordBotCore.Others; + +public class OS +{ + public enum OperatingSystem : int + { + Windows = 0, + Linux = 1, + MacOS = 2 + } + + public static OperatingSystem GetOperatingSystem() + { + if(System.OperatingSystem.IsLinux()) return OperatingSystem.Linux; + if(System.OperatingSystem.IsWindows()) return OperatingSystem.Windows; + if(System.OperatingSystem.IsMacOS()) return OperatingSystem.MacOS; + throw new PlatformNotSupportedException(); + } + + public static string GetOperatingSystemString(OperatingSystem os) + { + return os switch + { + OperatingSystem.Windows => "Windows", + OperatingSystem.Linux => "Linux", + OperatingSystem.MacOS => "MacOS", + _ => throw new ArgumentOutOfRangeException() + }; + } + + public static OperatingSystem GetOperatingSystemFromString(string os) + { + return os.ToLower() switch + { + "windows" => OperatingSystem.Windows, + "linux" => OperatingSystem.Linux, + "macos" => OperatingSystem.MacOS, + _ => throw new ArgumentOutOfRangeException() + }; + } + + public static int GetOperatingSystemInt() + { + return (int) GetOperatingSystem(); + } +} \ No newline at end of file diff --git a/DiscordBotCore/Plugin/OnlineDependencyInfo.cs b/DiscordBotCore/Plugin/OnlineDependencyInfo.cs index b1e678d..7055017 100644 --- a/DiscordBotCore/Plugin/OnlineDependencyInfo.cs +++ b/DiscordBotCore/Plugin/OnlineDependencyInfo.cs @@ -6,7 +6,9 @@ namespace DiscordBotCore.Plugin; public class OnlineDependencyInfo { public string DependencyName { get; private set; } + [JsonPropertyName("dependencyLink")] public string DownloadLink { get; private set; } + [JsonPropertyName("dependencyLocation")] public string DownloadLocation { get; private set; } public bool IsExecutable { get; private set; } diff --git a/DiscordBotCore/Plugin/OnlinePlugin.cs b/DiscordBotCore/Plugin/OnlinePlugin.cs new file mode 100644 index 0000000..e27c69f --- /dev/null +++ b/DiscordBotCore/Plugin/OnlinePlugin.cs @@ -0,0 +1,27 @@ +using System.Text.Json.Serialization; + +namespace DiscordBotCore.Plugin; + +public class OnlinePlugin +{ + public int PluginId { get; private set; } + public string PluginName { get; private set; } + public string PluginDescription { get; private set; } + public string LatestVersion { get; private set; } + public string PluginAuthor { get; private set; } + public string PluginLink { get; private set; } + public int OperatingSystem { get; private set; } + + [JsonConstructor] + public OnlinePlugin(int pluginId, string pluginName, string pluginDescription, string latestVersion, + string pluginAuthor, string pluginLink, int operatingSystem) + { + PluginId = pluginId; + PluginName = pluginName; + PluginDescription = pluginDescription; + LatestVersion = latestVersion; + PluginAuthor = pluginAuthor; + PluginLink = pluginLink; + OperatingSystem = operatingSystem; + } +} \ No newline at end of file diff --git a/DiscordBotCore/Plugin/OnlineScriptDependencyInfo.cs b/DiscordBotCore/Plugin/OnlineScriptDependencyInfo.cs deleted file mode 100644 index 5999876..0000000 --- a/DiscordBotCore/Plugin/OnlineScriptDependencyInfo.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace DiscordBotCore.Plugin -{ - public class OnlineScriptDependencyInfo - { - public string DependencyName { get; private set; } - public string ScriptContent { get; private set; } - - public OnlineScriptDependencyInfo(string dependencyName, string scriptContent) - { - DependencyName = dependencyName; - ScriptContent = scriptContent; - } - } -} diff --git a/DiscordBotCore/Plugin/PluginInfo.cs b/DiscordBotCore/Plugin/PluginInfo.cs index 07f49b7..65094e4 100644 --- a/DiscordBotCore/Plugin/PluginInfo.cs +++ b/DiscordBotCore/Plugin/PluginInfo.cs @@ -8,7 +8,7 @@ namespace DiscordBotCore.Plugin; public class PluginInfo { public string PluginName { get; private set; } - public PluginVersion PluginVersion { get; private set; } + public string PluginVersion { get; private set; } public string FilePath { get; private set; } public Dictionary ListOfExecutableDependencies {get; private set;} public bool IsMarkedToUninstall {get; internal set;} @@ -16,7 +16,7 @@ public class PluginInfo public bool IsEnabled { get; internal set; } [JsonConstructor] - public PluginInfo(string pluginName, PluginVersion pluginVersion, Dictionary listOfExecutableDependencies, bool isMarkedToUninstall, bool isOfflineAdded, bool isEnabled) + public PluginInfo(string pluginName, string pluginVersion, Dictionary listOfExecutableDependencies, bool isMarkedToUninstall, bool isOfflineAdded, bool isEnabled) { PluginName = pluginName; PluginVersion = pluginVersion; @@ -27,7 +27,7 @@ public class PluginInfo IsEnabled = isEnabled; } - public PluginInfo(string pluginName, PluginVersion pluginVersion, Dictionary listOfExecutableDependencies) + public PluginInfo(string pluginName, string pluginVersion, Dictionary listOfExecutableDependencies) { PluginName = pluginName; PluginVersion = pluginVersion; @@ -38,21 +38,14 @@ public class PluginInfo IsEnabled = true; } - public static PluginInfo FromOnlineInfo(PluginOnlineInfo onlineInfo) + public static PluginInfo FromOnlineInfo(OnlinePlugin plugin, List dependencies) { - var pluginName = onlineInfo.Name; - var version = onlineInfo.Version; - var dependencies= onlineInfo.Dependencies; - - if(dependencies is null) - { - return new PluginInfo(pluginName, version, new Dictionary()); - } - - var executableDependencies = dependencies.Where(dep => dep.IsExecutable); - var dictDependencies = executableDependencies.Select(dep => new KeyValuePair(dep.DependencyName, dep.DownloadLocation)).ToDictionary(); - - return new PluginInfo(pluginName, version, dictDependencies); + PluginInfo pluginInfo = new PluginInfo( + plugin.PluginName, plugin.LatestVersion, + dependencies.Where(dependency => dependency.IsExecutable) + .ToDictionary(dependency => dependency.DependencyName, dependency => dependency.DownloadLocation) + ); + return pluginInfo; } } diff --git a/DiscordBotCore/Plugin/PluginOnlineInfo.cs b/DiscordBotCore/Plugin/PluginOnlineInfo.cs deleted file mode 100644 index c9eefa4..0000000 --- a/DiscordBotCore/Plugin/PluginOnlineInfo.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; -using System.Threading.Tasks; -using DiscordBotCore.Online.Helpers; -using DiscordBotCore.Others; - -namespace DiscordBotCore.Plugin; - -public class PluginOnlineInfo -{ - public string Name { get; private set; } - public string Author { get; private set; } - public PluginVersion Version { get; private set; } - public string DownLoadLink { get; private set; } - public string Description { get; private set; } - public List? Dependencies { get; private set; } - public List? ScriptDependencies { get; private set; } - public OSType SupportedOS { get; private set; } - public bool HasFileDependencies => Dependencies is not null && Dependencies.Count > 0; - public bool HasScriptDependencies => ScriptDependencies is not null && ScriptDependencies.Count > 0; - - [JsonConstructor] - public PluginOnlineInfo(string name, string author, PluginVersion version, string description, string downLoadLink, OSType supportedOS, List dependencies, List scriptDependencies) - { - Name = name; - Author = author; - Version = version; - Description = description; - DownLoadLink = downLoadLink; - SupportedOS = supportedOS; - Dependencies = dependencies; - ScriptDependencies = scriptDependencies; - } - - public PluginOnlineInfo(string name, string author, PluginVersion version, string description, string downLoadLink, OSType supportedOS) - { - Name = name; - Author = author; - Version = version; - Description = description; - DownLoadLink = downLoadLink; - SupportedOS = supportedOS; - Dependencies = new List(); - ScriptDependencies = new List(); - } - - public static async Task FromRawData(string jsonText) - { - return await JsonManager.ConvertFromJson(jsonText); - } - - public override string ToString() - { - return $"{Name} <{Author}> - {Version} ({Description})"; - } -} diff --git a/DiscordBotCore/Repository/PluginRepository.cs b/DiscordBotCore/Repository/PluginRepository.cs deleted file mode 100644 index c7a9f1e..0000000 --- a/DiscordBotCore/Repository/PluginRepository.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Threading.Tasks; -using DiscordBotCore.Online; - -namespace DiscordBotCore.Repository; - -public sealed class PluginRepository : RepositoryBase -{ - public static readonly PluginRepository Default = new PluginRepository("Testing", "https://files.wizzy-server.ro/SethDiscordBot/PluginsRepo", "PluginsList.json"); - - private PluginRepository(string repositoryName, string repositoryUrl, string databaseFile) : base(repositoryName, repositoryUrl, databaseFile) - { - } - - private static PluginRepository From(string repositoryName, string repositoryUrl, string databaseFile) - { - return new PluginRepository(repositoryName, repositoryUrl, databaseFile); - } - - public async Task JsonGetAllPlugins() - { - var jsonResponse = await ServerCom.GetAllTextFromUrl(DatabasePath); - - return jsonResponse; - } - - internal static PluginRepository SolveRepo() - { - if (!Application.CurrentApplication.ApplicationEnvironmentVariables.ContainsKey("PluginRepository")) - { - return Default; - } - - try - { - var pluginRepoDict = Application.CurrentApplication.ApplicationEnvironmentVariables.GetDictionary("PluginRepository"); - var pluginRepo = PluginRepository.From( - pluginRepoDict["Name"], - pluginRepoDict["Url"], - pluginRepoDict["DatabaseFile"] - ); - return pluginRepo; - } - catch(Exception ex) - { - Application.CurrentApplication.Logger.LogException(ex, Application.CurrentApplication); - - return Default; - } - } -} diff --git a/DiscordBotCore/Repository/RepositoryBase.cs b/DiscordBotCore/Repository/RepositoryBase.cs deleted file mode 100644 index 6890192..0000000 --- a/DiscordBotCore/Repository/RepositoryBase.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace DiscordBotCore.Repository; - -public abstract class RepositoryBase -{ -/* - Example of JSON for config file : - Please mind that the URL should not end with a slash. ! - - "PluginRepository": { - "Name": "Default Plugins Repository", - "Url": "http://blahblahblah.blah/MyCustomRepo", - "DatabaseFile": "PluginsList.json" - }, - "ModuleRepository": { - "Name": "Default Modules Repository", - "Url": "http://blahblahblah.blah/MyCustomRepo", - "DatabaseFile": "modules.json" - } - */ - public string RepositoryName { get; init; } - private string RepositoryUrl { get; init; } - private string DatabaseFile { get; init; } - - protected string DatabasePath => $"{RepositoryUrl}/{DatabaseFile}"; - - protected RepositoryBase(string repositoryName, string repositoryUrl, string databaseFile) - { - RepositoryName = repositoryName; - RepositoryUrl = repositoryUrl; - DatabaseFile = databaseFile; - } - -} diff --git a/DiscordBotCore/Updater/Application/AppUpdater.cs b/DiscordBotCore/Updater/Application/AppUpdater.cs deleted file mode 100644 index 29f2d2a..0000000 --- a/DiscordBotCore/Updater/Application/AppUpdater.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Net.Http; -using System.Threading.Tasks; -using DiscordBotCore.Interfaces.Updater; -using DiscordBotCore.Online; -using DiscordBotCore.Others; - -namespace DiscordBotCore.Updater.Application -{ - public class AppUpdater - { - - const string ProjectName = "SethDiscordBot"; - - private static readonly string _DefaultUpdateUrl = $"https://github.com/andreitdr/{ProjectName}/releases/latest"; - private static readonly string _DefaultUpdateDownloadUrl = $"https://github.com/andreitdr/{ProjectName}/releases/download/v"; - - private static readonly string _WindowsUpdateFile = "win-x64.zip"; - private static readonly string _LinuxUpdateFile = "linux-x64.zip"; - private static readonly string _MacOSUpdateFile = "osx-x64.zip"; - - private static readonly string _TempUpdateFolder = "temp"; - - private async Task GetOnlineVersion() - { - HttpClient client = new HttpClient(); - var response = await client.GetAsync(_DefaultUpdateUrl); - - if (!response.IsSuccessStatusCode) - return AppVersion.CurrentAppVersion; - - var url = response.RequestMessage.RequestUri.ToString(); - var version = url.Split('/')[^1].Substring(1); // Remove the 'v' from the version number - - return new AppVersion(version); - } - - private string GetDownloadUrl(AppVersion version) - { - string downloadUrl = _DefaultUpdateDownloadUrl; - - downloadUrl += $"{version.ToShortString()}/"; - - if(OperatingSystem.IsWindows()) - downloadUrl += _WindowsUpdateFile; - else if (OperatingSystem.IsLinux()) - downloadUrl += _LinuxUpdateFile; - else if (OperatingSystem.IsMacOS()) - downloadUrl += _MacOSUpdateFile; - else - throw new PlatformNotSupportedException("Unsupported operating system"); - - return downloadUrl; - } - - public async Task PrepareUpdate() - { - AppVersion currentVersion = AppVersion.CurrentAppVersion; - AppVersion newVersion = await GetOnlineVersion(); - - if(!newVersion.IsNewerThan(currentVersion)) - return null; - - string downloadUrl = GetDownloadUrl(newVersion); - Update update = new Update(currentVersion, newVersion, downloadUrl); - - return update; - } - - private void PrepareCurrentFolderForOverwrite() - { - List files = new List(); - files.AddRange(Directory.GetFiles("./")); - - foreach (var file in files) - { - File.Move(file, file + ".bak"); - } - } - - private async Task CopyFolderContentOverCurrentFolder(string current, string otherFolder) - { - var files = Directory.GetFiles(otherFolder, "*.*", SearchOption.AllDirectories); - foreach (var file in files) - { - string relativePath = file.Replace(otherFolder, ""); - string newPath = current + relativePath; - Directory.CreateDirectory(Path.GetDirectoryName(newPath)); - File.Copy(file, newPath); - } - } - - public async Task SelfUpdate(Update update, IProgress progress) - { - Directory.CreateDirectory(_TempUpdateFolder); - - string tempFile = $"./{_TempUpdateFolder}/update.zip"; - string tempFolder = $"./{_TempUpdateFolder}/"; - - Directory.CreateDirectory(tempFolder); - - await ServerCom.DownloadFileAsync(update.UpdateUrl, tempFile, progress); - - await ArchiveManager.ExtractArchive(tempFile, tempFolder, progress, UnzipProgressType.PERCENTAGE_FROM_TOTAL_SIZE); - - PrepareCurrentFolderForOverwrite(); - - if (OperatingSystem.IsWindows()) - tempFolder += _WindowsUpdateFile; - else if (OperatingSystem.IsLinux()) - tempFolder += _LinuxUpdateFile; - else if (OperatingSystem.IsMacOS()) - tempFolder += _MacOSUpdateFile; - else throw new PlatformNotSupportedException(); - - await CopyFolderContentOverCurrentFolder("./", tempFolder.Substring(0, tempFolder.Length-4)); // Remove the .zip from the folder name - - Process.Start("DiscordBot", "--update-cleanup"); - Environment.Exit(0); - - } - - } -} diff --git a/DiscordBotCore/Updater/Application/Update.cs b/DiscordBotCore/Updater/Application/Update.cs deleted file mode 100644 index 758c419..0000000 --- a/DiscordBotCore/Updater/Application/Update.cs +++ /dev/null @@ -1,19 +0,0 @@ - -using DiscordBotCore.Interfaces.Updater; - -namespace DiscordBotCore.Updater.Application -{ - public class Update - { - public AppVersion NewVersion { get; private set; } - public AppVersion CurrentVersion { get; private set; } - public string UpdateUrl { get; private set; } - - public Update(AppVersion currentVersion, AppVersion updateVersion, string updateUrl) - { - NewVersion = updateVersion; - CurrentVersion = currentVersion; - UpdateUrl = updateUrl; - } - } -} diff --git a/DiscordBotCore/Updater/Plugins/PluginUpdater.cs b/DiscordBotCore/Updater/Plugins/PluginUpdater.cs deleted file mode 100644 index 878e8ba..0000000 --- a/DiscordBotCore/Updater/Plugins/PluginUpdater.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using DiscordBotCore.Online; -using DiscordBotCore.Others; -using DiscordBotCore.Others.Exceptions; -using DiscordBotCore.Plugin; - -namespace DiscordBotCore.Updater.Plugins; - -public class PluginUpdater -{ - private readonly PluginManager _PluginsManager; - - public PluginUpdater(PluginManager pluginManager) - { - _PluginsManager = pluginManager; - } - - private async Task GetPluginInfo(string pluginName) - { - var result = await _PluginsManager.GetPluginDataByName(pluginName); - if(result is null) - throw new PluginNotFoundException(pluginName); - return result; - } - - private async Task GetLocalPluginInfo(string pluginName) - { - string pluginsDatabase = File.ReadAllText(DiscordBotCore.Application.CurrentApplication.PluginDatabase); - List installedPlugins = await JsonManager.ConvertFromJson>(pluginsDatabase); - - var result = installedPlugins.Find(p => p.PluginName == pluginName); - if (result is null) - throw new PluginNotFoundException(pluginName); - return result; - } - - public async Task UpdatePlugin(string pluginName, IProgress? progressMeter = null) - { - PluginOnlineInfo pluginInfo = await GetPluginInfo(pluginName); - await ServerCom.DownloadFileAsync(pluginInfo.DownLoadLink, $"{DiscordBotCore.Application.CurrentApplication.ApplicationEnvironmentVariables.Get("PluginFolder")}/{pluginName}.dll", progressMeter); - - if(pluginInfo.Dependencies is not null) - foreach(OnlineDependencyInfo dependency in pluginInfo.Dependencies) - await ServerCom.DownloadFileAsync(dependency.DownloadLink, dependency.DownloadLocation, progressMeter); - - await _PluginsManager.RemovePluginFromDatabase(pluginName); - await _PluginsManager.AppendPluginToDatabase(PluginInfo.FromOnlineInfo(pluginInfo)); - } - - public async Task HasUpdate(string pluginName) - { - var localPluginInfo = await GetLocalPluginInfo(pluginName); - if(localPluginInfo.IsOfflineAdded) - return false; - - var pluginInfo = await GetPluginInfo(pluginName); - - return pluginInfo.Version.IsNewerThan(localPluginInfo.PluginVersion); - - } - -}