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>
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>
/// Gets application instance configuration.
/// </summary>
internal InstanceConfig Config => _icfg;
internal InstanceConfig Config { get; }
/// <summary>
/// Gets the Discord client instance.
/// </summary>
public DiscordSocketClient DiscordClient => _client;
public DiscordSocketClient DiscordClient { get; }
/// <summary>
/// All loaded services in an iterable form.
/// Gets all loaded services in an iterable form.
/// </summary>
internal IReadOnlyCollection<Service> Services => _services;
internal IReadOnlyCollection<Service> Services { get; }
/// <summary>
/// All loaded modules in an iterable form.
/// Gets all loaded modules in an iterable form.
/// </summary>
internal IReadOnlyCollection<ModuleBase> Modules => _modules;
internal IReadOnlyCollection<ModuleBase> Modules { get; }
internal Kerobot(InstanceConfig conf, DiscordSocketClient client)
{
_icfg = conf;
_client = client;
// 'Ready' event handler. Because there's no other place for it.
_client.Ready += async delegate
{
await InstanceLogAsync(true, "Kerobot", "Connected and ready.");
};
Config = conf;
DiscordClient = client;
// Get all services started up
_services = InitializeServices();
Services = InitializeServices();
// Load externally defined functionality
_modules = ModuleLoader.Load(_icfg, this);
Modules = ModuleLoader.Load(Config, this);
// Everything's ready to go. Print the welcome message here.
var ver = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
@ -81,7 +70,7 @@ namespace Kerobot
/// </param>
internal async Task<NpgsqlConnection> GetOpenNpgsqlConnectionAsync(ulong? guild)
{
string cs = _icfg.PostgresConnString;
string cs = Config.PostgresConnString;
if (guild.HasValue) cs += ";searchpath=guild_" + guild.Value;
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.Threading.Tasks;
@ -9,23 +10,26 @@ namespace Kerobot
/// user input (both by means of configuration and incoming Discord events) and process it accordingly.
/// </summary>
/// <remarks>
/// Implementing classes should not rely on local/instance variables to store data. Make use of
/// <see cref="CreateGuildStateAsync(JToken)"/> and <see cref="GetGuildState{T}(ulong)"/>.
/// Implementing classes should not rely on local variables to store runtime data regarding guilds.
/// Use <see cref="CreateGuildStateAsync(JToken)"/> and <see cref="GetGuildState{T}(ulong)"/>.
/// </remarks>
public abstract class ModuleBase
{
private readonly Kerobot _kb;
/// <summary>
/// Retrieves the Kerobot instance.
/// </summary>
public Kerobot Kerobot => _kb;
public Kerobot Kerobot { get; }
/// <summary>
/// Retrieves the Discord client instance.
/// </summary>
public DiscordSocketClient DiscordClient { get => Kerobot.DiscordClient; }
/// <summary>
/// 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.
/// </summary>
public ModuleBase(Kerobot kb) => _kb = kb;
public ModuleBase(Kerobot kb) => Kerobot = kb;
/// <summary>
/// Gets the module name.
@ -53,6 +57,20 @@ namespace Kerobot
/// Thrown if the stored state object cannot be cast as specified.
/// </exception>
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>

View file

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

View file

@ -10,11 +10,10 @@ namespace Kerobot.Services.GuildState
{
static readonly TimeSpan TimeUntilStale = new TimeSpan(0, 15, 0);
private readonly object _data;
/// <summary>
/// Module-provided data.
/// </summary>
public object Data => _data;
public object Data { get; }
/// <summary>
/// Hash of the JToken used to generate the data. In certain casaes, it is used to check
@ -22,28 +21,29 @@ namespace Kerobot.Services.GuildState
/// </summary>
private readonly int _configHash;
private readonly DateTimeOffset _creationTs;
private DateTimeOffset _lastStaleCheck;
public StateInfo(object data, int configHash)
{
_data = data;
Data = data;
_configHash = configHash;
_creationTs = DateTimeOffset.UtcNow;
_lastStaleCheck = DateTimeOffset.UtcNow;
}
public void Dispose()
{
if (_data is IDisposable dd) { dd.Dispose(); }
if (Data is IDisposable dd) { dd.Dispose(); }
}
/// <summary>
/// Checks if the current data may be stale, based on the data's age or
/// through comparison with incoming configuration.
/// Checks if the current data may be stale, based on the last staleness check or
/// if the underlying configuration has changed.
/// </summary>
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;
_lastStaleCheck = DateTimeOffset.UtcNow;
return false;
}
}

View file

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

View file

@ -3,7 +3,7 @@
namespace Kerobot.Services
{
/// <summary>
/// Base class for Kerobot service.
/// Base class for Kerobot services.
/// </summary>
/// <remarks>
/// Services provide the core functionality of this program. Modules are expected to call into methods
@ -11,18 +11,15 @@ namespace Kerobot.Services
/// </remarks>
internal abstract class Service
{
private readonly Kerobot _kb;
public Kerobot Kerobot => _kb;
public Kerobot Kerobot { get; }
public string Name => this.GetType().Name;
public Service(Kerobot kb)
{
_kb = kb;
}
public Service(Kerobot kb) => Kerobot = kb;
/// <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>
/// <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>