diff --git a/Kerobot/Kerobot.cs b/Kerobot/Kerobot.cs index e750e1f..14c2f49 100644 --- a/Kerobot/Kerobot.cs +++ b/Kerobot/Kerobot.cs @@ -12,47 +12,36 @@ namespace 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 _services; - private IReadOnlyCollection _modules; - /// /// Gets application instance configuration. /// - internal InstanceConfig Config => _icfg; + internal InstanceConfig Config { get; } + /// /// Gets the Discord client instance. /// - public DiscordSocketClient DiscordClient => _client; + public DiscordSocketClient DiscordClient { get; } + /// - /// All loaded services in an iterable form. + /// Gets all loaded services in an iterable form. /// - internal IReadOnlyCollection Services => _services; + internal IReadOnlyCollection Services { get; } + /// - /// All loaded modules in an iterable form. + /// Gets all loaded modules in an iterable form. /// - internal IReadOnlyCollection Modules => _modules; + internal IReadOnlyCollection 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 /// internal async Task GetOpenNpgsqlConnectionAsync(ulong? guild) { - string cs = _icfg.PostgresConnString; + string cs = Config.PostgresConnString; if (guild.HasValue) cs += ";searchpath=guild_" + guild.Value; var db = new NpgsqlConnection(cs); diff --git a/Kerobot/ModuleBase.cs b/Kerobot/ModuleBase.cs index 21febef..d879768 100644 --- a/Kerobot/ModuleBase.cs +++ b/Kerobot/ModuleBase.cs @@ -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. /// /// - /// Implementing classes should not rely on local/instance variables to store data. Make use of - /// and . + /// Implementing classes should not rely on local variables to store runtime data regarding guilds. + /// Use and . /// public abstract class ModuleBase { - private readonly Kerobot _kb; - /// /// Retrieves the Kerobot instance. /// - public Kerobot Kerobot => _kb; + public Kerobot Kerobot { get; } + + /// + /// Retrieves the Discord client instance. + /// + public DiscordSocketClient DiscordClient { get => Kerobot.DiscordClient; } /// /// 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. /// - public ModuleBase(Kerobot kb) => _kb = kb; + public ModuleBase(Kerobot kb) => Kerobot = kb; /// /// Gets the module name. @@ -53,6 +57,20 @@ namespace Kerobot /// Thrown if the stored state object cannot be cast as specified. /// protected T GetGuildState(ulong guildId) => Kerobot.GetGuildState(guildId, GetType()); + + /// + /// Appends a message to the global instance log. Use sparingly. + /// + /// + /// Specifies if the log message should be sent to the reporting channel. + /// Only messages of very high importance should use this option. + /// + protected Task LogAsync(string message, bool report = false) => Kerobot.InstanceLogAsync(report, Name, message); + + /// + /// Appends a message to the log for the specified guild. + /// + protected Task LogAsync(ulong guild, string message) => Kerobot.GuildLogAsync(guild, Name, message); } /// diff --git a/Kerobot/Program.cs b/Kerobot/Program.cs index 251d0ad..97ccc3f 100644 --- a/Kerobot/Program.cs +++ b/Kerobot/Program.cs @@ -10,12 +10,11 @@ namespace Kerobot /// class Program { - static DateTimeOffset _startTime; /// /// Timestamp specifying the date and time that the program began running. /// - public static DateTimeOffset StartTime => _startTime; - + public static DateTimeOffset StartTime { get; private set; } + static Kerobot _main; /// @@ -23,8 +22,8 @@ namespace Kerobot /// 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); } diff --git a/Kerobot/Services/GuildState/StateInfo.cs b/Kerobot/Services/GuildState/StateInfo.cs index 5307f5e..e348b8a 100644 --- a/Kerobot/Services/GuildState/StateInfo.cs +++ b/Kerobot/Services/GuildState/StateInfo.cs @@ -9,41 +9,41 @@ namespace Kerobot.Services.GuildState class StateInfo : IDisposable { static readonly TimeSpan TimeUntilStale = new TimeSpan(0, 15, 0); - - private readonly object _data; + /// /// Module-provided data. /// - public object Data => _data; + public object Data { get; } /// /// 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. /// 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(); } } /// - /// 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. /// 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; } } diff --git a/Kerobot/Services/Logging/LoggingService.cs b/Kerobot/Services/Logging/LoggingService.cs index 9a8c448..e68d31b 100644 --- a/Kerobot/Services/Logging/LoggingService.cs +++ b/Kerobot/Services/Logging/LoggingService.cs @@ -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."); }; } /// diff --git a/Kerobot/Services/Service.cs b/Kerobot/Services/Service.cs index 23ba25a..8dce988 100644 --- a/Kerobot/Services/Service.cs +++ b/Kerobot/Services/Service.cs @@ -3,7 +3,7 @@ namespace Kerobot.Services { /// - /// Base class for Kerobot service. + /// Base class for Kerobot services. /// /// /// Services provide the core functionality of this program. Modules are expected to call into methods @@ -11,18 +11,15 @@ namespace Kerobot.Services /// 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; /// - /// 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. /// /// An opened database connection with the appropriate schema option set. /// If overriding, calling the base method is not necessary.