diff --git a/DiscordBot/Bot/Actions/Extra/PluginMethods.cs b/DiscordBot/Bot/Actions/Extra/PluginMethods.cs index 5458f93..dacb43a 100644 --- a/DiscordBot/Bot/Actions/Extra/PluginMethods.cs +++ b/DiscordBot/Bot/Actions/Extra/PluginMethods.cs @@ -131,7 +131,9 @@ internal static class PluginMethods IProgress progress = new Progress(p => { downloadTask.Value = p; }); - await ServerCom.DownloadFileAsync(pluginLink, $"{Application.CurrentApplication.ApplicationEnvironmentVariables["PluginFolder"]}/{pluginData.Name}.dll", progress); + string baseFolder = Application.CurrentApplication.ApplicationEnvironmentVariables.Get("PluginFolder"); + + await ServerCom.DownloadFileAsync(pluginLink, $"{baseFolder}/{pluginData.Name}.dll", progress); downloadTask.Increment(100); @@ -177,12 +179,9 @@ internal static class PluginMethods task.IsIndeterminate = true; downloadTasks.Add(new Tuple, string, string>(task, progress, dependency.DownloadLink, dependency.DownloadLocation)); } - - int maxParallelDownloads = 5; - - if (Application.CurrentApplication.ApplicationEnvironmentVariables.ContainsKey("MaxParallelDownloads")) - maxParallelDownloads = int.Parse(Application.CurrentApplication.ApplicationEnvironmentVariables["MaxParallelDownloads"]); - + + int maxParallelDownloads = Application.CurrentApplication.ApplicationEnvironmentVariables.Get("MaxParallelDownloads", 5); + var options = new ParallelOptions() { MaxDegreeOfParallelism = maxParallelDownloads, diff --git a/DiscordBot/Bot/Actions/Extra/SettingsConfigExtra.cs b/DiscordBot/Bot/Actions/Extra/SettingsConfigExtra.cs index 3d34fe2..b0a6317 100644 --- a/DiscordBot/Bot/Actions/Extra/SettingsConfigExtra.cs +++ b/DiscordBot/Bot/Actions/Extra/SettingsConfigExtra.cs @@ -15,8 +15,7 @@ internal static class SettingsConfigExtra if (!Application.CurrentApplication.ApplicationEnvironmentVariables.ContainsKey(key)) return; - Application.CurrentApplication.ApplicationEnvironmentVariables[key] = string.Join(' ', value); - // Config.Application.CurrentApplication.ApplicationEnvironmentVariables.SaveToFile().Wait(); + Application.CurrentApplication.ApplicationEnvironmentVariables.Add(key, string.Join(' ', value)); } internal static void RemoveSettings(string key) diff --git a/DiscordBot/Bot/Actions/Module.cs b/DiscordBot/Bot/Actions/Module.cs index 0f59ed2..ed47832 100644 --- a/DiscordBot/Bot/Actions/Module.cs +++ b/DiscordBot/Bot/Actions/Module.cs @@ -14,9 +14,11 @@ namespace DiscordBot.Bot.Actions public string Description => "Access module commands"; - public string Usage => "module "; + public string Usage => "module "; - public IEnumerable ListOfOptions => []; + public IEnumerable ListOfOptions => [ + new InternalActionOption("list", "List all loaded modules") + ]; public InternalActionRunType RunType => InternalActionRunType.OnCall; @@ -24,7 +26,7 @@ namespace DiscordBot.Bot.Actions public Task Execute(string[] args) { - string command = args[0]; + string command = args?[0]; switch(command) { case "list": diff --git a/DiscordBot/Bot/Actions/SettingsConfig.cs b/DiscordBot/Bot/Actions/SettingsConfig.cs index 68e9288..2f7c58d 100644 --- a/DiscordBot/Bot/Actions/SettingsConfig.cs +++ b/DiscordBot/Bot/Actions/SettingsConfig.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using DiscordBot.Bot.Actions.Extra; using DiscordBotCore; @@ -14,6 +15,7 @@ public class SettingsConfig: ICommandAction public string ActionName => "config"; public string Description => "Change the settings of the bot"; public string Usage => "config "; + public IEnumerable ListOfOptions => new List { new InternalActionOption("help", "Displays this message"), @@ -21,16 +23,16 @@ public class SettingsConfig: ICommandAction new InternalActionOption("remove", "Remove a setting"), new InternalActionOption("add", "Add a setting") }; + public InternalActionRunType RunType => InternalActionRunType.OnCall; - + public bool RequireOtherThread => false; - + public Task Execute(string[] args) { if (args is null) { - foreach (var settings in Application.CurrentApplication.ApplicationEnvironmentVariables) - Console.WriteLine(settings.Key + ": " + settings.Value); + PrintAllSettings(); return Task.CompletedTask; } @@ -59,7 +61,7 @@ public class SettingsConfig: ICommandAction break; case "-h": - case "-help": + case "help": Console.WriteLine("Options:"); Console.WriteLine("-s : Set a setting"); Console.WriteLine("-r : Remove a setting"); @@ -76,4 +78,85 @@ public class SettingsConfig: ICommandAction return Task.CompletedTask; } + private void PrintList(IList list, int indentLevel) + { + bool isListOfDictionaries = list.All(item => item is IDictionary); + + if (isListOfDictionaries) + { + foreach (var item in list) + { + if (item is IDictionary dict) + { + PrintDictionary(dict, indentLevel + 1); + } + } + } + else + { + PrintIndent(indentLevel); + Console.WriteLine(string.Join(",", list)); + } + } + + private void PrintDictionary(IDictionary dictionary, int indentLevel) + { + foreach (var kvp in dictionary) + { + PrintIndent(indentLevel); + Console.Write(kvp.Key + ": "); + + var value = kvp.Value; + if (value is IDictionary dict) + { + Console.WriteLine(); + PrintDictionary(dict, indentLevel + 1); + } + else if (value is IList list) + { + if (list.All(item => item is IDictionary)) + { + Console.WriteLine(); + PrintList(list, indentLevel + 1); + } + else + { + PrintList(list, indentLevel); + } + } + else + { + Console.WriteLine(value); + } + } + } + + private void PrintIndent(int indentLevel) + { + for (int i = 0; i < indentLevel; i++) + { + Console.Write(" "); // Two spaces for each indentation level + } + } + + private void PrintAllSettings() + { + var settings = Application.CurrentApplication.ApplicationEnvironmentVariables; + foreach (var setting in settings) + { + Console.WriteLine("Setting: " + setting.Key); + if (setting.Value is IDictionary dict) + { + PrintDictionary(dict, 1); + } + else if (setting.Value is IList list) + { + PrintList(list, 1); + } + else + { + Console.WriteLine(setting.Value); + } + } + } } diff --git a/DiscordBot/Bot/Commands/NormalCommands/Help.cs b/DiscordBot/Bot/Commands/NormalCommands/Help.cs index cf940ba..4c5ffc6 100644 --- a/DiscordBot/Bot/Commands/NormalCommands/Help.cs +++ b/DiscordBot/Bot/Commands/NormalCommands/Help.cs @@ -79,7 +79,9 @@ internal class Help: DBCommand ); if (cmd == null) return null; - embedBuilder.AddField("Usage", Application.CurrentApplication.ApplicationEnvironmentVariables["prefix"] + cmd.Usage); + string prefix = Application.CurrentApplication.ApplicationEnvironmentVariables.Get("prefix"); + + embedBuilder.AddField("Usage", prefix + cmd.Usage); embedBuilder.AddField("Description", cmd.Description); if (cmd.Aliases is null) return embedBuilder; diff --git a/DiscordBot/Installer.cs b/DiscordBot/Installer.cs index a115919..ed19c28 100644 --- a/DiscordBot/Installer.cs +++ b/DiscordBot/Installer.cs @@ -7,7 +7,7 @@ namespace DiscordBot; public static class Installer { - private static void AskForConfig(string key, string message) + private static string AskForConfig(string key, string message) { var value = AnsiConsole.Ask($"[green]{message}[/]"); @@ -18,19 +18,29 @@ public static class Installer Environment.Exit(-20); } - Application.CurrentApplication.ApplicationEnvironmentVariables.Add(key, value); + return value; + } public static async Task GenerateStartupConfig() { if(!Application.CurrentApplication.ApplicationEnvironmentVariables.ContainsKey("token")) - AskForConfig("token", "Token:"); + { + string response = AskForConfig("token", "Token:"); + Application.CurrentApplication.ApplicationEnvironmentVariables.Add("token", response); + } - if(!Application.CurrentApplication.ApplicationEnvironmentVariables.ContainsKey("prefix")) - AskForConfig("prefix", "Prefix:"); + if (!Application.CurrentApplication.ApplicationEnvironmentVariables.ContainsKey("prefix")) + { + string response = AskForConfig("prefix", "Prefix:"); + Application.CurrentApplication.ApplicationEnvironmentVariables.Add("prefix", response); + } - if(!Application.CurrentApplication.ApplicationEnvironmentVariables.ContainsKey("ServerID")) - AskForConfig("ServerID", "Server ID:"); + if (!Application.CurrentApplication.ApplicationEnvironmentVariables.ContainsKey("ServerID")) + { + string response = AskForConfig("ServerID", "Please enter the server Ids where the bot will be used (separated by ;):"); + Application.CurrentApplication.ApplicationEnvironmentVariables.Add("ServerID", response); + } await Application.CurrentApplication.ApplicationEnvironmentVariables.SaveToFile(); diff --git a/DiscordBot/Program.cs b/DiscordBot/Program.cs index 40cd712..a7df45c 100644 --- a/DiscordBot/Program.cs +++ b/DiscordBot/Program.cs @@ -64,8 +64,8 @@ public class Program try { - var token = Application.CurrentApplication.ApplicationEnvironmentVariables["token"]; - var prefix = Application.CurrentApplication.ApplicationEnvironmentVariables["prefix"]; + var token = Application.CurrentApplication.ApplicationEnvironmentVariables.Get("token"); + var prefix = Application.CurrentApplication.ApplicationEnvironmentVariables.Get("prefix"); var discordbooter = new App(token, prefix); await discordbooter.Awake(); } diff --git a/DiscordBotCore/Application.cs b/DiscordBotCore/Application.cs index e108e2b..f55a1d1 100644 --- a/DiscordBotCore/Application.cs +++ b/DiscordBotCore/Application.cs @@ -13,6 +13,7 @@ using DiscordBotCore.Interfaces.Logger; using DiscordBotCore.Modules; using System.Diagnostics; using DiscordBotCore.Online.Helpers; +using DiscordBotCore.Others.Settings; namespace DiscordBotCore @@ -26,21 +27,17 @@ namespace DiscordBotCore private static readonly string _ConfigFile = "./Data/Resources/config.json"; private static readonly string _PluginsDatabaseFile = "./Data/Resources/plugins.json"; - private static readonly string _ModuleFolder = "./Data/Modules"; private static readonly string _ResourcesFolder = "./Data/Resources"; private static readonly string _PluginsFolder = "./Data/Plugins"; - private static readonly string _ArchivesFolder = "./Data/Archives"; private static readonly string _LogsFolder = "./Data/Logs"; - private static readonly string _MaxParallelDownloads = "3"; - - public string ServerID => ApplicationEnvironmentVariables["ServerID"]; - public string PluginDatabase => ApplicationEnvironmentVariables["PluginDatabase"] ?? _PluginsDatabaseFile; + public List ServerIDs => ApplicationEnvironmentVariables.GetList("ServerID", new List()); + public string PluginDatabase => ApplicationEnvironmentVariables.Get("PluginDatabase", _PluginsDatabaseFile); private ModuleManager _ModuleManager; - public SettingsDictionary ApplicationEnvironmentVariables { get; private set; } + public CustomSettingsDictionary ApplicationEnvironmentVariables { get; private set; } public InternalActionManager InternalActionManager { get; private set; } public ILogger Logger @@ -84,27 +81,19 @@ namespace DiscordBotCore return; CurrentApplication = new Application(); - - Directory.CreateDirectory(_ResourcesFolder); Directory.CreateDirectory(_PluginsFolder); - Directory.CreateDirectory(_ArchivesFolder); Directory.CreateDirectory(_LogsFolder); - Directory.CreateDirectory(_ModuleFolder); - CurrentApplication.ApplicationEnvironmentVariables = new SettingsDictionary(_ConfigFile); - bool result = await CurrentApplication.ApplicationEnvironmentVariables.LoadFromFile(); - if(!result) - { - PopulateEnvWithDefault(); - File.Delete(_ConfigFile); - await CurrentApplication.ApplicationEnvironmentVariables.SaveToFile(); - } + CurrentApplication.ApplicationEnvironmentVariables = await CustomSettingsDictionary.CreateFromFile(_ConfigFile, true); - CurrentApplication.ApplicationEnvironmentVariables.Add("ModuleFolder", _ModuleFolder); - - CurrentApplication._ModuleManager = new(_ModuleFolder); + CurrentApplication.ApplicationEnvironmentVariables.Set("PluginFolder", _PluginsFolder); + CurrentApplication.ApplicationEnvironmentVariables.Set("ResourceFolder", _ResourcesFolder); + CurrentApplication.ApplicationEnvironmentVariables.Set("LogsFolder", _LogsFolder); + + + CurrentApplication._ModuleManager = new ModuleManager(); await CurrentApplication._ModuleManager.LoadModules(); if (!File.Exists(_PluginsDatabaseFile)) @@ -130,29 +119,13 @@ namespace DiscordBotCore public IReadOnlyDictionary> GetLoadedCoreModules() => _ModuleManager.LoadedModules.AsReadOnly(); - private static void PopulateEnvWithDefault() - { - if (CurrentApplication is null) - return; - - if (CurrentApplication.ApplicationEnvironmentVariables is null) - return; - - - CurrentApplication.ApplicationEnvironmentVariables["LogFolder"] = _LogsFolder; - CurrentApplication.ApplicationEnvironmentVariables["PluginFolder"] = _PluginsFolder; - CurrentApplication.ApplicationEnvironmentVariables["ArchiveFolder"] = _ArchivesFolder; - CurrentApplication.ApplicationEnvironmentVariables["PluginDatabase"] = _PluginsDatabaseFile; - CurrentApplication.ApplicationEnvironmentVariables["MaxParallelDownloads"] = _MaxParallelDownloads; - } - public static string GetResourceFullPath(string path) { string result = Path.Combine(_ResourcesFolder, path); return result; } - public static string GetResourceFullPath() => _ResourcesFolder; + public static string GetResourceFullPath() => CurrentApplication.ApplicationEnvironmentVariables.Get("ResourceFolder", _ResourcesFolder); public static string GetPluginFullPath(string path) { @@ -160,7 +133,7 @@ namespace DiscordBotCore return result; } - public static string GetPluginFullPath() => _PluginsFolder; + public static string GetPluginFullPath() => CurrentApplication.ApplicationEnvironmentVariables.Get("PluginFolder", _PluginsFolder); public static async Task GetPluginDependencyPath(string dependencyName, string? pluginName = null) { diff --git a/DiscordBotCore/Loaders/ModuleLoader.cs b/DiscordBotCore/Loaders/ModuleLoader.cs index 08d3fc5..95a2fed 100644 --- a/DiscordBotCore/Loaders/ModuleLoader.cs +++ b/DiscordBotCore/Loaders/ModuleLoader.cs @@ -11,16 +11,17 @@ namespace DiscordBotCore.Loaders { internal class ModuleLoader { - private string _moduleFolder; + private readonly string _ModuleFolder; public ModuleLoader(string moduleFolder) { - _moduleFolder = moduleFolder; + _ModuleFolder = moduleFolder; + Directory.CreateDirectory(moduleFolder); } public Task LoadFileModules() { - var files = Directory.GetFiles(_moduleFolder, "*.dll"); + var files = Directory.GetFiles(_ModuleFolder, "*.dll"); foreach (var file in files) { try diff --git a/DiscordBotCore/Loaders/PluginLoaderExtensions.cs b/DiscordBotCore/Loaders/PluginLoaderExtensions.cs index cbb3e2e..cc1e7b1 100644 --- a/DiscordBotCore/Loaders/PluginLoaderExtensions.cs +++ b/DiscordBotCore/Loaders/PluginLoaderExtensions.cs @@ -3,10 +3,12 @@ using System.Collections.Generic; using System.Threading.Tasks; using Discord; +using Discord.Interactions; using Discord.WebSocket; using DiscordBotCore.Interfaces; using DiscordBotCore.Others; +using ContextType = Discord.Commands.ContextType; namespace DiscordBotCore.Loaders; @@ -31,60 +33,75 @@ internal static class PluginLoaderExtensions return false; } } - - internal static async Task ResetSlashCommands(this PluginLoader pluginLoader) + + internal static Task ResetSlashCommands(this PluginLoader pluginLoader) { - await pluginLoader._Client.Rest.DeleteAllGlobalCommandsAsync(); - - if(pluginLoader._Client.Guilds.Count == 0) return; - if (!ulong.TryParse(Application.CurrentApplication.ServerID, out _)) - { - Application.CurrentApplication.Logger.Log("Invalid ServerID in config file. Can not reset specific guild commands", typeof(PluginLoader), LogType.Error); - return; - } - - SocketGuild? guild = pluginLoader._Client.GetGuild(ulong.Parse(Application.CurrentApplication.ServerID)); - if(guild is null) - { - Application.CurrentApplication.Logger.Log("Failed to get guild with ID " + Application.CurrentApplication.ServerID, typeof(PluginLoader), LogType.Error); - return; - } - - await guild.DeleteApplicationCommandsAsync(); - - Application.CurrentApplication.Logger.Log($"Cleared all slash commands from guild {guild.Id}", typeof(PluginLoader)); + throw new NotImplementedException("This method is not implemented yet."); + + // await pluginLoader._Client.Rest.DeleteAllGlobalCommandsAsync(); + // + // if(pluginLoader._Client.Guilds.Count == 0) return; + // if (!ulong.TryParse(Application.CurrentApplication.ServerID, out _)) + // { + // Application.CurrentApplication.Logger.Log("Invalid ServerID in config file. Can not reset specific guild commands", typeof(PluginLoader), LogType.Error); + // return; + // } + // + // SocketGuild? guild = pluginLoader._Client.GetGuild(ulong.Parse(Application.CurrentApplication.ServerID)); + // if(guild is null) + // { + // Application.CurrentApplication.Logger.Log("Failed to get guild with ID " + Application.CurrentApplication.ServerID, typeof(PluginLoader), LogType.Error); + // return; + // } + // + // await guild.DeleteApplicationCommandsAsync(); + // + // Application.CurrentApplication.Logger.Log($"Cleared all slash commands from guild {guild.Id}", typeof(PluginLoader)); } - + internal static async Task TryStartSlashCommand(this PluginLoader pluginLoader, DBSlashCommand? dbSlashCommand) { try { if (dbSlashCommand is null) { - //throw new ArgumentNullException(nameof(dbSlashCommand)); return false; } - if (pluginLoader._Client.Guilds.Count == 0) return false; - + if (pluginLoader._Client.Guilds.Count == 0) + { + return false; + } var builder = new SlashCommandBuilder(); builder.WithName(dbSlashCommand.Name); builder.WithDescription(dbSlashCommand.Description); - builder.WithDMPermission(dbSlashCommand.canUseDM); builder.Options = dbSlashCommand.Options; - if (uint.TryParse(Application.CurrentApplication.ServerID, out uint result)) - { - SocketGuild? guild = pluginLoader._Client.GetGuild(result); - if (guild is null) - { - Application.CurrentApplication.Logger.Log("Failed to get guild with ID " + Application.CurrentApplication.ServerID, typeof(PluginLoader), LogType.Error); - return false; - } + if (dbSlashCommand.canUseDM) + builder.WithContextTypes(InteractionContextType.BotDm, InteractionContextType.Guild); + else + builder.WithContextTypes(InteractionContextType.Guild); - await guild.CreateApplicationCommandAsync(builder.Build()); - }else await pluginLoader._Client.CreateGlobalApplicationCommandAsync(builder.Build()); + // if (uint.TryParse(Application.CurrentApplication.ServerID, out uint result)) + // { + // SocketGuild? guild = pluginLoader._Client.GetGuild(result); + // if (guild is null) + // { + // Application.CurrentApplication.Logger.Log("Failed to get guild with ID " + Application.CurrentApplication.ServerID, typeof(PluginLoader), LogType.Error); + // return false; + // } + // + // await guild.CreateApplicationCommandAsync(builder.Build()); + // } + // else + + foreach(ulong guildId in Application.CurrentApplication.ServerIDs) + { + await pluginLoader.EnableSlashCommandPerGuild(guildId, builder); + } + + await pluginLoader._Client.CreateGlobalApplicationCommandAsync(builder.Build()); return true; } @@ -94,4 +111,19 @@ internal static class PluginLoaderExtensions return false; } } + + private static async Task EnableSlashCommandPerGuild(this PluginLoader pluginLoader, ulong guildId, SlashCommandBuilder builder) + { + SocketGuild? guild = pluginLoader._Client.GetGuild(guildId); + if (guild is null) + { + Application.CurrentApplication.Logger.Log("Failed to get guild with ID " + guildId, typeof(PluginLoader), LogType.Error); + return false; + } + + await guild.CreateApplicationCommandAsync(builder.Build()); + + return true; + } + } diff --git a/DiscordBotCore/Modules/ModuleManager.cs b/DiscordBotCore/Modules/ModuleManager.cs index c28b6fa..45e7d31 100644 --- a/DiscordBotCore/Modules/ModuleManager.cs +++ b/DiscordBotCore/Modules/ModuleManager.cs @@ -12,12 +12,20 @@ namespace DiscordBotCore.Modules { internal class ModuleManager { - private string _moduleFolder; - internal Dictionary> LoadedModules { get; private set; } - + private static readonly string _BaseModuleFolder = "./Data/Modules"; + + private readonly string _ModuleFolder; + internal Dictionary> LoadedModules { get; } + public ModuleManager(string moduleFolder) { - _moduleFolder = moduleFolder; + _ModuleFolder = moduleFolder; + LoadedModules = new Dictionary>(); + } + + public ModuleManager() + { + _ModuleFolder = Application.CurrentApplication.ApplicationEnvironmentVariables.Get("ModuleFolder", _BaseModuleFolder); LoadedModules = new Dictionary>(); } @@ -35,11 +43,10 @@ namespace DiscordBotCore.Modules public async Task LoadModules() { - ModuleLoader loader = new ModuleLoader(_moduleFolder); + ModuleLoader loader = new ModuleLoader(_ModuleFolder); await loader.LoadFileModules(); - - // Load All Loggers + var loggers = await loader.LoadModules(); foreach (var logger in loggers) { diff --git a/DiscordBotCore/Online/PluginManager.cs b/DiscordBotCore/Online/PluginManager.cs index b066b10..18acec6 100644 --- a/DiscordBotCore/Online/PluginManager.cs +++ b/DiscordBotCore/Online/PluginManager.cs @@ -224,7 +224,7 @@ public class PluginManager : IPluginManager installProgress?.Report(currentProgress + stepProgress * p); }); - await ServerCom.DownloadFileAsync(pluginData.DownLoadLink, $"{Application.CurrentApplication.ApplicationEnvironmentVariables["PluginFolder"]}/{pluginData.Name}.dll", progress); + await ServerCom.DownloadFileAsync(pluginData.DownLoadLink, $"{Application.CurrentApplication.ApplicationEnvironmentVariables.Get("PluginFolder")}/{pluginData.Name}.dll", progress); if (pluginData.HasFileDependencies) foreach (var dependency in pluginData.Dependencies) diff --git a/DiscordBotCore/Others/ArchiveManager.cs b/DiscordBotCore/Others/ArchiveManager.cs index ff83e32..3bbfa47 100644 --- a/DiscordBotCore/Others/ArchiveManager.cs +++ b/DiscordBotCore/Others/ArchiveManager.cs @@ -8,6 +8,8 @@ namespace DiscordBotCore.Others; public static class ArchiveManager { + + private static readonly string _ArchivesFolder = "./Data/Archives"; public static void CreateFromFile(string file, string folder) { @@ -33,7 +35,13 @@ public static class ArchiveManager public static async Task ReadAllBytes(string fileName, string archName) { - archName = Path.Combine(Application.CurrentApplication.ApplicationEnvironmentVariables["ArchiveFolder"], archName); + string? archiveFolderBasePath = Application.CurrentApplication.ApplicationEnvironmentVariables.Get("ArchiveFolder", _ArchivesFolder); + if(archiveFolderBasePath is null) + throw new Exception("Archive folder not found"); + + Directory.CreateDirectory(archiveFolderBasePath); + + archName = Path.Combine(archiveFolderBasePath, archName); if (!File.Exists(archName)) throw new Exception("Failed to load file !"); @@ -64,7 +72,14 @@ public static class ArchiveManager /// A string that represents the content of the file or null if the file does not exists or it has no content public static async Task ReadFromPakAsync(string fileName, string archFile) { - archFile = Application.CurrentApplication.ApplicationEnvironmentVariables["ArchiveFolder"] + archFile; + string? archiveFolderBasePath = Application.CurrentApplication.ApplicationEnvironmentVariables.Get("ArchiveFolder", _ArchivesFolder); + if(archiveFolderBasePath is null) + throw new Exception("Archive folder not found"); + + Directory.CreateDirectory(archiveFolderBasePath); + + archFile = Path.Combine(archiveFolderBasePath, archFile); + if (!File.Exists(archFile)) throw new Exception("Failed to load file !"); diff --git a/DiscordBotCore/Others/Settings/CustomSettingsDictionary.cs b/DiscordBotCore/Others/Settings/CustomSettingsDictionary.cs new file mode 100644 index 0000000..1fba782 --- /dev/null +++ b/DiscordBotCore/Others/Settings/CustomSettingsDictionary.cs @@ -0,0 +1,93 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace DiscordBotCore.Others.Settings; + +public class CustomSettingsDictionary : CustomSettingsDictionaryBase +{ + private bool _EnableAutoAddOnGetWithDefault; + private CustomSettingsDictionary(string diskLocation, bool enableAutoAddOnGetWithDefault): base(diskLocation) + { + _EnableAutoAddOnGetWithDefault = enableAutoAddOnGetWithDefault; + } + + public override async Task SaveToFile() + { + var json = JsonConvert.SerializeObject(_InternalDictionary, Formatting.Indented); + await File.WriteAllTextAsync(_DiskLocation, json); + } + + public override T Get(string key, T defaultValue) + { + T value = base.Get(key, defaultValue); + + if (_EnableAutoAddOnGetWithDefault && value.Equals(defaultValue)) + { + Add(key, defaultValue); + } + + return value; + } + + public override List GetList(string key, List defaultValue) + { + List result = base.GetList(key, defaultValue); + + if (_EnableAutoAddOnGetWithDefault && defaultValue.All(result.Contains)) + { + Add(key,defaultValue); + } + + return result; + } + + public override async Task LoadFromFile() + { + string jsonContent = await File.ReadAllTextAsync(_DiskLocation); + var jObject = JsonConvert.DeserializeObject(jsonContent); + _InternalDictionary.Clear(); + + foreach (var kvp in jObject) + { + AddPairToDictionary(kvp, _InternalDictionary); + } + } + + private void AddPairToDictionary(KeyValuePair kvp, IDictionary dict) + { + if (kvp.Value is JObject nestedJObject) + { + dict[kvp.Key] = nestedJObject.ToObject>(); + + foreach (var nestedKvp in nestedJObject) + { + AddPairToDictionary(nestedKvp, dict[kvp.Key] as Dictionary); + } + } + else if (kvp.Value is JArray nestedJArray) + { + dict[kvp.Key] = nestedJArray.ToObject>(); + + } + else + { + dict[kvp.Key] = kvp.Value; + } + } + + /// + /// Create a new Settings Dictionary from a file + /// + /// The file location + /// Set this to true if you want to update the dictionary with default values on get + internal static async Task CreateFromFile(string baseFile, bool enableAutoAddOnGetWithDefault) + { + var settings = new CustomSettingsDictionary(baseFile, enableAutoAddOnGetWithDefault); + await settings.LoadFromFile(); + return settings; + } +} diff --git a/DiscordBotCore/Others/Settings/CustomSettingsDictionaryBase.cs b/DiscordBotCore/Others/Settings/CustomSettingsDictionaryBase.cs new file mode 100644 index 0000000..abe20cf --- /dev/null +++ b/DiscordBotCore/Others/Settings/CustomSettingsDictionaryBase.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace DiscordBotCore.Others.Settings; + +public abstract class CustomSettingsDictionaryBase : ICustomSettingsDictionary +{ + protected readonly IDictionary _InternalDictionary = new Dictionary(); + protected readonly string _DiskLocation; + + protected CustomSettingsDictionaryBase(string diskLocation) + { + this._DiskLocation = diskLocation; + } + + public virtual void Add(TKey key, TValue value) + { + if (_InternalDictionary.ContainsKey(key)) + return; + + if (value is null) + return; + + _InternalDictionary.Add(key, value); + } + + public virtual void Set(TKey key, TValue value) + { + _InternalDictionary[key] = value; + } + + public virtual TValue Get(TKey key) + { + return _InternalDictionary[key]; + } + + public virtual T Get(TKey key, T defaultValue) + { + if (_InternalDictionary.TryGetValue(key, out var value)) + { + return (T)Convert.ChangeType(value, typeof(T)); + } + + return defaultValue; + } + + public virtual T? Get(TKey key) + { + if (_InternalDictionary.TryGetValue(key, out var value)) + { + return (T)Convert.ChangeType(value, typeof(T)); + } + + return default; + } + + public virtual List GetList(TKey key, List defaultValue) + { + if(_InternalDictionary.TryGetValue(key, out var value)) + { + if (value is not IList) + { + throw new Exception("The value is not a list"); + } + + var list = new List(); + foreach (var item in (IList)value) + { + list.Add(ConvertValue(item)); + } + + return list; + } + + return defaultValue; + } + + public virtual void Remove(TKey key) + { + _InternalDictionary.Remove(key); + } + + public virtual IEnumerator> GetEnumerator() + { + return _InternalDictionary.GetEnumerator(); + } + + public virtual void Clear() + { + _InternalDictionary.Clear(); + } + + public virtual bool ContainsKey(TKey key) + { + return _InternalDictionary.ContainsKey(key); + } + + public virtual IEnumerable> Where(Func, bool> predicate) + { + return _InternalDictionary.Where(predicate); + } + + public virtual IEnumerable> Where(Func, int, bool> predicate) + { + return _InternalDictionary.Where(predicate); + } + + public virtual IEnumerable Where(Func, TResult> selector) + { + return _InternalDictionary.Select(selector); + } + + public virtual IEnumerable Where(Func, int, TResult> selector) + { + return _InternalDictionary.Select(selector); + } + + public virtual KeyValuePair FirstOrDefault(Func, bool> predicate) + { + return _InternalDictionary.FirstOrDefault(predicate); + } + + public virtual KeyValuePair FirstOrDefault() + { + return _InternalDictionary.FirstOrDefault(); + } + + public virtual bool ContainsAllKeys(params TKey[] keys) + { + return keys.All(ContainsKey); + } + + public virtual bool TryGetValue(TKey key, out TValue? value) + { + return _InternalDictionary.TryGetValue(key, out value); + } + + public abstract Task SaveToFile(); + + public abstract Task LoadFromFile(); + + protected virtual T? ConvertValue(object value) + { + + if (typeof(T) == typeof(ulong) && value is long longValue) + { + return (T)(object)Convert.ToUInt64(longValue); + } + + if (typeof(T).IsEnum && value is string stringValue) + { + return (T)Enum.Parse(typeof(T), stringValue); + } + + return (T)Convert.ChangeType(value, typeof(T)); + } +} diff --git a/DiscordBotCore/Others/Settings/ICustomSettingsDictionary.cs b/DiscordBotCore/Others/Settings/ICustomSettingsDictionary.cs new file mode 100644 index 0000000..e802e5b --- /dev/null +++ b/DiscordBotCore/Others/Settings/ICustomSettingsDictionary.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace DiscordBotCore.Others.Settings; + +internal interface ICustomSettingsDictionary +{ + /// + /// Adds an element to the custom settings dictionary + /// + /// The key + /// The value + void Add(TKey key, TValue value); + + /// + /// Sets the value of a key in the custom settings dictionary + /// + /// The key + /// The value + void Set(TKey key, TValue value); + + /// + /// Gets the value of a key in the custom settings dictionary. If the T type is different then the TValue type, it will try to convert it. + /// + /// The key + /// The default value to be returned if the searched value is not found + /// The type of the returned value + /// + T Get(TKey key, T defaultValue); + + /// + /// Gets the value of a key in the custom settings dictionary. If the T type is different then the TValue type, it will try to convert it. + /// + /// The key + /// The type of the returned value + /// + T? Get(TKey key); + + /// + /// Get a list of values from the custom settings dictionary + /// + /// The key + /// The default list to be returned if nothing is found + /// The type of the returned value + /// + List GetList(TKey key, List defaultValue); + + /// + /// Remove a key from the custom settings dictionary + /// + /// The key + void Remove(TKey key); + + /// + /// Get the enumerator of the custom settings dictionary + /// + /// + IEnumerator> GetEnumerator(); + + /// + /// Clear the custom settings dictionary + /// + void Clear(); + + /// + /// Check if the custom settings dictionary contains a key + /// + /// The key + /// + bool ContainsKey(TKey key); + + /// + /// Filter the custom settings dictionary based on a predicate + /// + /// The predicate + /// + IEnumerable> Where(Func, bool> predicate); + + /// + /// Filter the custom settings dictionary based on a predicate + /// + /// The predicate + IEnumerable> Where(Func, int, bool> predicate); + + /// + /// Filter the custom settings dictionary based on a predicate + /// + /// The predicate + IEnumerable Where(Func, TResult> selector); + + /// + /// Filter the custom settings dictionary based on a predicate + /// + /// The predicate + IEnumerable Where(Func, int, TResult> selector); + + /// + /// Get the first element of the custom settings dictionary based on a predicate + /// + /// The predicate + KeyValuePair FirstOrDefault(Func, bool> predicate); + + /// + /// Get the first element of the custom settings dictionary + /// + /// + KeyValuePair FirstOrDefault(); + + /// + /// Checks if the custom settings dictionary contains all the keys + /// + /// A list of keys + /// + bool ContainsAllKeys(params TKey[] keys); + + /// + /// Try to get the value of a key in the custom settings dictionary + /// + /// The key + /// The value + /// + bool TryGetValue(TKey key, out TValue? value); + + /// + /// Save the custom settings dictionary to a file + /// + /// + Task SaveToFile(); + + /// + /// Load the custom settings dictionary from a file + /// + /// + Task LoadFromFile(); +} diff --git a/DiscordBotCore/Others/SettingsDictionary.cs b/DiscordBotCore/Others/SettingsDictionary.cs deleted file mode 100644 index fb489ef..0000000 --- a/DiscordBotCore/Others/SettingsDictionary.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; - -namespace DiscordBotCore.Others; - -public class SettingsDictionary -{ - private string _File { get; } - protected IDictionary _Dictionary; - - public SettingsDictionary(string file) - { - this._File = file; - _Dictionary = null!; - } - - public async Task SaveToFile() - { - if (!string.IsNullOrEmpty(_File)) - await JsonManager.SaveToJsonFile(_File, _Dictionary); - } - - public IEnumerator> GetEnumerator() - { - return _Dictionary.GetEnumerator(); - } - - public async Task LoadFromFile() - { - if (string.IsNullOrEmpty(_File)) - return false; - - if(!File.Exists(_File)) - { - _Dictionary = new Dictionary(); - await SaveToFile(); - return false; - } - - string fileAsText = await File.ReadAllTextAsync(_File); - if(string.IsNullOrEmpty(fileAsText) || string.IsNullOrWhiteSpace(fileAsText)) - { - _Dictionary = new Dictionary(); - await SaveToFile(); - return false; - } - - _Dictionary = await JsonManager.ConvertFromJson>(fileAsText); - - if (_Dictionary.Keys.Count == 0) - return false; - - return true; - } - - public void Add(TKey key, TValue value) - { - if (_Dictionary.ContainsKey(key)) - return; - - _Dictionary.Add(key, value); - } - - public bool ContainsAllKeys(params TKey[] keys) - { - return keys.All(key => _Dictionary.ContainsKey(key)); - } - - public bool ContainsKey(TKey key) - { - return _Dictionary.ContainsKey(key); - } - - public bool Remove(TKey key) - { - return _Dictionary.Remove(key); - } - - public TValue this[TKey key] - { - get - { - if(!_Dictionary.ContainsKey(key)) - throw new System.Exception($"The key {key} ({typeof(TKey)}) (file: {this._File}) was not present in the dictionary"); - - if(_Dictionary[key] is not TValue) - throw new System.Exception("The dictionary is corrupted. This error is critical !"); - - return _Dictionary[key]; - } - set => _Dictionary[key] = value; - } - - // First - public KeyValuePair FirstOrDefault(Func, bool> predicate) - { - return _Dictionary.FirstOrDefault(predicate); - } - - // Where - public IEnumerable> Where(Func, bool> predicate) - { - return _Dictionary.Where(predicate); - } - - public void Clear() - { - _Dictionary.Clear(); - } - - public IEnumerable Keys => _Dictionary.Keys; - public IEnumerable Values => _Dictionary.Values; - - -} diff --git a/DiscordBotCore/Plugin/PluginInfo.cs b/DiscordBotCore/Plugin/PluginInfo.cs index 62a2eec..9f7474d 100644 --- a/DiscordBotCore/Plugin/PluginInfo.cs +++ b/DiscordBotCore/Plugin/PluginInfo.cs @@ -22,20 +22,20 @@ public class PluginInfo PluginVersion = pluginVersion; ListOfExecutableDependencies = listOfExecutableDependencies; IsMarkedToUninstall = isMarkedToUninstall; - FilePath = $"{Application.CurrentApplication.ApplicationEnvironmentVariables["PluginFolder"]}/{pluginName}.dll"; + FilePath = $"{Application.CurrentApplication.ApplicationEnvironmentVariables.Get("PluginFolder")}/{pluginName}.dll"; IsOfflineAdded = isOfflineAdded; IsEnabled = isEnabled; } public PluginInfo(string pluginName, PluginVersion pluginVersion, Dictionary listOfExecutableDependencies) { - PluginName = pluginName; - PluginVersion = pluginVersion; + PluginName = pluginName; + PluginVersion = pluginVersion; ListOfExecutableDependencies = listOfExecutableDependencies; - IsMarkedToUninstall = false; - FilePath = $"{Application.CurrentApplication.ApplicationEnvironmentVariables["PluginFolder"]}/{pluginName}.dll"; - IsOfflineAdded = false; - IsEnabled = true; + IsMarkedToUninstall = false; + FilePath = $"{Application.CurrentApplication.ApplicationEnvironmentVariables.Get("PluginFolder")}/{pluginName}.dll"; + IsOfflineAdded = false; + IsEnabled = true; } public static PluginInfo FromOnlineInfo(PluginOnlineInfo onlineInfo) diff --git a/DiscordBotCore/Updater/Plugins/PluginUpdater.cs b/DiscordBotCore/Updater/Plugins/PluginUpdater.cs index 8187845..d77ab57 100644 --- a/DiscordBotCore/Updater/Plugins/PluginUpdater.cs +++ b/DiscordBotCore/Updater/Plugins/PluginUpdater.cs @@ -40,7 +40,7 @@ public class PluginUpdater public async Task UpdatePlugin(string pluginName, IProgress? progressMeter = null) { PluginOnlineInfo pluginInfo = await GetPluginInfo(pluginName); - await ServerCom.DownloadFileAsync(pluginInfo.DownLoadLink, $"{DiscordBotCore.Application.CurrentApplication.ApplicationEnvironmentVariables["PluginFolder"]}/{pluginName}.dll", progressMeter); + await ServerCom.DownloadFileAsync(pluginInfo.DownLoadLink, $"{DiscordBotCore.Application.CurrentApplication.ApplicationEnvironmentVariables.Get("PluginFolder")}/{pluginName}.dll", progressMeter); foreach(OnlineDependencyInfo dependency in pluginInfo.Dependencies) await ServerCom.DownloadFileAsync(dependency.DownloadLink, dependency.DownloadLocation, progressMeter); diff --git a/Plugins/CppWrapper/Objects/ApplicationStruct.cs b/Plugins/CppWrapper/Objects/ApplicationStruct.cs index 5255860..3e1d0b1 100644 --- a/Plugins/CppWrapper/Objects/ApplicationStruct.cs +++ b/Plugins/CppWrapper/Objects/ApplicationStruct.cs @@ -5,8 +5,8 @@ namespace CppWrapper.Objects [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct ApplicationStruct { - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 19)] - public string ServerId; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] + public ulong[] ServerIds; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)] public string Prefix; diff --git a/Plugins/CppWrapper/Objects/ObjectConvertor.cs b/Plugins/CppWrapper/Objects/ObjectConvertor.cs index 61421fd..a790387 100644 --- a/Plugins/CppWrapper/Objects/ObjectConvertor.cs +++ b/Plugins/CppWrapper/Objects/ObjectConvertor.cs @@ -8,9 +8,9 @@ namespace CppWrapper.Objects { return new ApplicationStruct { - Token = application.ApplicationEnvironmentVariables["token"], - Prefix = application.ApplicationEnvironmentVariables["prefix"], - ServerId = application.ServerID + Token = application.ApplicationEnvironmentVariables.Get("token") ?? throw new Exception("Token not found"), + Prefix = application.ApplicationEnvironmentVariables.Get("prefix") ?? throw new Exception("Prefix not found"), + ServerIds = application.ServerIDs.ToArray() }; } } diff --git a/Plugins/LevelingSystem/LevelEvent.cs b/Plugins/LevelingSystem/LevelEvent.cs index b651ad2..8eec510 100644 --- a/Plugins/LevelingSystem/LevelEvent.cs +++ b/Plugins/LevelingSystem/LevelEvent.cs @@ -47,7 +47,7 @@ internal class LevelEvent : DBEvent private async Task ClientOnMessageReceived(SocketMessage arg) { - if (arg.Author.IsBot || arg.IsTTS || arg.Content.StartsWith(Application.CurrentApplication.ApplicationEnvironmentVariables["prefix"])) + if (arg.Author.IsBot || arg.IsTTS || arg.Content.StartsWith(Application.CurrentApplication.ApplicationEnvironmentVariables.Get("prefix"))) return; if (WaitingList.ContainsKey(arg.Author.Id) && WaitingList[arg.Author.Id] > DateTime.Now.AddSeconds(-GlobalSettings.SecondsToWaitBetweenMessages)) diff --git a/Plugins/MusicPlayer/Commands/AddMelody.cs b/Plugins/MusicPlayer/Commands/AddMelody.cs index 802752f..ccfe5b0 100644 --- a/Plugins/MusicPlayer/Commands/AddMelody.cs +++ b/Plugins/MusicPlayer/Commands/AddMelody.cs @@ -78,8 +78,7 @@ public class AddMelody: DBCommand await msg.ModifyAsync(a => a.Content = "Done"); - var info = - MusicInfoExtensions.CreateMusicInfo(title, location, description ?? "Unknown", aliases.ToList(), bsize); + var info = MusicInfoExtensions.CreateMusicInfo(title, location, description ?? "Unknown", aliases.ToList(), bsize); Variables._MusicDatabase?.Add(title, info); diff --git a/Plugins/MusicPlayer/Commands/AddMelodyYoutube.cs b/Plugins/MusicPlayer/Commands/AddMelodyYoutube.cs index a5e20a8..7c5982f 100644 --- a/Plugins/MusicPlayer/Commands/AddMelodyYoutube.cs +++ b/Plugins/MusicPlayer/Commands/AddMelodyYoutube.cs @@ -21,6 +21,13 @@ public class AddMelodyYoutube: DBCommand public async void ExecuteServer(DbCommandExecutingArguments args) { + + if(Variables._MusicDatabase is null) + { + await args.Context.Channel.SendMessageAsync("Music Database is not loaded !"); + return; + } + if (args.Arguments is null) { await args.Context.Channel.SendMessageAsync("Invalid arguments given. Please use the following format:\nadd_melody_youtube [URL]"); @@ -28,9 +35,9 @@ public class AddMelodyYoutube: DBCommand } - var URL = args.Arguments[0]; + var url = args.Arguments[0]; - if (!URL.StartsWith("https://www.youtube.com/watch?v=") && !URL.StartsWith("https://youtu.be/")) + if (!url.StartsWith("https://www.youtube.com/watch?v=") && !url.StartsWith("https://youtu.be/")) { await args.Context.Channel.SendMessageAsync("Invalid URL given. Please use the following format:\nadd_melody_youtube [URL]"); return; @@ -44,7 +51,7 @@ public class AddMelodyYoutube: DBCommand var msg = await args.Context.Channel.SendMessageAsync("Saving melody ..."); - var title = await YoutubeDLP.DownloadMelody(URL); + var title = await YoutubeDLP.DownloadMelody(url); if (title == null) { @@ -56,8 +63,11 @@ public class AddMelodyYoutube: DBCommand List aliases = joinedAliases.Split('|').ToList(); - if (Variables._MusicDatabase.ContainsMelodyWithNameOrAlias(title)) - Variables._MusicDatabase.Remove(title); + if (Variables._MusicDatabase.GetMusicInfoWithTitleOrAlias(title).Any()) + { + await msg.ModifyAsync(x => x.Content = "Melody already exists !"); + return; + } Variables._MusicDatabase.Add(title, new MusicInfo() { diff --git a/Plugins/MusicPlayer/Commands/SearchMelody.cs b/Plugins/MusicPlayer/Commands/SearchMelody.cs index 81c9811..ea4e279 100644 --- a/Plugins/MusicPlayer/Commands/SearchMelody.cs +++ b/Plugins/MusicPlayer/Commands/SearchMelody.cs @@ -15,6 +15,19 @@ public class SearchMelody: DBCommand public void ExecuteServer(DbCommandExecutingArguments args) { + + if(Variables._MusicDatabase is null) + { + args.Context.Channel.SendMessageAsync("Music Database is not loaded !"); + return; + } + + if (args.Arguments is null || args.Arguments.Length == 0) + { + args.Context.Channel.SendMessageAsync("You need to specify a melody name"); + return; + } + var title = string.Join(" ", args.Arguments); if (string.IsNullOrWhiteSpace(title)) @@ -23,16 +36,13 @@ public class SearchMelody: DBCommand return; } - List? info = Variables._MusicDatabase.GetMusicInfoList(title); - if (info == null || info.Count == 0) + List info = Variables._MusicDatabase.GetMusicInfoWithTitleOrAlias(title); + if (!info.Any()) { args.Context.Channel.SendMessageAsync("No melody with that name or alias was found"); return; } - if (info.Count > 1) - args.Context.Channel.SendMessageAsync(embed: info.ToEmbed(Color.DarkOrange)); - else - args.Context.Channel.SendMessageAsync(embed: info[0].ToEmbed(Color.DarkOrange)); + args.Context.Channel.SendMessageAsync(embed: info.Count > 1 ? info.ToEmbed(Color.DarkOrange) : info[0].ToEmbed(Color.DarkOrange)); } } diff --git a/Plugins/MusicPlayer/MusicDatabase.cs b/Plugins/MusicPlayer/MusicDatabase.cs index 748d912..21cb7cb 100644 --- a/Plugins/MusicPlayer/MusicDatabase.cs +++ b/Plugins/MusicPlayer/MusicDatabase.cs @@ -1,64 +1,67 @@ using DiscordBotCore.Others; +using DiscordBotCore.Others.Settings; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace MusicPlayer; -public class MusicDatabase: SettingsDictionary +public class MusicDatabase: CustomSettingsDictionaryBase { - public MusicDatabase(string file): base(file) + public MusicDatabase(string diskLocation): base(diskLocation) { } - /// - /// Checks if the database contains a melody with the specified name or alias - /// - /// The name (alias) of the melody - /// - public bool ContainsMelodyWithNameOrAlias(string melodyName) + public List GetMusicInfoWithTitleOrAlias(string searchQuery) { - return ContainsKey(melodyName) || Values.Any(elem => elem.Aliases.Contains(melodyName, StringComparer.CurrentCultureIgnoreCase)); - } - - /// - /// Tries to get the music info of a melody with the specified name or alias. Returns the first match or null if no match was found. - /// - /// The music name or one of its aliases. - /// - public MusicInfo? GetMusicInfo(string searchQuery) - { - return FirstOrDefault(kvp => kvp.Key.Contains(searchQuery, StringComparison.InvariantCultureIgnoreCase) || - kvp.Value.Aliases.Any(alias => alias.Contains(searchQuery, StringComparison.InvariantCultureIgnoreCase)) - ).Value; - } - - /// - /// Get a list of music info that match the search query. Returns null if an error occurred, or empty list if no match was found. - /// - /// The search query - /// null if an error occured, otherwise a list with songs that match the search query. If no song match the list is empty - public List? GetMusicInfoList(string searchQuery) - { - try + List musicInfos = new List(); + foreach (var (title, musicInfo) in _InternalDictionary) { - return this.Where(kvp => - kvp.Key.Contains(searchQuery, StringComparison.InvariantCultureIgnoreCase) || - kvp.Value.Aliases.Any(alias => alias.Contains(searchQuery, StringComparison.InvariantCultureIgnoreCase)) - ) - .Select(item => item.Value).ToList(); + + if(title.StartsWith(searchQuery)) + { + musicInfos.Add(musicInfo); + } + + if (musicInfo.Aliases is null) + { + continue; + } + + if (musicInfo.Aliases.Contains(searchQuery) || musicInfo.Aliases.Any(x => x.StartsWith(searchQuery))) + { + musicInfos.Add(musicInfo); + } } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - return null; - } + return musicInfos; } - /// - /// Adds a new entry to the database based on the music info. It uses the title as the key. - /// - /// The music to add to - public void AddNewEntryBasedOnMusicInfo(MusicInfo musicInfo) + public override async Task SaveToFile() { - Add(musicInfo.Title, musicInfo); + var json = JsonConvert.SerializeObject(_InternalDictionary, Formatting.Indented); + await File.WriteAllTextAsync(_DiskLocation, json); + } + + public override async Task LoadFromFile() + { + string jsonContent = await File.ReadAllTextAsync(_DiskLocation); + var jObject = JsonConvert.DeserializeObject(jsonContent); + _InternalDictionary.Clear(); + + foreach (var (key,value) in jObject) + { + if (value is null || value.Type == JTokenType.Null) + { + continue; + } + + MusicInfo? info = value.ToObject(); + if (info is null) + { + continue; + } + + _InternalDictionary[key] = info; + } } } diff --git a/Plugins/MusicPlayer/MusicPlayer.cs b/Plugins/MusicPlayer/MusicPlayer.cs index 1ec9b84..ed7ddd2 100644 --- a/Plugins/MusicPlayer/MusicPlayer.cs +++ b/Plugins/MusicPlayer/MusicPlayer.cs @@ -137,10 +137,13 @@ public class MusicPlayer public bool Enqueue(string musicName) { - var minfo = Variables._MusicDatabase.GetMusicInfo(musicName); - if (minfo is null) return false; + var musicInfo = Variables._MusicDatabase?.Get(musicName); + if (musicInfo is null) + { + return false; + } - MusicQueue.Enqueue(minfo); + MusicQueue.Enqueue(musicInfo); return true; } @@ -149,11 +152,6 @@ public class MusicPlayer isPlaying = false; } - public void SetVolume(float volume) - { - // set volume - } - private static Process? CreateStream(string? fileName, string path) { return Process.Start(new ProcessStartInfo diff --git a/Plugins/MusicPlayer/SlashCommands/Play.cs b/Plugins/MusicPlayer/SlashCommands/Play.cs index ea6e8af..3547cbc 100644 --- a/Plugins/MusicPlayer/SlashCommands/Play.cs +++ b/Plugins/MusicPlayer/SlashCommands/Play.cs @@ -29,6 +29,13 @@ public class Play: DBSlashCommand public async void ExecuteServer(SocketSlashCommand context) { + + if(Variables._MusicDatabase is null) + { + await context.RespondAsync("Music Database is not loaded !"); + return; + } + var melodyName = context.Data.Options.First().Value as string; if (melodyName is null) @@ -37,8 +44,8 @@ public class Play: DBSlashCommand return; } - var melody = Variables._MusicDatabase.GetMusicInfo(melodyName); - if (melody is null) + var melody = Variables._MusicDatabase.GetMusicInfoWithTitleOrAlias(melodyName); + if (!melody.Any()) { await context.RespondAsync("The searched melody does not exists in the database. Sorry :("); return; @@ -71,6 +78,7 @@ public class Play: DBSlashCommand await context.RespondAsync("Failed to enqueue your request. Something went wrong !"); return; } + await context.RespondAsync("Enqueued your request"); await Variables._MusicPlayer.PlayQueue(); //start queue