RegexBot/Configuration.cs
2017-11-11 19:12:24 -08:00

185 lines
6.6 KiB
C#

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
{
/// <summary>
/// Configuration loader
/// </summary>
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<JObject> 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;
}
}
/// <summary>
/// Loads essential, unchanging values needed for bot startup. Returns false on failure.
/// </summary>
public bool LoadInitialConfig()
{
var lt = LoadFile();
lt.Wait();
JObject conf = lt.Result;
if (conf == null) return false;
_botToken = conf["bot-token"]?.Value<string>();
if (String.IsNullOrWhiteSpace(_botToken))
{
Logger.GetLogger(LogPrefix)("Error: Bot token not defined. Cannot continue.").Wait();
return false;
}
_currentGame = conf["playing"]?.Value<string>();
_dbConfig = new DatabaseConfig(conf["database"]);
return true;
}
/// <summary>
/// Reloads the server portion of the configuration file.
/// </summary>
/// <returns>False on failure. Specific reasons will have been sent to log.</returns>
public async Task<bool> ReloadServerConfig()
{
var config = await LoadFile();
if (config == null) return false;
return await ProcessServerConfig(config);
}
/// <summary>
/// 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.
/// </summary>
private async Task<bool> ProcessServerConfig(JObject conf)
{
var Log = Logger.GetLogger(LogPrefix);
if (!conf["servers"].HasValues)
{
await Log("Error: No server configurations are defined.");
return false;
}
List<ServerConfig> newservers = new List<ServerConfig>();
await Log("Reading server configurations...");
foreach (JObject sconf in conf["servers"].Children<JObject>())
{
// 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<ulong>();
string sname = sconf["name"]?.Value<string>();
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<BotModule, object> customConfs = new Dictionary<BotModule, object>();
foreach (var item in _bot.Modules)
{
var attr = item.GetType().GetTypeInfo()
.GetMethod("ProcessConfiguration").GetCustomAttribute<ConfigSectionAttribute>();
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<Tuple<Regex, string[]>> rulesfinal = new List<Tuple<Regex, string[]>>();
newservers.Add(new ServerConfig(sid, mods, new ReadOnlyDictionary<BotModule, object>(customConfs)));
}
_servers = newservers.ToArray();
return true;
}
}
}