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);
|
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]);
|
Application.CurrentApplication.Logger.Log("Adding plugin: " + args[^1]);
|
||||||
await Application.CurrentApplication.PluginManager.AppendPluginToDatabase(pluginInfo);
|
await Application.CurrentApplication.PluginManager.AppendPluginToDatabase(pluginInfo);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using DiscordBot.Utilities;
|
using DiscordBot.Utilities;
|
||||||
@@ -22,51 +23,20 @@ internal static class PluginMethods
|
|||||||
{
|
{
|
||||||
Console.WriteLine($"Fetching plugin list ...");
|
Console.WriteLine($"Fetching plugin list ...");
|
||||||
|
|
||||||
var data = await ConsoleUtilities.ExecuteWithProgressBar(Application.CurrentApplication.PluginManager.GetPluginsList(), "Reading remote database");
|
var onlinePlugins = await ConsoleUtilities.ExecuteWithProgressBar(Application.CurrentApplication.PluginManager.GetPluginsList(), "Reading remote database");
|
||||||
|
|
||||||
TableData tableData = new(["Name", "Description", "Version", "Installed", "Dependencies", "Enabled"]);
|
|
||||||
|
|
||||||
var installedPlugins = await ConsoleUtilities.ExecuteWithProgressBar(Application.CurrentApplication.PluginManager.GetInstalledPlugins(), "Reading local 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);
|
bool isInstalled = installedPlugins.Any(p => p.PluginName == onlinePlugin.PluginName);
|
||||||
|
tableData.AddRow([
|
||||||
if (!plugin.HasFileDependencies)
|
onlinePlugin.PluginName,
|
||||||
{
|
onlinePlugin.PluginDescription,
|
||||||
tableData.AddRow([plugin.Name, plugin.Description,
|
onlinePlugin.PluginAuthor,
|
||||||
plugin.Version.ToString(), isInstalled ? "[green]Yes[/]" : "[red]No[/]", "None",
|
onlinePlugin.LatestVersion,
|
||||||
isInstalled ? installedPlugins.First(p=>p.PluginName == plugin.Name).IsEnabled ? "[green]Enabled[/]" : "[red]Disabled[/]" : "[yellow]NOT INSTALLED[/]"]);
|
isInstalled ? installedPlugins.First(p => p.PluginName == onlinePlugin.PluginName).PluginVersion : "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[/]"]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tableData.HasRoundBorders = false;
|
tableData.HasRoundBorders = false;
|
||||||
@@ -100,114 +70,25 @@ internal static class PluginMethods
|
|||||||
await Application.CurrentApplication.PluginManager.SetEnabledStatus(pluginName, true);
|
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)
|
if (pluginData is null)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Plugin {pluginName} not found. Please check the spelling and try again.");
|
Console.WriteLine($"Plugin {pluginName} not found. Please check the spelling and try again.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// rename the plugin to the name of the plugin
|
var result = await Application.CurrentApplication.PluginManager.GatherInstallDataForPlugin(pluginData);
|
||||||
pluginName = pluginData.Name;
|
List<Tuple<string, string>> downloadList = result.Item1.Select(kvp => new Tuple<string, string>(kvp.Key, kvp.Value)).ToList();
|
||||||
|
|
||||||
var pluginLink = pluginData.DownLoadLink;
|
await ConsoleUtilities.ExecuteParallelDownload(FileDownloader.CreateDownloadTask, new HttpClient(), downloadList, "Downloading:");
|
||||||
|
|
||||||
|
await Application.CurrentApplication.PluginManager.AppendPluginToDatabase(PluginInfo.FromOnlineInfo(pluginData, result.Item2));
|
||||||
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);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal static async Task<bool> LoadPlugins(string[] args)
|
internal static async Task<bool> LoadPlugins(string[] args)
|
||||||
{
|
{
|
||||||
var loader = new PluginLoader(Application.CurrentApplication.DiscordBotClient.Client);
|
var loader = new PluginLoader(Application.CurrentApplication.DiscordBotClient.Client);
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ public class Plugin: ICommandAction
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await PluginMethods.DownloadPlugin(pluginName);
|
await PluginMethods.DownloadPluginWithParallelDownloads(pluginName);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Nullable>disable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ApplicationIcon />
|
<ApplicationIcon />
|
||||||
<StartupObject />
|
<StartupObject />
|
||||||
<SignAssembly>False</SignAssembly>
|
<SignAssembly>False</SignAssembly>
|
||||||
|
|||||||
@@ -4,11 +4,9 @@ using System.Reflection;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using DiscordBot.Bot.Actions.Extra;
|
using DiscordBot.Bot.Actions.Extra;
|
||||||
using DiscordBot.Utilities;
|
|
||||||
using DiscordBotCore;
|
using DiscordBotCore;
|
||||||
using DiscordBotCore.Bot;
|
using DiscordBotCore.Bot;
|
||||||
using DiscordBotCore.Others;
|
using DiscordBotCore.Others;
|
||||||
using DiscordBotCore.Updater.Application;
|
|
||||||
|
|
||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
|
|
||||||
@@ -81,14 +79,6 @@ public class Program
|
|||||||
{
|
{
|
||||||
await Application.CreateApplication();
|
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)
|
void LogMessageFunction(string message, LogType logType)
|
||||||
{
|
{
|
||||||
string messageAsString = message;
|
string messageAsString = message;
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using DiscordBotCore.Updater.Application;
|
using DiscordBotCore.Online;
|
||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
|
|
||||||
namespace DiscordBot.Utilities;
|
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);
|
Dictionary<string, string> jsonDict = await JsonManager.ConvertFromJson<Dictionary<string, string>>(jsonRequest);
|
||||||
string pluginName = jsonDict["pluginName"];
|
string pluginName = jsonDict["pluginName"];
|
||||||
|
|
||||||
PluginOnlineInfo? pluginInfo = await Application.CurrentApplication.PluginManager.GetPluginDataByName(pluginName);
|
OnlinePlugin? pluginInfo = await Application.CurrentApplication.PluginManager.GetPluginDataByName(pluginName);
|
||||||
|
|
||||||
if (pluginInfo == null)
|
if (pluginInfo == null)
|
||||||
{
|
{
|
||||||
return ApiResponse.Fail("Plugin not found.");
|
return ApiResponse.Fail("Plugin not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Application.CurrentApplication.PluginManager.InstallPluginWithNoProgress(pluginInfo);
|
Application.CurrentApplication.PluginManager.InstallPluginNoProgress(pluginInfo);
|
||||||
return ApiResponse.Ok();
|
return ApiResponse.Ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ public class PluginListEndpoint : IEndpoint
|
|||||||
var onlineInfos = await Application.CurrentApplication.PluginManager.GetPluginsList();
|
var onlineInfos = await Application.CurrentApplication.PluginManager.GetPluginsList();
|
||||||
|
|
||||||
var response = await JsonManager.ConvertToJson(onlineInfos, [
|
var response = await JsonManager.ConvertToJson(onlineInfos, [
|
||||||
nameof(PluginOnlineInfo.Name),
|
nameof(OnlinePlugin.PluginName),
|
||||||
nameof(PluginOnlineInfo.Author),
|
nameof(OnlinePlugin.PluginAuthor),
|
||||||
nameof(PluginOnlineInfo.Description)
|
nameof(OnlinePlugin.PluginDescription)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return ApiResponse.From(response, true);
|
return ApiResponse.From(response, true);
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ using DiscordBotCore.Others.Actions;
|
|||||||
using DiscordBotCore.Others.Settings;
|
using DiscordBotCore.Others.Settings;
|
||||||
using DiscordBotCore.Plugin;
|
using DiscordBotCore.Plugin;
|
||||||
using DiscordBotCore.Logging;
|
using DiscordBotCore.Logging;
|
||||||
using DiscordBotCore.Repository;
|
|
||||||
|
|
||||||
namespace DiscordBotCore
|
namespace DiscordBotCore
|
||||||
{
|
{
|
||||||
@@ -55,12 +54,6 @@ namespace DiscordBotCore
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task CreateApplication()
|
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)
|
if (CurrentApplication is not null)
|
||||||
{
|
{
|
||||||
CurrentApplication.Logger.Log("Application is already initialized. Reinitialization is not allowed", LogType.Error);
|
CurrentApplication.Logger.Log("Application is already initialized. Reinitialization is not allowed", LogType.Error);
|
||||||
@@ -87,32 +80,13 @@ namespace DiscordBotCore
|
|||||||
await JsonManager.SaveToJsonFile(_PluginsDatabaseFile, plugins);
|
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.UninstallMarkedPlugins();
|
||||||
await CurrentApplication.PluginManager.CheckForUpdates();
|
|
||||||
|
|
||||||
CurrentApplication.InternalActionManager = new InternalActionManager();
|
CurrentApplication.InternalActionManager = new InternalActionManager();
|
||||||
await CurrentApplication.InternalActionManager.Initialize();
|
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;
|
IsRunning = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +139,7 @@ namespace DiscordBotCore
|
|||||||
return result;
|
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);
|
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();
|
using var client = new HttpClient();
|
||||||
return await client.GetStringAsync(url, cancellation);
|
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.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using DiscordBotCore.Interfaces.PluginManagement;
|
||||||
using DiscordBotCore.Others;
|
using DiscordBotCore.Others;
|
||||||
using DiscordBotCore.Plugin;
|
using DiscordBotCore.Plugin;
|
||||||
using DiscordBotCore.Repository;
|
|
||||||
using DiscordBotCore.Updater.Plugins;
|
|
||||||
|
|
||||||
namespace DiscordBotCore.Online;
|
namespace DiscordBotCore.Online;
|
||||||
|
|
||||||
public sealed class PluginManager
|
public sealed class PluginManager
|
||||||
{
|
{
|
||||||
private static readonly string _LibrariesBaseFolder = "Libraries";
|
private static readonly string _LibrariesBaseFolder = "Libraries";
|
||||||
private readonly PluginRepository _PluginRepository;
|
private readonly IPluginRepository _PluginRepository;
|
||||||
internal InstallingPluginInformation? InstallingPluginInformation { get; private set; }
|
internal InstallingPluginInformation? InstallingPluginInformation { get; private set; }
|
||||||
|
|
||||||
public PluginManager(PluginRepository pluginRepository)
|
internal PluginManager(IPluginRepository pluginRepository)
|
||||||
{
|
{
|
||||||
_PluginRepository = pluginRepository;
|
_PluginRepository = pluginRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<PluginOnlineInfo>> GetPluginsList()
|
public async Task<List<OnlinePlugin>> GetPluginsList()
|
||||||
{
|
{
|
||||||
var jsonText = await _PluginRepository.JsonGetAllPlugins();
|
var onlinePlugins = await _PluginRepository.GetAllPlugins();
|
||||||
List<PluginOnlineInfo> result = await JsonManager.ConvertFromJson<List<PluginOnlineInfo>>(jsonText);
|
|
||||||
|
|
||||||
var currentOs = OperatingSystem.IsWindows() ? OSType.WINDOWS :
|
if (!onlinePlugins.Any())
|
||||||
OperatingSystem.IsLinux() ? OSType.LINUX :
|
{
|
||||||
OperatingSystem.IsMacOS() ? OSType.MACOSX : OSType.NONE;
|
Application.Log("Failed to get all plugins from the repository", LogType.Warning);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
return result.FindAll(pl => (pl.SupportedOS & currentOs) != 0);
|
int os = OS.GetOperatingSystemInt();
|
||||||
|
|
||||||
|
var response = onlinePlugins.Where(plugin => plugin.OperatingSystem == os).ToList();
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PluginOnlineInfo?> GetPluginDataByName(string pluginName)
|
public async Task<OnlinePlugin?> GetPluginDataByName(string pluginName)
|
||||||
{
|
{
|
||||||
List<PluginOnlineInfo>? plugins = await GetPluginsList();
|
var plugin = await _PluginRepository.GetPluginByName(pluginName);
|
||||||
|
|
||||||
if (plugins is null)
|
if (plugin == null)
|
||||||
{
|
{
|
||||||
|
Application.Log("Failed to get plugin from the repository", LogType.Warning);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginOnlineInfo? result = plugins.Find(pl => pl.Name.Contains(pluginName, StringComparison.CurrentCultureIgnoreCase));
|
return plugin;
|
||||||
if (result is null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
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);
|
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)
|
public async Task AppendPluginToDatabase(PluginInfo pluginData)
|
||||||
{
|
{
|
||||||
List<PluginInfo> installedPlugins = await JsonManager.ConvertFromJson<List<PluginInfo>>(await File.ReadAllTextAsync(Application.CurrentApplication.PluginDatabase));
|
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);
|
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)
|
public async Task<bool> MarkPluginToUninstall(string pluginName)
|
||||||
{
|
{
|
||||||
IEnumerable<PluginInfo> installedPlugins = await GetInstalledPlugins();
|
List<PluginInfo> installedPlugins = await GetInstalledPlugins();
|
||||||
IEnumerable<PluginInfo> info = installedPlugins.Where(info => info.PluginName == pluginName).AsEnumerable();
|
List<PluginInfo> info = installedPlugins.Where(info => info.PluginName == pluginName).ToList();
|
||||||
|
|
||||||
if (!info.Any())
|
if (!info.Any())
|
||||||
return false;
|
return false;
|
||||||
@@ -133,9 +97,7 @@ public sealed class PluginManager
|
|||||||
await AppendPluginToDatabase(item);
|
await AppendPluginToDatabase(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UninstallMarkedPlugins()
|
public async Task UninstallMarkedPlugins()
|
||||||
@@ -201,137 +163,54 @@ public sealed class PluginManager
|
|||||||
return relative;
|
return relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InstallPluginWithNoProgress(PluginOnlineInfo pluginData)
|
public async Task InstallPluginNoProgress(OnlinePlugin plugin)
|
||||||
{
|
{
|
||||||
InstallingPluginInformation = new InstallingPluginInformation() { PluginName = pluginData.Name };
|
InstallingPluginInformation = new InstallingPluginInformation() { PluginName = plugin.PluginName };
|
||||||
|
List<OnlineDependencyInfo> dependencies = await _PluginRepository.GetDependenciesForPlugin(plugin.PluginId);
|
||||||
|
|
||||||
// Calculate the total number of steps: main file + dependencies
|
int totalSteps = dependencies.Count + 1;
|
||||||
int totalSteps = pluginData.HasFileDependencies ? pluginData.Dependencies.Count + 1 : 1;
|
|
||||||
|
|
||||||
// Each step contributes this percentage to the total progress
|
|
||||||
float stepFraction = 100f / totalSteps;
|
float stepFraction = 100f / totalSteps;
|
||||||
|
|
||||||
// Tracks the current cumulative progress in percentage
|
|
||||||
float currentProgress = 0f;
|
float currentProgress = 0f;
|
||||||
|
|
||||||
InstallingPluginInformation.IsInstalling = true;
|
InstallingPluginInformation.IsInstalling = true;
|
||||||
|
var progress = currentProgress;
|
||||||
// Create a progress updater that maps the file's 0–100 progress to its portion of the total progress
|
|
||||||
IProgress<float> downloadProgress = new Progress<float>(fileProgress =>
|
IProgress<float> downloadProgress = new Progress<float>(fileProgress =>
|
||||||
{
|
{
|
||||||
// Map the file progress (0-100) to the total progress
|
InstallingPluginInformation.InstallationProgress = progress + (fileProgress / 100f) * stepFraction;
|
||||||
InstallingPluginInformation.InstallationProgress = currentProgress + (fileProgress / 100f) * stepFraction;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Download the main plugin file and map its progress
|
await ServerCom.DownloadFileAsync(plugin.PluginLink,
|
||||||
await ServerCom.DownloadFileAsync(pluginData.DownLoadLink,
|
$"{Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("PluginFolder")}/{plugin.PluginName}.dll",
|
||||||
$"{Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("PluginFolder")}/{pluginData.Name}.dll",
|
downloadProgress);
|
||||||
downloadProgress
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update cumulative progress after the main file
|
|
||||||
currentProgress += stepFraction;
|
currentProgress += stepFraction;
|
||||||
|
|
||||||
// Download file dependencies if they exist
|
if (dependencies.Count > 0)
|
||||||
if (pluginData.HasFileDependencies)
|
|
||||||
{
|
{
|
||||||
foreach (var dependency in pluginData.Dependencies)
|
foreach (var dependency in dependencies)
|
||||||
{
|
{
|
||||||
string dependencyLocation =
|
string dependencyLocation = GenerateDependencyRelativePath(plugin.PluginName, dependency.DownloadLocation);
|
||||||
GenerateDependencyRelativePath(pluginData.Name, dependency.DownloadLocation);
|
|
||||||
|
|
||||||
// Download dependency and map its progress
|
|
||||||
await ServerCom.DownloadFileAsync(dependency.DownloadLink, dependencyLocation, downloadProgress);
|
await ServerCom.DownloadFileAsync(dependency.DownloadLink, dependencyLocation, downloadProgress);
|
||||||
|
|
||||||
// Update cumulative progress after each dependency
|
|
||||||
currentProgress += stepFraction;
|
currentProgress += stepFraction;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run script dependencies if any (doesn't affect download progress percentage)
|
PluginInfo pluginInfo = PluginInfo.FromOnlineInfo(plugin, dependencies);
|
||||||
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);
|
await AppendPluginToDatabase(pluginInfo);
|
||||||
|
InstallingPluginInformation.IsInstalling = false;
|
||||||
InstallingPluginInformation.IsInstalling = false; // Mark installation as complete
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Tuple<Dictionary<string, string>, List<OnlineDependencyInfo>>> GatherInstallDataForPlugin(OnlinePlugin plugin)
|
||||||
|
|
||||||
|
|
||||||
public async Task InstallPluginWithProgressBar(PluginOnlineInfo pluginData, IProgress<float>? installProgress)
|
|
||||||
{
|
{
|
||||||
installProgress?.Report(0f);
|
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 } };
|
||||||
int totalSteps = pluginData.HasFileDependencies ? pluginData.Dependencies.Count + 1 : 1;
|
foreach(var dependency in dependencies)
|
||||||
|
|
||||||
float stepProgress = 1f / totalSteps;
|
|
||||||
|
|
||||||
float currentProgress = 0f;
|
|
||||||
|
|
||||||
IProgress<float> progress = new Progress<float>((p) =>
|
|
||||||
{
|
{
|
||||||
installProgress?.Report(currentProgress + stepProgress * p);
|
string dependencyLocation = GenerateDependencyRelativePath(plugin.PluginName, dependency.DownloadLocation);
|
||||||
});
|
downloads.Add(dependencyLocation, dependency.DownloadLink);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginInfo pluginInfo = PluginInfo.FromOnlineInfo(pluginData);
|
return (downloads, dependencies).ToTuple();
|
||||||
|
|
||||||
await AppendPluginToDatabase(pluginInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SetEnabledStatus(string pluginName, bool status)
|
public async Task SetEnabledStatus(string pluginName, bool status)
|
||||||
|
|||||||
@@ -1,48 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using DiscordBotCore.Online.Helpers;
|
using DiscordBotCore.Online.Helpers;
|
||||||
using DiscordBotCore.Others;
|
|
||||||
|
|
||||||
namespace DiscordBotCore.Online;
|
namespace DiscordBotCore.Online;
|
||||||
|
|
||||||
public static class ServerCom
|
public static class ServerCom
|
||||||
{
|
{
|
||||||
/// <summary>
|
private static async Task DownloadFileAsync(
|
||||||
/// 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(
|
|
||||||
string URL, string location, IProgress<float>? progress,
|
string URL, string location, IProgress<float>? progress,
|
||||||
IProgress<long>? downloadedBytes)
|
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
|
OnStartupAndCall
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
|
||||||
public enum OSType: byte
|
|
||||||
{
|
|
||||||
NONE = 0,
|
|
||||||
WINDOWS = 1 << 0,
|
|
||||||
LINUX = 2 << 1,
|
|
||||||
MACOSX = 3 << 2
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum PluginType
|
public enum PluginType
|
||||||
{
|
{
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
|
|||||||
@@ -93,7 +93,12 @@ public static class JsonManager
|
|||||||
|
|
||||||
text.Position = 0;
|
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();
|
await text.FlushAsync();
|
||||||
text.Close();
|
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 class OnlineDependencyInfo
|
||||||
{
|
{
|
||||||
public string DependencyName { get; private set; }
|
public string DependencyName { get; private set; }
|
||||||
|
[JsonPropertyName("dependencyLink")]
|
||||||
public string DownloadLink { get; private set; }
|
public string DownloadLink { get; private set; }
|
||||||
|
[JsonPropertyName("dependencyLocation")]
|
||||||
public string DownloadLocation { get; private set; }
|
public string DownloadLocation { get; private set; }
|
||||||
public bool IsExecutable { 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 class PluginInfo
|
||||||
{
|
{
|
||||||
public string PluginName { get; private set; }
|
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 string FilePath { get; private set; }
|
||||||
public Dictionary<string, string> ListOfExecutableDependencies {get; private set;}
|
public Dictionary<string, string> ListOfExecutableDependencies {get; private set;}
|
||||||
public bool IsMarkedToUninstall {get; internal set;}
|
public bool IsMarkedToUninstall {get; internal set;}
|
||||||
@@ -16,7 +16,7 @@ public class PluginInfo
|
|||||||
public bool IsEnabled { get; internal set; }
|
public bool IsEnabled { get; internal set; }
|
||||||
|
|
||||||
[JsonConstructor]
|
[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;
|
PluginName = pluginName;
|
||||||
PluginVersion = pluginVersion;
|
PluginVersion = pluginVersion;
|
||||||
@@ -27,7 +27,7 @@ public class PluginInfo
|
|||||||
IsEnabled = isEnabled;
|
IsEnabled = isEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PluginInfo(string pluginName, PluginVersion pluginVersion, Dictionary<string, string> listOfExecutableDependencies)
|
public PluginInfo(string pluginName, string pluginVersion, Dictionary<string, string> listOfExecutableDependencies)
|
||||||
{
|
{
|
||||||
PluginName = pluginName;
|
PluginName = pluginName;
|
||||||
PluginVersion = pluginVersion;
|
PluginVersion = pluginVersion;
|
||||||
@@ -38,21 +38,14 @@ public class PluginInfo
|
|||||||
IsEnabled = true;
|
IsEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PluginInfo FromOnlineInfo(PluginOnlineInfo onlineInfo)
|
public static PluginInfo FromOnlineInfo(OnlinePlugin plugin, List<OnlineDependencyInfo> dependencies)
|
||||||
{
|
{
|
||||||
var pluginName = onlineInfo.Name;
|
PluginInfo pluginInfo = new PluginInfo(
|
||||||
var version = onlineInfo.Version;
|
plugin.PluginName, plugin.LatestVersion,
|
||||||
var dependencies= onlineInfo.Dependencies;
|
dependencies.Where(dependency => dependency.IsExecutable)
|
||||||
|
.ToDictionary(dependency => dependency.DependencyName, dependency => dependency.DownloadLocation)
|
||||||
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);
|
|
||||||
|
|
||||||
|
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