Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d5f78c831e | |||
| 7847c6cc8d | |||
| 82716a4f4f | |||
| 9476f9ec31 | |||
| dc787ac130 | |||
| 9525394a6e | |||
| fc93255503 | |||
| cadf500400 | |||
| 780614e1e7 | |||
| f32920c564 | |||
| 123e8e90a1 | |||
| 0323c888b3 | |||
| 1bb6d3b731 | |||
| 4dc5819c4e | |||
| 5d4fa6fba7 | |||
| b6675af9cb | |||
| 23a914d2c9 | |||
| e8822deeac | |||
| 29ecdb6883 | |||
| 90aa5875b5 | |||
| 0fccf706a1 | |||
| fd9cd49844 | |||
| 3c3c6a1301 | |||
| a2179787b9 | |||
| 8c06df9110 | |||
| ef7a2c0896 | |||
| 14f280baef | |||
| 196fb6d3d1 | |||
| cc355d7d4f | |||
| af90ae5fba | |||
| c8480b3c83 | |||
| fe32ebc4d7 | |||
| 2280957ea9 | |||
| 944d59d9a3 | |||
| 79ecff971b | |||
| 1f0e6516fd | |||
| 692f3d8f8c | |||
| 6d41d51694 | |||
| 5f23bdadcf | |||
| d3555b6fca | |||
| b5cdc0afeb | |||
| 3858156393 | |||
| 6279c5c3a9 | |||
| f58a57c6cd | |||
| d00ebfd7ed | |||
| ab279bd284 | |||
| 89c4932cd7 | |||
| c577f625c2 | |||
| 58624f4037 | |||
| c9249dc71b | |||
| 5e4f1ca35f | |||
| 0d8fdb5904 | |||
| 92a18e3495 | |||
| e929646e8e | |||
| 6315d13d18 | |||
| ee527bb36f | |||
| 86514d1770 | |||
| 9e8ed1e911 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -373,4 +373,6 @@ FodyWeavers.xsd
|
|||||||
/DiscordBot/Updater/
|
/DiscordBot/Updater/
|
||||||
.idea/
|
.idea/
|
||||||
DiscordBot/Launcher.exe
|
DiscordBot/Launcher.exe
|
||||||
DiscordBotUI/*
|
DiscordBotUI/bin
|
||||||
|
DiscordBotUI/obj
|
||||||
|
/.vscode
|
||||||
|
|||||||
26
.vscode/launch.json
vendored
26
.vscode/launch.json
vendored
@@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
// Use IntelliSense to find out which attributes exist for C# debugging
|
|
||||||
// Use hover for the description of the existing attributes
|
|
||||||
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
|
|
||||||
"name": ".NET Core Launch (console)",
|
|
||||||
"type": "coreclr",
|
|
||||||
"request": "launch",
|
|
||||||
"preLaunchTask": "build",
|
|
||||||
// If you have changed target frameworks, make sure to update the program path.
|
|
||||||
"program": "${workspaceFolder}/DiscordBot/bin/Debug/net6.0/DiscordBot.dll",
|
|
||||||
"args": [],
|
|
||||||
"cwd": "${workspaceFolder}/DiscordBot/bin/Debug/net6.0/",
|
|
||||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
|
||||||
"console": "externalTerminal",
|
|
||||||
"stopAtEntry": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": ".NET Core Attach",
|
|
||||||
"type": "coreclr",
|
|
||||||
"request": "attach"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
41
.vscode/tasks.json
vendored
41
.vscode/tasks.json
vendored
@@ -1,41 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "2.0.0",
|
|
||||||
"tasks": [
|
|
||||||
{
|
|
||||||
"label": "build",
|
|
||||||
"command": "dotnet",
|
|
||||||
"type": "process",
|
|
||||||
"args": [
|
|
||||||
"build",
|
|
||||||
"${workspaceFolder}/DiscordBot/DiscordBot.csproj",
|
|
||||||
"/property:GenerateFullPaths=true",
|
|
||||||
"/consoleloggerparameters:NoSummary"
|
|
||||||
],
|
|
||||||
"problemMatcher": "$msCompile"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "publish",
|
|
||||||
"command": "dotnet",
|
|
||||||
"type": "process",
|
|
||||||
"args": [
|
|
||||||
"publish",
|
|
||||||
"${workspaceFolder}/DiscordBot/DiscordBot.csproj",
|
|
||||||
"/property:GenerateFullPaths=true",
|
|
||||||
"/consoleloggerparameters:NoSummary"
|
|
||||||
],
|
|
||||||
"problemMatcher": "$msCompile"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "watch",
|
|
||||||
"command": "dotnet",
|
|
||||||
"type": "process",
|
|
||||||
"args": [
|
|
||||||
"watch",
|
|
||||||
"run",
|
|
||||||
"--project",
|
|
||||||
"${workspaceFolder}/DiscordBot/DiscordBot.csproj"
|
|
||||||
],
|
|
||||||
"problemMatcher": "$msCompile"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using PluginManager;
|
|
||||||
using PluginManager.Interfaces;
|
using PluginManager.Interfaces;
|
||||||
using PluginManager.Others;
|
using PluginManager.Others;
|
||||||
|
using PluginManager.Others.Actions;
|
||||||
|
|
||||||
namespace DiscordBot.Bot.Actions;
|
namespace DiscordBot.Bot.Actions;
|
||||||
|
|
||||||
@@ -11,6 +13,8 @@ public class Clear : ICommandAction
|
|||||||
public string ActionName => "clear";
|
public string ActionName => "clear";
|
||||||
public string Description => "Clears the console";
|
public string Description => "Clears the console";
|
||||||
public string Usage => "clear";
|
public string Usage => "clear";
|
||||||
|
public IEnumerable<InternalActionOption> ListOfOptions => [];
|
||||||
|
|
||||||
public InternalActionRunType RunType => InternalActionRunType.ON_CALL;
|
public InternalActionRunType RunType => InternalActionRunType.ON_CALL;
|
||||||
|
|
||||||
public Task Execute(string[] args)
|
public Task Execute(string[] args)
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using PluginManager;
|
using PluginManager;
|
||||||
using PluginManager.Interfaces;
|
using PluginManager.Interfaces;
|
||||||
using PluginManager.Others;
|
using PluginManager.Others;
|
||||||
|
using PluginManager.Others.Actions;
|
||||||
|
|
||||||
namespace DiscordBot.Bot.Actions;
|
namespace DiscordBot.Bot.Actions;
|
||||||
|
|
||||||
@@ -10,16 +12,20 @@ public class Exit : ICommandAction
|
|||||||
{
|
{
|
||||||
public string ActionName => "exit";
|
public string ActionName => "exit";
|
||||||
public string Description => "Exits the bot and saves the config. Use exit help for more info.";
|
public string Description => "Exits the bot and saves the config. Use exit help for more info.";
|
||||||
public string Usage => "exit [help|force (-f)]";
|
public string Usage => "exit <option?>";
|
||||||
|
public IEnumerable<InternalActionOption> ListOfOptions => new List<InternalActionOption>
|
||||||
|
{
|
||||||
|
new InternalActionOption("help", "Displays this message"),
|
||||||
|
new InternalActionOption("force | -f", "Exits the bot without saving the config")
|
||||||
|
};
|
||||||
public InternalActionRunType RunType => InternalActionRunType.ON_CALL;
|
public InternalActionRunType RunType => InternalActionRunType.ON_CALL;
|
||||||
|
|
||||||
public async Task Execute(string[] args)
|
public async Task Execute(string[] args)
|
||||||
{
|
{
|
||||||
if (args is null || args.Length == 0)
|
if (args is null || args.Length == 0)
|
||||||
{
|
{
|
||||||
Config.Logger.Log("Exiting...", "Exit", isInternal: false);
|
Config.Logger.Log("Exiting...", typeof(ICommandAction), LogType.WARNING);
|
||||||
await Config.AppSettings.SaveToFile();
|
await Config.AppSettings.SaveToFile();
|
||||||
await Config.Logger.SaveToFile();
|
|
||||||
Environment.Exit(0);
|
Environment.Exit(0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -34,7 +40,7 @@ public class Exit : ICommandAction
|
|||||||
|
|
||||||
case "-f":
|
case "-f":
|
||||||
case "force":
|
case "force":
|
||||||
Config.Logger.Log("Exiting (FORCE)...", "Exit", LogLevel.WARNING, false);
|
Config.Logger.Log("Exiting (FORCE)...", typeof(ICommandAction), LogType.WARNING);
|
||||||
Environment.Exit(0);
|
Environment.Exit(0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
206
DiscordBot/Bot/Actions/Extra/PluginMethods.cs
Normal file
206
DiscordBot/Bot/Actions/Extra/PluginMethods.cs
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using DiscordBot.Utilities;
|
||||||
|
|
||||||
|
using PluginManager;
|
||||||
|
using PluginManager.Interfaces;
|
||||||
|
using PluginManager.Loaders;
|
||||||
|
using PluginManager.Online;
|
||||||
|
using PluginManager.Others;
|
||||||
|
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
namespace DiscordBot.Bot.Actions.Extra;
|
||||||
|
|
||||||
|
internal static class PluginMethods
|
||||||
|
{
|
||||||
|
internal static async Task List(PluginsManager manager)
|
||||||
|
{
|
||||||
|
var data = await ConsoleUtilities.ExecuteWithProgressBar(manager.GetPluginsList(), "Reading remote database");
|
||||||
|
|
||||||
|
TableData tableData = new(["Name", "Description", "Version", "Is Installed"]);
|
||||||
|
|
||||||
|
var installedPlugins = await ConsoleUtilities.ExecuteWithProgressBar(manager.GetInstalledPlugins(), "Reading local database ");
|
||||||
|
|
||||||
|
foreach (var plugin in data)
|
||||||
|
{
|
||||||
|
bool isInstalled = installedPlugins.Any(p => p.PluginName == plugin.Name);
|
||||||
|
tableData.AddRow([plugin.Name, plugin.Description, plugin.Version.ToString(), isInstalled ? "Yes" : "No"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
tableData.HasRoundBorders = false;
|
||||||
|
tableData.PrintTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static async Task RefreshPlugins(bool quiet)
|
||||||
|
{
|
||||||
|
await Program.internalActionManager.Execute("plugin", "load", quiet ? "-q" : string.Empty);
|
||||||
|
await Program.internalActionManager.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static async Task DownloadPlugin(PluginsManager manager, string pluginName)
|
||||||
|
{
|
||||||
|
var pluginData = await manager.GetPluginDataByName(pluginName);
|
||||||
|
if (pluginData is null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Plugin {pluginName} not found. Please check the spelling and try again.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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; });
|
||||||
|
|
||||||
|
await ServerCom.DownloadFileAsync(pluginLink, $"{Config.AppSettings["PluginFolder"]}/{pluginName}.dll", progress);
|
||||||
|
|
||||||
|
downloadTask.Increment(100);
|
||||||
|
|
||||||
|
ctx.Refresh();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!pluginData.HasDependencies)
|
||||||
|
{
|
||||||
|
await manager.AppendPluginToDatabase(new PluginManager.Plugin.PluginInfo(pluginName, pluginData.Version, []));
|
||||||
|
Console.WriteLine("Finished installing " + pluginName + " successfully");
|
||||||
|
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}: ");
|
||||||
|
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 = 5;
|
||||||
|
|
||||||
|
if (Config.AppSettings.ContainsKey("MaxParallelDownloads"))
|
||||||
|
maxParallelDownloads = int.Parse(Config.AppSettings["MaxParallelDownloads"]);
|
||||||
|
|
||||||
|
var options = new ParallelOptions()
|
||||||
|
{
|
||||||
|
MaxDegreeOfParallelism = maxParallelDownloads,
|
||||||
|
TaskScheduler = TaskScheduler.Default
|
||||||
|
};
|
||||||
|
|
||||||
|
await Parallel.ForEachAsync(downloadTasks, options, async (tuple, token) =>
|
||||||
|
{
|
||||||
|
tuple.Item1.IsIndeterminate = false;
|
||||||
|
await ServerCom.DownloadFileAsync(tuple.Item3, $"./{tuple.Item4}", tuple.Item2);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await manager.AppendPluginToDatabase(new PluginManager.Plugin.PluginInfo(pluginName, pluginData.Version, pluginData.Dependencies.Select(sep => sep.DownloadLocation).ToList()));
|
||||||
|
await RefreshPlugins(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static async Task<bool> LoadPlugins(string[] args)
|
||||||
|
{
|
||||||
|
var loader = new PluginLoader(Config.DiscordBot.Client);
|
||||||
|
if (args.Length == 2 && args[1] == "-q")
|
||||||
|
{
|
||||||
|
await loader.LoadPlugins();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cc = Console.ForegroundColor;
|
||||||
|
loader.OnCommandLoaded += (data) =>
|
||||||
|
{
|
||||||
|
if (data.IsSuccess)
|
||||||
|
{
|
||||||
|
Config.Logger.Log("Successfully loaded command : " + data.PluginName, typeof(ICommandAction),
|
||||||
|
LogType.INFO
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Config.Logger.Log("Failed to load command : " + data.PluginName + " because " + data.ErrorMessage,
|
||||||
|
typeof(ICommandAction), LogType.ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.ForegroundColor = cc;
|
||||||
|
};
|
||||||
|
loader.OnEventLoaded += (data) =>
|
||||||
|
{
|
||||||
|
if (data.IsSuccess)
|
||||||
|
{
|
||||||
|
Config.Logger.Log("Successfully loaded event : " + data.PluginName, typeof(ICommandAction),
|
||||||
|
LogType.INFO
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Config.Logger.Log("Failed to load event : " + data.PluginName + " because " + data.ErrorMessage,
|
||||||
|
typeof(ICommandAction), LogType.ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.ForegroundColor = cc;
|
||||||
|
};
|
||||||
|
|
||||||
|
loader.OnSlashCommandLoaded += (data) =>
|
||||||
|
{
|
||||||
|
if (data.IsSuccess)
|
||||||
|
{
|
||||||
|
Config.Logger.Log("Successfully loaded slash command : " + data.PluginName, typeof(ICommandAction),
|
||||||
|
LogType.INFO
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Config.Logger.Log("Failed to load slash command : " + data.PluginName + " because " + data.ErrorMessage,
|
||||||
|
typeof(ICommandAction), LogType.ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.ForegroundColor = cc;
|
||||||
|
};
|
||||||
|
|
||||||
|
await loader.LoadPlugins();
|
||||||
|
Console.ForegroundColor = cc;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
45
DiscordBot/Bot/Actions/Extra/SettingsConfigExtra.cs
Normal file
45
DiscordBot/Bot/Actions/Extra/SettingsConfigExtra.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using PluginManager;
|
||||||
|
using PluginManager.Loaders;
|
||||||
|
|
||||||
|
namespace DiscordBot.Bot.Actions.Extra;
|
||||||
|
|
||||||
|
internal static class SettingsConfigExtra
|
||||||
|
{
|
||||||
|
|
||||||
|
internal static void SetSettings(string key, params string[] value)
|
||||||
|
{
|
||||||
|
if (key is null) return;
|
||||||
|
|
||||||
|
if (value is null) return;
|
||||||
|
|
||||||
|
if (!Config.AppSettings.ContainsKey(key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Config.AppSettings[key] = string.Join(' ', value);
|
||||||
|
// Config.AppSettings.SaveToFile().Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void RemoveSettings(string key)
|
||||||
|
{
|
||||||
|
if (key is null) return;
|
||||||
|
|
||||||
|
if (!Config.AppSettings.ContainsKey(key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Config.AppSettings.Remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void AddSettings(string key, params string[] value)
|
||||||
|
{
|
||||||
|
if (key is null) return;
|
||||||
|
|
||||||
|
if (value is null) return;
|
||||||
|
|
||||||
|
if (Config.AppSettings.ContainsKey(key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Config.AppSettings.Add(key, string.Join(' ', value));
|
||||||
|
// Config.AppSettings.SaveToFile().Wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
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.Interfaces;
|
using PluginManager.Interfaces;
|
||||||
using PluginManager.Others;
|
using PluginManager.Others;
|
||||||
|
using PluginManager.Others.Actions;
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
namespace DiscordBot.Bot.Actions;
|
namespace DiscordBot.Bot.Actions;
|
||||||
|
|
||||||
@@ -13,29 +16,55 @@ public class Help : ICommandAction
|
|||||||
|
|
||||||
public string Description => "Shows the list of commands and their usage";
|
public string Description => "Shows the list of commands and their usage";
|
||||||
|
|
||||||
public string Usage => "help [command]";
|
public string Usage => "help <command?>";
|
||||||
|
|
||||||
|
public IEnumerable<InternalActionOption> ListOfOptions => [];
|
||||||
|
|
||||||
public InternalActionRunType RunType => InternalActionRunType.ON_CALL;
|
public InternalActionRunType RunType => InternalActionRunType.ON_CALL;
|
||||||
|
|
||||||
public async Task Execute(string[] args)
|
public async Task Execute(string[] args)
|
||||||
{
|
{
|
||||||
|
TableData tableData = new TableData();
|
||||||
if (args == null || args.Length == 0)
|
if (args == null || args.Length == 0)
|
||||||
{
|
{
|
||||||
var items = new List<string[]>
|
|
||||||
{
|
tableData.Columns = ["Command", "Usage", "Description", "Options"];
|
||||||
new[] { "-", "-", "-" },
|
|
||||||
new[] { "Command", "Usage", "Description" },
|
|
||||||
new[] { "-", "-", "-" }
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var a in Program.internalActionManager.Actions)
|
foreach (var a in Program.internalActionManager.Actions)
|
||||||
items.Add(new[] { a.Key, a.Value.Usage, a.Value.Description });
|
{
|
||||||
|
Markup actionName = new Markup($"[bold]{a.Key}[/]");
|
||||||
|
Markup usage = new Markup($"[italic]{a.Value.Usage}[/]");
|
||||||
|
Markup description = new Markup($"[dim]{a.Value.Description}[/]");
|
||||||
|
|
||||||
|
if (a.Value.ListOfOptions.Any())
|
||||||
|
{
|
||||||
|
|
||||||
|
var optionsTable = new Table();
|
||||||
|
optionsTable.AddColumn("Option");
|
||||||
|
optionsTable.AddColumn("Description");
|
||||||
|
|
||||||
|
foreach (var option in a.Value.ListOfOptions)
|
||||||
|
{
|
||||||
|
|
||||||
|
optionsTable.AddRow(option.OptionName, option.OptionDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
tableData.AddRow([actionName, usage, description, optionsTable]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tableData.AddRow([actionName, usage, description]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// render the table
|
||||||
|
tableData.HasRoundBorders = true;
|
||||||
|
tableData.DisplayLinesBetweenRows = true;
|
||||||
|
tableData.PrintTable();
|
||||||
|
|
||||||
items.Add(new[] { "-", "-", "-" });
|
|
||||||
|
|
||||||
Utilities.Utilities.FormatAndAlignTable(items,
|
|
||||||
TableFormat.CENTER_EACH_COLUMN_BASED
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,17 +75,10 @@ public class Help : ICommandAction
|
|||||||
}
|
}
|
||||||
|
|
||||||
var action = Program.internalActionManager.Actions[args[0]];
|
var action = Program.internalActionManager.Actions[args[0]];
|
||||||
var actionData = new List<string[]>
|
tableData.Columns = ["Command", "Usage", "Description"];
|
||||||
{
|
tableData.AddRow([action.ActionName, action.Usage, action.Description]);
|
||||||
new[] { "-", "-", "-" },
|
|
||||||
new[] { "Command", "Usage", "Description" },
|
|
||||||
new[] { "-", "-", "-" },
|
|
||||||
new[] { action.ActionName, action.Usage, action.Description },
|
|
||||||
new[] { "-", "-", "-" }
|
|
||||||
};
|
|
||||||
|
|
||||||
Utilities.Utilities.FormatAndAlignTable(actionData,
|
|
||||||
TableFormat.CENTER_EACH_COLUMN_BASED
|
tableData.PrintTable();
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using DiscordBot.Utilities;
|
using DiscordBot.Bot.Actions.Extra;
|
||||||
using PluginManager;
|
using PluginManager;
|
||||||
using PluginManager.Interfaces;
|
using PluginManager.Interfaces;
|
||||||
using PluginManager.Loaders;
|
|
||||||
using PluginManager.Online;
|
|
||||||
using PluginManager.Others;
|
using PluginManager.Others;
|
||||||
|
using PluginManager.Others.Actions;
|
||||||
|
|
||||||
namespace DiscordBot.Bot.Actions;
|
namespace DiscordBot.Bot.Actions;
|
||||||
|
|
||||||
@@ -15,7 +14,18 @@ public class Plugin : ICommandAction
|
|||||||
private bool pluginsLoaded;
|
private bool pluginsLoaded;
|
||||||
public string ActionName => "plugin";
|
public string ActionName => "plugin";
|
||||||
public string Description => "Manages plugins. Use plugin help for more info.";
|
public string Description => "Manages plugins. Use plugin help for more info.";
|
||||||
public string Usage => "plugin [help|list|load|install]";
|
public string Usage => "plugin <option!>";
|
||||||
|
|
||||||
|
public IEnumerable<InternalActionOption> ListOfOptions => new List<InternalActionOption>
|
||||||
|
{
|
||||||
|
new InternalActionOption("help", "Displays this message"),
|
||||||
|
new InternalActionOption("list", "Lists all plugins"),
|
||||||
|
new InternalActionOption("load", "Loads all plugins"),
|
||||||
|
new InternalActionOption("install", "Installs a plugin"),
|
||||||
|
new InternalActionOption("refresh", "Refreshes the plugin list"),
|
||||||
|
new InternalActionOption("uninstall", "Uninstalls a plugin")
|
||||||
|
};
|
||||||
|
|
||||||
public InternalActionRunType RunType => InternalActionRunType.ON_CALL;
|
public InternalActionRunType RunType => InternalActionRunType.ON_CALL;
|
||||||
|
|
||||||
public async Task Execute(string[] args)
|
public async Task Execute(string[] args)
|
||||||
@@ -27,103 +37,41 @@ public class Plugin : ICommandAction
|
|||||||
Console.WriteLine("list : Lists all plugins");
|
Console.WriteLine("list : Lists all plugins");
|
||||||
Console.WriteLine("load : Loads all plugins");
|
Console.WriteLine("load : Loads all plugins");
|
||||||
Console.WriteLine("install : Installs a plugin");
|
Console.WriteLine("install : Installs a plugin");
|
||||||
|
Console.WriteLine("refresh : Refreshes the plugin list");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (args[0])
|
switch (args[0])
|
||||||
{
|
{
|
||||||
case "list":
|
case "refresh":
|
||||||
var manager =
|
await PluginMethods.RefreshPlugins(true);
|
||||||
new PluginsManager(Program.URLs["PluginList"], Program.URLs["PluginVersions"]);
|
|
||||||
|
|
||||||
var data = await manager.GetAvailablePlugins();
|
|
||||||
var items = new List<string[]>
|
|
||||||
{
|
|
||||||
new[] { "-", "-", "-", "-" },
|
|
||||||
new[] { "Name", "Description", "Type", "Version" },
|
|
||||||
new[] { "-", "-", "-", "-" }
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var plugin in data) items.Add(new[] { plugin[0], plugin[1], plugin[2], plugin[3] });
|
|
||||||
|
|
||||||
items.Add(new[] { "-", "-", "-", "-" });
|
|
||||||
|
|
||||||
Utilities.Utilities.FormatAndAlignTable(items, TableFormat.DEFAULT);
|
|
||||||
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":
|
||||||
|
await PluginMethods.List(Config.PluginsManager);
|
||||||
|
break;
|
||||||
case "load":
|
case "load":
|
||||||
if (pluginsLoaded)
|
if (pluginsLoaded)
|
||||||
|
{
|
||||||
|
Config.Logger.Log("Plugins already loaded", typeof(ICommandAction), LogType.WARNING);
|
||||||
break;
|
break;
|
||||||
var loader = new PluginLoader(Config.DiscordBot.client);
|
|
||||||
var cc = Console.ForegroundColor;
|
|
||||||
loader.onCMDLoad += (name, typeName, success, exception) =>
|
|
||||||
{
|
|
||||||
if (name == null || name.Length < 2)
|
|
||||||
name = typeName;
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
Console.ForegroundColor = ConsoleColor.Green;
|
|
||||||
Console.WriteLine("[CMD] Successfully loaded command : " + name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
if (Config.DiscordBot is null)
|
||||||
{
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.Red;
|
Config.Logger.Log("DiscordBot is null", typeof(ICommandAction), LogType.WARNING);
|
||||||
if (exception is null)
|
break;
|
||||||
Console.WriteLine("An error occured while loading: " + name);
|
|
||||||
else
|
|
||||||
Console.WriteLine("[CMD] Failed to load command : " + name + " because " +
|
|
||||||
exception!.Message
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.ForegroundColor = cc;
|
pluginsLoaded = await PluginMethods.LoadPlugins(args);
|
||||||
};
|
|
||||||
loader.onEVELoad += (name, typeName, success, exception) =>
|
|
||||||
{
|
|
||||||
if (name == null || name.Length < 2)
|
|
||||||
name = typeName;
|
|
||||||
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
Console.ForegroundColor = ConsoleColor.Green;
|
|
||||||
Console.WriteLine("[EVENT] Successfully loaded event : " + name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.ForegroundColor = ConsoleColor.Red;
|
|
||||||
Console.WriteLine("[EVENT] Failed to load event : " + name + " because " + exception!.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.ForegroundColor = cc;
|
|
||||||
};
|
|
||||||
|
|
||||||
loader.onSLSHLoad += (name, typeName, success, exception) =>
|
|
||||||
{
|
|
||||||
if (name == null || name.Length < 2)
|
|
||||||
name = typeName;
|
|
||||||
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
Console.ForegroundColor = ConsoleColor.Green;
|
|
||||||
Console.WriteLine("[SLASH] Successfully loaded command : " + name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.ForegroundColor = ConsoleColor.Red;
|
|
||||||
Console.WriteLine("[SLASH] Failed to load command : " + name + " because " +
|
|
||||||
exception!.Message
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.ForegroundColor = cc;
|
|
||||||
};
|
|
||||||
|
|
||||||
loader.LoadPlugins();
|
|
||||||
Console.ForegroundColor = cc;
|
|
||||||
pluginsLoaded = true;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "install":
|
case "install":
|
||||||
@@ -140,57 +88,7 @@ public class Plugin : ICommandAction
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var pluginManager =
|
await PluginMethods.DownloadPlugin(Config.PluginsManager, pluginName);
|
||||||
new PluginsManager(Program.URLs["PluginList"], Program.URLs["PluginVersions"]);
|
|
||||||
var pluginData = await pluginManager.GetPluginLinkByName(pluginName);
|
|
||||||
if (pluginData == null || pluginData.Length == 0)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Plugin {pluginName} not found. Please check the spelling and try again.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pluginType = pluginData[0];
|
|
||||||
var pluginLink = pluginData[1];
|
|
||||||
var pluginRequirements = pluginData[2];
|
|
||||||
|
|
||||||
|
|
||||||
Console.WriteLine("Downloading plugin...");
|
|
||||||
//download plugin progress bar for linux and windows terminals
|
|
||||||
var spinner = new Utilities.Utilities.Spinner();
|
|
||||||
spinner.Start();
|
|
||||||
IProgress<float> progress = new Progress<float>(p => { spinner.Message = $"Downloading {pluginName}... {Math.Round(p, 2)}% "; });
|
|
||||||
await ServerCom.DownloadFileAsync(pluginLink, $"./Data/{pluginType}s/{pluginName}.dll", progress);
|
|
||||||
spinner.Stop();
|
|
||||||
Console.WriteLine();
|
|
||||||
|
|
||||||
if (pluginRequirements == string.Empty)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Plugin installed successfully");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("Downloading plugin requirements...");
|
|
||||||
var requirementsURLs = await ServerCom.ReadTextFromURL(pluginRequirements);
|
|
||||||
|
|
||||||
foreach (var requirement in requirementsURLs)
|
|
||||||
{
|
|
||||||
if (requirement.Length < 2)
|
|
||||||
continue;
|
|
||||||
var reqdata = requirement.Split(',');
|
|
||||||
var url = reqdata[0];
|
|
||||||
var filename = reqdata[1];
|
|
||||||
|
|
||||||
Console.WriteLine($"Downloading {filename}... ");
|
|
||||||
spinner.Start();
|
|
||||||
|
|
||||||
await ServerCom.DownloadFileAsync(url, $"./{filename}.dll", null);
|
|
||||||
spinner.Stop();
|
|
||||||
await Task.Delay(1000);
|
|
||||||
Console.WriteLine("Downloaded " + filename + " successfully");
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("Finished installing " + pluginName + " successfully");
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
76
DiscordBot/Bot/Actions/SettingsConfig.cs
Normal file
76
DiscordBot/Bot/Actions/SettingsConfig.cs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using DiscordBot.Bot.Actions.Extra;
|
||||||
|
using PluginManager;
|
||||||
|
using PluginManager.Interfaces;
|
||||||
|
using PluginManager.Others;
|
||||||
|
using PluginManager.Others.Actions;
|
||||||
|
|
||||||
|
namespace DiscordBot.Bot.Actions;
|
||||||
|
|
||||||
|
public class SettingsConfig: ICommandAction
|
||||||
|
{
|
||||||
|
public string ActionName => "config";
|
||||||
|
public string Description => "Change the settings of the bot";
|
||||||
|
public string Usage => "config <options!>";
|
||||||
|
public IEnumerable<InternalActionOption> ListOfOptions => new List<InternalActionOption>
|
||||||
|
{
|
||||||
|
new InternalActionOption("help", "Displays this message"),
|
||||||
|
new InternalActionOption("set", "Set a setting"),
|
||||||
|
new InternalActionOption("remove", "Remove a setting"),
|
||||||
|
new InternalActionOption("add", "Add a setting")
|
||||||
|
};
|
||||||
|
public InternalActionRunType RunType => InternalActionRunType.ON_CALL;
|
||||||
|
public Task Execute(string[] args)
|
||||||
|
{
|
||||||
|
if (args is null)
|
||||||
|
{
|
||||||
|
foreach (var settings in Config.AppSettings)
|
||||||
|
Console.WriteLine(settings.Key + ": " + settings.Value);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (args[0])
|
||||||
|
{
|
||||||
|
case "-s":
|
||||||
|
case "set":
|
||||||
|
if (args.Length < 3)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
SettingsConfigExtra.SetSettings(args[1], args[2..]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "-r":
|
||||||
|
case "remove":
|
||||||
|
if (args.Length < 2)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
SettingsConfigExtra.RemoveSettings(args[1]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "-a":
|
||||||
|
case "add":
|
||||||
|
if (args.Length < 3)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
SettingsConfigExtra.AddSettings(args[1], args[2..]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "-h":
|
||||||
|
case "-help":
|
||||||
|
Console.WriteLine("Options:");
|
||||||
|
Console.WriteLine("-s <settingName> <newValue>: Set a setting");
|
||||||
|
Console.WriteLine("-r <settingName>: Remove a setting");
|
||||||
|
Console.WriteLine("-a <settingName> <newValue>: Add a setting");
|
||||||
|
Console.WriteLine("-h: Show this help message");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Console.WriteLine("Invalid option");
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@ internal class Help : DBCommand
|
|||||||
/// The main body of the command
|
/// The main body of the command
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The command context</param>
|
/// <param name="context">The command context</param>
|
||||||
public void ExecuteServer(DBCommandExecutingArguments args)
|
public void ExecuteServer(DbCommandExecutingArguments args)
|
||||||
{
|
{
|
||||||
if (args.arguments is not null)
|
if (args.arguments is not null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ public class Help : DBSlashCommand
|
|||||||
public string Description => "This command allows you to check all loaded commands";
|
public string Description => "This command allows you to check all loaded commands";
|
||||||
public bool canUseDM => true;
|
public bool canUseDM => true;
|
||||||
|
|
||||||
|
public bool HasInteraction => false;
|
||||||
|
|
||||||
public List<SlashCommandOptionBuilder> Options =>
|
public List<SlashCommandOptionBuilder> Options =>
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
@@ -40,15 +42,15 @@ public class Help : DBSlashCommand
|
|||||||
|
|
||||||
if (options.Count > 0)
|
if (options.Count > 0)
|
||||||
{
|
{
|
||||||
var commandName = options.First().Name;
|
var commandName = options.First().Value;
|
||||||
var slashCommand = slashCommands.FirstOrDefault(x => x.Name == commandName);
|
var slashCommand = slashCommands.FirstOrDefault(x => x.Name.TrimEnd() == commandName.ToString());
|
||||||
if (slashCommand is null)
|
if (slashCommand is null)
|
||||||
{
|
{
|
||||||
await context.RespondAsync("Unknown Command " + commandName);
|
await context.RespondAsync("Unknown Command " + commandName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
embedBuilder.AddField(slashCommand.Name, slashCommand.canUseDM)
|
embedBuilder.AddField("DM Usable:", slashCommand.canUseDM, true)
|
||||||
.WithDescription(slashCommand.Description);
|
.WithDescription(slashCommand.Description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Nullable>disable</Nullable>
|
<Nullable>disable</Nullable>
|
||||||
<ApplicationIcon />
|
<ApplicationIcon />
|
||||||
<StartupObject />
|
<StartupObject />
|
||||||
<SignAssembly>False</SignAssembly>
|
<SignAssembly>False</SignAssembly>
|
||||||
<IsPublishable>True</IsPublishable>
|
<IsPublishable>True</IsPublishable>
|
||||||
<AssemblyVersion>1.0.2.2</AssemblyVersion>
|
<AssemblyVersion>1.0.4.0</AssemblyVersion>
|
||||||
|
<PublishAot>False</PublishAot>
|
||||||
|
<FileVersion>1.0.4.0</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<DebugType>none</DebugType>
|
<DebugType>none</DebugType>
|
||||||
@@ -33,8 +35,7 @@
|
|||||||
<None Remove="builder.sh" />
|
<None Remove="builder.sh" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Discord.Net" Version="3.11.0"/>
|
<PackageReference Include="Spectre.Console" Version="0.49.1" />
|
||||||
<PackageReference Include="Sodium.Core" Version="1.3.3"/>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\PluginManager\PluginManager.csproj" />
|
<ProjectReference Include="..\PluginManager\PluginManager.csproj" />
|
||||||
|
|||||||
@@ -2,20 +2,45 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
|
|
||||||
namespace DiscordBot;
|
namespace DiscordBot;
|
||||||
|
|
||||||
public class Entry
|
public static class Entry
|
||||||
{
|
{
|
||||||
|
private static readonly string logo = @"
|
||||||
|
_____ _ _ _____ _ _ ____ _
|
||||||
|
/ ____| | | | | | __ \(_) | | | _ \ | |
|
||||||
|
| (___ ___| |_| |__ | | | |_ ___ ___ ___ _ __ __| | | |_) | ___ | |_
|
||||||
|
\___ \ / _ \ __| '_ \ | | | | / __|/ __/ _ \| '__/ _` | | _ < / _ \| __|
|
||||||
|
____) | __/ |_| | | | | |__| | \__ \ (_| (_) | | | (_| | | |_) | (_) | |_
|
||||||
|
|_____/ \___|\__|_| |_| |_____/|_|___/\___\___/|_| \__,_| |____/ \___/ \__|
|
||||||
|
|
||||||
|
|
||||||
|
";
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
|
if (args.Length == 1 && args[0] == "/purge_plugins" )
|
||||||
|
{
|
||||||
|
foreach (var plugin in Directory.GetFiles("./Data/Plugins", "*.dll", SearchOption.AllDirectories))
|
||||||
|
{
|
||||||
|
File.Delete(plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||||
|
Console.WriteLine(logo);
|
||||||
|
Console.ResetColor();
|
||||||
|
|
||||||
|
|
||||||
var currentDomain = AppDomain.CurrentDomain;
|
var currentDomain = AppDomain.CurrentDomain;
|
||||||
currentDomain.AssemblyResolve += LoadFromSameFolder;
|
currentDomain.AssemblyResolve += LoadFromSameFolder;
|
||||||
|
|
||||||
static Assembly LoadFromSameFolder(object sender, ResolveEventArgs args)
|
static Assembly LoadFromSameFolder(object sender, ResolveEventArgs args)
|
||||||
{
|
{
|
||||||
var folderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
|
var folderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "./Libraries");
|
||||||
"./Libraries"
|
|
||||||
);
|
|
||||||
var assemblyPath = Path.Combine(folderPath, new AssemblyName(args.Name).Name + ".dll");
|
var assemblyPath = Path.Combine(folderPath, new AssemblyName(args.Name).Name + ".dll");
|
||||||
if (!File.Exists(assemblyPath)) return null;
|
if (!File.Exists(assemblyPath)) return null;
|
||||||
var assembly = Assembly.LoadFrom(assemblyPath);
|
var assembly = Assembly.LoadFrom(assemblyPath);
|
||||||
|
|||||||
@@ -1,64 +1,39 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using PluginManager;
|
using PluginManager;
|
||||||
using PluginManager.Online;
|
using System.Threading.Tasks;
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
namespace DiscordBot;
|
namespace DiscordBot;
|
||||||
|
|
||||||
public static class Installer
|
public static class Installer
|
||||||
{
|
{
|
||||||
public static void GenerateStartupConfig()
|
private static async Task AskForConfig(string key, string message)
|
||||||
|
{
|
||||||
|
var value = AnsiConsole.Ask<string>($"[green]{message}[/]");
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
AnsiConsole.MarkupLine($"Invalid {key} !");
|
||||||
|
|
||||||
|
Environment.Exit(-20);
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.AppSettings.Add(key, value);
|
||||||
|
}
|
||||||
|
public static async Task GenerateStartupConfig()
|
||||||
{
|
{
|
||||||
Console.WriteLine("Welcome to the SethBot installer !");
|
|
||||||
Console.WriteLine("First, we need to configure the bot. Don't worry, it will be quick !");
|
|
||||||
Console.WriteLine("The following information will be stored in the config.json file in the ./Data/Resources folder. You can change it later from there.");
|
|
||||||
Console.WriteLine("The bot token is required to run the bot. You can get it from the Discord Developer Portal. (https://discord.com/developers/applications)");
|
|
||||||
|
|
||||||
if(!Config.AppSettings.ContainsKey("token"))
|
if(!Config.AppSettings.ContainsKey("token"))
|
||||||
{
|
await AskForConfig("token", "Token:");
|
||||||
Console.WriteLine("Please enter the bot token :");
|
|
||||||
var token = Console.ReadLine();
|
|
||||||
Config.AppSettings.Add("token", token);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!Config.AppSettings.ContainsKey("prefix"))
|
if(!Config.AppSettings.ContainsKey("prefix"))
|
||||||
{
|
await AskForConfig("prefix", "Prefix:");
|
||||||
Console.WriteLine("Please enter the bot prefix :");
|
|
||||||
var prefix = Console.ReadLine();
|
|
||||||
Config.AppSettings.Add("prefix", prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!Config.AppSettings.ContainsKey("ServerID"))
|
if(!Config.AppSettings.ContainsKey("ServerID"))
|
||||||
{
|
await AskForConfig("ServerID", "Server ID:");
|
||||||
Console.WriteLine("Please enter the Server ID :");
|
|
||||||
var serverId = Console.ReadLine();
|
|
||||||
Config.AppSettings.Add("ServerID", serverId);
|
|
||||||
}
|
|
||||||
|
|
||||||
Config.Logger.Log("Config Saved", "Installer", isInternal: true);
|
await Config.AppSettings.SaveToFile();
|
||||||
|
|
||||||
Config.AppSettings.SaveToFile();
|
Config.Logger.Log("Config Saved", typeof(Installer));
|
||||||
|
|
||||||
Console.WriteLine("Config saved !");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task SetupPluginDatabase()
|
|
||||||
{
|
|
||||||
Console.WriteLine("The plugin database is required to run the bot but there is nothing configured yet.");
|
|
||||||
Console.WriteLine("Downloading the default database...");
|
|
||||||
await DownloadPluginDatabase();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task DownloadPluginDatabase(
|
|
||||||
string url = "https://raw.githubusercontent.com/andreitdr/SethDiscordBot/gh-pages/defaultURLs.json")
|
|
||||||
{
|
|
||||||
var path = "./Data/Resources/URLs.json";
|
|
||||||
|
|
||||||
Directory.CreateDirectory("./Data/Resources");
|
|
||||||
var spinner = new Utilities.Utilities.Spinner();
|
|
||||||
spinner.Start();
|
|
||||||
await ServerCom.DownloadFileAsync(url, path, null);
|
|
||||||
spinner.Stop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +1,32 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using DiscordBot.Utilities;
|
||||||
using PluginManager.Bot;
|
using PluginManager.Bot;
|
||||||
using PluginManager.Online;
|
|
||||||
using PluginManager.Online.Helpers;
|
|
||||||
using PluginManager.Others;
|
using PluginManager.Others;
|
||||||
using PluginManager.Others.Actions;
|
using PluginManager.Others.Actions;
|
||||||
|
using Spectre.Console;
|
||||||
using static PluginManager.Config;
|
using static PluginManager.Config;
|
||||||
|
|
||||||
namespace DiscordBot;
|
namespace DiscordBot;
|
||||||
|
|
||||||
public class Program
|
public class Program
|
||||||
{
|
{
|
||||||
public static SettingsDictionary<string, string> URLs;
|
|
||||||
public static InternalActionManager internalActionManager;
|
public static InternalActionManager internalActionManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The main entry point for the application.
|
/// The main entry point for the application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[STAThread]
|
|
||||||
public static void Startup(string[] args)
|
public static void Startup(string[] args)
|
||||||
{
|
{
|
||||||
PreLoadComponents(args).Wait();
|
PreLoadComponents(args).Wait();
|
||||||
|
|
||||||
if (!AppSettings.ContainsKey("ServerID") || !AppSettings.ContainsKey("token") ||
|
if (!AppSettings.ContainsKey("ServerID") || !AppSettings.ContainsKey("token") || !AppSettings.ContainsKey("prefix"))
|
||||||
AppSettings["token"] == null ||
|
Installer.GenerateStartupConfig().Wait();
|
||||||
AppSettings["token"]?.Length != 70 && AppSettings["token"]?.Length != 59 ||
|
|
||||||
!AppSettings.ContainsKey("prefix") || AppSettings["prefix"] == null ||
|
|
||||||
AppSettings["prefix"]?.Length != 1 ||
|
|
||||||
args.Length == 1 && args[0] == "/reset")
|
|
||||||
Installer.GenerateStartupConfig();
|
|
||||||
|
|
||||||
HandleInput(args.ToList()).Wait();
|
HandleInput().Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -42,10 +34,9 @@ public class Program
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private static void NoGUI()
|
private static void NoGUI()
|
||||||
{
|
{
|
||||||
#if DEBUG
|
internalActionManager.Initialize().Wait();
|
||||||
Console.WriteLine("Debug mode enabled");
|
internalActionManager.Execute("plugin", "load").Wait();
|
||||||
internalActionManager.Execute("plugin", "load").Wait(); // Load plugins at startup
|
internalActionManager.Refresh().Wait();
|
||||||
#endif
|
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@@ -64,97 +55,49 @@ public class Program
|
|||||||
/// Start the bot without user interface
|
/// Start the bot without user interface
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Returns the bootloader for the Discord Bot</returns>
|
/// <returns>Returns the bootloader for the Discord Bot</returns>
|
||||||
private static async Task<Boot> StartNoGui()
|
private static async Task StartNoGui()
|
||||||
{
|
{
|
||||||
Console.Clear();
|
|
||||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
|
||||||
|
|
||||||
var startupMessageList =
|
AnsiConsole.MarkupLine($"[yellow]Running on version: {AppSettings["Version"]}[/]");
|
||||||
await ServerCom.ReadTextFromURL(URLs["StartupMessage"]);
|
AnsiConsole.MarkupLine("[yellow]Git SethBot: https://github.com/andreitdr/SethDiscordBot [/]");
|
||||||
|
AnsiConsole.MarkupLine("[yellow]Git Plugins: https://github.com/andreitdr/SethPlugins [/]");
|
||||||
|
|
||||||
foreach (var message in startupMessageList)
|
AnsiConsole.MarkupLine("[yellow]Remember to close the bot using the shutdown command ([/][red]exit[/][yellow]) or some settings won't be saved[/]");
|
||||||
Console.WriteLine(message);
|
AnsiConsole.MarkupLine($"[yellow]Running on [/][magenta]{(OperatingSystem.IsWindows() ? "Windows" : "Linux")}[/]");
|
||||||
|
|
||||||
Console.WriteLine(
|
AnsiConsole.MarkupLine("[yellow]===== Seth Discord Bot =====[/]");
|
||||||
$"Running on version: {Assembly.GetExecutingAssembly().GetName().Version}"
|
|
||||||
);
|
|
||||||
Console.WriteLine($"Git URL: {AppSettings["GitURL"]}");
|
|
||||||
|
|
||||||
Utilities.Utilities.WriteColorText(
|
|
||||||
"&rRemember to close the bot using the ShutDown command (&ysd&r) or some settings won't be saved\n"
|
|
||||||
);
|
|
||||||
Console.ForegroundColor = ConsoleColor.White;
|
|
||||||
|
|
||||||
if (AppSettings.ContainsKey("LaunchMessage"))
|
|
||||||
Utilities.Utilities.WriteColorText(AppSettings["LaunchMessage"]);
|
|
||||||
|
|
||||||
|
|
||||||
Utilities.Utilities.WriteColorText(
|
|
||||||
"Please note that the bot saves a backup save file every time you are using the shudown command (&ysd&c)"
|
|
||||||
);
|
|
||||||
|
|
||||||
Console.WriteLine("Running on " + Functions.GetOperatingSystem());
|
|
||||||
Console.WriteLine("============================ LOG ============================");
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var token = "";
|
var token = AppSettings["token"];
|
||||||
#if DEBUG
|
|
||||||
if (File.Exists("./Data/Resources/token.txt")) token = File.ReadAllText("./Data/Resources/token.txt");
|
|
||||||
else token = AppSettings["token"];
|
|
||||||
#else
|
|
||||||
token = AppSettings["token"];
|
|
||||||
#endif
|
|
||||||
var prefix = AppSettings["prefix"];
|
var prefix = AppSettings["prefix"];
|
||||||
var discordbooter = new Boot(token, prefix);
|
var discordbooter = new Boot(token, prefix);
|
||||||
await discordbooter.Awake();
|
await discordbooter.Awake();
|
||||||
return discordbooter;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.Log(ex.ToString(), "Bot", LogLevel.ERROR);
|
Logger.Log(ex.ToString(), typeof(Program), LogType.CRITICAL);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle user input arguments from the startup of the application
|
/// Handle user input arguments from the startup of the application
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="args">The arguments</param>
|
private static async Task HandleInput()
|
||||||
private static async Task HandleInput(List<string> args)
|
|
||||||
{
|
{
|
||||||
Console.WriteLine("Loading Core ...");
|
await StartNoGui();
|
||||||
|
|
||||||
//Handle arguments here:
|
|
||||||
|
|
||||||
if (args.Contains("--gui"))
|
|
||||||
{
|
|
||||||
// GUI not implemented yet
|
|
||||||
Console.WriteLine("GUI not implemented yet");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Starting bot after all arguments are handled
|
|
||||||
|
|
||||||
var b = await StartNoGui();
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
internalActionManager = new InternalActionManager("./Data/Actions", "*.dll");
|
internalActionManager = new InternalActionManager(AppSettings["PluginFolder"], "*.dll");
|
||||||
await internalActionManager.Initialize();
|
|
||||||
|
|
||||||
NoGUI();
|
NoGUI();
|
||||||
}
|
}
|
||||||
catch (IOException ex)
|
catch (IOException ex)
|
||||||
{
|
{
|
||||||
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)
|
||||||
{
|
{
|
||||||
if (AppSettings.ContainsKey("LaunchMessage"))
|
Logger.Log("An error occured while closing the bot last time. Please consider closing the bot using the &rexit&c method !\n" +
|
||||||
AppSettings.Add("LaunchMessage",
|
"There is a risk of losing all data or corruption of the save file, which in some cases requires to reinstall the bot !",
|
||||||
"An error occured while closing the bot last time. Please consider closing the bot using the &rsd&c method !\nThere 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
|
||||||
);
|
|
||||||
Logger
|
|
||||||
.Log("An error occured while closing the bot last time. Please consider closing the bot using the &rsd&c method !\nThere is a risk of losing all data or corruption of the save file, which in some cases requires to reinstall the bot !",
|
|
||||||
"Bot", LogLevel.ERROR
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,84 +107,38 @@ public class Program
|
|||||||
{
|
{
|
||||||
await Initialize();
|
await Initialize();
|
||||||
|
|
||||||
Logger.LogEvent += (message, type, isInternal) =>
|
|
||||||
{
|
|
||||||
if (type == LogLevel.INFO)
|
|
||||||
Console.ForegroundColor = ConsoleColor.Green;
|
|
||||||
else if (type == LogLevel.WARNING)
|
|
||||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
|
||||||
else if (type == LogLevel.ERROR)
|
|
||||||
Console.ForegroundColor = ConsoleColor.Red;
|
|
||||||
else if (type == LogLevel.CRITICAL)
|
|
||||||
Console.ForegroundColor = ConsoleColor.DarkRed;
|
|
||||||
|
|
||||||
Console.WriteLine($"[{type.ToString()}] {message}");
|
|
||||||
Console.ResetColor();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!Directory.Exists("./Data/Resources") || !File.Exists("./Data/Resources/URLs.json"))
|
|
||||||
await Installer.SetupPluginDatabase();
|
|
||||||
|
|
||||||
|
|
||||||
URLs = new SettingsDictionary<string, string>("./Data/Resources/URLs.json");
|
|
||||||
|
|
||||||
|
|
||||||
Console.WriteLine("Loading resources ...");
|
|
||||||
|
|
||||||
if (AppSettings.ContainsKey("DeleteLogsAtStartup"))
|
|
||||||
if (AppSettings["DeleteLogsAtStartup"] == "true")
|
|
||||||
foreach (var file in Directory.GetFiles("./Output/Logs/"))
|
|
||||||
File.Delete(file);
|
|
||||||
|
|
||||||
var OnlineDefaultKeys = await ServerCom.ReadTextFromURL(URLs["SetupKeys"]);
|
|
||||||
|
|
||||||
|
|
||||||
AppSettings["Version"] = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
AppSettings["Version"] = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||||
|
|
||||||
foreach (var key in OnlineDefaultKeys)
|
PluginManager.Updater.Application.AppUpdater updater = new();
|
||||||
|
var update = await updater.CheckForUpdates();
|
||||||
|
|
||||||
|
if (update != PluginManager.Updater.Application.Update.None)
|
||||||
{
|
{
|
||||||
if (key.Length <= 3 || !key.Contains(' ')) continue;
|
Console.WriteLine($"New update available: {update.UpdateVersion}");
|
||||||
var s = key.Split(' ');
|
Console.WriteLine($"Download link: {update.UpdateUrl}");
|
||||||
try
|
Console.WriteLine($"Update notes: {update.UpdateNotes}\n\n");
|
||||||
{
|
|
||||||
AppSettings[s[0]] = s[1];
|
Environment.Exit(0);
|
||||||
}
|
|
||||||
catch ( Exception ex )
|
|
||||||
{
|
|
||||||
Logger.Log(ex.ToString(), "Bot", LogLevel.ERROR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var onlineSettingsList = await ServerCom.ReadTextFromURL(URLs["Versions"]);
|
Logger.OnLog += (sender, logMessage) =>
|
||||||
foreach (var key in onlineSettingsList)
|
|
||||||
{
|
{
|
||||||
if (key.Length <= 3 || !key.Contains(' ')) continue;
|
var messageColor = logMessage.Type switch
|
||||||
|
|
||||||
var s = key.Split(' ');
|
|
||||||
switch ( s[0] )
|
|
||||||
{
|
{
|
||||||
case "CurrentVersion":
|
LogType.INFO => "[green]",
|
||||||
var currentVersion = AppSettings["Version"];
|
LogType.WARNING => "[yellow]",
|
||||||
var newVersion = s[1];
|
LogType.ERROR => "[red]",
|
||||||
if (new VersionString(newVersion) != new VersionString(newVersion))
|
LogType.CRITICAL => "[red]",
|
||||||
|
_ => "[white]"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (logMessage.Message.Contains('['))
|
||||||
{
|
{
|
||||||
Console.WriteLine("A new updated was found. Check the changelog for more information.");
|
Console.WriteLine(logMessage.Message);
|
||||||
var changeLog = await ServerCom.ReadTextFromURL(URLs["Changelog"]);
|
return;
|
||||||
foreach (var item in changeLog)
|
|
||||||
Utilities.Utilities.WriteColorText(item);
|
|
||||||
Console.WriteLine("Current version: " + currentVersion);
|
|
||||||
Console.WriteLine("Latest version: " + newVersion);
|
|
||||||
|
|
||||||
Console.WriteLine("Download from here: https://github.com/andreitdr/SethDiscordBot/releases");
|
|
||||||
|
|
||||||
Console.WriteLine("Press any key to continue ...");
|
|
||||||
Console.ReadKey();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
AnsiConsole.MarkupLine($"{messageColor}{logMessage.ThrowTime} {logMessage.Message} [/]");
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
Console.Clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,243 +1,31 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
namespace DiscordBot.Utilities;
|
namespace DiscordBot.Utilities;
|
||||||
|
|
||||||
public static class Utilities
|
public static class ConsoleUtilities
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<char, ConsoleColor> Colors = new()
|
public static async Task<T> ExecuteWithProgressBar<T>(Task<T> function, string message)
|
||||||
{
|
{
|
||||||
{ 'g', ConsoleColor.Green },
|
T result = default;
|
||||||
{ 'b', ConsoleColor.Blue },
|
await AnsiConsole.Progress()
|
||||||
{ 'r', ConsoleColor.Red },
|
.AutoClear(true)
|
||||||
{ 'm', ConsoleColor.Magenta },
|
.Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn())
|
||||||
{ 'y', ConsoleColor.Yellow }
|
.StartAsync(
|
||||||
};
|
async ctx =>
|
||||||
|
|
||||||
private static readonly char ColorPrefix = '&';
|
|
||||||
|
|
||||||
|
|
||||||
private static bool CanAproximateTo(this float f, float y)
|
|
||||||
{
|
{
|
||||||
return MathF.Abs(f - y) < 0.000001;
|
var task = ctx.AddTask(message);
|
||||||
}
|
task.IsIndeterminate = true;
|
||||||
|
result = await function;
|
||||||
|
task.Increment(100);
|
||||||
/// <summary>
|
|
||||||
/// A way to create a table based on input data
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data">The List of arrays of strings that represent the rows.</param>
|
|
||||||
public static void FormatAndAlignTable(List<string[]> data, TableFormat format)
|
|
||||||
{
|
|
||||||
if (format == TableFormat.CENTER_EACH_COLUMN_BASED)
|
|
||||||
{
|
|
||||||
var tableLine = '-';
|
|
||||||
var tableCross = '+';
|
|
||||||
var tableWall = '|';
|
|
||||||
|
|
||||||
var len = new int[data[0].Length];
|
|
||||||
foreach (var line in data)
|
|
||||||
for (var i = 0; i < line.Length; i++)
|
|
||||||
if (line[i].Length > len[i])
|
|
||||||
len[i] = line[i].Length;
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var row in data)
|
|
||||||
{
|
|
||||||
if (row[0][0] == tableLine)
|
|
||||||
Console.Write(tableCross);
|
|
||||||
else
|
|
||||||
Console.Write(tableWall);
|
|
||||||
for (var l = 0; l < row.Length; l++)
|
|
||||||
{
|
|
||||||
if (row[l][0] == tableLine)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < len[l] + 4; ++i)
|
|
||||||
Console.Write(tableLine);
|
|
||||||
}
|
|
||||||
else if (row[l].Length == len[l])
|
|
||||||
{
|
|
||||||
Console.Write(" ");
|
|
||||||
Console.Write(row[l]);
|
|
||||||
Console.Write(" ");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var lenHalf = row[l].Length / 2;
|
|
||||||
for (var i = 0; i < (len[l] + 4) / 2 - lenHalf; ++i)
|
|
||||||
Console.Write(" ");
|
|
||||||
Console.Write(row[l]);
|
|
||||||
for (var i = (len[l] + 4) / 2 + lenHalf + 1; i < len[l] + 4; ++i)
|
|
||||||
Console.Write(" ");
|
|
||||||
if (row[l].Length % 2 == 0)
|
|
||||||
Console.Write(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.Write(row[l][0] == tableLine ? tableCross : tableWall);
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine(); //end line
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (format == TableFormat.CENTER_OVERALL_LENGTH)
|
|
||||||
{
|
|
||||||
var maxLen = 0;
|
|
||||||
foreach (var row in data)
|
|
||||||
foreach (var s in row)
|
|
||||||
if (s.Length > maxLen)
|
|
||||||
maxLen = s.Length;
|
|
||||||
|
|
||||||
var div = (maxLen + 4) / 2;
|
|
||||||
|
|
||||||
foreach (var row in data)
|
|
||||||
{
|
|
||||||
Console.Write("\t");
|
|
||||||
if (row[0] == "-")
|
|
||||||
Console.Write("+");
|
|
||||||
else
|
|
||||||
Console.Write("|");
|
|
||||||
|
|
||||||
foreach (var s in row)
|
|
||||||
{
|
|
||||||
if (s == "-")
|
|
||||||
{
|
|
||||||
for (var i = 0; i < maxLen + 4; ++i)
|
|
||||||
Console.Write("-");
|
|
||||||
}
|
|
||||||
else if (s.Length == maxLen)
|
|
||||||
{
|
|
||||||
Console.Write(" ");
|
|
||||||
Console.Write(s);
|
|
||||||
Console.Write(" ");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var lenHalf = s.Length / 2;
|
|
||||||
for (var i = 0; i < div - lenHalf; ++i)
|
|
||||||
Console.Write(" ");
|
|
||||||
Console.Write(s);
|
|
||||||
for (var i = div + lenHalf + 1; i < maxLen + 4; ++i)
|
|
||||||
Console.Write(" ");
|
|
||||||
if (s.Length % 2 == 0)
|
|
||||||
Console.Write(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s == "-")
|
|
||||||
Console.Write("+");
|
|
||||||
else
|
|
||||||
Console.Write("|");
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine(); //end line
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (format == TableFormat.DEFAULT)
|
|
||||||
{
|
|
||||||
var widths = new int[data[0].Length];
|
|
||||||
var space_between_columns = 3;
|
|
||||||
for (var i = 0; i < data.Count; i++)
|
|
||||||
for (var j = 0; j < data[i].Length; j++)
|
|
||||||
if (data[i][j].Length > widths[j])
|
|
||||||
widths[j] = data[i][j].Length;
|
|
||||||
|
|
||||||
for (var i = 0; i < data.Count; i++)
|
|
||||||
{
|
|
||||||
for (var j = 0; j < data[i].Length; j++)
|
|
||||||
{
|
|
||||||
if (data[i][j] == "-")
|
|
||||||
data[i][j] = " ";
|
|
||||||
Console.Write(data[i][j]);
|
|
||||||
for (var k = 0; k < widths[j] - data[i][j].Length + 1 + space_between_columns; k++)
|
|
||||||
Console.Write(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Exception("Unknown type of table");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WriteColorText(string text, bool appendNewLineAtEnd = true)
|
|
||||||
{
|
|
||||||
var initialForeGround = Console.ForegroundColor;
|
|
||||||
var input = text.ToCharArray();
|
|
||||||
for (var i = 0; i < input.Length; i++)
|
|
||||||
if (input[i] == ColorPrefix)
|
|
||||||
{
|
|
||||||
if (i + 1 < input.Length)
|
|
||||||
{
|
|
||||||
if (Colors.ContainsKey(input[i + 1]))
|
|
||||||
{
|
|
||||||
Console.ForegroundColor = Colors[input[i + 1]];
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
else if (input[i + 1] == 'c')
|
|
||||||
{
|
|
||||||
Console.ForegroundColor = initialForeGround;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.Write(input[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.ForegroundColor = initialForeGround;
|
|
||||||
if (appendNewLineAtEnd)
|
|
||||||
Console.WriteLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class Spinner
|
|
||||||
{
|
|
||||||
private readonly string[] Sequence;
|
|
||||||
private bool isRunning;
|
|
||||||
public string Message;
|
|
||||||
private int position;
|
|
||||||
private Thread thread;
|
|
||||||
|
|
||||||
public Spinner()
|
|
||||||
{
|
|
||||||
Sequence = new[] { "|", "/", "-", "\\" };
|
|
||||||
position = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Start()
|
|
||||||
{
|
|
||||||
Console.CursorVisible = false;
|
|
||||||
isRunning = true;
|
|
||||||
thread = new Thread(() =>
|
|
||||||
{
|
|
||||||
while (isRunning)
|
|
||||||
{
|
|
||||||
Console.SetCursorPosition(0, Console.CursorTop);
|
|
||||||
Console.Write(" " + Sequence[position] + " " + Message + " ");
|
|
||||||
position++;
|
|
||||||
if (position >= Sequence.Length)
|
|
||||||
position = 0;
|
|
||||||
Thread.Sleep(100);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
thread.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Stop()
|
return result;
|
||||||
{
|
|
||||||
isRunning = false;
|
|
||||||
Console.CursorVisible = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace DiscordBot.Utilities;
|
|
||||||
|
|
||||||
public enum TableFormat
|
|
||||||
{
|
|
||||||
CENTER_EACH_COLUMN_BASED,
|
|
||||||
CENTER_OVERALL_LENGTH,
|
|
||||||
DEFAULT
|
|
||||||
}
|
|
||||||
56
DiscordBot/Utilities/TableData.cs
Normal file
56
DiscordBot/Utilities/TableData.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices.ComTypes;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PluginManager.Others;
|
||||||
|
using Spectre.Console;
|
||||||
|
using Spectre.Console.Rendering;
|
||||||
|
|
||||||
|
namespace DiscordBot.Utilities
|
||||||
|
{
|
||||||
|
public class TableData
|
||||||
|
{
|
||||||
|
public List<string> Columns;
|
||||||
|
public List<OneOf<string, IRenderable>[]> Rows;
|
||||||
|
|
||||||
|
public TableData()
|
||||||
|
{
|
||||||
|
Columns = new List<string>();
|
||||||
|
Rows = new List<OneOf<string, IRenderable>[]>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TableData(List<string> columns)
|
||||||
|
{
|
||||||
|
Columns = columns;
|
||||||
|
Rows = new List<OneOf<string, IRenderable>[]>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsEmpty => Rows.Count == 0;
|
||||||
|
public bool HasRoundBorders { get; set; } = true;
|
||||||
|
public bool DisplayLinesBetweenRows { get; set; } = false;
|
||||||
|
|
||||||
|
public void AddRow(OneOf<string, IRenderable>[] row)
|
||||||
|
{
|
||||||
|
Rows.Add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PrintTable()
|
||||||
|
{
|
||||||
|
var table = new Table();
|
||||||
|
table.Border(this.HasRoundBorders ? TableBorder.Rounded : TableBorder.Square);
|
||||||
|
table.AddColumns(this.Columns.ToArray());
|
||||||
|
table.ShowRowSeparators = DisplayLinesBetweenRows;
|
||||||
|
foreach (var row in this.Rows)
|
||||||
|
{
|
||||||
|
table.AddRow(row.Select(element => element.Match(
|
||||||
|
(data) => new Markup(data),
|
||||||
|
(data) => data
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
AnsiConsole.Write(table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
454
DiscordBotUI/.gitignore
vendored
Normal file
454
DiscordBotUI/.gitignore
vendored
Normal file
@@ -0,0 +1,454 @@
|
|||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Mono auto generated files
|
||||||
|
mono_crash.*
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
[Ww][Ii][Nn]32/
|
||||||
|
[Aa][Rr][Mm]/
|
||||||
|
[Aa][Rr][Mm]64/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUnit
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
nunit-*.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# Tye
|
||||||
|
.tye/
|
||||||
|
|
||||||
|
# ASP.NET Scaffolding
|
||||||
|
ScaffoldingReadMe.txt
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_h.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Coverlet is a free, cross platform Code Coverage Tool
|
||||||
|
coverage*.json
|
||||||
|
coverage*.xml
|
||||||
|
coverage*.info
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# NuGet Symbol Packages
|
||||||
|
*.snupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
*.appxbundle
|
||||||
|
*.appxupload
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!?*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
*- [Bb]ackup.rdl
|
||||||
|
*- [Bb]ackup ([0-9]).rdl
|
||||||
|
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
.localhistory/
|
||||||
|
|
||||||
|
# BeatPulse healthcheck temp database
|
||||||
|
healthchecksdb
|
||||||
|
|
||||||
|
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||||
|
MigrationBackup/
|
||||||
|
|
||||||
|
# Ionide (cross platform F# VS Code tools) working folder
|
||||||
|
.ionide/
|
||||||
|
|
||||||
|
# Fody - auto-generated XML schema
|
||||||
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
##
|
||||||
|
## Visual studio for Mac
|
||||||
|
##
|
||||||
|
|
||||||
|
|
||||||
|
# globs
|
||||||
|
Makefile.in
|
||||||
|
*.userprefs
|
||||||
|
*.usertasks
|
||||||
|
config.make
|
||||||
|
config.status
|
||||||
|
aclocal.m4
|
||||||
|
install-sh
|
||||||
|
autom4te.cache/
|
||||||
|
*.tar.gz
|
||||||
|
tarballs/
|
||||||
|
test-results/
|
||||||
|
|
||||||
|
# Mac bundle stuff
|
||||||
|
*.dmg
|
||||||
|
*.app
|
||||||
|
|
||||||
|
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
.idea/
|
||||||
|
*.sln.iml
|
||||||
|
|
||||||
|
##
|
||||||
|
## Visual Studio Code
|
||||||
|
##
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<!--If you are willing to use Windows/MacOS native APIs you will need to create 3 projects.
|
||||||
|
One for Windows with net8.0-windows TFM, one for MacOS with net8.0-macos and one with net8.0 TFM for Linux.-->
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Avalonia.Desktop" Version="11.0.10" />
|
||||||
|
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||||
|
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.10" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\DiscordBotUI\DiscordBotUI.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
25
DiscordBotUI/DiscordBotUI.Desktop/Program.cs
Normal file
25
DiscordBotUI/DiscordBotUI.Desktop/Program.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace DiscordBotUI.Desktop
|
||||||
|
{
|
||||||
|
internal sealed class Program
|
||||||
|
{
|
||||||
|
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
||||||
|
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||||
|
// yet and stuff might break.
|
||||||
|
[STAThread]
|
||||||
|
public static void Main(string[] args) => BuildAvaloniaApp()
|
||||||
|
.StartWithClassicDesktopLifetime(args);
|
||||||
|
|
||||||
|
// Avalonia configuration, don't remove; also used by visual designer.
|
||||||
|
public static AppBuilder BuildAvaloniaApp()
|
||||||
|
=> AppBuilder.Configure<App>()
|
||||||
|
.UsePlatformDetect()
|
||||||
|
.WithInterFont()
|
||||||
|
.LogToTrace()
|
||||||
|
.UseReactiveUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"DiscordBotUI.Desktop": {
|
||||||
|
"commandName": "Project"
|
||||||
|
},
|
||||||
|
"WSL": {
|
||||||
|
"commandName": "WSL2",
|
||||||
|
"distributionName": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
DiscordBotUI/DiscordBotUI.Desktop/app.manifest
Normal file
18
DiscordBotUI/DiscordBotUI.Desktop/app.manifest
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<!-- This manifest is used on Windows only.
|
||||||
|
Don't remove it as it might cause problems with window transparency and embedded controls.
|
||||||
|
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="DiscordBotUI.Desktop"/>
|
||||||
|
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!-- A list of the Windows versions that this application has been tested on
|
||||||
|
and is designed to work with. Uncomment the appropriate elements
|
||||||
|
and Windows will automatically select the most compatible environment. -->
|
||||||
|
|
||||||
|
<!-- Windows 10 -->
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
</assembly>
|
||||||
35
DiscordBotUI/DiscordBotUI.Desktop/builder.bat
Normal file
35
DiscordBotUI/DiscordBotUI.Desktop/builder.bat
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
@echo off
|
||||||
|
echo "Building..."
|
||||||
|
|
||||||
|
echo "Building linux-x64 not self-contained"
|
||||||
|
dotnet publish -r linux-x64 -p:PublishSingleFile=false --self-contained true -c Release -o ../publish/linux-x64
|
||||||
|
|
||||||
|
echo "Building win-x64 not self-contained"
|
||||||
|
dotnet publish -r win-x64 -p:PublishSingleFile=false --self-contained true -c Release -o ../publish/win-x64
|
||||||
|
|
||||||
|
echo "Building osx-x64 not self-contained"
|
||||||
|
dotnet publish -r osx-x64 -p:PublishSingleFile=false --self-contained true -c Release -o ../publish/osx-x64
|
||||||
|
|
||||||
|
|
||||||
|
echo "Building linux-x64 self-contained"
|
||||||
|
dotnet publish -r linux-x64 -p:PublishSingleFile=true --self-contained true -c Release -o ../publish/linux-x64-selfcontained
|
||||||
|
|
||||||
|
echo "Building win-x64 self-contained"
|
||||||
|
dotnet publish -r win-x64 -p:PublishSingleFile=true --self-contained true -c Release -o ../publish/win-x64-selfcontained
|
||||||
|
|
||||||
|
echo "Building osx-x64 self-contained"
|
||||||
|
dotnet publish -r osx-x64 -p:PublishSingleFile=true --self-contained true -c Release -o ../publish/osx-x64-selfcontained
|
||||||
|
|
||||||
|
echo "Zipping..."
|
||||||
|
mkdir ../publish/zip
|
||||||
|
|
||||||
|
|
||||||
|
zip -r ../publish/zip/linux-x64.zip ../publish/linux-x64
|
||||||
|
zip -r ../publish/zip/win-x64.zip ../publish/win-x64
|
||||||
|
zip -r ../publish/zip/osx-x64.zip ../publish/osx-x64
|
||||||
|
|
||||||
|
zip -r ../publish/zip/linux-x64-selfcontained.zip ../publish/linux-x64-selfcontained
|
||||||
|
zip -r ../publish/zip/win-x64-selfcontained.zip ../publish/win-x64-selfcontained
|
||||||
|
zip -r ../publish/zip/osx-x64-selfcontained.zip ../publish/osx-x64-selfcontained
|
||||||
|
|
||||||
|
echo "Done!"
|
||||||
17
DiscordBotUI/DiscordBotUI/App.axaml
Normal file
17
DiscordBotUI/DiscordBotUI/App.axaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<Application xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="using:DiscordBotUI"
|
||||||
|
x:Class="DiscordBotUI.App"
|
||||||
|
RequestedThemeVariant="Default">
|
||||||
|
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||||
|
|
||||||
|
<Application.DataTemplates>
|
||||||
|
<local:ViewLocator/>
|
||||||
|
</Application.DataTemplates>
|
||||||
|
|
||||||
|
|
||||||
|
<Application.Styles>
|
||||||
|
<FluentTheme />
|
||||||
|
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
|
||||||
|
</Application.Styles>
|
||||||
|
</Application>
|
||||||
31
DiscordBotUI/DiscordBotUI/App.axaml.cs
Normal file
31
DiscordBotUI/DiscordBotUI/App.axaml.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
using DiscordBotUI.ViewModels;
|
||||||
|
using DiscordBotUI.Views;
|
||||||
|
|
||||||
|
namespace DiscordBotUI
|
||||||
|
{
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnFrameworkInitializationCompleted()
|
||||||
|
{
|
||||||
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
|
{
|
||||||
|
desktop.MainWindow = new HomePage();
|
||||||
|
}
|
||||||
|
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
|
||||||
|
{
|
||||||
|
singleViewPlatform.MainView = new HomePage();
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnFrameworkInitializationCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
DiscordBotUI/DiscordBotUI/Assets/avalonia-logo.ico
Normal file
BIN
DiscordBotUI/DiscordBotUI/Assets/avalonia-logo.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 172 KiB |
90
DiscordBotUI/DiscordBotUI/Bot/Commands/Help.cs
Normal file
90
DiscordBotUI/DiscordBotUI/Bot/Commands/Help.cs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Discord;
|
||||||
|
using PluginManager;
|
||||||
|
using PluginManager.Interfaces;
|
||||||
|
using PluginManager.Loaders;
|
||||||
|
using PluginManager.Others;
|
||||||
|
|
||||||
|
namespace DiscordBotUI.Bot.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The help command
|
||||||
|
/// </summary>
|
||||||
|
internal class Help: DBCommand
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Command name
|
||||||
|
/// </summary>
|
||||||
|
public string Command => "help";
|
||||||
|
|
||||||
|
public List<string> Aliases => null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Command Description
|
||||||
|
/// </summary>
|
||||||
|
public string Description => "This command allows you to check all loaded commands";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Command usage
|
||||||
|
/// </summary>
|
||||||
|
public string Usage => "help <command>";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the command require administrator to be executed
|
||||||
|
/// </summary>
|
||||||
|
public bool requireAdmin => false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The main body of the command
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The command context</param>
|
||||||
|
public void ExecuteServer(DbCommandExecutingArguments args)
|
||||||
|
{
|
||||||
|
if (args.arguments is not null)
|
||||||
|
{
|
||||||
|
var e = GenerateHelpCommand(args.arguments[0]);
|
||||||
|
if (e is null)
|
||||||
|
args.context.Channel.SendMessageAsync("Unknown Command " + args.arguments[0]);
|
||||||
|
else
|
||||||
|
args.context.Channel.SendMessageAsync(embed: e.Build());
|
||||||
|
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var embedBuilder = new EmbedBuilder();
|
||||||
|
|
||||||
|
var adminCommands = "";
|
||||||
|
var normalCommands = "";
|
||||||
|
|
||||||
|
foreach (var cmd in PluginLoader.Commands)
|
||||||
|
if (cmd.requireAdmin)
|
||||||
|
adminCommands += cmd.Command + " ";
|
||||||
|
else
|
||||||
|
normalCommands += cmd.Command + " ";
|
||||||
|
|
||||||
|
|
||||||
|
if (adminCommands.Length > 0)
|
||||||
|
embedBuilder.AddField("Admin Commands", adminCommands);
|
||||||
|
if (normalCommands.Length > 0)
|
||||||
|
embedBuilder.AddField("Normal Commands", normalCommands);
|
||||||
|
args.context.Channel.SendMessageAsync(embed: embedBuilder.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private EmbedBuilder GenerateHelpCommand(string command)
|
||||||
|
{
|
||||||
|
var embedBuilder = new EmbedBuilder();
|
||||||
|
var cmd = PluginLoader.Commands.Find(p => p.Command == command ||
|
||||||
|
p.Aliases is not null && p.Aliases.Contains(command)
|
||||||
|
);
|
||||||
|
if (cmd == null) return null;
|
||||||
|
|
||||||
|
embedBuilder.AddField("Usage", Config.AppSettings["prefix"] + cmd.Usage);
|
||||||
|
embedBuilder.AddField("Description", cmd.Description);
|
||||||
|
if (cmd.Aliases is null)
|
||||||
|
return embedBuilder;
|
||||||
|
embedBuilder.AddField("Alias", cmd.Aliases.Count == 0 ? "-" : string.Join(", ", cmd.Aliases));
|
||||||
|
|
||||||
|
return embedBuilder;
|
||||||
|
}
|
||||||
|
}
|
||||||
83
DiscordBotUI/DiscordBotUI/Bot/DiscordBot.cs
Normal file
83
DiscordBotUI/DiscordBotUI/Bot/DiscordBot.cs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using PluginManager;
|
||||||
|
using PluginManager.Interfaces;
|
||||||
|
using PluginManager.Loaders;
|
||||||
|
using PluginManager.Others;
|
||||||
|
|
||||||
|
namespace DiscordBotUI.Bot
|
||||||
|
{
|
||||||
|
internal class DiscordBot
|
||||||
|
{
|
||||||
|
private readonly string[] _StartArguments;
|
||||||
|
|
||||||
|
public DiscordBot(string[] args)
|
||||||
|
{
|
||||||
|
this._StartArguments = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeBot()
|
||||||
|
{
|
||||||
|
string token = Config.AppSettings["token"];
|
||||||
|
string prefix = Config.AppSettings["prefix"];
|
||||||
|
PluginManager.Bot.Boot discordBooter = new PluginManager.Bot.Boot(token, prefix);
|
||||||
|
await discordBooter.Awake();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LoadPlugins()
|
||||||
|
{
|
||||||
|
var loader = new PluginLoader(Config.DiscordBot.Client);
|
||||||
|
|
||||||
|
loader.OnCommandLoaded += (data) =>
|
||||||
|
{
|
||||||
|
if (data.IsSuccess)
|
||||||
|
{
|
||||||
|
Config.Logger.Log("Successfully loaded command : " + data.PluginName, typeof(ICommandAction),
|
||||||
|
LogType.INFO
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Config.Logger.Log("Failed to load command : " + data.PluginName + " because " + data.ErrorMessage,
|
||||||
|
typeof(ICommandAction), LogType.ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loader.OnEventLoaded += (data) =>
|
||||||
|
{
|
||||||
|
if (data.IsSuccess)
|
||||||
|
{
|
||||||
|
Config.Logger.Log("Successfully loaded event : " + data.PluginName, typeof(ICommandAction),
|
||||||
|
LogType.INFO
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Config.Logger.Log("Failed to load event : " + data.PluginName + " because " + data.ErrorMessage,
|
||||||
|
typeof(ICommandAction), LogType.ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loader.OnSlashCommandLoaded += (data) =>
|
||||||
|
{
|
||||||
|
if (data.IsSuccess)
|
||||||
|
{
|
||||||
|
Config.Logger.Log("Successfully loaded slash command : " + data.PluginName, typeof(ICommandAction),
|
||||||
|
LogType.INFO
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Config.Logger.Log("Failed to load slash command : " + data.PluginName + " because " + data.ErrorMessage,
|
||||||
|
typeof(ICommandAction), LogType.ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await loader.LoadPlugins();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
25
DiscordBotUI/DiscordBotUI/DiscordBotUI.csproj
Normal file
25
DiscordBotUI/DiscordBotUI/DiscordBotUI.csproj
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<AvaloniaResource Include="Assets\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Avalonia" Version="11.0.10" />
|
||||||
|
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.10" />
|
||||||
|
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.0.10" />
|
||||||
|
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.10" />
|
||||||
|
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||||
|
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.10" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\PluginManager\PluginManager.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
33
DiscordBotUI/DiscordBotUI/ViewLocator.cs
Normal file
33
DiscordBotUI/DiscordBotUI/ViewLocator.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Templates;
|
||||||
|
|
||||||
|
using DiscordBotUI.ViewModels;
|
||||||
|
|
||||||
|
namespace DiscordBotUI
|
||||||
|
{
|
||||||
|
public class ViewLocator : IDataTemplate
|
||||||
|
{
|
||||||
|
public Control? Build(object? data)
|
||||||
|
{
|
||||||
|
if (data is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var name = data.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
|
||||||
|
var type = Type.GetType(name);
|
||||||
|
|
||||||
|
if (type != null)
|
||||||
|
{
|
||||||
|
return (Control)Activator.CreateInstance(type)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TextBlock { Text = "Not Found: " + name };
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Match(object? data)
|
||||||
|
{
|
||||||
|
return data is ViewModelBase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
DiscordBotUI/DiscordBotUI/ViewModels/OnlinePlugin.cs
Normal file
22
DiscordBotUI/DiscordBotUI/ViewModels/OnlinePlugin.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DiscordBotUI.ViewModels
|
||||||
|
{
|
||||||
|
public class OnlinePlugin
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public string Version { get; set; }
|
||||||
|
|
||||||
|
public OnlinePlugin(string name, string description, string version) {
|
||||||
|
Name = name;
|
||||||
|
Description = description;
|
||||||
|
Version = version;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
DiscordBotUI/DiscordBotUI/ViewModels/Plugin.cs
Normal file
22
DiscordBotUI/DiscordBotUI/ViewModels/Plugin.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DiscordBotUI.ViewModels
|
||||||
|
{
|
||||||
|
public class Plugin
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Version { get; set; }
|
||||||
|
public bool IsMarkedToUninstall { get; set; }
|
||||||
|
|
||||||
|
public Plugin(string Name, string Version, bool isMarkedToUninstall)
|
||||||
|
{
|
||||||
|
this.Name = Name;
|
||||||
|
this.Version = Version;
|
||||||
|
IsMarkedToUninstall = isMarkedToUninstall;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
DiscordBotUI/DiscordBotUI/ViewModels/ViewModelBase.cs
Normal file
11
DiscordBotUI/DiscordBotUI/ViewModels/ViewModelBase.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace DiscordBotUI.ViewModels
|
||||||
|
{
|
||||||
|
public class ViewModelBase : ReactiveObject
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
41
DiscordBotUI/DiscordBotUI/Views/HomePage.axaml
Normal file
41
DiscordBotUI/DiscordBotUI/Views/HomePage.axaml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="DiscordBotUI.Views.HomePage"
|
||||||
|
Title="HomePage" MinWidth="900" MinHeight="500">
|
||||||
|
|
||||||
|
<DockPanel LastChildFill="True">
|
||||||
|
<Menu DockPanel.Dock="Top">
|
||||||
|
<MenuItem Header="Settings" Click="SettingsMenuClick"></MenuItem>
|
||||||
|
<MenuItem Header="Installed Plugins" Click="PluginsMenuClick"></MenuItem>
|
||||||
|
<MenuItem Header="New Plugins" Click="NewPluginsMenuClick"></MenuItem>
|
||||||
|
</Menu>
|
||||||
|
|
||||||
|
<Border Width="500" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Right">
|
||||||
|
<RelativePanel Margin="10">
|
||||||
|
<Label Content="Bot Token: " Name="labelToken" RelativePanel.AlignTopWithPanel="True"/>
|
||||||
|
<TextBox Name="textBoxToken" Text="" IsReadOnly="True" RelativePanel.AlignRightWithPanel="True" Width="350" />
|
||||||
|
|
||||||
|
<Label Content="Bot Prefix: " Name="labelPrefix" RelativePanel.Below="labelToken" Margin="0,20,0,0"/>
|
||||||
|
<TextBox Name="textBoxPrefix" Text="" RelativePanel.AlignRightWithPanel="True" RelativePanel.Below="textBoxToken"
|
||||||
|
IsReadOnly="True" Margin="0,10,0,0" Width="350" />
|
||||||
|
|
||||||
|
<Label Content="Server Id: " Name="labelServerId" RelativePanel.Below="labelPrefix" Margin="0,20,0,0"/>
|
||||||
|
<TextBox Name="textBoxServerId" Text="" RelativePanel.AlignRightWithPanel="True"
|
||||||
|
IsReadOnly="True" RelativePanel.Below="textBoxPrefix" Margin="0,10,0,0" Width="350" />
|
||||||
|
|
||||||
|
<Button Click="ButtonStartBotClick" Name="buttonStartBot" Content="Start" RelativePanel.AlignBottomWithPanel="True" Margin="0,-100,0,0"
|
||||||
|
Width="120" Height="40" Background="#FF008CFF" Foreground="White" BorderThickness="0" CornerRadius="5" FontWeight="Bold"
|
||||||
|
RelativePanel.AlignHorizontalCenterWithPanel="True" />
|
||||||
|
</RelativePanel>
|
||||||
|
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border Background="White" BorderBrush="Black" BorderThickness="1">
|
||||||
|
<TextBlock Name="logTextBlock" Foreground="Black" Text="" />
|
||||||
|
</Border>
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
</Window>
|
||||||
80
DiscordBotUI/DiscordBotUI/Views/HomePage.axaml.cs
Normal file
80
DiscordBotUI/DiscordBotUI/Views/HomePage.axaml.cs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
|
||||||
|
using DiscordBotUI.Bot;
|
||||||
|
|
||||||
|
using PluginManager;
|
||||||
|
using PluginManager.Others.Logger;
|
||||||
|
|
||||||
|
namespace DiscordBotUI.Views;
|
||||||
|
|
||||||
|
public partial class HomePage : Window
|
||||||
|
{
|
||||||
|
private readonly DiscordBot _DiscordBot;
|
||||||
|
|
||||||
|
public HomePage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_DiscordBot = new DiscordBot(null!);
|
||||||
|
|
||||||
|
Loaded += HomePage_Loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void HomePage_Loaded(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
await Config.Initialize();
|
||||||
|
|
||||||
|
if(!Config.AppSettings.ContainsAllKeys("token", "prefix"))
|
||||||
|
{
|
||||||
|
await new SettingsPage().ShowDialog(this);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(Config.AppSettings["token"]) || string.IsNullOrWhiteSpace(Config.AppSettings["prefix"]))
|
||||||
|
Environment.Exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
textBoxToken.Text = Config.AppSettings["token"];
|
||||||
|
textBoxPrefix.Text = Config.AppSettings["prefix"];
|
||||||
|
textBoxServerId.Text = Config.AppSettings["ServerID"];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetTextToTB(Log logMessage)
|
||||||
|
{
|
||||||
|
logTextBlock.Text += $"[{logMessage.Type}] [{logMessage.ThrowTime.ToShortTimeString()}] {logMessage.Message}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void ButtonStartBotClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
Config.Logger.OnLog += async (sender, logMessage) =>
|
||||||
|
{
|
||||||
|
await Dispatcher.UIThread.InvokeAsync(() => SetTextToTB(logMessage), DispatcherPriority.Background);
|
||||||
|
};
|
||||||
|
|
||||||
|
await _DiscordBot.InitializeBot();
|
||||||
|
|
||||||
|
await _DiscordBot.LoadPlugins();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void SettingsMenuClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
//await new SettingsPage().ShowDialog(this);
|
||||||
|
new SettingsPage().Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void PluginsMenuClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
//await new PluginsPage().ShowDialog(this);
|
||||||
|
new PluginsPage().Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NewPluginsMenuClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
new PluginInstaller().Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
33
DiscordBotUI/DiscordBotUI/Views/PluginInstaller.axaml
Normal file
33
DiscordBotUI/DiscordBotUI/Views/PluginInstaller.axaml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:views="clr-namespace:DiscordBotUI.Views;assembly=DiscordBotUI"
|
||||||
|
xmlns:viewmodels="using:DiscordBotUI.ViewModels"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="300"
|
||||||
|
x:Class="DiscordBotUI.Views.PluginInstaller"
|
||||||
|
x:DataType="views:PluginInstaller"
|
||||||
|
Title="PluginInstaller"
|
||||||
|
Name="PluginInstallerWindow"
|
||||||
|
>
|
||||||
|
|
||||||
|
<DataGrid Name="dataGridInstallablePlugins" ItemsSource="{Binding Plugins}">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="Plugin Name" Foreground="Aquamarine" Binding="{Binding Name}"/>
|
||||||
|
<DataGridTextColumn Header="Plugin Version" Binding="{Binding Version}"/>
|
||||||
|
<DataGridTextColumn Header="Plugin Description" Binding="{Binding Description}" Width="*"/>
|
||||||
|
|
||||||
|
<DataGridTemplateColumn Header="Download">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate DataType="viewmodels:OnlinePlugin">
|
||||||
|
<Button Content="Download"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Command="{Binding InstallPlugin, RelativeSource={RelativeSource AncestorType=views:PluginInstaller}}"
|
||||||
|
CommandParameter="{Binding Name}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</Window>
|
||||||
62
DiscordBotUI/DiscordBotUI/Views/PluginInstaller.axaml.cs
Normal file
62
DiscordBotUI/DiscordBotUI/Views/PluginInstaller.axaml.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Markup.Xaml.Templates;
|
||||||
|
using Avalonia.Media;
|
||||||
|
|
||||||
|
using DiscordBotUI.ViewModels;
|
||||||
|
|
||||||
|
using PluginManager;
|
||||||
|
using PluginManager.Plugin;
|
||||||
|
|
||||||
|
namespace DiscordBotUI.Views;
|
||||||
|
|
||||||
|
public partial class PluginInstaller : Window
|
||||||
|
{
|
||||||
|
|
||||||
|
public ObservableCollection<OnlinePlugin> Plugins { get; private set; }
|
||||||
|
|
||||||
|
public PluginInstaller()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
Loaded += OnPageLoaded;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnPageLoaded(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (Config.PluginsManager is null) return;
|
||||||
|
|
||||||
|
List<PluginOnlineInfo>? onlineInfos = await Config.PluginsManager.GetPluginsList();
|
||||||
|
|
||||||
|
if(onlineInfos is null) return;
|
||||||
|
|
||||||
|
List<OnlinePlugin> plugins = new List<OnlinePlugin>();
|
||||||
|
|
||||||
|
foreach(PluginOnlineInfo onlinePlugin in onlineInfos)
|
||||||
|
{
|
||||||
|
plugins.Add(new OnlinePlugin(onlinePlugin.Name, onlinePlugin.Description, onlinePlugin.Version.ToShortString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugins = new ObservableCollection<OnlinePlugin>(plugins);
|
||||||
|
|
||||||
|
dataGridInstallablePlugins.ItemsSource = Plugins;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void InstallPlugin(string name)
|
||||||
|
{
|
||||||
|
|
||||||
|
PluginOnlineInfo? info = await Config.PluginsManager.GetPluginDataByName(name);
|
||||||
|
if(info is null) return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await Config.PluginsManager.InstallPlugin(info, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
DiscordBotUI/DiscordBotUI/Views/PluginsPage.axaml
Normal file
25
DiscordBotUI/DiscordBotUI/Views/PluginsPage.axaml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
xmlns:model ="clr-namespace:DiscordBotUI.Views;assembly=DiscordBotUI"
|
||||||
|
x:Class="DiscordBotUI.Views.PluginsPage"
|
||||||
|
Title="Plugins Page"
|
||||||
|
x:DataType="model:PluginsPage">
|
||||||
|
|
||||||
|
|
||||||
|
<DataGrid Name="dataGridPlugins" Margin="20" ItemsSource="{Binding Plugins}"
|
||||||
|
IsReadOnly="False"
|
||||||
|
CanUserSortColumns="False"
|
||||||
|
GridLinesVisibility="All"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
BorderThickness="1" BorderBrush="Gray">
|
||||||
|
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="Plugin Name" Foreground="Aquamarine" Binding="{Binding Name}"/>
|
||||||
|
<DataGridTextColumn Header="Plugin Version" Binding="{Binding Version}"/>
|
||||||
|
<DataGridCheckBoxColumn Header="Is Marked for Uninstall" Binding="{Binding IsMarkedToUninstall}"/>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</Window>
|
||||||
43
DiscordBotUI/DiscordBotUI/Views/PluginsPage.axaml.cs
Normal file
43
DiscordBotUI/DiscordBotUI/Views/PluginsPage.axaml.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
|
||||||
|
using DiscordBotUI.ViewModels;
|
||||||
|
|
||||||
|
using PluginManager;
|
||||||
|
|
||||||
|
namespace DiscordBotUI.Views;
|
||||||
|
|
||||||
|
|
||||||
|
public partial class PluginsPage: Window
|
||||||
|
{
|
||||||
|
|
||||||
|
public ObservableCollection<Plugin> Plugins { get; private set; }
|
||||||
|
|
||||||
|
public PluginsPage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
Loaded += OnPageLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnPageLoaded(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (Config.PluginsManager is null) return;
|
||||||
|
|
||||||
|
var plugins = await Config.PluginsManager.GetInstalledPlugins();
|
||||||
|
var localList = new List<Plugin>();
|
||||||
|
foreach (var plugin in plugins)
|
||||||
|
{
|
||||||
|
localList.Add(new Plugin(plugin.PluginName, plugin.PluginVersion.ToShortString(), plugin.IsMarkedToUninstall));
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugins = new ObservableCollection<Plugin>(localList);
|
||||||
|
|
||||||
|
|
||||||
|
dataGridPlugins.ItemsSource = Plugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
26
DiscordBotUI/DiscordBotUI/Views/SettingsPage.axaml
Normal file
26
DiscordBotUI/DiscordBotUI/Views/SettingsPage.axaml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="200"
|
||||||
|
x:Class="DiscordBotUI.Views.SettingsPage"
|
||||||
|
Title="SettingsPage" Width="500" Height="200" MinWidth="500" MaxWidth="500" MinHeight="200" MaxHeight="200">
|
||||||
|
<RelativePanel Margin="10,10,10,0">
|
||||||
|
<Label Content="Bot Token: " Name="labelToken" RelativePanel.AlignTopWithPanel="True"/>
|
||||||
|
<TextBox Name="textBoxToken" Text="" IsReadOnly="False" RelativePanel.AlignRightWithPanel="True" Width="350" />
|
||||||
|
|
||||||
|
<Label Content="Bot Prefix: " Name="labelPrefix" RelativePanel.Below="labelToken" Margin="0,20,0,0"/>
|
||||||
|
<TextBox Name="textBoxPrefix" Text="" RelativePanel.AlignRightWithPanel="True" RelativePanel.Below="textBoxToken"
|
||||||
|
IsReadOnly="False" Margin="0,10,0,0" Width="350" />
|
||||||
|
|
||||||
|
<Label Content="Server Id: " Name="labelServerId" RelativePanel.Below="labelPrefix" Margin="0,20,0,0"/>
|
||||||
|
<TextBox Name="textBoxServerId" Text="" RelativePanel.AlignRightWithPanel="True"
|
||||||
|
IsReadOnly="False" RelativePanel.Below="textBoxPrefix" Margin="0,10,0,0" Width="350" />
|
||||||
|
|
||||||
|
<Label Name="labelErrorMessage" Foreground="Red" RelativePanel.Below="textBoxServerId" />
|
||||||
|
|
||||||
|
<Button Name="buttonSaveSettings" Click="ButtonSaveSettingsClick" Content="Save" RelativePanel.AlignBottomWithPanel="True" Margin="0,-50,0,0"
|
||||||
|
Width="120" Height="40" Background="#FF008CFF" Foreground="White" BorderThickness="0" CornerRadius="5" FontWeight="Bold"
|
||||||
|
RelativePanel.AlignHorizontalCenterWithPanel="True" />
|
||||||
|
</RelativePanel>
|
||||||
|
</Window>
|
||||||
44
DiscordBotUI/DiscordBotUI/Views/SettingsPage.axaml.cs
Normal file
44
DiscordBotUI/DiscordBotUI/Views/SettingsPage.axaml.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
using PluginManager;
|
||||||
|
|
||||||
|
namespace DiscordBotUI.Views;
|
||||||
|
|
||||||
|
public partial class SettingsPage : Window
|
||||||
|
{
|
||||||
|
public SettingsPage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void ButtonSaveSettingsClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
string token = textBoxToken.Text;
|
||||||
|
string botPrefix = textBoxPrefix.Text;
|
||||||
|
string serverId = textBoxServerId.Text;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(serverId)) serverId = string.Empty;
|
||||||
|
if (string.IsNullOrWhiteSpace(token))
|
||||||
|
{
|
||||||
|
labelErrorMessage.Content = "The token is invalid";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(string.IsNullOrWhiteSpace(botPrefix) || botPrefix.Length > 1 || botPrefix.Length < 1)
|
||||||
|
{
|
||||||
|
labelErrorMessage.Content = "The prefix is invalid";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.AppSettings.Add("token", token);
|
||||||
|
Config.AppSettings.Add("prefix", botPrefix);
|
||||||
|
Config.AppSettings.Add("ServerID", serverId);
|
||||||
|
|
||||||
|
await Config.AppSettings.SaveToFile();
|
||||||
|
|
||||||
|
Config.Logger.Log("Config Saved");
|
||||||
|
|
||||||
|
Close();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ using Discord.Commands;
|
|||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
using PluginManager.Others;
|
using PluginManager.Others;
|
||||||
|
|
||||||
|
|
||||||
namespace PluginManager.Bot;
|
namespace PluginManager.Bot;
|
||||||
|
|
||||||
public class Boot
|
public class Boot
|
||||||
@@ -12,27 +13,27 @@ public class Boot
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The bot prefix
|
/// The bot prefix
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly string botPrefix;
|
public readonly string BotPrefix;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The bot token
|
/// The bot token
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly string botToken;
|
public readonly string BotToken;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The bot client
|
/// The bot client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DiscordSocketClient client;
|
public DiscordSocketClient Client;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The bot command handler
|
/// The bot command handler
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private CommandHandler commandServiceHandler;
|
private CommandHandler _commandServiceHandler;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The command service
|
/// The command service
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private CommandService service;
|
private CommandService _service;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The main Boot constructor
|
/// The main Boot constructor
|
||||||
@@ -41,8 +42,8 @@ public class Boot
|
|||||||
/// <param name="botPrefix">The bot prefix</param>
|
/// <param name="botPrefix">The bot prefix</param>
|
||||||
public Boot(string botToken, string botPrefix)
|
public Boot(string botToken, string botPrefix)
|
||||||
{
|
{
|
||||||
this.botPrefix = botPrefix;
|
this.BotPrefix = botPrefix;
|
||||||
this.botToken = botToken;
|
this.BotToken = botToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@ public class Boot
|
|||||||
/// Checks if the bot is ready
|
/// Checks if the bot is ready
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value> true if the bot is ready, otherwise false </value>
|
/// <value> true if the bot is ready, otherwise false </value>
|
||||||
public bool isReady { get; private set; }
|
public bool IsReady { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The start method for the bot. This method is used to load the bot
|
/// The start method for the bot. This method is used to load the bot
|
||||||
@@ -72,35 +73,33 @@ public class Boot
|
|||||||
GatewayIntents = GatewayIntents.All
|
GatewayIntents = GatewayIntents.All
|
||||||
};
|
};
|
||||||
|
|
||||||
client = new DiscordSocketClient(config);
|
Client = new DiscordSocketClient(config);
|
||||||
service = new CommandService();
|
_service = new CommandService();
|
||||||
|
|
||||||
CommonTasks();
|
CommonTasks();
|
||||||
|
|
||||||
await client.LoginAsync(TokenType.Bot, botToken);
|
await Client.LoginAsync(TokenType.Bot, BotToken);
|
||||||
|
|
||||||
await client.StartAsync();
|
await Client.StartAsync();
|
||||||
|
|
||||||
commandServiceHandler = new CommandHandler(client, service, botPrefix);
|
_commandServiceHandler = new CommandHandler(Client, _service, BotPrefix);
|
||||||
|
|
||||||
await commandServiceHandler.InstallCommandsAsync();
|
await _commandServiceHandler.InstallCommandsAsync();
|
||||||
|
|
||||||
|
Config.DiscordBotClient = this;
|
||||||
|
|
||||||
await Task.Delay(2000);
|
while (!IsReady) ;
|
||||||
|
|
||||||
Config._DiscordBotClient = this;
|
|
||||||
|
|
||||||
while (!isReady) ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void CommonTasks()
|
private void CommonTasks()
|
||||||
{
|
{
|
||||||
if (client == null) return;
|
if (Client == null) return;
|
||||||
client.LoggedOut += Client_LoggedOut;
|
Client.LoggedOut += Client_LoggedOut;
|
||||||
client.Log += Log;
|
Client.Log += Log;
|
||||||
client.LoggedIn += LoggedIn;
|
Client.LoggedIn += LoggedIn;
|
||||||
client.Ready += Ready;
|
Client.Ready += Ready;
|
||||||
client.Disconnected += Client_Disconnected;
|
Client.Disconnected += Client_Disconnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Client_Disconnected(Exception arg)
|
private async Task Client_Disconnected(Exception arg)
|
||||||
@@ -108,29 +107,27 @@ public class Boot
|
|||||||
if (arg.Message.Contains("401"))
|
if (arg.Message.Contains("401"))
|
||||||
{
|
{
|
||||||
Config.AppSettings.Remove("token");
|
Config.AppSettings.Remove("token");
|
||||||
Config.Logger.Log("The token is invalid. Please restart the bot and enter a valid token.", this,
|
Config.Logger.Log("The token is invalid. Please restart the bot and follow the instructions", typeof(Boot), LogType.CRITICAL);
|
||||||
LogLevel.ERROR);
|
|
||||||
await Config.AppSettings.SaveToFile();
|
await Config.AppSettings.SaveToFile();
|
||||||
await Task.Delay(4000);
|
|
||||||
Environment.Exit(0);
|
Environment.Exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Client_LoggedOut()
|
private async Task Client_LoggedOut()
|
||||||
{
|
{
|
||||||
Config.Logger.Log("Successfully Logged Out", this);
|
Config.Logger.Log("Successfully Logged Out", typeof(Boot));
|
||||||
await Log(new LogMessage(LogSeverity.Info, "Boot", "Successfully logged out from discord !"));
|
await Log(new LogMessage(LogSeverity.Info, "Boot", "Successfully logged out from discord !"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task Ready()
|
private Task Ready()
|
||||||
{
|
{
|
||||||
isReady = true;
|
IsReady = true;
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task LoggedIn()
|
private Task LoggedIn()
|
||||||
{
|
{
|
||||||
Config.Logger.Log("Successfully Logged In", this);
|
Config.Logger.Log("Successfully Logged In", typeof(Boot));
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,13 +137,12 @@ public class Boot
|
|||||||
{
|
{
|
||||||
case LogSeverity.Error:
|
case LogSeverity.Error:
|
||||||
case LogSeverity.Critical:
|
case LogSeverity.Critical:
|
||||||
Config.Logger.Log(message.Message, this, LogLevel.ERROR);
|
Config.Logger.Log(message.Message, typeof(Boot), LogType.ERROR);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LogSeverity.Info:
|
case LogSeverity.Info:
|
||||||
case LogSeverity.Debug:
|
case LogSeverity.Debug:
|
||||||
Config.Logger.Log(message.Message, this);
|
Config.Logger.Log(message.Message, typeof(Boot), LogType.INFO);
|
||||||
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Discord.Commands;
|
|||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
using PluginManager.Interfaces;
|
using PluginManager.Interfaces;
|
||||||
using PluginManager.Loaders;
|
using PluginManager.Loaders;
|
||||||
|
using PluginManager.Online;
|
||||||
using PluginManager.Others;
|
using PluginManager.Others;
|
||||||
using PluginManager.Others.Permissions;
|
using PluginManager.Others.Permissions;
|
||||||
|
|
||||||
@@ -13,9 +14,9 @@ namespace PluginManager.Bot;
|
|||||||
|
|
||||||
internal class CommandHandler
|
internal class CommandHandler
|
||||||
{
|
{
|
||||||
private readonly string botPrefix;
|
private readonly string _botPrefix;
|
||||||
private readonly DiscordSocketClient client;
|
private readonly DiscordSocketClient _client;
|
||||||
private readonly CommandService commandService;
|
private readonly CommandService _commandService;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Command handler constructor
|
/// Command handler constructor
|
||||||
@@ -25,9 +26,9 @@ internal class CommandHandler
|
|||||||
/// <param name="botPrefix">The prefix to watch for</param>
|
/// <param name="botPrefix">The prefix to watch for</param>
|
||||||
public CommandHandler(DiscordSocketClient client, CommandService commandService, string botPrefix)
|
public CommandHandler(DiscordSocketClient client, CommandService commandService, string botPrefix)
|
||||||
{
|
{
|
||||||
this.client = client;
|
_client = client;
|
||||||
this.commandService = commandService;
|
_commandService = commandService;
|
||||||
this.botPrefix = botPrefix;
|
_botPrefix = botPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -36,20 +37,19 @@ internal class CommandHandler
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task InstallCommandsAsync()
|
public async Task InstallCommandsAsync()
|
||||||
{
|
{
|
||||||
client.MessageReceived += MessageHandler;
|
_client.MessageReceived += MessageHandler;
|
||||||
client.SlashCommandExecuted += Client_SlashCommandExecuted;
|
_client.SlashCommandExecuted += Client_SlashCommandExecuted;
|
||||||
await commandService.AddModulesAsync(Assembly.GetEntryAssembly(), null);
|
await _commandService.AddModulesAsync(Assembly.GetEntryAssembly(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task Client_SlashCommandExecuted(SocketSlashCommand arg)
|
private Task Client_SlashCommandExecuted(SocketSlashCommand arg)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var plugin = PluginLoader.SlashCommands!.FirstOrDefault(p => p.Name == arg.Data.Name);
|
var plugin = PluginLoader.SlashCommands.FirstOrDefault(p => p.Name == arg.Data.Name);
|
||||||
|
|
||||||
if (plugin is null)
|
if (plugin is null)
|
||||||
throw new Exception("Failed to run command. !");
|
throw new Exception("Failed to run command !");
|
||||||
|
|
||||||
|
|
||||||
if (arg.Channel is SocketDMChannel)
|
if (arg.Channel is SocketDMChannel)
|
||||||
plugin.ExecuteDM(arg);
|
plugin.ExecuteDM(arg);
|
||||||
@@ -57,7 +57,7 @@ internal class CommandHandler
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Config.Logger.Log(ex.Message, "CommandHandler", LogLevel.ERROR);
|
Config.Logger.Log(ex.Message, type: LogType.ERROR, source: typeof(CommandHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
@@ -85,30 +85,30 @@ internal class CommandHandler
|
|||||||
|
|
||||||
var argPos = 0;
|
var argPos = 0;
|
||||||
|
|
||||||
if (!message.Content.StartsWith(botPrefix) && !message.HasMentionPrefix(client.CurrentUser, ref argPos))
|
if (!message.Content.StartsWith(_botPrefix) && !message.HasMentionPrefix(_client.CurrentUser, ref argPos))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var context = new SocketCommandContext(client, message);
|
var context = new SocketCommandContext(_client, message);
|
||||||
|
|
||||||
await commandService.ExecuteAsync(context, argPos, null);
|
await _commandService.ExecuteAsync(context, argPos, null);
|
||||||
|
|
||||||
DBCommand? plugin;
|
DBCommand? plugin;
|
||||||
var cleanMessage = "";
|
var cleanMessage = "";
|
||||||
|
|
||||||
if (message.HasMentionPrefix(client.CurrentUser, ref argPos))
|
if (message.HasMentionPrefix(_client.CurrentUser, ref argPos))
|
||||||
{
|
{
|
||||||
var mentionPrefix = "<@" + client.CurrentUser.Id + ">";
|
var mentionPrefix = "<@" + _client.CurrentUser.Id + ">";
|
||||||
|
|
||||||
plugin = PluginLoader.Commands!
|
plugin = PluginLoader.Commands!
|
||||||
.FirstOrDefault(plug => plug.Command ==
|
.FirstOrDefault(plug => plug.Command ==
|
||||||
message.Content.Substring(mentionPrefix.Length + 1)
|
message.Content.Substring(mentionPrefix.Length + 1)
|
||||||
.Split(' ')[0] ||
|
.Split(' ')[0] ||
|
||||||
(
|
|
||||||
plug.Aliases is not null &&
|
plug.Aliases is not null &&
|
||||||
plug.Aliases.Contains(message.CleanContent
|
plug.Aliases.Contains(message.CleanContent
|
||||||
.Substring(mentionPrefix.Length + 1)
|
.Substring(mentionPrefix.Length + 1)
|
||||||
.Split(' ')[0])
|
.Split(' ')[0]
|
||||||
));
|
)
|
||||||
|
);
|
||||||
|
|
||||||
cleanMessage = message.Content.Substring(mentionPrefix.Length + 1);
|
cleanMessage = message.Content.Substring(mentionPrefix.Length + 1);
|
||||||
}
|
}
|
||||||
@@ -117,19 +117,20 @@ internal class CommandHandler
|
|||||||
{
|
{
|
||||||
plugin = PluginLoader.Commands!
|
plugin = PluginLoader.Commands!
|
||||||
.FirstOrDefault(p => p.Command ==
|
.FirstOrDefault(p => p.Command ==
|
||||||
message.Content.Split(' ')[0].Substring(botPrefix.Length) ||
|
message.Content.Split(' ')[0].Substring(_botPrefix.Length) ||
|
||||||
(p.Aliases is not null &&
|
p.Aliases is not null &&
|
||||||
p.Aliases.Contains(
|
p.Aliases.Contains(
|
||||||
message.Content.Split(' ')[0]
|
message.Content.Split(' ')[0]
|
||||||
.Substring(botPrefix.Length))));
|
.Substring(_botPrefix.Length)
|
||||||
cleanMessage = message.Content.Substring(botPrefix.Length);
|
)
|
||||||
|
);
|
||||||
|
cleanMessage = message.Content.Substring(_botPrefix.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plugin is null)
|
if (plugin is null)
|
||||||
throw new Exception("Failed to run command ! " + message.CleanContent + " (user: " +
|
return;
|
||||||
context.Message.Author.Username + " - " + context.Message.Author.Id + ")");
|
|
||||||
|
|
||||||
if (plugin.requireAdmin && !context.Message.Author.isAdmin())
|
if (plugin.requireAdmin && !context.Message.Author.IsAdmin())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var split = cleanMessage.Split(' ');
|
var split = cleanMessage.Split(' ');
|
||||||
@@ -138,7 +139,13 @@ internal class CommandHandler
|
|||||||
if (split.Length > 1)
|
if (split.Length > 1)
|
||||||
argsClean = string.Join(' ', split, 1, split.Length - 1).Split(' ');
|
argsClean = string.Join(' ', split, 1, split.Length - 1).Split(' ');
|
||||||
|
|
||||||
DBCommandExecutingArguments cmd = new(context, cleanMessage, split[0], argsClean);
|
DbCommandExecutingArguments cmd = new(context, cleanMessage, split[0], argsClean);
|
||||||
|
|
||||||
|
Config.Logger.Log(
|
||||||
|
$"User ({context.User.Username}) from Guild \"{context.Guild.Name}\" executed command \"{cmd.cleanContent}\"",
|
||||||
|
typeof(CommandHandler),
|
||||||
|
LogType.INFO
|
||||||
|
);
|
||||||
|
|
||||||
if (context.Channel is SocketDMChannel)
|
if (context.Channel is SocketDMChannel)
|
||||||
plugin.ExecuteDM(cmd);
|
plugin.ExecuteDM(cmd);
|
||||||
@@ -146,7 +153,7 @@ internal class CommandHandler
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Config.Logger.Log(ex.Message, this, LogLevel.ERROR);
|
Config.Logger.Log(ex.Message, type: LogType.ERROR, source: typeof(CommandHandler));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,84 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using PluginManager.Bot;
|
using PluginManager.Bot;
|
||||||
|
using PluginManager.Interfaces.Updater;
|
||||||
|
using PluginManager.Online;
|
||||||
using PluginManager.Others;
|
using PluginManager.Others;
|
||||||
|
using PluginManager.Others.Actions;
|
||||||
using PluginManager.Others.Logger;
|
using PluginManager.Others.Logger;
|
||||||
|
using PluginManager.Plugin;
|
||||||
|
using PluginManager.Updater.Application;
|
||||||
|
|
||||||
namespace PluginManager;
|
namespace PluginManager;
|
||||||
|
|
||||||
public class Config
|
public class Config
|
||||||
{
|
{
|
||||||
private static bool IsLoaded;
|
|
||||||
public static DBLogger Logger;
|
|
||||||
public static SettingsDictionary<string, string>? AppSettings;
|
|
||||||
|
|
||||||
internal static Boot? _DiscordBotClient;
|
private static readonly string _DefaultBranchForPlugins = "releases";
|
||||||
|
|
||||||
public static Boot? DiscordBot => _DiscordBotClient;
|
private static readonly string _ConfigFile = "./Data/Resources/config.json";
|
||||||
|
private static readonly string _PluginsDatabaseFile = "./Data/Resources/plugins.json";
|
||||||
|
private static readonly string _ResourcesFolder = "./Data/Resources";
|
||||||
|
private static readonly string _PluginsFolder = "./Data/Plugins";
|
||||||
|
private static readonly string _ArchivesFolder = "./Data/Archives";
|
||||||
|
private static readonly string _LogsFolder = "./Data/Logs";
|
||||||
|
|
||||||
|
private static bool _isLoaded;
|
||||||
|
public static Logger Logger;
|
||||||
|
public static SettingsDictionary<string, string> AppSettings;
|
||||||
|
|
||||||
|
internal static string PluginDatabase => AppSettings["PluginDatabase"];
|
||||||
|
internal static string ServerID => AppSettings["ServerID"];
|
||||||
|
|
||||||
|
public static InternalActionManager InternalActionManager;
|
||||||
|
|
||||||
|
public static PluginsManager PluginsManager;
|
||||||
|
|
||||||
|
internal static Boot? DiscordBotClient;
|
||||||
|
|
||||||
|
public static Boot? DiscordBot => DiscordBotClient;
|
||||||
|
|
||||||
public static async Task Initialize()
|
public static async Task Initialize()
|
||||||
{
|
{
|
||||||
if (IsLoaded) return;
|
if (_isLoaded) return;
|
||||||
|
|
||||||
Directory.CreateDirectory("./Data/Resources");
|
Directory.CreateDirectory(_ResourcesFolder);
|
||||||
Directory.CreateDirectory("./Data/Plugins");
|
Directory.CreateDirectory(_PluginsFolder);
|
||||||
Directory.CreateDirectory("./Data/PAKS");
|
Directory.CreateDirectory(_ArchivesFolder);
|
||||||
Directory.CreateDirectory("./Data/Logs/Logs");
|
Directory.CreateDirectory(_LogsFolder);
|
||||||
Directory.CreateDirectory("./Data/Logs/Errors");
|
|
||||||
|
|
||||||
AppSettings = new SettingsDictionary<string, string>("./Data/Resources/config.json");
|
AppSettings = new SettingsDictionary<string, string>(_ConfigFile);
|
||||||
|
bool response = await AppSettings.LoadFromFile();
|
||||||
|
|
||||||
AppSettings["LogFolder"] = "./Data/Logs/Logs";
|
if (!response)
|
||||||
AppSettings["ErrorFolder"] = "./Data/Logs/Errors";
|
throw new Exception("Invalid config file");
|
||||||
|
|
||||||
Logger = new DBLogger();
|
AppSettings["LogFolder"] = _LogsFolder;
|
||||||
|
AppSettings["PluginFolder"] = _PluginsFolder;
|
||||||
|
AppSettings["ArchiveFolder"] = _ArchivesFolder;
|
||||||
|
AppSettings["PluginDatabase"] = _PluginsDatabaseFile;
|
||||||
|
|
||||||
ArchiveManager.Initialize();
|
if (!File.Exists(_PluginsDatabaseFile))
|
||||||
|
{
|
||||||
|
List<PluginInfo> plugins = new();
|
||||||
|
await JsonManager.SaveToJsonFile(_PluginsDatabaseFile, plugins);
|
||||||
|
}
|
||||||
|
|
||||||
IsLoaded = true;
|
Logger = new Logger(false, true, _LogsFolder + $"/{DateTime.Today.ToShortDateString().Replace("/", "")}.log");
|
||||||
|
|
||||||
|
PluginsManager = new PluginsManager(_DefaultBranchForPlugins);
|
||||||
|
|
||||||
|
await PluginsManager.UninstallMarkedPlugins();
|
||||||
|
|
||||||
|
await PluginsManager.CheckForUpdates();
|
||||||
|
|
||||||
|
_isLoaded = true;
|
||||||
|
|
||||||
|
Logger.Log("Config initialized", typeof(Config));
|
||||||
|
|
||||||
Logger.Log("Config initialized", LogLevel.INFO);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace PluginManager.Database;
|
|||||||
|
|
||||||
public class SqlDatabase
|
public class SqlDatabase
|
||||||
{
|
{
|
||||||
private readonly SQLiteConnection Connection;
|
private readonly SQLiteConnection _connection;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initialize a SQL connection by specifing its private path
|
/// Initialize a SQL connection by specifing its private path
|
||||||
@@ -22,7 +22,7 @@ public class SqlDatabase
|
|||||||
if (!File.Exists(fileName))
|
if (!File.Exists(fileName))
|
||||||
SQLiteConnection.CreateFile(fileName);
|
SQLiteConnection.CreateFile(fileName);
|
||||||
var connectionString = $"URI=file:{fileName}";
|
var connectionString = $"URI=file:{fileName}";
|
||||||
Connection = new SQLiteConnection(connectionString);
|
_connection = new SQLiteConnection(connectionString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ public class SqlDatabase
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task Open()
|
public async Task Open()
|
||||||
{
|
{
|
||||||
await Connection.OpenAsync();
|
await _connection.OpenAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -55,7 +55,7 @@ public class SqlDatabase
|
|||||||
|
|
||||||
query += ")";
|
query += ")";
|
||||||
|
|
||||||
var command = new SQLiteCommand(query, Connection);
|
var command = new SQLiteCommand(query, _connection);
|
||||||
await command.ExecuteNonQueryAsync();
|
await command.ExecuteNonQueryAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ public class SqlDatabase
|
|||||||
|
|
||||||
query += ")";
|
query += ")";
|
||||||
|
|
||||||
var command = new SQLiteCommand(query, Connection);
|
var command = new SQLiteCommand(query, _connection);
|
||||||
command.ExecuteNonQuery();
|
command.ExecuteNonQuery();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ public class SqlDatabase
|
|||||||
{
|
{
|
||||||
var query = $"DELETE FROM {tableName} WHERE {KeyName} = '{KeyValue}'";
|
var query = $"DELETE FROM {tableName} WHERE {KeyName} = '{KeyValue}'";
|
||||||
|
|
||||||
var command = new SQLiteCommand(query, Connection);
|
var command = new SQLiteCommand(query, _connection);
|
||||||
await command.ExecuteNonQueryAsync();
|
await command.ExecuteNonQueryAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ public class SqlDatabase
|
|||||||
{
|
{
|
||||||
var query = $"DELETE FROM {tableName} WHERE {KeyName} = '{KeyValue}'";
|
var query = $"DELETE FROM {tableName} WHERE {KeyName} = '{KeyValue}'";
|
||||||
|
|
||||||
var command = new SQLiteCommand(query, Connection);
|
var command = new SQLiteCommand(query, _connection);
|
||||||
command.ExecuteNonQuery();
|
command.ExecuteNonQuery();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +163,8 @@ public class SqlDatabase
|
|||||||
throw new Exception($"Table {tableName} does not exist");
|
throw new Exception($"Table {tableName} does not exist");
|
||||||
|
|
||||||
await ExecuteAsync(
|
await ExecuteAsync(
|
||||||
$"UPDATE {tableName} SET {ResultColumnName}='{ResultColumnValue}' WHERE {keyName}='{KeyValue}'");
|
$"UPDATE {tableName} SET {ResultColumnName}='{ResultColumnValue}' WHERE {keyName}='{KeyValue}'"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -224,7 +225,7 @@ public class SqlDatabase
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async void Stop()
|
public async void Stop()
|
||||||
{
|
{
|
||||||
await Connection.CloseAsync();
|
await _connection.CloseAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -236,7 +237,7 @@ public class SqlDatabase
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task AddColumnsToTableAsync(string tableName, string[] columns, string TYPE = "TEXT")
|
public async Task AddColumnsToTableAsync(string tableName, string[] columns, string TYPE = "TEXT")
|
||||||
{
|
{
|
||||||
var command = Connection.CreateCommand();
|
var command = _connection.CreateCommand();
|
||||||
command.CommandText = $"SELECT * FROM {tableName}";
|
command.CommandText = $"SELECT * FROM {tableName}";
|
||||||
var reader = await command.ExecuteReaderAsync();
|
var reader = await command.ExecuteReaderAsync();
|
||||||
var tableColumns = new List<string>();
|
var tableColumns = new List<string>();
|
||||||
@@ -260,7 +261,7 @@ public class SqlDatabase
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public void AddColumnsToTable(string tableName, string[] columns, string TYPE = "TEXT")
|
public void AddColumnsToTable(string tableName, string[] columns, string TYPE = "TEXT")
|
||||||
{
|
{
|
||||||
var command = Connection.CreateCommand();
|
var command = _connection.CreateCommand();
|
||||||
command.CommandText = $"SELECT * FROM {tableName}";
|
command.CommandText = $"SELECT * FROM {tableName}";
|
||||||
var reader = command.ExecuteReader();
|
var reader = command.ExecuteReader();
|
||||||
var tableColumns = new List<string>();
|
var tableColumns = new List<string>();
|
||||||
@@ -282,7 +283,7 @@ public class SqlDatabase
|
|||||||
/// <returns>True if the table exists, false if not</returns>
|
/// <returns>True if the table exists, false if not</returns>
|
||||||
public async Task<bool> TableExistsAsync(string tableName)
|
public async Task<bool> TableExistsAsync(string tableName)
|
||||||
{
|
{
|
||||||
var cmd = Connection.CreateCommand();
|
var cmd = _connection.CreateCommand();
|
||||||
cmd.CommandText = $"SELECT name FROM sqlite_master WHERE type='table' AND name='{tableName}'";
|
cmd.CommandText = $"SELECT name FROM sqlite_master WHERE type='table' AND name='{tableName}'";
|
||||||
var result = await cmd.ExecuteScalarAsync();
|
var result = await cmd.ExecuteScalarAsync();
|
||||||
|
|
||||||
@@ -298,7 +299,7 @@ public class SqlDatabase
|
|||||||
/// <returns>True if the table exists, false if not</returns>
|
/// <returns>True if the table exists, false if not</returns>
|
||||||
public bool TableExists(string tableName)
|
public bool TableExists(string tableName)
|
||||||
{
|
{
|
||||||
var cmd = Connection.CreateCommand();
|
var cmd = _connection.CreateCommand();
|
||||||
cmd.CommandText = $"SELECT name FROM sqlite_master WHERE type='table' AND name='{tableName}'";
|
cmd.CommandText = $"SELECT name FROM sqlite_master WHERE type='table' AND name='{tableName}'";
|
||||||
var result = cmd.ExecuteScalar();
|
var result = cmd.ExecuteScalar();
|
||||||
|
|
||||||
@@ -315,7 +316,7 @@ public class SqlDatabase
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task CreateTableAsync(string tableName, params string[] columns)
|
public async Task CreateTableAsync(string tableName, params string[] columns)
|
||||||
{
|
{
|
||||||
var cmd = Connection.CreateCommand();
|
var cmd = _connection.CreateCommand();
|
||||||
cmd.CommandText = $"CREATE TABLE IF NOT EXISTS {tableName} ({string.Join(", ", columns)})";
|
cmd.CommandText = $"CREATE TABLE IF NOT EXISTS {tableName} ({string.Join(", ", columns)})";
|
||||||
await cmd.ExecuteNonQueryAsync();
|
await cmd.ExecuteNonQueryAsync();
|
||||||
}
|
}
|
||||||
@@ -328,7 +329,7 @@ public class SqlDatabase
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public void CreateTable(string tableName, params string[] columns)
|
public void CreateTable(string tableName, params string[] columns)
|
||||||
{
|
{
|
||||||
var cmd = Connection.CreateCommand();
|
var cmd = _connection.CreateCommand();
|
||||||
cmd.CommandText = $"CREATE TABLE IF NOT EXISTS {tableName} ({string.Join(", ", columns)})";
|
cmd.CommandText = $"CREATE TABLE IF NOT EXISTS {tableName} ({string.Join(", ", columns)})";
|
||||||
cmd.ExecuteNonQuery();
|
cmd.ExecuteNonQuery();
|
||||||
}
|
}
|
||||||
@@ -340,9 +341,9 @@ public class SqlDatabase
|
|||||||
/// <returns>The number of rows that the query modified</returns>
|
/// <returns>The number of rows that the query modified</returns>
|
||||||
public async Task<int> ExecuteAsync(string query)
|
public async Task<int> ExecuteAsync(string query)
|
||||||
{
|
{
|
||||||
if (!Connection.State.HasFlag(ConnectionState.Open))
|
if (!_connection.State.HasFlag(ConnectionState.Open))
|
||||||
await Connection.OpenAsync();
|
await _connection.OpenAsync();
|
||||||
var command = new SQLiteCommand(query, Connection);
|
var command = new SQLiteCommand(query, _connection);
|
||||||
var answer = await command.ExecuteNonQueryAsync();
|
var answer = await command.ExecuteNonQueryAsync();
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
@@ -354,9 +355,9 @@ public class SqlDatabase
|
|||||||
/// <returns>The number of rows that the query modified</returns>
|
/// <returns>The number of rows that the query modified</returns>
|
||||||
public int Execute(string query)
|
public int Execute(string query)
|
||||||
{
|
{
|
||||||
if (!Connection.State.HasFlag(ConnectionState.Open))
|
if (!_connection.State.HasFlag(ConnectionState.Open))
|
||||||
Connection.Open();
|
_connection.Open();
|
||||||
var command = new SQLiteCommand(query, Connection);
|
var command = new SQLiteCommand(query, _connection);
|
||||||
var r = command.ExecuteNonQuery();
|
var r = command.ExecuteNonQuery();
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
@@ -369,9 +370,9 @@ public class SqlDatabase
|
|||||||
/// <returns>The result is a string that has all values separated by space character</returns>
|
/// <returns>The result is a string that has all values separated by space character</returns>
|
||||||
public async Task<string?> ReadDataAsync(string query)
|
public async Task<string?> ReadDataAsync(string query)
|
||||||
{
|
{
|
||||||
if (!Connection.State.HasFlag(ConnectionState.Open))
|
if (!_connection.State.HasFlag(ConnectionState.Open))
|
||||||
await Connection.OpenAsync();
|
await _connection.OpenAsync();
|
||||||
var command = new SQLiteCommand(query, Connection);
|
var command = new SQLiteCommand(query, _connection);
|
||||||
var reader = await command.ExecuteReaderAsync();
|
var reader = await command.ExecuteReaderAsync();
|
||||||
|
|
||||||
var values = new object[reader.FieldCount];
|
var values = new object[reader.FieldCount];
|
||||||
@@ -391,9 +392,9 @@ public class SqlDatabase
|
|||||||
/// <returns>The result is a string that has all values separated by space character</returns>
|
/// <returns>The result is a string that has all values separated by space character</returns>
|
||||||
public string? ReadData(string query)
|
public string? ReadData(string query)
|
||||||
{
|
{
|
||||||
if (!Connection.State.HasFlag(ConnectionState.Open))
|
if (!_connection.State.HasFlag(ConnectionState.Open))
|
||||||
Connection.Open();
|
_connection.Open();
|
||||||
var command = new SQLiteCommand(query, Connection);
|
var command = new SQLiteCommand(query, _connection);
|
||||||
var reader = command.ExecuteReader();
|
var reader = command.ExecuteReader();
|
||||||
|
|
||||||
var values = new object[reader.FieldCount];
|
var values = new object[reader.FieldCount];
|
||||||
@@ -413,9 +414,9 @@ public class SqlDatabase
|
|||||||
/// <returns>The first row as separated items</returns>
|
/// <returns>The first row as separated items</returns>
|
||||||
public async Task<object[]?> ReadDataArrayAsync(string query)
|
public async Task<object[]?> ReadDataArrayAsync(string query)
|
||||||
{
|
{
|
||||||
if (!Connection.State.HasFlag(ConnectionState.Open))
|
if (!_connection.State.HasFlag(ConnectionState.Open))
|
||||||
await Connection.OpenAsync();
|
await _connection.OpenAsync();
|
||||||
var command = new SQLiteCommand(query, Connection);
|
var command = new SQLiteCommand(query, _connection);
|
||||||
var reader = await command.ExecuteReaderAsync();
|
var reader = await command.ExecuteReaderAsync();
|
||||||
|
|
||||||
var values = new object[reader.FieldCount];
|
var values = new object[reader.FieldCount];
|
||||||
@@ -436,9 +437,9 @@ public class SqlDatabase
|
|||||||
/// <returns>The first row as separated items</returns>
|
/// <returns>The first row as separated items</returns>
|
||||||
public object[]? ReadDataArray(string query)
|
public object[]? ReadDataArray(string query)
|
||||||
{
|
{
|
||||||
if (!Connection.State.HasFlag(ConnectionState.Open))
|
if (!_connection.State.HasFlag(ConnectionState.Open))
|
||||||
Connection.Open();
|
_connection.Open();
|
||||||
var command = new SQLiteCommand(query, Connection);
|
var command = new SQLiteCommand(query, _connection);
|
||||||
var reader = command.ExecuteReader();
|
var reader = command.ExecuteReader();
|
||||||
|
|
||||||
var values = new object[reader.FieldCount];
|
var values = new object[reader.FieldCount];
|
||||||
@@ -459,9 +460,9 @@ public class SqlDatabase
|
|||||||
/// <returns>A list of string arrays representing the values that the query returns</returns>
|
/// <returns>A list of string arrays representing the values that the query returns</returns>
|
||||||
public async Task<List<string[]>?> ReadAllRowsAsync(string query)
|
public async Task<List<string[]>?> ReadAllRowsAsync(string query)
|
||||||
{
|
{
|
||||||
if (!Connection.State.HasFlag(ConnectionState.Open))
|
if (!_connection.State.HasFlag(ConnectionState.Open))
|
||||||
await Connection.OpenAsync();
|
await _connection.OpenAsync();
|
||||||
var command = new SQLiteCommand(query, Connection);
|
var command = new SQLiteCommand(query, _connection);
|
||||||
var reader = await command.ExecuteReaderAsync();
|
var reader = await command.ExecuteReaderAsync();
|
||||||
|
|
||||||
if (!reader.HasRows)
|
if (!reader.HasRows)
|
||||||
@@ -479,4 +480,165 @@ public class SqlDatabase
|
|||||||
|
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a parameter for a query
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name of the parameter</param>
|
||||||
|
/// <param name="value">The value of the parameter</param>
|
||||||
|
/// <returns>The SQLiteParameter that has the name, value and DBType set according to your inputs</returns>
|
||||||
|
private SQLiteParameter? CreateParameter(string name, object value)
|
||||||
|
{
|
||||||
|
var parameter = new SQLiteParameter(name);
|
||||||
|
parameter.Value = value;
|
||||||
|
|
||||||
|
if (value is string)
|
||||||
|
parameter.DbType = DbType.String;
|
||||||
|
else if (value is int)
|
||||||
|
parameter.DbType = DbType.Int32;
|
||||||
|
else if (value is long)
|
||||||
|
parameter.DbType = DbType.Int64;
|
||||||
|
else if (value is float)
|
||||||
|
parameter.DbType = DbType.Single;
|
||||||
|
else if (value is double)
|
||||||
|
parameter.DbType = DbType.Double;
|
||||||
|
else if (value is bool)
|
||||||
|
parameter.DbType = DbType.Boolean;
|
||||||
|
else if (value is DateTime)
|
||||||
|
parameter.DbType = DbType.DateTime;
|
||||||
|
else if (value is byte[])
|
||||||
|
parameter.DbType = DbType.Binary;
|
||||||
|
else if (value is Guid)
|
||||||
|
parameter.DbType = DbType.Guid;
|
||||||
|
else if (value is decimal)
|
||||||
|
parameter.DbType = DbType.Decimal;
|
||||||
|
else if (value is TimeSpan)
|
||||||
|
parameter.DbType = DbType.Time;
|
||||||
|
else if (value is DateTimeOffset)
|
||||||
|
parameter.DbType = DbType.DateTimeOffset;
|
||||||
|
else if (value is ushort)
|
||||||
|
parameter.DbType = DbType.UInt16;
|
||||||
|
else if (value is uint)
|
||||||
|
parameter.DbType = DbType.UInt32;
|
||||||
|
else if (value is ulong)
|
||||||
|
parameter.DbType = DbType.UInt64;
|
||||||
|
else if (value is sbyte)
|
||||||
|
parameter.DbType = DbType.SByte;
|
||||||
|
else if (value is short)
|
||||||
|
parameter.DbType = DbType.Int16;
|
||||||
|
else if (value is byte)
|
||||||
|
parameter.DbType = DbType.Byte;
|
||||||
|
else if (value is char)
|
||||||
|
parameter.DbType = DbType.StringFixedLength;
|
||||||
|
else if (value is char[])
|
||||||
|
parameter.DbType = DbType.StringFixedLength;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a parameter for a query. The function automatically detects the type of the value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameterValues">The parameter raw inputs. The Key is name and the Value is the value of the parameter</param>
|
||||||
|
/// <returns>The SQLiteParameter that has the name, value and DBType set according to your inputs</returns>
|
||||||
|
private SQLiteParameter? CreateParameter(KeyValuePair<string, object> parameterValues)
|
||||||
|
{
|
||||||
|
return CreateParameter(parameterValues.Key, parameterValues.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a query with parameters
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">The query to execute</param>
|
||||||
|
/// <param name="parameters">The parameters of the query</param>
|
||||||
|
/// <returns>The number of rows that the query modified in the database</returns>
|
||||||
|
public async Task<int> ExecuteNonQueryAsync(string query, params KeyValuePair<string, object>[] parameters)
|
||||||
|
{
|
||||||
|
if (!_connection.State.HasFlag(ConnectionState.Open))
|
||||||
|
await _connection.OpenAsync();
|
||||||
|
|
||||||
|
var command = new SQLiteCommand(query, _connection);
|
||||||
|
foreach (var parameter in parameters)
|
||||||
|
{
|
||||||
|
var p = CreateParameter(parameter);
|
||||||
|
if (p is not null)
|
||||||
|
command.Parameters.Add(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await command.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a query with parameters that returns a specific type of object. The function will return the first row of the result transformed into the specified type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">The query to execute</param>
|
||||||
|
/// <param name="convertor">The convertor function that will convert each row of the response into an object of <typeparamref name="T"/></param>
|
||||||
|
/// <param name="parameters">The parameters of the query</param>
|
||||||
|
/// <typeparam name="T">The return object type</typeparam>
|
||||||
|
/// <returns>An object of type T that represents the output of the convertor function based on the array of objects that the first row of the result has</returns>
|
||||||
|
public async Task<T?> ReadObjectOfTypeAsync<T>(string query, Func<object[], T> convertor, params KeyValuePair<string, object>[] parameters)
|
||||||
|
{
|
||||||
|
if (!_connection.State.HasFlag(ConnectionState.Open))
|
||||||
|
await _connection.OpenAsync();
|
||||||
|
|
||||||
|
var command = new SQLiteCommand(query, _connection);
|
||||||
|
foreach (var parameter in parameters)
|
||||||
|
{
|
||||||
|
var p = CreateParameter(parameter);
|
||||||
|
if (p is not null)
|
||||||
|
command.Parameters.Add(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
var reader = await command.ExecuteReaderAsync();
|
||||||
|
var values = new object[reader.FieldCount];
|
||||||
|
if (reader.Read())
|
||||||
|
{
|
||||||
|
reader.GetValues(values);
|
||||||
|
return convertor(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a query with parameters that returns a specific type of object. The function will return a list of objects of the specified type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">The query to execute</param>
|
||||||
|
/// <param name="convertor">The convertor from object[] to T</param>
|
||||||
|
/// <param name="parameters">The parameters of the query</param>
|
||||||
|
/// <typeparam name="T">The expected object type</typeparam>
|
||||||
|
/// <returns>A list of objects of type T that represents each line of the output of the specified query, converted to T</returns>
|
||||||
|
public async Task<List<T>> ReadListOfTypeAsync<T>(string query, Func<object[], T> convertor,
|
||||||
|
params KeyValuePair<string, object>[] parameters)
|
||||||
|
{
|
||||||
|
if (!_connection.State.HasFlag(ConnectionState.Open))
|
||||||
|
await _connection.OpenAsync();
|
||||||
|
|
||||||
|
var command = new SQLiteCommand(query, _connection);
|
||||||
|
foreach (var parameter in parameters)
|
||||||
|
{
|
||||||
|
var p = CreateParameter(parameter);
|
||||||
|
if (p is not null)
|
||||||
|
command.Parameters.Add(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
var reader = await command.ExecuteReaderAsync();
|
||||||
|
//
|
||||||
|
if (!reader.HasRows)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
List<T> rows = new();
|
||||||
|
while (await reader.ReadAsync())
|
||||||
|
{
|
||||||
|
var values = new object[reader.FieldCount];
|
||||||
|
reader.GetValues(values);
|
||||||
|
rows.Add(convertor(values));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public interface DBCommand
|
|||||||
/// The main body of the command. This is what is executed when user calls the command in Server
|
/// The main body of the command. This is what is executed when user calls the command in Server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="args">The disocrd Context</param>
|
/// <param name="args">The disocrd Context</param>
|
||||||
void ExecuteServer(DBCommandExecutingArguments args)
|
void ExecuteServer(DbCommandExecutingArguments args)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ public interface DBCommand
|
|||||||
/// The main body of the command. This is what is executed when user calls the command in DM
|
/// The main body of the command. This is what is executed when user calls the command in DM
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="args">The disocrd Context</param>
|
/// <param name="args">The disocrd Context</param>
|
||||||
void ExecuteDM(DBCommandExecutingArguments args)
|
void ExecuteDM(DbCommandExecutingArguments args)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Discord;
|
using Discord;
|
||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
|
|
||||||
@@ -8,14 +10,15 @@ public interface DBSlashCommand
|
|||||||
{
|
{
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
string Description { get; }
|
string Description { get; }
|
||||||
|
|
||||||
bool canUseDM { get; }
|
bool canUseDM { get; }
|
||||||
|
bool HasInteraction { get; }
|
||||||
|
|
||||||
List<SlashCommandOptionBuilder> Options { get; }
|
List<SlashCommandOptionBuilder> Options { get; }
|
||||||
|
|
||||||
void ExecuteServer(SocketSlashCommand context)
|
void ExecuteServer(SocketSlashCommand context)
|
||||||
{
|
{ }
|
||||||
}
|
|
||||||
|
|
||||||
void ExecuteDM(SocketSlashCommand context) { }
|
void ExecuteDM(SocketSlashCommand context) { }
|
||||||
|
|
||||||
|
Task ExecuteInteraction(SocketInteraction interaction) => Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace PluginManager.Interfaces.Exceptions;
|
|
||||||
|
|
||||||
public interface IException
|
|
||||||
{
|
|
||||||
public List<string> Messages { get; set; }
|
|
||||||
public bool isFatal { get; }
|
|
||||||
public string GenerateFullMessage();
|
|
||||||
public void HandleException();
|
|
||||||
|
|
||||||
public IException AppendError(string message);
|
|
||||||
|
|
||||||
public IException AppendError(List<string> messages);
|
|
||||||
public IException IsFatal(bool isFatal = true);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using PluginManager.Others;
|
using PluginManager.Others;
|
||||||
|
using PluginManager.Others.Actions;
|
||||||
|
|
||||||
namespace PluginManager.Interfaces;
|
namespace PluginManager.Interfaces;
|
||||||
|
|
||||||
@@ -11,6 +14,8 @@ public interface ICommandAction
|
|||||||
|
|
||||||
public string? Usage { get; }
|
public string? Usage { get; }
|
||||||
|
|
||||||
|
public IEnumerable<InternalActionOption> ListOfOptions { get; }
|
||||||
|
|
||||||
public InternalActionRunType RunType { get; }
|
public InternalActionRunType RunType { get; }
|
||||||
|
|
||||||
public Task Execute(string[]? args);
|
public Task Execute(string[]? args);
|
||||||
14
PluginManager/Interfaces/Logger/ILog.cs
Normal file
14
PluginManager/Interfaces/Logger/ILog.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using PluginManager.Others;
|
||||||
|
|
||||||
|
namespace PluginManager.Interfaces.Logger;
|
||||||
|
|
||||||
|
internal interface ILog
|
||||||
|
{
|
||||||
|
string Message { get; set; }
|
||||||
|
|
||||||
|
Type? Source { get; set; }
|
||||||
|
|
||||||
|
LogType Type { get; set; }
|
||||||
|
DateTime ThrowTime { get; set; }
|
||||||
|
}
|
||||||
19
PluginManager/Interfaces/Logger/ILogger.cs
Normal file
19
PluginManager/Interfaces/Logger/ILogger.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PluginManager.Others;
|
||||||
|
using PluginManager.Others.Logger;
|
||||||
|
|
||||||
|
namespace PluginManager.Interfaces.Logger;
|
||||||
|
|
||||||
|
internal interface ILogger
|
||||||
|
{
|
||||||
|
bool IsEnabled { get; init; }
|
||||||
|
bool OutputToFile { get; init; }
|
||||||
|
|
||||||
|
string OutputFile { get; init; }
|
||||||
|
|
||||||
|
event EventHandler<Log> OnLog;
|
||||||
|
void Log(
|
||||||
|
string message = "", Type? source = default, LogType type = LogType.INFO,
|
||||||
|
DateTime throwTime = default);
|
||||||
|
}
|
||||||
87
PluginManager/Interfaces/Updater/AppVersion.cs
Normal file
87
PluginManager/Interfaces/Updater/AppVersion.cs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace PluginManager.Interfaces.Updater
|
||||||
|
{
|
||||||
|
public class AppVersion : IVersion
|
||||||
|
{
|
||||||
|
public int Major { get; set; }
|
||||||
|
|
||||||
|
public int Minor { get; set; }
|
||||||
|
|
||||||
|
public int Patch { get; set; }
|
||||||
|
|
||||||
|
public int PatchVersion { get; set; }
|
||||||
|
|
||||||
|
public static readonly AppVersion CurrentAppVersion = new AppVersion(Config.AppSettings["Version"]);
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
PluginManager/Interfaces/Updater/IVersion.cs
Normal file
17
PluginManager/Interfaces/Updater/IVersion.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace PluginManager.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();
|
||||||
|
}
|
||||||
76
PluginManager/Interfaces/Updater/Version.cs
Normal file
76
PluginManager/Interfaces/Updater/Version.cs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PluginManager.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,22 +13,22 @@ public class ActionsLoader
|
|||||||
{
|
{
|
||||||
public delegate void ActionLoaded(string name, string typeName, bool success, Exception? e = null);
|
public delegate void ActionLoaded(string name, string typeName, bool success, Exception? e = null);
|
||||||
|
|
||||||
private readonly string actionExtension = "dll";
|
private readonly string _actionExtension = "dll";
|
||||||
|
|
||||||
private readonly string actionFolder = @"./Data/Actions/";
|
private readonly string _actionFolder = @"./Data/Plugins/";
|
||||||
|
|
||||||
public ActionsLoader(string path, string extension)
|
public ActionsLoader(string path, string extension)
|
||||||
{
|
{
|
||||||
actionFolder = path;
|
_actionFolder = path;
|
||||||
actionExtension = extension;
|
_actionExtension = extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
public event ActionLoaded? ActionLoadedEvent;
|
public event ActionLoaded? ActionLoadedEvent;
|
||||||
|
|
||||||
public async Task<List<ICommandAction>?> Load()
|
public async Task<List<ICommandAction>?> Load()
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(actionFolder);
|
Directory.CreateDirectory(_actionFolder);
|
||||||
var files = Directory.GetFiles(actionFolder, $"*.{actionExtension}", SearchOption.AllDirectories);
|
var files = Directory.GetFiles(_actionFolder, $"*.{_actionExtension}", SearchOption.AllDirectories);
|
||||||
|
|
||||||
var actions = new List<ICommandAction>();
|
var actions = new List<ICommandAction>();
|
||||||
|
|
||||||
|
|||||||
21
PluginManager/Loaders/FileLoaderResult.cs
Normal file
21
PluginManager/Loaders/FileLoaderResult.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
namespace PluginManager.Loaders;
|
||||||
|
|
||||||
|
public class FileLoaderResult
|
||||||
|
{
|
||||||
|
public string PluginName { get; private set; }
|
||||||
|
|
||||||
|
public string ErrorMessage { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
|
public FileLoaderResult(string pluginName, string errorMessage)
|
||||||
|
{
|
||||||
|
PluginName = pluginName;
|
||||||
|
ErrorMessage = errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileLoaderResult(string pluginName)
|
||||||
|
{
|
||||||
|
PluginName = pluginName;
|
||||||
|
ErrorMessage = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,145 +1,89 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using PluginManager.Interfaces;
|
using PluginManager.Interfaces;
|
||||||
using PluginManager.Others;
|
using PluginManager.Others;
|
||||||
|
|
||||||
namespace PluginManager.Loaders;
|
namespace PluginManager.Loaders;
|
||||||
|
|
||||||
internal class LoaderArgs : EventArgs
|
|
||||||
{
|
|
||||||
internal string? PluginName { get; init; }
|
|
||||||
internal string? TypeName { get; init; }
|
|
||||||
internal bool IsLoaded { get; init; }
|
|
||||||
internal Exception? Exception { get; init; }
|
|
||||||
internal object? Plugin { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class Loader
|
internal class Loader
|
||||||
{
|
{
|
||||||
internal Loader(string path, string extension)
|
private readonly string _SearchPath;
|
||||||
|
private readonly string _FileExtension;
|
||||||
|
|
||||||
|
internal delegate void FileLoadedHandler(FileLoaderResult result);
|
||||||
|
|
||||||
|
internal delegate void PluginLoadedHandler(PluginLoadResultData result);
|
||||||
|
|
||||||
|
internal event FileLoadedHandler? OnFileLoadedException;
|
||||||
|
internal event PluginLoadedHandler? OnPluginLoaded;
|
||||||
|
|
||||||
|
internal Loader(string searchPath, string fileExtension)
|
||||||
{
|
{
|
||||||
this.path = path;
|
_SearchPath = searchPath;
|
||||||
this.extension = extension;
|
_FileExtension = fileExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal async Task Load()
|
||||||
private string path { get; }
|
|
||||||
private string extension { get; }
|
|
||||||
|
|
||||||
internal event FileLoadedEventHandler? FileLoaded;
|
|
||||||
|
|
||||||
internal event PluginLoadedEventHandler? PluginLoaded;
|
|
||||||
|
|
||||||
|
|
||||||
internal (List<DBEvent>?, List<DBCommand>?, List<DBSlashCommand>?) Load()
|
|
||||||
{
|
{
|
||||||
List<DBEvent> events = new();
|
if (!Directory.Exists(_SearchPath))
|
||||||
List<DBSlashCommand> slashCommands = new();
|
|
||||||
List<DBCommand> commands = new();
|
|
||||||
|
|
||||||
if (!Directory.Exists(path))
|
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(path);
|
Directory.CreateDirectory(_SearchPath);
|
||||||
return (null, null, null);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var files = Directory.GetFiles(path, $"*.{extension}", SearchOption.AllDirectories);
|
var files = Directory.GetFiles(_SearchPath, $"*.{_FileExtension}", SearchOption.TopDirectoryOnly);
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Assembly.LoadFrom(file);
|
Assembly.LoadFrom(file);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch
|
||||||
{
|
{
|
||||||
Config.Logger.Log("PluginName: " + new FileInfo(file).Name.Split('.')[0] + " not loaded", this,
|
OnFileLoadedException?.Invoke(new FileLoaderResult(file, $"Failed to load file {file}"));
|
||||||
LogLevel.ERROR);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FileLoaded != null)
|
|
||||||
{
|
|
||||||
var args = new LoaderArgs
|
|
||||||
{
|
|
||||||
Exception = null,
|
|
||||||
TypeName = null,
|
|
||||||
IsLoaded = false,
|
|
||||||
PluginName = new FileInfo(file).Name.Split('.')[0],
|
|
||||||
Plugin = null
|
|
||||||
};
|
|
||||||
FileLoaded.Invoke(args);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await LoadEverythingOfType<DBEvent>();
|
||||||
return (LoadItems<DBEvent>(), LoadItems<DBCommand>(), LoadItems<DBSlashCommand>());
|
await LoadEverythingOfType<DBCommand>();
|
||||||
|
await LoadEverythingOfType<DBSlashCommand>();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal List<T> LoadItems<T>()
|
private async Task LoadEverythingOfType<T>()
|
||||||
{
|
{
|
||||||
List<T> list = new();
|
|
||||||
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var interfaceType = typeof(T);
|
|
||||||
var types = AppDomain.CurrentDomain.GetAssemblies()
|
var types = AppDomain.CurrentDomain.GetAssemblies()
|
||||||
.SelectMany(a => a.GetTypes())
|
.SelectMany(s => s.GetTypes())
|
||||||
.Where(p => interfaceType.IsAssignableFrom(p) && p.IsClass)
|
.Where(p => typeof(T).IsAssignableFrom(p) && !p.IsInterface);
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
|
|
||||||
list.Clear();
|
|
||||||
foreach (var type in types)
|
foreach (var type in types)
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var plugin = (T)Activator.CreateInstance(type)!;
|
var plugin = (T?)Activator.CreateInstance(type);
|
||||||
list.Add(plugin);
|
|
||||||
|
|
||||||
|
if (plugin is null)
|
||||||
if (PluginLoaded != null)
|
|
||||||
PluginLoaded.Invoke(new LoaderArgs
|
|
||||||
{
|
{
|
||||||
Exception = null,
|
throw new Exception($"Failed to create instance of plugin with type {type.FullName} [{type.Assembly}]");
|
||||||
IsLoaded = true,
|
|
||||||
PluginName = type.FullName,
|
|
||||||
TypeName = typeof(T) == typeof(DBCommand) ? "DBCommand" :
|
|
||||||
typeof(T) == typeof(DBEvent) ? "DBEvent" :
|
|
||||||
typeof(T) == typeof(DBSlashCommand) ? "DBSlashCommand" :
|
|
||||||
null,
|
|
||||||
Plugin = plugin
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
var pluginType = plugin switch
|
||||||
|
{
|
||||||
|
DBEvent => PluginType.EVENT,
|
||||||
|
DBCommand => PluginType.COMMAND,
|
||||||
|
DBSlashCommand => PluginType.SLASH_COMMAND,
|
||||||
|
_ => PluginType.UNKNOWN
|
||||||
|
};
|
||||||
|
|
||||||
|
OnPluginLoaded?.Invoke(new PluginLoadResultData(type.FullName, pluginType, true, plugin: plugin));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
if (PluginLoaded != null)
|
OnPluginLoaded?.Invoke(new PluginLoadResultData(type.FullName, PluginType.UNKNOWN, false, ex.Message));
|
||||||
PluginLoaded.Invoke(new LoaderArgs
|
}
|
||||||
{
|
}
|
||||||
Exception = ex,
|
|
||||||
IsLoaded = false,
|
|
||||||
PluginName = type.FullName,
|
|
||||||
TypeName = nameof(T)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Config.Logger.Log(ex.Message, this, LogLevel.ERROR);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal delegate void FileLoadedEventHandler(LoaderArgs args);
|
|
||||||
|
|
||||||
internal delegate void PluginLoadedEventHandler(LoaderArgs args);
|
|
||||||
}
|
}
|
||||||
|
|||||||
23
PluginManager/Loaders/PluginLoadResultData.cs
Normal file
23
PluginManager/Loaders/PluginLoadResultData.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using PluginManager.Others;
|
||||||
|
|
||||||
|
namespace PluginManager.Loaders;
|
||||||
|
|
||||||
|
public class PluginLoadResultData
|
||||||
|
{
|
||||||
|
public string PluginName { get; init; }
|
||||||
|
public PluginType PluginType { get; init; }
|
||||||
|
public string? ErrorMessage { get; init; }
|
||||||
|
public bool IsSuccess { get; init; }
|
||||||
|
|
||||||
|
public object Plugin { get; init; }
|
||||||
|
|
||||||
|
public PluginLoadResultData(string pluginName, PluginType pluginType, bool isSuccess, string? errorMessage = null,
|
||||||
|
object? plugin = null)
|
||||||
|
{
|
||||||
|
PluginName = pluginName;
|
||||||
|
PluginType = pluginType;
|
||||||
|
IsSuccess = isSuccess;
|
||||||
|
ErrorMessage = errorMessage;
|
||||||
|
Plugin = plugin is null ? new() : plugin;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,183 +1,85 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Discord;
|
|
||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
using PluginManager.Interfaces;
|
using PluginManager.Interfaces;
|
||||||
using PluginManager.Others;
|
using PluginManager.Others;
|
||||||
|
|
||||||
|
|
||||||
namespace PluginManager.Loaders;
|
namespace PluginManager.Loaders;
|
||||||
|
|
||||||
public class PluginLoader
|
public class PluginLoader
|
||||||
{
|
{
|
||||||
public delegate void CMDLoaded(string name, string typeName, bool success, Exception? e = null);
|
internal readonly DiscordSocketClient _Client;
|
||||||
|
|
||||||
public delegate void EVELoaded(string name, string typeName, bool success, Exception? e = null);
|
public delegate void CommandLoaded(PluginLoadResultData resultData);
|
||||||
|
|
||||||
public delegate void SLSHLoaded(string name, string tyypename, bool success, Exception? e = null);
|
public delegate void EventLoaded(PluginLoadResultData resultData);
|
||||||
|
|
||||||
private const string pluginFolder = @"./Data/Plugins/";
|
public delegate void SlashCommandLoaded(PluginLoadResultData resultData);
|
||||||
|
|
||||||
internal const string pluginExtension = "dll";
|
public CommandLoaded? OnCommandLoaded;
|
||||||
private readonly DiscordSocketClient _client;
|
public EventLoaded? OnEventLoaded;
|
||||||
|
public SlashCommandLoaded? OnSlashCommandLoaded;
|
||||||
|
|
||||||
/// <summary>
|
public static List<DBCommand> Commands { get; private set; } = new List<DBCommand>();
|
||||||
/// Event that is fired when a <see cref="DBCommand" /> is successfully loaded into commands list
|
public static List<DBEvent> Events { get; private set; } = new List<DBEvent>();
|
||||||
/// </summary>
|
public static List<DBSlashCommand> SlashCommands { get; private set; } = new List<DBSlashCommand>();
|
||||||
public CMDLoaded? onCMDLoad;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event that is fired when a <see cref="DBEvent" /> is successfully loaded into events list
|
|
||||||
/// </summary>
|
|
||||||
public EVELoaded? onEVELoad;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event that is fired when a <see cref="DBEvent" /> is successfully loaded into events list
|
|
||||||
/// </summary>
|
|
||||||
public SLSHLoaded? onSLSHLoad;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The Plugin Loader constructor
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="discordSocketClient">The discord bot client where the plugins will pe attached to</param>
|
|
||||||
public PluginLoader(DiscordSocketClient discordSocketClient)
|
public PluginLoader(DiscordSocketClient discordSocketClient)
|
||||||
{
|
{
|
||||||
_client = discordSocketClient;
|
_Client = discordSocketClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task LoadPlugins()
|
||||||
/// <summary>
|
|
||||||
/// A list of <see cref="DBCommand" /> commands
|
|
||||||
/// </summary>
|
|
||||||
public static List<DBCommand>? Commands { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A list of <see cref="DBEvent" /> commands
|
|
||||||
/// </summary>
|
|
||||||
public static List<DBEvent>? Events { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A list of <see cref="DBSlashCommand" /> commands
|
|
||||||
/// </summary>
|
|
||||||
public static List<DBSlashCommand>? SlashCommands { get; set; }
|
|
||||||
|
|
||||||
public static int PluginsLoaded
|
|
||||||
{
|
{
|
||||||
get
|
Config.Logger.Log("Loading plugins...", typeof(PluginLoader));
|
||||||
{
|
|
||||||
var count = 0;
|
var loader = new Loader(Config.AppSettings["PluginFolder"], "dll");
|
||||||
if (Commands is not null)
|
|
||||||
count += Commands.Count;
|
//await this.ResetSlashCommands();
|
||||||
if (Events is not null)
|
|
||||||
count += Events.Count;
|
loader.OnFileLoadedException += FileLoadedException;
|
||||||
if (SlashCommands is not null)
|
loader.OnPluginLoaded += OnPluginLoaded;
|
||||||
count += SlashCommands.Count;
|
|
||||||
return count;
|
await loader.Load();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private void FileLoadedException(FileLoaderResult result)
|
||||||
/// The main mathod that is called to load all events
|
|
||||||
/// </summary>
|
|
||||||
public async void LoadPlugins()
|
|
||||||
{
|
{
|
||||||
//Load all plugins
|
Config.Logger.Log(result.ErrorMessage, typeof(PluginLoader), LogType.ERROR);
|
||||||
|
|
||||||
Commands = new List<DBCommand>();
|
|
||||||
Events = new List<DBEvent>();
|
|
||||||
SlashCommands = new List<DBSlashCommand>();
|
|
||||||
|
|
||||||
Config.Logger.Log("Starting plugin loader ... Client: " + _client.CurrentUser.Username, this,
|
|
||||||
LogLevel.INFO);
|
|
||||||
|
|
||||||
var loader = new Loader("./Data/Plugins", "dll");
|
|
||||||
loader.FileLoaded += args => Config.Logger.Log($"{args.PluginName} file Loaded", this, LogLevel.INFO);
|
|
||||||
loader.PluginLoaded += Loader_PluginLoaded;
|
|
||||||
var res = loader.Load();
|
|
||||||
Events = res.Item1;
|
|
||||||
Commands = res.Item2;
|
|
||||||
SlashCommands = res.Item3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Loader_PluginLoaded(LoaderArgs args)
|
private async void OnPluginLoaded(PluginLoadResultData result)
|
||||||
{
|
{
|
||||||
switch (args.TypeName)
|
switch (result.PluginType)
|
||||||
{
|
{
|
||||||
case "DBCommand":
|
case PluginType.COMMAND:
|
||||||
onCMDLoad?.Invoke(((DBCommand)args.Plugin!).Command, args.TypeName!, args.IsLoaded, args.Exception);
|
Commands.Add((DBCommand)result.Plugin);
|
||||||
|
OnCommandLoaded?.Invoke(result);
|
||||||
break;
|
break;
|
||||||
case "DBEvent":
|
case PluginType.EVENT:
|
||||||
try
|
if (this.TryStartEvent((DBEvent)result.Plugin))
|
||||||
{
|
{
|
||||||
if (args.IsLoaded)
|
Events.Add((DBEvent)result.Plugin);
|
||||||
((DBEvent)args.Plugin!).Start(_client);
|
OnEventLoaded?.Invoke(result);
|
||||||
|
|
||||||
onEVELoad?.Invoke(((DBEvent)args.Plugin!).Name, args.TypeName!, args.IsLoaded, args.Exception);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Config.Logger.Log(ex.Message, this, LogLevel.ERROR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "DBSlashCommand":
|
case PluginType.SLASH_COMMAND:
|
||||||
if (args.IsLoaded)
|
if (await this.TryStartSlashCommand((DBSlashCommand)result.Plugin))
|
||||||
{
|
{
|
||||||
var slash = (DBSlashCommand)args.Plugin;
|
if(((DBSlashCommand)result.Plugin).HasInteraction)
|
||||||
var builder = new SlashCommandBuilder();
|
_Client.InteractionCreated += ((DBSlashCommand)result.Plugin).ExecuteInteraction;
|
||||||
builder.WithName(slash.Name);
|
SlashCommands.Add((DBSlashCommand)result.Plugin);
|
||||||
builder.WithDescription(slash.Description);
|
OnSlashCommandLoaded?.Invoke(result);
|
||||||
builder.WithDMPermission(slash.canUseDM);
|
|
||||||
builder.Options = slash.Options;
|
|
||||||
|
|
||||||
onSLSHLoad?.Invoke(((DBSlashCommand)args.Plugin!).Name, args.TypeName, args.IsLoaded,
|
|
||||||
args.Exception);
|
|
||||||
await _client.CreateGlobalApplicationCommandAsync(builder.Build());
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
Config.Logger.Log($"Failed to start slash command {result.PluginName}", typeof(PluginLoader), LogType.ERROR);
|
||||||
|
break;
|
||||||
|
case PluginType.UNKNOWN:
|
||||||
|
default:
|
||||||
|
Config.Logger.Log("Unknown plugin type", typeof(PluginLoader), LogType.ERROR);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task LoadPluginFromAssembly(Assembly asmb, DiscordSocketClient client)
|
|
||||||
{
|
|
||||||
var types = asmb.GetTypes();
|
|
||||||
foreach (var type in types)
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (type.IsClass && typeof(DBEvent).IsAssignableFrom(type))
|
|
||||||
{
|
|
||||||
var instance = (DBEvent)Activator.CreateInstance(type);
|
|
||||||
instance.Start(client);
|
|
||||||
Events.Add(instance);
|
|
||||||
Config.Logger.Log($"[EVENT] Loaded external {type.FullName}!", LogLevel.INFO);
|
|
||||||
}
|
|
||||||
else if (type.IsClass && typeof(DBCommand).IsAssignableFrom(type))
|
|
||||||
{
|
|
||||||
var instance = (DBCommand)Activator.CreateInstance(type);
|
|
||||||
Commands.Add(instance);
|
|
||||||
Config.Logger.Log($"[CMD] Instance: {type.FullName} loaded !", LogLevel.INFO);
|
|
||||||
}
|
|
||||||
else if (type.IsClass && typeof(DBSlashCommand).IsAssignableFrom(type))
|
|
||||||
{
|
|
||||||
var instance = (DBSlashCommand)Activator.CreateInstance(type);
|
|
||||||
var builder = new SlashCommandBuilder();
|
|
||||||
builder.WithName(instance.Name);
|
|
||||||
builder.WithDescription(instance.Description);
|
|
||||||
builder.WithDMPermission(instance.canUseDM);
|
|
||||||
builder.Options = instance.Options;
|
|
||||||
|
|
||||||
await client.CreateGlobalApplicationCommandAsync(builder.Build());
|
|
||||||
SlashCommands.Add(instance);
|
|
||||||
Config.Logger.Log($"[SLASH] Instance: {type.FullName} loaded !", LogLevel.INFO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
//Console.WriteLine(ex.Message);
|
|
||||||
Config.Logger.Error(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
97
PluginManager/Loaders/PluginLoaderExtensions.cs
Normal file
97
PluginManager/Loaders/PluginLoaderExtensions.cs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Discord;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
|
||||||
|
using PluginManager.Interfaces;
|
||||||
|
using PluginManager.Others;
|
||||||
|
|
||||||
|
namespace PluginManager.Loaders;
|
||||||
|
|
||||||
|
internal static class PluginLoaderExtensions
|
||||||
|
{
|
||||||
|
internal static bool TryStartEvent(this PluginLoader pluginLoader, DBEvent? dbEvent)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (dbEvent is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(dbEvent));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dbEvent.Start(pluginLoader._Client);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Config.Logger.Log($"Error starting event {dbEvent.Name}: {e.Message}", typeof(PluginLoader), LogType.ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static async Task ResetSlashCommands(this PluginLoader pluginLoader)
|
||||||
|
{
|
||||||
|
await pluginLoader._Client.Rest.DeleteAllGlobalCommandsAsync();
|
||||||
|
|
||||||
|
if(pluginLoader._Client.Guilds.Count == 0) return;
|
||||||
|
if (!ulong.TryParse(Config.ServerID, out _))
|
||||||
|
{
|
||||||
|
Config.Logger.Log("Invalid ServerID in config file. Can not reset specific guild commands", typeof(PluginLoader), LogType.ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SocketGuild? guild = pluginLoader._Client.GetGuild(ulong.Parse(Config.ServerID));
|
||||||
|
if(guild is null)
|
||||||
|
{
|
||||||
|
Config.Logger.Log("Failed to get guild with ID " + Config.ServerID, typeof(PluginLoader), LogType.ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await guild.DeleteApplicationCommandsAsync();
|
||||||
|
|
||||||
|
Config.Logger.Log($"Cleared all slash commands from guild {guild.Id}", typeof(PluginLoader));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static async Task<bool> TryStartSlashCommand(this PluginLoader pluginLoader, DBSlashCommand? dbSlashCommand)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (dbSlashCommand is null)
|
||||||
|
{
|
||||||
|
//throw new ArgumentNullException(nameof(dbSlashCommand));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pluginLoader._Client.Guilds.Count == 0) return false;
|
||||||
|
|
||||||
|
|
||||||
|
var builder = new SlashCommandBuilder();
|
||||||
|
builder.WithName(dbSlashCommand.Name);
|
||||||
|
builder.WithDescription(dbSlashCommand.Description);
|
||||||
|
builder.WithDMPermission(dbSlashCommand.canUseDM);
|
||||||
|
builder.Options = dbSlashCommand.Options;
|
||||||
|
|
||||||
|
if (uint.TryParse(Config.ServerID, out uint result))
|
||||||
|
{
|
||||||
|
SocketGuild? guild = pluginLoader._Client.GetGuild(result);
|
||||||
|
if (guild is null)
|
||||||
|
{
|
||||||
|
Config.Logger.Log("Failed to get guild with ID " + Config.ServerID, typeof(PluginLoader), LogType.ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await guild.CreateApplicationCommandAsync(builder.Build());
|
||||||
|
}else await pluginLoader._Client.CreateGlobalApplicationCommandAsync(builder.Build());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Config.Logger.Log($"Error starting slash command {dbSlashCommand.Name}: {e.Message}", typeof(PluginLoader), LogType.ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,21 +35,25 @@ internal static class OnlineFunctions
|
|||||||
if (progress == null || !contentLength.HasValue)
|
if (progress == null || !contentLength.HasValue)
|
||||||
{
|
{
|
||||||
await download.CopyToAsync(destination, cancellation);
|
await download.CopyToAsync(destination, cancellation);
|
||||||
|
if (!contentLength.HasValue)
|
||||||
|
progress?.Report(100f);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert absolute progress (bytes downloaded) into relative progress (0% - 100%)
|
// Convert absolute progress (bytes downloaded) into relative progress (0% - 100%)
|
||||||
var relativeProgress = new Progress<long>(totalBytes =>
|
// total ... 100%
|
||||||
|
// downloaded ... x%
|
||||||
|
// x = downloaded * 100 / total => x = downloaded / total * 100
|
||||||
|
var relativeProgress = new Progress<long>(totalBytesDownloaded =>
|
||||||
{
|
{
|
||||||
progress?.Report((float)totalBytes / contentLength.Value *
|
progress?.Report(totalBytesDownloaded / (float)contentLength.Value * 100);
|
||||||
100);
|
downloadedBytes?.Report(totalBytesDownloaded);
|
||||||
downloadedBytes?.Report(totalBytes);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Use extension method to report progress while downloading
|
// Use extension method to report progress while downloading
|
||||||
await download.CopyToOtherStreamAsync(destination, bufferSize, relativeProgress, cancellation);
|
await download.CopyToOtherStreamAsync(destination, bufferSize, relativeProgress, cancellation);
|
||||||
progress.Report(100);
|
progress.Report(100f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
PluginManager/Online/Helpers/PluginVersion.cs
Normal file
20
PluginManager/Online/Helpers/PluginVersion.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using PluginManager.Interfaces.Updater;
|
||||||
|
|
||||||
|
namespace PluginManager.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace PluginManager.Online.Helpers;
|
|
||||||
|
|
||||||
public class VersionString
|
|
||||||
{
|
|
||||||
public int PackageCheckVersion;
|
|
||||||
public int PackageMainVersion;
|
|
||||||
public int PackageVersionID;
|
|
||||||
|
|
||||||
public VersionString(string version)
|
|
||||||
{
|
|
||||||
var data = version.Split('.');
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (data.Length == 3)
|
|
||||||
{
|
|
||||||
PackageVersionID = int.Parse(data[0]);
|
|
||||||
PackageMainVersion = int.Parse(data[1]);
|
|
||||||
PackageCheckVersion = int.Parse(data[2]);
|
|
||||||
}
|
|
||||||
else if (data.Length == 4)
|
|
||||||
{
|
|
||||||
// ignore the first item data[0]
|
|
||||||
PackageVersionID = int.Parse(data[1]);
|
|
||||||
PackageMainVersion = int.Parse(data[2]);
|
|
||||||
PackageCheckVersion = int.Parse(data[3]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new Exception("Invalid version string");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(version);
|
|
||||||
throw new Exception("Failed to write Version", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool Equals(VersionString other)
|
|
||||||
{
|
|
||||||
return PackageCheckVersion == other.PackageCheckVersion && PackageMainVersion == other.PackageMainVersion &&
|
|
||||||
PackageVersionID == other.PackageVersionID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
|
||||||
{
|
|
||||||
if (ReferenceEquals(null, obj)) return false;
|
|
||||||
if (ReferenceEquals(this, obj)) return true;
|
|
||||||
if (obj.GetType() != GetType()) return false;
|
|
||||||
return Equals((VersionString)obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return HashCode.Combine(PackageCheckVersion, PackageMainVersion, PackageVersionID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return "{PackageID: " + PackageVersionID + ", PackageVersion: " + PackageMainVersion +
|
|
||||||
", PackageCheckVersion: " + PackageCheckVersion + "}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ToShortString()
|
|
||||||
{
|
|
||||||
if (PackageVersionID == 0 && PackageCheckVersion == 0 && PackageMainVersion == 0)
|
|
||||||
return "Unknown";
|
|
||||||
return $"{PackageVersionID}.{PackageMainVersion}.{PackageCheckVersion}";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#region operators
|
|
||||||
|
|
||||||
public static bool operator >(VersionString s1, VersionString s2)
|
|
||||||
{
|
|
||||||
if (s1.PackageVersionID > s2.PackageVersionID) return true;
|
|
||||||
if (s1.PackageVersionID == s2.PackageVersionID)
|
|
||||||
{
|
|
||||||
if (s1.PackageMainVersion > s2.PackageMainVersion) return true;
|
|
||||||
if (s1.PackageMainVersion == s2.PackageMainVersion &&
|
|
||||||
s1.PackageCheckVersion > s2.PackageCheckVersion) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator <(VersionString s1, VersionString s2)
|
|
||||||
{
|
|
||||||
return !(s1 > s2) && s1 != s2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(VersionString s1, VersionString s2)
|
|
||||||
{
|
|
||||||
if (s1.PackageVersionID == s2.PackageVersionID && s1.PackageMainVersion == s2.PackageMainVersion &&
|
|
||||||
s1.PackageCheckVersion == s2.PackageCheckVersion) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator !=(VersionString s1, VersionString s2)
|
|
||||||
{
|
|
||||||
return !(s1 == s2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator <=(VersionString s1, VersionString s2)
|
|
||||||
{
|
|
||||||
return s1 < s2 || s1 == s2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator >=(VersionString s1, VersionString s2)
|
|
||||||
{
|
|
||||||
return s1 > s2 || s1 == s2;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@@ -1,145 +1,172 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using PluginManager.Online.Helpers;
|
|
||||||
using PluginManager.Others;
|
using PluginManager.Others;
|
||||||
using OperatingSystem = PluginManager.Others.OperatingSystem;
|
using PluginManager.Plugin;
|
||||||
|
using PluginManager.Updater.Plugins;
|
||||||
|
|
||||||
namespace PluginManager.Online;
|
namespace PluginManager.Online;
|
||||||
|
|
||||||
public class PluginsManager
|
public class PluginsManager
|
||||||
{
|
{
|
||||||
/// <summary>
|
private static readonly string _DefaultBranch = "releases";
|
||||||
/// The Plugin Manager constructor
|
private static readonly string _DefaultBaseUrl = "https://raw.githubusercontent.com/andreitdr/SethPlugins";
|
||||||
/// </summary>
|
|
||||||
/// <param name="plink">The link to the file where all plugins are stored</param>
|
private static readonly string _DefaultPluginsLink = "PluginsList.json";
|
||||||
/// <param name="vlink">The link to the file where all plugin versions are stored</param>
|
|
||||||
public PluginsManager(string plink, string vlink)
|
|
||||||
|
public string Branch { get; init; }
|
||||||
|
public string BaseUrl { get; init; }
|
||||||
|
|
||||||
|
|
||||||
|
private string PluginsLink => $"{BaseUrl}/{Branch}/{_DefaultPluginsLink}";
|
||||||
|
|
||||||
|
public PluginsManager(Uri baseUrl, string branch)
|
||||||
{
|
{
|
||||||
PluginsLink = plink;
|
BaseUrl = baseUrl.ToString();
|
||||||
VersionsLink = vlink;
|
Branch = branch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public PluginsManager(string branch)
|
||||||
/// The URL of the server
|
|
||||||
/// </summary>
|
|
||||||
public string PluginsLink { get; }
|
|
||||||
|
|
||||||
public string VersionsLink { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The method to load all plugins
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task<List<string[]>> GetAvailablePlugins()
|
|
||||||
{
|
{
|
||||||
Config.Logger.Log("Got data from " + VersionsLink, this, LogLevel.INFO);
|
BaseUrl = _DefaultBaseUrl;
|
||||||
try
|
Branch = branch;
|
||||||
{
|
|
||||||
var list = await ServerCom.ReadTextFromURL(PluginsLink);
|
|
||||||
var lines = list.ToArray();
|
|
||||||
|
|
||||||
var data = new List<string[]>();
|
|
||||||
var op = Functions.GetOperatingSystem();
|
|
||||||
|
|
||||||
var len = lines.Length;
|
|
||||||
for (var i = 0; i < len; i++)
|
|
||||||
{
|
|
||||||
if (lines[i].Length <= 2)
|
|
||||||
continue;
|
|
||||||
var content = lines[i].Split(',');
|
|
||||||
var display = new string[4]; // 4 columns
|
|
||||||
if (op == OperatingSystem.WINDOWS)
|
|
||||||
{
|
|
||||||
if (content[4].Contains("Windows"))
|
|
||||||
{
|
|
||||||
display[0] = content[0];
|
|
||||||
display[1] = content[1];
|
|
||||||
display[2] = content[2];
|
|
||||||
display[3] =
|
|
||||||
(await GetVersionOfPackageFromWeb(content[0]) ?? new VersionString("0.0.0"))
|
|
||||||
.ToShortString();
|
|
||||||
data.Add(display);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PluginsManager()
|
||||||
|
{
|
||||||
|
BaseUrl = _DefaultBaseUrl;
|
||||||
|
Branch = _DefaultBranch;
|
||||||
}
|
}
|
||||||
else if (op == OperatingSystem.LINUX)
|
|
||||||
|
public async Task<List<PluginOnlineInfo>?> GetPluginsList()
|
||||||
{
|
{
|
||||||
if (content[4].Contains("Linux"))
|
var jsonText = await ServerCom.GetAllTextFromUrl(PluginsLink);
|
||||||
|
List<PluginOnlineInfo> result = await JsonManager.ConvertFromJson<List<PluginOnlineInfo>>(jsonText);
|
||||||
|
|
||||||
|
var currentOS = OperatingSystem.IsWindows() ? OSType.WINDOWS :
|
||||||
|
OperatingSystem.IsLinux() ? OSType.LINUX :
|
||||||
|
OperatingSystem.IsMacOS() ? OSType.MACOSX : OSType.NONE;
|
||||||
|
|
||||||
|
return result.FindAll(pl => (pl.SupportedOS & currentOS) != 0);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PluginOnlineInfo?> GetPluginDataByName(string pluginName)
|
||||||
{
|
{
|
||||||
display[0] = content[0];
|
List<PluginOnlineInfo>? plugins = await GetPluginsList();
|
||||||
display[1] = content[1];
|
var result = plugins?.Find(p => p.Name == pluginName);
|
||||||
display[2] = content[2];
|
|
||||||
display[3] =
|
return result;
|
||||||
(await GetVersionOfPackageFromWeb(content[0]) ?? new VersionString("0.0.0"))
|
}
|
||||||
.ToShortString();
|
|
||||||
data.Add(display);
|
public async Task RemovePluginFromDatabase(string pluginName)
|
||||||
|
{
|
||||||
|
List<PluginInfo> installedPlugins = await JsonManager.ConvertFromJson<List<PluginInfo>>(await File.ReadAllTextAsync(Config.PluginDatabase));
|
||||||
|
|
||||||
|
installedPlugins.RemoveAll(p => p.PluginName == pluginName);
|
||||||
|
await JsonManager.SaveToJsonFile( Config.PluginDatabase,installedPlugins);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AppendPluginToDatabase(PluginInfo pluginData)
|
||||||
|
{
|
||||||
|
List<PluginInfo> installedPlugins = await JsonManager.ConvertFromJson<List<PluginInfo>>(await File.ReadAllTextAsync(Config.PluginDatabase));
|
||||||
|
|
||||||
|
installedPlugins.Add(pluginData);
|
||||||
|
await JsonManager.SaveToJsonFile( Config.PluginDatabase, installedPlugins);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<PluginInfo>> GetInstalledPlugins()
|
||||||
|
{
|
||||||
|
return await JsonManager.ConvertFromJson<List<PluginInfo>>(await File.ReadAllTextAsync(Config.PluginDatabase));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> IsPluginInstalled(string pluginName)
|
||||||
|
{
|
||||||
|
List<PluginInfo> installedPlugins = await JsonManager.ConvertFromJson<List<PluginInfo>>(await File.ReadAllTextAsync(Config.PluginDatabase));
|
||||||
|
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
Config.Logger.Log("Updating plugin: " + plugin.PluginName, typeof(PluginsManager), LogType.INFO);
|
||||||
|
await pluginUpdater.UpdatePlugin(plugin.PluginName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data.Add(new[] { "-", "-", "-", "-" });
|
public async Task<bool> MarkPluginToUninstall(string pluginName)
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
{
|
||||||
Config.Logger.Log("Failed to execute command: listplugs\nReason: " + exception.Message, this,
|
List<PluginInfo> installedPlugins = await GetInstalledPlugins();
|
||||||
LogLevel.ERROR);
|
PluginInfo? info = installedPlugins.Find(info => info.PluginName == pluginName);
|
||||||
|
|
||||||
|
if(info == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
await RemovePluginFromDatabase(pluginName);
|
||||||
|
info.IsMarkedToUninstall = true;
|
||||||
|
await AppendPluginToDatabase(info);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
public async Task UninstallMarkedPlugins()
|
||||||
}
|
{
|
||||||
|
List<PluginInfo> installedPlugins = await GetInstalledPlugins();
|
||||||
|
foreach(PluginInfo plugin in installedPlugins)
|
||||||
|
{
|
||||||
|
if(!plugin.IsMarkedToUninstall) continue;
|
||||||
|
|
||||||
public async Task<VersionString?> GetVersionOfPackageFromWeb(string pakName)
|
await UninstallPlugin(plugin);
|
||||||
{
|
|
||||||
var data = await ServerCom.ReadTextFromURL(VersionsLink);
|
|
||||||
foreach (var item in data)
|
|
||||||
{
|
|
||||||
if (item.StartsWith("#"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var split = item.Split(',');
|
|
||||||
if (split[0] == pakName)
|
|
||||||
{
|
|
||||||
Config.Logger.Log("Searched for " + pakName + " and found " + split[1] + " as version.", LogLevel.INFO);
|
|
||||||
return new VersionString(split[1]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
private async Task UninstallPlugin(PluginInfo pluginInfo)
|
||||||
|
{
|
||||||
|
File.Delete(pluginInfo.FilePath);
|
||||||
|
|
||||||
|
foreach(string dependency in pluginInfo.ListOfDependancies)
|
||||||
|
File.Delete(dependency);
|
||||||
|
|
||||||
|
await RemovePluginFromDatabase(pluginInfo.PluginName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public async Task InstallPlugin(PluginOnlineInfo pluginData, IProgress<float>? installProgress)
|
||||||
/// The method to get plugin information by its name
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">The plugin name</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task<string[]> GetPluginLinkByName(string name)
|
|
||||||
{
|
{
|
||||||
try
|
installProgress?.Report(0f);
|
||||||
|
|
||||||
|
int totalSteps = pluginData.HasDependencies ? pluginData.Dependencies.Count + 1 : 1;
|
||||||
|
|
||||||
|
float stepProgress = 1f / totalSteps;
|
||||||
|
|
||||||
|
float currentProgress = 0f;
|
||||||
|
|
||||||
|
IProgress<float> progress = new Progress<float>((p) => {
|
||||||
|
installProgress?.Report(currentProgress + stepProgress * p);
|
||||||
|
});
|
||||||
|
|
||||||
|
await ServerCom.DownloadFileAsync(pluginData.DownLoadLink, $"{Config.AppSettings["PluginFolder"]}/{pluginData.Name}.dll", progress);
|
||||||
|
|
||||||
|
foreach (var dependency in pluginData.Dependencies)
|
||||||
{
|
{
|
||||||
var list = await ServerCom.ReadTextFromURL(PluginsLink);
|
await ServerCom.DownloadFileAsync(dependency.DownloadLink, dependency.DownloadLocation, progress);
|
||||||
var lines = list.ToArray();
|
currentProgress += stepProgress;
|
||||||
var len = lines.Length;
|
|
||||||
for (var i = 0; i < len; i++)
|
|
||||||
{
|
|
||||||
var contents = lines[i].Split(',');
|
|
||||||
if (contents[0].ToLowerInvariant() == name.ToLowerInvariant())
|
|
||||||
{
|
|
||||||
if (contents.Length == 6)
|
|
||||||
return new[] { contents[2], contents[3], contents[5] };
|
|
||||||
if (contents.Length == 5)
|
|
||||||
return new[] { contents[2], contents[3], string.Empty };
|
|
||||||
throw new Exception("Failed to download plugin. Invalid Argument Length");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Config.Logger.Log("Failed to execute command: listplugs\nReason: " + exception.Message, this,
|
|
||||||
LogLevel.ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,17 @@ public static class ServerCom
|
|||||||
return lines.ToList();
|
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>
|
/// <summary>
|
||||||
/// Download file from url
|
/// Download file from url
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -30,7 +41,7 @@ public static class ServerCom
|
|||||||
/// <param name="progress">The <see cref="IProgress{T}" /> to track the download</param>
|
/// <param name="progress">The <see cref="IProgress{T}" /> to track the download</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static async Task DownloadFileAsync(
|
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)
|
||||||
{
|
{
|
||||||
using (var client = new HttpClient())
|
using (var client = new HttpClient())
|
||||||
@@ -48,4 +59,20 @@ public static class ServerCom
|
|||||||
{
|
{
|
||||||
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
20
PluginManager/Others/Actions/InternalActionOption.cs
Normal file
20
PluginManager/Others/Actions/InternalActionOption.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace PluginManager.Others.Actions
|
||||||
|
{
|
||||||
|
public class InternalActionOption
|
||||||
|
{
|
||||||
|
public string OptionName { get; set; }
|
||||||
|
public string OptionDescription { get; set; }
|
||||||
|
|
||||||
|
public InternalActionOption(string optionName, string optionDescription)
|
||||||
|
{
|
||||||
|
OptionName = optionName;
|
||||||
|
OptionDescription = optionDescription;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,50 +9,46 @@ namespace PluginManager.Others.Actions;
|
|||||||
public class InternalActionManager
|
public class InternalActionManager
|
||||||
{
|
{
|
||||||
public Dictionary<string, ICommandAction> Actions = new();
|
public Dictionary<string, ICommandAction> Actions = new();
|
||||||
public ActionsLoader loader;
|
private readonly ActionsLoader _loader;
|
||||||
|
|
||||||
public InternalActionManager(string path, string extension)
|
public InternalActionManager(string path, string extension)
|
||||||
{
|
{
|
||||||
loader = new ActionsLoader(path, extension);
|
_loader = new ActionsLoader(path, extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Initialize()
|
public async Task Initialize()
|
||||||
{
|
{
|
||||||
loader.ActionLoadedEvent += OnActionLoaded;
|
var loadedActions = await _loader.Load();
|
||||||
var m_actions = await loader.Load();
|
if (loadedActions == null)
|
||||||
if (m_actions == null) return;
|
|
||||||
foreach (var action in m_actions)
|
|
||||||
Actions.Add(action.ActionName, action);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnActionLoaded(string name, string typeName, bool success, Exception? e)
|
|
||||||
{
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
Config.Logger.Error(e);
|
|
||||||
return;
|
return;
|
||||||
|
foreach (var action in loadedActions)
|
||||||
|
Actions.TryAdd(action.ActionName, action);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Config.Logger.Log($"Action {name} loaded successfully", LogLevel.INFO, true);
|
public async Task Refresh()
|
||||||
|
{
|
||||||
|
Actions.Clear();
|
||||||
|
await Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> Execute(string actionName, params string[]? args)
|
public async Task<bool> Execute(string actionName, params string[]? args)
|
||||||
{
|
{
|
||||||
if (!Actions.ContainsKey(actionName))
|
if (!Actions.ContainsKey(actionName))
|
||||||
{
|
{
|
||||||
Config.Logger.Log($"Action {actionName} not found", "InternalActionManager", LogLevel.WARNING, true);
|
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, "InternalActionManager", LogLevel.ERROR);
|
Config.Logger.Log(e.Message, type: LogType.ERROR, source: typeof(InternalActionManager));
|
||||||
return e.Message;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,26 +2,26 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.Serialization.Formatters.Binary;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace PluginManager.Others;
|
namespace PluginManager.Others;
|
||||||
|
|
||||||
public static class ArchiveManager
|
public static class ArchiveManager
|
||||||
{
|
{
|
||||||
private static string? archiveFolder;
|
|
||||||
public static bool isInitialized { get; private set; }
|
|
||||||
|
|
||||||
public static void Initialize()
|
public static void CreateFromFile(string file, string folder)
|
||||||
{
|
{
|
||||||
if (isInitialized) throw new Exception("ArchiveManager is already initialized");
|
if (!Directory.Exists(folder))
|
||||||
|
Directory.CreateDirectory(folder);
|
||||||
|
|
||||||
if (!Config.AppSettings.ContainsKey("ArchiveFolder"))
|
var archiveName = folder + Path.GetFileNameWithoutExtension(file) + ".zip";
|
||||||
Config.AppSettings["ArchiveFolder"] = "./Data/PAKS/";
|
if (File.Exists(archiveName))
|
||||||
|
File.Delete(archiveName);
|
||||||
|
|
||||||
archiveFolder = Config.AppSettings["ArchiveFolder"];
|
using(ZipArchive archive = ZipFile.Open(archiveName, ZipArchiveMode.Create))
|
||||||
|
{
|
||||||
isInitialized = true;
|
archive.CreateEntryFromFile(file, Path.GetFileName(file));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -30,31 +30,25 @@ public static class ArchiveManager
|
|||||||
/// <param name="fileName">The file name in the archive</param>
|
/// <param name="fileName">The file name in the archive</param>
|
||||||
/// <param name="archName">The archive location on the disk</param>
|
/// <param name="archName">The archive location on the disk</param>
|
||||||
/// <returns>An array of bytes that represents the Stream value from the file that was read inside the archive</returns>
|
/// <returns>An array of bytes that represents the Stream value from the file that was read inside the archive</returns>
|
||||||
public async static Task<byte[]?> ReadStreamFromPakAsync(string fileName, string archName)
|
public static async Task<byte[]?> ReadStreamFromPakAsync(string fileName, string archName)
|
||||||
{
|
{
|
||||||
if (!isInitialized) throw new Exception("ArchiveManager is not initialized");
|
|
||||||
|
|
||||||
archName = archiveFolder + archName;
|
archName = Config.AppSettings["ArchiveFolder"] + archName;
|
||||||
|
|
||||||
if (!File.Exists(archName))
|
if (!File.Exists(archName))
|
||||||
throw new Exception("Failed to load file !");
|
throw new Exception("Failed to load file !");
|
||||||
|
|
||||||
byte[]? data = null;
|
using var zip = ZipFile.OpenRead(archName);
|
||||||
|
|
||||||
using (var zip = ZipFile.OpenRead(archName))
|
|
||||||
{
|
|
||||||
var entry = zip.Entries.FirstOrDefault(entry => entry.FullName == fileName || entry.Name == fileName);
|
var entry = zip.Entries.FirstOrDefault(entry => entry.FullName == fileName || entry.Name == fileName);
|
||||||
if (entry is null) throw new Exception("File not found in archive");
|
if (entry is null) throw new Exception("File not found in archive");
|
||||||
|
|
||||||
var MemoryStream = new MemoryStream();
|
await using var memoryStream = new MemoryStream();
|
||||||
|
|
||||||
var stream = entry.Open();
|
var stream = entry.Open();
|
||||||
await stream.CopyToAsync(MemoryStream);
|
await stream.CopyToAsync(memoryStream);
|
||||||
data = MemoryStream.ToArray();
|
var data = memoryStream.ToArray();
|
||||||
|
|
||||||
stream.Close();
|
stream.Close();
|
||||||
MemoryStream.Close();
|
memoryStream.Close();
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -62,13 +56,12 @@ public static class ArchiveManager
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read data from a file that is inside an archive (ZIP format)
|
/// Read data from a file that is inside an archive (ZIP format)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="FileName">The file name that is inside the archive or its full path</param>
|
/// <param name="fileName">The file name that is inside the archive or its full path</param>
|
||||||
/// <param name="archFile">The archive location from the PAKs folder</param>
|
/// <param name="archFile">The archive location from the PAKs folder</param>
|
||||||
/// <returns>A string that represents the content of the file or null if the file does not exists or it has no content</returns>
|
/// <returns>A string that represents the content of the file or null if the file does not exists or it has no content</returns>
|
||||||
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");
|
archFile = Config.AppSettings["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 !");
|
||||||
|
|
||||||
@@ -79,7 +72,7 @@ public static class ArchiveManager
|
|||||||
using (var zip = new ZipArchive(fs, ZipArchiveMode.Read))
|
using (var zip = new ZipArchive(fs, ZipArchiveMode.Read))
|
||||||
{
|
{
|
||||||
foreach (var entry in zip.Entries)
|
foreach (var entry in zip.Entries)
|
||||||
if (entry.Name == FileName || entry.FullName == FileName)
|
if (entry.Name == fileName || entry.FullName == fileName)
|
||||||
using (var s = entry.Open())
|
using (var s = entry.Open())
|
||||||
using (var reader = new StreamReader(s))
|
using (var reader = new StreamReader(s))
|
||||||
{
|
{
|
||||||
@@ -94,9 +87,9 @@ public static class ArchiveManager
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Config.Logger.Log(ex.Message, "Archive Manager", LogLevel.ERROR); // Write the error to a file
|
Config.Logger.Log(ex.Message, typeof(ArchiveManager), LogType.ERROR); // Write the error to a file
|
||||||
await Task.Delay(100);
|
await Task.Delay(100);
|
||||||
return await ReadFromPakAsync(FileName, archFile);
|
return await ReadFromPakAsync(fileName, archFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,14 +105,12 @@ public static class ArchiveManager
|
|||||||
string zip, string folder, IProgress<float> progress,
|
string zip, string folder, IProgress<float> progress,
|
||||||
UnzipProgressType type)
|
UnzipProgressType type)
|
||||||
{
|
{
|
||||||
if (!isInitialized) throw new Exception("ArchiveManager is not initialized");
|
|
||||||
Directory.CreateDirectory(folder);
|
Directory.CreateDirectory(folder);
|
||||||
using (var archive = ZipFile.OpenRead(zip))
|
using var archive = ZipFile.OpenRead(zip);
|
||||||
{
|
var totalZipFiles = archive.Entries.Count();
|
||||||
if (type == UnzipProgressType.PERCENTAGE_FROM_NUMBER_OF_FILES)
|
if (type == UnzipProgressType.PERCENTAGE_FROM_NUMBER_OF_FILES)
|
||||||
{
|
{
|
||||||
var totalZIPFiles = archive.Entries.Count();
|
var currentZipFile = 0;
|
||||||
var currentZIPFile = 0;
|
|
||||||
foreach (var entry in archive.Entries)
|
foreach (var entry in archive.Entries)
|
||||||
{
|
{
|
||||||
if (entry.FullName.EndsWith("/")) // it is a folder
|
if (entry.FullName.EndsWith("/")) // it is a folder
|
||||||
@@ -132,14 +123,13 @@ public static class ArchiveManager
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Config.Logger.Log($"Failed to extract {entry.Name}. Exception: {ex.Message}",
|
Config.Logger.Log(ex.Message, typeof(ArchiveManager), LogType.ERROR);
|
||||||
"Archive Manager", LogLevel.ERROR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentZIPFile++;
|
currentZipFile++;
|
||||||
await Task.Delay(10);
|
await Task.Delay(10);
|
||||||
if (progress != null)
|
if (progress != null)
|
||||||
progress.Report((float)currentZIPFile / totalZIPFiles * 100);
|
progress.Report((float)currentZipFile / totalZipFiles * 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (type == UnzipProgressType.PERCENTAGE_FROM_TOTAL_SIZE)
|
else if (type == UnzipProgressType.PERCENTAGE_FROM_TOTAL_SIZE)
|
||||||
@@ -165,8 +155,7 @@ public static class ArchiveManager
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Config.Logger.Log($"Failed to extract {entry.Name}. Exception: {ex.Message}",
|
Config.Logger.Log(ex.Message, typeof(ArchiveManager), LogType.ERROR);
|
||||||
"Archive Manager", LogLevel.ERROR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.Delay(10);
|
await Task.Delay(10);
|
||||||
@@ -176,4 +165,3 @@ public static class ArchiveManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -1,10 +1,19 @@
|
|||||||
using Discord.Commands;
|
using Discord.Commands;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
|
||||||
|
|
||||||
namespace PluginManager.Others;
|
namespace PluginManager.Others;
|
||||||
|
|
||||||
public class DBCommandExecutingArguments
|
public class DbCommandExecutingArguments
|
||||||
{
|
{
|
||||||
public DBCommandExecutingArguments(
|
|
||||||
|
public SocketCommandContext context { get; init; }
|
||||||
|
public string cleanContent { get; init; }
|
||||||
|
public string commandUsed { get; init; }
|
||||||
|
public string[]? arguments { get; init; }
|
||||||
|
public ISocketMessageChannel Channel => context.Channel;
|
||||||
|
|
||||||
|
public DbCommandExecutingArguments(
|
||||||
SocketCommandContext context, string cleanContent, string commandUsed, string[]? arguments)
|
SocketCommandContext context, string cleanContent, string commandUsed, string[]? arguments)
|
||||||
{
|
{
|
||||||
this.context = context;
|
this.context = context;
|
||||||
@@ -13,8 +22,27 @@ public class DBCommandExecutingArguments
|
|||||||
this.arguments = arguments;
|
this.arguments = arguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SocketCommandContext context { get; init; }
|
public DbCommandExecutingArguments(SocketUserMessage? message, DiscordSocketClient client)
|
||||||
public string cleanContent { get; init; }
|
{
|
||||||
public string commandUsed { get; init; }
|
context = new SocketCommandContext(client, message);
|
||||||
public string[]? arguments { get; init; }
|
var pos = 0;
|
||||||
|
if (message.HasMentionPrefix(client.CurrentUser, ref pos))
|
||||||
|
{
|
||||||
|
var mentionPrefix = "<@" + client.CurrentUser.Id + ">";
|
||||||
|
cleanContent = message.Content.Substring(mentionPrefix.Length + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cleanContent = message.Content.Substring(Config.DiscordBot.BotPrefix.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
var split = cleanContent.Split(' ');
|
||||||
|
|
||||||
|
string[]? argsClean = null;
|
||||||
|
if (split.Length > 1)
|
||||||
|
argsClean = string.Join(' ', split, 1, split.Length - 1).Split(' ');
|
||||||
|
|
||||||
|
commandUsed = split[0];
|
||||||
|
arguments = argsClean;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,11 @@
|
|||||||
namespace PluginManager.Others;
|
using System;
|
||||||
|
|
||||||
/// <summary>
|
namespace PluginManager.Others;
|
||||||
/// A list of operating systems
|
|
||||||
/// </summary>
|
|
||||||
public enum OperatingSystem
|
|
||||||
{
|
|
||||||
WINDOWS,
|
|
||||||
LINUX,
|
|
||||||
MAC_OS,
|
|
||||||
UNKNOWN
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The output log type
|
/// The output log type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum LogLevel
|
public enum LogType
|
||||||
{
|
{
|
||||||
INFO,
|
INFO,
|
||||||
WARNING,
|
WARNING,
|
||||||
@@ -28,20 +19,25 @@ public enum UnzipProgressType
|
|||||||
PERCENTAGE_FROM_TOTAL_SIZE
|
PERCENTAGE_FROM_TOTAL_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SaveType
|
|
||||||
{
|
|
||||||
NORMAL,
|
|
||||||
BACKUP
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum InternalActionRunType
|
public enum InternalActionRunType
|
||||||
{
|
{
|
||||||
ON_STARTUP,
|
ON_STARTUP,
|
||||||
ON_CALL
|
ON_CALL
|
||||||
}
|
}
|
||||||
|
|
||||||
internal enum ExceptionExitCode : int
|
[Flags]
|
||||||
|
public enum OSType: byte
|
||||||
{
|
{
|
||||||
CONFIG_FAILED_TO_LOAD = 1,
|
NONE = 0,
|
||||||
CONFIG_KEY_NOT_FOUND = 2,
|
WINDOWS = 1 << 0,
|
||||||
|
LINUX = 2 << 1,
|
||||||
|
MACOSX = 3 << 2
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PluginType
|
||||||
|
{
|
||||||
|
UNKNOWN,
|
||||||
|
COMMAND,
|
||||||
|
EVENT,
|
||||||
|
SLASH_COMMAND
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using PluginManager.Interfaces.Exceptions;
|
|
||||||
|
|
||||||
namespace PluginManager.Others.Exceptions;
|
|
||||||
|
|
||||||
public class ConfigFailedToLoad : IException
|
|
||||||
{
|
|
||||||
public List<string>? Messages { get; set; }
|
|
||||||
public bool isFatal { get; private set; }
|
|
||||||
public string? File { get; }
|
|
||||||
|
|
||||||
|
|
||||||
public ConfigFailedToLoad(string message, bool isFatal, string file)
|
|
||||||
{
|
|
||||||
this.isFatal = isFatal;
|
|
||||||
Messages = new List<string>() {message};
|
|
||||||
this.File = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigFailedToLoad(string message, bool isFatal)
|
|
||||||
{
|
|
||||||
this.isFatal = isFatal;
|
|
||||||
Messages = new List<string>() {message};
|
|
||||||
this.File = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigFailedToLoad(string message)
|
|
||||||
{
|
|
||||||
this.isFatal = false;
|
|
||||||
Messages = new List<string>() {message};
|
|
||||||
this.File = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GenerateFullMessage()
|
|
||||||
{
|
|
||||||
string messages = "";
|
|
||||||
foreach (var message in Messages)
|
|
||||||
{
|
|
||||||
messages += message + "\n";
|
|
||||||
}
|
|
||||||
return $"\nMessage: {messages}\nIsFatal: {isFatal}\nFile: {File ?? "null"}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public void HandleException()
|
|
||||||
{
|
|
||||||
if (isFatal)
|
|
||||||
{
|
|
||||||
Config.Logger.Log(GenerateFullMessage(), LogLevel.CRITICAL, true);
|
|
||||||
Environment.Exit((int)ExceptionExitCode.CONFIG_FAILED_TO_LOAD);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Config.Logger.Log(GenerateFullMessage(), LogLevel.WARNING);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IException AppendError(string message)
|
|
||||||
{
|
|
||||||
Messages.Add(message);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IException AppendError(List<string> messages)
|
|
||||||
{
|
|
||||||
Messages.AddRange(messages);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IException IsFatal(bool isFatal = true)
|
|
||||||
{
|
|
||||||
this.isFatal = isFatal;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static ConfigFailedToLoad CreateError(string message, bool isFatal, string? file = null)
|
|
||||||
{
|
|
||||||
if (file is not null)
|
|
||||||
return new ConfigFailedToLoad(message, isFatal, file);
|
|
||||||
return new ConfigFailedToLoad(message, isFatal);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ConfigFailedToLoad CreateError(string message)
|
|
||||||
{
|
|
||||||
return new ConfigFailedToLoad(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using PluginManager.Interfaces.Exceptions;
|
|
||||||
|
|
||||||
namespace PluginManager.Others.Exceptions;
|
|
||||||
|
|
||||||
public class ConfigNoKeyWasPresent: IException
|
|
||||||
{
|
|
||||||
public List<string> Messages { get; set; }
|
|
||||||
public bool isFatal { get; private set; }
|
|
||||||
|
|
||||||
public ConfigNoKeyWasPresent(string message, bool isFatal)
|
|
||||||
{
|
|
||||||
this.Messages = new List<string>() { message };
|
|
||||||
this.isFatal = isFatal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigNoKeyWasPresent(string message)
|
|
||||||
{
|
|
||||||
this.Messages = new List<string>() { message };
|
|
||||||
this.isFatal = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GenerateFullMessage()
|
|
||||||
{
|
|
||||||
string messages = "";
|
|
||||||
foreach (var message in Messages)
|
|
||||||
{
|
|
||||||
messages += message + "\n";
|
|
||||||
}
|
|
||||||
return $"\nMessage: {messages}\nIsFatal: {isFatal}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public void HandleException()
|
|
||||||
{
|
|
||||||
if (isFatal)
|
|
||||||
{
|
|
||||||
|
|
||||||
Config.Logger.Log(GenerateFullMessage(), LogLevel.CRITICAL, true);
|
|
||||||
Environment.Exit((int)ExceptionExitCode.CONFIG_KEY_NOT_FOUND);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Config.Logger.Log(GenerateFullMessage(), LogLevel.WARNING);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IException AppendError(string message)
|
|
||||||
{
|
|
||||||
Messages.Add(message);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IException AppendError(List<string> messages)
|
|
||||||
{
|
|
||||||
Messages.AddRange(messages);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IException IsFatal(bool isFatal = true)
|
|
||||||
{
|
|
||||||
this.isFatal = isFatal;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ConfigNoKeyWasPresent CreateError(string message)
|
|
||||||
{
|
|
||||||
return new ConfigNoKeyWasPresent(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ConfigNoKeyWasPresent CreateError(string message, bool isFatal)
|
|
||||||
{
|
|
||||||
return new ConfigNoKeyWasPresent(message, isFatal);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -29,18 +29,6 @@ public static class Functions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the Operating system you are runnin on
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>An Operating system</returns>
|
|
||||||
public static OperatingSystem GetOperatingSystem()
|
|
||||||
{
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return OperatingSystem.WINDOWS;
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return OperatingSystem.LINUX;
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return OperatingSystem.MAC_OS;
|
|
||||||
return OperatingSystem.UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copy one Stream to another <see langword="async" />
|
/// Copy one Stream to another <see langword="async" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -18,7 +18,11 @@ public class JsonManager
|
|||||||
public static async Task SaveToJsonFile<T>(string file, T Data)
|
public static async Task SaveToJsonFile<T>(string file, T Data)
|
||||||
{
|
{
|
||||||
var str = new MemoryStream();
|
var str = new MemoryStream();
|
||||||
await JsonSerializer.SerializeAsync(str, Data, typeof(T), new JsonSerializerOptions { WriteIndented = true });
|
await JsonSerializer.SerializeAsync(str, Data, typeof(T), new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
WriteIndented = true
|
||||||
|
}
|
||||||
|
);
|
||||||
await File.WriteAllBytesAsync(file, str.ToArray());
|
await File.WriteAllBytesAsync(file, str.ToArray());
|
||||||
await str.FlushAsync();
|
await str.FlushAsync();
|
||||||
str.Close();
|
str.Close();
|
||||||
@@ -37,11 +41,13 @@ public class JsonManager
|
|||||||
text = new MemoryStream(await File.ReadAllBytesAsync(input));
|
text = new MemoryStream(await File.ReadAllBytesAsync(input));
|
||||||
else
|
else
|
||||||
text = new MemoryStream(Encoding.ASCII.GetBytes(input));
|
text = new MemoryStream(Encoding.ASCII.GetBytes(input));
|
||||||
|
|
||||||
text.Position = 0;
|
text.Position = 0;
|
||||||
|
|
||||||
var obj = await JsonSerializer.DeserializeAsync<T>(text);
|
var obj = await JsonSerializer.DeserializeAsync<T>(text);
|
||||||
await text.FlushAsync();
|
await text.FlushAsync();
|
||||||
text.Close();
|
text.Close();
|
||||||
|
|
||||||
return (obj ?? default)!;
|
return (obj ?? default)!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace PluginManager.Others.Logger;
|
|
||||||
|
|
||||||
public class DBLogger
|
|
||||||
{
|
|
||||||
public delegate void LogHandler(string message, LogLevel logType, bool isInternal = false);
|
|
||||||
|
|
||||||
private readonly string _errFolder;
|
|
||||||
|
|
||||||
private readonly string _logFolder;
|
|
||||||
private readonly List<LogMessage> ErrorHistory = new();
|
|
||||||
private readonly List<LogMessage> LogHistory = new();
|
|
||||||
|
|
||||||
public DBLogger()
|
|
||||||
{
|
|
||||||
_logFolder = Config.AppSettings["LogFolder"];
|
|
||||||
_errFolder = Config.AppSettings["ErrorFolder"];
|
|
||||||
}
|
|
||||||
|
|
||||||
public IReadOnlyList<LogMessage> Logs => LogHistory;
|
|
||||||
public IReadOnlyList<LogMessage> Errors => ErrorHistory;
|
|
||||||
|
|
||||||
public event LogHandler? LogEvent;
|
|
||||||
|
|
||||||
public void Log(string message, LogLevel type = LogLevel.INFO)
|
|
||||||
{
|
|
||||||
Log(new LogMessage(message, type));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Log(string message, LogLevel type= LogLevel.INFO, bool isInternal = false)
|
|
||||||
{
|
|
||||||
Log(new LogMessage(message, type,"unknown", isInternal));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Log(string message, string sender = "unknown", LogLevel type = LogLevel.INFO, bool isInternal = false)
|
|
||||||
{
|
|
||||||
Log(new LogMessage(message, type,sender,isInternal));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Log(string message, string sender = "unknown", LogLevel type = LogLevel.INFO)
|
|
||||||
{
|
|
||||||
Log(new LogMessage(message, type, sender));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Error(Exception? e)
|
|
||||||
{
|
|
||||||
Log(e.Message, e.Source, LogLevel.ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Log(LogMessage message)
|
|
||||||
{
|
|
||||||
LogEvent?.Invoke(message.Message, message.Type);
|
|
||||||
|
|
||||||
if (message.Type != LogLevel.ERROR && message.Type != LogLevel.CRITICAL)
|
|
||||||
LogHistory.Add(message);
|
|
||||||
else
|
|
||||||
ErrorHistory.Add(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Log(string message, object sender, LogLevel type = LogLevel.INFO)
|
|
||||||
{
|
|
||||||
Log(message, sender.GetType().Name, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SaveToFile(bool ErrorsOnly = true)
|
|
||||||
{
|
|
||||||
if(!ErrorsOnly)
|
|
||||||
await JsonManager.SaveToJsonFile(_logFolder + "/" + DateTime.Now.ToString("yyyy-MM-dd") + ".json",
|
|
||||||
LogHistory);
|
|
||||||
await JsonManager.SaveToJsonFile(_errFolder + "/" + DateTime.Now.ToString("yyyy-MM-dd") + ".json",
|
|
||||||
ErrorHistory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
70
PluginManager/Others/Logger/Log.cs
Normal file
70
PluginManager/Others/Logger/Log.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using PluginManager.Interfaces.Logger;
|
||||||
|
|
||||||
|
namespace PluginManager.Others.Logger;
|
||||||
|
|
||||||
|
public class Log: ILog
|
||||||
|
{
|
||||||
|
public string Message { get; set; }
|
||||||
|
public Type? Source { get; set; }
|
||||||
|
public LogType Type { get; set; }
|
||||||
|
public DateTime ThrowTime { get; set; }
|
||||||
|
|
||||||
|
public Log(string message, Type? source, LogType type, DateTime throwTime)
|
||||||
|
{
|
||||||
|
Message = message;
|
||||||
|
Source = source;
|
||||||
|
Type = type;
|
||||||
|
ThrowTime = throwTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Log(string message, Type? source, LogType type)
|
||||||
|
{
|
||||||
|
Message = message;
|
||||||
|
Source = source;
|
||||||
|
Type = type;
|
||||||
|
ThrowTime = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Log(string message, Type? source)
|
||||||
|
{
|
||||||
|
Message = message;
|
||||||
|
Source = source;
|
||||||
|
Type = LogType.INFO;
|
||||||
|
ThrowTime = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Log(string message)
|
||||||
|
{
|
||||||
|
Message = message;
|
||||||
|
Source = typeof(Log);
|
||||||
|
Type = LogType.INFO;
|
||||||
|
ThrowTime = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator Log(string message)
|
||||||
|
{
|
||||||
|
return new Log(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator string(Log log)
|
||||||
|
{
|
||||||
|
return $"[{log.ThrowTime}] {log.Message}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public string AsLongString()
|
||||||
|
{
|
||||||
|
return $"[{ThrowTime}] [{Source}] [{Type}] {Message}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public string AsShortString()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FormatedLongString()
|
||||||
|
{
|
||||||
|
return $"[{ThrowTime}]\t[{Source}]\t\t\t[{Type}]\t{Message}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace PluginManager.Others.Logger;
|
|
||||||
|
|
||||||
public class LogMessage
|
|
||||||
{
|
|
||||||
public LogMessage(string message, LogLevel type)
|
|
||||||
{
|
|
||||||
Message = message;
|
|
||||||
Type = type;
|
|
||||||
Time = DateTime.Now.ToString("HH:mm:ss");
|
|
||||||
isInternal = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LogMessage(string message, LogLevel type, string sender, bool isInternal) : this(message, type)
|
|
||||||
{
|
|
||||||
Sender = sender;
|
|
||||||
this.isInternal = isInternal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LogMessage(string message, LogLevel type, string sender) : this (message, type, sender, false)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Message { get; set; }
|
|
||||||
public LogLevel Type { get; set; }
|
|
||||||
public string Time { get; set; }
|
|
||||||
public string Sender { get; set; }
|
|
||||||
public bool isInternal { get; set; }
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return $"[{Time}] {Message}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static explicit operator LogMessage(string message)
|
|
||||||
{
|
|
||||||
return new LogMessage(message, LogLevel.INFO);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static explicit operator LogMessage((string message, LogLevel type) tuple)
|
|
||||||
{
|
|
||||||
return new LogMessage(tuple.message, tuple.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static explicit operator LogMessage((string message, LogLevel type, string sender) tuple)
|
|
||||||
{
|
|
||||||
return new LogMessage(tuple.message, tuple.type, tuple.sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
79
PluginManager/Others/Logger/Logger.cs
Normal file
79
PluginManager/Others/Logger/Logger.cs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PluginManager.Interfaces.Logger;
|
||||||
|
|
||||||
|
namespace PluginManager.Others.Logger;
|
||||||
|
|
||||||
|
public sealed class Logger: ILogger
|
||||||
|
{
|
||||||
|
public bool IsEnabled { get; init; }
|
||||||
|
public bool OutputToFile { get; init; }
|
||||||
|
public string? OutputFile { get; init; }
|
||||||
|
|
||||||
|
private LogType LowestLogLevel { get; }
|
||||||
|
private bool UseShortVersion { get; }
|
||||||
|
|
||||||
|
public Logger(bool useShortVersion, bool outputToFile, string outputFile, LogType lowestLogLevel = LogType.INFO)
|
||||||
|
{
|
||||||
|
UseShortVersion = useShortVersion;
|
||||||
|
OutputToFile = outputToFile;
|
||||||
|
IsEnabled = true;
|
||||||
|
LowestLogLevel = lowestLogLevel;
|
||||||
|
OutputFile = outputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Logger(bool useShortVersion, LogType lowestLogLevel = LogType.INFO)
|
||||||
|
{
|
||||||
|
UseShortVersion = useShortVersion;
|
||||||
|
OutputToFile = false;
|
||||||
|
IsEnabled = true;
|
||||||
|
LowestLogLevel = lowestLogLevel;
|
||||||
|
OutputFile = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<Log>? OnLog;
|
||||||
|
|
||||||
|
private async Task Log(Log logMessage)
|
||||||
|
{
|
||||||
|
if (!IsEnabled) return;
|
||||||
|
|
||||||
|
OnLog?.Invoke(this, logMessage);
|
||||||
|
|
||||||
|
if (logMessage.Type < LowestLogLevel) return;
|
||||||
|
|
||||||
|
if (OutputToFile)
|
||||||
|
await File.AppendAllTextAsync(
|
||||||
|
OutputFile!,
|
||||||
|
(UseShortVersion ? logMessage : logMessage.AsLongString()) + "\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Log(string message = "", Type? source = default, LogType type = LogType.INFO, DateTime throwTime = default)
|
||||||
|
{
|
||||||
|
if (!IsEnabled) return;
|
||||||
|
|
||||||
|
if (type < LowestLogLevel) return;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(message)) return;
|
||||||
|
|
||||||
|
if (throwTime == default) throwTime = DateTime.Now;
|
||||||
|
|
||||||
|
if (source == default) source = typeof(Log);
|
||||||
|
|
||||||
|
await Log(new Log(message, source, type, throwTime));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Log(Exception exception, LogType logType = LogType.ERROR, Type? source = null)
|
||||||
|
{
|
||||||
|
if (!IsEnabled) return;
|
||||||
|
|
||||||
|
if (logType < LowestLogLevel) return;
|
||||||
|
|
||||||
|
await Log(new Log(exception.Message, source, logType, DateTime.Now));
|
||||||
|
}
|
||||||
|
}
|
||||||
139
PluginManager/Others/OneOf.cs
Normal file
139
PluginManager/Others/OneOf.cs
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace PluginManager.Others
|
||||||
|
{
|
||||||
|
public class OneOf<T0, T1>
|
||||||
|
{
|
||||||
|
public T0 Item0 { get; }
|
||||||
|
public T1 Item1 { get; }
|
||||||
|
|
||||||
|
public object? Value => Item0 != null ? Item0 : Item1;
|
||||||
|
|
||||||
|
public OneOf(T0 item0)
|
||||||
|
{
|
||||||
|
Item0 = item0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OneOf(T1 item1)
|
||||||
|
{
|
||||||
|
Item1 = item1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator OneOf<T0, T1>(T0 item0) => new OneOf<T0, T1>(item0);
|
||||||
|
public static implicit operator OneOf<T0, T1>(T1 item1) => new OneOf<T0, T1>(item1);
|
||||||
|
|
||||||
|
public void Match(Action<T0> item0, Action<T1> item1)
|
||||||
|
{
|
||||||
|
if (Item0 != null)
|
||||||
|
item0(Item0);
|
||||||
|
else
|
||||||
|
item1(Item1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TResult Match<TResult>(Func<T0, TResult> item0, Func<T1, TResult> item1)
|
||||||
|
{
|
||||||
|
return Item0 != null ? item0(Item0) : item1(Item1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type GetActualType()
|
||||||
|
{
|
||||||
|
return Item0 != null ? Item0.GetType() : Item1.GetType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OneOf<T0, T1, T2>
|
||||||
|
{
|
||||||
|
public T0 Item0 { get; }
|
||||||
|
public T1 Item1 { get; }
|
||||||
|
public T2 Item2 { get; }
|
||||||
|
|
||||||
|
public OneOf(T0 item0)
|
||||||
|
{
|
||||||
|
Item0 = item0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OneOf(T1 item1)
|
||||||
|
{
|
||||||
|
Item1 = item1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OneOf(T2 item2)
|
||||||
|
{
|
||||||
|
Item2 = item2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator OneOf<T0, T1, T2>(T0 item0) => new OneOf<T0, T1, T2>(item0);
|
||||||
|
public static implicit operator OneOf<T0, T1, T2>(T1 item1) => new OneOf<T0, T1, T2>(item1);
|
||||||
|
public static implicit operator OneOf<T0, T1, T2>(T2 item2) => new OneOf<T0, T1, T2>(item2);
|
||||||
|
|
||||||
|
public void Match(Action<T0> item0, Action<T1> item1, Action<T2> item2)
|
||||||
|
{
|
||||||
|
if (Item0 != null)
|
||||||
|
item0(Item0);
|
||||||
|
else if (Item1 != null)
|
||||||
|
item1(Item1);
|
||||||
|
else
|
||||||
|
item2(Item2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TResult Match<TResult>(Func<T0, TResult> item0, Func<T1, TResult> item1, Func<T2, TResult> item2)
|
||||||
|
{
|
||||||
|
return Item0 != null ? item0(Item0) : Item1 != null ? item1(Item1) : item2(Item2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OneOf<T0, T1, T2, T3>
|
||||||
|
{
|
||||||
|
public T0 Item0 { get; }
|
||||||
|
public T1 Item1 { get; }
|
||||||
|
public T2 Item2 { get; }
|
||||||
|
public T3 Item3 { get; }
|
||||||
|
|
||||||
|
public OneOf(T0 item0)
|
||||||
|
{
|
||||||
|
Item0 = item0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OneOf(T1 item1)
|
||||||
|
{
|
||||||
|
Item1 = item1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OneOf(T2 item2)
|
||||||
|
{
|
||||||
|
Item2 = item2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OneOf(T3 item3)
|
||||||
|
{
|
||||||
|
Item3 = item3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator OneOf<T0, T1, T2, T3>(T0 item0) => new OneOf<T0, T1, T2, T3>(item0);
|
||||||
|
public static implicit operator OneOf<T0, T1, T2, T3>(T1 item1) => new OneOf<T0, T1, T2, T3>(item1);
|
||||||
|
public static implicit operator OneOf<T0, T1, T2, T3>(T2 item2) => new OneOf<T0, T1, T2, T3>(item2);
|
||||||
|
public static implicit operator OneOf<T0, T1, T2, T3>(T3 item3) => new OneOf<T0, T1, T2, T3>(item3);
|
||||||
|
|
||||||
|
public void Match(Action<T0> item0, Action<T1> item1, Action<T2> item2, Action<T3> item3)
|
||||||
|
{
|
||||||
|
if (Item0 != null)
|
||||||
|
item0(Item0);
|
||||||
|
else if (Item1 != null)
|
||||||
|
item1(Item1);
|
||||||
|
else if (Item2 != null)
|
||||||
|
item2(Item2);
|
||||||
|
else
|
||||||
|
item3(Item3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TResult Match<TResult>(Func<T0, TResult> item0, Func<T1, TResult> item1, Func<T2, TResult> item2, Func<T3, TResult> item3)
|
||||||
|
{
|
||||||
|
return Item0 != null ? item0(Item0) : Item1 != null ? item1(Item1) : Item2 != null ? item2(Item2) : item3(Item3);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ public static class DiscordPermissions
|
|||||||
/// <param name="user">The user</param>
|
/// <param name="user">The user</param>
|
||||||
/// <param name="role">The role</param>
|
/// <param name="role">The role</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static bool hasRole(this SocketGuildUser user, IRole role)
|
public static bool HasRole(this SocketGuildUser user, IRole role)
|
||||||
{
|
{
|
||||||
return user.Roles.Contains(role);
|
return user.Roles.Contains(role);
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ public static class DiscordPermissions
|
|||||||
/// <param name="user">The user</param>
|
/// <param name="user">The user</param>
|
||||||
/// <param name="permission">The permission</param>
|
/// <param name="permission">The permission</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static bool hasPermission(this SocketGuildUser user, GuildPermission permission)
|
public static bool HasPermission(this SocketGuildUser user, GuildPermission permission)
|
||||||
{
|
{
|
||||||
return user.Roles.Where(role => role.hasPermission(permission)).Any() || user.Guild.Owner == user;
|
return user.Roles.Where(role => role.hasPermission(permission)).Any() || user.Guild.Owner == user;
|
||||||
}
|
}
|
||||||
@@ -47,9 +47,9 @@ public static class DiscordPermissions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="user">The user</param>
|
/// <param name="user">The user</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static bool isAdmin(this SocketGuildUser user)
|
public static bool IsAdmin(this SocketGuildUser user)
|
||||||
{
|
{
|
||||||
return user.hasPermission(GuildPermission.Administrator);
|
return user.HasPermission(GuildPermission.Administrator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -57,8 +57,8 @@ public static class DiscordPermissions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="user">The user</param>
|
/// <param name="user">The user</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static bool isAdmin(this SocketUser user)
|
public static bool IsAdmin(this SocketUser user)
|
||||||
{
|
{
|
||||||
return isAdmin((SocketGuildUser)user);
|
return IsAdmin((SocketGuildUser)user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,141 +1,88 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
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.Others.Exceptions;
|
|
||||||
|
|
||||||
namespace PluginManager.Others;
|
namespace PluginManager.Others;
|
||||||
|
|
||||||
public class SettingsDictionary<TKey, TValue> : IDictionary<TKey, TValue>
|
public class SettingsDictionary<TKey, TValue>
|
||||||
{
|
{
|
||||||
public string? _file { get; }
|
private string _File { get; }
|
||||||
private IDictionary<TKey, TValue>? _dictionary;
|
private IDictionary<TKey, TValue> _Dictionary;
|
||||||
|
|
||||||
public SettingsDictionary(string? file)
|
public SettingsDictionary(string file)
|
||||||
{
|
{
|
||||||
_file = file;
|
this._File = file;
|
||||||
if (!LoadFromFile())
|
_Dictionary = null!;
|
||||||
{
|
|
||||||
ConfigFailedToLoad.CreateError("Failed to load config")
|
|
||||||
.AppendError("The file is empty or does not exist")
|
|
||||||
.IsFatal()
|
|
||||||
.HandleException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SaveToFile()
|
public async Task SaveToFile()
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(_file))
|
if (!string.IsNullOrEmpty(_File))
|
||||||
await JsonManager.SaveToJsonFile(_file, _dictionary);
|
await JsonManager.SaveToJsonFile(_File, _Dictionary);
|
||||||
}
|
|
||||||
|
|
||||||
private bool LoadFromFile()
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(_file))
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (File.Exists(_file))
|
|
||||||
{
|
|
||||||
string FileContent = File.ReadAllText(_file);
|
|
||||||
if (string.IsNullOrEmpty(FileContent))
|
|
||||||
File.WriteAllText(_file, "{}");
|
|
||||||
|
|
||||||
if(!FileContent.Contains("{") || !FileContent.Contains("}"))
|
|
||||||
File.WriteAllText(_file, "{}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
File.WriteAllText(_file, "{}");
|
|
||||||
_dictionary = JsonManager.ConvertFromJson<IDictionary<TKey, TValue>>(_file).Result;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
ConfigFailedToLoad
|
|
||||||
.CreateError("Failed to load config")
|
|
||||||
.IsFatal()
|
|
||||||
.HandleException();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||||
{
|
{
|
||||||
return _dictionary!.GetEnumerator();
|
return _Dictionary.GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
public async Task<bool> LoadFromFile()
|
||||||
{
|
{
|
||||||
return ((IEnumerable) _dictionary!).GetEnumerator();
|
if (string.IsNullOrEmpty(_File))
|
||||||
}
|
return false;
|
||||||
|
|
||||||
public void Add(KeyValuePair<TKey, TValue> item)
|
if(!File.Exists(_File))
|
||||||
{
|
{
|
||||||
this._dictionary!.Add(item);
|
_Dictionary = new Dictionary<TKey, TValue>();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
string fileAsText = await File.ReadAllTextAsync(_File);
|
||||||
|
if(string.IsNullOrEmpty(fileAsText) || string.IsNullOrWhiteSpace(fileAsText))
|
||||||
{
|
{
|
||||||
this._dictionary!.Clear();
|
_Dictionary = new Dictionary<TKey, TValue>();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Contains(KeyValuePair<TKey, TValue> item)
|
_Dictionary = await JsonManager.ConvertFromJson<IDictionary<TKey,TValue>>(fileAsText);
|
||||||
{
|
|
||||||
return this._dictionary!.Contains(item);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
|
|
||||||
{
|
|
||||||
this._dictionary!.CopyTo(array, arrayIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Remove(KeyValuePair<TKey, TValue> item)
|
|
||||||
{
|
|
||||||
return this._dictionary!.Remove(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count => _dictionary!.Count;
|
|
||||||
public bool IsReadOnly => _dictionary!.IsReadOnly;
|
|
||||||
public void Add(TKey key, TValue value)
|
public void Add(TKey key, TValue value)
|
||||||
{
|
{
|
||||||
this._dictionary!.Add(key, value);
|
_Dictionary.Add(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsAllKeys(params TKey[] keys)
|
||||||
|
{
|
||||||
|
return keys.All(key => _Dictionary.ContainsKey(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ContainsKey(TKey key)
|
public bool ContainsKey(TKey key)
|
||||||
{
|
{
|
||||||
return this._dictionary!.ContainsKey(key);
|
return _Dictionary.ContainsKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Remove(TKey key)
|
public bool Remove(TKey key)
|
||||||
{
|
{
|
||||||
return this._dictionary!.Remove(key);
|
return _Dictionary.Remove(key);
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetValue(TKey key, out TValue value)
|
|
||||||
{
|
|
||||||
return this._dictionary!.TryGetValue(key, out value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TValue this[TKey key]
|
public TValue this[TKey key]
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (this._dictionary!.ContainsKey(key))
|
if(!_Dictionary.ContainsKey(key))
|
||||||
if(this._dictionary[key] is string s && !string.IsNullOrEmpty(s) && !string.IsNullOrWhiteSpace(s))
|
throw new System.Exception($"The key {key} ({typeof(TKey)}) was not present in the dictionary");
|
||||||
return this._dictionary[key];
|
|
||||||
|
|
||||||
ConfigNoKeyWasPresent.CreateError($"Key {(key is string ? key : typeof(TKey).Name)} was not present in {_file ?? "config"}")
|
if(_Dictionary[key] is not TValue)
|
||||||
.AppendError("Deleting the file may fix this issue")
|
throw new System.Exception("The dictionary is corrupted. This error is critical !");
|
||||||
.IsFatal()
|
|
||||||
.HandleException();
|
|
||||||
|
|
||||||
return default!;
|
return _Dictionary[key];
|
||||||
}
|
}
|
||||||
set => this._dictionary![key] = value;
|
set => _Dictionary[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICollection<TKey> Keys => _dictionary!.Keys;
|
|
||||||
public ICollection<TValue> Values => _dictionary!.Values;
|
|
||||||
}
|
}
|
||||||
13
PluginManager/Plugin/OnlineDependencyInfo.cs
Normal file
13
PluginManager/Plugin/OnlineDependencyInfo.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace PluginManager.Plugin;
|
||||||
|
|
||||||
|
public class OnlineDependencyInfo
|
||||||
|
{
|
||||||
|
public string DownloadLink { get; private set; }
|
||||||
|
public string DownloadLocation { get; private set; }
|
||||||
|
|
||||||
|
public OnlineDependencyInfo(string downloadLink, string downloadLocation)
|
||||||
|
{
|
||||||
|
DownloadLink = downloadLink;
|
||||||
|
DownloadLocation = downloadLocation;
|
||||||
|
}
|
||||||
|
}
|
||||||
39
PluginManager/Plugin/PluginInfo.cs
Normal file
39
PluginManager/Plugin/PluginInfo.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using PluginManager.Online.Helpers;
|
||||||
|
|
||||||
|
namespace PluginManager.Plugin;
|
||||||
|
|
||||||
|
public class PluginInfo
|
||||||
|
{
|
||||||
|
public string PluginName { get; private set; }
|
||||||
|
public PluginVersion PluginVersion { get; private set; }
|
||||||
|
public string FilePath { get; private set; }
|
||||||
|
public List<string> ListOfDependancies {get; private set;}
|
||||||
|
public bool IsMarkedToUninstall {get; internal set;}
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
|
public PluginInfo(string pluginName, PluginVersion pluginVersion, List<string> listOfDependancies, bool isMarkedToUninstall)
|
||||||
|
{
|
||||||
|
PluginName = pluginName;
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PluginInfo FromOnlineInfo(PluginOnlineInfo onlineInfo)
|
||||||
|
{
|
||||||
|
return new PluginInfo(onlineInfo.Name, onlineInfo.Version, onlineInfo.Dependencies.Select(dep => dep.DownloadLocation).ToList());
|
||||||
|
}
|
||||||
|
}
|
||||||
51
PluginManager/Plugin/PluginOnlineInfo.cs
Normal file
51
PluginManager/Plugin/PluginOnlineInfo.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PluginManager.Online.Helpers;
|
||||||
|
using PluginManager.Others;
|
||||||
|
|
||||||
|
namespace PluginManager.Plugin;
|
||||||
|
|
||||||
|
public class PluginOnlineInfo
|
||||||
|
{
|
||||||
|
public string Name { 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 OSType SupportedOS { get; private set; }
|
||||||
|
public bool HasDependencies { get; init; }
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
|
public PluginOnlineInfo(string name, PluginVersion version, string description, string downLoadLink, OSType supportedOS, List<OnlineDependencyInfo> dependencies)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Version = version;
|
||||||
|
Description = description;
|
||||||
|
DownLoadLink = downLoadLink;
|
||||||
|
SupportedOS = supportedOS;
|
||||||
|
Dependencies = dependencies;
|
||||||
|
HasDependencies = dependencies.Count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PluginOnlineInfo(string name, PluginVersion version, string description, string downLoadLink, OSType supportedOS)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Version = version;
|
||||||
|
Description = description;
|
||||||
|
DownLoadLink = downLoadLink;
|
||||||
|
SupportedOS = supportedOS;
|
||||||
|
Dependencies = new List<OnlineDependencyInfo>();
|
||||||
|
HasDependencies = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<PluginOnlineInfo> FromRawData(string jsonText)
|
||||||
|
{
|
||||||
|
return await JsonManager.ConvertFromJson<PluginOnlineInfo>(jsonText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{Name} - {Version} ({Description})";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
@@ -12,7 +12,10 @@
|
|||||||
<None Remove="BlankWindow1.xaml" />
|
<None Remove="BlankWindow1.xaml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Discord.Net" Version="3.11.0" />
|
<PackageReference Include="Discord.Net" Version="3.14.1" />
|
||||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.118" />
|
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.118" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<UpToDateCheckInput Remove="UI\Controls\MessageBox.axaml" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
94
PluginManager/Updater/Application/AppUpdater.cs
Normal file
94
PluginManager/Updater/Application/AppUpdater.cs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using PluginManager.Interfaces.Updater;
|
||||||
|
|
||||||
|
namespace PluginManager.Updater.Application
|
||||||
|
{
|
||||||
|
public class AppUpdater
|
||||||
|
{
|
||||||
|
private static readonly string _DefaultUpdateUrl = "https://github.com/andreitdr/SethDiscordBot/releases/latest";
|
||||||
|
|
||||||
|
private async Task<AppVersion> GetOnlineVersion()
|
||||||
|
{
|
||||||
|
HttpClient client = new HttpClient();
|
||||||
|
var response = await client.GetAsync(_DefaultUpdateUrl);
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
var version = Regex.Match(content, @"<title>.+?v(\d+\.\d+\.\d+.\d+).+?</title>").Groups[1].Value;
|
||||||
|
|
||||||
|
return new AppVersion(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AppVersion.CurrentAppVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Update> CheckForUpdates()
|
||||||
|
{
|
||||||
|
var latestVersion = await GetOnlineVersion();
|
||||||
|
if(latestVersion.IsNewerThan(AppVersion.CurrentAppVersion))
|
||||||
|
{
|
||||||
|
return new Update(AppVersion.CurrentAppVersion, latestVersion, _DefaultUpdateUrl, await GetUpdateNotes());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Update.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> GetUpdateNotes()
|
||||||
|
{
|
||||||
|
HttpClient client = new HttpClient();
|
||||||
|
var response = await client.GetAsync(_DefaultUpdateUrl);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
var markdownStart = content.IndexOf("<div data-pjax=\"true\" data-test-selector=\"body-content\"");
|
||||||
|
if(markdownStart == -1)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
markdownStart = content.IndexOf(">", markdownStart) + 1; // Move past the opening tag
|
||||||
|
var markdownEnd = content.IndexOf("</div>", markdownStart);
|
||||||
|
var markdown = content.Substring(markdownStart, markdownEnd - markdownStart).Trim();
|
||||||
|
markdown = RemoveHtmlTags(markdown);
|
||||||
|
|
||||||
|
markdown = ApplyMarkdownFormatting(markdown);
|
||||||
|
|
||||||
|
return markdown;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private string RemoveHtmlTags(string text)
|
||||||
|
{
|
||||||
|
return Regex.Replace(text, "<.*?>", "").Trim();
|
||||||
|
}
|
||||||
|
private string ApplyMarkdownFormatting(string markdown)
|
||||||
|
{
|
||||||
|
// Apply markdown formatting
|
||||||
|
markdown = markdown.Replace("**", "**"); // Bold
|
||||||
|
markdown = markdown.Replace("*", "*"); // Italic
|
||||||
|
markdown = markdown.Replace("`", "`"); // Inline code
|
||||||
|
markdown = markdown.Replace("```", "```"); // Code block
|
||||||
|
markdown = markdown.Replace(">", ">"); // Greater than symbol
|
||||||
|
markdown = markdown.Replace("<", "<"); // Less than symbol
|
||||||
|
markdown = markdown.Replace("&", "&"); // Ampersand
|
||||||
|
markdown = markdown.Replace(""", "\""); // Double quote
|
||||||
|
markdown = markdown.Replace("'", "'"); // Single quote
|
||||||
|
markdown = markdown.Replace(" - ", "\n- "); // Convert bullet points to markdown list items
|
||||||
|
|
||||||
|
return markdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
30
PluginManager/Updater/Application/Update.cs
Normal file
30
PluginManager/Updater/Application/Update.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Discord.Commands;
|
||||||
|
|
||||||
|
using PluginManager.Interfaces.Updater;
|
||||||
|
|
||||||
|
namespace PluginManager.Updater.Application
|
||||||
|
{
|
||||||
|
public class Update
|
||||||
|
{
|
||||||
|
public readonly static Update None = new Update(AppVersion.CurrentAppVersion, AppVersion.CurrentAppVersion, string.Empty, string.Empty);
|
||||||
|
|
||||||
|
public AppVersion UpdateVersion { get; private set; }
|
||||||
|
public AppVersion CurrentVersion { get; private set; }
|
||||||
|
public string UpdateUrl { get; private set; }
|
||||||
|
public string UpdateNotes { get; private set; }
|
||||||
|
|
||||||
|
public Update(AppVersion currentVersion, AppVersion updateVersion, string updateUrl, string updateNotes)
|
||||||
|
{
|
||||||
|
UpdateVersion = updateVersion;
|
||||||
|
CurrentVersion = currentVersion;
|
||||||
|
UpdateUrl = updateUrl;
|
||||||
|
UpdateNotes = updateNotes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
56
PluginManager/Updater/Plugins/PluginUpdater.cs
Normal file
56
PluginManager/Updater/Plugins/PluginUpdater.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PluginManager.Online;
|
||||||
|
using PluginManager.Plugin;
|
||||||
|
|
||||||
|
namespace PluginManager.Updater.Plugins;
|
||||||
|
|
||||||
|
public class PluginUpdater
|
||||||
|
{
|
||||||
|
private readonly PluginsManager _PluginsManager;
|
||||||
|
|
||||||
|
public PluginUpdater(PluginsManager pluginManager)
|
||||||
|
{
|
||||||
|
_PluginsManager = pluginManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PluginOnlineInfo> GetPluginInfo(string pluginName)
|
||||||
|
{
|
||||||
|
var result = await _PluginsManager.GetPluginDataByName(pluginName);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PluginInfo> GetLocalPluginInfo(string pluginName)
|
||||||
|
{
|
||||||
|
string pluginsDatabase = File.ReadAllText(Config.AppSettings["PluginDatabase"]);
|
||||||
|
List<PluginInfo> installedPlugins = await JsonManager.ConvertFromJson<List<PluginInfo>>(pluginsDatabase);
|
||||||
|
|
||||||
|
var result = installedPlugins.Find(p => p.PluginName == pluginName);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdatePlugin(string pluginName, IProgress<float>? progressMeter = null)
|
||||||
|
{
|
||||||
|
PluginOnlineInfo pluginInfo = await GetPluginInfo(pluginName);
|
||||||
|
await ServerCom.DownloadFileAsync(pluginInfo.DownLoadLink, $"{Config.AppSettings["PluginFolder"]}/{pluginName}.dll", progressMeter);
|
||||||
|
|
||||||
|
foreach(OnlineDependencyInfo dependency in pluginInfo.Dependencies)
|
||||||
|
await ServerCom.DownloadFileAsync(dependency.DownloadLocation, 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);
|
||||||
|
var pluginInfo = await GetPluginInfo(pluginName);
|
||||||
|
|
||||||
|
return pluginInfo.Version.IsNewerThan(localPluginInfo.PluginVersion);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
65
README.md
65
README.md
@@ -3,16 +3,12 @@
|
|||||||
This is a Discord Bot made with C# that accepts plugins as extensions for more commands and events. All basic commands are built in already in the PluginManager class library.
|
This is a Discord Bot made with C# that accepts plugins as extensions for more commands and events. All basic commands are built in already in the PluginManager class library.
|
||||||
This project is based on:
|
This project is based on:
|
||||||
|
|
||||||
- [.NET 6 (C#)](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)
|
- [.NET 8 (C#)](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
|
||||||
- [Discord.Net](https://github.com/discord-net/Discord.Net)
|
- [Discord.Net](https://github.com/discord-net/Discord.Net)
|
||||||
|
|
||||||
- Some plugins can be found [here](https://github.com/andreitdr/SethPlugins).
|
|
||||||
|
|
||||||
|
|
||||||
## Plugins
|
## Plugins
|
||||||
#### Requirements:
|
- Some plugins can be found in [this repo](https://github.com/andreitdr/SethPlugins).
|
||||||
- [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&channel=Release&version=VS2022&source=VSLandingPage&cid=2030&passive=false)
|
|
||||||
- .NET 6 (downloaded with Visual Studio)
|
|
||||||
|
|
||||||
Plugin Types:
|
Plugin Types:
|
||||||
1. Commands
|
1. Commands
|
||||||
@@ -21,6 +17,10 @@ Plugin Types:
|
|||||||
|
|
||||||
### How to create a plugin
|
### How to create a plugin
|
||||||
|
|
||||||
|
#### Requirements:
|
||||||
|
- [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&channel=Release&version=VS2022&source=VSLandingPage&cid=2030&passive=false)
|
||||||
|
- .NET 8 (downloaded with Visual Studio)
|
||||||
|
|
||||||
First of all, create a new project (class library) in Visual Studio.
|
First of all, create a new project (class library) in Visual Studio.
|
||||||
Then import the PluginManager as reference to your project.
|
Then import the PluginManager as reference to your project.
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ public class LevelCommand : DBCommand
|
|||||||
|
|
||||||
From here on, start coding. When your plugin is done, build it as any DLL project then add it to the following path
|
From here on, start coding. When your plugin is done, build it as any DLL project then add it to the following path
|
||||||
`{bot_executable}/Data/Plugins/<optional subfolder>/[plugin name].dll`
|
`{bot_executable}/Data/Plugins/<optional subfolder>/[plugin name].dll`
|
||||||
Then, reload bot and execute command `lp` in bot's console. The plugin should be loaded into memory or an error is thrown if not. If an error is thrown, then
|
Then, reload bot and execute command `plugin load` in the console. The plugin should be loaded into memory or an error is thrown if not. If an error is thrown, then
|
||||||
there is something wrong in your command's code.
|
there is something wrong in your command's code.
|
||||||
|
|
||||||
## 2. Events
|
## 2. Events
|
||||||
@@ -147,7 +147,7 @@ namespace SlashCommands
|
|||||||
{
|
{
|
||||||
public string Name => "random";
|
public string Name => "random";
|
||||||
|
|
||||||
public string Description => "Generates a random nunber between 2 values";
|
public string Description => "Generates a random number between 2 values";
|
||||||
|
|
||||||
public bool canUseDM => true;
|
public bool canUseDM => true;
|
||||||
|
|
||||||
@@ -192,4 +192,51 @@ namespace SlashCommands
|
|||||||
You can create multiple commands, events and slash commands into one single plugin (class library). The PluginManager will detect the classes and load them individualy. If there are more commands (normal commands, events or slash commands) into a single project (class library) they can use the same resources (a class for example) that is contained within the plugin.
|
You can create multiple commands, events and slash commands into one single plugin (class library). The PluginManager will detect the classes and load them individualy. If there are more commands (normal commands, events or slash commands) into a single project (class library) they can use the same resources (a class for example) that is contained within the plugin.
|
||||||
|
|
||||||
|
|
||||||
> Updated: 5.08.2023
|
# Building from source
|
||||||
|
|
||||||
|
## Required tools
|
||||||
|
You must have dotnet 8 installed in order to compile.
|
||||||
|
You might run this commands with sudo in order to install dotnet successfully.
|
||||||
|
### On Linux
|
||||||
|
#### Arch
|
||||||
|
```sh
|
||||||
|
pacman -S dotnet-sdk-8.0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Debian / Ubuntu
|
||||||
|
```sh
|
||||||
|
apt install dotnet-sdk-8.0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Fedora / RHEL
|
||||||
|
```sh
|
||||||
|
dnf install dotnet-sdk-8.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### On Windows
|
||||||
|
#### Default method
|
||||||
|
Download and install dotnet 8 from the official Microsoft website using [this](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) link.
|
||||||
|
|
||||||
|
#### Using Visual Studio
|
||||||
|
Download and install Visual Studio 2022 and select .NET Desktop Development while installing Visual Studio 2022.
|
||||||
|
Open Visual Studio and select Clone a repo and paste the following link: `https://github.com/andreitdr/SethDiscordBot`.
|
||||||
|
|
||||||
|
Open the solution in Visual Studio and build it.
|
||||||
|
|
||||||
|
> Note: You might need to manually restore the NuGet packages, but VS2022 should take care of them automatically for you.
|
||||||
|
> If not then you will need to click on Dependencies -> Packages for each project that has a yellow sign over the Dependancies tab and click Update.
|
||||||
|
|
||||||
|
## Cloning the repository
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/andreitdr/SethDiscordBot
|
||||||
|
cd SethDiscordBot
|
||||||
|
dotnet build
|
||||||
|
```
|
||||||
|
|
||||||
|
After the build succeeds, check the `/bin/Debug` folders for each project to see the built items.
|
||||||
|
|
||||||
|
Follow the on-screen prompts to make the bot run.
|
||||||
|
|
||||||
|
> Updated: 01.04.2024
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 17
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 17.1.32421.90
|
VisualStudioVersion = 17.1.32421.90
|
||||||
@@ -9,9 +8,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PluginManager", "PluginMana
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SethPlugins", "SethPlugins", "{78B6D390-F61A-453F-B38D-E4C054321615}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SethPlugins", "SethPlugins", "{78B6D390-F61A-453F-B38D-E4C054321615}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MusicPlayer", "..\SethPlugins\MusicPlayer\MusicPlayer.csproj", "{1690CBBC-BDC0-4DD8-B701-F8817189D9D5}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscordBotUI", "DiscordBotUI\DiscordBotUI\DiscordBotUI.csproj", "{71293BF7-79D0-4707-AB4B-FDD16800FA81}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LevelingSystem", "..\SethPlugins\LevelingSystem\LevelingSystem.csproj", "{BFE3491C-AC01-4252-B242-6451270FC548}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscordBotUI.Desktop", "DiscordBotUI\DiscordBotUI.Desktop\DiscordBotUI.Desktop.csproj", "{F23CF852-2042-4BDE-ABFE-D4F5BD9B991D}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@@ -27,22 +26,18 @@ Global
|
|||||||
{EDD4D9B3-98DD-4367-A09F-D1C5ACB61132}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{EDD4D9B3-98DD-4367-A09F-D1C5ACB61132}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{EDD4D9B3-98DD-4367-A09F-D1C5ACB61132}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{EDD4D9B3-98DD-4367-A09F-D1C5ACB61132}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{EDD4D9B3-98DD-4367-A09F-D1C5ACB61132}.Release|Any CPU.Build.0 = Release|Any CPU
|
{EDD4D9B3-98DD-4367-A09F-D1C5ACB61132}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{1690CBBC-BDC0-4DD8-B701-F8817189D9D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{71293BF7-79D0-4707-AB4B-FDD16800FA81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{1690CBBC-BDC0-4DD8-B701-F8817189D9D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{71293BF7-79D0-4707-AB4B-FDD16800FA81}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{1690CBBC-BDC0-4DD8-B701-F8817189D9D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{71293BF7-79D0-4707-AB4B-FDD16800FA81}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{1690CBBC-BDC0-4DD8-B701-F8817189D9D5}.Release|Any CPU.Build.0 = Release|Any CPU
|
{71293BF7-79D0-4707-AB4B-FDD16800FA81}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{BFE3491C-AC01-4252-B242-6451270FC548}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{F23CF852-2042-4BDE-ABFE-D4F5BD9B991D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{BFE3491C-AC01-4252-B242-6451270FC548}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{F23CF852-2042-4BDE-ABFE-D4F5BD9B991D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{BFE3491C-AC01-4252-B242-6451270FC548}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{F23CF852-2042-4BDE-ABFE-D4F5BD9B991D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{BFE3491C-AC01-4252-B242-6451270FC548}.Release|Any CPU.Build.0 = Release|Any CPU
|
{F23CF852-2042-4BDE-ABFE-D4F5BD9B991D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
|
||||||
{1690CBBC-BDC0-4DD8-B701-F8817189D9D5} = {78B6D390-F61A-453F-B38D-E4C054321615}
|
|
||||||
{BFE3491C-AC01-4252-B242-6451270FC548} = {78B6D390-F61A-453F-B38D-E4C054321615}
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {3FB3C5DE-ED21-4D2E-ABDD-3A00EE4A2FFF}
|
SolutionGuid = {3FB3C5DE-ED21-4D2E-ABDD-3A00EE4A2FFF}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
|||||||
Reference in New Issue
Block a user