mirror of
https://github.com/NoiTheCat/BirthdayBot.git
synced 2024-11-21 13:54:36 +00:00
Several improvements for program loading
-Add command line options for shards -Set consistent exit codes -Turn on nullable option and edit code further to conform and reduce warnings -Update libraries
This commit is contained in:
parent
0c56a0859a
commit
16ac294fe3
4 changed files with 73 additions and 24 deletions
|
@ -3,13 +3,13 @@
|
|||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.1.4</Version>
|
||||
<Version>3.2.0</Version>
|
||||
<PackageId>BirthdayBot</PackageId>
|
||||
<Authors>NoiTheCat</Authors>
|
||||
<Description>Discord bot for birthday recognition and reminders.</Description>
|
||||
<AssemblyName>BirthdayBot</AssemblyName>
|
||||
<RootNamespace>BirthdayBot</RootNamespace>
|
||||
<Nullable>annotations</Nullable>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
@ -24,10 +24,11 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
||||
<PackageReference Include="Discord.Net" Version="3.0.0-dev-20210822.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="NodaTime" Version="3.0.7" />
|
||||
<PackageReference Include="Npgsql" Version="5.0.10" />
|
||||
<PackageReference Include="NodaTime" Version="3.0.9" />
|
||||
<PackageReference Include="Npgsql" Version="6.0.0-rc.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -5,6 +5,9 @@ using System;
|
|||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using CommandLine;
|
||||
using CommandLine.Text;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace BirthdayBot;
|
||||
|
||||
|
@ -27,15 +30,13 @@ class Configuration {
|
|||
public int ShardAmount { get; }
|
||||
public int ShardTotal { get; }
|
||||
|
||||
public Configuration() {
|
||||
// Looks for settings.json in the executable directory.
|
||||
var confPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
confPath += Path.DirectorySeparatorChar + "settings.json";
|
||||
public Configuration(string[] args) {
|
||||
var cmdline = CmdLineOpts.Parse(args);
|
||||
|
||||
if (!File.Exists(confPath)) {
|
||||
throw new Exception("Settings file not found."
|
||||
+ " Create a file in the executable directory named 'settings.json'.");
|
||||
}
|
||||
// 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);
|
||||
|
||||
var jc = JObject.Parse(File.ReadAllText(confPath));
|
||||
|
||||
|
@ -44,10 +45,10 @@ class Configuration {
|
|||
DBotsToken = ReadConfKey<string>(jc, nameof(DBotsToken), false);
|
||||
QuitOnFails = ReadConfKey<bool?>(jc, nameof(QuitOnFails), false) ?? false;
|
||||
|
||||
ShardTotal = ReadConfKey<int?>(jc, nameof(ShardTotal), false) ?? 1;
|
||||
ShardTotal = cmdline.ShardTotal ?? ReadConfKey<int?>(jc, nameof(ShardTotal), false) ?? 1;
|
||||
if (ShardTotal < 1) throw new Exception($"'{nameof(ShardTotal)}' must be a positive integer.");
|
||||
|
||||
string shardRangeInput = ReadConfKey<string>(jc, KeyShardRange, false);
|
||||
string shardRangeInput = cmdline.ShardRange ?? ReadConfKey<string>(jc, KeyShardRange, false);
|
||||
if (!string.IsNullOrWhiteSpace(shardRangeInput)) {
|
||||
Regex srPicker = new(@"(?<low>\d{1,2})[-,]{1}(?<high>\d{1,2})");
|
||||
var m = srPicker.Match(shardRangeInput);
|
||||
|
@ -80,9 +81,39 @@ class Configuration {
|
|||
Database.DBConnectionString = csb.ToString();
|
||||
}
|
||||
|
||||
private static T ReadConfKey<T>(JObject jc, string key, bool failOnEmpty) {
|
||||
if (jc.ContainsKey(key)) return jc[key].Value<T>();
|
||||
private static T? ReadConfKey<T>(JObject jc, string key, [DoesNotReturnIf(true)] bool failOnEmpty) {
|
||||
if (jc.ContainsKey(key)) return jc[key]!.Value<T>();
|
||||
if (failOnEmpty) throw new Exception($"'{key}' must be specified.");
|
||||
return default;
|
||||
}
|
||||
|
||||
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!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
31
Program.cs
31
Program.cs
|
@ -5,7 +5,7 @@ using System.Threading.Tasks;
|
|||
namespace BirthdayBot;
|
||||
|
||||
class Program {
|
||||
private static ShardManager _bot;
|
||||
private static ShardManager? _bot;
|
||||
private static readonly DateTimeOffset _botStartTime = DateTimeOffset.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
|
@ -13,13 +13,20 @@ class Program {
|
|||
/// </summary>
|
||||
public static string BotUptime => (DateTimeOffset.UtcNow - _botStartTime).ToString("d' days, 'hh':'mm':'ss");
|
||||
|
||||
static async Task Main() {
|
||||
var cfg = new Configuration();
|
||||
static async Task Main(string[] args) {
|
||||
Configuration? cfg = null;
|
||||
try {
|
||||
cfg = new Configuration(args);
|
||||
} catch (Exception ex) {
|
||||
Console.WriteLine(ex);
|
||||
Environment.Exit((int)ExitCodes.ConfigError);
|
||||
}
|
||||
|
||||
try {
|
||||
await Database.DoInitialDatabaseSetupAsync();
|
||||
} catch (Npgsql.NpgsqlException e) {
|
||||
Console.WriteLine("Error when attempting to connect to database: " + e.Message);
|
||||
Environment.Exit(1);
|
||||
Environment.Exit((int)ExitCodes.DatabaseError);
|
||||
}
|
||||
|
||||
Console.CancelKeyPress += OnCancelKeyPressed;
|
||||
|
@ -38,7 +45,7 @@ class Program {
|
|||
Console.WriteLine($"{ts:u} [{source}] {item}");
|
||||
}
|
||||
|
||||
private static void OnCancelKeyPressed(object sender, ConsoleCancelEventArgs e) {
|
||||
private static void OnCancelKeyPressed(object? sender, ConsoleCancelEventArgs e) {
|
||||
e.Cancel = true;
|
||||
Log("Shutdown", "Captured cancel key; sending shutdown.");
|
||||
ProgramStop();
|
||||
|
@ -50,11 +57,21 @@ class Program {
|
|||
_stopping = true;
|
||||
Log("Shutdown", "Commencing shutdown...");
|
||||
|
||||
var dispose = Task.Run(_bot.Dispose);
|
||||
var dispose = Task.Run(_bot!.Dispose);
|
||||
if (!dispose.Wait(90000)) {
|
||||
Log("Shutdown", "Normal shutdown has not concluded after 90 seconds. Will force quit.");
|
||||
Environment.ExitCode += 0x200;
|
||||
Environment.ExitCode &= (int)ExitCodes.ForcedExit;
|
||||
}
|
||||
Environment.Exit(Environment.ExitCode);
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ExitCodes {
|
||||
Normal = 0x0,
|
||||
ForcedExit = 0x1,
|
||||
ConfigError = 0x2,
|
||||
DatabaseError = 0x4,
|
||||
DeadShardThreshold = 0x8,
|
||||
BadCommand = 0x10,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -213,7 +213,7 @@ class ShardManager : IDisposable {
|
|||
_destroyedShards++;
|
||||
}
|
||||
if (Config.QuitOnFails && _destroyedShards > MaxDestroyedShards) {
|
||||
Environment.ExitCode = 0x04;
|
||||
Environment.ExitCode = (int)Program.ExitCodes.DeadShardThreshold;
|
||||
Program.ProgramStop();
|
||||
} else {
|
||||
// Start up any missing shards
|
||||
|
|
Loading…
Reference in a new issue