RegexBot/BotFeature.cs
Noikoio 27a25d90fc Added BotFeature base class, implemented in RegexResponder
BotFeature is a new base class that all new individual bot features
will derive from. At least one new feature is planned for this bot,
and in time it may be opened up so external assemblies can be loaded.

Full list of changes:
-Added BotFeature and ConfigSectionAttribute classes
-Renamed ConfigLoader to Configuration
-Removed RegexResponder specific configuration data
-Added per-feature configuration data storage
-LoadInitialConfig() no longer loads all configuration
-ReloadServerConfig() now loads remaining configuration, and allows
 for actual configuration reloading. Live configuration reloading
 is not an exposed feature yet.
-Can now delegate feature-specific configuration loading to feature
 classes that make use of ConfigSectionAttribute

-RegexResponder fully implements BotFeature
-Rule configuration loading moved to RegexResponder
-Logging output has changed slightly in regards to rule triggering
 and execution

-Changed configuration load behavior on startup
-Version pushed up to 1.0.0
2017-07-26 15:36:59 -07:00

94 lines
3.4 KiB
C#

using Discord.WebSocket;
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Noikoio.RegexBot
{
/// <summary>
/// Base class for bot features
/// </summary>
/// <remarks>
/// This may have use in some sort of external plugin system later.
/// </remarks>
abstract class BotFeature
{
private readonly DiscordSocketClient _client;
private readonly AsyncLogger _logger;
public abstract string Name { get; }
protected BotFeature(DiscordSocketClient client)
{
_client = client;
_logger = Logger.GetLogger(this.Name);
}
/// <summary>
/// Processes feature-specific configuration.
/// </summary>
/// <remarks>
/// Feature code <i>should not</i> hold on to this data, but instead use <see cref="GetConfig{T}"/> to retrieve
/// them. This is in the event that configuration is reverted to an earlier state and allows for the
/// bot and all features to revert to previously used configuration values with no effort on the part
/// of individual features.
/// </remarks>
/// <returns>
/// Processed configuration data prepared for later use.
/// </returns>
/// <exception cref="ConfigItem.RuleImportException">
/// This method should throw RuleImportException in the event of any error.
/// The exception message will be properly logged.
/// </exception>
public abstract Task<object> ProcessConfiguration(JToken configSection);
/// <summary>
/// Gets this feature's relevant configuration data associated with the given Discord guild.
/// </summary>
/// <returns>
/// The stored configuration data, or null if none exists.
/// </returns>
protected object GetConfig(ulong guildId)
{
var sc = RegexBot.Config.Servers.FirstOrDefault(g => g.Id == guildId);
if (sc == null)
{
throw new ArgumentException("There is no known configuration associated with the given Guild ID.");
}
if (sc.FeatureConfigs.TryGetValue(this, out var item)) return item;
else return null;
}
protected async Task Log(string text)
{
await _logger(text);
}
public sealed override bool Equals(object obj) => base.Equals(obj);
public sealed override int GetHashCode() => base.GetHashCode();
public sealed override string ToString() => base.ToString();
}
/// <summary>
/// Indicates which section under an individual Discord guild configuration should be passed to the
/// feature's <see cref="BotFeature.ProcessConfiguration(JObject)"/> method during configuration load.
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public class ConfigSectionAttribute : Attribute
{
private readonly string _sectionName;
public string SectionName => _sectionName;
public ConfigSectionAttribute(string sectionName)
{
if (string.IsNullOrWhiteSpace(sectionName))
{
throw new ArgumentNullException("Configuration section name cannot be blank.");
}
_sectionName = sectionName;
}
}
}