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