Updated API for plugins to work with database from remote. Added PluginRepository and removed Installation Scripts

This commit is contained in:
2025-01-26 20:34:34 +02:00
parent 8b2169dc7b
commit 84b19e2069
35 changed files with 435 additions and 1056 deletions

View File

@@ -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);
}

View File

@@ -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)
{

View File

@@ -118,7 +118,7 @@ public class Plugin: ICommandAction
}
}
await PluginMethods.DownloadPlugin(pluginName);
await PluginMethods.DownloadPluginWithParallelDownloads(pluginName);
break;
}

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>disable</Nullable>
<Nullable>enable</Nullable>
<ApplicationIcon />
<StartupObject />
<SignAssembly>False</SignAssembly>

View File

@@ -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;

View File

@@ -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);
});
}
}