2022-08-10 00:37:29 +00:00
|
|
|
|
using CommandLine;
|
|
|
|
|
using Newtonsoft.Json;
|
2020-04-02 18:27:55 +00:00
|
|
|
|
using Newtonsoft.Json.Linq;
|
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;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Loads and holds configuration values.
|
|
|
|
|
/// </summary>
|
2024-04-29 01:15:12 +00:00
|
|
|
|
partial class Configuration {
|
|
|
|
|
[GeneratedRegex(@"(?<low>\d{1,2})[-,](?<high>\d{1,2})")]
|
|
|
|
|
private static partial Regex ShardRangeParser();
|
2021-10-19 00:26:10 +00:00
|
|
|
|
const string KeyShardRange = "ShardRange";
|
|
|
|
|
|
2021-10-15 01:55:04 +00:00
|
|
|
|
public string BotToken { get; }
|
|
|
|
|
public string? DBotsToken { 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; }
|
2024-04-28 08:51:58 +00:00
|
|
|
|
|
2022-08-10 00:37:29 +00:00
|
|
|
|
public string? SqlHost { get; }
|
|
|
|
|
public string? SqlDatabase { get; }
|
|
|
|
|
public string SqlUsername { get; }
|
|
|
|
|
public string SqlPassword { get; }
|
|
|
|
|
internal string SqlApplicationName { get; }
|
2020-04-02 18:27:55 +00:00
|
|
|
|
|
2023-05-28 01:07:45 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Number of seconds between each time the status task runs, in seconds.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public int StatusInterval { get; }
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Number of concurrent shard startups to happen on each check.
|
|
|
|
|
/// This value also determines the maximum amount of concurrent background database operations.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public int MaxConcurrentOperations { get; }
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Amount of time to wait between background task runs within each shard.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public int BackgroundInterval { get; }
|
2023-05-28 01:46:28 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets whether to show common connect/disconnect events and other related messages.
|
|
|
|
|
/// This is disabled in the public instance, but it's worth keeping enabled in self-hosted bots.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool LogConnectionStatus { get; }
|
2023-05-28 01:07:45 +00:00
|
|
|
|
|
2022-08-10 00:37:29 +00:00
|
|
|
|
public Configuration() {
|
|
|
|
|
var args = CommandLineParameters.Parse(Environment.GetCommandLineArgs());
|
|
|
|
|
var path = args?.ConfigFile ?? Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)
|
|
|
|
|
+ Path.DirectorySeparatorChar + "." + Path.DirectorySeparatorChar + "settings.json";
|
2020-04-02 18:27:55 +00:00
|
|
|
|
|
2021-10-23 00:44:53 +00:00
|
|
|
|
// Looks for configuration file
|
2022-08-10 00:37:29 +00:00
|
|
|
|
JObject jc;
|
|
|
|
|
try {
|
|
|
|
|
var conftxt = File.ReadAllText(path);
|
|
|
|
|
jc = JObject.Parse(conftxt);
|
|
|
|
|
} catch (Exception ex) {
|
|
|
|
|
string pfx;
|
|
|
|
|
if (ex is JsonException) pfx = "Unable to parse configuration: ";
|
|
|
|
|
else pfx = "Unable to access configuration: ";
|
|
|
|
|
|
|
|
|
|
throw new Exception(pfx + ex.Message, ex);
|
|
|
|
|
}
|
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);
|
2020-04-02 18:27:55 +00:00
|
|
|
|
|
2022-08-10 00:37:29 +00:00
|
|
|
|
ShardTotal = args.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
|
|
|
|
|
2022-08-10 00:37:29 +00:00
|
|
|
|
var shardRangeInput = args.ShardRange ?? ReadConfKey<string>(jc, KeyShardRange, false);
|
2021-10-19 00:26:10 +00:00
|
|
|
|
if (!string.IsNullOrWhiteSpace(shardRangeInput)) {
|
2024-04-29 01:15:12 +00:00
|
|
|
|
var m = ShardRangeParser().Match(shardRangeInput);
|
2021-10-15 01:55:04 +00:00
|
|
|
|
if (m.Success) {
|
|
|
|
|
ShardStart = int.Parse(m.Groups["low"].Value);
|
2022-08-10 00:37:29 +00:00
|
|
|
|
var high = int.Parse(m.Groups["high"].Value);
|
2021-10-15 01:55:04 +00:00
|
|
|
|
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
|
|
|
|
|
2022-08-10 00:37:29 +00:00
|
|
|
|
SqlHost = ReadConfKey<string>(jc, nameof(SqlHost), false);
|
|
|
|
|
SqlDatabase = ReadConfKey<string?>(jc, nameof(SqlDatabase), false);
|
|
|
|
|
SqlUsername = ReadConfKey<string>(jc, nameof(SqlUsername), true);
|
|
|
|
|
SqlPassword = ReadConfKey<string>(jc, nameof(SqlPassword), true);
|
2022-11-21 18:45:46 +00:00
|
|
|
|
SqlApplicationName = $"Shard{ShardStart:00}-{ShardStart + ShardAmount - 1:00}";
|
2023-05-28 01:07:45 +00:00
|
|
|
|
|
|
|
|
|
StatusInterval = ReadConfKey<int?>(jc, nameof(StatusInterval), false) ?? 90;
|
|
|
|
|
MaxConcurrentOperations = ReadConfKey<int?>(jc, nameof(MaxConcurrentOperations), false) ?? 4;
|
|
|
|
|
BackgroundInterval = ReadConfKey<int?>(jc, nameof(BackgroundInterval), false) ?? 60;
|
2023-05-28 01:46:28 +00:00
|
|
|
|
LogConnectionStatus = ReadConfKey<bool?>(jc, nameof(LogConnectionStatus), false) ?? true;
|
2021-10-19 00:26:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
2022-08-10 00:37:29 +00:00
|
|
|
|
class CommandLineParameters {
|
|
|
|
|
[Option('c', "config")]
|
|
|
|
|
public string? ConfigFile { get; set; }
|
2021-10-23 00:44:53 +00:00
|
|
|
|
|
2022-08-10 00:37:29 +00:00
|
|
|
|
[Option("shardtotal")]
|
2021-10-23 00:44:53 +00:00
|
|
|
|
public int? ShardTotal { get; set; }
|
|
|
|
|
|
2022-08-10 00:37:29 +00:00
|
|
|
|
[Option("shardrange")]
|
2021-10-23 00:44:53 +00:00
|
|
|
|
public string? ShardRange { get; set; }
|
|
|
|
|
|
2022-08-10 00:37:29 +00:00
|
|
|
|
public static CommandLineParameters? Parse(string[] args) {
|
|
|
|
|
CommandLineParameters? result = null;
|
|
|
|
|
|
|
|
|
|
new Parser(settings => {
|
|
|
|
|
settings.IgnoreUnknownArguments = true;
|
|
|
|
|
settings.AutoHelp = false;
|
|
|
|
|
settings.AutoVersion = false;
|
|
|
|
|
}).ParseArguments<CommandLineParameters>(args)
|
|
|
|
|
.WithParsed(p => result = p)
|
|
|
|
|
.WithNotParsed(e => { /* ignore */ });
|
|
|
|
|
return result;
|
2021-10-23 00:44:53 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-02 18:27:55 +00:00
|
|
|
|
}
|