Updated API for plugins to work with database from remote. Added PluginRepository and removed Installation Scripts
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<float> progress = new Progress<float>(p => { downloadTask.Value = p; });
|
||||
|
||||
string baseFolder = Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("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<Tuple<ProgressTask, IProgress<float>, 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<float> progress = new Progress<float>(p =>
|
||||
{
|
||||
task.Value = p;
|
||||
}
|
||||
);
|
||||
|
||||
task.IsIndeterminate = true;
|
||||
downloadTasks.Add(new Tuple<ProgressTask, IProgress<float>, 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<Tuple<string, string>> downloadList = result.Item1.Select(kvp => new Tuple<string, string>(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<bool> LoadPlugins(string[] args)
|
||||
{
|
||||
|
||||
@@ -118,7 +118,7 @@ public class Plugin: ICommandAction
|
||||
}
|
||||
}
|
||||
|
||||
await PluginMethods.DownloadPlugin(pluginName);
|
||||
await PluginMethods.DownloadPluginWithParallelDownloads(pluginName);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>disable</Nullable>
|
||||
<Nullable>enable</Nullable>
|
||||
<ApplicationIcon />
|
||||
<StartupObject />
|
||||
<SignAssembly>False</SignAssembly>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<HttpClient, string, string, IProgress<float>, Task> method, HttpClient client,
|
||||
List<Tuple<string, string>> parameters, string taskMessage)
|
||||
{
|
||||
await AnsiConsole.Progress().AutoClear(false).HideCompleted(false)
|
||||
.Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn())
|
||||
.StartAsync(async ctx =>
|
||||
{
|
||||
var tasks = new List<Task>();
|
||||
foreach (var (location, url) in parameters)
|
||||
{
|
||||
var task = ctx.AddTask(taskMessage + " " + url);
|
||||
IProgress<float> progress = new Progress<float>(x => task.Value = x * 100);
|
||||
tasks.Add(method(client, url, location, progress));
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,14 @@ public class PluginInstallEndpoint : IEndpoint
|
||||
Dictionary<string, string> jsonDict = await JsonManager.ConvertFromJson<Dictionary<string, string>>(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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using DiscordBotCore.Plugin;
|
||||
|
||||
namespace DiscordBotCore.Interfaces.PluginManagement;
|
||||
|
||||
public interface IPluginRepository
|
||||
{
|
||||
public Task<List<OnlinePlugin>> GetAllPlugins();
|
||||
|
||||
public Task<OnlinePlugin?> GetPluginById(int pluginId);
|
||||
public Task<OnlinePlugin?> GetPluginByName(string pluginName);
|
||||
|
||||
public Task<List<OnlineDependencyInfo>> GetDependenciesForPlugin(int pluginId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace DiscordBotCore.Interfaces.PluginManagement;
|
||||
|
||||
public interface IPluginRepositoryConfiguration
|
||||
{
|
||||
public string BaseUrl { get; }
|
||||
|
||||
public string PluginRepositoryLocation { get; }
|
||||
public string DependenciesRepositoryLocation { get; }
|
||||
|
||||
|
||||
}
|
||||
@@ -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<OnlineScriptDependencyInfo> listOfDependencies);
|
||||
string GenerateDependencyRelativePath(string pluginName, string dependencyPath);
|
||||
Task<string?> GetDependencyLocation(string dependencyName);
|
||||
Task<string?> GetDependencyLocation(string pluginName, string dependencyName);
|
||||
Task<List<PluginInfo>> GetInstalledPlugins();
|
||||
Task<PluginOnlineInfo?> GetPluginDataByName(string pluginName);
|
||||
Task<List<PluginOnlineInfo>?> GetPluginsList();
|
||||
Task InstallPlugin(PluginOnlineInfo pluginData, IProgress<float>? installProgress);
|
||||
Task<bool> IsPluginInstalled(string pluginName);
|
||||
Task<bool> MarkPluginToUninstall(string pluginName);
|
||||
Task RemovePluginFromDatabase(string pluginName);
|
||||
Task UninstallMarkedPlugins();
|
||||
Task SetEnabledStatus(string pluginName, bool status);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
94
DiscordBotCore/Online/FileDownloader.cs
Normal file
94
DiscordBotCore/Online/FileDownloader.cs
Normal file
@@ -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<Task> 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<Task>();
|
||||
|
||||
IsParallelDownloader = isParallelDownloader;
|
||||
}
|
||||
|
||||
public FileDownloader(bool isParallelDownloader, Action finishAction)
|
||||
{
|
||||
_httpClient = new HttpClient();
|
||||
ListOfDownloadTasks = new List<Task>();
|
||||
|
||||
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<float> progress)
|
||||
{
|
||||
ListOfDownloadTasks.Add(CreateDownloadTask(_httpClient, downloadLink, downloadLocation, progress));
|
||||
}
|
||||
|
||||
public void AppendDownloadTask(string downloadLink, string downloadLocation, Action<float> progressCallback)
|
||||
{
|
||||
ListOfDownloadTasks.Add(CreateDownloadTask(_httpClient, downloadLink, downloadLocation, new Progress<float>(progressCallback)));
|
||||
}
|
||||
|
||||
public static async Task CreateDownloadTask(HttpClient client, string url, string targetPath, IProgress<float> 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);
|
||||
}
|
||||
}
|
||||
@@ -107,21 +107,4 @@ internal static class OnlineFunctions
|
||||
using var client = new HttpClient();
|
||||
return await client.GetStringAsync(url, cancellation);
|
||||
}
|
||||
|
||||
internal static async Task<bool> IsInternetConnected()
|
||||
{
|
||||
bool result = false;
|
||||
try
|
||||
{
|
||||
using var client = new HttpClient();
|
||||
await client.GetStringAsync("files.wizzy-server.ro");
|
||||
result = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
89
DiscordBotCore/Online/Helpers/PluginRepository.cs
Normal file
89
DiscordBotCore/Online/Helpers/PluginRepository.cs
Normal file
@@ -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<List<OnlinePlugin>> 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<OnlinePlugin> plugins = await JsonManager.ConvertFromJson<List<OnlinePlugin>>(content);
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
||||
public async Task<OnlinePlugin?> 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<OnlinePlugin>(content);
|
||||
|
||||
return plugin;
|
||||
}
|
||||
|
||||
public async Task<OnlinePlugin?> 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<OnlinePlugin>(content);
|
||||
|
||||
return plugin;
|
||||
}
|
||||
|
||||
public async Task<List<OnlineDependencyInfo>> 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<OnlineDependencyInfo> dependencies = await JsonManager.ConvertFromJson<List<OnlineDependencyInfo>>(content);
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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<List<PluginOnlineInfo>> GetPluginsList()
|
||||
public async Task<List<OnlinePlugin>> GetPluginsList()
|
||||
{
|
||||
var jsonText = await _PluginRepository.JsonGetAllPlugins();
|
||||
List<PluginOnlineInfo> result = await JsonManager.ConvertFromJson<List<PluginOnlineInfo>>(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<PluginOnlineInfo?> GetPluginDataByName(string pluginName)
|
||||
{
|
||||
List<PluginOnlineInfo>? 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<OnlinePlugin?> 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<PluginInfo> installedPlugins = await JsonManager.ConvertFromJson<List<PluginInfo>>(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<OnlineScriptDependencyInfo> listOfDependencies)
|
||||
{
|
||||
string? console = Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("console.terminal");
|
||||
if (string.IsNullOrEmpty(console))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string? cmd_prefix = Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("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<PluginInfo> installedPlugins = await JsonManager.ConvertFromJson<List<PluginInfo>>(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<PluginInfo> 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<bool> MarkPluginToUninstall(string pluginName)
|
||||
{
|
||||
IEnumerable<PluginInfo> installedPlugins = await GetInstalledPlugins();
|
||||
IEnumerable<PluginInfo> info = installedPlugins.Where(info => info.PluginName == pluginName).AsEnumerable();
|
||||
List<PluginInfo> installedPlugins = await GetInstalledPlugins();
|
||||
List<PluginInfo> 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<OnlineDependencyInfo> 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<float> downloadProgress = new Progress<float>(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<string>("PluginFolder")}/{pluginData.Name}.dll",
|
||||
downloadProgress
|
||||
);
|
||||
|
||||
// Update cumulative progress after the main file
|
||||
|
||||
await ServerCom.DownloadFileAsync(plugin.PluginLink,
|
||||
$"{Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("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<string>("console.terminal");
|
||||
if (string.IsNullOrEmpty(console))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string? cmdPrefix = Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("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<float>? installProgress)
|
||||
{
|
||||
installProgress?.Report(0f);
|
||||
|
||||
int totalSteps = pluginData.HasFileDependencies ? pluginData.Dependencies.Count + 1 : 1;
|
||||
|
||||
float stepProgress = 1f / totalSteps;
|
||||
|
||||
float currentProgress = 0f;
|
||||
|
||||
IProgress<float> progress = new Progress<float>((p) =>
|
||||
PluginInfo pluginInfo = PluginInfo.FromOnlineInfo(plugin, dependencies);
|
||||
await AppendPluginToDatabase(pluginInfo);
|
||||
InstallingPluginInformation.IsInstalling = false;
|
||||
}
|
||||
|
||||
public async Task<Tuple<Dictionary<string, string>, List<OnlineDependencyInfo>>> GatherInstallDataForPlugin(OnlinePlugin plugin)
|
||||
{
|
||||
List<OnlineDependencyInfo> dependencies = await _PluginRepository.GetDependenciesForPlugin(plugin.PluginId);
|
||||
var downloads = new Dictionary<string, string> { { $"{Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("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<string>("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<string>("console.terminal");
|
||||
if (string.IsNullOrEmpty(console))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string? cmdPrefix = Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("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)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Read all lines from a file async
|
||||
/// </summary>
|
||||
/// <param name="link">The link of the file</param>
|
||||
/// <returns></returns>
|
||||
public static async Task<List<string>> ReadTextFromURL(string link)
|
||||
{
|
||||
var response = await OnlineFunctions.DownloadStringAsync(link);
|
||||
var lines = response.Split('\n');
|
||||
return lines.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all text from a file async
|
||||
/// </summary>
|
||||
/// <param name="link">The link of the file</param>
|
||||
/// <returns></returns>
|
||||
public static async Task<string> GetAllTextFromUrl(string link)
|
||||
{
|
||||
var response = await OnlineFunctions.DownloadStringAsync(link);
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download file from url
|
||||
/// </summary>
|
||||
/// <param name="URL">The url to the file</param>
|
||||
/// <param name="location">The location where to store the downloaded data</param>
|
||||
/// <param name="progress">The <see cref="IProgress{T}" /> to track the download</param>
|
||||
/// <returns></returns>
|
||||
public static async Task DownloadFileAsync(
|
||||
private static async Task DownloadFileAsync(
|
||||
string URL, string location, IProgress<float>? progress,
|
||||
IProgress<long>? downloadedBytes)
|
||||
{
|
||||
@@ -60,34 +29,8 @@ public static class ServerCom
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task DownloadFileAsync(string URl, string location, IProgress<float> progress)
|
||||
public static async Task DownloadFileAsync(string url, string location, IProgress<float> 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<float> 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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -93,7 +93,12 @@ public static class JsonManager
|
||||
|
||||
text.Position = 0;
|
||||
|
||||
var obj = await JsonSerializer.DeserializeAsync<T>(text);
|
||||
JsonSerializerOptions options = new JsonSerializerOptions()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
|
||||
var obj = await JsonSerializer.DeserializeAsync<T>(text, options);
|
||||
await text.FlushAsync();
|
||||
text.Close();
|
||||
|
||||
|
||||
48
DiscordBotCore/Others/OS.cs
Normal file
48
DiscordBotCore/Others/OS.cs
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
|
||||
|
||||
27
DiscordBotCore/Plugin/OnlinePlugin.cs
Normal file
27
DiscordBotCore/Plugin/OnlinePlugin.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<string, string> 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<string, string> listOfExecutableDependencies, bool isMarkedToUninstall, bool isOfflineAdded, bool isEnabled)
|
||||
public PluginInfo(string pluginName, string pluginVersion, Dictionary<string, string> 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<string, string> listOfExecutableDependencies)
|
||||
public PluginInfo(string pluginName, string pluginVersion, Dictionary<string, string> 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<OnlineDependencyInfo> dependencies)
|
||||
{
|
||||
var pluginName = onlineInfo.Name;
|
||||
var version = onlineInfo.Version;
|
||||
var dependencies= onlineInfo.Dependencies;
|
||||
|
||||
if(dependencies is null)
|
||||
{
|
||||
return new PluginInfo(pluginName, version, new Dictionary<string, string>());
|
||||
}
|
||||
|
||||
var executableDependencies = dependencies.Where(dep => dep.IsExecutable);
|
||||
var dictDependencies = executableDependencies.Select(dep => new KeyValuePair<string, string>(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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<OnlineDependencyInfo>? Dependencies { get; private set; }
|
||||
public List<OnlineScriptDependencyInfo>? 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<OnlineDependencyInfo> dependencies, List<OnlineScriptDependencyInfo> 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<OnlineDependencyInfo>();
|
||||
ScriptDependencies = new List<OnlineScriptDependencyInfo>();
|
||||
}
|
||||
|
||||
public static async Task<PluginOnlineInfo> FromRawData(string jsonText)
|
||||
{
|
||||
return await JsonManager.ConvertFromJson<PluginOnlineInfo>(jsonText);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Name} <{Author}> - {Version} ({Description})";
|
||||
}
|
||||
}
|
||||
@@ -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<string> 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<string, string>("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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<AppVersion> 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<Update?> 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<string> files = new List<string>();
|
||||
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<float> 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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<PluginOnlineInfo> GetPluginInfo(string pluginName)
|
||||
{
|
||||
var result = await _PluginsManager.GetPluginDataByName(pluginName);
|
||||
if(result is null)
|
||||
throw new PluginNotFoundException(pluginName);
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<PluginInfo> GetLocalPluginInfo(string pluginName)
|
||||
{
|
||||
string pluginsDatabase = File.ReadAllText(DiscordBotCore.Application.CurrentApplication.PluginDatabase);
|
||||
List<PluginInfo> installedPlugins = await JsonManager.ConvertFromJson<List<PluginInfo>>(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<float>? progressMeter = null)
|
||||
{
|
||||
PluginOnlineInfo pluginInfo = await GetPluginInfo(pluginName);
|
||||
await ServerCom.DownloadFileAsync(pluginInfo.DownLoadLink, $"{DiscordBotCore.Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("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<bool> HasUpdate(string pluginName)
|
||||
{
|
||||
var localPluginInfo = await GetLocalPluginInfo(pluginName);
|
||||
if(localPluginInfo.IsOfflineAdded)
|
||||
return false;
|
||||
|
||||
var pluginInfo = await GetPluginInfo(pluginName);
|
||||
|
||||
return pluginInfo.Version.IsNewerThan(localPluginInfo.PluginVersion);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user