Deleting plugins is now available

This commit is contained in:
2024-02-28 13:57:12 +02:00
parent 3c3c6a1301
commit fd9cd49844
17 changed files with 128 additions and 300 deletions

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using DiscordBot.Utilities; using DiscordBot.Utilities;
using PluginManager; using PluginManager;
@@ -71,7 +72,7 @@ internal static class PluginMethods
if (!pluginData.HasDependencies) if (!pluginData.HasDependencies)
{ {
await manager.AppendPluginToDatabase(pluginName, pluginData.Version); await manager.AppendPluginToDatabase(new PluginManager.Plugin.PluginInfo(pluginName, pluginData.Version, []));
Console.WriteLine("Finished installing " + pluginName + " successfully"); Console.WriteLine("Finished installing " + pluginName + " successfully");
await RefreshPlugins(false); await RefreshPlugins(false);
return; return;
@@ -126,7 +127,7 @@ internal static class PluginMethods
} }
); );
await manager.AppendPluginToDatabase(pluginName, pluginData.Version); await manager.AppendPluginToDatabase(new PluginManager.Plugin.PluginInfo(pluginName, pluginData.Version, pluginData.Dependencies.Select(sep => sep.DownloadLocation).ToList()));
await RefreshPlugins(false); await RefreshPlugins(false);
} }

View File

@@ -40,6 +40,13 @@ public class Plugin: ICommandAction
await PluginMethods.RefreshPlugins(true); await PluginMethods.RefreshPlugins(true);
break; break;
case "uninstall":
string plugName = string.Join(' ', args, 1, args.Length-1);
bool result = await Config.PluginsManager.MarkPluginToUninstall(plugName);
if(result)
Console.WriteLine($"Marked to uninstall plugin {plugName}. Please restart the bot");
break;
case "list": case "list":
await PluginMethods.List(Config.PluginsManager); await PluginMethods.List(Config.PluginsManager);
break; break;

View File

@@ -1,9 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using PluginManager.UX;
namespace DiscordBot; namespace DiscordBot;
@@ -13,7 +10,7 @@ public static class Entry
public static void Main(string[] args) public static void Main(string[] args)
{ {
#if DEBUG #if DEBUG
if (args.Length == 1 && args[0] == "/purge_plugins") if (args.Length == 1 && args[0] == "/purge_plugins" )
{ {
foreach (var plugin in Directory.GetFiles("./Data/Plugins", "*.dll", SearchOption.AllDirectories)) foreach (var plugin in Directory.GetFiles("./Data/Plugins", "*.dll", SearchOption.AllDirectories))
{ {

View File

@@ -1,7 +1,7 @@
using System; using System;
using PluginManager; using PluginManager;
using Spectre.Console;
using System.Threading.Tasks; using System.Threading.Tasks;
using Spectre.Console;
namespace DiscordBot; namespace DiscordBot;
@@ -9,15 +9,16 @@ public static class Installer
{ {
public static async Task GenerateStartupConfig() public static async Task GenerateStartupConfig()
{ {
var token = await PluginManager.UX.UxHandler.ShowInputBox("SethBot", "Please enter the bot token:"); var token = AnsiConsole.Ask<string>("[green]Token:[/]");
var botPrefix = await PluginManager.UX.UxHandler.ShowInputBox("SethBot", "Please enter the bot prefix:"); var botPrefix = AnsiConsole.Ask<string>("[yellow]Prefix:[/]");
var serverId = await PluginManager.UX.UxHandler.ShowInputBox("SethBot", "Please enter the Server ID:"); var serverId = AnsiConsole.Ask<string>("[deeppink1]Server ID:[/]");
if (string.IsNullOrWhiteSpace(serverId)) serverId = "NULL"; if (string.IsNullOrWhiteSpace(serverId)) serverId = string.Empty;
if (string.IsNullOrWhiteSpace(token) || string.IsNullOrWhiteSpace(botPrefix)) if (string.IsNullOrWhiteSpace(token) || string.IsNullOrWhiteSpace(botPrefix))
{ {
await PluginManager.UX.UxHandler.ShowMessageBox("SethBot", "Invalid token or prefix !", PluginManager.UX.MessageBoxType.Error); AnsiConsole.MarkupLine("Invalid token or prefix !");
Environment.Exit(-20); Environment.Exit(-20);
} }

View File

@@ -7,7 +7,6 @@ using DiscordBot.Utilities;
using PluginManager.Bot; using PluginManager.Bot;
using PluginManager.Others; using PluginManager.Others;
using PluginManager.Others.Actions; using PluginManager.Others.Actions;
using PluginManager.UX;
using Spectre.Console; using Spectre.Console;
using static PluginManager.Config; using static PluginManager.Config;
@@ -100,11 +99,6 @@ public class Program
{ {
if (ex.Message == "No process is on the other end of the pipe." || (uint)ex.HResult == 0x800700E9) if (ex.Message == "No process is on the other end of the pipe." || (uint)ex.HResult == 0x800700E9)
{ {
UxHandler.ShowMessageBox("SethBot", "An error occured while closing the bot last time. Please consider closing the bot using the &rexit&c method !\n" +
"There is a risk of losing all data or corruption of the save file, which in some cases requires to reinstall the bot !", MessageBoxType.Error
).Wait();
Logger.Log("An error occured while closing the bot last time. Please consider closing the bot using the &rexit&c method !\n" + Logger.Log("An error occured while closing the bot last time. Please consider closing the bot using the &rexit&c method !\n" +
"There is a risk of losing all data or corruption of the save file, which in some cases requires to reinstall the bot !", "There is a risk of losing all data or corruption of the save file, which in some cases requires to reinstall the bot !",
typeof(Program), LogType.ERROR typeof(Program), LogType.ERROR
@@ -130,7 +124,6 @@ public class Program
if (logMessage.Message.Contains('[')) if (logMessage.Message.Contains('['))
{ {
// If the message contains a tag, just print it as it is. No need to format it
Console.WriteLine(logMessage.Message); Console.WriteLine(logMessage.Message);
return; return;
} }

View File

@@ -4,7 +4,7 @@ using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket; using Discord.WebSocket;
using PluginManager.Others; using PluginManager.Others;
using PluginManager.UX;
namespace PluginManager.Bot; namespace PluginManager.Bot;
@@ -91,6 +91,7 @@ public class Boot
while (!isReady) ; while (!isReady) ;
} }
private void CommonTasks() private void CommonTasks()
{ {
if (client == null) return; if (client == null) return;
@@ -122,7 +123,7 @@ public class Boot
private Task Ready() private Task Ready()
{ {
isReady = true; isReady = true;
UxHandler.ShowNotification("SethBot", "Seth Discord Bot is now up and running !").Wait(); // UxHandler.ShowNotification("SethBot", "Seth Discord Bot is now up and running !").Wait();
return Task.CompletedTask; return Task.CompletedTask;
} }

View File

@@ -7,7 +7,6 @@ using PluginManager.Online;
using PluginManager.Others; using PluginManager.Others;
using PluginManager.Others.Logger; using PluginManager.Others.Logger;
using PluginManager.Plugin; using PluginManager.Plugin;
using OperatingSystem = System.OperatingSystem;
namespace PluginManager; namespace PluginManager;
@@ -48,29 +47,15 @@ public class Config
await JsonManager.SaveToJsonFile(AppSettings["PluginDatabase"], plugins); await JsonManager.SaveToJsonFile(AppSettings["PluginDatabase"], plugins);
} }
if (OperatingSystem.IsLinux()) Logger = new Logger(false, true, AppSettings["LogFolder"] + $"/{DateTime.Today.ToShortDateString().Replace("/", "")}.log");
{
var windowManager = Environment.GetEnvironmentVariable("XDG_CURRENT_DESKTOP");
AppSettings["UI"] = windowManager switch
{
"KDE" => "KDE",
"GNOME" => "GNOME",
_ => "CONSOLE"
};
}
else AppSettings["UI"] = "CONSOLE";
Logger = new Logger(false, true,
AppSettings["LogFolder"] + $"/{DateTime.Today.ToShortDateString().Replace("/", "")}.log"
);
UX.UxHandler.Init();
_isLoaded = true; _isLoaded = true;
PluginsManager = new PluginsManager("releases"); PluginsManager = new PluginsManager("releases");
await PluginsManager.UninstallMarkedPlugins();
await PluginsManager.CheckForUpdates(); await PluginsManager.CheckForUpdates();
Logger.Log("Config initialized", typeof(Config)); Logger.Log("Config initialized", typeof(Config));

View File

@@ -1,6 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO; using System.IO;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using PluginManager.Online.Helpers; using PluginManager.Online.Helpers;
using PluginManager.Others; using PluginManager.Others;
@@ -68,11 +70,11 @@ public class PluginsManager
await JsonManager.SaveToJsonFile( Config.AppSettings["PluginDatabase"],installedPlugins); await JsonManager.SaveToJsonFile( Config.AppSettings["PluginDatabase"],installedPlugins);
} }
public async Task AppendPluginToDatabase(string pluginName, PluginVersion version) public async Task AppendPluginToDatabase(PluginInfo pluginData)
{ {
List<PluginInfo> installedPlugins = await JsonManager.ConvertFromJson<List<PluginInfo>>(await File.ReadAllTextAsync(Config.AppSettings["PluginDatabase"])); List<PluginInfo> installedPlugins = await JsonManager.ConvertFromJson<List<PluginInfo>>(await File.ReadAllTextAsync(Config.AppSettings["PluginDatabase"]));
installedPlugins.Add(new PluginInfo(pluginName, version)); installedPlugins.Add(pluginData);
await JsonManager.SaveToJsonFile( Config.AppSettings["PluginDatabase"],installedPlugins); await JsonManager.SaveToJsonFile( Config.AppSettings["PluginDatabase"],installedPlugins);
} }
@@ -97,5 +99,42 @@ public class PluginsManager
} }
} }
public async Task<bool> MarkPluginToUninstall(string pluginName)
{
List<PluginInfo> installedPlugins = await GetInstalledPlugins();
PluginInfo? info = installedPlugins.Find(info => info.PluginName == pluginName);
if(info == null)
return false;
await RemovePluginFromDatabase(pluginName);
info.IsMarkedToUninstall = true;
await AppendPluginToDatabase(info);
return true;
}
public async Task UninstallMarkedPlugins()
{
List<PluginInfo> installedPlugins = await GetInstalledPlugins();
foreach(PluginInfo plugin in installedPlugins)
{
if(!plugin.IsMarkedToUninstall) continue;
await UninstallPlugin(plugin);
}
}
private async Task UninstallPlugin(PluginInfo pluginInfo)
{
File.Delete(pluginInfo.FilePath);
foreach(string dependency in pluginInfo.ListOfDependancies)
File.Delete(dependency);
await RemovePluginFromDatabase(pluginInfo.PluginName);
}
} }

View File

@@ -18,13 +18,12 @@ public class InternalActionManager
public async Task Initialize() public async Task Initialize()
{ {
//loader.ActionLoadedEvent += OnActionLoaded;
var m_actions = await loader.Load(); var m_actions = await loader.Load();
if (m_actions == null) return; if (m_actions == null)
return;
foreach (var action in m_actions) foreach (var action in m_actions)
{
Actions.TryAdd(action.ActionName, action); Actions.TryAdd(action.ActionName, action);
}
} }
public async Task Refresh() public async Task Refresh()
@@ -33,34 +32,23 @@ public class InternalActionManager
await Initialize(); await Initialize();
} }
// private void OnActionLoaded(string name, string typeName, bool success, Exception? e) public async Task<bool> Execute(string actionName, params string[]? args)
// {
// if (!success)
// {
// Config.Logger.Error(e);
// return;
// }
//
// Config.Logger.Log($"Action {name} loaded successfully", LogLevel.INFO, true);
// }
public async Task<string> Execute(string actionName, params string[]? args)
{ {
if (!Actions.ContainsKey(actionName)) if (!Actions.ContainsKey(actionName))
{ {
Config.Logger.Log($"Action {actionName} not found", type: LogType.ERROR, source: typeof(InternalActionManager)); Config.Logger.Log($"Action {actionName} not found", type: LogType.ERROR, source: typeof(InternalActionManager));
return "Action not found"; return false;
} }
try try
{ {
await Actions[actionName].Execute(args); await Actions[actionName].Execute(args);
return "Action executed"; return true;
} }
catch (Exception e) catch (Exception e)
{ {
Config.Logger.Log(e.Message, type: LogType.ERROR, source: typeof(InternalActionManager)); Config.Logger.Log(e.Message, type: LogType.ERROR, source: typeof(InternalActionManager));
return e.Message; return false;
} }
} }
} }

View File

@@ -8,7 +8,7 @@ namespace PluginManager.Others;
public static class ArchiveManager public static class ArchiveManager
{ {
private static string? _archiveFolder; private static string? _ArchiveFolder;
private static bool IsInitialized { get; set; } private static bool IsInitialized { get; set; }
public static void Initialize() public static void Initialize()
@@ -19,7 +19,7 @@ public static class ArchiveManager
if (!Config.AppSettings.ContainsKey("ArchiveFolder")) if (!Config.AppSettings.ContainsKey("ArchiveFolder"))
Config.AppSettings["ArchiveFolder"] = "./Data/Archives/"; Config.AppSettings["ArchiveFolder"] = "./Data/Archives/";
_archiveFolder = Config.AppSettings["ArchiveFolder"]; _ArchiveFolder = Config.AppSettings["ArchiveFolder"];
IsInitialized = true; IsInitialized = true;
} }
@@ -52,7 +52,7 @@ public static class ArchiveManager
{ {
if (!IsInitialized) throw new Exception("ArchiveManager is not initialized"); if (!IsInitialized) throw new Exception("ArchiveManager is not initialized");
archName = _archiveFolder + archName; archName = _ArchiveFolder + archName;
if (!File.Exists(archName)) if (!File.Exists(archName))
throw new Exception("Failed to load file !"); throw new Exception("Failed to load file !");
@@ -81,7 +81,7 @@ public static class ArchiveManager
public static async Task<string?> ReadFromPakAsync(string fileName, string archFile) public static async Task<string?> ReadFromPakAsync(string fileName, string archFile)
{ {
if (!IsInitialized) throw new Exception("ArchiveManager is not initialized"); if (!IsInitialized) throw new Exception("ArchiveManager is not initialized");
archFile = _archiveFolder + archFile; archFile = _ArchiveFolder + archFile;
if (!File.Exists(archFile)) if (!File.Exists(archFile))
throw new Exception("Failed to load file !"); throw new Exception("Failed to load file !");

View File

@@ -1,5 +1,6 @@
using System.IO; using System.Collections.Generic;
using PluginManager.Interfaces.Updater; using System.Linq;
using System.Text.Json.Serialization;
using PluginManager.Online.Helpers; using PluginManager.Online.Helpers;
namespace PluginManager.Plugin; namespace PluginManager.Plugin;
@@ -9,17 +10,30 @@ public class PluginInfo
public string PluginName { get; private set; } public string PluginName { get; private set; }
public PluginVersion PluginVersion { get; private set; } public PluginVersion PluginVersion { get; private set; }
public string FilePath { get; private set; } public string FilePath { get; private set; }
public List<string> ListOfDependancies {get; private set;}
public bool IsMarkedToUninstall {get; internal set;}
public PluginInfo(string pluginName, PluginVersion pluginVersion) [JsonConstructor]
public PluginInfo(string pluginName, PluginVersion pluginVersion, List<string> listOfDependancies, bool isMarkedToUninstall)
{ {
PluginName = pluginName; PluginName = pluginName;
PluginVersion = pluginVersion; PluginVersion = pluginVersion;
ListOfDependancies = listOfDependancies;
IsMarkedToUninstall = isMarkedToUninstall;
FilePath = $"{Config.AppSettings["PluginFolder"]}/{pluginName}.dll";
}
public PluginInfo(string pluginName, PluginVersion pluginVersion, List<string> listOfDependancies)
{
PluginName = pluginName;
PluginVersion = pluginVersion;
ListOfDependancies = listOfDependancies;
IsMarkedToUninstall = false;
FilePath = $"{Config.AppSettings["PluginFolder"]}/{pluginName}.dll"; FilePath = $"{Config.AppSettings["PluginFolder"]}/{pluginName}.dll";
} }
public static PluginInfo FromOnlineInfo(PluginOnlineInfo onlineInfo) public static PluginInfo FromOnlineInfo(PluginOnlineInfo onlineInfo)
{ {
return new PluginInfo(onlineInfo.Name, onlineInfo.Version); return new PluginInfo(onlineInfo.Name, onlineInfo.Version, onlineInfo.Dependencies.Select(dep => dep.DownloadLocation).ToList());
} }
} }

View File

@@ -12,7 +12,6 @@
<None Remove="BlankWindow1.xaml"/> <None Remove="BlankWindow1.xaml"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.6"/>
<PackageReference Include="Discord.Net" Version="3.11.0"/> <PackageReference Include="Discord.Net" Version="3.11.0"/>
<PackageReference Include="Spectre.Console" Version="0.47.0"/> <PackageReference Include="Spectre.Console" Version="0.47.0"/>
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.118"/> <PackageReference Include="System.Data.SQLite.Core" Version="1.0.118"/>
@@ -26,6 +25,6 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Updater\Application\" /> <Folder Include="Updater\Application\"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,31 +0,0 @@
using System.Threading.Tasks;
namespace PluginManager.UX;
public enum MessageBoxType
{
Info,
Warning,
Error
}
public enum MessageBoxButtons
{
YesNo,
YesNoCancel,
ContinueCancel
}
internal interface IOutputModel
{
internal Task ShowMessageBox(string title, string message, MessageBoxType type);
internal Task<string> ShowInputBox(string title, string message);
internal Task ShowMessageBox(string message);
internal Task<int> ShowMessageBox(string title, string message, MessageBoxButtons buttons, bool isWarning);
internal Task ShowNotification(string title, string message, int timeout_seconds = 5);
}

View File

@@ -1,77 +0,0 @@
using System.Diagnostics;
using System.Threading.Tasks;
namespace PluginManager.UX.Linux;
internal class KDE: IOutputModel
{
internal string KdeDialogApplication { get; } = "kdialog";
public async Task ShowMessageBox(string title, string message, MessageBoxType type)
{
var process = new Process();
process.StartInfo.FileName = KdeDialogApplication;
var typeStr = type switch
{
MessageBoxType.Info => "msgbox",
MessageBoxType.Warning => "sorry",
MessageBoxType.Error => "error",
_ => "info"
};
process.StartInfo.Arguments = $"--title \"{title}\" --{typeStr} \"{message}\"";
process.Start();
await process.WaitForExitAsync();
}
public async Task<string> ShowInputBox(string title, string message)
{
var process = new Process();
process.StartInfo.FileName = KdeDialogApplication;
process.StartInfo.Arguments = $"--title \"{title}\" --inputbox \"{message}\"";
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardInput = true;
process.Start();
await process.WaitForExitAsync();
return await process.StandardOutput.ReadToEndAsync();
}
public async Task ShowMessageBox(string message)
{
var process = new Process();
process.StartInfo.FileName = KdeDialogApplication;
process.StartInfo.Arguments = $"--msgbox \"{message}\"";
process.Start();
await process.WaitForExitAsync();
}
public async Task<int> ShowMessageBox(string title, string message, MessageBoxButtons buttons, bool isWarning)
{
var process = new Process();
process.StartInfo.FileName = KdeDialogApplication;
var buttonsStr = buttons switch
{
MessageBoxButtons.YesNo => "yesno",
MessageBoxButtons.YesNoCancel => "yesnocancel",
MessageBoxButtons.ContinueCancel => "continuecancel",
_ => "yesno"
};
var typeStr = isWarning ? "warning" : "";
process.StartInfo.Arguments = $"--title \"{title}\" --{typeStr}{buttonsStr} \"{message}\"";
process.Start();
await process.WaitForExitAsync();
return process.ExitCode;
}
public async Task ShowNotification(string title, string message, int timeout_seconds = 5)
{
var process = new Process();
process.StartInfo.FileName = KdeDialogApplication;
process.StartInfo.Arguments = $"--title \"{title}\" --passivepopup \"{message}\" {timeout_seconds}";
process.Start();
await process.WaitForExitAsync();
}
}

View File

@@ -1,45 +0,0 @@
using System.Threading.Tasks;
using Spectre.Console;
namespace PluginManager.UX.Other;
internal class Console: IOutputModel
{
public Task ShowMessageBox(string title, string message, MessageBoxType type)
{
AnsiConsole.Markup(title);
AnsiConsole.Markup(message);
return Task.CompletedTask;
}
public Task<string> ShowInputBox(string title, string message)
{
AnsiConsole.MarkupLine(title);
var input = AnsiConsole.Ask<string>(message);
return Task.FromResult(input);
}
public Task ShowMessageBox(string message)
{
AnsiConsole.MarkupLine(message);
return Task.CompletedTask;
}
public Task<int> ShowMessageBox(string title, string message, MessageBoxButtons buttons, bool isWarning)
{
AnsiConsole.MarkupLine(title);
AnsiConsole.MarkupLine(message);
return Task.FromResult(0);
}
public Task ShowNotification(string title, string message, int timeout_seconds = 5)
{
return Task.CompletedTask;
}
}

View File

@@ -1,45 +0,0 @@
using System.Threading.Tasks;
namespace PluginManager.UX;
public static class UxHandler
{
private static IOutputModel? _model;
public static void Init()
{
_model = Config.AppSettings["UI"] switch
{
"KDE" => new Linux.KDE(),
"CONSOLE" => new Other.Console(),
_ => null
};
}
public static async Task ShowMessageBox(string title, string message, MessageBoxType type = MessageBoxType.Info)
{
await _model.ShowMessageBox(title, message, type);
}
public static async Task<string> ShowInputBox(string title, string message)
{
return await _model.ShowInputBox(title, message);
}
public static async Task ShowMessageBox(string message)
{
await _model.ShowMessageBox(message);
}
public static async Task<int> ShowMessageBox(string title, string message, MessageBoxButtons buttons, bool isWarning)
{
return await _model.ShowMessageBox(title, message, buttons, isWarning);
}
public static async Task ShowNotification(string title, string message, int timeout_seconds = 5)
{
await _model.ShowNotification(title, message, timeout_seconds);
}
}

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using PluginManager.Online; using PluginManager.Online;
using PluginManager.Plugin; using PluginManager.Plugin;
@@ -41,7 +42,7 @@ public class PluginUpdater
await ServerCom.DownloadFileAsync(dependency.DownloadLocation, dependency.DownloadLocation, progressMeter); await ServerCom.DownloadFileAsync(dependency.DownloadLocation, dependency.DownloadLocation, progressMeter);
await _PluginManager.RemovePluginFromDatabase(pluginName); await _PluginManager.RemovePluginFromDatabase(pluginName);
await _PluginManager.AppendPluginToDatabase(pluginName, pluginInfo.Version); await _PluginManager.AppendPluginToDatabase(PluginInfo.FromOnlineInfo(pluginInfo));
} }
public async Task<bool> HasUpdate(string pluginName) public async Task<bool> HasUpdate(string pluginName)