2020-04-02 18:27:55 +00:00
|
|
|
|
using BirthdayBot.Data;
|
2021-12-06 02:58:10 +00:00
|
|
|
|
using CommandLine;
|
|
|
|
|
using CommandLine.Text;
|
2020-04-02 18:27:55 +00:00
|
|
|
|
using Newtonsoft.Json.Linq;
|
2020-10-05 22:52:29 +00:00
|
|
|
|
using Npgsql;
|
2021-12-06 02:58:10 +00:00
|
|
|
|
using System.Diagnostics.CodeAnalysis;
|
2020-04-02 18:27:55 +00:00
|
|
|
|
using System.Reflection;
|
2021-06-16 02:29:35 +00:00
|
|
|
|
using System.Text.RegularExpressions;
|
2020-04-02 18:27:55 +00:00
|
|
|
|
|
2021-10-15 01:55:04 +00:00
|
|
|
|
namespace BirthdayBot;
|
2021-06-16 02:29:35 +00:00
|
|
|
|
|
2021-10-15 01:55:04 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Loads and holds configuration values.
|
|
|
|
|
/// </summary>
|
|
|
|
|
class Configuration {
|
2021-10-19 00:26:10 +00:00
|
|
|
|
const string KeySqlHost = "SqlHost";
|
|
|
|
|
const string KeySqlUsername = "SqlUsername";
|
|
|
|
|
const string KeySqlPassword = "SqlPassword";
|
|
|
|
|
const string KeySqlDatabase = "SqlDatabase";
|
|
|
|
|
const string KeyShardRange = "ShardRange";
|
|
|
|
|
|
2021-10-15 01:55:04 +00:00
|
|
|
|
public string BotToken { get; }
|
|
|
|
|
public string? DBotsToken { get; }
|
2021-10-19 00:26:10 +00:00
|
|
|
|
public bool QuitOnFails { get; }
|
2021-06-16 02:29:35 +00:00
|
|
|
|
|
2021-10-15 01:55:04 +00:00
|
|
|
|
public int ShardStart { get; }
|
|
|
|
|
public int ShardAmount { get; }
|
|
|
|
|
public int ShardTotal { get; }
|
2020-04-02 18:27:55 +00:00
|
|
|
|
|
2021-10-23 00:44:53 +00:00
|
|
|
|
public Configuration(string[] args) {
|
|
|
|
|
var cmdline = CmdLineOpts.Parse(args);
|
2020-04-02 18:27:55 +00:00
|
|
|
|
|
2021-10-23 00:44:53 +00:00
|
|
|
|
// Looks for configuration file
|
|
|
|
|
var confPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + Path.DirectorySeparatorChar;
|
|
|
|
|
confPath += cmdline.Config!;
|
|
|
|
|
if (!File.Exists(confPath)) throw new Exception("Settings file not found in path: " + confPath);
|
2020-04-02 18:27:55 +00:00
|
|
|
|
|
2021-10-15 01:55:04 +00:00
|
|
|
|
var jc = JObject.Parse(File.ReadAllText(confPath));
|
2020-04-02 18:27:55 +00:00
|
|
|
|
|
2021-10-19 00:26:10 +00:00
|
|
|
|
BotToken = ReadConfKey<string>(jc, nameof(BotToken), true);
|
|
|
|
|
DBotsToken = ReadConfKey<string>(jc, nameof(DBotsToken), false);
|
|
|
|
|
QuitOnFails = ReadConfKey<bool?>(jc, nameof(QuitOnFails), false) ?? false;
|
2020-04-02 18:27:55 +00:00
|
|
|
|
|
2021-10-23 00:44:53 +00:00
|
|
|
|
ShardTotal = cmdline.ShardTotal ?? ReadConfKey<int?>(jc, nameof(ShardTotal), false) ?? 1;
|
2021-10-19 00:26:10 +00:00
|
|
|
|
if (ShardTotal < 1) throw new Exception($"'{nameof(ShardTotal)}' must be a positive integer.");
|
2021-06-16 02:29:35 +00:00
|
|
|
|
|
2021-10-23 00:44:53 +00:00
|
|
|
|
string shardRangeInput = cmdline.ShardRange ?? ReadConfKey<string>(jc, KeyShardRange, false);
|
2021-10-19 00:26:10 +00:00
|
|
|
|
if (!string.IsNullOrWhiteSpace(shardRangeInput)) {
|
2021-10-15 01:55:04 +00:00
|
|
|
|
Regex srPicker = new(@"(?<low>\d{1,2})[-,]{1}(?<high>\d{1,2})");
|
2021-10-19 00:26:10 +00:00
|
|
|
|
var m = srPicker.Match(shardRangeInput);
|
2021-10-15 01:55:04 +00:00
|
|
|
|
if (m.Success) {
|
|
|
|
|
ShardStart = int.Parse(m.Groups["low"].Value);
|
|
|
|
|
int high = int.Parse(m.Groups["high"].Value);
|
|
|
|
|
ShardAmount = high - (ShardStart - 1);
|
|
|
|
|
} else {
|
2021-10-19 00:26:10 +00:00
|
|
|
|
throw new Exception($"Shard range not properly formatted in '{KeyShardRange}'.");
|
2021-06-16 02:29:35 +00:00
|
|
|
|
}
|
2021-10-15 01:55:04 +00:00
|
|
|
|
} else {
|
2021-10-19 00:26:10 +00:00
|
|
|
|
// Default: this instance handles all shards
|
2021-10-15 01:55:04 +00:00
|
|
|
|
ShardStart = 0;
|
|
|
|
|
ShardAmount = ShardTotal;
|
2020-04-02 18:27:55 +00:00
|
|
|
|
}
|
2021-10-15 01:55:04 +00:00
|
|
|
|
|
2021-10-19 00:26:10 +00:00
|
|
|
|
var sqlhost = ReadConfKey<string>(jc, KeySqlHost, false) ?? "localhost"; // Default to localhost
|
|
|
|
|
var sqluser = ReadConfKey<string>(jc, KeySqlUsername, false);
|
|
|
|
|
var sqlpass = ReadConfKey<string>(jc, KeySqlPassword, false);
|
|
|
|
|
if (string.IsNullOrWhiteSpace(sqluser) || string.IsNullOrWhiteSpace(sqlpass))
|
|
|
|
|
throw new Exception("'SqlUsername', 'SqlPassword' must be specified.");
|
|
|
|
|
var csb = new NpgsqlConnectionStringBuilder() {
|
|
|
|
|
Host = sqlhost,
|
|
|
|
|
Username = sqluser,
|
|
|
|
|
Password = sqlpass,
|
|
|
|
|
ApplicationName = $"ClientShard{ShardStart}+{ShardAmount}"
|
|
|
|
|
};
|
|
|
|
|
var sqldb = ReadConfKey<string>(jc, KeySqlDatabase, false);
|
|
|
|
|
if (sqldb != null) csb.Database = sqldb; // Optional database setting
|
|
|
|
|
Database.DBConnectionString = csb.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-23 00:44:53 +00:00
|
|
|
|
private static T? ReadConfKey<T>(JObject jc, string key, [DoesNotReturnIf(true)] bool failOnEmpty) {
|
|
|
|
|
if (jc.ContainsKey(key)) return jc[key]!.Value<T>();
|
2021-10-19 00:26:10 +00:00
|
|
|
|
if (failOnEmpty) throw new Exception($"'{key}' must be specified.");
|
|
|
|
|
return default;
|
2020-04-02 18:27:55 +00:00
|
|
|
|
}
|
2021-10-23 00:44:53 +00:00
|
|
|
|
|
|
|
|
|
private class CmdLineOpts {
|
|
|
|
|
[Option('c', "config", Default = "settings.json",
|
|
|
|
|
HelpText = "Custom path to instance configuration, relative from executable directory.")]
|
|
|
|
|
public string? Config { get; set; }
|
|
|
|
|
|
|
|
|
|
[Option("shardtotal",
|
|
|
|
|
HelpText = "Total number of shards online. MUST be the same for all instances.\n"
|
|
|
|
|
+ "This value overrides the config file value.")]
|
|
|
|
|
public int? ShardTotal { get; set; }
|
|
|
|
|
|
|
|
|
|
[Option("shardrange", HelpText = "Shard range for this instance to handle.\n"
|
|
|
|
|
+ "This value overrides the config file value.")]
|
|
|
|
|
public string? ShardRange { get; set; }
|
|
|
|
|
|
|
|
|
|
public static CmdLineOpts Parse(string[] args) {
|
|
|
|
|
// Do not automatically print help message
|
|
|
|
|
var clp = new Parser(c => c.HelpWriter = null);
|
|
|
|
|
|
|
|
|
|
CmdLineOpts? result = null;
|
|
|
|
|
var r = clp.ParseArguments<CmdLineOpts>(args);
|
|
|
|
|
r.WithParsed(parsed => result = parsed);
|
|
|
|
|
r.WithNotParsed(err => {
|
|
|
|
|
var ht = HelpText.AutoBuild(r);
|
|
|
|
|
Console.WriteLine(ht.ToString());
|
|
|
|
|
Environment.Exit((int)Program.ExitCodes.BadCommand);
|
|
|
|
|
});
|
|
|
|
|
return result!;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-02 18:27:55 +00:00
|
|
|
|
}
|