Minor improvements throughout

This commit is contained in:
Noikoio 2018-06-06 15:19:21 -07:00
parent 4ca38fa881
commit 9bd9a21531
6 changed files with 65 additions and 58 deletions

View file

@ -12,47 +12,36 @@ namespace Kerobot
/// </summary> /// </summary>
public partial class Kerobot 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 different and unrelated features.
private readonly InstanceConfig _icfg;
private readonly DiscordSocketClient _client;
private IReadOnlyCollection<Service> _services;
private IReadOnlyCollection<ModuleBase> _modules;
/// <summary> /// <summary>
/// Gets application instance configuration. /// Gets application instance configuration.
/// </summary> /// </summary>
internal InstanceConfig Config => _icfg; internal InstanceConfig Config { get; }
/// <summary> /// <summary>
/// Gets the Discord client instance. /// Gets the Discord client instance.
/// </summary> /// </summary>
public DiscordSocketClient DiscordClient => _client; public DiscordSocketClient DiscordClient { get; }
/// <summary> /// <summary>
/// All loaded services in an iterable form. /// Gets all loaded services in an iterable form.
/// </summary> /// </summary>
internal IReadOnlyCollection<Service> Services => _services; internal IReadOnlyCollection<Service> Services { get; }
/// <summary> /// <summary>
/// All loaded modules in an iterable form. /// Gets all loaded modules in an iterable form.
/// </summary> /// </summary>
internal IReadOnlyCollection<ModuleBase> Modules => _modules; internal IReadOnlyCollection<ModuleBase> Modules { get; }
internal Kerobot(InstanceConfig conf, DiscordSocketClient client) internal Kerobot(InstanceConfig conf, DiscordSocketClient client)
{ {
_icfg = conf; Config = conf;
_client = client; DiscordClient = client;
// 'Ready' event handler. Because there's no other place for it.
_client.Ready += async delegate
{
await InstanceLogAsync(true, "Kerobot", "Connected and ready.");
};
// Get all services started up // Get all services started up
_services = InitializeServices(); Services = InitializeServices();
// Load externally defined functionality // Load externally defined functionality
_modules = ModuleLoader.Load(_icfg, this); Modules = ModuleLoader.Load(Config, this);
// Everything's ready to go. Print the welcome message here. // Everything's ready to go. Print the welcome message here.
var ver = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; var ver = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
@ -81,7 +70,7 @@ namespace Kerobot
/// </param> /// </param>
internal async Task<NpgsqlConnection> GetOpenNpgsqlConnectionAsync(ulong? guild) internal async Task<NpgsqlConnection> GetOpenNpgsqlConnectionAsync(ulong? guild)
{ {
string cs = _icfg.PostgresConnString; string cs = Config.PostgresConnString;
if (guild.HasValue) cs += ";searchpath=guild_" + guild.Value; if (guild.HasValue) cs += ";searchpath=guild_" + guild.Value;
var db = new NpgsqlConnection(cs); var db = new NpgsqlConnection(cs);

View file

@ -1,4 +1,5 @@
using Newtonsoft.Json.Linq; using Discord.WebSocket;
using Newtonsoft.Json.Linq;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -9,23 +10,26 @@ namespace Kerobot
/// user input (both by means of configuration and incoming Discord events) and process it accordingly. /// user input (both by means of configuration and incoming Discord events) and process it accordingly.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Implementing classes should not rely on local/instance variables to store data. Make use of /// Implementing classes should not rely on local variables to store runtime data regarding guilds.
/// <see cref="CreateGuildStateAsync(JToken)"/> and <see cref="GetGuildState{T}(ulong)"/>. /// Use <see cref="CreateGuildStateAsync(JToken)"/> and <see cref="GetGuildState{T}(ulong)"/>.
/// </remarks> /// </remarks>
public abstract class ModuleBase public abstract class ModuleBase
{ {
private readonly Kerobot _kb;
/// <summary> /// <summary>
/// Retrieves the Kerobot instance. /// Retrieves the Kerobot instance.
/// </summary> /// </summary>
public Kerobot Kerobot => _kb; public Kerobot Kerobot { get; }
/// <summary>
/// Retrieves the Discord client instance.
/// </summary>
public DiscordSocketClient DiscordClient { get => Kerobot.DiscordClient; }
/// <summary> /// <summary>
/// When a module is loaded, this constructor is called. /// When a module is loaded, this constructor is called.
/// Services are available at this point. Do not attempt to communicate to Discord within the constructor. /// Services are available at this point. Do not attempt to communicate to Discord within the constructor.
/// </summary> /// </summary>
public ModuleBase(Kerobot kb) => _kb = kb; public ModuleBase(Kerobot kb) => Kerobot = kb;
/// <summary> /// <summary>
/// Gets the module name. /// Gets the module name.
@ -53,6 +57,20 @@ namespace Kerobot
/// Thrown if the stored state object cannot be cast as specified. /// Thrown if the stored state object cannot be cast as specified.
/// </exception> /// </exception>
protected T GetGuildState<T>(ulong guildId) => Kerobot.GetGuildState<T>(guildId, GetType()); protected T GetGuildState<T>(ulong guildId) => Kerobot.GetGuildState<T>(guildId, GetType());
/// <summary>
/// Appends a message to the global instance log. Use sparingly.
/// </summary>
/// <param name="report">
/// Specifies if the log message should be sent to the reporting channel.
/// Only messages of very high importance should use this option.
/// </param>
protected Task LogAsync(string message, bool report = false) => Kerobot.InstanceLogAsync(report, Name, message);
/// <summary>
/// Appends a message to the log for the specified guild.
/// </summary>
protected Task LogAsync(ulong guild, string message) => Kerobot.GuildLogAsync(guild, Name, message);
} }
/// <summary> /// <summary>

View file

@ -10,12 +10,11 @@ namespace Kerobot
/// </summary> /// </summary>
class Program class Program
{ {
static DateTimeOffset _startTime;
/// <summary> /// <summary>
/// Timestamp specifying the date and time that the program began running. /// Timestamp specifying the date and time that the program began running.
/// </summary> /// </summary>
public static DateTimeOffset StartTime => _startTime; public static DateTimeOffset StartTime { get; private set; }
static Kerobot _main; static Kerobot _main;
/// <summary> /// <summary>
@ -23,8 +22,8 @@ namespace Kerobot
/// </summary> /// </summary>
static async Task Main(string[] args) static async Task Main(string[] args)
{ {
_startTime = DateTimeOffset.UtcNow; StartTime = DateTimeOffset.UtcNow;
Console.WriteLine("Bot start time: " + _startTime.ToString("u")); Console.WriteLine("Bot start time: " + StartTime.ToString("u"));
// Get instance configuration from file and parameters // Get instance configuration from file and parameters
var opts = Options.ParseOptions(args); // Program can exit here. var opts = Options.ParseOptions(args); // Program can exit here.
@ -73,7 +72,7 @@ namespace Kerobot
// send error notification to instance log channel, if possible // send error notification to instance log channel, if possible
// And off we go. // And off we go.
await _main.DiscordClient.LoginAsync(Discord.TokenType.Bot, cfg.BotToken); await _main.DiscordClient.LoginAsync(TokenType.Bot, cfg.BotToken);
await _main.DiscordClient.StartAsync(); await _main.DiscordClient.StartAsync();
await Task.Delay(-1); await Task.Delay(-1);
} }

View file

@ -9,41 +9,41 @@ namespace Kerobot.Services.GuildState
class StateInfo : IDisposable class StateInfo : IDisposable
{ {
static readonly TimeSpan TimeUntilStale = new TimeSpan(0, 15, 0); static readonly TimeSpan TimeUntilStale = new TimeSpan(0, 15, 0);
private readonly object _data;
/// <summary> /// <summary>
/// Module-provided data. /// Module-provided data.
/// </summary> /// </summary>
public object Data => _data; public object Data { get; }
/// <summary> /// <summary>
/// Hash of the JToken used to generate the data. In certain casaes, it is used to check /// Hash of the JToken used to generate the data. In certain casaes, it is used to check
/// if the configuration may be stale and needs to be reloaded. /// if the configuration may be stale and needs to be reloaded.
/// </summary> /// </summary>
private readonly int _configHash; private readonly int _configHash;
private readonly DateTimeOffset _creationTs; private DateTimeOffset _lastStaleCheck;
public StateInfo(object data, int configHash) public StateInfo(object data, int configHash)
{ {
_data = data; Data = data;
_configHash = configHash; _configHash = configHash;
_creationTs = DateTimeOffset.UtcNow; _lastStaleCheck = DateTimeOffset.UtcNow;
} }
public void Dispose() public void Dispose()
{ {
if (_data is IDisposable dd) { dd.Dispose(); } if (Data is IDisposable dd) { dd.Dispose(); }
} }
/// <summary> /// <summary>
/// Checks if the current data may be stale, based on the data's age or /// Checks if the current data may be stale, based on the last staleness check or
/// through comparison with incoming configuration. /// if the underlying configuration has changed.
/// </summary> /// </summary>
public bool IsStale(JToken comparison) public bool IsStale(JToken comparison)
{ {
if (DateTimeOffset.UtcNow - _creationTs > TimeUntilStale) return true; if (DateTimeOffset.UtcNow - _lastStaleCheck > TimeUntilStale) return true;
if (comparison.GetHashCode() != _configHash) return true; if (comparison.GetHashCode() != _configHash) return true;
_lastStaleCheck = DateTimeOffset.UtcNow;
return false; return false;
} }
} }

View file

@ -26,6 +26,10 @@ namespace Kerobot.Services.Logging
// Discord.Net log handling (client logging option is specified in Program.cs) // Discord.Net log handling (client logging option is specified in Program.cs)
kb.DiscordClient.Log += DiscordClient_Log; kb.DiscordClient.Log += DiscordClient_Log;
// Ready message too
kb.DiscordClient.Ready +=
async delegate { await DoInstanceLogAsync(true, "Kerobot", "Connected and ready."); };
} }
/// <summary> /// <summary>

View file

@ -3,7 +3,7 @@
namespace Kerobot.Services namespace Kerobot.Services
{ {
/// <summary> /// <summary>
/// Base class for Kerobot service. /// Base class for Kerobot services.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Services provide the core functionality of this program. Modules are expected to call into methods /// Services provide the core functionality of this program. Modules are expected to call into methods
@ -11,18 +11,15 @@ namespace Kerobot.Services
/// </remarks> /// </remarks>
internal abstract class Service internal abstract class Service
{ {
private readonly Kerobot _kb; public Kerobot Kerobot { get; }
public Kerobot Kerobot => _kb;
public string Name => this.GetType().Name; public string Name => this.GetType().Name;
public Service(Kerobot kb) public Service(Kerobot kb) => Kerobot = kb;
{
_kb = kb;
}
/// <summary> /// <summary>
/// Initializes database tables per-guild. Called when entering a guild. /// Initializes database tables per-guild.
/// This method is called by GuildStateService when entering a guild.
/// </summary> /// </summary>
/// <param name="db">An opened database connection with the appropriate schema option set.</param> /// <param name="db">An opened database connection with the appropriate schema option set.</param>
/// <remarks>If overriding, calling the base method is not necessary.</remarks> /// <remarks>If overriding, calling the base method is not necessary.</remarks>