diff --git a/Feature/AutoRespond/AutoRespond.cs b/Feature/AutoRespond/AutoRespond.cs index 918e620..b550ea1 100644 --- a/Feature/AutoRespond/AutoRespond.cs +++ b/Feature/AutoRespond/AutoRespond.cs @@ -1,9 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using Discord.WebSocket; +using Discord.WebSocket; using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using System.Threading.Tasks; namespace Noikoio.RegexBot.Feature.AutoRespond { @@ -14,10 +12,9 @@ namespace Noikoio.RegexBot.Feature.AutoRespond /// The major differences between this and include: /// /// Does not listen for message edits. - /// Moderators are not exempt from any defined triggers by default. - /// Responses are limited to only two types, and only one is allowed per rule. - /// Does not support fine-grained matching options. - /// Support for rate limiting. + /// Moderators are not exempt from any defined triggers. + /// Responses are limited to the invoking channel. + /// Per-channel rate limiting. /// /// /// @@ -47,7 +44,14 @@ namespace Noikoio.RegexBot.Feature.AutoRespond [ConfigSection("autoresponses")] public override Task ProcessConfiguration(JToken configSection) { - throw new NotImplementedException(); + var responses = new List(); + foreach (JObject def in configSection) + { + // Everything is left to the constructor + responses.Add(new ResponseDefinition(def)); + } + + return Task.FromResult(responses.AsReadOnly()); } } } diff --git a/Feature/AutoRespond/AutoRespond_Process.cs b/Feature/AutoRespond/AutoRespond_Process.cs index 0cd5418..3e773de 100644 --- a/Feature/AutoRespond/AutoRespond_Process.cs +++ b/Feature/AutoRespond/AutoRespond_Process.cs @@ -1,7 +1,5 @@ using Discord.WebSocket; -using System; -using System.Collections.Generic; -using System.Text; +using System.Diagnostics; using System.Threading.Tasks; namespace Noikoio.RegexBot.Feature.AutoRespond @@ -10,10 +8,53 @@ namespace Noikoio.RegexBot.Feature.AutoRespond { private async Task ProcessMessage(SocketMessage msg, ResponseDefinition def) { - // Filtering checks + // Checks before executing + if (def.Filter.IsFiltered(msg)) return; + if (!def.RateLimit.AddUsage(msg.Channel.Id)) return; - // Rate limit checks + await Log($"'{def.Label}' triggered in #{msg.Channel.Name} by {msg.Author}"); + var (type, text) = def.Response; + if (type == ResponseDefinition.ResponseType.Reply) await ProcessReply(msg, text); + else if (type == ResponseDefinition.ResponseType.Exec) await ProcessExec(msg, text); + } + + private async Task ProcessReply(SocketMessage msg, string text) + { + await msg.Channel.SendMessageAsync(text); + } + + private async Task ProcessExec(SocketMessage msg, string text) + { + string[] cmdline = text.Split(new char[] { ' ' }, 2); + + ProcessStartInfo ps = new ProcessStartInfo() + { + FileName = cmdline[0], + Arguments = (cmdline.Length == 2 ? cmdline[1] : ""), + UseShellExecute = false, // ??? + CreateNoWindow = true, + RedirectStandardOutput = true + }; + using (Process p = Process.Start(ps)) + { + p.WaitForExit(5000); // waiting at most 5 seconds + if (p.HasExited) + { + if (p.ExitCode != 0) await Log("exec: Process returned exit code " + p.ExitCode); + using (var stdout = p.StandardOutput) + { + var result = await stdout.ReadToEndAsync(); + await msg.Channel.SendMessageAsync(result); + } + } + else + { + await Log("exec: Process is taking too long to exit. Killing process."); + p.Kill(); + return; + } + } } } } diff --git a/Feature/AutoRespond/RateLimitCache.cs b/Feature/AutoRespond/RateLimitCache.cs index 19e3514..cc3e4f2 100644 --- a/Feature/AutoRespond/RateLimitCache.cs +++ b/Feature/AutoRespond/RateLimitCache.cs @@ -8,7 +8,7 @@ namespace Noikoio.RegexBot.Feature.AutoRespond /// class RateLimitCache { - public const ushort DefaultTimeout = 30; // TODO make configurable + public const ushort DefaultTimeout = 20; // this is Skeeter's fault private readonly ushort _timeout; private Dictionary _cache; @@ -32,6 +32,8 @@ namespace Noikoio.RegexBot.Feature.AutoRespond /// True on success. False if the given ID already exists. public bool AddUsage(ulong id) { + if (_timeout == 0) return true; + Clean(); if (_cache.ContainsKey(id)) return false; _cache.Add(id, DateTime.Now);