'First' commit; add most of the initialization routine
This commit is contained in:
parent
3bf509c5d4
commit
ea4d7b0a29
7 changed files with 299 additions and 0 deletions
72
Kerobot/InstanceConfig.cs
Normal file
72
Kerobot/InstanceConfig.cs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Kerobot
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains instance configuration for this bot,
|
||||||
|
/// including Discord connection settings and service configuration.
|
||||||
|
/// </summary>
|
||||||
|
class InstanceConfig
|
||||||
|
{
|
||||||
|
const string JBotToken = "BotToken";
|
||||||
|
readonly string _botToken;
|
||||||
|
/// <summary>
|
||||||
|
/// Token used for Discord authentication.
|
||||||
|
/// </summary>
|
||||||
|
internal string BotToken => _botToken;
|
||||||
|
|
||||||
|
const string JPgSqlConnectionString = "SqlConnectionString";
|
||||||
|
readonly string _pgSqlConnectionString;
|
||||||
|
/// <summary>
|
||||||
|
/// Connection string for accessing the PostgreSQL database.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// That's right, the user can specify the -entire- thing.
|
||||||
|
/// Should problems arise, this will be replaced by a full section within configuration.
|
||||||
|
/// </remarks>
|
||||||
|
internal string PostgresConnString => _pgSqlConnectionString;
|
||||||
|
|
||||||
|
// TODO add fields for services to be configurable: DMRelay, InstanceLog
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets up instance configuration object from file and command line parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">Path to file from which to load configuration. If null, uses default path.</param>
|
||||||
|
internal InstanceConfig(Options options)
|
||||||
|
{
|
||||||
|
string path = options.ConfigFile;
|
||||||
|
if (path == null) // default: config.json in working directory
|
||||||
|
{
|
||||||
|
path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)
|
||||||
|
+ "." + Path.DirectorySeparatorChar + "config.json";
|
||||||
|
}
|
||||||
|
|
||||||
|
JObject conf;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var conftxt = File.ReadAllText(path);
|
||||||
|
conf = 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input validation - throw exception on errors. Exception messages printed as-is.
|
||||||
|
_botToken = conf[JBotToken]?.Value<string>();
|
||||||
|
if (string.IsNullOrEmpty(_botToken))
|
||||||
|
throw new Exception($"'{JBotToken}' was not properly specified in configuration.");
|
||||||
|
_pgSqlConnectionString = conf[JPgSqlConnectionString]?.Value<string>();
|
||||||
|
if (string.IsNullOrEmpty(_pgSqlConnectionString))
|
||||||
|
throw new Exception($"'{JPgSqlConnectionString}' was not properly specified in configuration.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
63
Kerobot/Kerobot.cs
Normal file
63
Kerobot/Kerobot.cs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
using Discord;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using Kerobot.Services;
|
||||||
|
using Npgsql;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Kerobot
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Kerobot main class, and the most accessible and useful class in the whole program.
|
||||||
|
/// Provides an interface for any part of the program to call into all existing services.
|
||||||
|
/// </summary>
|
||||||
|
public partial class Kerobot
|
||||||
|
{
|
||||||
|
// Partial class: Services are able to add their own methods and properties to this class.
|
||||||
|
// This is to prevent this file from having too many references to many different and unrelated features.
|
||||||
|
|
||||||
|
private readonly InstanceConfig _icfg;
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets application instance configuration.
|
||||||
|
/// </summary>
|
||||||
|
internal InstanceConfig Config => _icfg;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Discord client instance.
|
||||||
|
/// </summary>
|
||||||
|
public DiscordSocketClient DiscordClient => _client;
|
||||||
|
|
||||||
|
internal Kerobot(InstanceConfig conf, DiscordSocketClient client)
|
||||||
|
{
|
||||||
|
_icfg = conf;
|
||||||
|
_client = client;
|
||||||
|
|
||||||
|
InitializeServices();
|
||||||
|
|
||||||
|
// and prepare modules here
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeServices()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an open NpgsqlConnection instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="guild">
|
||||||
|
/// If manipulating guild-specific information, this parameter sets the database connection's search path.
|
||||||
|
/// </param>
|
||||||
|
internal async Task<NpgsqlConnection> GetOpenNpgsqlConnectionAsync(ulong? guild)
|
||||||
|
{
|
||||||
|
string cs = _icfg.PostgresConnString;
|
||||||
|
if (guild.HasValue) cs += ";searchpath=guild_" + guild.Value;
|
||||||
|
|
||||||
|
var db = new NpgsqlConnection(cs);
|
||||||
|
await db.OpenAsync();
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@
|
||||||
<Description>Advanced and flexible Discord moderation bot.</Description>
|
<Description>Advanced and flexible Discord moderation bot.</Description>
|
||||||
<FileVersion>0.0.1</FileVersion>
|
<FileVersion>0.0.1</FileVersion>
|
||||||
<Version>0.0.1</Version>
|
<Version>0.0.1</Version>
|
||||||
|
<LangVersion>7.2</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
38
Kerobot/Options.cs
Normal file
38
Kerobot/Options.cs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
using CommandLine;
|
||||||
|
using CommandLine.Text;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Kerobot
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Command line options
|
||||||
|
/// </summary>
|
||||||
|
class Options
|
||||||
|
{
|
||||||
|
[Option('c', "config", Default = null,
|
||||||
|
HelpText = "Custom path to instance configuration. Defaults to config.json in bot directory.")]
|
||||||
|
public string ConfigFile { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Command line arguments parsed here. Depending on inputs, the program can exit here.
|
||||||
|
/// </summary>
|
||||||
|
public static Options ParseOptions(string[] args)
|
||||||
|
{
|
||||||
|
// Parser will not write out to console by itself
|
||||||
|
var parser = new Parser(config => config.HelpWriter = null);
|
||||||
|
Options opts = null;
|
||||||
|
|
||||||
|
var result = parser.ParseArguments<Options>(args);
|
||||||
|
result.WithParsed(p => opts = p);
|
||||||
|
result.WithNotParsed(p =>
|
||||||
|
{
|
||||||
|
// Taking some extra steps to modify the header to make it resemble our welcome message.
|
||||||
|
var ht = HelpText.AutoBuild(result);
|
||||||
|
ht.Heading = ht.Heading += " - https://github.com/Noikoio/Kerobot";
|
||||||
|
Console.WriteLine(ht.ToString());
|
||||||
|
Environment.Exit(1);
|
||||||
|
});
|
||||||
|
return opts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
90
Kerobot/Program.cs
Normal file
90
Kerobot/Program.cs
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
using Discord;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Kerobot
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Program startup class. Does initialization before starting the Discord client.
|
||||||
|
/// </summary>
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static DateTimeOffset _startTime;
|
||||||
|
/// <summary>
|
||||||
|
/// Timestamp specifying the date and time that the program began running.
|
||||||
|
/// </summary>
|
||||||
|
public static DateTimeOffset StartTime => _startTime;
|
||||||
|
|
||||||
|
static Kerobot _main;
|
||||||
|
|
||||||
|
static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
_startTime = DateTimeOffset.UtcNow;
|
||||||
|
Console.WriteLine("Bot start time: " + _startTime.ToString("u"));
|
||||||
|
|
||||||
|
// Get instance config figured out
|
||||||
|
var opts = Options.ParseOptions(args); // Program can exit here.
|
||||||
|
InstanceConfig cfg;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cfg = new InstanceConfig(opts);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.Message);
|
||||||
|
Environment.ExitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quick test if database configuration works
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var d = new Npgsql.NpgsqlConnection(cfg.PostgresConnString))
|
||||||
|
{
|
||||||
|
await d.OpenAsync();
|
||||||
|
d.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Could not establish a database connection! Check your settings and try again.");
|
||||||
|
Console.WriteLine($"Error: {ex.GetType().FullName}: {ex.Message}");
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure Discord client
|
||||||
|
var client = new DiscordSocketClient(new DiscordSocketConfig()
|
||||||
|
{
|
||||||
|
DefaultRetryMode = RetryMode.AlwaysRetry,
|
||||||
|
MessageCacheSize = 0 // using our own
|
||||||
|
});
|
||||||
|
|
||||||
|
// Kerobot class initialization - will set up services and modules
|
||||||
|
_main = new Kerobot(cfg, client);
|
||||||
|
|
||||||
|
// Set up application close handler
|
||||||
|
Console.CancelKeyPress += Console_CancelKeyPress;
|
||||||
|
|
||||||
|
// TODO Set up unhandled exception handler
|
||||||
|
// send error notification to instance log channel, if possible
|
||||||
|
|
||||||
|
// And off we go.
|
||||||
|
await _main.DiscordClient.LoginAsync(Discord.TokenType.Bot, cfg.BotToken);
|
||||||
|
await _main.DiscordClient.StartAsync();
|
||||||
|
await Task.Delay(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
|
||||||
|
{
|
||||||
|
// TODO finish implementation when logging is set up
|
||||||
|
e.Cancel = true;
|
||||||
|
// _main.Log("Received Cancel event. Application will shut down...");
|
||||||
|
// stop periodic task processing - wait for current run to finish if executing (handled by service?)
|
||||||
|
// notify services of shutdown
|
||||||
|
bool success = _main.DiscordClient.LogoutAsync().Wait(10000);
|
||||||
|
// if (!success) _main.Log("Failed to disconnect cleanly from Discord. Will force shut down.");
|
||||||
|
Environment.Exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
Kerobot/Services/GuildStateManager/Manager.cs
Normal file
10
Kerobot/Services/GuildStateManager/Manager.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Kerobot.Services.GuildStateManager
|
||||||
|
{
|
||||||
|
class Manager
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
25
Kerobot/Services/Service.cs
Normal file
25
Kerobot/Services/Service.cs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Kerobot.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for Kerobot service.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Services provide the core functionality of this program. Modules are expected to call into methods
|
||||||
|
/// provided by services for the times when processor-intensive or shared functionality needs to be utilized.
|
||||||
|
/// </remarks>
|
||||||
|
internal class Service
|
||||||
|
{
|
||||||
|
private readonly Kerobot _kb;
|
||||||
|
|
||||||
|
public Kerobot Kerobot => _kb;
|
||||||
|
|
||||||
|
protected internal Service(Kerobot kb)
|
||||||
|
{
|
||||||
|
_kb = kb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue