2017-06-23 19:31:47 +00:00
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
|
using Noikoio.RegexBot.ConfigItem;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2017-07-26 22:36:59 +00:00
|
|
|
|
using System.Collections.ObjectModel;
|
2017-06-23 19:31:47 +00:00
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
namespace Noikoio.RegexBot
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Configuration loader
|
|
|
|
|
/// </summary>
|
2017-07-26 22:36:59 +00:00
|
|
|
|
class Configuration
|
2017-06-23 19:31:47 +00:00
|
|
|
|
{
|
|
|
|
|
public const string LogPrefix = "Config";
|
|
|
|
|
|
2017-07-26 22:36:59 +00:00
|
|
|
|
private readonly RegexBot _bot;
|
2017-06-23 19:31:47 +00:00
|
|
|
|
private readonly string _configPath;
|
2017-11-01 20:56:06 +00:00
|
|
|
|
private DatabaseConfig _dbConfig;
|
2017-07-26 22:36:59 +00:00
|
|
|
|
private ServerConfig[] _servers;
|
2017-06-23 19:31:47 +00:00
|
|
|
|
|
2017-07-26 22:36:59 +00:00
|
|
|
|
// The following values do not change on reload:
|
2017-06-23 19:31:47 +00:00
|
|
|
|
private string _botToken;
|
|
|
|
|
private string _currentGame;
|
|
|
|
|
|
|
|
|
|
public string BotUserToken => _botToken;
|
|
|
|
|
public string CurrentGame => _currentGame;
|
2017-11-01 20:56:06 +00:00
|
|
|
|
|
2017-07-26 22:36:59 +00:00
|
|
|
|
public ServerConfig[] Servers => _servers;
|
2017-06-23 19:31:47 +00:00
|
|
|
|
|
2017-07-26 22:36:59 +00:00
|
|
|
|
public Configuration(RegexBot bot)
|
2017-06-23 19:31:47 +00:00
|
|
|
|
{
|
2017-07-26 22:36:59 +00:00
|
|
|
|
_bot = bot;
|
2017-06-23 19:31:47 +00:00
|
|
|
|
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>
|
2017-07-26 22:36:59 +00:00
|
|
|
|
/// Loads essential, unchanging values needed for bot startup. Returns false on failure.
|
2017-06-23 19:31:47 +00:00
|
|
|
|
/// </summary>
|
2018-02-17 07:41:12 +00:00
|
|
|
|
internal bool LoadInitialConfig()
|
2017-06-23 19:31:47 +00:00
|
|
|
|
{
|
|
|
|
|
var lt = LoadFile();
|
|
|
|
|
lt.Wait();
|
|
|
|
|
JObject conf = lt.Result;
|
|
|
|
|
if (conf == null) return false;
|
|
|
|
|
|
2018-02-17 07:41:12 +00:00
|
|
|
|
var log = Logger.GetLogger(LogPrefix);
|
|
|
|
|
|
2017-06-23 19:31:47 +00:00
|
|
|
|
_botToken = conf["bot-token"]?.Value<string>();
|
|
|
|
|
if (String.IsNullOrWhiteSpace(_botToken))
|
|
|
|
|
{
|
2018-02-17 07:41:12 +00:00
|
|
|
|
log("Error: Bot token not defined. Cannot continue.").Wait();
|
2017-06-23 19:31:47 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
_currentGame = conf["playing"]?.Value<string>();
|
|
|
|
|
|
2018-02-17 07:41:12 +00:00
|
|
|
|
// Database configuration:
|
|
|
|
|
// Either it exists or it doesn't. Read config, but also attempt to make a database connection
|
|
|
|
|
// right here, or else make it known that database support is disabled for this instance.
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
_dbConfig = new DatabaseConfig(conf["database"]);
|
|
|
|
|
var conn = _dbConfig.GetOpenConnectionAsync().GetAwaiter().GetResult();
|
|
|
|
|
conn.Dispose();
|
|
|
|
|
}
|
|
|
|
|
catch (DatabaseConfig.DatabaseConfigLoadException ex)
|
|
|
|
|
{
|
|
|
|
|
if (ex.Message == "") log("Database configuration not found.").Wait();
|
|
|
|
|
else log("Error within database config: " + ex.Message).Wait();
|
|
|
|
|
_dbConfig = null;
|
|
|
|
|
}
|
|
|
|
|
catch (Npgsql.NpgsqlException ex)
|
|
|
|
|
{
|
|
|
|
|
log("An error occurred while establishing initial database connection: " + ex.Message).Wait();
|
|
|
|
|
_dbConfig = null;
|
|
|
|
|
}
|
|
|
|
|
// Modules that will not enable due to lack of database access should say so in their constructors.
|
2017-11-01 20:56:06 +00:00
|
|
|
|
|
2017-07-26 22:36:59 +00:00
|
|
|
|
return true;
|
2017-06-23 19:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <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()
|
|
|
|
|
{
|
2017-07-26 22:36:59 +00:00
|
|
|
|
var config = await LoadFile();
|
|
|
|
|
if (config == null) return false;
|
2017-06-23 19:31:47 +00:00
|
|
|
|
|
2017-07-26 22:36:59 +00:00
|
|
|
|
return await ProcessServerConfig(config);
|
2017-06-23 19:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <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;
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-26 22:36:59 +00:00
|
|
|
|
List<ServerConfig> newservers = new List<ServerConfig>();
|
2017-06-23 19:31:47 +00:00
|
|
|
|
await Log("Reading server configurations...");
|
|
|
|
|
foreach (JObject sconf in conf["servers"].Children<JObject>())
|
|
|
|
|
{
|
|
|
|
|
// Server name
|
2017-10-21 20:14:51 +00:00
|
|
|
|
//if (sconf["id"] == null || sconf["id"].Type != JTokenType.Integer))
|
|
|
|
|
if (sconf["id"] == null)
|
2017-06-23 19:31:47 +00:00
|
|
|
|
{
|
2017-10-21 20:14:51 +00:00
|
|
|
|
await Log("Error: Server ID is missing within definition.");
|
2017-06-23 19:31:47 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2017-10-21 20:14:51 +00:00
|
|
|
|
ulong sid = sconf["id"].Value<ulong>();
|
|
|
|
|
string sname = sconf["name"]?.Value<string>();
|
2017-06-23 19:31:47 +00:00
|
|
|
|
|
2017-10-21 20:14:51 +00:00
|
|
|
|
var SLog = Logger.GetLogger(LogPrefix + "/" + (sname ?? sid.ToString()));
|
2017-06-23 19:31:47 +00:00
|
|
|
|
|
|
|
|
|
// Load server moderator list
|
|
|
|
|
EntityList mods = new EntityList(sconf["moderators"]);
|
|
|
|
|
if (sconf["moderators"] != null) await SLog("Moderator " + mods.ToString());
|
2017-07-26 22:36:59 +00:00
|
|
|
|
|
2018-03-22 07:30:22 +00:00
|
|
|
|
// Set up module state / load configurations
|
2017-11-12 03:12:24 +00:00
|
|
|
|
Dictionary<BotModule, object> customConfs = new Dictionary<BotModule, object>();
|
|
|
|
|
foreach (var item in _bot.Modules)
|
2017-06-23 19:31:47 +00:00
|
|
|
|
{
|
2018-03-22 06:27:19 +00:00
|
|
|
|
var confSection = item.Name;
|
|
|
|
|
|
|
|
|
|
var section = sconf[confSection];
|
2018-03-22 07:30:22 +00:00
|
|
|
|
await SLog("Setting up " + item.Name);
|
2017-07-26 22:36:59 +00:00
|
|
|
|
object result;
|
2017-06-23 19:31:47 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
2018-03-22 07:30:22 +00:00
|
|
|
|
result = await item.CreateInstanceState(section);
|
2017-07-26 22:36:59 +00:00
|
|
|
|
}
|
|
|
|
|
catch (RuleImportException ex)
|
2017-06-23 19:31:47 +00:00
|
|
|
|
{
|
2017-07-26 22:36:59 +00:00
|
|
|
|
await SLog($"{item.Name} failed to load configuration: " + ex.Message);
|
2017-06-23 19:31:47 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-03-22 07:30:22 +00:00
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
await SLog("Encountered unhandled exception:");
|
|
|
|
|
await SLog(ex.ToString());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2017-07-26 22:36:59 +00:00
|
|
|
|
|
|
|
|
|
customConfs.Add(item, result);
|
2017-06-23 19:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-22 06:27:19 +00:00
|
|
|
|
// Switch to new configuration
|
2017-06-23 19:31:47 +00:00
|
|
|
|
List<Tuple<Regex, string[]>> rulesfinal = new List<Tuple<Regex, string[]>>();
|
2017-11-12 03:12:24 +00:00
|
|
|
|
newservers.Add(new ServerConfig(sid, mods, new ReadOnlyDictionary<BotModule, object>(customConfs)));
|
2017-06-23 19:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_servers = newservers.ToArray();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-17 07:41:12 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets a value stating if database access is available.
|
|
|
|
|
/// Specifically, indicates if <see cref="GetOpenDatabaseConnectionAsync"/> will return a non-null value.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// Ideally, this value remains constant on runtime. It does not take into account
|
|
|
|
|
/// the possibility of the database connection failing during the program's run time.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public bool DatabaseAvailable => _dbConfig != null;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets an opened connection to the SQL database, if available.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>
|
|
|
|
|
/// An <see cref="Npgsql.NpgsqlConnection"/> in the opened state,
|
|
|
|
|
/// or null if an SQL database is not available.
|
|
|
|
|
/// </returns>
|
|
|
|
|
public Task<Npgsql.NpgsqlConnection> GetOpenDatabaseConnectionAsync() => _dbConfig?.GetOpenConnectionAsync();
|
|
|
|
|
}
|
2017-06-23 19:31:47 +00:00
|
|
|
|
}
|