Compare commits
126 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f3c3c7939c | |||
| 361ed37362 | |||
| ed3128b940 | |||
| 6bbd68a135 | |||
| 06d322b5b3 | |||
| 5497ee9119 | |||
| 0aa78e3560 | |||
| 41ad37b3bb | |||
| 0104d09509 | |||
| b4f5e40f12 | |||
| 7107b17f19 | |||
| 42e1fd917e | |||
| 9e6fcdbe6f | |||
| a24d8aa222 | |||
| 0e5c9ff14b | |||
| f8977d8840 | |||
| bb9768f3a1 | |||
| b3d6930142 | |||
| 5254be44be | |||
| 207e0d6abd | |||
| 968d23380f | |||
| fff9e2e897 | |||
| 3e4e777d8d | |||
| 7f906d400f | |||
| c1161a3bca | |||
| ac512e3a27 | |||
| 730b628fe3 | |||
| 701edc5c6a | |||
| e7688762b8 | |||
| a7a71bf49a | |||
| ac7212ca00 | |||
| 298e557260 | |||
| 7ba791f906 | |||
| 4a6a12baae | |||
| f1dda5da3c | |||
| 3ab96e2d0d | |||
| 970c519a32 | |||
| 188920ec7f | |||
| dcfc4ea32f | |||
| a8c02176d3 | |||
| 1665d47a25 | |||
| 0b2f1e6ab6 | |||
| bcd9245502 | |||
| 59da9b295b | |||
| 99d7d5e7e7 | |||
| e4c60f1606 | |||
| 77f1bef862 | |||
| f16c139362 | |||
| c94cdca6eb | |||
| dcdf80112d | |||
| eb836c5b74 | |||
| de680c6771 | |||
|
|
bcef58a46b | ||
|
|
0dc8cdbce5 | ||
|
|
dbdbaa9802 | ||
|
|
5edcf93371 | ||
|
|
b0be76c62b | ||
| 75a77389a8 | |||
| 0bbced3d58 | |||
| 244209093e | |||
| 54a68d635d | |||
| d7a5cb5a64 | |||
| 6124f89cb0 | |||
| 810a527cc1 | |||
| 0a2dff0c6d | |||
| 382c376c03 | |||
| 84b7d663bc | |||
| 623232b67e | |||
| d5df6cfb9d | |||
| 10b9548c29 | |||
| fa1a136ef1 | |||
| d20cb62139 | |||
| f2418d0395 | |||
| 460a85944a | |||
| 7e2fa02d07 | |||
| 873855937f | |||
| 1cdd2644df | |||
| 532540b74f | |||
| 9ba4ca43e2 | |||
| 8bcaf3f254 | |||
| 0d5c90323a | |||
| 5b01b15216 | |||
| 4f18f505f4 | |||
| 2d3566a01a | |||
| 22f2cd4e59 | |||
| 1683234376 | |||
| 69d99b4189 | |||
| 4a5e0ef2f3 | |||
| 79731a9704 | |||
| bd53d099d1 | |||
| de61f5de88 | |||
| 0527d43dd2 | |||
| e3511cd96b | |||
| d355d3c9b7 | |||
| 5bb13aa4a6 | |||
| 655f5e2ce0 | |||
| 9014d78a7d | |||
| 1c026e7f49 | |||
| d32b3902c9 | |||
| e5f3aff39a | |||
| 11ec02ef68 | |||
| cad19935d5 | |||
| 47f88f167f | |||
| 9d6c335799 | |||
| cbaf552e7a | |||
| a4975a4578 | |||
| 725d02d152 | |||
| ae7118e89a | |||
| cad3bb5b75 | |||
| 269d7d56ff | |||
| 403c023191 | |||
| 3f7a8e04d4 | |||
| 0abbd24b86 | |||
| 21f1975fbc | |||
| d6f072904e | |||
| 6fc491a0d6 | |||
| 7bc9db03f0 | |||
| 641f0f2856 | |||
| ef5439d204 | |||
| c2093c2aca | |||
| a39f7bb0c9 | |||
| 6d73ec7f24 | |||
| 24d4bee85b | |||
| a1bde013e6 | |||
| 23f951167b | |||
| 26505fd5c9 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -98,6 +98,7 @@ StyleCopReport.xml
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
*.code-workspace
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
@@ -368,3 +369,8 @@ FodyWeavers.xsd
|
||||
#folders
|
||||
/Plugins/
|
||||
/DiscordBot.rar
|
||||
/DiscordBot/Data/
|
||||
/DiscordBot/Updater/
|
||||
.idea/
|
||||
DiscordBot/Launcher.exe
|
||||
DiscordBotUI/*
|
||||
|
||||
26
.vscode/launch.json
vendored
Normal file
26
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"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
Normal file
41
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"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,4 +0,0 @@
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="4.0.0.0" />
|
||||
</dependentAssembly>
|
||||
24
DiscordBot/Bot/Actions/Clear.cs
Normal file
24
DiscordBot/Bot/Actions/Clear.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using PluginManager;
|
||||
using PluginManager.Interfaces;
|
||||
using PluginManager.Others;
|
||||
|
||||
namespace DiscordBot.Bot.Actions;
|
||||
|
||||
public class Clear : ICommandAction
|
||||
{
|
||||
public string ActionName => "clear";
|
||||
public string Description => "Clears the console";
|
||||
public string Usage => "clear";
|
||||
public InternalActionRunType RunType => InternalActionRunType.ON_CALL;
|
||||
|
||||
public Task Execute(string[] args)
|
||||
{
|
||||
Console.Clear();
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
Console.WriteLine("===== Seth Discord Bot =====");
|
||||
Console.ResetColor();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
47
DiscordBot/Bot/Actions/Exit.cs
Normal file
47
DiscordBot/Bot/Actions/Exit.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using PluginManager;
|
||||
using PluginManager.Interfaces;
|
||||
using PluginManager.Others;
|
||||
|
||||
namespace DiscordBot.Bot.Actions;
|
||||
|
||||
public class Exit : ICommandAction
|
||||
{
|
||||
public string ActionName => "exit";
|
||||
public string Description => "Exits the bot and saves the config. Use exit help for more info.";
|
||||
public string Usage => "exit [help|force (-f)]";
|
||||
public InternalActionRunType RunType => InternalActionRunType.ON_CALL;
|
||||
|
||||
public async Task Execute(string[] args)
|
||||
{
|
||||
if (args is null || args.Length == 0)
|
||||
{
|
||||
Config.Logger.Log("Exiting...", "Exit", isInternal: false);
|
||||
await Config.AppSettings.SaveToFile();
|
||||
await Config.Logger.SaveToFile();
|
||||
Environment.Exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch ( args[0] )
|
||||
{
|
||||
case "help":
|
||||
Console.WriteLine("Usage : exit [help|force]");
|
||||
Console.WriteLine("help : Displays this message");
|
||||
Console.WriteLine("force | -f : Exits the bot without saving the config");
|
||||
break;
|
||||
|
||||
case "-f":
|
||||
case "force":
|
||||
Config.Logger.Log("Exiting (FORCE)...", "Exit", LogLevel.WARNING, false);
|
||||
Environment.Exit(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
Console.WriteLine("Invalid argument !");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
62
DiscordBot/Bot/Actions/Help.cs
Normal file
62
DiscordBot/Bot/Actions/Help.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using DiscordBot.Utilities;
|
||||
using PluginManager.Interfaces;
|
||||
using PluginManager.Others;
|
||||
|
||||
namespace DiscordBot.Bot.Actions;
|
||||
|
||||
public class Help : ICommandAction
|
||||
{
|
||||
public string ActionName => "help";
|
||||
|
||||
public string Description => "Shows the list of commands and their usage";
|
||||
|
||||
public string Usage => "help [command]";
|
||||
|
||||
public InternalActionRunType RunType => InternalActionRunType.ON_CALL;
|
||||
|
||||
public async Task Execute(string[] args)
|
||||
{
|
||||
if (args == null || args.Length == 0)
|
||||
{
|
||||
var items = new List<string[]>
|
||||
{
|
||||
new[] { "-", "-", "-" },
|
||||
new[] { "Command", "Usage", "Description" },
|
||||
new[] { "-", "-", "-" }
|
||||
};
|
||||
|
||||
foreach (var a in Program.internalActionManager.Actions)
|
||||
items.Add(new[] { a.Key, a.Value.Usage, a.Value.Description });
|
||||
|
||||
items.Add(new[] { "-", "-", "-" });
|
||||
|
||||
Utilities.Utilities.FormatAndAlignTable(items,
|
||||
TableFormat.CENTER_EACH_COLUMN_BASED
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Program.internalActionManager.Actions.ContainsKey(args[0]))
|
||||
{
|
||||
Console.WriteLine("Command not found");
|
||||
return;
|
||||
}
|
||||
|
||||
var action = Program.internalActionManager.Actions[args[0]];
|
||||
var actionData = new List<string[]>
|
||||
{
|
||||
new[] { "-", "-", "-" },
|
||||
new[] { "Command", "Usage", "Description" },
|
||||
new[] { "-", "-", "-" },
|
||||
new[] { action.ActionName, action.Usage, action.Description },
|
||||
new[] { "-", "-", "-" }
|
||||
};
|
||||
|
||||
Utilities.Utilities.FormatAndAlignTable(actionData,
|
||||
TableFormat.CENTER_EACH_COLUMN_BASED
|
||||
);
|
||||
}
|
||||
}
|
||||
197
DiscordBot/Bot/Actions/Plugin.cs
Normal file
197
DiscordBot/Bot/Actions/Plugin.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using DiscordBot.Utilities;
|
||||
using PluginManager;
|
||||
using PluginManager.Interfaces;
|
||||
using PluginManager.Loaders;
|
||||
using PluginManager.Online;
|
||||
using PluginManager.Others;
|
||||
|
||||
namespace DiscordBot.Bot.Actions;
|
||||
|
||||
public class Plugin : ICommandAction
|
||||
{
|
||||
private bool pluginsLoaded;
|
||||
public string ActionName => "plugin";
|
||||
public string Description => "Manages plugins. Use plugin help for more info.";
|
||||
public string Usage => "plugin [help|list|load|install]";
|
||||
public InternalActionRunType RunType => InternalActionRunType.ON_CALL;
|
||||
|
||||
public async Task Execute(string[] args)
|
||||
{
|
||||
if (args is null || args.Length == 0 || args[0] == "help")
|
||||
{
|
||||
Console.WriteLine("Usage : plugin [help|list|load|install]");
|
||||
Console.WriteLine("help : Displays this message");
|
||||
Console.WriteLine("list : Lists all plugins");
|
||||
Console.WriteLine("load : Loads all plugins");
|
||||
Console.WriteLine("install : Installs a plugin");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch ( args[0] )
|
||||
{
|
||||
case "list":
|
||||
var manager =
|
||||
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;
|
||||
|
||||
|
||||
case "load":
|
||||
if (pluginsLoaded)
|
||||
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
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
if (exception is null)
|
||||
Console.WriteLine("An error occured while loading: " + name);
|
||||
else
|
||||
Console.WriteLine("[CMD] Failed to load command : " + name + " because " +
|
||||
exception!.Message
|
||||
);
|
||||
}
|
||||
|
||||
Console.ForegroundColor = cc;
|
||||
};
|
||||
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;
|
||||
|
||||
case "install":
|
||||
var pluginName = string.Join(' ', args, 1, args.Length - 1);
|
||||
if (string.IsNullOrEmpty(pluginName) || pluginName.Length < 2)
|
||||
{
|
||||
Console.WriteLine("Please specify a plugin name");
|
||||
Console.Write("Plugin name : ");
|
||||
pluginName = Console.ReadLine();
|
||||
if (string.IsNullOrEmpty(pluginName) || pluginName.Length < 2)
|
||||
{
|
||||
Console.WriteLine("Invalid plugin name");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var pluginManager =
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
|
||||
using PluginManager;
|
||||
using PluginManager.Interfaces;
|
||||
using PluginManager.Loaders;
|
||||
using PluginManager.Others;
|
||||
|
||||
namespace DiscordBot.Discord.Commands;
|
||||
namespace DiscordBot.Bot.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// The help command
|
||||
@@ -43,19 +38,16 @@ internal class Help : DBCommand
|
||||
/// The main body of the command
|
||||
/// </summary>
|
||||
/// <param name="context">The command context</param>
|
||||
public void ExecuteServer(SocketCommandContext context)
|
||||
public void ExecuteServer(DBCommandExecutingArguments args)
|
||||
{
|
||||
var args = Functions.GetArguments(context.Message);
|
||||
if (args.Count != 0)
|
||||
if (args.arguments is not null)
|
||||
{
|
||||
foreach (var item in args)
|
||||
{
|
||||
var e = GenerateHelpCommand(item);
|
||||
var e = GenerateHelpCommand(args.arguments[0]);
|
||||
if (e is null)
|
||||
context.Channel.SendMessageAsync("Unknown Command " + item);
|
||||
args.context.Channel.SendMessageAsync("Unknown Command " + args.arguments[0]);
|
||||
else
|
||||
context.Channel.SendMessageAsync(embed: e.Build());
|
||||
}
|
||||
args.context.Channel.SendMessageAsync(embed: e.Build());
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -65,26 +57,29 @@ internal class Help : DBCommand
|
||||
var adminCommands = "";
|
||||
var normalCommands = "";
|
||||
|
||||
foreach (var cmd in PluginLoader.Commands!)
|
||||
{
|
||||
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);
|
||||
context.Channel.SendMessageAsync(embed: embedBuilder.Build());
|
||||
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)));
|
||||
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.GetValue<string>("prefix") + cmd.Usage);
|
||||
embedBuilder.AddField("Usage", Config.AppSettings["prefix"] + cmd.Usage);
|
||||
embedBuilder.AddField("Description", cmd.Description);
|
||||
if (cmd.Aliases is null)
|
||||
return embedBuilder;
|
||||
57
DiscordBot/Bot/Commands/SlashCommands/Help.cs
Normal file
57
DiscordBot/Bot/Commands/SlashCommands/Help.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using PluginManager.Interfaces;
|
||||
using PluginManager.Loaders;
|
||||
using PluginManager.Others;
|
||||
|
||||
namespace DiscordBot.Bot.Commands.SlashCommands;
|
||||
|
||||
public class Help : DBSlashCommand
|
||||
{
|
||||
public string Name => "help";
|
||||
public string Description => "This command allows you to check all loaded commands";
|
||||
public bool canUseDM => true;
|
||||
|
||||
public List<SlashCommandOptionBuilder> Options =>
|
||||
new()
|
||||
{
|
||||
new SlashCommandOptionBuilder()
|
||||
.WithName("command")
|
||||
.WithDescription("The command you want to get help for")
|
||||
.WithRequired(false)
|
||||
.WithType(ApplicationCommandOptionType.String)
|
||||
};
|
||||
|
||||
public async void ExecuteServer(SocketSlashCommand context)
|
||||
{
|
||||
EmbedBuilder embedBuilder = new();
|
||||
|
||||
embedBuilder.WithTitle("Help Command");
|
||||
embedBuilder.WithColor(Functions.RandomColor);
|
||||
var slashCommands = PluginLoader.SlashCommands;
|
||||
var options = context.Data.Options;
|
||||
|
||||
//Console.WriteLine("Options: " + options.Count);
|
||||
if (options is null || options.Count == 0)
|
||||
foreach (var slashCommand in slashCommands)
|
||||
embedBuilder.AddField(slashCommand.Name, slashCommand.Description);
|
||||
|
||||
if (options.Count > 0)
|
||||
{
|
||||
var commandName = options.First().Name;
|
||||
var slashCommand = slashCommands.FirstOrDefault(x => x.Name == commandName);
|
||||
if (slashCommand is null)
|
||||
{
|
||||
await context.RespondAsync("Unknown Command " + commandName);
|
||||
return;
|
||||
}
|
||||
|
||||
embedBuilder.AddField(slashCommand.Name, slashCommand.canUseDM)
|
||||
.WithDescription(slashCommand.Description);
|
||||
}
|
||||
|
||||
await context.RespondAsync(embed: embedBuilder.Build());
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Discord.WebSocket;
|
||||
using PluginManager.Interfaces;
|
||||
using PluginManager.Others;
|
||||
using PluginManager.Others.Permissions;
|
||||
using DiscordLibCommands = Discord.Commands;
|
||||
using DiscordLib = Discord;
|
||||
using OperatingSystem = PluginManager.Others.OperatingSystem;
|
||||
|
||||
namespace DiscordBot.Discord.Commands;
|
||||
|
||||
internal class Restart : DBCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Command name
|
||||
/// </summary>
|
||||
public string Command => "restart";
|
||||
|
||||
public List<string> Aliases => null;
|
||||
|
||||
/// <summary>
|
||||
/// Command Description
|
||||
/// </summary>
|
||||
public string Description => "Restart the bot";
|
||||
|
||||
/// <summary>
|
||||
/// Command usage
|
||||
/// </summary>
|
||||
public string Usage => "restart [-p | -c | -args | -cmd] <args>";
|
||||
|
||||
/// <summary>
|
||||
/// Check if the command require administrator to be executed
|
||||
/// </summary>
|
||||
public bool requireAdmin => true;
|
||||
|
||||
/// <summary>
|
||||
/// The main body of the command
|
||||
/// </summary>
|
||||
/// <param name="context">The command context</param>
|
||||
public async void ExecuteServer(DiscordLibCommands.SocketCommandContext context)
|
||||
{
|
||||
var args = Functions.GetArguments(context.Message);
|
||||
var OS = Functions.GetOperatingSystem();
|
||||
if (args.Count == 0)
|
||||
{
|
||||
switch (OS)
|
||||
{
|
||||
case OperatingSystem.WINDOWS:
|
||||
Process.Start("./DiscordBot.exe");
|
||||
break;
|
||||
case OperatingSystem.LINUX:
|
||||
case OperatingSystem.MAC_OS:
|
||||
Process.Start("./DiscordBot");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args[0])
|
||||
{
|
||||
case "-p":
|
||||
case "-poweroff":
|
||||
case "-c":
|
||||
case "-close":
|
||||
Environment.Exit(0);
|
||||
break;
|
||||
case "-cmd":
|
||||
case "-args":
|
||||
var cmd = "--args";
|
||||
|
||||
if (args.Count > 1)
|
||||
for (var i = 1; i < args.Count; i++)
|
||||
cmd += $" {args[i]}";
|
||||
|
||||
|
||||
switch (OS)
|
||||
{
|
||||
case OperatingSystem.WINDOWS:
|
||||
Functions.WriteLogFile("Restarting the bot with the following arguments: \"" + cmd + "\"");
|
||||
Process.Start("./DiscordBot.exe", cmd);
|
||||
break;
|
||||
case OperatingSystem.LINUX:
|
||||
//case PluginManager.Others.OperatingSystem.MAC_OS: ?? - not tested
|
||||
Process.Start("./DiscordBot", cmd);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
Environment.Exit(0);
|
||||
break;
|
||||
default:
|
||||
await context.Channel.SendMessageAsync("Invalid argument. Use `help restart` to see the usage.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
|
||||
using PluginManager;
|
||||
using PluginManager.Interfaces;
|
||||
|
||||
namespace DiscordBot.Discord.Commands;
|
||||
|
||||
internal class Settings : DBCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Command name
|
||||
/// </summary>
|
||||
public string Command => "set";
|
||||
|
||||
public List<string> Aliases => null;
|
||||
|
||||
/// <summary>
|
||||
/// Command Description
|
||||
/// </summary>
|
||||
public string Description => "This command allows you change all settings. Use \"set help\" to show details";
|
||||
|
||||
/// <summary>
|
||||
/// Command usage
|
||||
/// </summary>
|
||||
public string Usage => "set [keyword] [new Value]";
|
||||
|
||||
/// <summary>
|
||||
/// Check if the command require administrator to be executed
|
||||
/// </summary>
|
||||
public bool requireAdmin => true;
|
||||
|
||||
/// <summary>
|
||||
/// The main body of the command
|
||||
/// </summary>
|
||||
/// <param name="context">The command context</param>
|
||||
public async void Execute(SocketCommandContext context)
|
||||
{
|
||||
var channel = context.Message.Channel;
|
||||
try
|
||||
{
|
||||
var content = context.Message.Content;
|
||||
var data = content.Split(' ');
|
||||
var keyword = data[1];
|
||||
if (keyword.ToLower() == "help")
|
||||
{
|
||||
await channel.SendMessageAsync("set token [new value] -- set the value of the new token (require restart)");
|
||||
await channel.SendMessageAsync("set prefix [new value] -- set the value of the new preifx (require restart)");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (keyword.ToLower())
|
||||
{
|
||||
case "token":
|
||||
if (data.Length != 3)
|
||||
{
|
||||
await channel.SendMessageAsync("Invalid token !");
|
||||
return;
|
||||
}
|
||||
|
||||
Config.SetValue("token", data[2]);
|
||||
break;
|
||||
case "prefix":
|
||||
if (data.Length != 3)
|
||||
{
|
||||
await channel.SendMessageAsync("Invalid token !");
|
||||
return;
|
||||
}
|
||||
|
||||
Config.SetValue("token", data[2]);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
await channel.SendMessageAsync("Restart required ...");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
await channel.SendMessageAsync("Unknown usage to this command !\nUsage: " + Usage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
|
||||
using PluginManager.Loaders;
|
||||
using PluginManager.Others;
|
||||
using PluginManager.Others.Permissions;
|
||||
|
||||
namespace DiscordBot.Discord.Core;
|
||||
|
||||
internal class CommandHandler
|
||||
{
|
||||
private readonly string botPrefix;
|
||||
private readonly DiscordSocketClient client;
|
||||
private readonly CommandService commandService;
|
||||
|
||||
/// <summary>
|
||||
/// Command handler constructor
|
||||
/// </summary>
|
||||
/// <param name="client">The discord bot client</param>
|
||||
/// <param name="commandService">The discord bot command service</param>
|
||||
/// <param name="botPrefix">The prefix to watch for</param>
|
||||
public CommandHandler(DiscordSocketClient client, CommandService commandService, string botPrefix)
|
||||
{
|
||||
this.client = client;
|
||||
this.commandService = commandService;
|
||||
this.botPrefix = botPrefix;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The method to initialize all commands
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task InstallCommandsAsync()
|
||||
{
|
||||
client.MessageReceived += MessageHandler;
|
||||
await commandService.AddModulesAsync(Assembly.GetEntryAssembly(), null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The message handler for the bot
|
||||
/// </summary>
|
||||
/// <param name="Message">The message got from the user in discord chat</param>
|
||||
/// <returns></returns>
|
||||
private async Task MessageHandler(SocketMessage Message)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Message as SocketUserMessage == null)
|
||||
return;
|
||||
|
||||
var message = Message as SocketUserMessage;
|
||||
|
||||
if (message == null)
|
||||
return;
|
||||
|
||||
if (!message.Content.StartsWith(botPrefix))
|
||||
return;
|
||||
|
||||
var argPos = 0;
|
||||
|
||||
if (message.HasMentionPrefix(client.CurrentUser, ref argPos))
|
||||
{
|
||||
await message.Channel.SendMessageAsync("Can not exec mentioned commands !");
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.Author.IsBot)
|
||||
return;
|
||||
|
||||
var context = new SocketCommandContext(client, message);
|
||||
|
||||
await commandService.ExecuteAsync(context, argPos, null);
|
||||
|
||||
var plugin = PluginLoader.Commands!.Where(p => p.Command == message.Content.Split(' ')[0].Substring(botPrefix.Length) || (p.Aliases is not null && p.Aliases.Contains(message.Content.Split(' ')[0].Substring(botPrefix.Length)))).FirstOrDefault();
|
||||
|
||||
if (plugin is null) throw new System.Exception("Failed to run command. !");
|
||||
|
||||
if (plugin.requireAdmin && !context.Message.Author.isAdmin())
|
||||
return;
|
||||
|
||||
if (context.Channel is SocketDMChannel)
|
||||
plugin.ExecuteDM(context);
|
||||
else plugin.ExecuteServer(context);
|
||||
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
ex.WriteErrFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
@@ -8,41 +7,36 @@
|
||||
<StartupObject/>
|
||||
<SignAssembly>False</SignAssembly>
|
||||
<IsPublishable>True</IsPublishable>
|
||||
<AssemblyVersion>1.0.0.12</AssemblyVersion>
|
||||
<AssemblyVersion>1.0.2.2</AssemblyVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebugType>none</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType>none</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Data\**"/>
|
||||
<Compile Remove="obj\**"/>
|
||||
<Compile Remove="Output\**"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Remove="Data\**"/>
|
||||
<EmbeddedResource Remove="obj\**"/>
|
||||
<EmbeddedResource Remove="Output\**"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Data\**"/>
|
||||
<None Remove="obj\**"/>
|
||||
<None Remove="Output\**"/>
|
||||
<None Remove="builder.bat" />
|
||||
<None Remove="builder.sh" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Discord.Net" Version="3.7.2" />
|
||||
<PackageReference Include="Discord.Net" Version="3.11.0"/>
|
||||
<PackageReference Include="Sodium.Core" Version="1.3.3"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PluginManager\PluginManager.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
28
DiscordBot/Entry.cs
Normal file
28
DiscordBot/Entry.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace DiscordBot;
|
||||
|
||||
public class Entry
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var currentDomain = AppDomain.CurrentDomain;
|
||||
currentDomain.AssemblyResolve += LoadFromSameFolder;
|
||||
|
||||
static Assembly LoadFromSameFolder(object sender, ResolveEventArgs args)
|
||||
{
|
||||
var folderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
|
||||
"./Libraries"
|
||||
);
|
||||
var assemblyPath = Path.Combine(folderPath, new AssemblyName(args.Name).Name + ".dll");
|
||||
if (!File.Exists(assemblyPath)) return null;
|
||||
var assembly = Assembly.LoadFrom(assemblyPath);
|
||||
|
||||
return assembly;
|
||||
}
|
||||
|
||||
Program.Startup(args);
|
||||
}
|
||||
}
|
||||
64
DiscordBot/Installer.cs
Normal file
64
DiscordBot/Installer.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using PluginManager;
|
||||
using PluginManager.Online;
|
||||
|
||||
namespace DiscordBot;
|
||||
|
||||
public static class Installer
|
||||
{
|
||||
public static void 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"))
|
||||
{
|
||||
Console.WriteLine("Please enter the bot token :");
|
||||
var token = Console.ReadLine();
|
||||
Config.AppSettings.Add("token", token);
|
||||
}
|
||||
|
||||
if (!Config.AppSettings.ContainsKey("prefix"))
|
||||
{
|
||||
Console.WriteLine("Please enter the bot prefix :");
|
||||
var prefix = Console.ReadLine();
|
||||
Config.AppSettings.Add("prefix", prefix);
|
||||
}
|
||||
|
||||
if (!Config.AppSettings.ContainsKey("ServerID"))
|
||||
{
|
||||
Console.WriteLine("Please enter the Server ID :");
|
||||
var serverId = Console.ReadLine();
|
||||
Config.AppSettings.Add("ServerID", serverId);
|
||||
}
|
||||
|
||||
Config.Logger.Log("Config Saved", "Installer", isInternal: true);
|
||||
|
||||
Config.AppSettings.SaveToFile();
|
||||
|
||||
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,131 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Discord;
|
||||
|
||||
using DiscordBot.Discord.Core;
|
||||
|
||||
using PluginManager;
|
||||
using PluginManager.Items;
|
||||
using PluginManager.Bot;
|
||||
using PluginManager.Online;
|
||||
using PluginManager.Online.Helpers;
|
||||
using PluginManager.Others;
|
||||
using PluginManager.Others.Actions;
|
||||
using static PluginManager.Config;
|
||||
|
||||
namespace DiscordBot;
|
||||
|
||||
public class Program
|
||||
{
|
||||
private static bool loadPluginsOnStartup;
|
||||
private static bool listPluginsAtStartup;
|
||||
private static ConsoleCommandsHandler consoleCommandsHandler;
|
||||
public static SettingsDictionary<string, string> URLs;
|
||||
public static InternalActionManager internalActionManager;
|
||||
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
[Obsolete]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Loading resources ...");
|
||||
PreLoadComponents().Wait();
|
||||
do
|
||||
{
|
||||
if (!Config.ContainsKey("ServerID"))
|
||||
public static void Startup(string[] args)
|
||||
{
|
||||
PreLoadComponents(args).Wait();
|
||||
|
||||
if (!AppSettings.ContainsKey("ServerID") || !AppSettings.ContainsKey("token") ||
|
||||
AppSettings["token"] == null ||
|
||||
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();
|
||||
|
||||
Console.WriteLine("Please enter the server ID: ");
|
||||
Console_Utilities.WriteColorText("You can find it in the Server Settings at &r\"Widget\"&c section");
|
||||
Console.WriteLine("Example: 1234567890123456789");
|
||||
|
||||
Console.WriteLine("This is not required, but is recommended. If you refuse to provide the ID, just press enter.\nThe server id is required to make easier for the bot to interact with the server.\nRemember: this bot is for one server ONLY.");
|
||||
Console.Write("User Input > ");
|
||||
ConsoleKeyInfo key = Console.ReadKey();
|
||||
if (key.Key == ConsoleKey.Enter)
|
||||
Config.AddValueToVariables("ServerID", "null", false);
|
||||
else
|
||||
{
|
||||
string SID = key.KeyChar + Console.ReadLine();
|
||||
if (SID.Length != 18)
|
||||
{
|
||||
Console.Clear();
|
||||
Console_Utilities.WriteColorText("&rYour server ID is not 18 characters long. Please try again. \n");
|
||||
|
||||
continue;
|
||||
}
|
||||
Config.AddValueToVariables("ServerID", SID, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Config.ContainsKey("token") || Config.GetValue<string>("token") == null || (Config.GetValue<string>("token")?.Length != 70 && Config.GetValue<string>("token")?.Length != 59))
|
||||
{
|
||||
Console.WriteLine("Please insert your token");
|
||||
Console.Write("Token = ");
|
||||
var token = Console.ReadLine();
|
||||
if (token?.Length == 59 || token?.Length == 70)
|
||||
Config.AddValueToVariables("token", token, true);
|
||||
else
|
||||
{
|
||||
Console.Clear();
|
||||
Console_Utilities.WriteColorText("&rThe token length is invalid !");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Config.ContainsKey("prefix") || Config.GetValue<string>("prefix") == null || Config.GetValue<string>("prefix")?.Length != 1)
|
||||
{
|
||||
Console.WriteLine("Please insert your prefix (max. 1 character long):");
|
||||
Console.WriteLine("For a prefix longer then one character, the first character will be saved and the others will be ignored.\n No spaces, numbers, '/' or '\\' allowed");
|
||||
Console.Write("Prefix = ");
|
||||
var prefix = Console.ReadLine()![0];
|
||||
|
||||
if (prefix == ' ' || char.IsDigit(prefix) || prefix == '/' || prefix == '\\')
|
||||
{
|
||||
Console.Clear();
|
||||
Console_Utilities.WriteColorText("&rThe prefix is invalid");
|
||||
continue;
|
||||
}
|
||||
Config.AddValueToVariables("prefix", prefix.ToString(), false);
|
||||
}
|
||||
|
||||
break;
|
||||
} while (true);
|
||||
|
||||
HandleInput(args).Wait();
|
||||
HandleInput(args.ToList()).Wait();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The main loop for the discord bot
|
||||
/// </summary>
|
||||
/// <param name="discordbooter">The discord booter used to start the application</param>
|
||||
private static void NoGUI(Boot discordbooter)
|
||||
private static void NoGUI()
|
||||
{
|
||||
|
||||
#if DEBUG
|
||||
Console.WriteLine();
|
||||
ConsoleCommandsHandler.ExecuteCommad("lp").Wait();
|
||||
#else
|
||||
if (loadPluginsOnStartup) consoleCommandsHandler.HandleCommand("lp");
|
||||
if (listPluginsAtStartup) consoleCommandsHandler.HandleCommand("listplugs");
|
||||
Console.WriteLine("Debug mode enabled");
|
||||
internalActionManager.Execute("plugin", "load").Wait(); // Load plugins at startup
|
||||
#endif
|
||||
Config.SaveConfig(SaveType.NORMAL).Wait();
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
||||
var cmd = Console.ReadLine();
|
||||
if (!consoleCommandsHandler.HandleCommand(cmd!
|
||||
#if DEBUG
|
||||
, false
|
||||
#endif
|
||||
var args = cmd.Split(' ');
|
||||
var command = args[0];
|
||||
args = args.Skip(1).ToArray();
|
||||
if (args.Length == 0)
|
||||
args = null;
|
||||
|
||||
) && cmd.Length > 0)
|
||||
Console.WriteLine("Failed to run command " + cmd);
|
||||
internalActionManager.Execute(command, args).Wait(); // Execute the command
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,295 +64,184 @@ public class Program
|
||||
/// Start the bot without user interface
|
||||
/// </summary>
|
||||
/// <returns>Returns the boot loader for the Discord Bot</returns>
|
||||
private static async Task<Boot> StartNoGUI()
|
||||
private static async Task<Boot> StartNoGui()
|
||||
{
|
||||
Console.Clear();
|
||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||
|
||||
List<string> startupMessageList = await ServerCom.ReadTextFromURL("https://raw.githubusercontent.com/Wizzy69/installer/discord-bot-files/StartupMessage");
|
||||
var startupMessageList =
|
||||
await ServerCom.ReadTextFromURL(URLs["StartupMessage"]);
|
||||
|
||||
foreach (var message in startupMessageList)
|
||||
Console.WriteLine(message);
|
||||
|
||||
Console.WriteLine($"Running on version: {Config.GetValue<string>("Version") ?? System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()}");
|
||||
Console.WriteLine($"Git URL: {Config.GetValue<string>("GitURL") ?? " Could not find Git URL"}");
|
||||
Console.WriteLine(
|
||||
$"Running on version: {Assembly.GetExecutingAssembly().GetName().Version}"
|
||||
);
|
||||
Console.WriteLine($"Git URL: {AppSettings["GitURL"]}");
|
||||
|
||||
Console_Utilities.WriteColorText("&rRemember to close the bot using the ShutDown command (&ysd&r) or some settings won't be saved\n");
|
||||
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 (Config.ContainsKey("LaunchMessage"))
|
||||
{
|
||||
Console_Utilities.WriteColorText(Config.GetValue<string>("LaunchMessage"));
|
||||
Config.RemoveKey("LaunchMessage");
|
||||
}
|
||||
if (AppSettings.ContainsKey("LaunchMessage"))
|
||||
Utilities.Utilities.WriteColorText(AppSettings["LaunchMessage"]);
|
||||
|
||||
Console_Utilities.WriteColorText("Please note that the bot saves a backup save file every time you are using the shudown command (&ysd&c)");
|
||||
Console.WriteLine($"============================ LOG ============================");
|
||||
|
||||
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
|
||||
{
|
||||
var token = Config.GetValue<string>("token");
|
||||
var token = "";
|
||||
#if DEBUG
|
||||
Console.WriteLine("Starting in DEBUG MODE");
|
||||
if (!Directory.Exists("./Data/BetaTest"))
|
||||
Console.WriteLine("Failed to start in debug mode because the folder ./Data/BetaTest does not exist");
|
||||
else
|
||||
{
|
||||
token = File.ReadAllText("./Data/BetaTest/token.txt");
|
||||
|
||||
//Debug mode code...
|
||||
}
|
||||
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 = Config.GetValue<string>("prefix");
|
||||
var prefix = AppSettings["prefix"];
|
||||
var discordbooter = new Boot(token, prefix);
|
||||
await discordbooter.Awake();
|
||||
return discordbooter;
|
||||
}
|
||||
catch ( Exception ex )
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
Logger.Log(ex.ToString(), "Bot", LogLevel.ERROR);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear folder
|
||||
/// </summary>
|
||||
/// <param name="d">Directory path</param>
|
||||
private static Task ClearFolder(string d)
|
||||
{
|
||||
var files = Directory.GetFiles(d);
|
||||
var fileNumb = files.Length;
|
||||
for (var i = 0; i < fileNumb; i++)
|
||||
{
|
||||
File.Delete(files[i]);
|
||||
Console.WriteLine("Deleting : " + files[i]);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle user input arguments from the startup of the application
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments</param>
|
||||
private static async Task HandleInput(string[] args)
|
||||
private static async Task HandleInput(List<string> args)
|
||||
{
|
||||
Console.WriteLine("Loading Core ...");
|
||||
|
||||
var len = args.Length;
|
||||
//Handle arguments here:
|
||||
|
||||
if (len == 3 && args[0] == "/download")
|
||||
if (args.Contains("--gui"))
|
||||
{
|
||||
var url = args[1];
|
||||
var location = args[2];
|
||||
|
||||
await ServerCom.DownloadFileAsync(url, location);
|
||||
|
||||
// GUI not implemented yet
|
||||
Console.WriteLine("GUI not implemented yet");
|
||||
return;
|
||||
}
|
||||
|
||||
if (len > 0 && (args.Contains("--cmd") || args.Contains("--args") || args.Contains("--nomessage")))
|
||||
{
|
||||
if (args.Contains("lp") || args.Contains("loadplugins"))
|
||||
loadPluginsOnStartup = true;
|
||||
if (args.Contains("listplugs"))
|
||||
listPluginsAtStartup = true;
|
||||
// Starting bot after all arguments are handled
|
||||
|
||||
len = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var b = await StartNoGUI();
|
||||
consoleCommandsHandler = new ConsoleCommandsHandler(b.client);
|
||||
|
||||
if (len > 0 && args[0] == "/remplug")
|
||||
{
|
||||
|
||||
string plugName = Functions.MergeStrings(args, 1);
|
||||
Console.WriteLine("Starting to remove " + plugName);
|
||||
await ConsoleCommandsHandler.ExecuteCommad("remplug " + plugName);
|
||||
loadPluginsOnStartup = true;
|
||||
len = 0;
|
||||
}
|
||||
|
||||
if (len > 0 && args[0] == "/updateplug")
|
||||
{
|
||||
string plugName = args.MergeStrings(1);
|
||||
Console.WriteLine("Updating " + plugName);
|
||||
await ConsoleCommandsHandler.ExecuteCommad("dwplug" + plugName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (len == 0 || (args[0] != "--exec" && args[0] != "--execute"))
|
||||
{
|
||||
|
||||
Thread mainThread = new Thread(() =>
|
||||
{
|
||||
var b = await StartNoGui();
|
||||
try
|
||||
{
|
||||
NoGUI(b);
|
||||
internalActionManager = new InternalActionManager("./Data/Actions", "*.dll");
|
||||
await internalActionManager.Initialize();
|
||||
|
||||
NoGUI();
|
||||
}
|
||||
catch ( IOException ex )
|
||||
{
|
||||
if (ex.Message == "No process is on the other end of the pipe." || (uint)ex.HResult == 0x800700E9)
|
||||
{
|
||||
if (!Config.ContainsKey("LaunchMessage"))
|
||||
Config.AddValueToVariables("LaunchMessage", "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 !", false);
|
||||
Functions.WriteErrFile(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
});
|
||||
mainThread.Start();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||
Console.WriteLine("Execute command interface noGUI\n\n");
|
||||
Console.WriteLine(
|
||||
"\tCommand name\t\t\t\tDescription\n" +
|
||||
"-- help | -help\t\t ------ \tDisplay the help message\n" +
|
||||
"--reset-full\t\t ------ \tReset all files (clear files)\n" +
|
||||
"--reset-logs\t\t ------ \tClear up the output folder\n" +
|
||||
"--start\t\t ------ \tStart the bot\n" +
|
||||
"exit\t\t\t ------ \tClose the application"
|
||||
if (AppSettings.ContainsKey("LaunchMessage"))
|
||||
AppSettings.Add("LaunchMessage",
|
||||
"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 !"
|
||||
);
|
||||
while (true)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.Write("> ");
|
||||
var message = Console.ReadLine().Split(' ');
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (message[0])
|
||||
private static async Task PreLoadComponents(string[] args)
|
||||
{
|
||||
case "--help":
|
||||
case "-help":
|
||||
await Initialize();
|
||||
|
||||
Logger.LogEvent += (message, type, isInternal) =>
|
||||
{
|
||||
if (type == LogLevel.INFO)
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
else if (type == LogLevel.WARNING)
|
||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||
Console.WriteLine("\tCommand name\t\t\t\tDescription\n" + "-- help | -help\t\t ------ \tDisplay the help message\n" + "--reset-full\t\t ------ \tReset all files (clear files)\n" + "--reset-settings\t ------ \tReset only bot settings\n" + "--reset-logs\t\t ------ \tClear up the output folder\n" + "--start\t\t ------ \tStart the bot\n" + "exit\t\t\t ------ \tClose the application");
|
||||
break;
|
||||
case "--reset-full":
|
||||
await ClearFolder("./Data/Resources/");
|
||||
await ClearFolder("./Output/Logs/");
|
||||
await ClearFolder("./Output/Errors");
|
||||
await ClearFolder("./Data/Languages/");
|
||||
await ClearFolder("./Data/Plugins/Commands");
|
||||
await ClearFolder("./Data/Plugins/Events");
|
||||
Console.WriteLine("Successfully cleared all folders");
|
||||
break;
|
||||
case "--reset-logs":
|
||||
await ClearFolder("./Output/Logs");
|
||||
await ClearFolder("./Output/Errors");
|
||||
Console.WriteLine("Successfully clear logs folder");
|
||||
break;
|
||||
case "--exit":
|
||||
case "exit":
|
||||
Environment.Exit(0);
|
||||
break;
|
||||
else if (type == LogLevel.ERROR)
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
else if (type == LogLevel.CRITICAL)
|
||||
Console.ForegroundColor = ConsoleColor.DarkRed;
|
||||
|
||||
default:
|
||||
Console.WriteLine("Failed to execute command " + message[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Console.WriteLine($"[{type.ToString()}] {message}");
|
||||
Console.ResetColor();
|
||||
};
|
||||
|
||||
private static async Task PreLoadComponents()
|
||||
{
|
||||
Console_Utilities.ProgressBar main = new Console_Utilities.ProgressBar(ProgressBarType.NO_END);
|
||||
main.Start();
|
||||
Directory.CreateDirectory("./Data/Resources");
|
||||
Directory.CreateDirectory("./Data/Plugins/Commands");
|
||||
Directory.CreateDirectory("./Data/Plugins/Events");
|
||||
Directory.CreateDirectory("./Data/PAKS");
|
||||
await Config.LoadConfig();
|
||||
if (Config.ContainsKey("DeleteLogsAtStartup"))
|
||||
if (Config.GetValue<bool>("DeleteLogsAtStartup"))
|
||||
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);
|
||||
List<string> OnlineDefaultKeys = await ServerCom.ReadTextFromURL("https://raw.githubusercontent.com/Wizzy69/installer/discord-bot-files/SetupKeys");
|
||||
|
||||
Config.PluginConfig.Load();
|
||||
var OnlineDefaultKeys = await ServerCom.ReadTextFromURL(URLs["SetupKeys"]);
|
||||
|
||||
if (!Config.ContainsKey("Version"))
|
||||
Config.AddValueToVariables("Version", Assembly.GetExecutingAssembly().GetName().Version.ToString(), false);
|
||||
else
|
||||
Config.SetValue("Version", Assembly.GetExecutingAssembly().GetName().Version.ToString());
|
||||
|
||||
AppSettings["Version"] = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
|
||||
foreach (var key in OnlineDefaultKeys)
|
||||
{
|
||||
if (key.Length <= 3 || !key.Contains(' ')) continue;
|
||||
string[] s = key.Split(' ');
|
||||
var s = key.Split(' ');
|
||||
try
|
||||
{
|
||||
if (Config.ContainsKey(s[0])) Config.SetValue(s[0], s[1]);
|
||||
else Config.GetAndAddValueToVariable(s[0], s[1], s[2].Equals("true", StringComparison.CurrentCultureIgnoreCase));
|
||||
AppSettings[s[0]] = s[1];
|
||||
}
|
||||
catch ( Exception ex )
|
||||
{
|
||||
Functions.WriteErrFile(ex.Message);
|
||||
Logger.Log(ex.ToString(), "Bot", LogLevel.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
List<string> onlineSettingsList = await ServerCom.ReadTextFromURL("https://raw.githubusercontent.com/Wizzy69/installer/discord-bot-files/OnlineData");
|
||||
main.Stop();
|
||||
var onlineSettingsList = await ServerCom.ReadTextFromURL(URLs["Versions"]);
|
||||
foreach (var key in onlineSettingsList)
|
||||
{
|
||||
if (key.Length <= 3 || !key.Contains(' ')) continue;
|
||||
|
||||
string[] s = key.Split(' ');
|
||||
var s = key.Split(' ');
|
||||
switch ( s[0] )
|
||||
{
|
||||
case "CurrentVersion":
|
||||
string newVersion = s[1];
|
||||
if (!newVersion.Equals(Config.GetValue<string>("Version")))
|
||||
{
|
||||
if (Functions.GetOperatingSystem() == PluginManager.Others.OperatingSystem.WINDOWS)
|
||||
var currentVersion = AppSettings["Version"];
|
||||
var newVersion = s[1];
|
||||
if (new VersionString(newVersion) != new VersionString(newVersion))
|
||||
{
|
||||
Console.WriteLine("A new updated was found. Check the changelog for more information.");
|
||||
var changeLog = await ServerCom.ReadTextFromURL(URLs["Changelog"]);
|
||||
foreach (var item in changeLog)
|
||||
Utilities.Utilities.WriteColorText(item);
|
||||
Console.WriteLine("Current version: " + currentVersion);
|
||||
Console.WriteLine("Latest version: " + newVersion);
|
||||
|
||||
string url = $"https://github.com/Wizzy69/SethDiscordBot/releases/download/v{newVersion}/net6.0.zip";
|
||||
//string url2 = $"https://github.com/Wizzy69/SethDiscordBot/releases/download/v{newVersion}-preview/net6.0.zip";
|
||||
Process.Start("./Updater/Updater.exe", $"/update {url} ./DiscordBot.exe ./");
|
||||
}
|
||||
else
|
||||
{
|
||||
string url = $"https://github.com/Wizzy69/SethDiscordBot/releases/download/v{newVersion}/net6.0_linux.zip";
|
||||
Process.Start("./Updater/Updater", $"/update {url} ./DiscordBot ./");
|
||||
}
|
||||
Environment.Exit(0);
|
||||
Console.WriteLine("Download from here: https://github.com/andreitdr/SethDiscordBot/releases");
|
||||
|
||||
Console.WriteLine("Press any key to continue ...");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
break;
|
||||
case "UpdaterVersion":
|
||||
string updaternewversion = s[1];
|
||||
if (Config.UpdaterVersion != updaternewversion)
|
||||
{
|
||||
Console.Clear();
|
||||
Console.WriteLine("Installing updater ...\nDo NOT close the bot during update !");
|
||||
Console_Utilities.ProgressBar bar = new Console_Utilities.ProgressBar(ProgressBarType.NO_END);
|
||||
bar.Start();
|
||||
await ServerCom.DownloadFileNoProgressAsync("https://github.com/Wizzy69/installer/releases/download/release-1-discordbot/Updater.zip", "./Updater.zip");
|
||||
await Functions.ExtractArchive("./Updater.zip", "./", null, UnzipProgressType.PercentageFromTotalSize);
|
||||
Config.UpdaterVersion = updaternewversion;
|
||||
File.Delete("Updater.zip");
|
||||
await Config.SaveConfig(SaveType.NORMAL);
|
||||
bar.Stop();
|
||||
Console.Clear();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Console_Utilities.Initialize();
|
||||
await Config.SaveConfig(SaveType.NORMAL);
|
||||
Console.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
243
DiscordBot/Utilities/Console Utilities.cs
Normal file
243
DiscordBot/Utilities/Console Utilities.cs
Normal file
@@ -0,0 +1,243 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace DiscordBot.Utilities;
|
||||
|
||||
public static class Utilities
|
||||
{
|
||||
private static readonly Dictionary<char, ConsoleColor> Colors = new()
|
||||
{
|
||||
{ 'g', ConsoleColor.Green },
|
||||
{ 'b', ConsoleColor.Blue },
|
||||
{ 'r', ConsoleColor.Red },
|
||||
{ 'm', ConsoleColor.Magenta },
|
||||
{ 'y', ConsoleColor.Yellow }
|
||||
};
|
||||
|
||||
private static readonly char ColorPrefix = '&';
|
||||
|
||||
|
||||
private static bool CanAproximateTo(this float f, float y)
|
||||
{
|
||||
return MathF.Abs(f - y) < 0.000001;
|
||||
}
|
||||
|
||||
|
||||
/// <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()
|
||||
{
|
||||
isRunning = false;
|
||||
Console.CursorVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
8
DiscordBot/Utilities/Enums.cs
Normal file
8
DiscordBot/Utilities/Enums.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace DiscordBot.Utilities;
|
||||
|
||||
public enum TableFormat
|
||||
{
|
||||
CENTER_EACH_COLUMN_BASED,
|
||||
CENTER_OVERALL_LENGTH,
|
||||
DEFAULT
|
||||
}
|
||||
35
DiscordBot/builder.bat
Normal file
35
DiscordBot/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!"
|
||||
36
DiscordBot/builder.sh
Normal file
36
DiscordBot/builder.sh
Normal file
@@ -0,0 +1,36 @@
|
||||
# All files in this directory will be copied to the root of the container
|
||||
|
||||
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
|
||||
|
||||
#One file per platform
|
||||
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!"
|
||||
@@ -1,18 +1,13 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using PluginManager.Others;
|
||||
|
||||
using PluginManager;
|
||||
namespace PluginManager.Bot;
|
||||
|
||||
using static PluginManager.Others.Functions;
|
||||
|
||||
namespace DiscordBot.Discord.Core;
|
||||
|
||||
internal class Boot
|
||||
public class Boot
|
||||
{
|
||||
/// <summary>
|
||||
/// The bot prefix
|
||||
@@ -54,16 +49,28 @@ internal class Boot
|
||||
/// <summary>
|
||||
/// Checks if the bot is ready
|
||||
/// </summary>
|
||||
/// <value> true if the bot is ready, othwerwise false </value>
|
||||
/// <value> true if the bot is ready, otherwise false </value>
|
||||
public bool isReady { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The start method for the bot. This method is used to load the bot
|
||||
/// </summary>
|
||||
/// <param name="config">
|
||||
/// The discord socket config. If null then the default one will be applied (AlwaysDownloadUsers=true,
|
||||
/// UseInteractionSnowflakeDate=false, GatewayIntents=GatewayIntents.All)
|
||||
/// </param>
|
||||
/// <returns>Task</returns>
|
||||
public async Task Awake()
|
||||
public async Task Awake(DiscordSocketConfig? config = null)
|
||||
{
|
||||
DiscordSocketConfig config = new DiscordSocketConfig { AlwaysDownloadUsers = true };
|
||||
if (config is null)
|
||||
config = new DiscordSocketConfig
|
||||
{
|
||||
AlwaysDownloadUsers = true,
|
||||
|
||||
//Disable system clock checkup (for responses at slash commands)
|
||||
UseInteractionSnowflakeDate = false,
|
||||
GatewayIntents = GatewayIntents.All
|
||||
};
|
||||
|
||||
client = new DiscordSocketClient(config);
|
||||
service = new CommandService();
|
||||
@@ -71,12 +78,18 @@ internal class Boot
|
||||
CommonTasks();
|
||||
|
||||
await client.LoginAsync(TokenType.Bot, botToken);
|
||||
|
||||
await client.StartAsync();
|
||||
|
||||
commandServiceHandler = new CommandHandler(client, service, botPrefix);
|
||||
|
||||
await commandServiceHandler.InstallCommandsAsync();
|
||||
|
||||
|
||||
await Task.Delay(2000);
|
||||
|
||||
Config._DiscordBotClient = this;
|
||||
|
||||
while (!isReady) ;
|
||||
}
|
||||
|
||||
@@ -87,29 +100,37 @@ internal class Boot
|
||||
client.Log += Log;
|
||||
client.LoggedIn += LoggedIn;
|
||||
client.Ready += Ready;
|
||||
client.Disconnected += Client_Disconnected;
|
||||
}
|
||||
|
||||
private Task Client_LoggedOut()
|
||||
private async Task Client_Disconnected(Exception arg)
|
||||
{
|
||||
WriteLogFile("Successfully Logged Out");
|
||||
Log(new LogMessage(LogSeverity.Info, "Boot", "Successfully logged out from discord !"));
|
||||
return Task.CompletedTask;
|
||||
if (arg.Message.Contains("401"))
|
||||
{
|
||||
Config.AppSettings.Remove("token");
|
||||
Config.Logger.Log("The token is invalid. Please restart the bot and enter a valid token.", this,
|
||||
LogLevel.ERROR);
|
||||
await Config.AppSettings.SaveToFile();
|
||||
await Task.Delay(4000);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Client_LoggedOut()
|
||||
{
|
||||
Config.Logger.Log("Successfully Logged Out", this);
|
||||
await Log(new LogMessage(LogSeverity.Info, "Boot", "Successfully logged out from discord !"));
|
||||
}
|
||||
|
||||
private Task Ready()
|
||||
{
|
||||
Console.Title = "ONLINE";
|
||||
isReady = true;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task LoggedIn()
|
||||
{
|
||||
Console.Title = "CONNECTED";
|
||||
WriteLogFile("The bot has been logged in at " + DateTime.Now.ToShortDateString() + " (" +
|
||||
DateTime.Now.ToShortTimeString() + ")"
|
||||
);
|
||||
Config.Logger.Log("Successfully Logged In", this);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -119,21 +140,13 @@ internal class Boot
|
||||
{
|
||||
case LogSeverity.Error:
|
||||
case LogSeverity.Critical:
|
||||
WriteErrFile(message.Message);
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine("[ERROR] " + message.Message);
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Config.Logger.Log(message.Message, this, LogLevel.ERROR);
|
||||
|
||||
break;
|
||||
|
||||
case LogSeverity.Info:
|
||||
case LogSeverity.Debug:
|
||||
WriteLogFile(message.Message);
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||
Console.WriteLine("[INFO] " + message.Message);
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Config.Logger.Log(message.Message, this);
|
||||
|
||||
|
||||
break;
|
||||
@@ -141,5 +154,4 @@ internal class Boot
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
}
|
||||
152
PluginManager/Bot/CommandHandler.cs
Normal file
152
PluginManager/Bot/CommandHandler.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using PluginManager.Interfaces;
|
||||
using PluginManager.Loaders;
|
||||
using PluginManager.Others;
|
||||
using PluginManager.Others.Permissions;
|
||||
|
||||
namespace PluginManager.Bot;
|
||||
|
||||
internal class CommandHandler
|
||||
{
|
||||
private readonly string botPrefix;
|
||||
private readonly DiscordSocketClient client;
|
||||
private readonly CommandService commandService;
|
||||
|
||||
/// <summary>
|
||||
/// Command handler constructor
|
||||
/// </summary>
|
||||
/// <param name="client">The discord bot client</param>
|
||||
/// <param name="commandService">The discord bot command service</param>
|
||||
/// <param name="botPrefix">The prefix to watch for</param>
|
||||
public CommandHandler(DiscordSocketClient client, CommandService commandService, string botPrefix)
|
||||
{
|
||||
this.client = client;
|
||||
this.commandService = commandService;
|
||||
this.botPrefix = botPrefix;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The method to initialize all commands
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task InstallCommandsAsync()
|
||||
{
|
||||
client.MessageReceived += MessageHandler;
|
||||
client.SlashCommandExecuted += Client_SlashCommandExecuted;
|
||||
await commandService.AddModulesAsync(Assembly.GetEntryAssembly(), null);
|
||||
}
|
||||
|
||||
private Task Client_SlashCommandExecuted(SocketSlashCommand arg)
|
||||
{
|
||||
try
|
||||
{
|
||||
var plugin = PluginLoader.SlashCommands!.FirstOrDefault(p => p.Name == arg.Data.Name);
|
||||
|
||||
if (plugin is null)
|
||||
throw new Exception("Failed to run command. !");
|
||||
|
||||
|
||||
if (arg.Channel is SocketDMChannel)
|
||||
plugin.ExecuteDM(arg);
|
||||
else plugin.ExecuteServer(arg);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Config.Logger.Log(ex.Message, "CommandHandler", LogLevel.ERROR);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The message handler for the bot
|
||||
/// </summary>
|
||||
/// <param name="Message">The message got from the user in discord chat</param>
|
||||
/// <returns></returns>
|
||||
private async Task MessageHandler(SocketMessage Message)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Message.Author.IsBot)
|
||||
return;
|
||||
|
||||
if (Message as SocketUserMessage == null)
|
||||
return;
|
||||
|
||||
var message = Message as SocketUserMessage;
|
||||
|
||||
if (message is null)
|
||||
return;
|
||||
|
||||
var argPos = 0;
|
||||
|
||||
if (!message.Content.StartsWith(botPrefix) && !message.HasMentionPrefix(client.CurrentUser, ref argPos))
|
||||
return;
|
||||
|
||||
var context = new SocketCommandContext(client, message);
|
||||
|
||||
await commandService.ExecuteAsync(context, argPos, null);
|
||||
|
||||
DBCommand? plugin;
|
||||
var cleanMessage = "";
|
||||
|
||||
if (message.HasMentionPrefix(client.CurrentUser, ref argPos))
|
||||
{
|
||||
var mentionPrefix = "<@" + client.CurrentUser.Id + ">";
|
||||
|
||||
plugin = PluginLoader.Commands!
|
||||
.FirstOrDefault(plug => plug.Command ==
|
||||
message.Content.Substring(mentionPrefix.Length + 1)
|
||||
.Split(' ')[0] ||
|
||||
(
|
||||
plug.Aliases is not null &&
|
||||
plug.Aliases.Contains(message.CleanContent
|
||||
.Substring(mentionPrefix.Length + 1)
|
||||
.Split(' ')[0])
|
||||
));
|
||||
|
||||
cleanMessage = message.Content.Substring(mentionPrefix.Length + 1);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
plugin = PluginLoader.Commands!
|
||||
.FirstOrDefault(p => p.Command ==
|
||||
message.Content.Split(' ')[0].Substring(botPrefix.Length) ||
|
||||
(p.Aliases is not null &&
|
||||
p.Aliases.Contains(
|
||||
message.Content.Split(' ')[0]
|
||||
.Substring(botPrefix.Length))));
|
||||
cleanMessage = message.Content.Substring(botPrefix.Length);
|
||||
}
|
||||
|
||||
if (plugin is null)
|
||||
throw new Exception("Failed to run command ! " + message.CleanContent + " (user: " +
|
||||
context.Message.Author.Username + " - " + context.Message.Author.Id + ")");
|
||||
|
||||
if (plugin.requireAdmin && !context.Message.Author.isAdmin())
|
||||
return;
|
||||
|
||||
var split = cleanMessage.Split(' ');
|
||||
|
||||
string[]? argsClean = null;
|
||||
if (split.Length > 1)
|
||||
argsClean = string.Join(' ', split, 1, split.Length - 1).Split(' ');
|
||||
|
||||
DBCommandExecutingArguments cmd = new(context, cleanMessage, split[0], argsClean);
|
||||
|
||||
if (context.Channel is SocketDMChannel)
|
||||
plugin.ExecuteDM(cmd);
|
||||
else plugin.ExecuteServer(cmd);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Config.Logger.Log(ex.Message, this, LogLevel.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,239 +1,44 @@
|
||||
using System;
|
||||
using PluginManager.Others;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading;
|
||||
using PluginManager.Bot;
|
||||
using PluginManager.Others;
|
||||
using PluginManager.Others.Logger;
|
||||
|
||||
namespace PluginManager
|
||||
namespace PluginManager;
|
||||
|
||||
public class Config
|
||||
{
|
||||
internal class AppConfig
|
||||
private static bool IsLoaded;
|
||||
public static DBLogger Logger;
|
||||
public static SettingsDictionary<string, string>? AppSettings;
|
||||
|
||||
internal static Boot? _DiscordBotClient;
|
||||
|
||||
public static Boot? DiscordBot => _DiscordBotClient;
|
||||
|
||||
public static async Task Initialize()
|
||||
{
|
||||
public string UpdaterVersion { get; set; }
|
||||
public Dictionary<string, object>? ApplicationVariables { get; init; }
|
||||
public List<string>? ProtectedKeyWords { get; init; }
|
||||
public Dictionary<string, string>? PluginVersions { get; init; }
|
||||
}
|
||||
if (IsLoaded) return;
|
||||
|
||||
public static class Config
|
||||
{
|
||||
public static class PluginConfig
|
||||
{
|
||||
public static readonly List<Tuple<string, PluginType>> InstalledPlugins = new();
|
||||
Directory.CreateDirectory("./Data/Resources");
|
||||
Directory.CreateDirectory("./Data/Plugins");
|
||||
Directory.CreateDirectory("./Data/PAKS");
|
||||
Directory.CreateDirectory("./Data/Logs/Logs");
|
||||
Directory.CreateDirectory("./Data/Logs/Errors");
|
||||
|
||||
public static void Load()
|
||||
{
|
||||
new Thread(LoadCommands).Start();
|
||||
new Thread(LoadEvents).Start();
|
||||
}
|
||||
AppSettings = new SettingsDictionary<string, string>("./Data/Resources/config.json");
|
||||
|
||||
private static void LoadCommands()
|
||||
{
|
||||
string cmd_path = "./Data/Plugins/Commands/";
|
||||
string[] files = Directory.GetFiles(cmd_path, $"*.{Loaders.PluginLoader.pluginCMDExtension}", SearchOption.AllDirectories);
|
||||
foreach (var file in files)
|
||||
if (!file.Contains("PluginManager", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
string PluginName = new FileInfo(file).Name;
|
||||
string name = PluginName.Substring(0, PluginName.Length - 1 - PluginManager.Loaders.PluginLoader.pluginCMDExtension.Length);
|
||||
InstalledPlugins.Add(new(name, PluginType.Command));
|
||||
}
|
||||
}
|
||||
AppSettings["LogFolder"] = "./Data/Logs/Logs";
|
||||
AppSettings["ErrorFolder"] = "./Data/Logs/Errors";
|
||||
|
||||
private static void LoadEvents()
|
||||
{
|
||||
string eve_path = "./Data/Plugins/Events/";
|
||||
string[] files = Directory.GetFiles(eve_path, $"*.{Loaders.PluginLoader.pluginEVEExtension}", SearchOption.AllDirectories);
|
||||
foreach (var file in files)
|
||||
if (!file.Contains("PluginManager", StringComparison.InvariantCultureIgnoreCase))
|
||||
if (!file.Contains("PluginManager", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
string PluginName = new FileInfo(file).Name;
|
||||
string name = PluginName.Substring(0, PluginName.Length - 1 - PluginManager.Loaders.PluginLoader.pluginEVEExtension.Length);
|
||||
InstalledPlugins.Add(new(name, PluginType.Event));
|
||||
}
|
||||
}
|
||||
Logger = new DBLogger();
|
||||
|
||||
public static bool Contains(string pluginName)
|
||||
{
|
||||
foreach (var tuple in InstalledPlugins)
|
||||
if (tuple.Item1 == pluginName)
|
||||
return true;
|
||||
ArchiveManager.Initialize();
|
||||
|
||||
return false;
|
||||
}
|
||||
IsLoaded = true;
|
||||
|
||||
public static PluginType GetPluginType(string pluginName)
|
||||
{
|
||||
foreach (var tuple in InstalledPlugins)
|
||||
if (tuple.Item1 == pluginName)
|
||||
return tuple.Item2;
|
||||
|
||||
|
||||
return PluginType.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
private static AppConfig? appConfig { get; set; }
|
||||
|
||||
public static string UpdaterVersion { get => appConfig.UpdaterVersion; set => appConfig.UpdaterVersion = value; }
|
||||
|
||||
public static string GetPluginVersion(string pluginName) => appConfig!.PluginVersions![pluginName];
|
||||
public static void SetPluginVersion(string pluginName, string newVersion)
|
||||
{
|
||||
if (appConfig!.PluginVersions!.ContainsKey(pluginName))
|
||||
appConfig.PluginVersions[pluginName] = newVersion;
|
||||
else appConfig.PluginVersions.Add(pluginName, newVersion);
|
||||
|
||||
// SaveConfig();
|
||||
}
|
||||
|
||||
public static void RemovePluginVersion(string pluginName) => appConfig!.PluginVersions!.Remove(pluginName);
|
||||
public static bool PluginVersionsContainsKey(string pluginName) => appConfig!.PluginVersions!.ContainsKey(pluginName);
|
||||
|
||||
public static void AddValueToVariables<T>(string key, T value, bool isProtected)
|
||||
{
|
||||
if (value == null)
|
||||
throw new Exception("The value cannot be null");
|
||||
if (appConfig!.ApplicationVariables!.ContainsKey(key))
|
||||
throw new Exception($"The key ({key}) already exists in the variables. Value {GetValue<T>(key)}");
|
||||
|
||||
appConfig.ApplicationVariables.Add(key, value);
|
||||
if (isProtected && key != "Version")
|
||||
appConfig.ProtectedKeyWords!.Add(key);
|
||||
|
||||
SaveConfig(SaveType.NORMAL);
|
||||
}
|
||||
|
||||
public static Type GetVariableType(string value)
|
||||
{
|
||||
if (int.TryParse(value, out var intValue))
|
||||
return typeof(int);
|
||||
if (bool.TryParse(value, out var boolValue))
|
||||
return typeof(bool);
|
||||
if (float.TryParse(value, out var floatValue))
|
||||
return typeof(float);
|
||||
if (double.TryParse(value, out var doubleValue))
|
||||
return typeof(double);
|
||||
if (uint.TryParse(value, out var uintValue))
|
||||
return typeof(uint);
|
||||
if (long.TryParse(value, out var longValue))
|
||||
return typeof(long);
|
||||
if (byte.TryParse(value, out var byteValue))
|
||||
return typeof(byte);
|
||||
return typeof(string);
|
||||
}
|
||||
|
||||
public static void GetAndAddValueToVariable(string key, string value, bool isReadOnly)
|
||||
{
|
||||
if (Config.ContainsKey(key))
|
||||
return;
|
||||
if (int.TryParse(value, out var intValue))
|
||||
Config.AddValueToVariables(key, intValue, isReadOnly);
|
||||
else if (bool.TryParse(value, out var boolValue))
|
||||
Config.AddValueToVariables(key, boolValue, isReadOnly);
|
||||
else if (float.TryParse(value, out var floatValue))
|
||||
Config.AddValueToVariables(key, floatValue, isReadOnly);
|
||||
else if (double.TryParse(value, out var doubleValue))
|
||||
Config.AddValueToVariables(key, doubleValue, isReadOnly);
|
||||
else if (uint.TryParse(value, out var uintValue))
|
||||
Config.AddValueToVariables(key, uintValue, isReadOnly);
|
||||
else if (long.TryParse(value, out var longValue))
|
||||
Config.AddValueToVariables(key, longValue, isReadOnly);
|
||||
else if (byte.TryParse(value, out var byteValue))
|
||||
Config.AddValueToVariables(key, byteValue, isReadOnly);
|
||||
else
|
||||
Config.AddValueToVariables(key, value, isReadOnly);
|
||||
}
|
||||
|
||||
public static T? GetValue<T>(string key)
|
||||
{
|
||||
if (!appConfig!.ApplicationVariables!.ContainsKey(key)) return default;
|
||||
try
|
||||
{
|
||||
JsonElement element = (JsonElement)appConfig.ApplicationVariables[key];
|
||||
return element.Deserialize<T>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return (T)appConfig.ApplicationVariables[key];
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetValue<T>(string key, T value)
|
||||
{
|
||||
if (value == null)
|
||||
throw new Exception("Value is null");
|
||||
if (!appConfig!.ApplicationVariables!.ContainsKey(key))
|
||||
throw new Exception("Key does not exist in the config file");
|
||||
if (appConfig.ProtectedKeyWords!.Contains(key))
|
||||
throw new Exception("Key is protected");
|
||||
|
||||
appConfig.ApplicationVariables[key] = JsonSerializer.SerializeToElement(value);
|
||||
SaveConfig(SaveType.NORMAL);
|
||||
}
|
||||
|
||||
public static void RemoveKey(string key)
|
||||
{
|
||||
if (key == "Version" || key == "token" || key == "prefix")
|
||||
throw new Exception("Key is protected");
|
||||
appConfig!.ApplicationVariables!.Remove(key);
|
||||
appConfig.ProtectedKeyWords!.Remove(key);
|
||||
SaveConfig(SaveType.NORMAL);
|
||||
}
|
||||
|
||||
public static bool IsReadOnly(string key)
|
||||
{
|
||||
return appConfig.ProtectedKeyWords.Contains(key);
|
||||
}
|
||||
|
||||
public static async Task SaveConfig(SaveType type)
|
||||
{
|
||||
if (type == SaveType.NORMAL)
|
||||
{
|
||||
string path = Functions.dataFolder + "config.json";
|
||||
await Functions.SaveToJsonFile<AppConfig>(path, appConfig!);
|
||||
return;
|
||||
}
|
||||
if (type == SaveType.BACKUP)
|
||||
{
|
||||
string path = Functions.dataFolder + "config.json.bak";
|
||||
await Functions.SaveToJsonFile<AppConfig>(path, appConfig!);
|
||||
return;
|
||||
Logger.Log("Config initialized", LogLevel.INFO);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static async Task LoadConfig()
|
||||
{
|
||||
string path = Functions.dataFolder + "config.json";
|
||||
if (File.Exists(path))
|
||||
{
|
||||
try
|
||||
{
|
||||
appConfig = await Functions.ConvertFromJson<AppConfig>(path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
File.Delete(path);
|
||||
Console.WriteLine("An error occured while loading the settings. Importing from backup file...");
|
||||
path = Functions.dataFolder + "config.json.bak";
|
||||
appConfig = await Functions.ConvertFromJson<AppConfig>(path);
|
||||
Functions.WriteErrFile(ex.Message);
|
||||
}
|
||||
|
||||
|
||||
Functions.WriteLogFile($"Loaded {appConfig.ApplicationVariables!.Keys.Count} application variables.\nLoaded {appConfig.ProtectedKeyWords!.Count} readonly variables.");
|
||||
return;
|
||||
}
|
||||
appConfig = new() { ApplicationVariables = new Dictionary<string, object>(), ProtectedKeyWords = new List<string>(), PluginVersions = new Dictionary<string, string>(), UpdaterVersion = "-1" };
|
||||
}
|
||||
|
||||
public static bool ContainsValue<T>(T value) => appConfig!.ApplicationVariables!.ContainsValue(value!);
|
||||
public static bool ContainsKey(string key) => appConfig!.ApplicationVariables!.ContainsKey(key);
|
||||
|
||||
public static IDictionary<string, object> GetAllVariables() => appConfig.ApplicationVariables;
|
||||
}
|
||||
}
|
||||
|
||||
482
PluginManager/Database/SqlDatabase.cs
Normal file
482
PluginManager/Database/SqlDatabase.cs
Normal file
@@ -0,0 +1,482 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SQLite;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PluginManager.Database;
|
||||
|
||||
public class SqlDatabase
|
||||
{
|
||||
private readonly SQLiteConnection Connection;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a SQL connection by specifing its private path
|
||||
/// </summary>
|
||||
/// <param name="fileName">The path to the database (it is starting from ./Data/Resources/)</param>
|
||||
public SqlDatabase(string fileName)
|
||||
{
|
||||
if (!fileName.StartsWith("./Data/Resources/"))
|
||||
fileName = Path.Combine("./Data/Resources", fileName);
|
||||
if (!File.Exists(fileName))
|
||||
SQLiteConnection.CreateFile(fileName);
|
||||
var connectionString = $"URI=file:{fileName}";
|
||||
Connection = new SQLiteConnection(connectionString);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Open the SQL Connection. To close use the Stop() method
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task Open()
|
||||
{
|
||||
await Connection.OpenAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Insert into a specified table some values
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="tableName">The table name</param>
|
||||
/// <param name="values">The values to be inserted (in the correct order and number)</param>
|
||||
/// <returns></returns>
|
||||
public async Task InsertAsync(string tableName, params string[] values)
|
||||
{
|
||||
var query = $"INSERT INTO {tableName} VALUES (";
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
query += $"'{values[i]}'";
|
||||
if (i != values.Length - 1)
|
||||
query += ", ";
|
||||
}
|
||||
|
||||
query += ")";
|
||||
|
||||
var command = new SQLiteCommand(query, Connection);
|
||||
await command.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Insert into a specified table some values
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="tableName">The table name</param>
|
||||
/// <param name="values">The values to be inserted (in the correct order and number)</param>
|
||||
/// <returns></returns>
|
||||
public void Insert(string tableName, params string[] values)
|
||||
{
|
||||
var query = $"INSERT INTO {tableName} VALUES (";
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
query += $"'{values[i]}'";
|
||||
if (i != values.Length - 1)
|
||||
query += ", ";
|
||||
}
|
||||
|
||||
query += ")";
|
||||
|
||||
var command = new SQLiteCommand(query, Connection);
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove every row in a table that has a certain propery
|
||||
/// </summary>
|
||||
/// <param name="tableName">The table name</param>
|
||||
/// <param name="KeyName">The column name that the search is made by</param>
|
||||
/// <param name="KeyValue">The value that is searched in the specified column</param>
|
||||
/// <returns></returns>
|
||||
public async Task RemoveKeyAsync(string tableName, string KeyName, string KeyValue)
|
||||
{
|
||||
var query = $"DELETE FROM {tableName} WHERE {KeyName} = '{KeyValue}'";
|
||||
|
||||
var command = new SQLiteCommand(query, Connection);
|
||||
await command.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove every row in a table that has a certain propery
|
||||
/// </summary>
|
||||
/// <param name="tableName">The table name</param>
|
||||
/// <param name="KeyName">The column name that the search is made by</param>
|
||||
/// <param name="KeyValue">The value that is searched in the specified column</param>
|
||||
/// <returns></returns>
|
||||
public void RemoveKey(string tableName, string KeyName, string KeyValue)
|
||||
{
|
||||
var query = $"DELETE FROM {tableName} WHERE {KeyName} = '{KeyValue}'";
|
||||
|
||||
var command = new SQLiteCommand(query, Connection);
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the key exists in the table
|
||||
/// </summary>
|
||||
/// <param name="tableName">The table name</param>
|
||||
/// <param name="keyName">The column that the search is made by</param>
|
||||
/// <param name="KeyValue">The value that is searched in the specified column</param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> KeyExistsAsync(string tableName, string keyName, string KeyValue)
|
||||
{
|
||||
var query = $"SELECT * FROM {tableName} where {keyName} = '{KeyValue}'";
|
||||
|
||||
if (await ReadDataAsync(query) is not null)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the key exists in the table
|
||||
/// </summary>
|
||||
/// <param name="tableName">The table name</param>
|
||||
/// <param name="keyName">The column that the search is made by</param>
|
||||
/// <param name="KeyValue">The value that is searched in the specified column</param>
|
||||
/// <returns></returns>
|
||||
public bool KeyExists(string tableName, string keyName, string KeyValue)
|
||||
{
|
||||
var query = $"SELECT * FROM {tableName} where {keyName} = '{KeyValue}'";
|
||||
|
||||
if (ReadData(query) is not null)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set value of a column in a table
|
||||
/// </summary>
|
||||
/// <param name="tableName">The table name</param>
|
||||
/// <param name="keyName">The column that the search is made by</param>
|
||||
/// <param name="KeyValue">The value that is searched in the column specified</param>
|
||||
/// <param name="ResultColumnName">The column that has to be modified</param>
|
||||
/// <param name="ResultColumnValue">The new value that will replace the old value from the column specified</param>
|
||||
public async Task SetValueAsync(
|
||||
string tableName, string keyName, string KeyValue, string ResultColumnName,
|
||||
string ResultColumnValue)
|
||||
{
|
||||
if (!await TableExistsAsync(tableName))
|
||||
throw new Exception($"Table {tableName} does not exist");
|
||||
|
||||
await ExecuteAsync(
|
||||
$"UPDATE {tableName} SET {ResultColumnName}='{ResultColumnValue}' WHERE {keyName}='{KeyValue}'");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set value of a column in a table
|
||||
/// </summary>
|
||||
/// <param name="tableName">The table name</param>
|
||||
/// <param name="keyName">The column that the search is made by</param>
|
||||
/// <param name="KeyValue">The value that is searched in the column specified</param>
|
||||
/// <param name="ResultColumnName">The column that has to be modified</param>
|
||||
/// <param name="ResultColumnValue">The new value that will replace the old value from the column specified</param>
|
||||
public void SetValue(
|
||||
string tableName, string keyName, string KeyValue, string ResultColumnName,
|
||||
string ResultColumnValue)
|
||||
{
|
||||
if (!TableExists(tableName))
|
||||
throw new Exception($"Table {tableName} does not exist");
|
||||
|
||||
Execute($"UPDATE {tableName} SET {ResultColumnName}='{ResultColumnValue}' WHERE {keyName}='{KeyValue}'");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get value from a column in a table
|
||||
/// </summary>
|
||||
/// <param name="tableName">The table name</param>
|
||||
/// <param name="keyName">The column that the search is made by</param>
|
||||
/// <param name="KeyValue">The value that is searched in the specified column</param>
|
||||
/// <param name="ResultColumnName">The column that has the result</param>
|
||||
/// <returns>A string that has the requested value (can be null if nothing found)</returns>
|
||||
public async Task<string?> GetValueAsync(
|
||||
string tableName, string keyName, string KeyValue,
|
||||
string ResultColumnName)
|
||||
{
|
||||
if (!await TableExistsAsync(tableName))
|
||||
throw new Exception($"Table {tableName} does not exist");
|
||||
|
||||
return await ReadDataAsync($"SELECT {ResultColumnName} FROM {tableName} WHERE {keyName}='{KeyValue}'");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get value from a column in a table
|
||||
/// </summary>
|
||||
/// <param name="tableName">The table name</param>
|
||||
/// <param name="keyName">The column that the search is made by</param>
|
||||
/// <param name="KeyValue">The value that is searched in the specified column</param>
|
||||
/// <param name="ResultColumnName">The column that has the result</param>
|
||||
/// <returns>A string that has the requested value (can be null if nothing found)</returns>
|
||||
public string? GetValue(string tableName, string keyName, string KeyValue, string ResultColumnName)
|
||||
{
|
||||
if (!TableExists(tableName))
|
||||
throw new Exception($"Table {tableName} does not exist");
|
||||
|
||||
return ReadData($"SELECT {ResultColumnName} FROM {tableName} WHERE {keyName}='{KeyValue}'");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop the connection to the SQL Database
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async void Stop()
|
||||
{
|
||||
await Connection.CloseAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the structure of a table by adding new columns
|
||||
/// </summary>
|
||||
/// <param name="tableName">The table name</param>
|
||||
/// <param name="columns">The columns to be added</param>
|
||||
/// <param name="TYPE">The type of the columns (TEXT, INTEGER, FLOAT, etc)</param>
|
||||
/// <returns></returns>
|
||||
public async Task AddColumnsToTableAsync(string tableName, string[] columns, string TYPE = "TEXT")
|
||||
{
|
||||
var command = Connection.CreateCommand();
|
||||
command.CommandText = $"SELECT * FROM {tableName}";
|
||||
var reader = await command.ExecuteReaderAsync();
|
||||
var tableColumns = new List<string>();
|
||||
for (var i = 0; i < reader.FieldCount; i++)
|
||||
tableColumns.Add(reader.GetName(i));
|
||||
|
||||
foreach (var column in columns)
|
||||
if (!tableColumns.Contains(column))
|
||||
{
|
||||
command.CommandText = $"ALTER TABLE {tableName} ADD COLUMN {column} {TYPE}";
|
||||
await command.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the structure of a table by adding new columns
|
||||
/// </summary>
|
||||
/// <param name="tableName">The table name</param>
|
||||
/// <param name="columns">The columns to be added</param>
|
||||
/// <param name="TYPE">The type of the columns (TEXT, INTEGER, FLOAT, etc)</param>
|
||||
/// <returns></returns>
|
||||
public void AddColumnsToTable(string tableName, string[] columns, string TYPE = "TEXT")
|
||||
{
|
||||
var command = Connection.CreateCommand();
|
||||
command.CommandText = $"SELECT * FROM {tableName}";
|
||||
var reader = command.ExecuteReader();
|
||||
var tableColumns = new List<string>();
|
||||
for (var i = 0; i < reader.FieldCount; i++)
|
||||
tableColumns.Add(reader.GetName(i));
|
||||
|
||||
foreach (var column in columns)
|
||||
if (!tableColumns.Contains(column))
|
||||
{
|
||||
command.CommandText = $"ALTER TABLE {tableName} ADD COLUMN {column} {TYPE}";
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a table exists
|
||||
/// </summary>
|
||||
/// <param name="tableName">The table name</param>
|
||||
/// <returns>True if the table exists, false if not</returns>
|
||||
public async Task<bool> TableExistsAsync(string tableName)
|
||||
{
|
||||
var cmd = Connection.CreateCommand();
|
||||
cmd.CommandText = $"SELECT name FROM sqlite_master WHERE type='table' AND name='{tableName}'";
|
||||
var result = await cmd.ExecuteScalarAsync();
|
||||
|
||||
if (result == null)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a table exists
|
||||
/// </summary>
|
||||
/// <param name="tableName">The table name</param>
|
||||
/// <returns>True if the table exists, false if not</returns>
|
||||
public bool TableExists(string tableName)
|
||||
{
|
||||
var cmd = Connection.CreateCommand();
|
||||
cmd.CommandText = $"SELECT name FROM sqlite_master WHERE type='table' AND name='{tableName}'";
|
||||
var result = cmd.ExecuteScalar();
|
||||
|
||||
if (result == null)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a table
|
||||
/// </summary>
|
||||
/// <param name="tableName">The table name</param>
|
||||
/// <param name="columns">The columns of the table</param>
|
||||
/// <returns></returns>
|
||||
public async Task CreateTableAsync(string tableName, params string[] columns)
|
||||
{
|
||||
var cmd = Connection.CreateCommand();
|
||||
cmd.CommandText = $"CREATE TABLE IF NOT EXISTS {tableName} ({string.Join(", ", columns)})";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a table
|
||||
/// </summary>
|
||||
/// <param name="tableName">The table name</param>
|
||||
/// <param name="columns">The columns of the table</param>
|
||||
/// <returns></returns>
|
||||
public void CreateTable(string tableName, params string[] columns)
|
||||
{
|
||||
var cmd = Connection.CreateCommand();
|
||||
cmd.CommandText = $"CREATE TABLE IF NOT EXISTS {tableName} ({string.Join(", ", columns)})";
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute a custom query
|
||||
/// </summary>
|
||||
/// <param name="query">The query</param>
|
||||
/// <returns>The number of rows that the query modified</returns>
|
||||
public async Task<int> ExecuteAsync(string query)
|
||||
{
|
||||
if (!Connection.State.HasFlag(ConnectionState.Open))
|
||||
await Connection.OpenAsync();
|
||||
var command = new SQLiteCommand(query, Connection);
|
||||
var answer = await command.ExecuteNonQueryAsync();
|
||||
return answer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute a custom query
|
||||
/// </summary>
|
||||
/// <param name="query">The query</param>
|
||||
/// <returns>The number of rows that the query modified</returns>
|
||||
public int Execute(string query)
|
||||
{
|
||||
if (!Connection.State.HasFlag(ConnectionState.Open))
|
||||
Connection.Open();
|
||||
var command = new SQLiteCommand(query, Connection);
|
||||
var r = command.ExecuteNonQuery();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read data from the result table and return the first row
|
||||
/// </summary>
|
||||
/// <param name="query">The query</param>
|
||||
/// <returns>The result is a string that has all values separated by space character</returns>
|
||||
public async Task<string?> ReadDataAsync(string query)
|
||||
{
|
||||
if (!Connection.State.HasFlag(ConnectionState.Open))
|
||||
await Connection.OpenAsync();
|
||||
var command = new SQLiteCommand(query, Connection);
|
||||
var reader = await command.ExecuteReaderAsync();
|
||||
|
||||
var values = new object[reader.FieldCount];
|
||||
if (reader.Read())
|
||||
{
|
||||
reader.GetValues(values);
|
||||
return string.Join<object>(" ", values);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read data from the result table and return the first row
|
||||
/// </summary>
|
||||
/// <param name="query">The query</param>
|
||||
/// <returns>The result is a string that has all values separated by space character</returns>
|
||||
public string? ReadData(string query)
|
||||
{
|
||||
if (!Connection.State.HasFlag(ConnectionState.Open))
|
||||
Connection.Open();
|
||||
var command = new SQLiteCommand(query, Connection);
|
||||
var reader = command.ExecuteReader();
|
||||
|
||||
var values = new object[reader.FieldCount];
|
||||
if (reader.Read())
|
||||
{
|
||||
reader.GetValues(values);
|
||||
return string.Join<object>(" ", values);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read data from the result table and return the first row
|
||||
/// </summary>
|
||||
/// <param name="query">The query</param>
|
||||
/// <returns>The first row as separated items</returns>
|
||||
public async Task<object[]?> ReadDataArrayAsync(string query)
|
||||
{
|
||||
if (!Connection.State.HasFlag(ConnectionState.Open))
|
||||
await Connection.OpenAsync();
|
||||
var command = new SQLiteCommand(query, Connection);
|
||||
var reader = await command.ExecuteReaderAsync();
|
||||
|
||||
var values = new object[reader.FieldCount];
|
||||
if (reader.Read())
|
||||
{
|
||||
reader.GetValues(values);
|
||||
return values;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Read data from the result table and return the first row
|
||||
/// </summary>
|
||||
/// <param name="query">The query</param>
|
||||
/// <returns>The first row as separated items</returns>
|
||||
public object[]? ReadDataArray(string query)
|
||||
{
|
||||
if (!Connection.State.HasFlag(ConnectionState.Open))
|
||||
Connection.Open();
|
||||
var command = new SQLiteCommand(query, Connection);
|
||||
var reader = command.ExecuteReader();
|
||||
|
||||
var values = new object[reader.FieldCount];
|
||||
if (reader.Read())
|
||||
{
|
||||
reader.GetValues(values);
|
||||
return values;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read all rows from the result table and return them as a list of string arrays. The string arrays contain the
|
||||
/// values of each row
|
||||
/// </summary>
|
||||
/// <param name="query">The query</param>
|
||||
/// <returns>A list of string arrays representing the values that the query returns</returns>
|
||||
public async Task<List<string[]>?> ReadAllRowsAsync(string query)
|
||||
{
|
||||
if (!Connection.State.HasFlag(ConnectionState.Open))
|
||||
await Connection.OpenAsync();
|
||||
var command = new SQLiteCommand(query, Connection);
|
||||
var reader = await command.ExecuteReaderAsync();
|
||||
|
||||
if (!reader.HasRows)
|
||||
return null;
|
||||
|
||||
List<string[]> rows = new();
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
var values = new string[reader.FieldCount];
|
||||
reader.GetValues(values);
|
||||
rows.Add(values);
|
||||
}
|
||||
|
||||
if (rows.Count == 0) return null;
|
||||
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using PluginManager.Others;
|
||||
|
||||
namespace PluginManager.Interfaces;
|
||||
|
||||
@@ -37,12 +35,16 @@ public interface DBCommand
|
||||
/// <summary>
|
||||
/// The main body of the command. This is what is executed when user calls the command in Server
|
||||
/// </summary>
|
||||
/// <param name="context">The disocrd Context</param>
|
||||
void ExecuteServer(SocketCommandContext context) { }
|
||||
/// <param name="args">The disocrd Context</param>
|
||||
void ExecuteServer(DBCommandExecutingArguments args)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The main body of the command. This is what is executed when user calls the command in DM
|
||||
/// </summary>
|
||||
/// <param name="context">The disocrd Context</param>
|
||||
void ExecuteDM(SocketCommandContext context) { }
|
||||
/// <param name="args">The disocrd Context</param>
|
||||
void ExecuteDM(DBCommandExecutingArguments args)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ public interface DBEvent
|
||||
/// <summary>
|
||||
/// The name of the event
|
||||
/// </summary>
|
||||
string name { get; }
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The description of the event
|
||||
/// </summary>
|
||||
string description { get; }
|
||||
string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The method that is invoked when the event is loaded into memory
|
||||
|
||||
21
PluginManager/Interfaces/DBSlashCommand.cs
Normal file
21
PluginManager/Interfaces/DBSlashCommand.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.Collections.Generic;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
|
||||
namespace PluginManager.Interfaces;
|
||||
|
||||
public interface DBSlashCommand
|
||||
{
|
||||
string Name { get; }
|
||||
string Description { get; }
|
||||
|
||||
bool canUseDM { get; }
|
||||
|
||||
List<SlashCommandOptionBuilder> Options { get; }
|
||||
|
||||
void ExecuteServer(SocketSlashCommand context)
|
||||
{
|
||||
}
|
||||
|
||||
void ExecuteDM(SocketSlashCommand context) { }
|
||||
}
|
||||
17
PluginManager/Interfaces/Exceptions/IException.cs
Normal file
17
PluginManager/Interfaces/Exceptions/IException.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
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);
|
||||
|
||||
}
|
||||
17
PluginManager/Interfaces/IInternalAction.cs
Normal file
17
PluginManager/Interfaces/IInternalAction.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Threading.Tasks;
|
||||
using PluginManager.Others;
|
||||
|
||||
namespace PluginManager.Interfaces;
|
||||
|
||||
public interface ICommandAction
|
||||
{
|
||||
public string ActionName { get; }
|
||||
|
||||
public string? Description { get; }
|
||||
|
||||
public string? Usage { get; }
|
||||
|
||||
public InternalActionRunType RunType { get; }
|
||||
|
||||
public Task Execute(string[]? args);
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.WebSocket;
|
||||
using PluginManager.Others;
|
||||
|
||||
namespace PluginManager.Items;
|
||||
|
||||
public class Command
|
||||
{
|
||||
/// <summary>
|
||||
/// The author of the command
|
||||
/// </summary>
|
||||
public SocketUser? Author;
|
||||
|
||||
/// <summary>
|
||||
/// The Command class contructor
|
||||
/// </summary>
|
||||
/// <param name="message">The message that was sent</param>
|
||||
public Command(SocketMessage message)
|
||||
{
|
||||
Author = message.Author;
|
||||
var data = message.Content.Split(' ');
|
||||
Arguments = data.Length > 1 ? new List<string>(data.MergeStrings(1).Split(' ')) : new List<string>();
|
||||
CommandName = data[0].Substring(1);
|
||||
PrefixUsed = data[0][0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The list of arguments
|
||||
/// </summary>
|
||||
public List<string> Arguments { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The command that is executed
|
||||
/// </summary>
|
||||
public string CommandName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The prefix that is used for the command
|
||||
/// </summary>
|
||||
public char PrefixUsed { get; }
|
||||
}
|
||||
|
||||
public class ConsoleCommand
|
||||
{
|
||||
public string CommandName { get; init; }
|
||||
public string Description { get; init; }
|
||||
public string Usage { get; init; }
|
||||
public Action<string[]> Action { get; init; }
|
||||
}
|
||||
@@ -1,468 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Discord.WebSocket;
|
||||
|
||||
using PluginManager.Interfaces;
|
||||
using PluginManager.Loaders;
|
||||
using PluginManager.Online;
|
||||
using PluginManager.Online.Helpers;
|
||||
using PluginManager.Online.Updates;
|
||||
using PluginManager.Others;
|
||||
|
||||
namespace PluginManager.Items;
|
||||
|
||||
public class ConsoleCommandsHandler
|
||||
{
|
||||
private static readonly PluginsManager manager = new("https://raw.githubusercontent.com/Wizzy69/installer/discord-bot-files/Plugins.txt");
|
||||
private static readonly List<ConsoleCommand> commandList = new();
|
||||
private readonly DiscordSocketClient? client;
|
||||
|
||||
|
||||
private static bool isDownloading = false;
|
||||
private static bool pluginsLoaded = false;
|
||||
|
||||
public ConsoleCommandsHandler(DiscordSocketClient client)
|
||||
{
|
||||
this.client = client;
|
||||
InitializeBasicCommands();
|
||||
//Console.WriteLine("Initialized console command handler !");
|
||||
}
|
||||
|
||||
private void InitializeBasicCommands()
|
||||
{
|
||||
|
||||
commandList.Clear();
|
||||
|
||||
AddCommand("help", "Show help", "help <command>", args =>
|
||||
{
|
||||
if (args.Length <= 1)
|
||||
{
|
||||
Console.WriteLine("Available commands:");
|
||||
List<string[]> items = new List<string[]>();
|
||||
items.Add(new[] { "-", "-", "-" });
|
||||
items.Add(new[] { "Command", "Description", "Usage" });
|
||||
items.Add(new[] { " ", " ", "Argument type: <optional> [required]" });
|
||||
items.Add(new[] { "-", "-", "-" });
|
||||
|
||||
foreach (var command in commandList)
|
||||
{
|
||||
var pa = from p in command.Action.Method.GetParameters() where p.Name != null select p.ParameterType.FullName;
|
||||
items.Add(new[] { command.CommandName, command.Description, command.Usage });
|
||||
}
|
||||
|
||||
items.Add(new[] { "-", "-", "-" });
|
||||
Console_Utilities.FormatAndAlignTable(items, TableFormat.DEFAULT);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var command in commandList)
|
||||
if (command.CommandName == args[1])
|
||||
{
|
||||
Console.WriteLine("Command description: " + command.Description);
|
||||
Console.WriteLine("Command execution format:" + command.Usage);
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine("Command not found");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
AddCommand("lp", "Load plugins", () =>
|
||||
{
|
||||
if (pluginsLoaded)
|
||||
return;
|
||||
var loader = new PluginLoader(client!);
|
||||
ConsoleColor 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
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
|
||||
Console.WriteLine("[CMD] Failed to load command : " + name + " because " + exception!.Message);
|
||||
}
|
||||
Console.ForegroundColor = cc;
|
||||
};
|
||||
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.LoadPlugins();
|
||||
Console.ForegroundColor = cc;
|
||||
pluginsLoaded = true;
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
AddCommand("listplugs", "list available plugins", () => { manager.ListAvailablePlugins().Wait(); });
|
||||
|
||||
AddCommand("dwplug", "download plugin", "dwplug [name]", async args =>
|
||||
{
|
||||
isDownloading = true;
|
||||
if (args.Length == 1)
|
||||
{
|
||||
isDownloading = false;
|
||||
Console.WriteLine("Please specify plugin name");
|
||||
return;
|
||||
}
|
||||
|
||||
var name = args.MergeStrings(1);
|
||||
// info[0] = plugin type
|
||||
// info[1] = plugin link
|
||||
// info[2] = if others are required, or string.Empty if none
|
||||
var info = await manager.GetPluginLinkByName(name);
|
||||
if (info[1] == null) // link is null
|
||||
{
|
||||
if (name == "")
|
||||
{
|
||||
isDownloading = false;
|
||||
Console_Utilities.WriteColorText("Name is invalid");
|
||||
return;
|
||||
}
|
||||
isDownloading = false;
|
||||
Console_Utilities.WriteColorText($"Failed to find plugin &b{name} &c!" + " Use &glistplugs &ccommand to display all available plugins !");
|
||||
return;
|
||||
}
|
||||
|
||||
string path;
|
||||
if (info[0] == "Command" || info[0] == "Event")
|
||||
path = "./Data/Plugins/" + info[0] + "s/" + name + "." + (info[0] == "Command" ? PluginLoader.pluginCMDExtension : PluginLoader.pluginEVEExtension);
|
||||
else
|
||||
path = $"./{info[1].Split('/')[info[1].Split('/').Length - 1]}";
|
||||
//Console.WriteLine("Downloading: " + path + " [" + info[1] + "]");
|
||||
await ServerCom.DownloadFileAsync(info[1], path);
|
||||
if (info[0] == "Event")
|
||||
Config.PluginConfig.InstalledPlugins.Add(new(name, PluginType.Event));
|
||||
else if (info[0] == "Command")
|
||||
Config.PluginConfig.InstalledPlugins.Add(new(name, PluginType.Command));
|
||||
|
||||
|
||||
Console.WriteLine("\n");
|
||||
|
||||
// check requirements if any
|
||||
|
||||
if (info.Length == 3 && info[2] != string.Empty && info[2] != null)
|
||||
{
|
||||
Console.WriteLine($"Downloading requirements for plugin : {name}");
|
||||
|
||||
var lines = await ServerCom.ReadTextFromURL(info[2]);
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (!(line.Length > 0 && line.Contains(",")))
|
||||
continue;
|
||||
var split = line.Split(',');
|
||||
Console.WriteLine($"\nDownloading item: {split[1]}");
|
||||
if (File.Exists("./" + split[1])) File.Delete("./" + split[1]);
|
||||
await ServerCom.DownloadFileAsync(split[0], "./" + split[1]);
|
||||
Console.WriteLine();
|
||||
if (split[0].EndsWith(".pak"))
|
||||
File.Move("./" + split[1], "./Data/PAKS/" + split[1], true);
|
||||
else if (split[0].EndsWith(".zip") || split[0].EndsWith(".pkg"))
|
||||
{
|
||||
Console.WriteLine($"Extracting {split[1]} ...");
|
||||
var bar = new Console_Utilities.ProgressBar(ProgressBarType.NO_END);// { Max = 100f, Color = ConsoleColor.Green };
|
||||
bar.Start();
|
||||
await Functions.ExtractArchive("./" + split[1], "./", null, UnzipProgressType.PercentageFromTotalSize);
|
||||
bar.Stop();
|
||||
Console.WriteLine("\n");
|
||||
File.Delete("./" + split[1]);
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
}
|
||||
VersionString? ver = await VersionString.GetVersionOfPackageFromWeb(name);
|
||||
if (ver is null) throw new Exception("Incorrect version");
|
||||
Config.SetPluginVersion(name, $"{ver.PackageVersionID}.{ver.PackageMainVersion}.{ver.PackageCheckVersion}");
|
||||
// Console.WriteLine();
|
||||
|
||||
isDownloading = false;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
AddCommand("value", "read value from VariableStack", "value [key]", args =>
|
||||
{
|
||||
if (args.Length != 2)
|
||||
return;
|
||||
if (!Config.ContainsKey(args[1]))
|
||||
return;
|
||||
|
||||
var data = Config.GetValue<string>(args[1]);
|
||||
Console.WriteLine($"{args[1]} => {data}");
|
||||
}
|
||||
);
|
||||
|
||||
AddCommand("add", "add variable to the system variables", "add [key] [value] [isReadOnly=true/false]", args =>
|
||||
{
|
||||
if (args.Length < 4)
|
||||
return;
|
||||
var key = args[1];
|
||||
var value = args[2];
|
||||
var isReadOnly = args[3].Equals("true", StringComparison.CurrentCultureIgnoreCase);
|
||||
|
||||
try
|
||||
{
|
||||
Config.GetAndAddValueToVariable(key, value, isReadOnly);
|
||||
Console.WriteLine($"Updated config file with the following command: {args[1]} => {value}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
AddCommand("remv", "remove variable from system variables", "remv [key]", args =>
|
||||
{
|
||||
if (args.Length < 2)
|
||||
return;
|
||||
Config.RemoveKey(args[1]);
|
||||
}
|
||||
);
|
||||
|
||||
AddCommand("sd", "Shuts down the discord bot", async () =>
|
||||
{
|
||||
if (client is null)
|
||||
return;
|
||||
Console_Utilities.ProgressBar bar = new Console_Utilities.ProgressBar(ProgressBarType.NO_END);
|
||||
|
||||
bar.Start();
|
||||
await Config.SaveConfig(SaveType.NORMAL);
|
||||
await Config.SaveConfig(SaveType.BACKUP);
|
||||
await Task.Delay(4000);
|
||||
bar.Stop();
|
||||
Console.WriteLine();
|
||||
await client.StopAsync();
|
||||
await client.DisposeAsync();
|
||||
Environment.Exit(0);
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
AddCommand("extern", "Load an external command", "extern [pluginName]", async (args) =>
|
||||
{
|
||||
if (args.Length <= 1) return;
|
||||
string pName = Functions.MergeStrings(args, 1);
|
||||
HttpClient client = new HttpClient();
|
||||
string url = (await manager.GetPluginLinkByName(pName))[1];
|
||||
Stream s = await client.GetStreamAsync(url);
|
||||
MemoryStream str = new MemoryStream();
|
||||
await s.CopyToAsync(str);
|
||||
var asmb = Assembly.Load(str.ToArray());
|
||||
|
||||
var types = asmb.GetTypes();
|
||||
foreach (var type in types)
|
||||
{
|
||||
if (type.IsClass && typeof(DBEvent).IsAssignableFrom(type))
|
||||
{
|
||||
DBEvent instance = (DBEvent)Activator.CreateInstance(type);
|
||||
instance.Start(this.client);
|
||||
Console.WriteLine($"Loaded external {type.FullName}!");
|
||||
}
|
||||
else if (type.IsClass && typeof(DBCommand).IsAssignableFrom(type))
|
||||
{
|
||||
Console.WriteLine("Only events can be loaded from external sources !");
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AddCommand("remplug", "Remove a plugin", "remplug [plugName]", async args =>
|
||||
{
|
||||
|
||||
if (args.Length <= 1) return;
|
||||
|
||||
isDownloading = true;
|
||||
string plugName = Functions.MergeStrings(args, 1);
|
||||
if (pluginsLoaded)
|
||||
{
|
||||
if (Functions.GetOperatingSystem() == Others.OperatingSystem.WINDOWS)
|
||||
{
|
||||
Process.Start("DiscordBot.exe", $"/remplug {plugName}");
|
||||
await Task.Delay(100);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Process.Start("./DiscordBot", $"/remplug {plugName}");
|
||||
await Task.Delay(100);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
isDownloading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
string location = "./Data/Plugins/";
|
||||
|
||||
location = Config.PluginConfig.GetPluginType(plugName) switch
|
||||
{
|
||||
PluginType.Command => location + "Commands/" + plugName + "." + PluginLoader.pluginCMDExtension,
|
||||
PluginType.Event => location + "Events/" + plugName + "." + PluginLoader.pluginEVEExtension,
|
||||
PluginType.Unknown => "./",
|
||||
_ => throw new NotImplementedException("Plugin type incorrect")
|
||||
};
|
||||
|
||||
if (!File.Exists(location))
|
||||
{
|
||||
Console.WriteLine("The plugin does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
File.Delete(location);
|
||||
if (Config.PluginConfig.Contains(plugName))
|
||||
{
|
||||
var tuple = Config.PluginConfig.InstalledPlugins.Where(t => t.Item1 == plugName).FirstOrDefault();
|
||||
Console.WriteLine("Found: " + tuple.ToString());
|
||||
Config.PluginConfig.InstalledPlugins.Remove(tuple);
|
||||
Config.RemovePluginVersion(plugName);
|
||||
await Config.SaveConfig(SaveType.NORMAL);
|
||||
}
|
||||
Console.WriteLine("Removed the plugin DLL. Checking for other files ...");
|
||||
|
||||
var info = await manager.GetPluginLinkByName(plugName);
|
||||
if (info[2] != string.Empty)
|
||||
{
|
||||
var lines = await ServerCom.ReadTextFromURL(info[2]);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (!(line.Length > 0 && line.Contains(",")))
|
||||
continue;
|
||||
var split = line.Split(',');
|
||||
if (File.Exists("./" + split[1]))
|
||||
File.Delete("./" + split[1]);
|
||||
|
||||
|
||||
Console.WriteLine("Removed: " + split[1]);
|
||||
}
|
||||
|
||||
|
||||
if (Directory.Exists(plugName))
|
||||
Directory.Delete(plugName, true);
|
||||
}
|
||||
isDownloading = false;
|
||||
Console.WriteLine(plugName + " has been successfully deleted !");
|
||||
|
||||
});
|
||||
|
||||
AddCommand("reload", "Reload the bot with all plugins", () =>
|
||||
{
|
||||
if (Functions.GetOperatingSystem() == Others.OperatingSystem.WINDOWS)
|
||||
{
|
||||
Process.Start("DiscordBot.exe", $"lp");
|
||||
HandleCommand("sd");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Process.Start("./DiscordBot", $"lp");
|
||||
HandleCommand("sd");
|
||||
}
|
||||
});
|
||||
|
||||
//Sort the commands by name
|
||||
commandList.Sort((x, y) => x.CommandName.CompareTo(y.CommandName));
|
||||
}
|
||||
|
||||
public static void AddCommand(string command, string description, string usage, Action<string[]> action)
|
||||
{
|
||||
commandList.Add(new ConsoleCommand { CommandName = command, Description = description, Action = action, Usage = usage });
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console_Utilities.WriteColorText($"Command &r{command} &cadded to the list of commands");
|
||||
}
|
||||
|
||||
public static void AddCommand(string command, string description, Action action)
|
||||
{
|
||||
AddCommand(command, description, command, args => action());
|
||||
}
|
||||
|
||||
public static void RemoveCommand(string command)
|
||||
{
|
||||
commandList.RemoveAll(x => x.CommandName == command);
|
||||
}
|
||||
|
||||
public static bool CommandExists(string command)
|
||||
{
|
||||
return GetCommand(command) is not null;
|
||||
}
|
||||
|
||||
public static ConsoleCommand? GetCommand(string command)
|
||||
{
|
||||
return commandList.FirstOrDefault(t => t.CommandName == command);
|
||||
}
|
||||
|
||||
public static async Task ExecuteCommad(string command)
|
||||
{
|
||||
var args = command.Split(' ');
|
||||
// Console.WriteLine(command);
|
||||
foreach (var item in commandList.ToList())
|
||||
if (item.CommandName == args[0])
|
||||
{
|
||||
item.Action.Invoke(args);
|
||||
Console.WriteLine();
|
||||
|
||||
while (isDownloading) await Task.Delay(1000);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public bool HandleCommand(string command, bool removeCommandExecution = true)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
var args = command.Split(' ');
|
||||
foreach (var item in commandList.ToList())
|
||||
if (item.CommandName == args[0])
|
||||
{
|
||||
if (removeCommandExecution)
|
||||
{
|
||||
Console.SetCursorPosition(0, Console.CursorTop - 1);
|
||||
for (int i = 0; i < command.Length + 30; i++)
|
||||
Console.Write(" ");
|
||||
Console.SetCursorPosition(0, Console.CursorTop);
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
item.Action(args);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
//Console.WriteLine($"Executing: {args[0]} with the following parameters: {args.MergeStrings(1)}");
|
||||
}
|
||||
}
|
||||
72
PluginManager/Loaders/ActionsLoader.cs
Normal file
72
PluginManager/Loaders/ActionsLoader.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using PluginManager.Interfaces;
|
||||
using PluginManager.Others;
|
||||
|
||||
namespace PluginManager.Loaders;
|
||||
|
||||
public class ActionsLoader
|
||||
{
|
||||
public delegate void ActionLoaded(string name, string typeName, bool success, Exception? e = null);
|
||||
|
||||
private readonly string actionExtension = "dll";
|
||||
|
||||
private readonly string actionFolder = @"./Data/Actions/";
|
||||
|
||||
public ActionsLoader(string path, string extension)
|
||||
{
|
||||
actionFolder = path;
|
||||
actionExtension = extension;
|
||||
}
|
||||
|
||||
public event ActionLoaded? ActionLoadedEvent;
|
||||
|
||||
public async Task<List<ICommandAction>?> Load()
|
||||
{
|
||||
Directory.CreateDirectory(actionFolder);
|
||||
var files = Directory.GetFiles(actionFolder, $"*.{actionExtension}", SearchOption.AllDirectories);
|
||||
|
||||
var actions = new List<ICommandAction>();
|
||||
|
||||
foreach (var file in files)
|
||||
try
|
||||
{
|
||||
Assembly.LoadFrom(file);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ActionLoadedEvent?.Invoke(file, "", false, e);
|
||||
}
|
||||
|
||||
var types = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(s => s.GetTypes())
|
||||
.Where(p => typeof(ICommandAction).IsAssignableFrom(p) && !p.IsInterface);
|
||||
|
||||
foreach (var type in types)
|
||||
try
|
||||
{
|
||||
var action = (ICommandAction)Activator.CreateInstance(type);
|
||||
if (action.ActionName == null)
|
||||
{
|
||||
ActionLoadedEvent?.Invoke(action.ActionName, type.Name, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (action.RunType == InternalActionRunType.ON_STARTUP)
|
||||
await action.Execute(null);
|
||||
|
||||
ActionLoadedEvent?.Invoke(action.ActionName, type.Name, true);
|
||||
actions.Add(action);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ActionLoadedEvent?.Invoke(type.Name, type.Name, false, e);
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using PluginManager.Online.Updates;
|
||||
using PluginManager.Interfaces;
|
||||
using PluginManager.Others;
|
||||
|
||||
namespace PluginManager.Loaders;
|
||||
@@ -19,7 +17,7 @@ internal class LoaderArgs : EventArgs
|
||||
internal object? Plugin { get; init; }
|
||||
}
|
||||
|
||||
internal class Loader<T>
|
||||
internal class Loader
|
||||
{
|
||||
internal Loader(string path, string extension)
|
||||
{
|
||||
@@ -31,35 +29,43 @@ internal class Loader<T>
|
||||
private string path { get; }
|
||||
private string extension { get; }
|
||||
|
||||
|
||||
internal delegate void FileLoadedEventHandler(LoaderArgs args);
|
||||
|
||||
internal delegate void PluginLoadedEventHandler(LoaderArgs args);
|
||||
|
||||
internal event FileLoadedEventHandler? FileLoaded;
|
||||
|
||||
internal event PluginLoadedEventHandler? PluginLoaded;
|
||||
|
||||
internal List<T>? Load()
|
||||
|
||||
internal (List<DBEvent>?, List<DBCommand>?, List<DBSlashCommand>?) Load()
|
||||
{
|
||||
var list = new List<T>();
|
||||
List<DBEvent> events = new();
|
||||
List<DBSlashCommand> slashCommands = new();
|
||||
List<DBCommand> commands = new();
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
return null;
|
||||
return (null, null, null);
|
||||
}
|
||||
|
||||
var files = Directory.GetFiles(path, $"*.{extension}", SearchOption.AllDirectories);
|
||||
foreach (var file in files)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
Assembly.LoadFrom(file);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Config.Logger.Log("PluginName: " + new FileInfo(file).Name.Split('.')[0] + " not loaded", this,
|
||||
LogLevel.ERROR);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FileLoaded != null)
|
||||
{
|
||||
var args = new LoaderArgs
|
||||
{
|
||||
Exception = null,
|
||||
TypeName = nameof(T),
|
||||
TypeName = null,
|
||||
IsLoaded = false,
|
||||
PluginName = new FileInfo(file).Name.Split('.')[0],
|
||||
Plugin = null
|
||||
@@ -68,6 +74,15 @@ internal class Loader<T>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (LoadItems<DBEvent>(), LoadItems<DBCommand>(), LoadItems<DBSlashCommand>());
|
||||
}
|
||||
|
||||
internal List<T> LoadItems<T>()
|
||||
{
|
||||
List<T> list = new();
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
var interfaceType = typeof(T);
|
||||
@@ -91,22 +106,40 @@ internal class Loader<T>
|
||||
Exception = null,
|
||||
IsLoaded = true,
|
||||
PluginName = type.FullName,
|
||||
TypeName = nameof(T),
|
||||
TypeName = typeof(T) == typeof(DBCommand) ? "DBCommand" :
|
||||
typeof(T) == typeof(DBEvent) ? "DBEvent" :
|
||||
typeof(T) == typeof(DBSlashCommand) ? "DBSlashCommand" :
|
||||
null,
|
||||
Plugin = plugin
|
||||
}
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (PluginLoaded != null) PluginLoaded.Invoke(new LoaderArgs { Exception = ex, IsLoaded = false, PluginName = type.FullName, TypeName = nameof(T) });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
if (PluginLoaded != null)
|
||||
PluginLoaded.Invoke(new LoaderArgs
|
||||
{
|
||||
Functions.WriteErrFile(ex.ToString());
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
|
||||
using PluginManager.Interfaces;
|
||||
using PluginManager.Online.Helpers;
|
||||
using PluginManager.Online.Updates;
|
||||
using PluginManager.Others;
|
||||
|
||||
namespace PluginManager.Loaders;
|
||||
@@ -20,11 +15,11 @@ public class PluginLoader
|
||||
|
||||
public delegate void EVELoaded(string name, string typeName, bool success, Exception? e = null);
|
||||
|
||||
private const string pluginCMDFolder = @"./Data/Plugins/Commands/";
|
||||
private const string pluginEVEFolder = @"./Data/Plugins/Events/";
|
||||
public delegate void SLSHLoaded(string name, string tyypename, bool success, Exception? e = null);
|
||||
|
||||
internal const string pluginCMDExtension = "dll";
|
||||
internal const string pluginEVEExtension = "dll";
|
||||
private const string pluginFolder = @"./Data/Plugins/";
|
||||
|
||||
internal const string pluginExtension = "dll";
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
/// <summary>
|
||||
@@ -37,6 +32,11 @@ public class PluginLoader
|
||||
/// </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>
|
||||
@@ -57,104 +57,127 @@ public class PluginLoader
|
||||
/// </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
|
||||
{
|
||||
var count = 0;
|
||||
if (Commands is not null)
|
||||
count += Commands.Count;
|
||||
if (Events is not null)
|
||||
count += Events.Count;
|
||||
if (SlashCommands is not null)
|
||||
count += SlashCommands.Count;
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The main mathod that is called to load all events
|
||||
/// </summary>
|
||||
public async void LoadPlugins()
|
||||
{
|
||||
//Check for updates in commands
|
||||
foreach (var file in Directory.GetFiles("./Data/Plugins/Commands", $"*.{pluginCMDExtension}", SearchOption.AllDirectories))
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
string name = new FileInfo(file).Name.Split('.')[0];
|
||||
if (!Config.PluginVersionsContainsKey(name))
|
||||
Config.SetPluginVersion(name, (await VersionString.GetVersionOfPackageFromWeb(name))?.PackageVersionID + ".0.0");
|
||||
|
||||
if (await PluginUpdater.CheckForUpdates(name))
|
||||
await PluginUpdater.Download(name);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
//Check for updates in events
|
||||
foreach (var file in Directory.GetFiles("./Data/Plugins/Events", $"*.{pluginEVEExtension}", SearchOption.AllDirectories))
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
string name = new FileInfo(file).Name.Split('.')[0];
|
||||
if (!Config.PluginVersionsContainsKey(name))
|
||||
Config.SetPluginVersion(name, (await VersionString.GetVersionOfPackageFromWeb(name))?.PackageVersionID + ".0.0");
|
||||
|
||||
if (await PluginUpdater.CheckForUpdates(name))
|
||||
await PluginUpdater.Download(name);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
//Save the new config file (after the updates)
|
||||
await Config.SaveConfig(SaveType.NORMAL);
|
||||
|
||||
|
||||
//Load all plugins
|
||||
|
||||
Commands = new List<DBCommand>();
|
||||
Events = new List<DBEvent>();
|
||||
SlashCommands = new List<DBSlashCommand>();
|
||||
|
||||
Functions.WriteLogFile("Starting plugin loader ... Client: " + _client.CurrentUser.Username);
|
||||
Console.WriteLine("Loading plugins");
|
||||
|
||||
var commandsLoader = new Loader<DBCommand>(pluginCMDFolder, pluginCMDExtension);
|
||||
var eventsLoader = new Loader<DBEvent>(pluginEVEFolder, pluginEVEExtension);
|
||||
|
||||
commandsLoader.FileLoaded += OnCommandFileLoaded;
|
||||
commandsLoader.PluginLoaded += OnCommandLoaded;
|
||||
|
||||
eventsLoader.FileLoaded += EventFileLoaded;
|
||||
eventsLoader.PluginLoaded += OnEventLoaded;
|
||||
|
||||
Commands = commandsLoader.Load();
|
||||
Events = eventsLoader.Load();
|
||||
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 void EventFileLoaded(LoaderArgs e)
|
||||
private async void Loader_PluginLoaded(LoaderArgs args)
|
||||
{
|
||||
if (!e.IsLoaded)
|
||||
{
|
||||
Functions.WriteLogFile($"[EVENT] Event from file [{e.PluginName}] has been successfully created !");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCommandFileLoaded(LoaderArgs e)
|
||||
{
|
||||
if (!e.IsLoaded)
|
||||
{
|
||||
Functions.WriteLogFile($"[CMD] Command from file [{e.PluginName}] has been successfully loaded !");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEventLoaded(LoaderArgs e)
|
||||
switch (args.TypeName)
|
||||
{
|
||||
case "DBCommand":
|
||||
onCMDLoad?.Invoke(((DBCommand)args.Plugin!).Command, args.TypeName!, args.IsLoaded, args.Exception);
|
||||
break;
|
||||
case "DBEvent":
|
||||
try
|
||||
{
|
||||
if (e.IsLoaded)
|
||||
((DBEvent)e.Plugin!).Start(_client);
|
||||
if (args.IsLoaded)
|
||||
((DBEvent)args.Plugin!).Start(_client);
|
||||
|
||||
onEVELoad?.Invoke(((DBEvent)e.Plugin!).name, e.TypeName!, e.IsLoaded, e.Exception);
|
||||
onEVELoad?.Invoke(((DBEvent)args.Plugin!).Name, args.TypeName!, args.IsLoaded, args.Exception);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
Console.WriteLine("Plugin: " + e.PluginName);
|
||||
Console.WriteLine("Type: " + e.TypeName);
|
||||
Console.WriteLine("IsLoaded: " + e.IsLoaded);
|
||||
Config.Logger.Log(ex.Message, this, LogLevel.ERROR);
|
||||
}
|
||||
|
||||
break;
|
||||
case "DBSlashCommand":
|
||||
if (args.IsLoaded)
|
||||
{
|
||||
var slash = (DBSlashCommand)args.Plugin;
|
||||
var builder = new SlashCommandBuilder();
|
||||
builder.WithName(slash.Name);
|
||||
builder.WithDescription(slash.Description);
|
||||
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());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCommandLoaded(LoaderArgs e)
|
||||
public static async Task LoadPluginFromAssembly(Assembly asmb, DiscordSocketClient client)
|
||||
{
|
||||
onCMDLoad?.Invoke(((DBCommand)e.Plugin!).Command, e.TypeName!, e.IsLoaded, e.Exception);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using PluginManager.Others;
|
||||
|
||||
namespace PluginManager.Online.Helpers
|
||||
{
|
||||
namespace PluginManager.Online.Helpers;
|
||||
|
||||
internal static class OnlineFunctions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -18,7 +18,11 @@ namespace PluginManager.Online.Helpers
|
||||
/// <param name="progress">The <see cref="IProgress{T}" /> that is used to track the download progress</param>
|
||||
/// <param name="cancellation">The cancellation token</param>
|
||||
/// <returns></returns>
|
||||
internal static async Task DownloadFileAsync(this HttpClient client, string url, Stream destination, IProgress<float>? progress = null, IProgress<long>? downloadedBytes = null, int bufferSize = 81920, CancellationToken cancellation = default)
|
||||
internal static async Task DownloadFileAsync(
|
||||
this HttpClient client, string url, Stream destination,
|
||||
IProgress<float>? progress = null,
|
||||
IProgress<long>? downloadedBytes = null, int bufferSize = 81920,
|
||||
CancellationToken cancellation = default)
|
||||
{
|
||||
using (var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellation))
|
||||
{
|
||||
@@ -37,14 +41,15 @@ namespace PluginManager.Online.Helpers
|
||||
// Convert absolute progress (bytes downloaded) into relative progress (0% - 100%)
|
||||
var relativeProgress = new Progress<long>(totalBytes =>
|
||||
{
|
||||
progress.Report((float)totalBytes / contentLength.Value * 100);
|
||||
progress?.Report((float)totalBytes / contentLength.Value *
|
||||
100);
|
||||
downloadedBytes?.Report(totalBytes);
|
||||
}
|
||||
);
|
||||
|
||||
// Use extension method to report progress while downloading
|
||||
await download.CopyToOtherStreamAsync(destination, bufferSize, relativeProgress, cancellation);
|
||||
progress.Report(1);
|
||||
progress.Report(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,8 +65,4 @@ namespace PluginManager.Online.Helpers
|
||||
using var client = new HttpClient();
|
||||
return await client.GetStringAsync(url, cancellation);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,65 +1,66 @@
|
||||
using PluginManager.Others;
|
||||
using System;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
namespace PluginManager.Online.Helpers;
|
||||
|
||||
namespace PluginManager.Online.Helpers
|
||||
{
|
||||
public class VersionString
|
||||
{
|
||||
public int PackageVersionID;
|
||||
public int PackageMainVersion;
|
||||
public int PackageCheckVersion;
|
||||
public int PackageMainVersion;
|
||||
public int PackageVersionID;
|
||||
|
||||
public VersionString(string version)
|
||||
{
|
||||
string[] data = version.Split('.');
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region operators
|
||||
public static bool operator >(VersionString s1, VersionString s2)
|
||||
private bool Equals(VersionString other)
|
||||
{
|
||||
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) => !(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;
|
||||
return PackageCheckVersion == other.PackageCheckVersion && PackageMainVersion == other.PackageMainVersion &&
|
||||
PackageVersionID == other.PackageVersionID;
|
||||
}
|
||||
|
||||
public static bool operator !=(VersionString s1, VersionString s2) => !(s1 == s2);
|
||||
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 static bool operator <=(VersionString s1, VersionString s2) => (s1 < s2 || s1 == s2);
|
||||
public static bool operator >=(VersionString s1, VersionString s2) => (s1 > s2 || s1 == s2);
|
||||
|
||||
#endregion
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(PackageCheckVersion, PackageMainVersion, PackageVersionID);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "{PackageID: " + PackageVersionID + ", PackageVersion: " + PackageMainVersion + ", PackageCheckVersion: " + PackageCheckVersion + "}";
|
||||
return "{PackageID: " + PackageVersionID + ", PackageVersion: " + PackageMainVersion +
|
||||
", PackageCheckVersion: " + PackageCheckVersion + "}";
|
||||
}
|
||||
|
||||
public string ToShortString()
|
||||
@@ -69,23 +70,48 @@ namespace PluginManager.Online.Helpers
|
||||
return $"{PackageVersionID}.{PackageMainVersion}.{PackageCheckVersion}";
|
||||
}
|
||||
|
||||
public static VersionString? GetVersionOfPackage(string pakName)
|
||||
|
||||
#region operators
|
||||
|
||||
public static bool operator >(VersionString s1, VersionString s2)
|
||||
{
|
||||
if (!Config.PluginVersionsContainsKey(pakName))
|
||||
return null;
|
||||
return new VersionString(Config.GetPluginVersion(pakName));
|
||||
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;
|
||||
}
|
||||
|
||||
public static async Task<VersionString?> GetVersionOfPackageFromWeb(string pakName)
|
||||
{
|
||||
string url = "https://raw.githubusercontent.com/Wizzy69/installer/discord-bot-files/Versions";
|
||||
List<string> data = await ServerCom.ReadTextFromURL(url);
|
||||
string? version = (from item in data
|
||||
where !item.StartsWith("#") && item.StartsWith(pakName)
|
||||
select item.Split(',')[1]).FirstOrDefault();
|
||||
if (version == default || version == null) return null;
|
||||
return new VersionString(version);
|
||||
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,10 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using PluginManager.Online.Helpers;
|
||||
using PluginManager.Others;
|
||||
|
||||
using OperatingSystem = PluginManager.Others.OperatingSystem;
|
||||
|
||||
namespace PluginManager.Online;
|
||||
@@ -14,10 +12,12 @@ public class PluginsManager
|
||||
/// <summary>
|
||||
/// The Plugin Manager constructor
|
||||
/// </summary>
|
||||
/// <param name="link">The link to the file where all plugins are stored</param>
|
||||
public PluginsManager(string link)
|
||||
/// <param name="plink">The link to the file where all plugins are stored</param>
|
||||
/// <param name="vlink">The link to the file where all plugin versions are stored</param>
|
||||
public PluginsManager(string plink, string vlink)
|
||||
{
|
||||
PluginsLink = link;
|
||||
PluginsLink = plink;
|
||||
VersionsLink = vlink;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -25,12 +25,15 @@ public class PluginsManager
|
||||
/// </summary>
|
||||
public string PluginsLink { get; }
|
||||
|
||||
public string VersionsLink { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The method to load all plugins
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task ListAvailablePlugins()
|
||||
public async Task<List<string[]>> GetAvailablePlugins()
|
||||
{
|
||||
Config.Logger.Log("Got data from " + VersionsLink, this, LogLevel.INFO);
|
||||
try
|
||||
{
|
||||
var list = await ServerCom.ReadTextFromURL(PluginsLink);
|
||||
@@ -40,16 +43,12 @@ public class PluginsManager
|
||||
var op = Functions.GetOperatingSystem();
|
||||
|
||||
var len = lines.Length;
|
||||
string[] titles = { "Name", "Description", "Type", "Version", "Installed" };
|
||||
data.Add(new[] { "-", "-", "-", "-", "-" });
|
||||
data.Add(titles);
|
||||
data.Add(new[] { "-", "-", "-", "-", "-" });
|
||||
for (var i = 0; i < len; i++)
|
||||
{
|
||||
if (lines[i].Length <= 2)
|
||||
continue;
|
||||
var content = lines[i].Split(',');
|
||||
var display = new string[titles.Length];
|
||||
var display = new string[4]; // 4 columns
|
||||
if (op == OperatingSystem.WINDOWS)
|
||||
{
|
||||
if (content[4].Contains("Windows"))
|
||||
@@ -57,11 +56,9 @@ public class PluginsManager
|
||||
display[0] = content[0];
|
||||
display[1] = content[1];
|
||||
display[2] = content[2];
|
||||
display[3] = (await VersionString.GetVersionOfPackageFromWeb(content[0]) ?? new VersionString("0.0.0")).ToShortString();
|
||||
if (Config.PluginConfig.Contains(content[0]) || Config.PluginConfig.Contains(content[0]))
|
||||
display[4] = "✓";
|
||||
else
|
||||
display[4] = "X";
|
||||
display[3] =
|
||||
(await GetVersionOfPackageFromWeb(content[0]) ?? new VersionString("0.0.0"))
|
||||
.ToShortString();
|
||||
data.Add(display);
|
||||
}
|
||||
}
|
||||
@@ -72,25 +69,44 @@ public class PluginsManager
|
||||
display[0] = content[0];
|
||||
display[1] = content[1];
|
||||
display[2] = content[2];
|
||||
display[3] = (await VersionString.GetVersionOfPackageFromWeb(content[0]) ?? new VersionString("0.0.0")).ToShortString();
|
||||
if (Config.PluginConfig.Contains(content[0]) || Config.PluginConfig.Contains(content[0]))
|
||||
display[4] = "✓";
|
||||
else
|
||||
display[4] = "X";
|
||||
display[3] =
|
||||
(await GetVersionOfPackageFromWeb(content[0]) ?? new VersionString("0.0.0"))
|
||||
.ToShortString();
|
||||
data.Add(display);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data.Add(new[] { "-", "-", "-", "-", "-" });
|
||||
data.Add(new[] { "-", "-", "-", "-" });
|
||||
|
||||
Console_Utilities.FormatAndAlignTable(data, TableFormat.CENTER_EACH_COLUMN_BASED);
|
||||
return data;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine("Failed to execute command: listplugs\nReason: " + exception.Message);
|
||||
Functions.WriteErrFile(exception.ToString());
|
||||
Config.Logger.Log("Failed to execute command: listplugs\nReason: " + exception.Message, this,
|
||||
LogLevel.ERROR);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<VersionString?> GetVersionOfPackageFromWeb(string pakName)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -108,7 +124,7 @@ public class PluginsManager
|
||||
for (var i = 0; i < len; i++)
|
||||
{
|
||||
var contents = lines[i].Split(',');
|
||||
if (contents[0] == name)
|
||||
if (contents[0].ToLowerInvariant() == name.ToLowerInvariant())
|
||||
{
|
||||
if (contents.Length == 6)
|
||||
return new[] { contents[2], contents[3], contents[5] };
|
||||
@@ -120,10 +136,10 @@ public class PluginsManager
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine("Failed to execute command: listplugs\nReason: " + exception.Message);
|
||||
Functions.WriteErrFile(exception.ToString());
|
||||
Config.Logger.Log("Failed to execute command: listplugs\nReason: " + exception.Message, this,
|
||||
LogLevel.ERROR);
|
||||
}
|
||||
|
||||
return new string[] { null!, null!, null! };
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using PluginManager.Online.Helpers;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using PluginManager.Others;
|
||||
using PluginManager.Online.Helpers;
|
||||
|
||||
namespace PluginManager.Online;
|
||||
|
||||
namespace PluginManager.Online
|
||||
{
|
||||
public static class ServerCom
|
||||
{
|
||||
/// <summary>
|
||||
@@ -18,8 +17,8 @@ namespace PluginManager.Online
|
||||
/// <returns></returns>
|
||||
public static async Task<List<string>> ReadTextFromURL(string link)
|
||||
{
|
||||
string response = await OnlineFunctions.DownloadStringAsync(link);
|
||||
string[] lines = response.Split('\n');
|
||||
var response = await OnlineFunctions.DownloadStringAsync(link);
|
||||
var lines = response.Split('\n');
|
||||
return lines.ToList();
|
||||
}
|
||||
|
||||
@@ -30,9 +29,11 @@ namespace PluginManager.Online
|
||||
/// <param name="location">The location where to store the downloaded data</param>
|
||||
/// <param name="progress">The <see cref="IProgress{T}" /> to track the download</param>
|
||||
/// <returns></returns>
|
||||
public static async Task DownloadFileAsync(string URL, string location, IProgress<float> progress, IProgress<long>? downloadedBytes = null)
|
||||
public static async Task DownloadFileAsync(
|
||||
string URL, string location, IProgress<float> progress,
|
||||
IProgress<long>? downloadedBytes)
|
||||
{
|
||||
using (var client = new System.Net.Http.HttpClient())
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
client.Timeout = TimeSpan.FromMinutes(5);
|
||||
|
||||
@@ -43,47 +44,8 @@ namespace PluginManager.Online
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download file from url
|
||||
/// </summary>
|
||||
/// <param name="URL">The url to the file</param>
|
||||
/// <param name="location">The location where to store the downloaded data</param>
|
||||
/// <returns></returns>
|
||||
public static async Task DownloadFileAsync(string URL, string location)
|
||||
public static async Task DownloadFileAsync(string URl, string location, IProgress<float> progress)
|
||||
{
|
||||
bool isDownloading = true;
|
||||
float c_progress = 0;
|
||||
|
||||
Console_Utilities.ProgressBar pbar = new Console_Utilities.ProgressBar(ProgressBarType.NORMAL) { Max = 100f, NoColor = true };
|
||||
|
||||
IProgress<float> progress = new Progress<float>(percent => { c_progress = percent; });
|
||||
|
||||
|
||||
Task updateProgressBarTask = new Task(() =>
|
||||
{
|
||||
while (isDownloading)
|
||||
{
|
||||
pbar.Update(c_progress);
|
||||
if (c_progress == 100f)
|
||||
break;
|
||||
Thread.Sleep(500);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
new Thread(updateProgressBarTask.Start).Start();
|
||||
await DownloadFileAsync(URL, location, progress);
|
||||
|
||||
|
||||
c_progress = pbar.Max;
|
||||
pbar.Update(100f);
|
||||
isDownloading = false;
|
||||
}
|
||||
public static async Task DownloadFileNoProgressAsync(string URL, string location)
|
||||
{
|
||||
IProgress<float> progress = new Progress<float>();
|
||||
await DownloadFileAsync(URL, location, progress);
|
||||
}
|
||||
|
||||
await DownloadFileAsync(URl, location, progress, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
using PluginManager.Items;
|
||||
using PluginManager.Online.Helpers;
|
||||
using PluginManager.Others;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PluginManager.Online.Updates
|
||||
{
|
||||
public class PluginUpdater
|
||||
{
|
||||
public static async Task<bool> CheckForUpdates(string pakName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var webV = await VersionString.GetVersionOfPackageFromWeb(pakName);
|
||||
var local = VersionString.GetVersionOfPackage(pakName);
|
||||
|
||||
if (local is null) return true;
|
||||
if (webV is null) return false;
|
||||
|
||||
if (webV == local) return false;
|
||||
if (webV > local) return true;
|
||||
}
|
||||
catch (Exception ex) { Console.WriteLine(ex.Message); }
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static async Task<Update> DownloadUpdateInfo(string pakName)
|
||||
{
|
||||
string url = "https://raw.githubusercontent.com/Wizzy69/installer/discord-bot-files/Versions";
|
||||
List<string> info = await ServerCom.ReadTextFromURL(url);
|
||||
VersionString? version = await VersionString.GetVersionOfPackageFromWeb(pakName);
|
||||
|
||||
if (version is null) return Update.Empty;
|
||||
Update update = new Update(pakName, string.Join('\n', info), version);
|
||||
return update;
|
||||
}
|
||||
|
||||
public static async Task Download(string pakName)
|
||||
{
|
||||
Console_Utilities.WriteColorText("An update was found for &g" + pakName + "&c. Version: &r" + (await VersionString.GetVersionOfPackageFromWeb(pakName))?.ToShortString() + "&c. Current Version: &y" + VersionString.GetVersionOfPackage(pakName)?.ToShortString());
|
||||
await ConsoleCommandsHandler.ExecuteCommad("dwplug " + pakName);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using PluginManager.Online.Helpers;
|
||||
|
||||
namespace PluginManager.Online.Updates
|
||||
{
|
||||
public class Update
|
||||
{
|
||||
public static Update Empty = new Update(null, null, null);
|
||||
public string pakName;
|
||||
public string UpdateMessage;
|
||||
|
||||
public VersionString newVersion;
|
||||
|
||||
private bool isEmpty;
|
||||
|
||||
public Update(string pakName, string updateMessage, VersionString newVersion)
|
||||
{
|
||||
this.pakName = pakName;
|
||||
UpdateMessage = updateMessage;
|
||||
this.newVersion = newVersion;
|
||||
|
||||
if (pakName is null && updateMessage is null && newVersion is null)
|
||||
isEmpty = true;
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (isEmpty)
|
||||
throw new System.Exception("The update is EMPTY. Can not print information about an empty update !");
|
||||
return $"Package Name: {this.pakName}\n" +
|
||||
$"Update Message: {UpdateMessage}\n" +
|
||||
$"Version: {newVersion.ToString()}";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
58
PluginManager/Others/Actions/InternalActionsManager.cs
Normal file
58
PluginManager/Others/Actions/InternalActionsManager.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using PluginManager.Interfaces;
|
||||
using PluginManager.Loaders;
|
||||
|
||||
namespace PluginManager.Others.Actions;
|
||||
|
||||
public class InternalActionManager
|
||||
{
|
||||
public Dictionary<string, ICommandAction> Actions = new();
|
||||
public ActionsLoader loader;
|
||||
|
||||
public InternalActionManager(string path, string extension)
|
||||
{
|
||||
loader = new ActionsLoader(path, extension);
|
||||
}
|
||||
|
||||
public async Task Initialize()
|
||||
{
|
||||
loader.ActionLoadedEvent += OnActionLoaded;
|
||||
var m_actions = await loader.Load();
|
||||
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;
|
||||
}
|
||||
|
||||
Config.Logger.Log($"Action {name} loaded successfully", LogLevel.INFO, true);
|
||||
}
|
||||
|
||||
public async Task<string> Execute(string actionName, params string[]? args)
|
||||
{
|
||||
if (!Actions.ContainsKey(actionName))
|
||||
{
|
||||
Config.Logger.Log($"Action {actionName} not found", "InternalActionManager", LogLevel.WARNING, true);
|
||||
return "Action not found";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Actions[actionName].Execute(args);
|
||||
return "Action executed";
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Config.Logger.Log(e.Message, "InternalActionManager", LogLevel.ERROR);
|
||||
return e.Message;
|
||||
}
|
||||
}
|
||||
}
|
||||
179
PluginManager/Others/ArchiveManager.cs
Normal file
179
PluginManager/Others/ArchiveManager.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PluginManager.Others;
|
||||
|
||||
public static class ArchiveManager
|
||||
{
|
||||
private static string? archiveFolder;
|
||||
public static bool isInitialized { get; private set; }
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
if (isInitialized) throw new Exception("ArchiveManager is already initialized");
|
||||
|
||||
if (!Config.AppSettings.ContainsKey("ArchiveFolder"))
|
||||
Config.AppSettings["ArchiveFolder"] = "./Data/PAKS/";
|
||||
|
||||
archiveFolder = Config.AppSettings["ArchiveFolder"];
|
||||
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a file from a zip archive. The output is a byte array
|
||||
/// </summary>
|
||||
/// <param name="fileName">The file name in the archive</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>
|
||||
public async static Task<byte[]?> ReadStreamFromPakAsync(string fileName, string archName)
|
||||
{
|
||||
if (!isInitialized) throw new Exception("ArchiveManager is not initialized");
|
||||
|
||||
archName = archiveFolder + archName;
|
||||
|
||||
if (!File.Exists(archName))
|
||||
throw new Exception("Failed to load file !");
|
||||
|
||||
byte[]? data = null;
|
||||
|
||||
using (var zip = ZipFile.OpenRead(archName))
|
||||
{
|
||||
var entry = zip.Entries.FirstOrDefault(entry => entry.FullName == fileName || entry.Name == fileName);
|
||||
if (entry is null) throw new Exception("File not found in archive");
|
||||
|
||||
var MemoryStream = new MemoryStream();
|
||||
|
||||
var stream = entry.Open();
|
||||
await stream.CopyToAsync(MemoryStream);
|
||||
data = MemoryStream.ToArray();
|
||||
|
||||
stream.Close();
|
||||
MemoryStream.Close();
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read data from a file that is inside an archive (ZIP format)
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// <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)
|
||||
{
|
||||
if (!isInitialized) throw new Exception("ArchiveManager is not initialized");
|
||||
archFile = archiveFolder + archFile;
|
||||
if (!File.Exists(archFile))
|
||||
throw new Exception("Failed to load file !");
|
||||
|
||||
try
|
||||
{
|
||||
string? textValue = null;
|
||||
using (var fs = new FileStream(archFile, FileMode.Open))
|
||||
using (var zip = new ZipArchive(fs, ZipArchiveMode.Read))
|
||||
{
|
||||
foreach (var entry in zip.Entries)
|
||||
if (entry.Name == FileName || entry.FullName == FileName)
|
||||
using (var s = entry.Open())
|
||||
using (var reader = new StreamReader(s))
|
||||
{
|
||||
textValue = await reader.ReadToEndAsync();
|
||||
reader.Close();
|
||||
s.Close();
|
||||
fs.Close();
|
||||
}
|
||||
}
|
||||
|
||||
return textValue;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Config.Logger.Log(ex.Message, "Archive Manager", LogLevel.ERROR); // Write the error to a file
|
||||
await Task.Delay(100);
|
||||
return await ReadFromPakAsync(FileName, archFile);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract zip to location
|
||||
/// </summary>
|
||||
/// <param name="zip">The zip location</param>
|
||||
/// <param name="folder">The target location</param>
|
||||
/// <param name="progress">The progress that is updated as a file is processed</param>
|
||||
/// <param name="type">The type of progress</param>
|
||||
/// <returns></returns>
|
||||
public static async Task ExtractArchive(
|
||||
string zip, string folder, IProgress<float> progress,
|
||||
UnzipProgressType type)
|
||||
{
|
||||
if (!isInitialized) throw new Exception("ArchiveManager is not initialized");
|
||||
Directory.CreateDirectory(folder);
|
||||
using (var archive = ZipFile.OpenRead(zip))
|
||||
{
|
||||
if (type == UnzipProgressType.PERCENTAGE_FROM_NUMBER_OF_FILES)
|
||||
{
|
||||
var totalZIPFiles = archive.Entries.Count();
|
||||
var currentZIPFile = 0;
|
||||
foreach (var entry in archive.Entries)
|
||||
{
|
||||
if (entry.FullName.EndsWith("/")) // it is a folder
|
||||
Directory.CreateDirectory(Path.Combine(folder, entry.FullName));
|
||||
|
||||
else
|
||||
try
|
||||
{
|
||||
entry.ExtractToFile(Path.Combine(folder, entry.FullName), true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Config.Logger.Log($"Failed to extract {entry.Name}. Exception: {ex.Message}",
|
||||
"Archive Manager", LogLevel.ERROR);
|
||||
}
|
||||
|
||||
currentZIPFile++;
|
||||
await Task.Delay(10);
|
||||
if (progress != null)
|
||||
progress.Report((float)currentZIPFile / totalZIPFiles * 100);
|
||||
}
|
||||
}
|
||||
else if (type == UnzipProgressType.PERCENTAGE_FROM_TOTAL_SIZE)
|
||||
{
|
||||
ulong zipSize = 0;
|
||||
|
||||
foreach (var entry in archive.Entries)
|
||||
zipSize += (ulong)entry.CompressedLength;
|
||||
|
||||
ulong currentSize = 0;
|
||||
foreach (var entry in archive.Entries)
|
||||
{
|
||||
if (entry.FullName.EndsWith("/"))
|
||||
{
|
||||
Directory.CreateDirectory(Path.Combine(folder, entry.FullName));
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
entry.ExtractToFile(Path.Combine(folder, entry.FullName), true);
|
||||
currentSize += (ulong)entry.CompressedLength;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Config.Logger.Log($"Failed to extract {entry.Name}. Exception: {ex.Message}",
|
||||
"Archive Manager", LogLevel.ERROR);
|
||||
}
|
||||
|
||||
await Task.Delay(10);
|
||||
if (progress != null)
|
||||
progress.Report((float)currentSize / zipSize * 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
|
||||
namespace PluginManager.Others;
|
||||
|
||||
/// <summary>
|
||||
/// A class that handles the sending of messages to the user.
|
||||
/// </summary>
|
||||
public static class ChannelManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the text channel by name from server
|
||||
/// </summary>
|
||||
/// <param name="server">The server</param>
|
||||
/// <param name="name">The channel name</param>
|
||||
/// <returns>
|
||||
/// <see cref="IGuildChannel" />
|
||||
/// </returns>
|
||||
public static IGuildChannel GetTextChannel(this IGuild server, string name)
|
||||
{
|
||||
return server.GetTextChannel(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the voice channel by name from server
|
||||
/// </summary>
|
||||
/// <param name="server">The server</param>
|
||||
/// <param name="name">The channel name</param>
|
||||
/// <returns>
|
||||
/// <see cref="IGuildChannel" />
|
||||
/// </returns>
|
||||
public static IGuildChannel GetVoiceChannel(this IGuild server, string name)
|
||||
{
|
||||
return server.GetVoiceChannel(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the DM channel between <see cref="Discord.WebSocket.DiscordSocketClient" /> and <see cref="IGuildUser" />
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <returns>
|
||||
/// <see cref="IDMChannel" />
|
||||
/// </returns>
|
||||
public static async Task<IDMChannel> GetDMChannel(IGuildUser user)
|
||||
{
|
||||
return await user.CreateDMChannelAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the channel where the message was sent
|
||||
/// </summary>
|
||||
/// <param name="message">The message</param>
|
||||
/// <returns>
|
||||
/// <see cref="IChannel" />
|
||||
/// </returns>
|
||||
public static IChannel GetChannel(IMessage message)
|
||||
{
|
||||
return message.Channel;
|
||||
}
|
||||
}
|
||||
@@ -1,324 +0,0 @@
|
||||
using Discord;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PluginManager.Others
|
||||
{
|
||||
public static class Console_Utilities
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
if (!Config.ContainsKey("TableVariables"))
|
||||
Config.AddValueToVariables("TableVariables", new Dictionary<string, string> { { "DefaultSpace", "3" } }, false);
|
||||
if (!Config.ContainsKey("ColorDataBase"))
|
||||
Config.AddValueToVariables("ColorDataBase", new Dictionary<char, ConsoleColor>()
|
||||
{
|
||||
{ 'g', ConsoleColor.Green },
|
||||
{ 'b', ConsoleColor.Blue },
|
||||
{ 'r', ConsoleColor.Red },
|
||||
{ 'm', ConsoleColor.Magenta },
|
||||
{ 'y', ConsoleColor.Yellow },
|
||||
}, false
|
||||
);
|
||||
|
||||
if (!Config.ContainsKey("ColorPrefix"))
|
||||
Config.AddValueToVariables("ColorPrefix", '&', false);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Progress bar object
|
||||
/// </summary>
|
||||
public class ProgressBar
|
||||
{
|
||||
public ProgressBar(ProgressBarType type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public float Max { get; init; }
|
||||
public ConsoleColor Color { get; init; }
|
||||
public bool NoColor { get; init; }
|
||||
public ProgressBarType type { get; set; }
|
||||
|
||||
private int BarLength = 32;
|
||||
private int position = 1;
|
||||
private bool positive = true;
|
||||
|
||||
private bool isRunning;
|
||||
|
||||
public async void Start()
|
||||
{
|
||||
if (type != ProgressBarType.NO_END)
|
||||
throw new Exception("Only NO_END progress bar can use this method");
|
||||
if (isRunning)
|
||||
throw new Exception("This progress bar is already running");
|
||||
|
||||
isRunning = true;
|
||||
while (isRunning)
|
||||
{
|
||||
UpdateNoEnd();
|
||||
await System.Threading.Tasks.Task.Delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (type != ProgressBarType.NO_END)
|
||||
throw new Exception("Only NO_END progress bar can use this method");
|
||||
if (!isRunning)
|
||||
throw new Exception("Can not stop a progressbar that did not start");
|
||||
isRunning = false;
|
||||
}
|
||||
|
||||
public void Update(float progress)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ProgressBarType.NORMAL:
|
||||
UpdateNormal(progress);
|
||||
return;
|
||||
case ProgressBarType.NO_END:
|
||||
if (progress <= 99.9f)
|
||||
UpdateNoEnd();
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateNoEnd()
|
||||
{
|
||||
Console.CursorLeft = 0;
|
||||
Console.Write("[");
|
||||
for (int i = 1; i <= position; i++)
|
||||
Console.Write(" ");
|
||||
Console.Write("<==()==>");
|
||||
position += positive ? 1 : -1;
|
||||
for (int i = position; i <= BarLength - 1 - (positive ? 0 : 2); i++)
|
||||
Console.Write(" ");
|
||||
Console.Write("]");
|
||||
|
||||
|
||||
if (position == BarLength - 1 || position == 1)
|
||||
positive = !positive;
|
||||
}
|
||||
|
||||
private void UpdateNormal(float progress)
|
||||
{
|
||||
Console.CursorLeft = 0;
|
||||
Console.Write("[");
|
||||
Console.CursorLeft = BarLength;
|
||||
Console.Write("]");
|
||||
Console.CursorLeft = 1;
|
||||
float onechunk = 30.0f / Max;
|
||||
|
||||
int position = 1;
|
||||
|
||||
for (int i = 0; i < onechunk * progress; i++)
|
||||
{
|
||||
Console.BackgroundColor = NoColor ? ConsoleColor.Black : this.Color;
|
||||
Console.CursorLeft = position++;
|
||||
Console.Write("#");
|
||||
}
|
||||
|
||||
for (int i = position; i < BarLength; i++)
|
||||
{
|
||||
Console.BackgroundColor = NoColor ? ConsoleColor.Black : ConsoleColor.DarkGray;
|
||||
Console.CursorLeft = position++;
|
||||
Console.Write(" ");
|
||||
}
|
||||
|
||||
Console.CursorLeft = BarLength + 4;
|
||||
Console.BackgroundColor = ConsoleColor.Black;
|
||||
if (progress.CanAproximateTo(Max))
|
||||
Console.Write(progress + " % ✓");
|
||||
else
|
||||
Console.Write(MathF.Round(progress, 2) + " % ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static bool CanAproximateTo(this float f, float y) => (MathF.Abs(f - y) < 0.000001);
|
||||
|
||||
|
||||
/// <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)
|
||||
{
|
||||
char tableLine = '-';
|
||||
char tableCross = '+';
|
||||
char tableWall = '|';
|
||||
|
||||
int[] len = new int[data[0].Length];
|
||||
foreach (var line in data)
|
||||
for (int i = 0; i < line.Length; i++)
|
||||
if (line[i].Length > len[i])
|
||||
len[i] = line[i].Length;
|
||||
|
||||
|
||||
foreach (string[] row in data)
|
||||
{
|
||||
if (row[0][0] == tableLine)
|
||||
Console.Write(tableCross);
|
||||
else
|
||||
Console.Write(tableWall);
|
||||
for (int l = 0; l < row.Length; l++)
|
||||
{
|
||||
if (row[l][0] == tableLine)
|
||||
{
|
||||
for (int 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
|
||||
{
|
||||
int lenHalf = row[l].Length / 2;
|
||||
for (int i = 0; i < ((len[l] + 4) / 2 - lenHalf); ++i)
|
||||
Console.Write(" ");
|
||||
Console.Write(row[l]);
|
||||
for (int 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)
|
||||
{
|
||||
int maxLen = 0;
|
||||
foreach (string[] row in data)
|
||||
foreach (string s in row)
|
||||
if (s.Length > maxLen)
|
||||
maxLen = s.Length;
|
||||
|
||||
int div = (maxLen + 4) / 2;
|
||||
|
||||
foreach (string[] row in data)
|
||||
{
|
||||
Console.Write("\t");
|
||||
if (row[0] == "-")
|
||||
Console.Write("+");
|
||||
else
|
||||
Console.Write("|");
|
||||
|
||||
foreach (string s in row)
|
||||
{
|
||||
if (s == "-")
|
||||
{
|
||||
for (int i = 0; i < maxLen + 4; ++i)
|
||||
Console.Write("-");
|
||||
}
|
||||
else if (s.Length == maxLen)
|
||||
{
|
||||
Console.Write(" ");
|
||||
Console.Write(s);
|
||||
Console.Write(" ");
|
||||
}
|
||||
else
|
||||
{
|
||||
int lenHalf = s.Length / 2;
|
||||
for (int i = 0; i < div - lenHalf; ++i)
|
||||
Console.Write(" ");
|
||||
Console.Write(s);
|
||||
for (int 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)
|
||||
{
|
||||
int[] widths = new int[data[0].Length];
|
||||
int space_between_columns = int.Parse(Config.GetValue<Dictionary<string, string>>("TableVariables")?["DefaultSpace"]!);
|
||||
for (int i = 0; i < data.Count; i++)
|
||||
{
|
||||
for (int j = 0; j < data[i].Length; j++)
|
||||
{
|
||||
if (data[i][j].Length > widths[j])
|
||||
widths[j] = data[i][j].Length;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < data.Count; i++)
|
||||
{
|
||||
for (int j = 0; j < data[i].Length; j++)
|
||||
{
|
||||
if (data[i][j] == "-")
|
||||
data[i][j] = " ";
|
||||
Console.Write(data[i][j]);
|
||||
for (int 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)
|
||||
{
|
||||
ConsoleColor initialForeGround = Console.ForegroundColor;
|
||||
char[] input = text.ToCharArray();
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
if (input[i] == Config.GetValue<char>("ColorPrefix"))
|
||||
{
|
||||
if (i + 1 < input.Length)
|
||||
{
|
||||
if (Config.GetValue<Dictionary<char, ConsoleColor>>("ColorDataBase")!.ContainsKey(input[i + 1]))
|
||||
{
|
||||
Console.ForegroundColor = Config.GetValue<Dictionary<char, ConsoleColor>>("ColorDataBase")![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();
|
||||
}
|
||||
}
|
||||
}
|
||||
20
PluginManager/Others/DBCommandExecutingArguments.cs
Normal file
20
PluginManager/Others/DBCommandExecutingArguments.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Discord.Commands;
|
||||
|
||||
namespace PluginManager.Others;
|
||||
|
||||
public class DBCommandExecutingArguments
|
||||
{
|
||||
public DBCommandExecutingArguments(
|
||||
SocketCommandContext context, string cleanContent, string commandUsed, string[]? arguments)
|
||||
{
|
||||
this.context = context;
|
||||
this.cleanContent = cleanContent;
|
||||
this.commandUsed = commandUsed;
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
public SocketCommandContext context { get; init; }
|
||||
public string cleanContent { get; init; }
|
||||
public string commandUsed { get; init; }
|
||||
public string[]? arguments { get; init; }
|
||||
}
|
||||
@@ -1,36 +1,47 @@
|
||||
using PluginManager.Interfaces;
|
||||
|
||||
namespace PluginManager.Others;
|
||||
namespace PluginManager.Others;
|
||||
|
||||
/// <summary>
|
||||
/// A list of operating systems
|
||||
/// </summary>
|
||||
public enum OperatingSystem
|
||||
{
|
||||
WINDOWS, LINUX, MAC_OS, UNKNOWN
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A list with all errors
|
||||
/// </summary>
|
||||
public enum Error
|
||||
{
|
||||
UNKNOWN_ERROR, GUILD_NOT_FOUND, STREAM_NOT_FOUND, INVALID_USER, INVALID_CHANNEL, INVALID_PERMISSIONS
|
||||
WINDOWS,
|
||||
LINUX,
|
||||
MAC_OS,
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The output log type
|
||||
/// </summary>
|
||||
public enum OutputLogLevel { NONE, INFO, WARNING, ERROR, CRITICAL }
|
||||
public enum LogLevel
|
||||
{
|
||||
INFO,
|
||||
WARNING,
|
||||
ERROR,
|
||||
CRITICAL
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plugin Type
|
||||
/// </summary>
|
||||
public enum PluginType { Command, Event, Unknown }
|
||||
public enum UnzipProgressType
|
||||
{
|
||||
PERCENTAGE_FROM_NUMBER_OF_FILES,
|
||||
PERCENTAGE_FROM_TOTAL_SIZE
|
||||
}
|
||||
|
||||
public enum UnzipProgressType { PercentageFromNumberOfFiles, PercentageFromTotalSize }
|
||||
public enum SaveType
|
||||
{
|
||||
NORMAL,
|
||||
BACKUP
|
||||
}
|
||||
|
||||
public enum TableFormat { CENTER_EACH_COLUMN_BASED, CENTER_OVERALL_LENGTH, DEFAULT }
|
||||
public enum InternalActionRunType
|
||||
{
|
||||
ON_STARTUP,
|
||||
ON_CALL
|
||||
}
|
||||
|
||||
public enum SaveType { NORMAL, BACKUP }
|
||||
public enum ProgressBarType { NORMAL, NO_END }
|
||||
internal enum ExceptionExitCode : int
|
||||
{
|
||||
CONFIG_FAILED_TO_LOAD = 1,
|
||||
CONFIG_KEY_NOT_FOUND = 2,
|
||||
}
|
||||
|
||||
91
PluginManager/Others/Exceptions/ConfigFailedToLoad.cs
Normal file
91
PluginManager/Others/Exceptions/ConfigFailedToLoad.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
75
PluginManager/Others/Exceptions/ConfigNoKeyWasPresent.cs
Normal file
75
PluginManager/Others/Exceptions/ConfigNoKeyWasPresent.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,18 +1,14 @@
|
||||
using System.IO.Compression;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using Discord.WebSocket;
|
||||
using PluginManager.Items;
|
||||
using System.Threading;
|
||||
using System.Text.Json;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
|
||||
namespace PluginManager.Others;
|
||||
|
||||
namespace PluginManager.Others
|
||||
{
|
||||
/// <summary>
|
||||
/// A special class with functions
|
||||
/// </summary>
|
||||
@@ -20,119 +16,18 @@ namespace PluginManager.Others
|
||||
{
|
||||
/// <summary>
|
||||
/// The location for the Resources folder
|
||||
/// String: ./Data/Resources/
|
||||
/// </summary>
|
||||
public static readonly string dataFolder = @"./Data/Resources/";
|
||||
|
||||
/// <summary>
|
||||
/// The location for all logs
|
||||
/// </summary>
|
||||
public static readonly string logFolder = @"./Data/Output/Logs/";
|
||||
|
||||
/// <summary>
|
||||
/// The location for all errors
|
||||
/// </summary>
|
||||
public static readonly string errFolder = @"./Data/Output/Errors/";
|
||||
|
||||
/// <summary>
|
||||
/// Archives folder
|
||||
/// </summary>
|
||||
public static readonly string pakFolder = @"./Data/PAKS/";
|
||||
|
||||
/// <summary>
|
||||
/// Beta testing folder
|
||||
/// </summary>
|
||||
public static readonly string betaFolder = @"./Data/BetaTest/";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Read data from a file that is inside an archive (ZIP format)
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// <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 Color RandomColor
|
||||
{
|
||||
archFile = pakFolder + archFile;
|
||||
if (!File.Exists(archFile))
|
||||
throw new Exception("Failed to load file !");
|
||||
|
||||
try
|
||||
get
|
||||
{
|
||||
string textValue = null;
|
||||
using (var fs = new FileStream(archFile, FileMode.Open))
|
||||
using (var zip = new ZipArchive(fs, ZipArchiveMode.Read))
|
||||
foreach (var entry in zip.Entries)
|
||||
{
|
||||
if (entry.Name == FileName || entry.FullName == FileName)
|
||||
{
|
||||
using (Stream s = entry.Open())
|
||||
using (StreamReader reader = new StreamReader(s))
|
||||
{
|
||||
textValue = await reader.ReadToEndAsync();
|
||||
reader.Close();
|
||||
s.Close();
|
||||
fs.Close();
|
||||
var random = new Random();
|
||||
return new Color(random.Next(0, 255), random.Next(0, 255), random.Next(0, 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
return textValue;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Task.Delay(100);
|
||||
return await ReadFromPakAsync(FileName, archFile);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Write logs to file
|
||||
/// </summary>
|
||||
/// <param name="LogMessage">The message to be wrote</param>
|
||||
public static void WriteLogFile(string LogMessage)
|
||||
{
|
||||
string logsPath = logFolder + $"{DateTime.Today.ToShortDateString().Replace("/", "-").Replace("\\", "-")} Log.txt";
|
||||
Directory.CreateDirectory(logFolder);
|
||||
File.AppendAllText(logsPath, LogMessage + " \n");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write error to file
|
||||
/// </summary>
|
||||
/// <param name="ErrMessage">The message to be wrote</param>
|
||||
public static void WriteErrFile(string ErrMessage)
|
||||
{
|
||||
string errPath = errFolder + $"{DateTime.Today.ToShortDateString().Replace("/", "-").Replace("\\", "-")} Error.txt";
|
||||
Directory.CreateDirectory(errFolder);
|
||||
File.AppendAllText(errPath, ErrMessage + " \n");
|
||||
}
|
||||
|
||||
public static void WriteErrFile(this Exception ex)
|
||||
{
|
||||
WriteErrFile(ex.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merge one array of strings into one string
|
||||
/// </summary>
|
||||
/// <param name="s">The array of strings</param>
|
||||
/// <param name="indexToStart">The index from where the merge should start (included)</param>
|
||||
/// <returns>A string built based on the array</returns>
|
||||
public static string MergeStrings(this string[] s, int indexToStart)
|
||||
{
|
||||
string r = "";
|
||||
int len = s.Length;
|
||||
if (len <= indexToStart) return "";
|
||||
for (int i = indexToStart; i < len - 1; ++i)
|
||||
{
|
||||
r += s[i] + " ";
|
||||
}
|
||||
|
||||
r += s[len - 1];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the Operating system you are runnin on
|
||||
@@ -140,18 +35,12 @@ namespace PluginManager.Others
|
||||
/// <returns>An Operating system</returns>
|
||||
public static OperatingSystem GetOperatingSystem()
|
||||
{
|
||||
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) return OperatingSystem.WINDOWS;
|
||||
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux)) return OperatingSystem.LINUX;
|
||||
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX)) return OperatingSystem.MAC_OS;
|
||||
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;
|
||||
}
|
||||
|
||||
public static List<string> GetArguments(SocketMessage message)
|
||||
{
|
||||
Command command = new Command(message);
|
||||
return command.Arguments;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy one Stream to another <see langword="async" />
|
||||
/// </summary>
|
||||
@@ -164,209 +53,46 @@ namespace PluginManager.Others
|
||||
/// <exception cref="ArgumentOutOfRangeException">Triggered if <paramref name="bufferSize" /> is less then or equal to 0</exception>
|
||||
/// <exception cref="InvalidOperationException">Triggered if <paramref name="stream" /> is not readable</exception>
|
||||
/// <exception cref="ArgumentException">Triggered in <paramref name="destination" /> is not writable</exception>
|
||||
public static async Task CopyToOtherStreamAsync(this Stream stream, Stream destination, int bufferSize, IProgress<long>? progress = null, CancellationToken cancellationToken = default)
|
||||
public static async Task CopyToOtherStreamAsync(
|
||||
this Stream stream, Stream destination, int bufferSize,
|
||||
IProgress<long>? progress = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (stream == null) throw new ArgumentNullException(nameof(stream));
|
||||
if (destination == null) throw new ArgumentNullException(nameof(destination));
|
||||
if (bufferSize <= 0) throw new ArgumentOutOfRangeException(nameof(bufferSize));
|
||||
if (!stream.CanRead) throw new InvalidOperationException("The stream is not readable.");
|
||||
if (!destination.CanWrite) throw new ArgumentException("Destination stream is not writable", nameof(destination));
|
||||
if (!destination.CanWrite)
|
||||
throw new ArgumentException("Destination stream is not writable", nameof(destination));
|
||||
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
var buffer = new byte[bufferSize];
|
||||
long totalBytesRead = 0;
|
||||
int bytesRead;
|
||||
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
|
||||
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)
|
||||
.ConfigureAwait(false)) != 0)
|
||||
{
|
||||
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
|
||||
totalBytesRead += bytesRead;
|
||||
progress?.Report(totalBytesRead);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract zip to location
|
||||
/// </summary>
|
||||
/// <param name="zip">The zip location</param>
|
||||
/// <param name="folder">The target location</param>
|
||||
/// <param name="progress">The progress that is updated as a file is processed</param>
|
||||
/// <param name="type">The type of progress</param>
|
||||
/// <returns></returns>
|
||||
public static async Task ExtractArchive(string zip, string folder, IProgress<float> progress, UnzipProgressType type)
|
||||
{
|
||||
Directory.CreateDirectory(folder);
|
||||
using (ZipArchive archive = ZipFile.OpenRead(zip))
|
||||
{
|
||||
if (type == UnzipProgressType.PercentageFromNumberOfFiles)
|
||||
{
|
||||
int totalZIPFiles = archive.Entries.Count();
|
||||
int currentZIPFile = 0;
|
||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||
{
|
||||
if (entry.FullName.EndsWith("/")) // it is a folder
|
||||
Directory.CreateDirectory(Path.Combine(folder, entry.FullName));
|
||||
|
||||
else
|
||||
try
|
||||
{
|
||||
entry.ExtractToFile(Path.Combine(folder, entry.FullName), true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Failed to extract {entry.Name}. Exception: {ex.Message}");
|
||||
}
|
||||
|
||||
currentZIPFile++;
|
||||
await Task.Delay(10);
|
||||
if (progress != null)
|
||||
progress.Report((float)currentZIPFile / totalZIPFiles * 100);
|
||||
}
|
||||
}
|
||||
else if (type == UnzipProgressType.PercentageFromTotalSize)
|
||||
{
|
||||
ulong zipSize = 0;
|
||||
|
||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||
zipSize += (ulong)entry.CompressedLength;
|
||||
|
||||
ulong currentSize = 0;
|
||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||
{
|
||||
if (entry.FullName.EndsWith("/"))
|
||||
{
|
||||
Directory.CreateDirectory(Path.Combine(folder, entry.FullName));
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
entry.ExtractToFile(Path.Combine(folder, entry.FullName), true);
|
||||
currentSize += (ulong)entry.CompressedLength;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Failed to extract {entry.Name}. Exception: {ex.Message}");
|
||||
}
|
||||
|
||||
await Task.Delay(10);
|
||||
if (progress != null)
|
||||
progress.Report((float)currentSize / zipSize * 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Convert Bytes to highest measurement unit possible
|
||||
/// </summary>
|
||||
/// <param name="bytes">The amount of bytes</param>
|
||||
/// <returns></returns>
|
||||
public static (double, string) ConvertBytes(long bytes)
|
||||
public static T SelectRandomValueOf<T>()
|
||||
{
|
||||
List<string> units = new List<string>()
|
||||
{
|
||||
"B",
|
||||
"KB",
|
||||
"MB",
|
||||
"GB",
|
||||
"TB"
|
||||
};
|
||||
int i = 0;
|
||||
while (bytes >= 1024)
|
||||
{
|
||||
i++;
|
||||
bytes /= 1024;
|
||||
var enums = Enum.GetValues(typeof(T));
|
||||
var random = new Random();
|
||||
return (T)enums.GetValue(random.Next(enums.Length));
|
||||
}
|
||||
|
||||
return (bytes, units[i]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save to JSON file
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The class type</typeparam>
|
||||
/// <param name="file">The file path</param>
|
||||
/// <param name="Data">The values</param>
|
||||
/// <returns></returns>
|
||||
public static async Task SaveToJsonFile<T>(string file, T Data)
|
||||
public static T RandomValue<T>(this T[] values)
|
||||
{
|
||||
MemoryStream str = new MemoryStream();
|
||||
await JsonSerializer.SerializeAsync(str, Data, typeof(T), new JsonSerializerOptions { WriteIndented = true });
|
||||
await File.WriteAllBytesAsync(file, str.ToArray());
|
||||
Random random = new();
|
||||
return values[random.Next(values.Length)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert json text or file to some kind of data
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The data type</typeparam>
|
||||
/// <param name="input">The file or json text</param>
|
||||
/// <returns></returns>
|
||||
public static async Task<T> ConvertFromJson<T>(string input)
|
||||
public static string ToResourcesPath(this string path)
|
||||
{
|
||||
Stream text;
|
||||
if (File.Exists(input))
|
||||
text = new MemoryStream(await File.ReadAllBytesAsync(input));
|
||||
else
|
||||
text = new MemoryStream(Encoding.ASCII.GetBytes(input));
|
||||
text.Position = 0;
|
||||
var obj = await JsonSerializer.DeserializeAsync<T>(text);
|
||||
text.Close();
|
||||
return (obj ?? default)!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if all words from <paramref name="str"/> are in <paramref name="baseString"/><br/>
|
||||
/// This function returns true if<br/>
|
||||
/// 1. The <paramref name="str"/> is part of <paramref name="baseString"/><br/>
|
||||
/// 2. The words (split by a space) of <paramref name="str"/> are located (separately) in <paramref name="baseString"/> <br/>
|
||||
/// <example>
|
||||
/// The following example will return <see langword="TRUE"/><br/>
|
||||
/// <c>STRContains("Hello World !", "I type word Hello and then i typed word World !")</c><br/>
|
||||
/// The following example will return <see langword="TRUE"/><br/>
|
||||
/// <c>STRContains("Hello World !", "I typed Hello World !" </c><br/>
|
||||
/// The following example will return <see langword="TRUE"/><br/>
|
||||
/// <c>STRContains("Hello World", "I type World then Hello")</c><br/>
|
||||
/// The following example will return <see langword="FALSE"/><br/>
|
||||
/// <c>STRContains("Hello World !", "I typed Hello World")</c><br/>
|
||||
/// </example>
|
||||
/// </summary>
|
||||
/// <param name="str">The string you are checking</param>
|
||||
/// <param name="baseString">The main string that should contain <paramref name="str"/></param>
|
||||
/// <returns></returns>
|
||||
public static bool STRContains(this string str, string baseString)
|
||||
{
|
||||
if (baseString.Contains(str)) return true;
|
||||
string[] array = str.Split(' ');
|
||||
foreach (var s in array)
|
||||
if (!baseString.Contains(s))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryReadValueFromJson(string input, string codeName, out JsonElement element)
|
||||
{
|
||||
Stream text;
|
||||
if (File.Exists(input))
|
||||
text = File.OpenRead(input);
|
||||
|
||||
else
|
||||
text = new MemoryStream(Encoding.ASCII.GetBytes(input));
|
||||
|
||||
var jsonObject = JsonDocument.Parse(text);
|
||||
|
||||
var data = jsonObject.RootElement.TryGetProperty(codeName, out element);
|
||||
return data;
|
||||
}
|
||||
|
||||
public static string CreateMD5(string input)
|
||||
{
|
||||
using (MD5 md5 = MD5.Create())
|
||||
{
|
||||
byte[] inputBytes = Encoding.ASCII.GetBytes(input);
|
||||
byte[] hashBytes = md5.ComputeHash(inputBytes);
|
||||
return Convert.ToHexString(hashBytes);
|
||||
}
|
||||
}
|
||||
return Path.Combine(dataFolder, path);
|
||||
}
|
||||
}
|
||||
|
||||
47
PluginManager/Others/JsonManager.cs
Normal file
47
PluginManager/Others/JsonManager.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PluginManager;
|
||||
|
||||
public class JsonManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Save to JSON file
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The class type</typeparam>
|
||||
/// <param name="file">The file path</param>
|
||||
/// <param name="Data">The values</param>
|
||||
/// <returns></returns>
|
||||
public static async Task SaveToJsonFile<T>(string file, T Data)
|
||||
{
|
||||
var str = new MemoryStream();
|
||||
await JsonSerializer.SerializeAsync(str, Data, typeof(T), new JsonSerializerOptions { WriteIndented = true });
|
||||
await File.WriteAllBytesAsync(file, str.ToArray());
|
||||
await str.FlushAsync();
|
||||
str.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert json text or file to some kind of data
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The data type</typeparam>
|
||||
/// <param name="input">The file or json text</param>
|
||||
/// <returns></returns>
|
||||
public static async Task<T> ConvertFromJson<T>(string input)
|
||||
{
|
||||
Stream text;
|
||||
if (File.Exists(input))
|
||||
text = new MemoryStream(await File.ReadAllBytesAsync(input));
|
||||
else
|
||||
text = new MemoryStream(Encoding.ASCII.GetBytes(input));
|
||||
text.Position = 0;
|
||||
|
||||
var obj = await JsonSerializer.DeserializeAsync<T>(text);
|
||||
await text.FlushAsync();
|
||||
text.Close();
|
||||
return (obj ?? default)!;
|
||||
}
|
||||
}
|
||||
76
PluginManager/Others/Logger/DBLogger.cs
Normal file
76
PluginManager/Others/Logger/DBLogger.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
51
PluginManager/Others/Logger/LogMessage.cs
Normal file
51
PluginManager/Others/Logger/LogMessage.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
141
PluginManager/Others/SettingsDictionary.cs
Normal file
141
PluginManager/Others/SettingsDictionary.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using PluginManager.Others.Exceptions;
|
||||
|
||||
namespace PluginManager.Others;
|
||||
|
||||
public class SettingsDictionary<TKey, TValue> : IDictionary<TKey, TValue>
|
||||
{
|
||||
public string? _file { get; }
|
||||
private IDictionary<TKey, TValue>? _dictionary;
|
||||
|
||||
public SettingsDictionary(string? file)
|
||||
{
|
||||
_file = file;
|
||||
if (!LoadFromFile())
|
||||
{
|
||||
ConfigFailedToLoad.CreateError("Failed to load config")
|
||||
.AppendError("The file is empty or does not exist")
|
||||
.IsFatal()
|
||||
.HandleException();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SaveToFile()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_file))
|
||||
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()
|
||||
{
|
||||
return _dictionary!.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable) _dictionary!).GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
this._dictionary!.Add(item);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
this._dictionary!.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
return this._dictionary!.Contains(item);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
this._dictionary!.Add(key, value);
|
||||
}
|
||||
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
return this._dictionary!.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
return this._dictionary!.Remove(key);
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
return this._dictionary!.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this._dictionary!.ContainsKey(key))
|
||||
if(this._dictionary[key] is string s && !string.IsNullOrEmpty(s) && !string.IsNullOrWhiteSpace(s))
|
||||
return this._dictionary[key];
|
||||
|
||||
ConfigNoKeyWasPresent.CreateError($"Key {(key is string ? key : typeof(TKey).Name)} was not present in {_file ?? "config"}")
|
||||
.AppendError("Deleting the file may fix this issue")
|
||||
.IsFatal()
|
||||
.HandleException();
|
||||
|
||||
return default!;
|
||||
}
|
||||
set => this._dictionary![key] = value;
|
||||
}
|
||||
|
||||
public ICollection<TKey> Keys => _dictionary!.Keys;
|
||||
public ICollection<TValue> Values => _dictionary!.Values;
|
||||
}
|
||||
@@ -1,22 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<DebugType>none</DebugType>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="BlankWindow1.xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Discord.Net" Version="3.7.2" />
|
||||
<PackageReference Include="Discord.Net" Version="3.11.0" />
|
||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.118" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
158
README.md
158
README.md
@@ -6,6 +6,8 @@ This project is based on:
|
||||
- [.NET 6 (C#)](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)
|
||||
- [Discord.Net](https://github.com/discord-net/Discord.Net)
|
||||
|
||||
- Some plugins can be found [here](https://github.com/andreitdr/SethPlugins).
|
||||
|
||||
|
||||
## Plugins
|
||||
#### Requirements:
|
||||
@@ -15,84 +17,87 @@ This project is based on:
|
||||
Plugin Types:
|
||||
1. Commands
|
||||
2. Events
|
||||
3. Slash Commands
|
||||
|
||||
### How to create a plugin
|
||||
|
||||
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.
|
||||
|
||||

|
||||
## 1. Commands
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Now, let's add the PluginManager reference. It can be found inside the bot's main folder under
|
||||
`DiscordBot/bin/Debug/net6.0/PluginManager.dll` or `PluginManager/bin/Debug/net6.0/PluginManager.dll`
|
||||
after one successfull build.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
1. Commands
|
||||
|
||||
Commands are loaded when all plugins are loaded into memory. When an user executes the command, only then the Execute function is called.
|
||||
Commands are loaded when all plugins are loaded into memory. The Execute method is called whenever any user (that respects the `requireAdmin` propery) calls the command using the bot prefix and the `Command`.
|
||||
Commands are plugins that allow users to interact with them.
|
||||
Here is an example of class that is a command class
|
||||
Here is an example:
|
||||
```cs
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
|
||||
using PluginManager.Interfaces;
|
||||
|
||||
namespace CMD_Utils
|
||||
namespace LevelingSystem;
|
||||
|
||||
public class LevelCommand : DBCommand
|
||||
{
|
||||
class FlipCoin : DBCommand
|
||||
{
|
||||
public string Command => "flip";
|
||||
public string Command => "level";
|
||||
|
||||
public string Description => "Flip a coin";
|
||||
public List<string> Aliases => new() { "lvl" };
|
||||
|
||||
public string Usage => "flip";
|
||||
public string Description => "Display your current level";
|
||||
|
||||
public bool canUseDM => true;
|
||||
|
||||
public bool canUseServer => true;
|
||||
public string Usage => "level";
|
||||
|
||||
public bool requireAdmin => false;
|
||||
|
||||
public async void Execute(SocketCommandContext context, SocketMessage message, DiscordSocketClient client, bool isDM)
|
||||
public async void ExecuteServer(DBCommandExecutingArguments context)
|
||||
{
|
||||
System.Random random = new System.Random();
|
||||
int r = random.Next(1, 3);
|
||||
if (r == 1)
|
||||
await message.Channel.SendMessageAsync("Heads");
|
||||
else await message.Channel.SendMessageAsync("Tails");
|
||||
//Variables.database is a sql connection that is defined in an auxiliary file in the same napespace as this class
|
||||
object[] user = await Variables.database.ReadDataArrayAsync($"SELECT * FROM Levels WHERE UserID='{context.Message.Author.Id}'");
|
||||
if (user is null)
|
||||
{
|
||||
await context.Channel.SendMessageAsync("You are now unranked !");
|
||||
return;
|
||||
}
|
||||
|
||||
int level = (int)user[1];
|
||||
int exp = (int)user[2];
|
||||
|
||||
var builder = new EmbedBuilder();
|
||||
var r = new Random();
|
||||
builder.WithColor(r.Next(256), r.Next(256), r.Next(256));
|
||||
builder.AddField("Current Level", level, true)
|
||||
.AddField("Current EXP", exp, true)
|
||||
.AddField("Required Exp", (level * 8 + 24).ToString(), true);
|
||||
builder.WithTimestamp(DateTimeOffset.Now);
|
||||
builder.WithAuthor(context.Message.Author.Mention);
|
||||
await context.Channel.SendMessageAsync(embed: builder.Build());
|
||||
}
|
||||
|
||||
//Optional method (tell the bot what should it do if the command is executed from a DM channel)
|
||||
//public async void ExecuteDM(DBCommandExecutingArguments context) {
|
||||
//
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
#### Code description:
|
||||
- Command - The keyword that triggers the execution for the command. This is what players must type in order to execute your command
|
||||
- Aliases - The aliases that can be used instead of the full name to execute the command
|
||||
- Description - The description of your command. Can be anything you like
|
||||
- Usage - The usage of your command. This is what `help [Command]` command will display
|
||||
- canUseDM - true if you plan to let users execute this command in DM chat with bot
|
||||
- canUseServer - true if you plan to let the users execute this command in a server chat
|
||||
- requireAdmin - true if this command requres an user with Administrator permission in the server
|
||||
- Execute () - the function of your command.
|
||||
- ExecuteServer () - the function that is executed only when the command is invoked in a server channel. (optional)
|
||||
- context - the command context
|
||||
- ExecuteDM () - the function that is executed only when the command is invoked in a private (DM) channel. (optional)
|
||||
- context - the command context
|
||||
- message - the message itself
|
||||
- client - the discord bot client
|
||||
- isDM - true if the message was sent from DM chat
|
||||
|
||||
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/Commands/<optional subfolder>/yourDLLName.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
|
||||
there is something wrong in your command's code.
|
||||
|
||||
2. Events
|
||||
## 2. Events
|
||||
|
||||
Events are loaded when all plugins are loaded. At the moment when they are loaded, the Start function is called.
|
||||
Events are used if you want the bot to do something when something happens in server. The following example shows you how to catch when a user joins the server
|
||||
@@ -110,8 +115,6 @@ public class OnUserJoin : DBEvent
|
||||
|
||||
public async void Start(Discord.WebSocket.DiscordSocketClient client)
|
||||
{
|
||||
Console.WriteLine($"Hello World from {name}");
|
||||
|
||||
client.UserJoined += async (user) => {
|
||||
await (await user.CreateDMChannelAsync()).SendMessageAsync("Welcome to server !");
|
||||
};
|
||||
@@ -125,3 +128,68 @@ public class OnUserJoin : DBEvent
|
||||
- Start() - The main body of your event. This is executed when the bot loads all plugins
|
||||
- client - the discord bot client
|
||||
|
||||
|
||||
## 3. Slash Commands
|
||||
|
||||
|
||||
Slash commands are server based commands. They work the same way as normal commands, but they require the `/` prefix as they are integrated
|
||||
with the UI of Discord.
|
||||
Here is an example:
|
||||
```cs
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
|
||||
using PluginManager.Interfaces;
|
||||
|
||||
namespace SlashCommands
|
||||
{
|
||||
public class Random : DBSlashCommand
|
||||
{
|
||||
public string Name => "random";
|
||||
|
||||
public string Description => "Generates a random nunber between 2 values";
|
||||
|
||||
public bool canUseDM => true;
|
||||
|
||||
public List<SlashCommandOptionBuilder> Options => new List<SlashCommandOptionBuilder>()
|
||||
{
|
||||
new SlashCommandOptionBuilder() {Name = "min-value", Description = "Minimum value", IsRequired=true, Type = ApplicationCommandOptionType.Integer, MinValue = 0, MaxValue = int.MaxValue-1},
|
||||
new SlashCommandOptionBuilder() {Name = "max-value", Description = "Maximum value", IsRequired=true, Type=ApplicationCommandOptionType.Integer,MinValue = 0, MaxValue = int.MaxValue-1}
|
||||
};
|
||||
|
||||
public async void ExecuteServer(SocketSlashCommand command)
|
||||
{
|
||||
var rnd = new System.Random();
|
||||
var options = command.Data.Options.ToArray();
|
||||
if (options.Count() != 2)
|
||||
{
|
||||
await command.RespondAsync("Invalid parameters", ephemeral: true);
|
||||
return;
|
||||
}
|
||||
|
||||
Int64 numberOne = (Int64)options[0].Value;
|
||||
Int64 numberTwo = (Int64)options[1].Value;
|
||||
|
||||
await command.RespondAsync("Your generated number is " + rnd.Next((int)numberOne, (int)numberTwo), ephemeral: true);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Code description:
|
||||
- Name - the command name (execute with /{Name})
|
||||
- Description - The description of the command
|
||||
- canUseDM - true id this command can be activated in DM chat, false otherwise
|
||||
- Options - the arguments of the command
|
||||
- ExecuteServer() - this function will be called if the command is invoked in a server channel (optional)
|
||||
- context - the command context
|
||||
- ExecuteDM() - this function will be called if the command is invoked in a DM channel (optional)
|
||||
- context - the command context
|
||||
|
||||
|
||||
## Note:
|
||||
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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.1.32421.90
|
||||
@@ -7,37 +7,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscordBot", "DiscordBot\Di
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PluginManager", "PluginManager\PluginManager.csproj", "{EDD4D9B3-98DD-4367-A09F-D1C5ACB61132}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{1862ABD5-7C30-4F15-A561-45AC8A9CA10E}"
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SethPlugins", "SethPlugins", "{78B6D390-F61A-453F-B38D-E4C054321615}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Events", "Events", "{A290C028-77C4-4D1D-AB43-DDFE6ABD9012}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MusicPlayer", "..\SethPlugins\MusicPlayer\MusicPlayer.csproj", "{1690CBBC-BDC0-4DD8-B701-F8817189D9D5}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Commands", "Commands", "{449FA364-0B72-43FF-B3A3-806E2916200E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CMD_Utils", "Plugins\CMD_Utils\CMD_Utils.csproj", "{E26C87A4-3DD6-4B58-B14B-C8E086B852F9}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MusicCommands", "Plugins\MusicCommands\MusicCommands.csproj", "{B1B4976E-5112-4217-B57B-3A03C5207B6E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EVE_LevelingSystem", "Plugins\EVE_LevelingSystem\EVE_LevelingSystem.csproj", "{EEC445DC-0C4B-43EA-8694-606BA0390B77}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CMD_LevelingSystem", "Plugins\CMD_LevelingSystem\CMD_LevelingSystem.csproj", "{1A4E49FF-9A0A-4C54-AF35-CFFBA64353D9}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Roles", "Plugins\Roles\Roles.csproj", "{954F2AA9-6624-4554-946D-0F17B84487C3}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Others", "Others", "{727BBA0B-9114-4BC8-B9A8-3F461449A564}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Updater", "Plugins\Updater\Updater.csproj", "{24616F7E-E2E9-45A3-8A44-AB51FCD2D525}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeGamesModule", "Plugins\FreeGamesModule\FreeGamesModule.csproj", "{8959C766-414D-4EF8-BC85-9928B30AAF0A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscordBotWindowsUI", "Plugins\DiscordBotWindowsUI\DiscordBotWindowsUI.csproj", "{EFE12083-F9FE-4807-8E39-809E0391BAF0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsUI", "Plugins\WindowsUI\WindowsUI.csproj", "{ECF79CD3-789E-476D-8512-CE0FAF71ADF5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SlashCommands", "Plugins\SlashCommands\SlashCommands.csproj", "{56D7545A-6DCF-4996-A1A5-40180CE9DE10}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Slash", "Slash", "{0B1FD8FA-35D3-4DC1-9D98-6178247B29CA}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SlashRandom", "Plugins\SlashRandom\SlashRandom.csproj", "{23A4778E-A65C-44B7-A82C-AE2A35103E8D}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LevelingSystem", "..\SethPlugins\LevelingSystem\LevelingSystem.csproj", "{BFE3491C-AC01-4252-B242-6451270FC548}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -53,70 +27,21 @@ Global
|
||||
{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.Build.0 = Release|Any CPU
|
||||
{E26C87A4-3DD6-4B58-B14B-C8E086B852F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E26C87A4-3DD6-4B58-B14B-C8E086B852F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E26C87A4-3DD6-4B58-B14B-C8E086B852F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E26C87A4-3DD6-4B58-B14B-C8E086B852F9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B1B4976E-5112-4217-B57B-3A03C5207B6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B1B4976E-5112-4217-B57B-3A03C5207B6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B1B4976E-5112-4217-B57B-3A03C5207B6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B1B4976E-5112-4217-B57B-3A03C5207B6E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EEC445DC-0C4B-43EA-8694-606BA0390B77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EEC445DC-0C4B-43EA-8694-606BA0390B77}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EEC445DC-0C4B-43EA-8694-606BA0390B77}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EEC445DC-0C4B-43EA-8694-606BA0390B77}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1A4E49FF-9A0A-4C54-AF35-CFFBA64353D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1A4E49FF-9A0A-4C54-AF35-CFFBA64353D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1A4E49FF-9A0A-4C54-AF35-CFFBA64353D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1A4E49FF-9A0A-4C54-AF35-CFFBA64353D9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{954F2AA9-6624-4554-946D-0F17B84487C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{954F2AA9-6624-4554-946D-0F17B84487C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{954F2AA9-6624-4554-946D-0F17B84487C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{954F2AA9-6624-4554-946D-0F17B84487C3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{24616F7E-E2E9-45A3-8A44-AB51FCD2D525}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{24616F7E-E2E9-45A3-8A44-AB51FCD2D525}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{24616F7E-E2E9-45A3-8A44-AB51FCD2D525}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{24616F7E-E2E9-45A3-8A44-AB51FCD2D525}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8959C766-414D-4EF8-BC85-9928B30AAF0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8959C766-414D-4EF8-BC85-9928B30AAF0A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8959C766-414D-4EF8-BC85-9928B30AAF0A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8959C766-414D-4EF8-BC85-9928B30AAF0A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EFE12083-F9FE-4807-8E39-809E0391BAF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EFE12083-F9FE-4807-8E39-809E0391BAF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EFE12083-F9FE-4807-8E39-809E0391BAF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EFE12083-F9FE-4807-8E39-809E0391BAF0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{ECF79CD3-789E-476D-8512-CE0FAF71ADF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ECF79CD3-789E-476D-8512-CE0FAF71ADF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ECF79CD3-789E-476D-8512-CE0FAF71ADF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ECF79CD3-789E-476D-8512-CE0FAF71ADF5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{56D7545A-6DCF-4996-A1A5-40180CE9DE10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{56D7545A-6DCF-4996-A1A5-40180CE9DE10}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{56D7545A-6DCF-4996-A1A5-40180CE9DE10}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{56D7545A-6DCF-4996-A1A5-40180CE9DE10}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{23A4778E-A65C-44B7-A82C-AE2A35103E8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{23A4778E-A65C-44B7-A82C-AE2A35103E8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{23A4778E-A65C-44B7-A82C-AE2A35103E8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{23A4778E-A65C-44B7-A82C-AE2A35103E8D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1690CBBC-BDC0-4DD8-B701-F8817189D9D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1690CBBC-BDC0-4DD8-B701-F8817189D9D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1690CBBC-BDC0-4DD8-B701-F8817189D9D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1690CBBC-BDC0-4DD8-B701-F8817189D9D5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BFE3491C-AC01-4252-B242-6451270FC548}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BFE3491C-AC01-4252-B242-6451270FC548}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BFE3491C-AC01-4252-B242-6451270FC548}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BFE3491C-AC01-4252-B242-6451270FC548}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{A290C028-77C4-4D1D-AB43-DDFE6ABD9012} = {1862ABD5-7C30-4F15-A561-45AC8A9CA10E}
|
||||
{449FA364-0B72-43FF-B3A3-806E2916200E} = {1862ABD5-7C30-4F15-A561-45AC8A9CA10E}
|
||||
{E26C87A4-3DD6-4B58-B14B-C8E086B852F9} = {449FA364-0B72-43FF-B3A3-806E2916200E}
|
||||
{B1B4976E-5112-4217-B57B-3A03C5207B6E} = {449FA364-0B72-43FF-B3A3-806E2916200E}
|
||||
{EEC445DC-0C4B-43EA-8694-606BA0390B77} = {A290C028-77C4-4D1D-AB43-DDFE6ABD9012}
|
||||
{1A4E49FF-9A0A-4C54-AF35-CFFBA64353D9} = {449FA364-0B72-43FF-B3A3-806E2916200E}
|
||||
{954F2AA9-6624-4554-946D-0F17B84487C3} = {449FA364-0B72-43FF-B3A3-806E2916200E}
|
||||
{727BBA0B-9114-4BC8-B9A8-3F461449A564} = {1862ABD5-7C30-4F15-A561-45AC8A9CA10E}
|
||||
{24616F7E-E2E9-45A3-8A44-AB51FCD2D525} = {727BBA0B-9114-4BC8-B9A8-3F461449A564}
|
||||
{8959C766-414D-4EF8-BC85-9928B30AAF0A} = {449FA364-0B72-43FF-B3A3-806E2916200E}
|
||||
{EFE12083-F9FE-4807-8E39-809E0391BAF0} = {1862ABD5-7C30-4F15-A561-45AC8A9CA10E}
|
||||
{ECF79CD3-789E-476D-8512-CE0FAF71ADF5} = {A290C028-77C4-4D1D-AB43-DDFE6ABD9012}
|
||||
{56D7545A-6DCF-4996-A1A5-40180CE9DE10} = {A290C028-77C4-4D1D-AB43-DDFE6ABD9012}
|
||||
{0B1FD8FA-35D3-4DC1-9D98-6178247B29CA} = {449FA364-0B72-43FF-B3A3-806E2916200E}
|
||||
{23A4778E-A65C-44B7-A82C-AE2A35103E8D} = {0B1FD8FA-35D3-4DC1-9D98-6178247B29CA}
|
||||
{1690CBBC-BDC0-4DD8-B701-F8817189D9D5} = {78B6D390-F61A-453F-B38D-E4C054321615}
|
||||
{BFE3491C-AC01-4252-B242-6451270FC548} = {78B6D390-F61A-453F-B38D-E4C054321615}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3FB3C5DE-ED21-4D2E-ABDD-3A00EE4A2FFF}
|
||||
|
||||
Reference in New Issue
Block a user