Reworked the Config system

This commit is contained in:
2024-08-06 19:07:08 +03:00
parent 8366de28cc
commit 721c28c283
28 changed files with 740 additions and 318 deletions

View File

@@ -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<ulong> ServerIDs => ApplicationEnvironmentVariables.GetList("ServerID", new List<ulong>());
public string PluginDatabase => ApplicationEnvironmentVariables.Get<string>("PluginDatabase", _PluginsDatabaseFile);
private ModuleManager _ModuleManager;
public SettingsDictionary<string, string> 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<string, string>(_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<Type, List<object>> 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<string>("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<string>("PluginFolder", _PluginsFolder);
public static async Task<string> GetPluginDependencyPath(string dependencyName, string? pluginName = null)
{

View File

@@ -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

View File

@@ -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<bool> TryStartSlashCommand(this PluginLoader pluginLoader, DBSlashCommand? dbSlashCommand)
{
try
{
if (dbSlashCommand is null)
{
//throw new ArgumentNullException(nameof(dbSlashCommand));
return false;
}
if (pluginLoader._Client.Guilds.Count == 0) return false;
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<bool> 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;
}
}

View File

@@ -12,12 +12,20 @@ namespace DiscordBotCore.Modules
{
internal class ModuleManager
{
private string _moduleFolder;
internal Dictionary<Type, List<object>> LoadedModules { get; private set; }
private static readonly string _BaseModuleFolder = "./Data/Modules";
private readonly string _ModuleFolder;
internal Dictionary<Type, List<object>> LoadedModules { get; }
public ModuleManager(string moduleFolder)
{
_moduleFolder = moduleFolder;
_ModuleFolder = moduleFolder;
LoadedModules = new Dictionary<Type, List<object>>();
}
public ModuleManager()
{
_ModuleFolder = Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("ModuleFolder", _BaseModuleFolder);
LoadedModules = new Dictionary<Type, List<object>>();
}
@@ -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<ILogger>();
foreach (var logger in loggers)
{

View File

@@ -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<string>("PluginFolder")}/{pluginData.Name}.dll", progress);
if (pluginData.HasFileDependencies)
foreach (var dependency in pluginData.Dependencies)

View File

@@ -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<byte[]?> ReadAllBytes(string fileName, string archName)
{
archName = Path.Combine(Application.CurrentApplication.ApplicationEnvironmentVariables["ArchiveFolder"], archName);
string? archiveFolderBasePath = Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("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
/// <returns>A string that represents the content of the file or null if the file does not exists or it has no content</returns>
public static async Task<string?> ReadFromPakAsync(string fileName, string archFile)
{
archFile = Application.CurrentApplication.ApplicationEnvironmentVariables["ArchiveFolder"] + archFile;
string? archiveFolderBasePath = Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("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 !");

View File

@@ -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<string, object>
{
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<T>(string key, T defaultValue)
{
T value = base.Get(key, defaultValue);
if (_EnableAutoAddOnGetWithDefault && value.Equals(defaultValue))
{
Add(key, defaultValue);
}
return value;
}
public override List<T> GetList<T>(string key, List<T> defaultValue)
{
List<T> 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<JObject>(jsonContent);
_InternalDictionary.Clear();
foreach (var kvp in jObject)
{
AddPairToDictionary(kvp, _InternalDictionary);
}
}
private void AddPairToDictionary(KeyValuePair<string, JToken> kvp, IDictionary<string, object> dict)
{
if (kvp.Value is JObject nestedJObject)
{
dict[kvp.Key] = nestedJObject.ToObject<Dictionary<string, object>>();
foreach (var nestedKvp in nestedJObject)
{
AddPairToDictionary(nestedKvp, dict[kvp.Key] as Dictionary<string, object>);
}
}
else if (kvp.Value is JArray nestedJArray)
{
dict[kvp.Key] = nestedJArray.ToObject<List<object>>();
}
else
{
dict[kvp.Key] = kvp.Value;
}
}
/// <summary>
/// Create a new Settings Dictionary from a file
/// </summary>
/// <param name="baseFile">The file location</param>
/// <param name="enableAutoAddOnGetWithDefault">Set this to true if you want to update the dictionary with default values on get</param>
internal static async Task<CustomSettingsDictionary> CreateFromFile(string baseFile, bool enableAutoAddOnGetWithDefault)
{
var settings = new CustomSettingsDictionary(baseFile, enableAutoAddOnGetWithDefault);
await settings.LoadFromFile();
return settings;
}
}

View File

@@ -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<TKey,TValue> : ICustomSettingsDictionary<TKey,TValue>
{
protected readonly IDictionary<TKey,TValue> _InternalDictionary = new Dictionary<TKey, TValue>();
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<T>(TKey key, T defaultValue)
{
if (_InternalDictionary.TryGetValue(key, out var value))
{
return (T)Convert.ChangeType(value, typeof(T));
}
return defaultValue;
}
public virtual T? Get<T>(TKey key)
{
if (_InternalDictionary.TryGetValue(key, out var value))
{
return (T)Convert.ChangeType(value, typeof(T));
}
return default;
}
public virtual List<T> GetList<T>(TKey key, List<T> 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<T>();
foreach (var item in (IList)value)
{
list.Add(ConvertValue<T>(item));
}
return list;
}
return defaultValue;
}
public virtual void Remove(TKey key)
{
_InternalDictionary.Remove(key);
}
public virtual IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _InternalDictionary.GetEnumerator();
}
public virtual void Clear()
{
_InternalDictionary.Clear();
}
public virtual bool ContainsKey(TKey key)
{
return _InternalDictionary.ContainsKey(key);
}
public virtual IEnumerable<KeyValuePair<TKey, TValue>> Where(Func<KeyValuePair<TKey, TValue>, bool> predicate)
{
return _InternalDictionary.Where(predicate);
}
public virtual IEnumerable<KeyValuePair<TKey, TValue>> Where(Func<KeyValuePair<TKey, TValue>, int, bool> predicate)
{
return _InternalDictionary.Where(predicate);
}
public virtual IEnumerable<TResult> Where<TResult>(Func<KeyValuePair<TKey, TValue>, TResult> selector)
{
return _InternalDictionary.Select(selector);
}
public virtual IEnumerable<TResult> Where<TResult>(Func<KeyValuePair<TKey, TValue>, int, TResult> selector)
{
return _InternalDictionary.Select(selector);
}
public virtual KeyValuePair<TKey, TValue> FirstOrDefault(Func<KeyValuePair<TKey, TValue>, bool> predicate)
{
return _InternalDictionary.FirstOrDefault(predicate);
}
public virtual KeyValuePair<TKey, TValue> 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<T>(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));
}
}

View File

@@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace DiscordBotCore.Others.Settings;
internal interface ICustomSettingsDictionary<TKey,TValue>
{
/// <summary>
/// Adds an element to the custom settings dictionary
/// </summary>
/// <param name="key">The key</param>
/// <param name="value">The value</param>
void Add(TKey key, TValue value);
/// <summary>
/// Sets the value of a key in the custom settings dictionary
/// </summary>
/// <param name="key">The key</param>
/// <param name="value">The value</param>
void Set(TKey key, TValue value);
/// <summary>
/// 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.
/// </summary>
/// <param name="key">The key</param>
/// <param name="defaultValue">The default value to be returned if the searched value is not found</param>
/// <typeparam name="T">The type of the returned value</typeparam>
/// <returns></returns>
T Get<T>(TKey key, T defaultValue);
/// <summary>
/// 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.
/// </summary>
/// <param name="key">The key</param>
/// <typeparam name="T">The type of the returned value</typeparam>
/// <returns></returns>
T? Get<T>(TKey key);
/// <summary>
/// Get a list of values from the custom settings dictionary
/// </summary>
/// <param name="key">The key</param>
/// <param name="defaultValue">The default list to be returned if nothing is found</param>
/// <typeparam name="T">The type of the returned value</typeparam>
/// <returns></returns>
List<T> GetList<T>(TKey key, List<T> defaultValue);
/// <summary>
/// Remove a key from the custom settings dictionary
/// </summary>
/// <param name="key">The key</param>
void Remove(TKey key);
/// <summary>
/// Get the enumerator of the custom settings dictionary
/// </summary>
/// <returns></returns>
IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator();
/// <summary>
/// Clear the custom settings dictionary
/// </summary>
void Clear();
/// <summary>
/// Check if the custom settings dictionary contains a key
/// </summary>
/// <param name="key">The key</param>
/// <returns></returns>
bool ContainsKey(TKey key);
/// <summary>
/// Filter the custom settings dictionary based on a predicate
/// </summary>
/// <param name="predicate">The predicate</param>
/// <returns></returns>
IEnumerable<KeyValuePair<TKey, TValue>> Where(Func<KeyValuePair<TKey, TValue>, bool> predicate);
/// <summary>
/// Filter the custom settings dictionary based on a predicate
/// </summary>
/// <param name="predicate">The predicate</param>
IEnumerable<KeyValuePair<TKey, TValue>> Where(Func<KeyValuePair<TKey, TValue>, int, bool> predicate);
/// <summary>
/// Filter the custom settings dictionary based on a predicate
/// </summary>
/// <param name="selector">The predicate</param>
IEnumerable<TResult> Where<TResult>(Func<KeyValuePair<TKey, TValue>, TResult> selector);
/// <summary>
/// Filter the custom settings dictionary based on a predicate
/// </summary>
/// <param name="selector">The predicate</param>
IEnumerable<TResult> Where<TResult>(Func<KeyValuePair<TKey, TValue>, int, TResult> selector);
/// <summary>
/// Get the first element of the custom settings dictionary based on a predicate
/// </summary>
/// <param name="predicate">The predicate</param>
KeyValuePair<TKey, TValue> FirstOrDefault(Func<KeyValuePair<TKey, TValue>, bool> predicate);
/// <summary>
/// Get the first element of the custom settings dictionary
/// </summary>
/// <returns></returns>
KeyValuePair<TKey, TValue> FirstOrDefault();
/// <summary>
/// Checks if the custom settings dictionary contains all the keys
/// </summary>
/// <param name="keys">A list of keys</param>
/// <returns></returns>
bool ContainsAllKeys(params TKey[] keys);
/// <summary>
/// Try to get the value of a key in the custom settings dictionary
/// </summary>
/// <param name="key">The key</param>
/// <param name="value">The value</param>
/// <returns></returns>
bool TryGetValue(TKey key, out TValue? value);
/// <summary>
/// Save the custom settings dictionary to a file
/// </summary>
/// <returns></returns>
Task SaveToFile();
/// <summary>
/// Load the custom settings dictionary from a file
/// </summary>
/// <returns></returns>
Task LoadFromFile();
}

View File

@@ -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<TKey, TValue>
{
private string _File { get; }
protected IDictionary<TKey, TValue> _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<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _Dictionary.GetEnumerator();
}
public async Task<bool> LoadFromFile()
{
if (string.IsNullOrEmpty(_File))
return false;
if(!File.Exists(_File))
{
_Dictionary = new Dictionary<TKey, TValue>();
await SaveToFile();
return false;
}
string fileAsText = await File.ReadAllTextAsync(_File);
if(string.IsNullOrEmpty(fileAsText) || string.IsNullOrWhiteSpace(fileAsText))
{
_Dictionary = new Dictionary<TKey, TValue>();
await SaveToFile();
return false;
}
_Dictionary = await JsonManager.ConvertFromJson<IDictionary<TKey,TValue>>(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<TKey, TValue> FirstOrDefault(Func<KeyValuePair<TKey, TValue>, bool> predicate)
{
return _Dictionary.FirstOrDefault(predicate);
}
// Where
public IEnumerable<KeyValuePair<TKey, TValue>> Where(Func<KeyValuePair<TKey, TValue>, bool> predicate)
{
return _Dictionary.Where(predicate);
}
public void Clear()
{
_Dictionary.Clear();
}
public IEnumerable<TKey> Keys => _Dictionary.Keys;
public IEnumerable<TValue> Values => _Dictionary.Values;
}

View File

@@ -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<string>("PluginFolder")}/{pluginName}.dll";
IsOfflineAdded = isOfflineAdded;
IsEnabled = isEnabled;
}
public PluginInfo(string pluginName, PluginVersion pluginVersion, Dictionary<string, string> 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<string>("PluginFolder")}/{pluginName}.dll";
IsOfflineAdded = false;
IsEnabled = true;
}
public static PluginInfo FromOnlineInfo(PluginOnlineInfo onlineInfo)

View File

@@ -40,7 +40,7 @@ public class PluginUpdater
public async Task UpdatePlugin(string pluginName, IProgress<float>? 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<string>("PluginFolder")}/{pluginName}.dll", progressMeter);
foreach(OnlineDependencyInfo dependency in pluginInfo.Dependencies)
await ServerCom.DownloadFileAsync(dependency.DownloadLink, dependency.DownloadLocation, progressMeter);