using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Noikoio.RegexBot.ConfigItem; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Reflection; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Noikoio.RegexBot { /// /// Configuration loader /// class Configuration { public const string LogPrefix = "Config"; private readonly RegexBot _bot; private readonly string _configPath; private DatabaseConfig _dbConfig; private ServerConfig[] _servers; // The following values do not change on reload: private string _botToken; private string _currentGame; public string BotUserToken => _botToken; public string CurrentGame => _currentGame; public DatabaseConfig Database => _dbConfig; public ServerConfig[] Servers => _servers; public Configuration(RegexBot bot) { _bot = bot; var dsc = Path.DirectorySeparatorChar; _configPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + dsc + "settings.json"; } private async Task LoadFile() { var Log = Logger.GetLogger(LogPrefix); JObject pcfg; try { var ctxt = File.ReadAllText(_configPath); pcfg = JObject.Parse(ctxt); return pcfg; } catch (Exception ex) when (ex is DirectoryNotFoundException || ex is FileNotFoundException) { await Log("Config file not found! Check bot directory for settings.json file."); return null; } catch (UnauthorizedAccessException) { await Log("Could not access config file. Check file permissions."); return null; } catch (JsonReaderException jex) { await Log("Failed to parse JSON."); await Log(jex.GetType().Name + " " + jex.Message); return null; } } /// /// Loads essential, unchanging values needed for bot startup. Returns false on failure. /// public bool LoadInitialConfig() { var lt = LoadFile(); lt.Wait(); JObject conf = lt.Result; if (conf == null) return false; _botToken = conf["bot-token"]?.Value(); if (String.IsNullOrWhiteSpace(_botToken)) { Logger.GetLogger(LogPrefix)("Error: Bot token not defined. Cannot continue.").Wait(); return false; } _currentGame = conf["playing"]?.Value(); _dbConfig = new DatabaseConfig(conf["database"]); return true; } /// /// Reloads the server portion of the configuration file. /// /// False on failure. Specific reasons will have been sent to log. public async Task ReloadServerConfig() { var config = await LoadFile(); if (config == null) return false; return await ProcessServerConfig(config); } /// /// Converts a json object containing bot configuration into data usable by this program. /// On success, updates the Servers values and returns true. Returns false on failure. /// private async Task ProcessServerConfig(JObject conf) { var Log = Logger.GetLogger(LogPrefix); if (!conf["servers"].HasValues) { await Log("Error: No server configurations are defined."); return false; } List newservers = new List(); await Log("Reading server configurations..."); foreach (JObject sconf in conf["servers"].Children()) { // Server name //if (sconf["id"] == null || sconf["id"].Type != JTokenType.Integer)) if (sconf["id"] == null) { await Log("Error: Server ID is missing within definition."); return false; } ulong sid = sconf["id"].Value(); string sname = sconf["name"]?.Value(); var SLog = Logger.GetLogger(LogPrefix + "/" + (sname ?? sid.ToString())); // Load server moderator list EntityList mods = new EntityList(sconf["moderators"]); if (sconf["moderators"] != null) await SLog("Moderator " + mods.ToString()); // Load module configurations Dictionary customConfs = new Dictionary(); foreach (var item in _bot.Modules) { var attr = item.GetType().GetTypeInfo() .GetMethod("ProcessConfiguration").GetCustomAttribute(); if (attr == null) { await SLog("No additional configuration for " + item.Name); continue; } var section = sconf[attr.SectionName]; if (section == null) { await SLog("Additional configuration not defined for " + item.Name); continue; } await SLog("Loading additional configuration for " + item.Name); object result; try { result = await item.ProcessConfiguration(section); } catch (RuleImportException ex) { await SLog($"{item.Name} failed to load configuration: " + ex.Message); return false; } customConfs.Add(item, result); } // Switch to using new data List> rulesfinal = new List>(); newservers.Add(new ServerConfig(sid, mods, new ReadOnlyDictionary(customConfs))); } _servers = newservers.ToArray(); return true; } } }