diff --git a/BotFeature.cs b/BotFeature.cs
new file mode 100644
index 0000000..f410afa
--- /dev/null
+++ b/BotFeature.cs
@@ -0,0 +1,94 @@
+using Discord.WebSocket;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Noikoio.RegexBot
+{
+ ///
+ /// Base class for bot features
+ ///
+ ///
+ /// This may have use in some sort of external plugin system later.
+ ///
+ 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);
+ }
+
+ ///
+ /// Processes feature-specific configuration.
+ ///
+ ///
+ /// Feature code should not hold on to this data, but instead use 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.
+ ///
+ ///
+ /// Processed configuration data prepared for later use.
+ ///
+ ///
+ /// This method should throw RuleImportException in the event of any error.
+ /// The exception message will be properly logged.
+ ///
+ public abstract Task ProcessConfiguration(JToken configSection);
+
+ ///
+ /// Gets this feature's relevant configuration data associated with the given Discord guild.
+ ///
+ ///
+ /// The stored configuration data, or null if none exists.
+ ///
+ 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();
+ }
+
+ ///
+ /// Indicates which section under an individual Discord guild configuration should be passed to the
+ /// feature's method during configuration load.
+ ///
+ [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;
+ }
+ }
+}
diff --git a/ConfigItem/EntityName.cs b/ConfigItem/EntityName.cs
index 9b796b1..3ce6528 100644
--- a/ConfigItem/EntityName.cs
+++ b/ConfigItem/EntityName.cs
@@ -71,7 +71,7 @@ namespace Noikoio.RegexBot.ConfigItem
if (_id.HasValue) return;
_id = id;
- var log = Logger.GetLogger(ConfigLoader.LogPrefix);
+ var log = Logger.GetLogger(Configuration.LogPrefix);
var thisstr = this.ToString();
log(String.Format(
"Suggestion: \"{0}\" may be written in configuration as \"{1}\"",
diff --git a/ConfigItem/Server.cs b/ConfigItem/ServerConfig.cs
similarity index 57%
rename from ConfigItem/Server.cs
rename to ConfigItem/ServerConfig.cs
index 428a612..d1b52bb 100644
--- a/ConfigItem/Server.cs
+++ b/ConfigItem/ServerConfig.cs
@@ -1,5 +1,5 @@
-using Noikoio.RegexBot.Feature.RegexResponder;
-using System.Collections.Generic;
+using System;
+using System.Collections.ObjectModel;
using System.Diagnostics;
namespace Noikoio.RegexBot.ConfigItem
@@ -7,27 +7,27 @@ namespace Noikoio.RegexBot.ConfigItem
///
/// Represents known information about a Discord guild (server) and other associated data
///
- class Server
+ class ServerConfig
{
private readonly string _name;
private ulong? _id;
- private IEnumerable _rules;
private EntityList _moderators;
+ private ReadOnlyDictionary _featureData;
public string Name => _name;
public ulong? Id {
get => _id; set { if (!_id.HasValue) _id = value; }
}
- public IEnumerable MatchResponseRules => _rules;
public EntityList Moderators => _moderators;
+ public ReadOnlyDictionary FeatureConfigs => _featureData;
- public Server(string name, ulong? id, IEnumerable rules, EntityList moderators)
+ public ServerConfig(string name, ulong? id, EntityList moderators, ReadOnlyDictionary featureconf)
{
_name = name;
_id = id;
- _rules = rules;
_moderators = moderators;
- Debug.Assert(_name != null && _rules != null && _moderators != null);
+ _featureData = featureconf;
+ Debug.Assert(_name != null && _moderators != null);
}
}
}
diff --git a/ConfigLoader.cs b/Configuration.cs
similarity index 71%
rename from ConfigLoader.cs
rename to Configuration.cs
index f9b286d..dc739f4 100644
--- a/ConfigLoader.cs
+++ b/Configuration.cs
@@ -1,9 +1,9 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Noikoio.RegexBot.ConfigItem;
-using Noikoio.RegexBot.Feature.RegexResponder;
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
@@ -14,22 +14,25 @@ namespace Noikoio.RegexBot
///
/// Configuration loader
///
- class ConfigLoader
+ class Configuration
{
public const string LogPrefix = "Config";
+ private readonly RegexBot _bot;
private readonly string _configPath;
- private Server[] _servers;
+ private ServerConfig[] _servers;
+ // The following values do not change on reload:
private string _botToken;
private string _currentGame;
public string BotUserToken => _botToken;
public string CurrentGame => _currentGame;
- public Server[] Servers => _servers;
+ public ServerConfig[] Servers => _servers;
- public ConfigLoader()
+ public Configuration(RegexBot bot)
{
+ _bot = bot;
var dsc = Path.DirectorySeparatorChar;
_configPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)
+ dsc + "settings.json";
@@ -64,7 +67,7 @@ namespace Noikoio.RegexBot
}
///
- /// Called only on bot startup. Returns false on failure.
+ /// Loads essential, unchanging values needed for bot startup. Returns false on failure.
///
public bool LoadInitialConfig()
{
@@ -81,7 +84,7 @@ namespace Noikoio.RegexBot
}
_currentGame = conf["playing"]?.Value();
- return ProcessServerConfig(conf).GetAwaiter().GetResult();
+ return true;
}
///
@@ -90,15 +93,10 @@ namespace Noikoio.RegexBot
/// False on failure. Specific reasons will have been sent to log.
public async Task ReloadServerConfig()
{
- await Logger.GetLogger(LogPrefix)("Configuration reload currently not supported.");
- return false;
- // TODO actually implement this
- var lt = LoadFile();
- lt.Wait();
- JObject conf = lt.Result;
- if (conf == null) return false;
+ var config = await LoadFile();
+ if (config == null) return false;
- return await ProcessServerConfig(conf);
+ return await ProcessServerConfig(config);
}
///
@@ -114,7 +112,7 @@ namespace Noikoio.RegexBot
return false;
}
- List newservers = new List();
+ List newservers = new List();
await Log("Reading server configurations...");
foreach (JObject sconf in conf["servers"].Children())
{
@@ -145,38 +143,44 @@ namespace Noikoio.RegexBot
// Load server moderator list
EntityList mods = new EntityList(sconf["moderators"]);
if (sconf["moderators"] != null) await SLog("Moderator " + mods.ToString());
-
- // Read rules
- // Also, parsed rules require a server reference. Creating it here.
- List rules = new List();
- Server newserver = new Server(sname, sid, rules, mods);
-
- foreach (JObject ruleconf in sconf["rules"])
+
+ // Load feature configurations
+ Dictionary customConfs = new Dictionary();
+ foreach (var item in _bot.Features)
{
- // Try and get at least the name before passing it to RuleItem
- string name = ruleconf["name"]?.Value();
- if (name == null)
+ var attr = item.GetType().GetTypeInfo()
+ .GetMethod("ProcessConfiguration").GetCustomAttribute();
+ if (attr == null)
{
- await SLog("Display name not defined within a rule section.");
- return false;
+ await SLog("No additional configuration for " + item.Name);
+ continue;
+ }
+ var section = sconf[attr.SectionName];
+ if (section == null)
+ {
+ await SLog("Additional configuration not defined for " + item.Name);
+ continue;
}
- await SLog($"Adding rule \"{name}\"");
- RuleConfig rule;
+ await SLog("Loading additional configuration for " + item.Name);
+ object result;
try
{
- rule = new RuleConfig(newserver, ruleconf);
- } catch (RuleImportException ex)
+ result = await item.ProcessConfiguration(section);
+ }
+ catch (RuleImportException ex)
{
- await SLog("-> Error: " + ex.Message);
+ await SLog($"{item.Name} failed to load configuration: " + ex.Message);
return false;
}
- rules.Add(rule);
+
+ customConfs.Add(item, result);
}
+
// Switch to using new data
List> rulesfinal = new List>();
- newservers.Add(newserver);
+ newservers.Add(new ServerConfig(sname, sid, mods, new ReadOnlyDictionary(customConfs)));
}
_servers = newservers.ToArray();
diff --git a/Feature/RegexResponder/EventProcessor.cs b/Feature/RegexResponder/EventProcessor.cs
index b8c546d..2d4d8c0 100644
--- a/Feature/RegexResponder/EventProcessor.cs
+++ b/Feature/RegexResponder/EventProcessor.cs
@@ -7,6 +7,7 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using Newtonsoft.Json.Linq;
namespace Noikoio.RegexBot.Feature.RegexResponder
{
@@ -14,15 +15,15 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
/// Implements per-message regex matching and executes customizable responses.
/// Namesake of this project.
///
- partial class EventProcessor
+ partial class EventProcessor : BotFeature
{
private readonly DiscordSocketClient _client;
- private readonly ConfigLoader _conf;
- public EventProcessor(DiscordSocketClient client, ConfigLoader conf)
+ public override string Name => "RegexResponder";
+
+ public EventProcessor(DiscordSocketClient client) : base(client)
{
_client = client;
- _conf = conf;
_client.MessageReceived += OnMessageReceived;
_client.MessageUpdated += OnMessageUpdated;
@@ -58,12 +59,16 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
///
private async Task ReceiveMessage(SocketMessage arg)
{
- if (arg.Author == _client.CurrentUser) return;
+ // Determine channel type - if not a guild channel, stop.
+ var ch = arg.Channel as SocketGuildChannel;
+ if (ch == null) return;
+
+ if (arg.Author == _client.CurrentUser) return; // Don't ever self-trigger
// Looking up server information and extracting settings
- SocketGuild g = ((SocketGuildUser)arg.Author).Guild;
- Server sd = null;
- foreach (var item in _conf.Servers)
+ SocketGuild g = ch.Guild;
+ ServerConfig sd = null;
+ foreach (var item in RegexBot.Config.Servers)
{
if (item.Id.HasValue)
{
@@ -81,7 +86,7 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
{
item.Id = g.Id;
sd = item;
- await Logger.GetLogger(ConfigLoader.LogPrefix)
+ await Logger.GetLogger(Configuration.LogPrefix)
($"Suggestion: Server \"{item.Name}\" can be defined as \"{item.Id}::{item.Name}\"");
break;
}
@@ -89,9 +94,11 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
}
if (sd == null) return; // No server configuration found
+ var rules = GetConfig(ch.Guild.Id) as IEnumerable;
+ if (rules == null) return;
// Further processing is sent to the thread pool
- foreach (var rule in sd.MatchResponseRules)
+ foreach (var rule in rules)
await Task.Run(async () => await ProcessMessage(sd, rule, arg));
}
@@ -99,7 +106,7 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
/// Uses information from a single rule and checks if the incoming message is a match.
/// If it matches, the rule's responses are executed. To be run in the thread pool.
///
- private async Task ProcessMessage(Server srv, RuleConfig rule, SocketMessage msg)
+ private async Task ProcessMessage(ServerConfig srv, RuleConfig rule, SocketMessage msg)
{
string msgcontent;
@@ -134,8 +141,7 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
if (!success) return;
// Prepare to execute responses
- var log = Logger.GetLogger(rule.DisplayName);
- await log($"Triggered in {srv.Name}/#{msg.Channel} by {msg.Author.ToString()}");
+ await Log($"\"{rule.DisplayName}\" triggered in {srv.Name}/#{msg.Channel} by {msg.Author.ToString()}");
foreach (string rcmd in rule.Responses)
{
@@ -145,19 +151,52 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
ResponseProcessor response;
if (!_commands.TryGetValue(cmd, out response))
{
- await log($"Unknown command \"{cmd}\"");
+ await Log($"Unknown command defined in response: \"{cmd}\"");
continue;
}
- await response.Invoke(log, rcmd, rule, msg);
+ await response.Invoke(rcmd, rule, msg);
}
catch (Exception ex)
{
- await log($"Encountered an error while processing \"{cmd}\"");
- await log(ex.ToString());
+ await Log($"Encountered an error while processing \"{cmd}\". Details follow:");
+ await Log(ex.ToString());
}
}
}
+ [ConfigSection("rules")]
+ public override async Task ProcessConfiguration(JToken configSection)
+ {
+ List rules = new List();
+ foreach (JObject ruleconf in configSection)
+ {
+ // Try and get at least the name before passing it to RuleItem
+ string name = ruleconf["name"]?.Value();
+ if (name == null)
+ {
+ await Log("Display name not defined within a rule section.");
+ return false;
+ }
+ await Log($"Adding rule \"{name}\"");
+
+ RuleConfig rule;
+ try
+ {
+ rule = new RuleConfig(ruleconf);
+ }
+ catch (RuleImportException ex)
+ {
+ await Log("-> Error: " + ex.Message);
+ return false;
+ }
+ rules.Add(rule);
+ }
+
+ return rules.AsReadOnly();
+ }
+
+ // -------------------------------------
+
///
/// Turns an embed into a single string for regex matching purposes
///
diff --git a/Feature/RegexResponder/Responses.cs b/Feature/RegexResponder/Responses.cs
index 8710702..ecd4b3e 100644
--- a/Feature/RegexResponder/Responses.cs
+++ b/Feature/RegexResponder/Responses.cs
@@ -11,7 +11,7 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
// Contains code for handling each response in a rule.
partial class EventProcessor
{
- private delegate Task ResponseProcessor(AsyncLogger l, string cmd, RuleConfig r, SocketMessage m);
+ private delegate Task ResponseProcessor(string cmd, RuleConfig r, SocketMessage m);
private readonly ReadOnlyDictionary _commands;
#if DEBUG
@@ -19,9 +19,8 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
/// Throws an exception. Meant to be a quick error handling test.
/// No parameters.
///
- private async Task RP_Crash(AsyncLogger l, string cmd, RuleConfig r, SocketMessage m)
+ private Task RP_Crash(string cmd, RuleConfig r, SocketMessage m)
{
- await l("Will throw an exception.");
throw new Exception("Requested in response.");
}
@@ -30,7 +29,7 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
/// The guild info displayed is the one in which the command is invoked.
/// No parameters.
///
- private Task RP_DumpID(AsyncLogger l, string cmd, RuleConfig r, SocketMessage m)
+ private Task RP_DumpID(string cmd, RuleConfig r, SocketMessage m)
{
var g = ((SocketGuildUser)m.Author).Guild;
var result = new StringBuilder();
@@ -55,19 +54,19 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
/// Sends a message to a specified channel.
/// Parameters: say (channel) (message)
///
- private async Task RP_Say(AsyncLogger l, string cmd, RuleConfig r, SocketMessage m)
+ private async Task RP_Say(string cmd, RuleConfig r, SocketMessage m)
{
string[] @in = SplitParams(cmd, 3);
if (@in.Length != 3)
{
- await l("Error: say: Incorrect number of parameters.");
+ await Log("Error: say: Incorrect number of parameters.");
return;
}
var target = await GetMessageTargetAsync(@in[1], m);
if (target == null)
{
- await l("Error: say: Unable to resolve given target.");
+ await Log("Error: say: Unable to resolve given target.");
return;
}
@@ -80,19 +79,19 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
/// Reports the incoming message to a given channel.
/// Parameters: report (channel)
///
- private async Task RP_Report(AsyncLogger l, string cmd, RuleConfig r, SocketMessage m)
+ private async Task RP_Report(string cmd, RuleConfig r, SocketMessage m)
{
string[] @in = SplitParams(cmd);
if (@in.Length != 2)
{
- await l("Error: report: Incorrect number of parameters.");
+ await Log("Error: report: Incorrect number of parameters.");
return;
}
var target = await GetMessageTargetAsync(@in[1], m);
if (target == null)
{
- await l("Error: report: Unable to resolve given target.");
+ await Log("Error: report: Unable to resolve given target.");
return;
}
@@ -136,7 +135,7 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
/// Deletes the incoming message.
/// No parameters.
///
- private async Task RP_Remove(AsyncLogger l, string cmd, RuleConfig r, SocketMessage m)
+ private async Task RP_Remove(string cmd, RuleConfig r, SocketMessage m)
{
// Parameters are not checked
await m.DeleteAsync();
@@ -146,19 +145,19 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
/// Executes an external program and sends standard output to the given channel.
/// Parameters: exec (channel) (command line)
///
- private async Task RP_Exec(AsyncLogger l, string cmd, RuleConfig r, SocketMessage m)
+ private async Task RP_Exec(string cmd, RuleConfig r, SocketMessage m)
{
var @in = SplitParams(cmd, 4);
if (@in.Length < 3)
{
- await l("exec: Incorrect number of parameters.");
+ await Log("exec: Incorrect number of parameters.");
}
string result;
var target = await GetMessageTargetAsync(@in[1], m);
if (target == null)
{
- await l("Error: exec: Unable to resolve given channel.");
+ await Log("Error: exec: Unable to resolve given channel.");
return;
}
@@ -174,7 +173,7 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
p.WaitForExit(5000); // waiting at most 5 seconds
if (p.HasExited)
{
- if (p.ExitCode != 0) await l("exec: Process returned exit code " + p.ExitCode);
+ if (p.ExitCode != 0) await Log("exec: Process returned exit code " + p.ExitCode);
using (var stdout = p.StandardOutput)
{
result = await stdout.ReadToEndAsync();
@@ -182,7 +181,7 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
}
else
{
- await l("exec: Process is taking too long to exit. Killing process.");
+ await Log("exec: Process is taking too long to exit. Killing process.");
p.Kill();
return;
}
@@ -197,7 +196,7 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
/// No parameters.
///
// TODO add parameter for message auto-deleting
- private async Task RP_Ban(AsyncLogger l, string cmd, RuleConfig r, SocketMessage m)
+ private async Task RP_Ban(string cmd, RuleConfig r, SocketMessage m)
{
SocketGuild g = ((SocketGuildUser)m.Author).Guild;
await g.AddBanAsync(m.Author);
@@ -207,17 +206,17 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
/// Grants or revokes a specified role to/from a given user.
/// Parameters: grantrole/revokerole (user ID or @_) (role ID)
///
- private async Task RP_GrantRevokeRole(AsyncLogger l, string cmd, RuleConfig r, SocketMessage m)
+ private async Task RP_GrantRevokeRole(string cmd, RuleConfig r, SocketMessage m)
{
string[] @in = SplitParams(cmd);
if (@in.Length != 3)
{
- await l($"Error: {@in[0]}: incorrect number of parameters.");
+ await Log($"Error: {@in[0]}: incorrect number of parameters.");
return;
}
if (!ulong.TryParse(@in[2], out var roleID))
{
- await l($"Error: {@in[0]}: Invalid role ID specified.");
+ await Log($"Error: {@in[0]}: Invalid role ID specified.");
return;
}
@@ -226,7 +225,7 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
SocketRole rl = gu.Guild.GetRole(roleID);
if (rl == null)
{
- await l($"Error: {@in[0]}: Specified role not found.");
+ await Log($"Error: {@in[0]}: Specified role not found.");
return;
}
@@ -240,13 +239,13 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
{
if (!ulong.TryParse(@in[1], out var userID))
{
- await l($"Error: {@in[0]}: Invalid user ID specified.");
+ await Log($"Error: {@in[0]}: Invalid user ID specified.");
return;
}
target = gu.Guild.GetUser(userID);
if (target == null)
{
- await l($"Error: {@in[0]}: Given user ID does not exist in this server.");
+ await Log($"Error: {@in[0]}: Given user ID does not exist in this server.");
return;
}
}
diff --git a/Feature/RegexResponder/RuleConfig.cs b/Feature/RegexResponder/RuleConfig.cs
index b626bba..f263d18 100644
--- a/Feature/RegexResponder/RuleConfig.cs
+++ b/Feature/RegexResponder/RuleConfig.cs
@@ -13,7 +13,6 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
internal struct RuleConfig
{
private string _displayName;
- private Server _server;
private IEnumerable _regex;
private IEnumerable _responses;
private FilterType _filtermode;
@@ -25,7 +24,6 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
private bool _matchEmbeds;
public string DisplayName => _displayName;
- public Server Server => _server;
public IEnumerable Regex => _regex;
public IEnumerable Responses => _responses;
public FilterType FilterMode => _filtermode;
@@ -43,10 +41,8 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
///
/// Thrown when encountering a missing or invalid value.
///
- public RuleConfig(Server serverref, JObject ruleconf)
+ public RuleConfig(JObject ruleconf)
{
- _server = serverref;
-
// display name - validation should've been done outside this constructor already
_displayName = ruleconf["name"]?.Value();
if (_displayName == null)
diff --git a/Program.cs b/Program.cs
index ddb3a20..f9108d6 100644
--- a/Program.cs
+++ b/Program.cs
@@ -10,18 +10,7 @@ namespace Noikoio.RegexBot
{
static void Main(string[] args)
{
- // Attempt to load basic configuration before setting up the client
- var config = new ConfigLoader();
- if (!config.LoadInitialConfig())
- {
-#if DEBUG
- Console.WriteLine("Press any key to exit.");
- Console.ReadKey();
-#endif
- Environment.Exit(1);
- }
-
- RegexBot rb = new RegexBot(config);
+ RegexBot rb = new RegexBot();
Console.CancelKeyPress += rb.Console_CancelKeyPress;
//AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
diff --git a/RegexBot.cs b/RegexBot.cs
index f5df43c..ddb58ed 100644
--- a/RegexBot.cs
+++ b/RegexBot.cs
@@ -1,6 +1,7 @@
using Discord;
using Discord.WebSocket;
using System;
+using System.Collections.Generic;
using System.Threading.Tasks;
namespace Noikoio.RegexBot
@@ -10,34 +11,63 @@ namespace Noikoio.RegexBot
///
class RegexBot
{
- private readonly ConfigLoader _config;
+ private static Configuration _config;
private readonly DiscordSocketClient _client;
+ private BotFeature[] _features;
- // Constructor loads all subsystems. Subsystem constructors hook up their event delegates.
- internal RegexBot(ConfigLoader conf)
+ internal static Configuration Config => _config;
+ internal IEnumerable Features => _features;
+
+ internal RegexBot()
{
+ // Load configuration
+ _config = new Configuration(this);
+ if (!_config.LoadInitialConfig())
+ {
+#if DEBUG
+ Console.WriteLine("Press any key to exit.");
+ Console.ReadKey();
+#endif
+ Environment.Exit(1);
+ }
+
_client = new DiscordSocketClient(new DiscordSocketConfig()
{
LogLevel = LogSeverity.Info,
DefaultRetryMode = RetryMode.AlwaysRetry,
MessageCacheSize = 50
});
- _config = conf;
// Hook up handlers for basic functions
_client.Connected += _client_Connected;
// Initialize features
- new Feature.RegexResponder.EventProcessor(_client, _config);
+ _features = new BotFeature[]
+ {
+ new Feature.RegexResponder.EventProcessor(_client)
+ };
+ var dlog = Logger.GetLogger("Discord.Net");
+ _client.Log += async (arg) =>
+ await dlog(
+ String.Format("{0}: {1}{2}", arg.Source, ((int)arg.Severity < 3 ? arg.Severity + ": " : ""),
+ arg.Message));
+
+ // With features initialized, finish loading configuration
+ if (!_config.ReloadServerConfig().GetAwaiter().GetResult())
+ {
+ Console.WriteLine("Failed to load server configuration.");
+#if DEBUG
+ Console.WriteLine("Press any key to exit.");
+ Console.ReadKey();
+#endif
+ Environment.Exit(1);
+ }
}
internal async Task Start()
{
- var dlog = Logger.GetLogger("Discord");
- _client.Log += async (arg) =>
- await dlog(String.Format("{0}: {1}{2}",
- arg.Source, ((int)arg.Severity < 3 ? arg.Severity + ": " : ""), arg.Message));
- await _client.LoginAsync(TokenType.Bot, _config.BotUserToken);
+
+ await _client.LoginAsync(TokenType.Bot, Config.BotUserToken);
await _client.StartAsync();
await Task.Delay(-1);
@@ -45,7 +75,7 @@ namespace Noikoio.RegexBot
private async Task _client_Connected()
{
- await _client.SetGameAsync(_config.CurrentGame);
+ await _client.SetGameAsync(Config.CurrentGame);
// TODO add support for making use of server invites somewhere around here
}
diff --git a/RegexBot.csproj b/RegexBot.csproj
index 791a982..53dfc70 100644
--- a/RegexBot.csproj
+++ b/RegexBot.csproj
@@ -4,7 +4,7 @@
Exe
netcoreapp1.1
Noikoio.RegexBot
- 0.15.0.0
+ 1.0.0.0
Highly configurable Discord moderation bot
Noikoio