diff --git a/.gitignore b/.gitignore index add3252..1eedd18 100644 --- a/.gitignore +++ b/.gitignore @@ -362,3 +362,5 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd + +*.txt \ No newline at end of file diff --git a/.idea/.idea.SethDiscordBot/.idea/projectSettingsUpdater.xml b/.idea/.idea.SethDiscordBot/.idea/projectSettingsUpdater.xml new file mode 100644 index 0000000..4bb9f4d --- /dev/null +++ b/.idea/.idea.SethDiscordBot/.idea/projectSettingsUpdater.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/.idea.SethDiscordBot/.idea/workspace.xml b/.idea/.idea.SethDiscordBot/.idea/workspace.xml new file mode 100644 index 0000000..39f0d0c --- /dev/null +++ b/.idea/.idea.SethDiscordBot/.idea/workspace.xml @@ -0,0 +1,107 @@ + + + + DiscordBot/DiscordBot.csproj + DiscordBot/DiscordBot.csproj + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1658854902538 + + + + + + + + + + + + + \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 6705416..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "rpc.enabled": true, - "discord.enabled": true -} \ No newline at end of file diff --git a/BUILDS/net6.0/CMD_Utils.dll b/BUILDS/net6.0/CMD_Utils.dll index d30bcd7..d0b729a 100644 Binary files a/BUILDS/net6.0/CMD_Utils.dll and b/BUILDS/net6.0/CMD_Utils.dll differ diff --git a/BUILDS/net6.0/MusicCommands.deps.json b/BUILDS/net6.0/Music Commands.deps.json similarity index 77% rename from BUILDS/net6.0/MusicCommands.deps.json rename to BUILDS/net6.0/Music Commands.deps.json index bf027a3..2834772 100644 --- a/BUILDS/net6.0/MusicCommands.deps.json +++ b/BUILDS/net6.0/Music Commands.deps.json @@ -6,12 +6,25 @@ "compilationOptions": {}, "targets": { ".NETCoreApp,Version=v6.0": { - "MusicCommands/1.0.0": { + "Music Commands/1.0.0": { "dependencies": { - "PluginManager": "1.0.0" + "PluginManager": "1.0.0", + "YoutubeExplode": "6.2.0" }, "runtime": { - "MusicCommands.dll": {} + "Music Commands.dll": {} + } + }, + "AngleSharp/0.17.0": { + "dependencies": { + "System.Buffers": "4.5.1", + "System.Text.Encoding.CodePages": "5.0.0" + }, + "runtime": { + "lib/netstandard2.0/AngleSharp.dll": { + "assemblyVersion": "0.17.0.0", + "fileVersion": "0.17.0.0" + } } }, "Discord.Net/3.7.2": { @@ -108,6 +121,7 @@ } } }, + "Microsoft.NETCore.Platforms/5.0.0": {}, "Newtonsoft.Json/13.0.1": { "runtime": { "lib/netstandard2.0/Newtonsoft.Json.dll": { @@ -116,6 +130,7 @@ } } }, + "System.Buffers/4.5.1": {}, "System.Collections.Immutable/5.0.0": {}, "System.Interactive.Async/5.0.0": { "dependencies": { @@ -144,7 +159,23 @@ } } }, + "System.Text.Encoding.CodePages/5.0.0": { + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0" + } + }, "System.ValueTuple/4.5.0": {}, + "YoutubeExplode/6.2.0": { + "dependencies": { + "AngleSharp": "0.17.0" + }, + "runtime": { + "lib/net5.0/YoutubeExplode.dll": { + "assemblyVersion": "6.2.0.0", + "fileVersion": "6.2.0.0" + } + } + }, "PluginManager/1.0.0": { "dependencies": { "Discord.Net": "3.7.2" @@ -156,11 +187,18 @@ } }, "libraries": { - "MusicCommands/1.0.0": { + "Music Commands/1.0.0": { "type": "project", "serviceable": false, "sha512": "" }, + "AngleSharp/0.17.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-74haoXINcj4SdMsmiNzk+9VUwIX1U9P61O6AZd5Uao8SGNnJJB8Y/r8VJRc8orn4c7Vk/oURAKSNF9XcSDxbfA==", + "path": "anglesharp/0.17.0", + "hashPath": "anglesharp.0.17.0.nupkg.sha512" + }, "Discord.Net/3.7.2": { "type": "package", "serviceable": true, @@ -217,6 +255,13 @@ "path": "microsoft.extensions.dependencyinjection.abstractions/5.0.0", "hashPath": "microsoft.extensions.dependencyinjection.abstractions.5.0.0.nupkg.sha512" }, + "Microsoft.NETCore.Platforms/5.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==", + "path": "microsoft.netcore.platforms/5.0.0", + "hashPath": "microsoft.netcore.platforms.5.0.0.nupkg.sha512" + }, "Newtonsoft.Json/13.0.1": { "type": "package", "serviceable": true, @@ -224,6 +269,13 @@ "path": "newtonsoft.json/13.0.1", "hashPath": "newtonsoft.json.13.0.1.nupkg.sha512" }, + "System.Buffers/4.5.1": { + "type": "package", + "serviceable": true, + "sha512": "sha512-Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==", + "path": "system.buffers/4.5.1", + "hashPath": "system.buffers.4.5.1.nupkg.sha512" + }, "System.Collections.Immutable/5.0.0": { "type": "package", "serviceable": true, @@ -252,6 +304,13 @@ "path": "system.reactive/5.0.0", "hashPath": "system.reactive.5.0.0.nupkg.sha512" }, + "System.Text.Encoding.CodePages/5.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-NyscU59xX6Uo91qvhOs2Ccho3AR2TnZPomo1Z0K6YpyztBPM/A5VbkzOO19sy3A3i1TtEnTxA7bCe3Us+r5MWg==", + "path": "system.text.encoding.codepages/5.0.0", + "hashPath": "system.text.encoding.codepages.5.0.0.nupkg.sha512" + }, "System.ValueTuple/4.5.0": { "type": "package", "serviceable": true, @@ -259,6 +318,13 @@ "path": "system.valuetuple/4.5.0", "hashPath": "system.valuetuple.4.5.0.nupkg.sha512" }, + "YoutubeExplode/6.2.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-oH5kst4w1QkUwRjJco0alF57JOmFofSGlPkr4OniODB8R6MEyRWn1xFg3JS2wFYd6scZluoXRDhM3/uyUjO9/g==", + "path": "youtubeexplode/6.2.0", + "hashPath": "youtubeexplode.6.2.0.nupkg.sha512" + }, "PluginManager/1.0.0": { "type": "project", "serviceable": false, diff --git a/BUILDS/net6.0/Music Commands.dll b/BUILDS/net6.0/Music Commands.dll new file mode 100644 index 0000000..cb4a5fc Binary files /dev/null and b/BUILDS/net6.0/Music Commands.dll differ diff --git a/BUILDS/net6.0/MusicCommands.dll b/BUILDS/net6.0/MusicCommands.dll deleted file mode 100644 index 19a1e1a..0000000 Binary files a/BUILDS/net6.0/MusicCommands.dll and /dev/null differ diff --git a/BUILDS/net6.0/PluginManager.dll b/BUILDS/net6.0/PluginManager.dll index bb6f20f..7c99f4f 100644 Binary files a/BUILDS/net6.0/PluginManager.dll and b/BUILDS/net6.0/PluginManager.dll differ diff --git a/BUILDS/net6.0/Plugins/Commands/CMD_LevelingSystem.dll b/BUILDS/net6.0/Plugins/Commands/CMD_LevelingSystem.dll index c1c7ecf..711b0d5 100644 Binary files a/BUILDS/net6.0/Plugins/Commands/CMD_LevelingSystem.dll and b/BUILDS/net6.0/Plugins/Commands/CMD_LevelingSystem.dll differ diff --git a/BUILDS/net6.0/Plugins/Commands/CMD_Utils.dll b/BUILDS/net6.0/Plugins/Commands/CMD_Utils.dll index d30bcd7..d0b729a 100644 Binary files a/BUILDS/net6.0/Plugins/Commands/CMD_Utils.dll and b/BUILDS/net6.0/Plugins/Commands/CMD_Utils.dll differ diff --git a/BUILDS/net6.0/Plugins/Commands/Music Commands.dll b/BUILDS/net6.0/Plugins/Commands/Music Commands.dll new file mode 100644 index 0000000..2341731 Binary files /dev/null and b/BUILDS/net6.0/Plugins/Commands/Music Commands.dll differ diff --git a/BUILDS/net6.0/Plugins/Commands/MusicCommands.dll b/BUILDS/net6.0/Plugins/Commands/MusicCommands.dll deleted file mode 100644 index b690a8c..0000000 Binary files a/BUILDS/net6.0/Plugins/Commands/MusicCommands.dll and /dev/null differ diff --git a/BUILDS/net6.0/Plugins/Commands/Roles.dll b/BUILDS/net6.0/Plugins/Commands/Roles.dll new file mode 100644 index 0000000..d278f8c Binary files /dev/null and b/BUILDS/net6.0/Plugins/Commands/Roles.dll differ diff --git a/BUILDS/net6.0/Plugins/Events/EVE_LevelingSystem.dll b/BUILDS/net6.0/Plugins/Events/EVE_LevelingSystem.dll index efc3f15..8d1dbfc 100644 Binary files a/BUILDS/net6.0/Plugins/Events/EVE_LevelingSystem.dll and b/BUILDS/net6.0/Plugins/Events/EVE_LevelingSystem.dll differ diff --git a/CMD_LevelingSystem/CMD_LevelingSystem.csproj b/CMD_LevelingSystem/CMD_LevelingSystem.csproj index 5058233..1fc57d3 100644 --- a/CMD_LevelingSystem/CMD_LevelingSystem.csproj +++ b/CMD_LevelingSystem/CMD_LevelingSystem.csproj @@ -4,7 +4,7 @@ net6.0 enable enable - ..\DiscordBot\bin\Debug\net6.0\Data\Plugins\Commands\LevelingSystem + bin\ diff --git a/CMD_LevelingSystem/Level.cs b/CMD_LevelingSystem/Level.cs index e11fd3f..ba66498 100644 --- a/CMD_LevelingSystem/Level.cs +++ b/CMD_LevelingSystem/Level.cs @@ -11,6 +11,8 @@ internal class Level : DBCommand { public string Command => "level"; + public List Aliases => new() { "lvl" }; + public string Description => "Display tour current level"; public string Usage => "level"; diff --git a/CMD_Utils/CMD_Utils.csproj b/CMD_Utils/CMD_Utils.csproj index 40c79f6..8ac772b 100644 --- a/CMD_Utils/CMD_Utils.csproj +++ b/CMD_Utils/CMD_Utils.csproj @@ -2,7 +2,7 @@ net6.0 - + bin\ diff --git a/CMD_Utils/Echo.cs b/CMD_Utils/Echo.cs index 83ec899..68e5ab2 100644 --- a/CMD_Utils/Echo.cs +++ b/CMD_Utils/Echo.cs @@ -1,11 +1,14 @@ using Discord.Commands; using Discord.WebSocket; using PluginManager.Interfaces; +using System.Collections.Generic; internal class Echo : DBCommand { public string Command => "echo"; + public List Aliases => null; + public string Description => "Replay with the same message"; public string Usage => "echo [message]"; diff --git a/CMD_Utils/FlipCoin.cs b/CMD_Utils/FlipCoin.cs index 0057f33..2ec361e 100644 --- a/CMD_Utils/FlipCoin.cs +++ b/CMD_Utils/FlipCoin.cs @@ -1,6 +1,7 @@ using Discord.Commands; using Discord.WebSocket; using PluginManager.Interfaces; +using System.Collections.Generic; namespace CMD_Utils; @@ -8,6 +9,8 @@ internal class FlipCoin : DBCommand { public string Command => "flip"; + public List Aliases => null; + public string Description => "Flip a coin"; public string Usage => "flip"; diff --git a/CMD_Utils/Poll.cs b/CMD_Utils/Poll.cs index c3624c6..3093e8b 100644 --- a/CMD_Utils/Poll.cs +++ b/CMD_Utils/Poll.cs @@ -11,6 +11,8 @@ public class Poll : DBCommand { public string Command => "poll"; + public List Aliases => null; + public string Description => "Create a poll with options"; public string Usage => "poll [This-is-question] [This-is-answer-1] [This-is-answer-2] ... "; diff --git a/CMD_Utils/Random.cs b/CMD_Utils/Random.cs index 7a93e0d..b69fc04 100644 --- a/CMD_Utils/Random.cs +++ b/CMD_Utils/Random.cs @@ -1,4 +1,5 @@ -using Discord.Commands; +using System.Collections.Generic; +using Discord.Commands; using Discord.WebSocket; using PluginManager.Interfaces; @@ -6,6 +7,8 @@ public class Random : DBCommand { public string Command => "random"; + public List Aliases => new() { "rnd" }; + public string Description => "random number between number1 and number2"; public string Usage => "random [number1] [number2]"; diff --git a/DiscordBot.dll b/DiscordBot.dll deleted file mode 100644 index 88b751e..0000000 Binary files a/DiscordBot.dll and /dev/null differ diff --git a/DiscordBot/Discord/Commands/Help.cs b/DiscordBot/Discord/Commands/Help.cs index 11fb0f9..570dd05 100644 --- a/DiscordBot/Discord/Commands/Help.cs +++ b/DiscordBot/Discord/Commands/Help.cs @@ -1,4 +1,6 @@ -using Discord; +using System.Collections.Generic; +using System.Linq; +using Discord; using Discord.Commands; using Discord.WebSocket; using PluginManager.Interfaces; @@ -17,15 +19,17 @@ internal class Help : DBCommand /// public string Command => "help"; + public List Aliases => null; + /// /// Command Description /// - public string Description => "This command allows you to check all loadded commands"; + public string Description => "This command allows you to check all loaded commands"; /// /// Command usage /// - public string Usage => "help"; + public string Usage => "help "; /// /// Check if the command can be used /> @@ -57,10 +61,10 @@ internal class Help : DBCommand foreach (var item in args) { var e = GenerateHelpCommand(item); - if (e != null) - context.Channel.SendMessageAsync(embed: e.Build()); - else + if (e is null) context.Channel.SendMessageAsync("Unknown Command " + item); + else + context.Channel.SendMessageAsync(embed: e.Build()); } return; @@ -74,10 +78,12 @@ internal class Help : DBCommand foreach (var cmd in PluginLoader.Commands!) { - if (cmd.canUseDM) DMCommands += cmd.Command + " "; + if (cmd.canUseDM) + DMCommands += cmd.Command + " "; if (cmd.requireAdmin) - adminCommands += cmd.Command + " "; - else if (cmd.canUseServer) normalCommands += cmd.Command + " "; + adminCommands += cmd.Command + " "; + if (cmd.canUseServer) + normalCommands += cmd.Command + " "; } embedBuilder.AddField("Admin Commands", adminCommands); @@ -89,11 +95,14 @@ internal class Help : DBCommand private EmbedBuilder GenerateHelpCommand(string command) { var embedBuilder = new EmbedBuilder(); - var cmd = PluginLoader.Commands.Find(p => p.Command == 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", cmd.Usage); embedBuilder.AddField("Description", cmd.Description); + if (cmd.Aliases is null) + return embedBuilder; + embedBuilder.AddField("Alias", cmd.Aliases.Count == 0 ? "-" : string.Join(", ", cmd.Aliases)); return embedBuilder; } diff --git a/DiscordBot/Discord/Commands/Restart.cs b/DiscordBot/Discord/Commands/Restart.cs index a567783..d9a7019 100644 --- a/DiscordBot/Discord/Commands/Restart.cs +++ b/DiscordBot/Discord/Commands/Restart.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using Discord.WebSocket; using PluginManager.Interfaces; @@ -17,6 +18,8 @@ internal class Restart : DBCommand /// public string Command => "restart"; + public List Aliases => null; + /// /// Command Description /// diff --git a/DiscordBot/Discord/Commands/Settings.cs b/DiscordBot/Discord/Commands/Settings.cs index 044a2fd..866a91f 100644 --- a/DiscordBot/Discord/Commands/Settings.cs +++ b/DiscordBot/Discord/Commands/Settings.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Discord; using Discord.Commands; using Discord.WebSocket; @@ -14,6 +15,8 @@ internal class Settings : DBCommand /// public string Command => "set"; + public List Aliases => null; + /// /// Command Description /// diff --git a/DiscordBot/Discord/Core/Boot.cs b/DiscordBot/Discord/Core/Boot.cs index ce2ad4c..e32a198 100644 --- a/DiscordBot/Discord/Core/Boot.cs +++ b/DiscordBot/Discord/Core/Boot.cs @@ -60,7 +60,9 @@ internal class Boot /// Task public async Task Awake() { - client = new DiscordSocketClient(); + DiscordSocketConfig config = new DiscordSocketConfig { AlwaysDownloadUsers = true }; + + client = new DiscordSocketClient(config); service = new CommandService(); CommonTasks(); @@ -96,16 +98,6 @@ internal class Boot Console.Title = "ONLINE"; isReady = true; - new Thread(() => - { - while (true) - { - Config.SaveConfig(); - Thread.Sleep(10000); - } - } - ).Start(); - return Task.CompletedTask; } diff --git a/DiscordBot/Discord/Core/CommandHandler.cs b/DiscordBot/Discord/Core/CommandHandler.cs index 3a87dbc..c89d84b 100644 --- a/DiscordBot/Discord/Core/CommandHandler.cs +++ b/DiscordBot/Discord/Core/CommandHandler.cs @@ -47,13 +47,16 @@ internal class CommandHandler { try { - if (Message as SocketUserMessage == null) return; + if (Message as SocketUserMessage == null) + return; var message = Message as SocketUserMessage; - if (message == null) return; + if (message == null) + return; - if (!message.Content.StartsWith(botPrefix)) return; + if (!message.Content.StartsWith(botPrefix)) + return; var argPos = 0; @@ -63,17 +66,14 @@ internal class CommandHandler return; } - if (message.Author.IsBot) return; + if (message.Author.IsBot) + return; var context = new SocketCommandContext(client, message); - await commandService.ExecuteAsync( - context, - argPos, - null - ); + await commandService.ExecuteAsync(context, argPos, null); - var plugin = PluginLoader.Commands!.Where(p => p.Command == message.Content.Split(' ')[0].Substring(botPrefix.Length)).FirstOrDefault(); + 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 != null) diff --git a/DiscordBot/DiscordBot.csproj b/DiscordBot/DiscordBot.csproj index da11f94..5aa59c2 100644 --- a/DiscordBot/DiscordBot.csproj +++ b/DiscordBot/DiscordBot.csproj @@ -8,15 +8,15 @@ False True - 1.0.0.1 + 1.0.0.3 - none + none - none + none diff --git a/DiscordBot/Program.cs b/DiscordBot/Program.cs index c78d07d..a94ff80 100644 --- a/DiscordBot/Program.cs +++ b/DiscordBot/Program.cs @@ -2,8 +2,14 @@ using System.Collections.Generic; 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.Online; @@ -15,6 +21,7 @@ public class Program { private static bool loadPluginsOnStartup; private static bool listPluginsAtStartup; + private static ConsoleCommandsHandler consoleCommandsHandler; /// /// The main entry point for the application. @@ -23,11 +30,45 @@ public class Program [Obsolete] public static void Main(string[] args) { + Directory.CreateDirectory("./Data/Resources"); Directory.CreateDirectory("./Data/Plugins/Commands"); Directory.CreateDirectory("./Data/Plugins/Events"); PreLoadComponents().Wait(); + + if (!Config.ContainsKey("ServerID")) + { + do + { + Console.Clear(); + 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.WriteLine("Your server ID is not 18 characters long. Please try again."); + continue; + } + + + Config.AddValueToVariables("ServerID", SID, false); + + } + break; + } while (true); + + } + if (!Config.ContainsKey("token") || Config.GetValue("token") == null || Config.GetValue("token")?.Length != 70) { Console.WriteLine("Please insert your token"); @@ -43,7 +84,8 @@ public class Program Console.Write("Prefix = "); var prefix = Console.ReadLine()![0]; - if (prefix == ' ' || char.IsDigit(prefix)) return; + if (prefix == ' ' || char.IsDigit(prefix)) + return; Config.AddValueToVariables("prefix", prefix.ToString(), false); } @@ -65,20 +107,28 @@ public class Program /// The main loop for the discord bot /// /// The discord booter used to start the application - private static Task NoGUI(Boot discordbooter) + private static void NoGUI(Boot discordbooter) { - var consoleCommandsHandler = new ConsoleCommandsHandler(discordbooter.client); + +#if DEBUG + Console.WriteLine(); + ConsoleCommandsHandler.ExecuteCommad("lp").Wait(); +#else if (loadPluginsOnStartup) consoleCommandsHandler.HandleCommand("lp"); if (listPluginsAtStartup) consoleCommandsHandler.HandleCommand("listplugs"); - - Config.SaveConfig(); - +#endif + Config.SaveConfig(SaveType.NORMAL).Wait(); while (true) { - Console.ForegroundColor = ConsoleColor.White; + var cmd = Console.ReadLine(); - if (!consoleCommandsHandler.HandleCommand(cmd)) + if (!consoleCommandsHandler.HandleCommand(cmd! +#if DEBUG + , false +#endif + + ) && cmd.Length > 0) Console.WriteLine("Failed to run command " + cmd); } } @@ -92,21 +142,42 @@ public class Program Console.Clear(); Console.ForegroundColor = ConsoleColor.DarkYellow; - List startupMessageList = await ServerCom.ReadTextFromFile("https://raw.githubusercontent.com/Wizzy69/installer/discord-bot-files/StartupMessage"); + List startupMessageList = await ServerCom.ReadTextFromURL("https://raw.githubusercontent.com/Wizzy69/installer/discord-bot-files/StartupMessage"); - foreach (var message in startupMessageList) Console.WriteLine(message); + foreach (var message in startupMessageList) + Console.WriteLine(message); Console.WriteLine($"Running on version: {Config.GetValue("Version") ?? System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()}"); Console.WriteLine($"Git URL: {Config.GetValue("GitURL") ?? " Could not find Git URL"}"); + Console_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("LaunchMessage")); + Config.RemoveKey("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 ============================"); try { - var token = Config.GetValue("token"); - var prefix = Config.GetValue("prefix"); + var token = Config.GetValue("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... + } +#endif + + var prefix = Config.GetValue("prefix"); var discordbooter = new Boot(token, prefix); await discordbooter.Awake(); return discordbooter; @@ -124,7 +195,7 @@ public class Program /// Directory path private static Task ClearFolder(string d) { - var files = Directory.GetFiles(d); + var files = Directory.GetFiles(d); var fileNumb = files.Length; for (var i = 0; i < fileNumb; i++) { @@ -141,11 +212,12 @@ public class Program /// The arguments private static async Task HandleInput(string[] args) { + var len = args.Length; if (len == 3 && args[0] == "/download") { - var url = args[1]; + var url = args[1]; var location = args[2]; await ServerCom.DownloadFileAsync(url, location); @@ -153,18 +225,72 @@ public class Program return; } + if (len > 0 && args[0] == "/test") + { + int p = 1; + bool allowed = true; + Console.CancelKeyPress += (sender, e) => allowed = false; + Console_Utilities.ProgressBar bar = new(ProgressBarType.NO_END);// { NoColor = false, Color = ConsoleColor.DarkRed }; + Console.WriteLine("Press Ctrl + C to stop."); + while (p <= int.MaxValue - 1 && allowed) + { + bar.Update(100 / p); + await Task.Delay(100); + p++; + } + + 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; + if (args.Contains("lp") || args.Contains("loadplugins")) + loadPluginsOnStartup = true; + if (args.Contains("listplugs")) + listPluginsAtStartup = true; + + 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] != "--exec" && args[0] != "--execute")) { - var b = await StartNoGUI(); - await NoGUI(b); + + Thread mainThread = new Thread(() => + { + try + { + NoGUI(b); + } + 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; } @@ -190,15 +316,7 @@ public class Program case "--help": case "-help": 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" - ); + 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/"); @@ -212,16 +330,12 @@ public class Program case "--reset-logs": await ClearFolder("./Output/Logs"); await ClearFolder("./Output/Errors"); - Console.WriteLine("Successfully cleard logs folder"); + Console.WriteLine("Successfully clear logs folder"); break; case "--exit": case "exit": Environment.Exit(0); break; - case "--start": - var booter = await StartNoGUI(); - await NoGUI(booter); - return; default: Console.WriteLine("Failed to execute command " + message[0]); @@ -237,14 +351,14 @@ public class Program if (Config.GetValue("DeleteLogsAtStartup")) foreach (var file in Directory.GetFiles("./Output/Logs/")) File.Delete(file); - List OnlineDefaultKeys = await ServerCom.ReadTextFromFile("https://raw.githubusercontent.com/Wizzy69/installer/discord-bot-files/SetupKeys"); + List OnlineDefaultKeys = await ServerCom.ReadTextFromURL("https://raw.githubusercontent.com/Wizzy69/installer/discord-bot-files/SetupKeys"); Config.PluginConfig.Load(); if (!Config.ContainsKey("Version")) - Config.AddValueToVariables("Version", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(), false); + Config.AddValueToVariables("Version", Assembly.GetExecutingAssembly().GetName().Version.ToString(), false); else - Config.SetValue("Version", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()); + Config.SetValue("Version", Assembly.GetExecutingAssembly().GetName().Version.ToString()); foreach (var key in OnlineDefaultKeys) { @@ -252,7 +366,8 @@ public class Program string[] s = key.Split(' '); try { - Config.GetAndAddValueToVariable(s[0], s[1], s[2].Equals("true", StringComparison.CurrentCultureIgnoreCase)); + if (Config.ContainsKey(s[0])) Config.SetValue(s[0], s[1]); + else Config.GetAndAddValueToVariable(s[0], s[1], s[2].Equals("true", StringComparison.CurrentCultureIgnoreCase)); } catch (Exception ex) { @@ -260,7 +375,7 @@ public class Program } } - List onlineSettingsList = await ServerCom.ReadTextFromFile("https://raw.githubusercontent.com/Wizzy69/installer/discord-bot-files/OnlineData"); + List onlineSettingsList = await ServerCom.ReadTextFromURL("https://raw.githubusercontent.com/Wizzy69/installer/discord-bot-files/OnlineData"); foreach (var key in onlineSettingsList) { if (key.Length <= 3 || !key.Contains(' ')) continue; @@ -288,7 +403,7 @@ public class Program Console.WriteLine("\n\n"); await Task.Delay(1000); - int waitTime = 20; //wait time to proceed + int waitTime = 10; //wait time to proceed Console.Write($"The bot will start in {waitTime} seconds"); while (waitTime > 0) @@ -306,7 +421,8 @@ public class Program } } + Console_Utilities.Initialize(); - Config.SaveConfig(); + Config.SaveConfig(SaveType.NORMAL); } } diff --git a/EVE_LevelingSystem/EVE_LevelingSystem.csproj b/EVE_LevelingSystem/EVE_LevelingSystem.csproj index ce1a63c..cf07d6d 100644 --- a/EVE_LevelingSystem/EVE_LevelingSystem.csproj +++ b/EVE_LevelingSystem/EVE_LevelingSystem.csproj @@ -4,7 +4,7 @@ net6.0 enable enable - ..\DiscordBot\bin\Debug\net6.0\Data\Plugins\Events\LevelingSystem + diff --git a/EVE_LevelingSystem/Level.cs b/EVE_LevelingSystem/Level.cs index f4a9c6b..68ece34 100644 --- a/EVE_LevelingSystem/Level.cs +++ b/EVE_LevelingSystem/Level.cs @@ -1,6 +1,8 @@ using Discord; using Discord.WebSocket; + using EVE_LevelingSystem.LevelingSystemCore; + using PluginManager; using PluginManager.Interfaces; using PluginManager.Others; @@ -9,17 +11,20 @@ namespace EVE_LevelingSystem { internal class Level : DBEvent { - public string name => "Leveling System Event Handler"; - public string description => "The Leveling System Event Handler"; + public string name => "Leveling System Event Handler"; + public string description => "The Leveling System Event Handler"; + internal static Settings globalSettings = new(); public async void Start(DiscordSocketClient client) { Directory.CreateDirectory("./Data/Resources/LevelingSystem"); - Config.AddValueToVariables("LevelingSystemPath", "./Data/Resources/LevelingSystem", true); - Config.AddValueToVariables("LevelingSystemSettingsFile", "./Data/Resources/LevelingSystemSettings.txt", true); - + if (!Config.ContainsKey("LevelingSystemPath")) + Config.AddValueToVariables("LevelingSystemPath", "./Data/Resources/LevelingSystem", true); + if (!Config.ContainsKey("LevelingSystemSettingsFile")) + Config.AddValueToVariables("LevelingSystemSettingsFile", "./Data/Resources/LevelingSystemSettings.txt", true); + //PluginManager.Config.AddValueToVariables if (!File.Exists(Config.GetValue("LevelingSystemSettingsFile"))) { globalSettings = new Settings { TimeToWaitBetweenMessages = 5 }; @@ -36,7 +41,7 @@ namespace EVE_LevelingSystem { if (arg.Author.IsBot || arg.IsTTS || arg.Content.StartsWith(Config.GetValue("prefix"))) return; string userID = arg.Author.Id.ToString(); - User user; + User user; if (File.Exists($"{Config.GetValue("LevelingSystemPath")}/{userID}.dat")) { user = await Functions.ConvertFromJson(Config.GetValue("LevelingSystemPath")! + $"/{userID}.dat"); diff --git a/EVE_LevelingSystem/LevelingSystemCore/LevelCalculator.cs b/EVE_LevelingSystem/LevelingSystemCore/LevelCalculator.cs index 733adac..0359c6b 100644 --- a/EVE_LevelingSystem/LevelingSystemCore/LevelCalculator.cs +++ b/EVE_LevelingSystem/LevelingSystemCore/LevelCalculator.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; + using PluginManager; namespace EVE_LevelingSystem.LevelingSystemCore @@ -18,7 +19,7 @@ namespace EVE_LevelingSystem.LevelingSystemCore internal static void LevelUp(this User user) { - user.CurrentEXP = 0; + user.CurrentEXP = 0; user.RequiredEXPToLevelUp = GetNextLevelRequiredEXP(user.CurrentLevel); user.CurrentLevel++; } @@ -26,15 +27,15 @@ namespace EVE_LevelingSystem.LevelingSystemCore internal static bool AddEXP(this User user) { if (OnWaitingList.Contains(user.user.userID.ToString())) return false; - Random r = new Random(); - int exp = r.Next(2, 12); - Int64 userXP = user.CurrentEXP; - Int64 reqEXP = user.RequiredEXPToLevelUp; + Random r = new Random(); + int exp = r.Next(Level.globalSettings.MinEXP, Level.globalSettings.MaxEXP + 1); + Int64 userXP = user.CurrentEXP; + Int64 reqEXP = user.RequiredEXPToLevelUp; if (userXP + exp >= reqEXP) { user.LevelUp(); user.CurrentEXP = exp - (reqEXP - userXP); - Console.WriteLine("Level up"); + //Console.WriteLine("Level up"); return true; } @@ -49,7 +50,7 @@ namespace EVE_LevelingSystem.LevelingSystemCore Thread.Sleep(60000 * minutesToWait); OnWaitingList.Remove(user.user.userID.ToString()); } - ); + ).Start(); return false; } diff --git a/EVE_LevelingSystem/Settings.cs b/EVE_LevelingSystem/Settings.cs index 38a8caa..5225e9d 100644 --- a/EVE_LevelingSystem/Settings.cs +++ b/EVE_LevelingSystem/Settings.cs @@ -9,5 +9,7 @@ namespace EVE_LevelingSystem public class Settings { public int TimeToWaitBetweenMessages { get; set; } + public int MinEXP { get; set; } + public int MaxEXP { get; set; } } } diff --git a/MusicCommands/AudioFile.cs b/MusicCommands/AudioFile.cs new file mode 100644 index 0000000..bd7b576 --- /dev/null +++ b/MusicCommands/AudioFile.cs @@ -0,0 +1,35 @@ +using AngleSharp.Dom; + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MusicCommands +{ + internal class AudioFile + { + internal string Name { get; set; } + internal string Url { get; set; } + + internal AudioFile(string name, string url) + { + Name = name; + Url = url; + } + + internal async Task DownloadAudioFile() + { + Process proc = new Process(); + proc.StartInfo.FileName = "MusicDownloader"; + proc.StartInfo.Arguments = $"{Url},{Name}"; + proc.StartInfo.UseShellExecute = false; + proc.StartInfo.RedirectStandardOutput = true; + + proc.Start(); + await proc.WaitForExitAsync(); + } + } +} diff --git a/MusicCommands/Data.cs b/MusicCommands/Data.cs index 066af23..ac61ac7 100644 --- a/MusicCommands/Data.cs +++ b/MusicCommands/Data.cs @@ -8,5 +8,6 @@ internal static class Data internal static IAudioClient audioClient = null; internal static IVoiceChannel voiceChannel = null; - internal static MusicPlayer CurrentlyRunning = null; + internal static MusicPlayer MusicPlayer = null; + internal static MusicPlaylist Playlist = new(); } diff --git a/MusicCommands/Leave.cs b/MusicCommands/Leave.cs index 3171489..79979e8 100644 --- a/MusicCommands/Leave.cs +++ b/MusicCommands/Leave.cs @@ -1,4 +1,5 @@ -using Discord.Commands; +using System.Collections.Generic; +using Discord.Commands; using Discord.WebSocket; using PluginManager.Interfaces; @@ -8,6 +9,8 @@ internal class Leave : DBCommand { public string Command => "leave"; + public List Aliases => null; + public string Description => "Leave the voice channel"; public string Usage => "leave"; @@ -22,10 +25,20 @@ internal class Leave : DBCommand { if (Data.audioClient is not null && Data.voiceChannel is not null) { - Data.CurrentlyRunning.Stop(); - Data.CurrentlyRunning = null; await Data.audioClient.StopAsync(); await Data.voiceChannel.DisconnectAsync(); } + + if (Data.Playlist is not null) + { + Data.Playlist.ClearQueue(); + Data.Playlist = new(); + } + + if (Data.MusicPlayer is not null) + { + Data.MusicPlayer.Stop(); + Data.MusicPlayer = null; + } } } diff --git a/MusicCommands/MusicCommands.csproj b/MusicCommands/MusicCommands.csproj index 9bd106b..037f43c 100644 --- a/MusicCommands/MusicCommands.csproj +++ b/MusicCommands/MusicCommands.csproj @@ -3,7 +3,8 @@ net6.0 warnings - ..\DiscordBot\bin\Debug\net6.0\Data\Plugins\Commands\MusicCommands + bin\ + Music Commands @@ -17,4 +18,8 @@ + + + + diff --git a/MusicCommands/MusicPlayer.cs b/MusicCommands/MusicPlayer.cs index 6d8e8f6..e1c974a 100644 --- a/MusicCommands/MusicPlayer.cs +++ b/MusicCommands/MusicPlayer.cs @@ -1,118 +1,53 @@ -using System; -using System.IO; -using System.Net.Http; -using System.Threading; +using System.IO; using System.Threading.Tasks; -using PluginManager.Others; namespace MusicCommands; internal class MusicPlayer { - public MusicPlayer(Stream input, Stream output) + private Stream outputStream { get; } + internal AudioFile NowPlaying = null; + + internal bool isPlaying, isPaused; + + public MusicPlayer(Stream outputChannel) { - inputStream = input; - outputStream = output; + outputStream = outputChannel; } - public MusicPlayer(Stream output) + public async Task Play(Stream source, int byteSize, AudioFile songPlaying) { - inputStream = null; - outputStream = output; + isPlaying = true; + NowPlaying = songPlaying; + while (isPlaying) + { + if (isPaused) + continue; + + var bits = new byte[byteSize]; + var read = await source.ReadAsync(bits, 0, byteSize); + if (read == 0) + break; + try + { + await outputStream.WriteAsync(bits, 0, read); + } + catch + { + break; + } + } + + + await source.FlushAsync(); + await source.DisposeAsync(); + source.Close(); + await outputStream.FlushAsync(); + isPlaying = false; } - public Stream inputStream { get; } // from FFMPEG - public Stream outputStream { get; } // to Voice Channel - - public bool Paused { get; set; } - private bool _stop { get; set; } - public void Stop() { - _stop = true; - } - - public async Task StartSendAudioFromLink(string URL) - { - /* using (HttpClient client = new HttpClient()) - using (HttpResponseMessage response = await client.GetAsync(URL)) - using (var content = response.Content) - { - await (await content.ReadAsStreamAsync()).CopyToAsync(outputStream); - }*/ - - - Stream ms = new MemoryStream(); - var bsize = 512; - new Thread(async delegate(object o) - { - var response = await new HttpClient().GetAsync(URL); - using (var stream = await response.Content.ReadAsStreamAsync()) - { - var buffer = new byte[bsize]; - int read; - while ((read = stream.Read(buffer, 0, buffer.Length)) > 0) - { - var pos = ms.Position; - ms.Position = ms.Length; - ms.Write(buffer, 0, read); - ms.Position = pos; - } - } - } - ).Start(); - Console.Write("Reading data: "); - while (ms.Length < bsize * 10) - { - await Task.Delay(1000); - Console.Title = "Reading data: " + ms.Length + " bytes read of " + bsize * 10; - Console.Write("."); - } - - Console.WriteLine("\nDone"); - ms.Position = 0; - - _stop = false; - Paused = false; - - while (!_stop) - { - if (Paused) continue; - var buffer = new byte[bsize]; - var read = await ms.ReadAsync(buffer, 0, buffer.Length); - if (read > 0) - await outputStream.WriteAsync(buffer, 0, read); - else - break; - } - } - - public async Task StartSendAudio() - { - Paused = false; - _stop = false; - while (!_stop) - { - if (Paused) continue; - var bsize = 512; - var buffer = new byte[bsize]; - var bcount = await inputStream.ReadAsync(buffer, 0, bsize); - if (bcount <= 0) - { - Stop(); - Data.CurrentlyRunning = null; - break; - } - - try - { - await outputStream.WriteAsync(buffer, 0, bcount); - } - catch (Exception ex) - { - await outputStream.FlushAsync(); - Functions.WriteLogFile(ex.ToString()); - } - } + isPlaying = false; } } diff --git a/MusicCommands/MusicPlaylist.cs b/MusicCommands/MusicPlaylist.cs new file mode 100644 index 0000000..f514fb1 --- /dev/null +++ b/MusicCommands/MusicPlaylist.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MusicCommands +{ + internal class MusicPlaylist + { + internal MusicPlaylist() + { + Console.WriteLine("Initialized playlist."); + } + + public Queue QueueList = new(); + + public void Enqueue(AudioFile query) => QueueList.Enqueue(query); + public void ClearQueue() => QueueList.Clear(); + + public int Count => QueueList.Count; + public AudioFile GetNextSong => QueueList.Dequeue(); + public AudioFile WhatIsNext => QueueList.Peek(); + } +} diff --git a/MusicCommands/Pause.cs b/MusicCommands/Pause.cs index 8a86430..065a02a 100644 --- a/MusicCommands/Pause.cs +++ b/MusicCommands/Pause.cs @@ -1,4 +1,5 @@ -using Discord.Commands; +using System.Collections.Generic; +using Discord.Commands; using Discord.WebSocket; using PluginManager.Interfaces; @@ -8,7 +9,9 @@ internal class Pause : DBCommand { public string Command => "pause"; - public string Description => "Pause the music"; + public List Aliases => null; + + public string Description => "Pause/Unpause the music that is currently running"; public string Usage => "pause"; @@ -20,6 +23,6 @@ internal class Pause : DBCommand public void Execute(SocketCommandContext context, SocketMessage message, DiscordSocketClient client, bool isDM) { - Data.CurrentlyRunning.Paused = true; + Data.MusicPlayer.isPaused = !Data.MusicPlayer.isPaused; } } diff --git a/MusicCommands/Play.cs b/MusicCommands/Play.cs index 0e9b181..9b46e7f 100644 --- a/MusicCommands/Play.cs +++ b/MusicCommands/Play.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using Discord; @@ -12,11 +13,13 @@ namespace MusicCommands; internal class Play : DBCommand { - public string Command => "fplay"; + public string Command => "play"; + + public List Aliases => new() { "p" }; public string Description => "Play music from a file"; - public string Usage => "fplay [name]"; + public string Usage => "play [name/url]"; public bool canUseDM => false; @@ -26,36 +29,90 @@ internal class Play : DBCommand public async void Execute(SocketCommandContext context, SocketMessage message, DiscordSocketClient client, bool isDM) { - var path = "./Music"; - var FileName = Functions.GetArguments(message).ToArray().MergeStrings(0); - path += "/" + FileName + ".ogg"; - if (!File.Exists(path)) - { - Console.WriteLine("Unknown path " + path); + Directory.CreateDirectory("Music"); + var path = "./Music/"; + string[] splitted = message.Content.Split(' '); + if (splitted.Length < 2) return; + do + { + if (splitted.Length == 2 && splitted[1].Contains("youtube.com") || splitted[1].Contains("youtu.be")) + { + var url = splitted[1]; + path += $"{Functions.CreateMD5(url)}"; + if (File.Exists(path)) + { + Data.Playlist.Enqueue(new AudioFile(path, null)); + } + else + { + var file = new AudioFile(path, url); + await file.DownloadAudioFile(); + Data.Playlist.Enqueue(file); + } + } + else + { + var searchString = splitted.MergeStrings(1); + path += $"{Functions.CreateMD5(searchString)}"; + if (File.Exists(path)) + { + Data.Playlist.Enqueue(new AudioFile(path, null)); + } + else + { + await context.Channel.SendMessageAsync("Searching for " + searchString); + var file = new AudioFile(path, searchString); + await file.DownloadAudioFile(); + Data.Playlist.Enqueue(file); + if (Data.MusicPlayer is null) + await context.Channel.SendMessageAsync("Playing: " + searchString); + } + } + + if (Data.MusicPlayer is not null) + { + await context.Channel.SendMessageAsync("Queued your request: " + splitted.MergeStrings(1)); + return; + } } + while (false); // run only one time ! Data.voiceChannel = (context.User as IGuildUser)?.VoiceChannel; + if (Data.voiceChannel == null) { await context.Channel.SendMessageAsync("User must be in a voice channel, or a voice channel must be passed as an argument."); return; } - Data.audioClient = await Data.voiceChannel.ConnectAsync(); - - using (var ffmpeg = CreateStream(path)) - using (var output = ffmpeg.StandardOutput.BaseStream) - using (var discord = Data.audioClient.CreatePCMStream(AudioApplication.Mixed)) + if (Data.audioClient is null) { - if (Data.CurrentlyRunning != null) Data.CurrentlyRunning.Stop(); - Data.CurrentlyRunning = new MusicPlayer(output, discord); - await Data.CurrentlyRunning.StartSendAudio(); + Data.audioClient = await Data.voiceChannel.ConnectAsync(true); + Data.MusicPlayer = null; + } + + + using (var discordChanneAudioOutStream = Data.audioClient.CreatePCMStream(AudioApplication.Mixed)) + { + Data.MusicPlayer ??= new MusicPlayer(discordChanneAudioOutStream); + while (Data.Playlist.Count > 0) + { + var nowPlaying = Data.Playlist.GetNextSong; + using (var ffmpeg = CreateStream(nowPlaying.Name)) + using (var ffmpegOutputBaseStream = ffmpeg.StandardOutput.BaseStream) + { + await Data.MusicPlayer.Play(ffmpegOutputBaseStream, 1024, nowPlaying); + Console.WriteLine("Finished playing from " + nowPlaying.Url); + } + } + + Data.MusicPlayer = null; } } - private Process CreateStream(string path) + private static Process CreateStream(string path) { return Process.Start(new ProcessStartInfo { FileName = "ffmpeg", Arguments = $"-hide_banner -loglevel panic -i \"{path}\" -ac 2 -f s16le -ar 48000 pipe:1", UseShellExecute = false, RedirectStandardOutput = true }); } diff --git a/MusicCommands/Skip.cs b/MusicCommands/Skip.cs new file mode 100644 index 0000000..0ca23b3 --- /dev/null +++ b/MusicCommands/Skip.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord.Commands; +using Discord.WebSocket; +using PluginManager.Interfaces; + +namespace MusicCommands +{ + public class Skip : DBCommand + { + public string Command => "skip"; + + public List Aliases => null; + + public string Description => "skip the music that is currently running"; + + public string Usage => "skip"; + + public bool canUseDM => false; + + public bool canUseServer => true; + + public bool requireAdmin => false; + + public void Execute(SocketCommandContext context, SocketMessage message, DiscordSocketClient client, bool isDM) + { + var loadedSong = Data.MusicPlayer.NowPlaying; + + if (loadedSong is null || Data.MusicPlayer.isPlaying == false) + { + message.Channel.SendMessageAsync("There is no music playing"); + return; + } + + Data.MusicPlayer.isPlaying = false; + message.Channel.SendMessageAsync($"You have skipped {loadedSong.Name}"); + } + } +} diff --git a/MusicCommands/Unpause.cs b/MusicCommands/Unpause.cs deleted file mode 100644 index cfe2040..0000000 --- a/MusicCommands/Unpause.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Discord.Commands; -using Discord.WebSocket; -using PluginManager.Interfaces; - -namespace MusicCommands; - -internal class Unpause : DBCommand -{ - public string Command => "unpause"; - - public string Description => "Unpause the music"; - - public string Usage => "unpause"; - - public bool canUseDM => false; - - public bool canUseServer => true; - - public bool requireAdmin => false; - - public void Execute(SocketCommandContext context, SocketMessage message, DiscordSocketClient client, bool isDM) - { - Data.CurrentlyRunning.Paused = false; - } -} diff --git a/MusicCommands/lplay.cs b/MusicCommands/lplay.cs deleted file mode 100644 index 6adb117..0000000 --- a/MusicCommands/lplay.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Discord; -using Discord.Audio; -using Discord.Commands; -using Discord.WebSocket; -using PluginManager.Interfaces; - -namespace MusicCommands; - -internal class lplay : DBCommand -{ - public string Command => "lplay"; - - public string Description => "Play music from a link"; - - public string Usage => "lplay [url]"; - - public bool canUseDM => false; - - public bool canUseServer => false; - - public bool requireAdmin => false; - - public async void Execute(SocketCommandContext context, SocketMessage message, DiscordSocketClient client, bool isDM) - { - var URL = message.Content.Split(' ')[1]; - if (!URL.EndsWith(".mp3") && !URL.EndsWith(".wav") && !URL.EndsWith(".flac") && !URL.EndsWith(".ogg")) - { - await message.Channel.SendMessageAsync("Invalid URL"); - return; - } - - Data.voiceChannel = (context.User as IGuildUser)?.VoiceChannel; - if (Data.voiceChannel == null) - { - await context.Channel.SendMessageAsync("User must be in a voice channel, or a voice channel must be passed as an argument."); - return; - } - - Data.audioClient = await Data.voiceChannel.ConnectAsync(); - - using (var discord = Data.audioClient.CreatePCMStream(AudioApplication.Mixed)) - { - await message.Channel.SendMessageAsync("Loading..."); - - Data.CurrentlyRunning = new MusicPlayer(discord); - await Data.CurrentlyRunning.StartSendAudioFromLink(URL); - } - } -} diff --git a/MusicCommands/queue.cs b/MusicCommands/queue.cs new file mode 100644 index 0000000..d918f30 --- /dev/null +++ b/MusicCommands/queue.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord.Commands; +using Discord.WebSocket; +using PluginManager.Interfaces; + +namespace MusicCommands +{ + public class queue : DBCommand + { + public string Command => "queue"; + public List Aliases => new() { "q" }; + + public string Description => "check queue"; + + public string Usage => "queue"; + + public bool canUseDM => false; + + public bool canUseServer => true; + + public bool requireAdmin => false; + + public async void Execute(SocketCommandContext context, SocketMessage message, DiscordSocketClient client, bool isDM) + { + await context.Channel.SendMessageAsync($"You have {Data.Playlist.Count} items in queue"); + } + } +} diff --git a/PluginManager/Config.cs b/PluginManager/Config.cs index 985abc8..9bb7140 100644 --- a/PluginManager/Config.cs +++ b/PluginManager/Config.cs @@ -4,22 +4,23 @@ using System.IO; using System.Text.Json; using System.Threading.Tasks; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Threading; -using Newtonsoft.Json.Linq; namespace PluginManager { internal class AppConfig { - public Dictionary? ApplicationVariables { get; set; } - public List? ProtectedKeyWords { get; set; } + public Dictionary? ApplicationVariables { get; init; } + public List? ProtectedKeyWords { get; init; } + public Dictionary? PluginVersions { get; init; } } public static class Config { public static class PluginConfig { - public static List> InstalledPlugins = new(); + public static readonly List> InstalledPlugins = new(); public static void Load() { @@ -29,27 +30,27 @@ namespace PluginManager private static void LoadCommands() { - string cmd_path = "./Data/Plugins/Commands/"; - string[] files = Directory.GetFiles(cmd_path, $"*.{Loaders.PluginLoader.pluginCMDExtension}", SearchOption.AllDirectories); + 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); + string name = PluginName.Substring(0, PluginName.Length - 1 - PluginManager.Loaders.PluginLoader.pluginCMDExtension.Length); InstalledPlugins.Add(new(name, PluginType.Command)); } } private static void LoadEvents() { - string eve_path = "./Data/Plugins/Events/"; - string[] files = Directory.GetFiles(eve_path, $"*.{Loaders.PluginLoader.pluginEVEExtension}", SearchOption.AllDirectories); + 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); + string name = PluginName.Substring(0, PluginName.Length - 1 - PluginManager.Loaders.PluginLoader.pluginEVEExtension.Length); InstalledPlugins.Add(new(name, PluginType.Event)); } } @@ -57,9 +58,8 @@ namespace PluginManager public static bool Contains(string pluginName) { foreach (var tuple in InstalledPlugins) - { - if (tuple.Item1 == pluginName) return true; - } + if (tuple.Item1 == pluginName) + return true; return false; } @@ -67,9 +67,9 @@ namespace PluginManager public static PluginType GetPluginType(string pluginName) { foreach (var tuple in InstalledPlugins) - { - if (tuple.Item1 == pluginName) return tuple.Item2; - } + if (tuple.Item1 == pluginName) + return tuple.Item2; + return PluginType.Unknown; } @@ -77,20 +77,56 @@ namespace PluginManager private static AppConfig? appConfig { get; set; } - public static bool AddValueToVariables(string key, T value, bool isProtected) + public static string GetPluginVersion(string pluginName) => appConfig!.PluginVersions![pluginName]; + public static void SetPluginVersion(string pluginName, string newVersion) { - if (appConfig!.ApplicationVariables!.ContainsKey(key)) return false; - if (value == null) return false; - appConfig.ApplicationVariables.Add(key, value); - if (isProtected && key != "Version") appConfig.ProtectedKeyWords!.Add(key); + if (appConfig!.PluginVersions!.ContainsKey(pluginName)) + appConfig.PluginVersions[pluginName] = newVersion; + else appConfig.PluginVersions.Add(pluginName, newVersion); - SaveConfig(); - return true; + // 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(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(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 (Config.ContainsKey(key)) + return; if (int.TryParse(value, out var intValue)) Config.AddValueToVariables(key, intValue, isReadOnly); else if (bool.TryParse(value, out var boolValue)) @@ -123,30 +159,43 @@ namespace PluginManager } } - public static bool SetValue(string key, T value) + public static void SetValue(string key, T value) { - if (!appConfig!.ApplicationVariables!.ContainsKey(key)) return false; - if (appConfig.ProtectedKeyWords!.Contains(key)) return false; - if (value == null) return false; + 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(); - return true; + SaveConfig(SaveType.NORMAL); } - public static bool RemoveKey(string key) + public static void RemoveKey(string key) { - if (key == "Version" || key == "token" || key == "prefix") return false; + if (key == "Version" || key == "token" || key == "prefix") + throw new Exception("Key is protected"); appConfig!.ApplicationVariables!.Remove(key); appConfig.ProtectedKeyWords!.Remove(key); - SaveConfig(); - return true; + SaveConfig(SaveType.NORMAL); } - public static async void SaveConfig() + public static async Task SaveConfig(SaveType type) { - string path = Functions.dataFolder + "config.json"; - await Functions.SaveToJsonFile(path, appConfig!); + if (type == SaveType.NORMAL) + { + string path = Functions.dataFolder + "config.json"; + await Functions.SaveToJsonFile(path, appConfig!); + return; + } + if (type == SaveType.BACKUP) + { + string path = Functions.dataFolder + "config.json.bak"; + await Functions.SaveToJsonFile(path, appConfig!); + return; + } + } public static async Task LoadConfig() @@ -154,16 +203,29 @@ namespace PluginManager string path = Functions.dataFolder + "config.json"; if (File.Exists(path)) { - appConfig = await Functions.ConvertFromJson(path); + try + { + appConfig = await Functions.ConvertFromJson(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(path); + Functions.WriteErrFile(ex.Message); + } + + Functions.WriteLogFile($"Loaded {appConfig.ApplicationVariables!.Keys.Count} application variables.\nLoaded {appConfig.ProtectedKeyWords!.Count} readonly variables."); + return; } - else - appConfig = new() { ApplicationVariables = new Dictionary(), ProtectedKeyWords = new List() }; + appConfig = new() { ApplicationVariables = new Dictionary(), ProtectedKeyWords = new List(), PluginVersions = new Dictionary() }; } public static bool ContainsValue(T value) => appConfig!.ApplicationVariables!.ContainsValue(value!); - public static bool ContainsKey(string key) => appConfig!.ApplicationVariables!.ContainsKey(key); + public static bool ContainsKey(string key) => appConfig!.ApplicationVariables!.ContainsKey(key); - public static Dictionary GetAllVariables() => new(appConfig!.ApplicationVariables!); + public static ReadOnlyDictionary GetAllVariables() => new(appConfig!.ApplicationVariables!); } } diff --git a/PluginManager/Interfaces/DBCommand.cs b/PluginManager/Interfaces/DBCommand.cs index 4095427..7dc759d 100644 --- a/PluginManager/Interfaces/DBCommand.cs +++ b/PluginManager/Interfaces/DBCommand.cs @@ -1,4 +1,5 @@ -using Discord.Commands; +using System.Collections.Generic; +using Discord.Commands; using Discord.WebSocket; namespace PluginManager.Interfaces; @@ -11,6 +12,11 @@ public interface DBCommand /// string Command { get; } + /// + /// Command aliases. Users may use this to execute the command + /// + List? Aliases { get; } + /// /// Command description /// @@ -44,8 +50,5 @@ public interface DBCommand /// The message that the user types /// The discord client of the bot /// true if the message was sent from DM, otherwise false. It is always false if canUseDM is false - void Execute(SocketCommandContext context, - SocketMessage message, - DiscordSocketClient client, - bool isDM); + void Execute(SocketCommandContext context, SocketMessage message, DiscordSocketClient client, bool isDM); } diff --git a/PluginManager/Items/Command.cs b/PluginManager/Items/Command.cs index ea8078b..d6b4ded 100644 --- a/PluginManager/Items/Command.cs +++ b/PluginManager/Items/Command.cs @@ -1,11 +1,12 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using Discord.WebSocket; using PluginManager.Others; namespace PluginManager.Items; -internal class Command +public class Command { /// /// The author of the command @@ -20,10 +21,7 @@ internal class Command { Author = message.Author; var data = message.Content.Split(' '); - if (data.Length > 1) - Arguments = new List(data.MergeStrings(1).Split(' ')); - else - Arguments = new List(); + Arguments = data.Length > 1 ? new List(data.MergeStrings(1).Split(' ')) : new List(); CommandName = data[0].Substring(1); PrefixUsed = data[0][0]; } @@ -46,8 +44,8 @@ internal class Command public class ConsoleCommand { - public string CommandName { get; set; } - public string Description { get; set; } - public string Usage { get; set; } - public Action Action { get; set; } + public string CommandName { get; init; } + public string Description { get; init; } + public string Usage { get; init; } + public Action Action { get; init; } } \ No newline at end of file diff --git a/PluginManager/Items/ConsoleCommandsHandler.cs b/PluginManager/Items/ConsoleCommandsHandler.cs index 0e88442..4a31091 100644 --- a/PluginManager/Items/ConsoleCommandsHandler.cs +++ b/PluginManager/Items/ConsoleCommandsHandler.cs @@ -3,31 +3,42 @@ 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"); - public static List commandList = new(); - private readonly DiscordSocketClient? client; + private static readonly PluginsManager manager = new("https://raw.githubusercontent.com/Wizzy69/installer/discord-bot-files/Plugins.txt"); + private static readonly List 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 !"); + //Console.WriteLine("Initialized console command handler !"); } private void InitializeBasicCommands() { - var pluginsLoaded = false; + commandList.Clear(); AddCommand("help", "Show help", "help ", args => @@ -43,14 +54,12 @@ public class ConsoleCommandsHandler foreach (var command in commandList) { - var pa = from p in command.Action.Method.GetParameters() - where p.Name != null - select p.ParameterType.FullName; + 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); + Console_Utilities.FormatAndAlignTable(items, TableFormat.DEFAULT); } else { @@ -70,39 +79,62 @@ public class ConsoleCommandsHandler AddCommand("lp", "Load plugins", () => { - if (pluginsLoaded) return; + if (pluginsLoaded) + return; var loader = new PluginLoader(client!); + ConsoleColor cc = Console.ForegroundColor; loader.onCMDLoad += (name, typeName, success, exception) => { - Console.ForegroundColor = ConsoleColor.Green; - if (name == null || name.Length < 2) name = typeName; + + 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 = ConsoleColor.Red; + } + Console.ForegroundColor = cc; }; loader.onEVELoad += (name, typeName, success, exception) => { - if (name == null || name.Length < 2) name = typeName; - Console.ForegroundColor = ConsoleColor.Green; + 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 = ConsoleColor.Red; + } + Console.ForegroundColor = cc; }; + loader.LoadPlugins(); + Console.ForegroundColor = cc; pluginsLoaded = true; + } ); - AddCommand("listplugs", "list available plugins", async () => { await manager.ListAvailablePlugins(); }); + 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; } @@ -116,26 +148,27 @@ public class ConsoleCommandsHandler { if (name == "") { + isDownloading = false; Console_Utilities.WriteColorText("Name is invalid"); return; } - - Console_Utilities.WriteColorText($"Failed to find plugin &b{name} &c!" + - " Use &glistplugs &ccommand to display all available plugins !" - ); + 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 + ".dll"; + 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] == "Command" || info[0] == "Event") 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)); + else if (info[0] == "Command") + Config.PluginConfig.InstalledPlugins.Add(new(name, PluginType.Command)); Console.WriteLine("\n"); @@ -146,37 +179,40 @@ public class ConsoleCommandsHandler { Console.WriteLine($"Downloading requirements for plugin : {name}"); - var lines = await ServerCom.ReadTextFromFile(info[2]); + var lines = await ServerCom.ReadTextFromURL(info[2]); foreach (var line in lines) { - if (!(line.Length > 0 && line.Contains(","))) continue; + 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(".zip")) + if (split[0].EndsWith(".zip") || split[0].EndsWith(".pak") || split[0].EndsWith(".pkg")) { Console.WriteLine($"Extracting {split[1]}"); - var proc = 0d; + var proc = 0f; var isExtracting = true; - var bar = new Console_Utilities.ProgressBar { Max = 100, Color = ConsoleColor.Green }; + var bar = new Console_Utilities.ProgressBar(ProgressBarType.NORMAL) { Max = 100f, Color = ConsoleColor.Green }; IProgress extractProgress = new Progress(value => { proc = value; }); new Thread(new Task(() => { while (isExtracting) { - bar.Update((int)proc); - if (proc >= 99.9f) break; + bar.Update(proc); + if (proc >= 99.9f) + isExtracting = false; Thread.Sleep(500); } } ).Start ).Start(); - await Functions.ExtractArchive("./" + split[1], "./", extractProgress); - bar.Update(100); + await Functions.ExtractArchive("./" + split[1], "./", extractProgress, UnzipProgressType.PercentageFromTotalSize); + bar.Update(100f); isExtracting = false; await Task.Delay(1000); bar.Update(100); @@ -187,25 +223,34 @@ public class ConsoleCommandsHandler 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 => + AddCommand("value", "read value from VariableStack", "value [key]", args => { - if (args.Length != 2) return; - if (!Config.ContainsKey(args[1])) return; + if (args.Length != 2) + return; + if (!Config.ContainsKey(args[1])) + return; var data = Config.GetValue(args[1]); Console.WriteLine($"{args[1]} => {data}"); } ); - AddCommand("add", "add variable to the system variables","add [key] [value] [isReadOnly=true/false]", args => + 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]; + if (args.Length < 4) + return; + var key = args[1]; + var value = args[2]; var isReadOnly = args[3].Equals("true", StringComparison.CurrentCultureIgnoreCase); try @@ -222,46 +267,167 @@ public class ConsoleCommandsHandler AddCommand("remv", "remove variable from system variables", "remv [key]", args => { - if (args.Length < 2) return; + if (args.Length < 2) + return; Config.RemoveKey(args[1]); } ); - AddCommand("vars", "Display all variables", () => - { - var d = Config.GetAllVariables(); - var data = new List(); - data.Add(new[] { "-", "-" }); - data.Add(new[] { "Key", "Value" }); - data.Add(new[] { "-", "-" }); - foreach (var kvp in d) data.Add(new[] { kvp.Key, kvp.Value.ToString()! }); - data.Add(new[] { "-", "-" }); - Console_Utilities.FormatAndAlignTable(data); - } - ); - AddCommand("sd", "Shuts down the discord bot", async () => { - if (client is null) return; + if (client is null) + return; + bool run = true; + var t = new Thread(() => + { + Console_Utilities.ProgressBar bar = new Console_Utilities.ProgressBar(ProgressBarType.NO_END); + while (run) + { + bar.Update(1); + Thread.Sleep(50); + } + }); + t.Start(); + await Config.SaveConfig(SaveType.NORMAL); + await Config.SaveConfig(SaveType.BACKUP); + await Task.Delay(4000); + run = false; + Console.WriteLine(); await client.StopAsync(); await client.DisposeAsync(); - Config.SaveConfig(); 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 action) { - commandList.Add(new ConsoleCommand - { - CommandName = command, - Description = description, - Action = action, - Usage = usage - }); + 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"); } @@ -278,7 +444,7 @@ public class ConsoleCommandsHandler public static bool CommandExists(string command) { - return !(GetCommand(command) is null); + return GetCommand(command) is not null; } public static ConsoleCommand? GetCommand(string command) @@ -286,8 +452,24 @@ public class ConsoleCommandsHandler 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]) @@ -295,12 +477,14 @@ public class ConsoleCommandsHandler if (removeCommandExecution) { Console.SetCursorPosition(0, Console.CursorTop - 1); - for (int i = 0; i < command.Length; i++) Console.Write(" "); + for (int i = 0; i < command.Length + 30; i++) + Console.Write(" "); Console.SetCursorPosition(0, Console.CursorTop); } Console.WriteLine(); item.Action(args); + return true; } diff --git a/PluginManager/Loaders/Loader.cs b/PluginManager/Loaders/Loader.cs index 82aac6f..d5e10da 100644 --- a/PluginManager/Loaders/Loader.cs +++ b/PluginManager/Loaders/Loader.cs @@ -3,29 +3,32 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using System.Threading.Tasks; + +using PluginManager.Online.Updates; using PluginManager.Others; namespace PluginManager.Loaders; internal class LoaderArgs : EventArgs { - internal string? PluginName { get; init; } - internal string? TypeName { get; init; } - internal bool IsLoaded { get; init; } - internal Exception? Exception { get; init; } - internal object? Plugin { get; init; } + internal string? PluginName { get; init; } + internal string? TypeName { get; init; } + internal bool IsLoaded { get; init; } + internal Exception? Exception { get; init; } + internal object? Plugin { get; init; } } internal class Loader { internal Loader(string path, string extension) { - this.path = path; + this.path = path; this.extension = extension; } - private string path { get; } + private string path { get; } private string extension { get; } @@ -49,16 +52,17 @@ internal class Loader var files = Directory.GetFiles(path, $"*.{extension}", SearchOption.AllDirectories); foreach (var file in files) { + Assembly.LoadFrom(file); if (FileLoaded != null) { var args = new LoaderArgs { - Exception = null, - TypeName = nameof(T), - IsLoaded = false, - PluginName = file, - Plugin = null + Exception = null, + TypeName = nameof(T), + IsLoaded = false, + PluginName = new FileInfo(file).Name.Split('.')[0], + Plugin = null }; FileLoaded.Invoke(args); } @@ -83,13 +87,13 @@ internal class Loader if (PluginLoaded != null) PluginLoaded.Invoke(new LoaderArgs - { - Exception = null, - IsLoaded = true, - PluginName = type.FullName, - TypeName = nameof(T), - Plugin = plugin - } + { + Exception = null, + IsLoaded = true, + PluginName = type.FullName, + TypeName = nameof(T), + Plugin = plugin + } ); } catch (Exception ex) diff --git a/PluginManager/Loaders/PluginLoader.cs b/PluginManager/Loaders/PluginLoader.cs index 6fdaf74..6e57705 100644 --- a/PluginManager/Loaders/PluginLoader.cs +++ b/PluginManager/Loaders/PluginLoader.cs @@ -1,7 +1,15 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + using Discord.WebSocket; + using PluginManager.Interfaces; +using PluginManager.Online.Helpers; +using PluginManager.Online.Updates; using PluginManager.Others; namespace PluginManager.Loaders; @@ -15,8 +23,8 @@ public class PluginLoader private const string pluginCMDFolder = @"./Data/Plugins/Commands/"; private const string pluginEVEFolder = @"./Data/Plugins/Events/"; - internal const string pluginCMDExtension = "dll"; - internal const string pluginEVEExtension = "dll"; + internal const string pluginCMDExtension = "dll"; + internal const string pluginEVEExtension = "dll"; private readonly DiscordSocketClient _client; /// @@ -52,42 +60,97 @@ public class PluginLoader /// /// The main mathod that is called to load all events /// - public void LoadPlugins() + 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(); - Events = new List(); + Events = new List(); Functions.WriteLogFile("Starting plugin loader ... Client: " + _client.CurrentUser.Username); Console.WriteLine("Loading plugins"); var commandsLoader = new Loader(pluginCMDFolder, pluginCMDExtension); - var eventsLoader = new Loader(pluginEVEFolder, pluginEVEExtension); + var eventsLoader = new Loader(pluginEVEFolder, pluginEVEExtension); - commandsLoader.FileLoaded += OnCommandFileLoaded; + commandsLoader.FileLoaded += OnCommandFileLoaded; commandsLoader.PluginLoaded += OnCommandLoaded; - eventsLoader.FileLoaded += EventFileLoaded; + eventsLoader.FileLoaded += EventFileLoaded; eventsLoader.PluginLoaded += OnEventLoaded; Commands = commandsLoader.Load(); - Events = eventsLoader.Load(); + Events = eventsLoader.Load(); + } private void EventFileLoaded(LoaderArgs e) { - if (e.IsLoaded) Functions.WriteLogFile($"[EVENT] Event from file [{e.PluginName}] has been successfully created !"); + 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 !"); + if (!e.IsLoaded) + { + Functions.WriteLogFile($"[CMD] Command from file [{e.PluginName}] has been successfully loaded !"); + } } private void OnEventLoaded(LoaderArgs e) { - if (e.IsLoaded) ((DBEvent)e.Plugin!).Start(_client); + try + { + if (e.IsLoaded) + ((DBEvent)e.Plugin!).Start(_client); - onEVELoad?.Invoke(((DBEvent)e.Plugin!).name, e.TypeName!, e.IsLoaded, e.Exception); + onEVELoad?.Invoke(((DBEvent)e.Plugin!).name, e.TypeName!, e.IsLoaded, e.Exception); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + Console.WriteLine("Plugin: " + e.PluginName); + Console.WriteLine("Type: " + e.TypeName); + Console.WriteLine("IsLoaded: " + e.IsLoaded); + } } private void OnCommandLoaded(LoaderArgs e) diff --git a/PluginManager/Online/Helpers/OnlineFunctions.cs b/PluginManager/Online/Helpers/OnlineFunctions.cs index 213a056..343ffe1 100644 --- a/PluginManager/Online/Helpers/OnlineFunctions.cs +++ b/PluginManager/Online/Helpers/OnlineFunctions.cs @@ -18,32 +18,32 @@ namespace PluginManager.Online.Helpers /// The that is used to track the download progress /// The cancellation token /// - internal static async Task DownloadFileAsync(this HttpClient client, string url, Stream destination, - IProgress? progress = null, IProgress? downloadedBytes = null, CancellationToken cancellation = default) + internal static async Task DownloadFileAsync(this HttpClient client, string url, Stream destination, IProgress? progress = null, IProgress? downloadedBytes = null, int bufferSize = 81920, CancellationToken cancellation = default) { - using (var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead)) + using (var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellation)) { var contentLength = response.Content.Headers.ContentLength; - using (var download = await response.Content.ReadAsStreamAsync()) + using (var download = await response.Content.ReadAsStreamAsync(cancellation)) { - // Ignore progress reporting when no progress reporter was // passed or when the content length is unknown if (progress == null || !contentLength.HasValue) { - await download.CopyToAsync(destination); + await download.CopyToAsync(destination, cancellation); return; } // Convert absolute progress (bytes downloaded) into relative progress (0% - 100%) var relativeProgress = new Progress(totalBytes => - { - progress.Report((float)totalBytes / contentLength.Value * 100); - downloadedBytes?.Report(totalBytes); - }); + { + progress.Report((float)totalBytes / contentLength.Value * 100); + downloadedBytes?.Report(totalBytes); + } + ); + // Use extension method to report progress while downloading - await download.CopyToOtherStreamAsync(destination, 81920, relativeProgress, cancellation); + await download.CopyToOtherStreamAsync(destination, bufferSize, relativeProgress, cancellation); progress.Report(1); } } @@ -57,10 +57,8 @@ namespace PluginManager.Online.Helpers /// internal static async Task DownloadStringAsync(string url, CancellationToken cancellation = default) { - using (var client = new HttpClient()) - { - return await client.GetStringAsync(url); - } + using var client = new HttpClient(); + return await client.GetStringAsync(url, cancellation); } diff --git a/PluginManager/Online/Helpers/VersionString.cs b/PluginManager/Online/Helpers/VersionString.cs new file mode 100644 index 0000000..1067228 --- /dev/null +++ b/PluginManager/Online/Helpers/VersionString.cs @@ -0,0 +1,91 @@ +using PluginManager.Others; + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Linq; + +namespace PluginManager.Online.Helpers +{ + public class VersionString + { + public int PackageVersionID; + public int PackageMainVersion; + public int PackageCheckVersion; + + public VersionString(string version) + { + string[] data = version.Split('.'); + try + { + PackageVersionID = int.Parse(data[0]); + PackageMainVersion = int.Parse(data[1]); + PackageCheckVersion = int.Parse(data[2]); + } + catch (Exception ex) + { + throw new Exception("Failed to write Version", ex); + } + } + + + + #region operators + public static bool operator >(VersionString s1, VersionString s2) + { + if (s1.PackageVersionID > s2.PackageVersionID) return true; + if (s1.PackageVersionID == s2.PackageVersionID) + { + if (s1.PackageMainVersion > s2.PackageMainVersion) return true; + if (s1.PackageMainVersion == s2.PackageMainVersion && s1.PackageCheckVersion > s2.PackageCheckVersion) return true; + + } + return false; + } + public static bool operator <(VersionString s1, VersionString s2) => !(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) => !(s1 == s2); + + 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 string ToString() + { + return "{PackageID: " + PackageVersionID + ", PackageVersion: " + PackageMainVersion + ", PackageCheckVersion: " + PackageCheckVersion + "}"; + } + + public string ToShortString() + { + if (PackageVersionID == 0 && PackageCheckVersion == 0 && PackageMainVersion == 0) + return "Unknown"; + return $"{PackageVersionID}.{PackageMainVersion}.{PackageCheckVersion}"; + } + + public static VersionString? GetVersionOfPackage(string pakName) + { + if (!Config.PluginVersionsContainsKey(pakName)) + return null; + return new VersionString(Config.GetPluginVersion(pakName)); + } + + public static async Task GetVersionOfPackageFromWeb(string pakName) + { + string url = "https://raw.githubusercontent.com/Wizzy69/installer/discord-bot-files/Versions"; + List 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); + } + + } +} diff --git a/PluginManager/Online/PluginsManager.cs b/PluginManager/Online/PluginsManager.cs index d3bf569..2e06edb 100644 --- a/PluginManager/Online/PluginsManager.cs +++ b/PluginManager/Online/PluginsManager.cs @@ -1,7 +1,10 @@ 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; @@ -30,20 +33,21 @@ public class PluginsManager { try { - var list = await ServerCom.ReadTextFromFile(PluginsLink); + var list = await ServerCom.ReadTextFromURL(PluginsLink); var lines = list.ToArray(); var data = new List(); - var op = Functions.GetOperatingSystem(); + var op = Functions.GetOperatingSystem(); - var len = lines.Length; - string[] titles = { "Name", "Description", "Plugin Type", "Libraries", "Installed" }; + 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; + if (lines[i].Length <= 2) + continue; var content = lines[i].Split(','); var display = new string[titles.Length]; if (op == OperatingSystem.WINDOWS) @@ -53,11 +57,7 @@ public class PluginsManager display[0] = content[0]; display[1] = content[1]; display[2] = content[2]; - if (content.Length == 6 && (content[5] != null || content[5].Length > 2)) - display[3] = ((await ServerCom.ReadTextFromFile(content[5])).Count + 1).ToString(); - - else - display[3] = "1"; + 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 @@ -72,7 +72,7 @@ public class PluginsManager display[0] = content[0]; display[1] = content[1]; display[2] = content[2]; - if (content.Length == 6 && (content[5] != null || content[5].Length > 2)) display[3] = ((await ServerCom.ReadTextFromFile(content[5])).Count + 1).ToString(); + 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 @@ -84,11 +84,11 @@ public class PluginsManager data.Add(new[] { "-", "-", "-", "-", "-" }); - Console_Utilities.FormatAndAlignTable(data); + Console_Utilities.FormatAndAlignTable(data, TableFormat.CENTER_EACH_COLUMN_BASED); } catch (Exception exception) { - Console.WriteLine("Failed to execute command: listlang\nReason: " + exception.Message); + Console.WriteLine("Failed to execute command: listplugs\nReason: " + exception.Message); Functions.WriteErrFile(exception.ToString()); } } @@ -102,16 +102,18 @@ public class PluginsManager { try { - var list = await ServerCom.ReadTextFromFile(PluginsLink); + var list = await ServerCom.ReadTextFromURL(PluginsLink); var lines = list.ToArray(); - var len = lines.Length; + var len = lines.Length; for (var i = 0; i < len; i++) { var contents = lines[i].Split(','); if (contents[0] == name) { - if (contents.Length == 6) return new[] { contents[2], contents[3], contents[5] }; - if (contents.Length == 5) return new[] { contents[2], contents[3], string.Empty }; + if (contents.Length == 6) + return new[] { contents[2], contents[3], contents[5] }; + if (contents.Length == 5) + return new[] { contents[2], contents[3], string.Empty }; throw new Exception("Failed to download plugin. Invalid Argument Length"); } } diff --git a/PluginManager/Online/ServerCom.cs b/PluginManager/Online/ServerCom.cs index 67c2401..925bbf5 100644 --- a/PluginManager/Online/ServerCom.cs +++ b/PluginManager/Online/ServerCom.cs @@ -9,15 +9,14 @@ using PluginManager.Others; namespace PluginManager.Online { - public class ServerCom + public static class ServerCom { - /// /// Read all lines from a file async /// /// The link of the file /// - public static async Task> ReadTextFromFile(string link) + public static async Task> ReadTextFromURL(string link) { string response = await OnlineFunctions.DownloadStringAsync(link); string[] lines = response.Split('\n'); @@ -52,15 +51,12 @@ namespace PluginManager.Online /// public static async Task DownloadFileAsync(string URL, string location) { - bool isDownloading = true; - int c_progress = 0; + bool isDownloading = true; + float c_progress = 0; - Console_Utilities.ProgressBar pbar = new Console_Utilities.ProgressBar { Max = 100, NoColor = true }; + Console_Utilities.ProgressBar pbar = new Console_Utilities.ProgressBar(ProgressBarType.NORMAL) { Max = 100f, NoColor = true }; - IProgress progress = new Progress(percent => - { - c_progress = (int)percent; - }); + IProgress progress = new Progress(percent => { c_progress = percent; }); Task updateProgressBarTask = new Task(() => @@ -68,7 +64,8 @@ namespace PluginManager.Online while (isDownloading) { pbar.Update(c_progress); - if (c_progress == 100) break; + if (c_progress == 100f) + break; Thread.Sleep(500); } } @@ -78,8 +75,8 @@ namespace PluginManager.Online await DownloadFileAsync(URL, location, progress); - c_progress = 100; - pbar.Update(100); + c_progress = pbar.Max; + pbar.Update(100f); isDownloading = false; } } diff --git a/PluginManager/Online/Updates/PluginUpdater.cs b/PluginManager/Online/Updates/PluginUpdater.cs new file mode 100644 index 0000000..fb43eb3 --- /dev/null +++ b/PluginManager/Online/Updates/PluginUpdater.cs @@ -0,0 +1,51 @@ +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 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 DownloadUpdateInfo(string pakName) + { + string url = "https://raw.githubusercontent.com/Wizzy69/installer/discord-bot-files/Versions"; + List 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); + } + + + } +} diff --git a/PluginManager/Online/Updates/Update.cs b/PluginManager/Online/Updates/Update.cs new file mode 100644 index 0000000..39eaed4 --- /dev/null +++ b/PluginManager/Online/Updates/Update.cs @@ -0,0 +1,36 @@ +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()}"; + } + + } +} diff --git a/PluginManager/Others/Console Utilities.cs b/PluginManager/Others/Console Utilities.cs index 00dc30a..c1277f0 100644 --- a/PluginManager/Others/Console Utilities.cs +++ b/PluginManager/Others/Console Utilities.cs @@ -1,25 +1,89 @@ -using System; +using Discord; + +using System; using System.Collections.Generic; namespace PluginManager.Others { - public class Console_Utilities + public static class Console_Utilities { + public static void Initialize() + { + if (!Config.ContainsKey("TableVariables")) + Config.AddValueToVariables("TableVariables", new Dictionary { { "DefaultSpace", "3" } }, false); + if (!Config.ContainsKey("ColorDataBase")) + Config.AddValueToVariables("ColorDataBase", new Dictionary() + { + { 'g', ConsoleColor.Green }, + { 'b', ConsoleColor.Blue }, + { 'r', ConsoleColor.Red }, + { 'm', ConsoleColor.Magenta }, + { 'y', ConsoleColor.Yellow }, + }, false + ); + + if (!Config.ContainsKey("ColorPrefix")) + Config.AddValueToVariables("ColorPrefix", '&', false); + } + + /// /// Progress bar object /// public class ProgressBar { - public int Max { get; init; } - public ConsoleColor Color { get; init; } - public bool NoColor { get; init; } + 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; } - public void Update(int progress, double speed = -1, string? unit = null) + private int BarLength = 32; + private int position = 1; + private bool positive = true; + + 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("["); - Console.CursorLeft = 32; + 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; @@ -28,129 +92,207 @@ namespace PluginManager.Others for (int i = 0; i < onechunk * progress; i++) { - if (NoColor) - Console.BackgroundColor = ConsoleColor.Black; //this.Color - else - Console.BackgroundColor = this.Color; - Console.CursorLeft = position++; + Console.BackgroundColor = NoColor ? ConsoleColor.Black : this.Color; + Console.CursorLeft = position++; Console.Write("#"); } - for (int i = position; i <= 31; i++) + for (int i = position; i < BarLength; i++) { - if (NoColor) - Console.BackgroundColor = ConsoleColor.Black; // background of empty bar - else - Console.BackgroundColor = ConsoleColor.DarkGray; - Console.CursorLeft = position++; + Console.BackgroundColor = NoColor ? ConsoleColor.Black : ConsoleColor.DarkGray; + Console.CursorLeft = position++; Console.Write(" "); } - Console.CursorLeft = 35; + Console.CursorLeft = BarLength + 4; Console.BackgroundColor = ConsoleColor.Black; - if (speed == -1 || unit == null) - { - if (progress == Max) - Console.Write(progress.ToString() + " % ✓"); - else Console.Write(progress.ToString() + " % "); - } + if (progress.CanAproximateTo(Max)) + Console.Write(progress + " % ✓"); else - Console.Write(progress.ToString() + $"{speed} {unit}/s "); - + Console.Write(MathF.Round(progress, 2) + " % "); } } + private static bool CanAproximateTo(this float f, float y) => (MathF.Abs(f - y) < 0.000001); + + /// /// A way to create a table based on input data /// /// The List of arrays of strings that represent the rows. - public static void FormatAndAlignTable(List data) + public static void FormatAndAlignTable(List data, TableFormat format) { - char tableLine = '-'; - char tableCross = '+'; - char tableWall = '|'; - - int[] len = new int[data[0].Length]; - foreach (var line in data) + if (format == TableFormat.CENTER_EACH_COLUMN_BASED) { - for (int i = 0; i < line.Length; i++) - if (line[i].Length > len[i]) - len[i] = line[i].Length; - } + 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++) + foreach (string[] row in data) { - 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(" "); - } + 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(" "); + } - 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(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>("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(" "); } - if (row[l][0] == tableLine) Console.Write(tableCross); - else Console.Write(tableWall); + Console.WriteLine(); } - Console.WriteLine(); //end line + return; } + + throw new Exception("Unknown type of table"); } - public static void WriteColorText(string text, bool appendNewLine = true) + public static void WriteColorText(string text, bool appendNewLineAtEnd = true) { - - string[] words = text.Split(' '); - ConsoleColor fg = Console.ForegroundColor; - Dictionary colors = new Dictionary() + ConsoleColor initialForeGround = Console.ForegroundColor; + char[] input = text.ToCharArray(); + for (int i = 0; i < input.Length; i++) { - { "&g", ConsoleColor.Green }, - { "&b", ConsoleColor.Blue }, - { "&r", ConsoleColor.Red }, - { "&m", ConsoleColor.Magenta }, - { "&y", ConsoleColor.Yellow }, - { "&c", fg } - }; - foreach (string word in words) - { - if (word.Length >= 2) + if (input[i] == Config.GetValue("ColorPrefix")) { - string prefix = word.Substring(0, 2); - if (colors.ContainsKey(prefix)) - Console.ForegroundColor = colors[prefix]; + if (i + 1 < input.Length) + { + if (Config.GetValue>("ColorDataBase")!.ContainsKey(input[i + 1])) + { + Console.ForegroundColor = Config.GetValue>("ColorDataBase")![input[i + 1]]; + i++; + } + else if (input[i + 1] == 'c') + { + Console.ForegroundColor = initialForeGround; + i++; + } + } } - - string m = word; - foreach (var key in colors.Keys) { m = m.Replace(key, ""); } - - Console.Write(m + " "); + else + Console.Write(input[i]); } - if (appendNewLine) - Console.Write('\n'); - Console.ForegroundColor = fg; + Console.ForegroundColor = initialForeGround; + if (appendNewLineAtEnd) + Console.WriteLine(); } - } } diff --git a/PluginManager/Others/Enums.cs b/PluginManager/Others/Enums.cs index c516802..fb63614 100644 --- a/PluginManager/Others/Enums.cs +++ b/PluginManager/Others/Enums.cs @@ -26,4 +26,11 @@ public enum OutputLogLevel { NONE, INFO, WARNING, ERROR, CRITICAL } /// /// Plugin Type /// -public enum PluginType { Command, Event, Unknown } \ No newline at end of file +public enum PluginType { Command, Event, Unknown } + +public enum UnzipProgressType { PercentageFromNumberOfFiles, PercentageFromTotalSize } + +public enum TableFormat { CENTER_EACH_COLUMN_BASED, CENTER_OVERALL_LENGTH, DEFAULT } + +public enum SaveType { NORMAL, BACKUP } +public enum ProgressBarType { NORMAL, NO_END } \ No newline at end of file diff --git a/PluginManager/Others/Functions.cs b/PluginManager/Others/Functions.cs index c1df251..2f26f9e 100644 --- a/PluginManager/Others/Functions.cs +++ b/PluginManager/Others/Functions.cs @@ -4,6 +4,7 @@ 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; @@ -25,17 +26,22 @@ namespace PluginManager.Others /// /// The location for all logs /// - public static readonly string logFolder = @"./Output/Logs/"; + public static readonly string logFolder = @"./Data/Output/Logs/"; /// /// The location for all errors /// - public static readonly string errFolder = @"./Output/Errors/"; + public static readonly string errFolder = @"./Data/Output/Errors/"; /// /// Archives folder /// - public static readonly string pakFolder = @"./Data/Resources/PAK/"; + public static readonly string pakFolder = @"./Data/PAKS/"; + + /// + /// Beta testing folder + /// + public static readonly string betaFolder = @"./Data/BetaTest/"; /// @@ -50,25 +56,13 @@ namespace PluginManager.Others Directory.CreateDirectory(pakFolder); if (!File.Exists(archFile)) throw new FileNotFoundException("Failed to load file !"); - Stream? textValue = null; - var fs = new FileStream(archFile, FileMode.Open); - var zip = new ZipArchive(fs, ZipArchiveMode.Read); - foreach (var entry in zip.Entries) - { - if (entry.Name == FileName || entry.FullName == FileName) - { - Stream s = entry.Open(); - StreamReader reader = new StreamReader(s); - textValue = reader.BaseStream; - textValue.Position = 0; - reader.Close(); - s.Close(); - fs.Close(); - break; - } - } + using ZipArchive archive = ZipFile.OpenRead(archFile); + ZipArchiveEntry? entry = archive.GetEntry(FileName); + if (entry is null) return Stream.Null; + MemoryStream stream = new MemoryStream(); + await (entry?.Open()!).CopyToAsync(stream); - return textValue; + return stream; } /// @@ -77,8 +71,8 @@ namespace PluginManager.Others /// The message to be wrote public static void WriteLogFile(string LogMessage) { - string logsPath = logFolder + "Log.txt"; - if (!Directory.Exists(logFolder)) Directory.CreateDirectory(logFolder); + string logsPath = logFolder + $"{DateTime.Today.ToShortDateString().Replace("/", "-").Replace("\\", "-")} Log.txt"; + Directory.CreateDirectory(logFolder); File.AppendAllText(logsPath, LogMessage + " \n"); } @@ -88,8 +82,8 @@ namespace PluginManager.Others /// The message to be wrote public static void WriteErrFile(string ErrMessage) { - string errPath = errFolder + "Error.txt"; - if (!Directory.Exists(errFolder)) Directory.CreateDirectory(errFolder); + string errPath = errFolder + $"{DateTime.Today.ToShortDateString().Replace("/", "-").Replace("\\", "-")} Error.txt"; + Directory.CreateDirectory(errFolder); File.AppendAllText(errPath, ErrMessage + " \n"); } @@ -101,8 +95,8 @@ namespace PluginManager.Others /// A string built based on the array public static string MergeStrings(this string[] s, int indexToStart) { - string r = ""; - int len = s.Length; + string r = ""; + int len = s.Length; if (len <= indexToStart) return ""; for (int i = indexToStart; i < len - 1; ++i) { @@ -152,9 +146,9 @@ namespace PluginManager.Others if (!stream.CanRead) throw new InvalidOperationException("The stream is not readable."); if (!destination.CanWrite) throw new ArgumentException("Destination stream is not writable", nameof(destination)); - byte[] buffer = new byte[bufferSize]; - long totalBytesRead = 0; - int bytesRead; + byte[] buffer = new byte[bufferSize]; + long totalBytesRead = 0; + int bytesRead; while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) { await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); @@ -169,33 +163,69 @@ namespace PluginManager.Others /// /// The zip location /// The target location + /// The progress that is updated as a file is processed + /// The type of progress /// - public static async Task ExtractArchive(string zip, string folder, IProgress progress) + public static async Task ExtractArchive(string zip, string folder, IProgress progress, UnzipProgressType type) { if (!Directory.Exists(folder)) Directory.CreateDirectory(folder); using (ZipArchive archive = ZipFile.OpenRead(zip)) { - int totalZIPFiles = archive.Entries.Count(); - int currentZIPFile = 0; - foreach (ZipArchiveEntry entry in archive.Entries) + if (type == UnzipProgressType.PercentageFromNumberOfFiles) { - if (entry.FullName.EndsWith("/")) - Directory.CreateDirectory(Path.Combine(folder, entry.FullName)); + 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); + 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; + } - else try { entry.ExtractToFile(Path.Combine(folder, entry.FullName), true); + currentSize += (ulong)entry.CompressedLength; } - catch + catch (Exception ex) { + Console.WriteLine($"Failed to extract {entry.Name}. Exception: {ex.Message}"); } - currentZIPFile++; - await Task.Delay(10); - progress.Report((float)currentZIPFile / totalZIPFiles * 100); + await Task.Delay(10); + progress.Report((float)currentSize / zipSize * 100); + } } } } @@ -235,9 +265,9 @@ namespace PluginManager.Others /// public static async Task SaveToJsonFile(string file, T Data) { - var s = File.OpenWrite(file); - await JsonSerializer.SerializeAsync(s, Data, typeof(T), new JsonSerializerOptions { WriteIndented = true }); - s.Close(); + MemoryStream str = new MemoryStream(); + await JsonSerializer.SerializeAsync(str, Data, typeof(T), new JsonSerializerOptions { WriteIndented = true }); + await File.WriteAllBytesAsync(file, str.ToArray()); } /// @@ -250,8 +280,7 @@ namespace PluginManager.Others { Stream text; if (File.Exists(input)) - text = File.Open(input, FileMode.OpenOrCreate); - + text = new MemoryStream(await File.ReadAllBytesAsync(input)); else text = new MemoryStream(Encoding.ASCII.GetBytes(input)); text.Position = 0; @@ -303,5 +332,15 @@ namespace PluginManager.Others 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); + } + } } } diff --git a/README.md b/README.md index 5aee53e..062e083 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ 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) -- [Avalonia UI](https://avaloniaui.net/) for `DiscordBotUI` extension ## Plugins @@ -17,9 +16,10 @@ Plugin Types: 1. Commands 2. Events + ### How to create a plugin diff --git a/Roles/AddRole.cs b/Roles/AddRole.cs new file mode 100644 index 0000000..c1ea4b8 --- /dev/null +++ b/Roles/AddRole.cs @@ -0,0 +1,57 @@ +using System.IO.Compression; +using System.Runtime.CompilerServices; +using Discord; +using Discord.Commands; +using Discord.Rest; +using Discord.WebSocket; +using Microsoft.Win32.SafeHandles; +using PluginManager.Interfaces; +using PluginManager.Others; +using Roles.Internals; + +namespace Roles +{ + public class AddRole : DBCommand + { + public string Command => "addrole"; + + public List Aliases => new() { "ar", "addr", "roleadd" }; + + public string Description => "Role options"; + + public string Usage => "addrole [user1] [user2] ... [role1] [role2] ..."; + + public bool canUseDM => false; + + public bool canUseServer => true; + + public bool requireAdmin => true; + + public async void Execute(SocketCommandContext context, SocketMessage message, DiscordSocketClient client, bool isDM) + { + if (message.MentionedUsers.Count == 0 || message.MentionedRoles.Count == 0) + { + await context.Channel.SendMessageAsync($"Invalid invocation\nUsage:{Usage}"); + return; + } + + try + { + var users = message.MentionedUsers; + var roles = message.MentionedRoles as IEnumerable; + + foreach (var user in users) + { + SocketGuildUser? usr = context.Client.GetUser(user.Username, user.Discriminator) as SocketGuildUser; + if (usr is null) + throw new Exception("User is null"); + await usr.AddRolesAsync(roles); + } + } + catch (Exception ex) + { + await context.Channel.SendMessageAsync(ex.Message); + } + } + } +} diff --git a/Roles/Internals/RoleManagement.cs b/Roles/Internals/RoleManagement.cs new file mode 100644 index 0000000..edcf616 --- /dev/null +++ b/Roles/Internals/RoleManagement.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord; +using Discord.WebSocket; +using PluginManager.Others; + +namespace Roles.Internals +{ + internal static class RoleManagement + { + internal static async void AddRole(this SocketGuildUser user, string roleName) + { + string role = roleName; + IRole? r = user.Guild.Roles.FirstOrDefault(rl => rl.Name == role || rl.Mention == role); + if (r is null) + throw new Exception("The role does not exist"); + + try + { + await user.AddRoleAsync(r); + } + catch (Exception ex) + { + if (ex.Message.Contains("Permission", StringComparison.CurrentCultureIgnoreCase)) + throw new Exception("Insufficient permissions"); + } + } + + internal static async void AddRole(this SocketGuildUser user, IRole role) + { + try + { + await user.AddRoleAsync(role); + } + catch (Exception ex) + { + if (ex.Message.Contains("Permission", StringComparison.CurrentCultureIgnoreCase)) + throw new Exception("Insufficient permissions"); + } + } + + internal static async void AddRoles(this SocketGuildUser user, string[] roleNames) + { + foreach (string rolename in roleNames) + { + string roleName = rolename; + IRole? r = user.Guild.Roles.FirstOrDefault(rl => rl.Name == roleName || rl.Mention == roleName); + if (r is null) + throw new Exception("The role does not exist"); + + try + { + await user.AddRoleAsync(r); + } + catch (Exception ex) + { + if (ex.Message.Contains("Permission", StringComparison.CurrentCultureIgnoreCase)) + throw new Exception("Insufficient permissions"); + } + } + } + + internal static async void AddRoles(this SocketGuildUser user, IEnumerable roles) + { + try + { + await user.AddRolesAsync(roles); + } + catch (Exception ex) + { + if (ex.Message.Contains("Permission", StringComparison.CurrentCultureIgnoreCase)) + throw new Exception("Insufficient permissions"); + } + } + } +} diff --git a/Roles/Roles.csproj b/Roles/Roles.csproj new file mode 100644 index 0000000..b468d70 --- /dev/null +++ b/Roles/Roles.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/DiscordBotWithAPI.sln b/SethDiscordBot.sln similarity index 89% rename from DiscordBotWithAPI.sln rename to SethDiscordBot.sln index bccffb6..13ea9af 100644 --- a/DiscordBotWithAPI.sln +++ b/SethDiscordBot.sln @@ -21,6 +21,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EVE_LevelingSystem", "EVE_L EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CMD_LevelingSystem", "CMD_LevelingSystem\CMD_LevelingSystem.csproj", "{1A4E49FF-9A0A-4C54-AF35-CFFBA64353D9}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Roles", "Roles\Roles.csproj", "{954F2AA9-6624-4554-946D-0F17B84487C3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -51,6 +53,10 @@ Global {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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -62,6 +68,7 @@ Global {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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3FB3C5DE-ED21-4D2E-ABDD-3A00EE4A2FFF} diff --git a/TODO b/TODO deleted file mode 100644 index 91e5426..0000000 --- a/TODO +++ /dev/null @@ -1,70 +0,0 @@ -Discord Bot: - ✔ Create bootloader - ✔ Create commands handler - ✔ Create bot launcher - ✔ Enable startup commands - ✔ Enable console input - ☐ Create self update feature - - - -Plugin Manager: - Define plugin interface: - ✔ DBCommand - ✔ DBPlugin - - Functions.cs: - ✔ Read from file - ✔ Read from archive (PAK) - ✔ Write Logs & Errors - ✔ Manipulate settings (files) and strings - ✔ Stream copy async - - Console Utilities: - ✔ Progress bar - ✔ Create table - ✔ Write to console with colors - - Discord Permissions: - ✔ Check if user has permission - ✔ Check if user is owner - - Discord Plugins: - ✔ Create loader for commands - ✔ Create loader for events - ☐ Improve memory efficiency - ☐ Improve performance - ☐ Improve stability - - Language System: - ✔ Create language system - ✔ Load language files - - Server Communication: - ✔ Plugin Download system - ✔ Language Download system - ☐ Move to a new server - ☐ Create plugin versioning system - ☐ Create plugin update system - - - -Plugins: - Events: - ✔ Leveling system - Utilities: - ✔ Random number generator - ✔ Flip a coin - ✔ Poll - - Commands: - ✔ Leveling system - ☐ Music Commands @started - - // Windows only version - // Download as a patch but replaces old DiscordBot executable with a new one - // Adds new dll to support windows forms - // Must act the same as the old version - ☐ Create version of discord bot with windows form - ☐ Download system and patch will result in a windows only based version of bot - ☐ Possibility to reverse patch to get back to original version \ No newline at end of file diff --git a/Version.txt b/Version.txt deleted file mode 100644 index c227083..0000000 --- a/Version.txt +++ /dev/null @@ -1 +0,0 @@ -0 \ No newline at end of file