From 514a83523393a0be8ad312ca1139e93f5e29c59b Mon Sep 17 00:00:00 2001 From: Daniel-I-Am Date: Sat, 26 Sep 2020 18:27:24 +0200 Subject: [PATCH 1/7] Store my changes --- ChaosBot/Discord/Services/CommandHandler.cs | 2 +- .../IProgrammingLanguageInterpreter.cs | 5 +++- .../LuaProgrammingLanguageInterpreter.cs | 29 ++++++++++++------- .../ProgrammingLanguageInterpreterFacade.cs | 22 +++++++++++++- 4 files changed, 44 insertions(+), 14 deletions(-) diff --git a/ChaosBot/Discord/Services/CommandHandler.cs b/ChaosBot/Discord/Services/CommandHandler.cs index 4ed20bd..1c370ea 100644 --- a/ChaosBot/Discord/Services/CommandHandler.cs +++ b/ChaosBot/Discord/Services/CommandHandler.cs @@ -192,7 +192,7 @@ namespace ChaosBot.Discord.Services { if (!command.IsSpecified) { - LoggingFacade.Error($"Command execution failed [{context.User.Username}#{context.User.Discriminator} -> {command.Value.Name}]"); + LoggingFacade.Error($"Command execution failed [{context.User.Username}#{context.User.Discriminator} -> {context.Message.Content.Split(" ").First()}]"); return; } diff --git a/ChaosBot/Services/ProgrammingLanguageInterpreter/IProgrammingLanguageInterpreter.cs b/ChaosBot/Services/ProgrammingLanguageInterpreter/IProgrammingLanguageInterpreter.cs index a9828fd..f43d14c 100644 --- a/ChaosBot/Services/ProgrammingLanguageInterpreter/IProgrammingLanguageInterpreter.cs +++ b/ChaosBot/Services/ProgrammingLanguageInterpreter/IProgrammingLanguageInterpreter.cs @@ -1,7 +1,10 @@ +using Discord.Commands; + namespace ChaosBot.Services.ProgrammingLanguageInterpreter { public interface IProgrammingLanguageInterpreter { - string Interpret(string content, string command); + string Interpret(SocketCommandContext context, string content, string command); + void StopExecution(); } } diff --git a/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs b/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs index 8da885d..c87c056 100644 --- a/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs +++ b/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs @@ -1,6 +1,8 @@ using System; +using System.Linq; using System.Text; using System.Text.RegularExpressions; +using Discord.Commands; using Neo.IronLua; using NLog; @@ -10,37 +12,42 @@ namespace ChaosBot.Services.ProgrammingLanguageInterpreter { private static readonly ILogger Logger = Program.GetLogger(); private readonly StringBuilder _outputBuilder = new StringBuilder(); + private Lua _lua; - public string Interpret(string content, string command) + public string Interpret(SocketCommandContext context, string content, string command) { - using (Lua lua = new Lua()) + using (_lua = new Lua()) { // This needs to be dynamic if we want to call // functions from within the lua environment // This is a runtime type check - dynamic env = lua.CreateEnvironment(); + dynamic env = _lua.CreateEnvironment(); ParamsDelegate printDel = Print; env.print = printDel; - env.dochunk(content, $"{command}.lua"); + env.context = context; + + string code = content; + + env.dochunk(code, $"{command}.lua"); return _outputBuilder.ToString(); } } + public void StopExecution() + { + _lua.Dispose(); + } + private delegate void ParamsDelegate(params object[] parameters); private void Print(params object[] parameters) { - StringBuilder sb = new StringBuilder(); + string str = string.Join(" ", from param in parameters select param == null ? string.Empty : param.ToString()); - foreach (object parameter in parameters) - { - sb.Append(parameter); - } - - _outputBuilder.Append(sb); + _outputBuilder.Append(str); } } } diff --git a/ChaosBot/Services/ProgrammingLanguageInterpreter/ProgrammingLanguageInterpreterFacade.cs b/ChaosBot/Services/ProgrammingLanguageInterpreter/ProgrammingLanguageInterpreterFacade.cs index e48d28a..94ede93 100644 --- a/ChaosBot/Services/ProgrammingLanguageInterpreter/ProgrammingLanguageInterpreterFacade.cs +++ b/ChaosBot/Services/ProgrammingLanguageInterpreter/ProgrammingLanguageInterpreterFacade.cs @@ -1,4 +1,6 @@ using System; +using System.Threading; +using System.Threading.Tasks; using ChaosBot.Models; using Discord.Commands; @@ -19,7 +21,25 @@ namespace ChaosBot.Services.ProgrammingLanguageInterpreter try { - string output = interpreter.Interpret(customCommand.Content, customCommand.Command); + Thread taskThread = null; + Task task = Task.Run(() => + { + taskThread = Thread.CurrentThread; + Thread.CurrentThread.Priority = ThreadPriority.Lowest; + return interpreter.Interpret(context, customCommand.Content, customCommand.Command); + }); + + const int timeout = 1000; + bool isTaskCompleted = task.Wait(TimeSpan.FromMilliseconds(timeout)); + + if (!isTaskCompleted) + { + interpreter.StopExecution(); + taskThread.Abort(); + throw new TimeoutException($"Command '{customCommand.Command}' took more than {timeout}ms to run. It has been killed."); + } + + string output = task.Result; if (output.Length > 0) context.Channel.SendMessageAsync(output); From 29813a599c2cbb1a7b393f708e28be0dac649516 Mon Sep 17 00:00:00 2001 From: Daniel-I-Am Date: Sun, 27 Sep 2020 12:13:20 +0200 Subject: [PATCH 2/7] Switch lua library to NLua and implement cancellation --- ChaosBot/ChaosBot.csproj | 2 +- .../Discord/Services/CustomCommandHandler.cs | 1 - .../IProgrammingLanguageInterpreter.cs | 4 +- .../LuaProgrammingLanguageInterpreter.cs | 68 ++++++++++--------- .../ProgrammingLanguageInterpreterFacade.cs | 13 ++-- 5 files changed, 46 insertions(+), 42 deletions(-) diff --git a/ChaosBot/ChaosBot.csproj b/ChaosBot/ChaosBot.csproj index c1612dd..82c074e 100644 --- a/ChaosBot/ChaosBot.csproj +++ b/ChaosBot/ChaosBot.csproj @@ -16,9 +16,9 @@ - + diff --git a/ChaosBot/Discord/Services/CustomCommandHandler.cs b/ChaosBot/Discord/Services/CustomCommandHandler.cs index b1e3bd3..da64597 100644 --- a/ChaosBot/Discord/Services/CustomCommandHandler.cs +++ b/ChaosBot/Discord/Services/CustomCommandHandler.cs @@ -6,7 +6,6 @@ using ChaosBot.Services; using ChaosBot.Services.ProgrammingLanguageInterpreter; using Discord; using Discord.Commands; -using Neo.IronLua; namespace ChaosBot.Discord.Services { diff --git a/ChaosBot/Services/ProgrammingLanguageInterpreter/IProgrammingLanguageInterpreter.cs b/ChaosBot/Services/ProgrammingLanguageInterpreter/IProgrammingLanguageInterpreter.cs index f43d14c..ac8f6ff 100644 --- a/ChaosBot/Services/ProgrammingLanguageInterpreter/IProgrammingLanguageInterpreter.cs +++ b/ChaosBot/Services/ProgrammingLanguageInterpreter/IProgrammingLanguageInterpreter.cs @@ -1,10 +1,10 @@ +using System.Threading; using Discord.Commands; namespace ChaosBot.Services.ProgrammingLanguageInterpreter { public interface IProgrammingLanguageInterpreter { - string Interpret(SocketCommandContext context, string content, string command); - void StopExecution(); + string Interpret(CancellationToken ct, SocketCommandContext context, string content, string command); } } diff --git a/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs b/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs index c87c056..bfdcb0a 100644 --- a/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs +++ b/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs @@ -1,48 +1,54 @@ -using System; using System.Linq; +using System.Reflection; using System.Text; -using System.Text.RegularExpressions; +using System.Threading; using Discord.Commands; -using Neo.IronLua; -using NLog; +using NLua; namespace ChaosBot.Services.ProgrammingLanguageInterpreter { internal class LuaProgrammingLanguageInterpreter : IProgrammingLanguageInterpreter { - private static readonly ILogger Logger = Program.GetLogger(); private readonly StringBuilder _outputBuilder = new StringBuilder(); - private Lua _lua; - - public string Interpret(SocketCommandContext context, string content, string command) + private CancellationToken _ct; + private Lua _state; + + public string Interpret(CancellationToken ct, SocketCommandContext context, string content, string command) { - using (_lua = new Lua()) + LoggingFacade.Debug($"Interpreting code for {command} using Lua"); + LoggingFacade.Trace($"Using CancellationToken: {ct}"); + _ct = ct; + LoggingFacade.Trace("Starting Lua interpreter"); + _state = new Lua(); + LoggingFacade.Trace("Setting Lua debug hook"); + _state.SetDebugHook(KeraLua.LuaHookMask.Line, 0); + _state.DebugHook += LuaDebugHook; + + MethodInfo printMethod = GetType().GetMethod("Print", BindingFlags.NonPublic | BindingFlags.Instance); + LoggingFacade.Info($"Overwriting print function with {printMethod}"); + _state["print"] = printMethod; + + LoggingFacade.Trace("Disabling Lua `import` function"); + _state.DoString ("\nimport = function () end\n"); + + LoggingFacade.Trace("Running user Lua code"); + _state.DoString(content, command); + + LoggingFacade.Trace("Returning Lua output"); + return _outputBuilder.ToString(); + } + + private void LuaDebugHook(object sender, NLua.Event.DebugHookEventArgs e) + { + if (_ct.IsCancellationRequested) { - // This needs to be dynamic if we want to call - // functions from within the lua environment - // This is a runtime type check - dynamic env = _lua.CreateEnvironment(); - - ParamsDelegate printDel = Print; - env.print = printDel; - - env.context = context; - - string code = content; - - env.dochunk(code, $"{command}.lua"); - - return _outputBuilder.ToString(); + Lua l = (Lua) sender; + l.State.Error("Timeout kill"); } } - public void StopExecution() - { - _lua.Dispose(); - } - - private delegate void ParamsDelegate(params object[] parameters); - + // This is being used in the `Interpret` function to register the print function + // ReSharper disable once UnusedMember.Local private void Print(params object[] parameters) { string str = string.Join(" ", from param in parameters select param == null ? string.Empty : param.ToString()); diff --git a/ChaosBot/Services/ProgrammingLanguageInterpreter/ProgrammingLanguageInterpreterFacade.cs b/ChaosBot/Services/ProgrammingLanguageInterpreter/ProgrammingLanguageInterpreterFacade.cs index 94ede93..59f85ce 100644 --- a/ChaosBot/Services/ProgrammingLanguageInterpreter/ProgrammingLanguageInterpreterFacade.cs +++ b/ChaosBot/Services/ProgrammingLanguageInterpreter/ProgrammingLanguageInterpreterFacade.cs @@ -21,21 +21,19 @@ namespace ChaosBot.Services.ProgrammingLanguageInterpreter try { - Thread taskThread = null; + CancellationTokenSource tokenSource = new CancellationTokenSource(); Task task = Task.Run(() => { - taskThread = Thread.CurrentThread; Thread.CurrentThread.Priority = ThreadPriority.Lowest; - return interpreter.Interpret(context, customCommand.Content, customCommand.Command); - }); + return interpreter.Interpret(tokenSource.Token, context, customCommand.Content, customCommand.Command); + }, tokenSource.Token); - const int timeout = 1000; + const int timeout = 250; bool isTaskCompleted = task.Wait(TimeSpan.FromMilliseconds(timeout)); if (!isTaskCompleted) { - interpreter.StopExecution(); - taskThread.Abort(); + tokenSource.Cancel(); throw new TimeoutException($"Command '{customCommand.Command}' took more than {timeout}ms to run. It has been killed."); } @@ -49,6 +47,7 @@ namespace ChaosBot.Services.ProgrammingLanguageInterpreter } catch (Exception ex) { + LoggingFacade.Exception(ex); errorReason = $"There was an error with your code ({ex.GetType().Name}): {ex.Message}"; return false; } From a1a3cc61cbfc1486132c7b652cd408894edb7ef7 Mon Sep 17 00:00:00 2001 From: Daniel-I-Am Date: Sun, 27 Sep 2020 12:27:17 +0200 Subject: [PATCH 3/7] Fix print not working --- .../LuaProgrammingLanguageInterpreter.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs b/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs index bfdcb0a..2322e9b 100644 --- a/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs +++ b/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs @@ -26,7 +26,7 @@ namespace ChaosBot.Services.ProgrammingLanguageInterpreter MethodInfo printMethod = GetType().GetMethod("Print", BindingFlags.NonPublic | BindingFlags.Instance); LoggingFacade.Info($"Overwriting print function with {printMethod}"); - _state["print"] = printMethod; + _state["print"] = _state.RegisterFunction("print", this, printMethod); LoggingFacade.Trace("Disabling Lua `import` function"); _state.DoString ("\nimport = function () end\n"); @@ -51,9 +51,9 @@ namespace ChaosBot.Services.ProgrammingLanguageInterpreter // ReSharper disable once UnusedMember.Local private void Print(params object[] parameters) { - string str = string.Join(" ", from param in parameters select param == null ? string.Empty : param.ToString()); + string str = string.Join("\t", from param in parameters select param == null ? string.Empty : param.ToString()); - _outputBuilder.Append(str); + _outputBuilder.AppendLine(str); } } } From 118aa28a11ea72e7a866e5d633819a242ff1801b Mon Sep 17 00:00:00 2001 From: Daniel-I-Am Date: Sun, 27 Sep 2020 12:32:05 +0200 Subject: [PATCH 4/7] Fix logging level of debug message --- .../LuaProgrammingLanguageInterpreter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs b/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs index 2322e9b..0176800 100644 --- a/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs +++ b/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs @@ -25,7 +25,7 @@ namespace ChaosBot.Services.ProgrammingLanguageInterpreter _state.DebugHook += LuaDebugHook; MethodInfo printMethod = GetType().GetMethod("Print", BindingFlags.NonPublic | BindingFlags.Instance); - LoggingFacade.Info($"Overwriting print function with {printMethod}"); + LoggingFacade.Trace($"Overwriting print function with {printMethod}"); _state["print"] = _state.RegisterFunction("print", this, printMethod); LoggingFacade.Trace("Disabling Lua `import` function"); From 94bbfb18ae5244344fcb7b4fb81e32f3b8805287 Mon Sep 17 00:00:00 2001 From: Daniel-I-Am Date: Sun, 27 Sep 2020 12:32:42 +0200 Subject: [PATCH 5/7] Add context variable to lua context --- .../LuaProgrammingLanguageInterpreter.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs b/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs index 0176800..ee8eb78 100644 --- a/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs +++ b/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs @@ -27,6 +27,8 @@ namespace ChaosBot.Services.ProgrammingLanguageInterpreter MethodInfo printMethod = GetType().GetMethod("Print", BindingFlags.NonPublic | BindingFlags.Instance); LoggingFacade.Trace($"Overwriting print function with {printMethod}"); _state["print"] = _state.RegisterFunction("print", this, printMethod); + LoggingFacade.Trace("Adding command context to environment"); + _state["context"] = context; LoggingFacade.Trace("Disabling Lua `import` function"); _state.DoString ("\nimport = function () end\n"); From 8062e6d98009836112ec01bffa70af9f12e7e075 Mon Sep 17 00:00:00 2001 From: Daniel-I-Am Date: Sun, 27 Sep 2020 12:33:06 +0200 Subject: [PATCH 6/7] Remove metatable functionality from lua context --- .../LuaProgrammingLanguageInterpreter.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs b/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs index ee8eb78..94a0a40 100644 --- a/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs +++ b/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs @@ -32,6 +32,9 @@ namespace ChaosBot.Services.ProgrammingLanguageInterpreter LoggingFacade.Trace("Disabling Lua `import` function"); _state.DoString ("\nimport = function () end\n"); + LoggingFacade.Trace("Disabling Lua `metatable` functionality"); + _state.DoString ("\ngetmetatable = function () end\n"); + _state.DoString ("\nsetmetatable = function () end\n"); LoggingFacade.Trace("Running user Lua code"); _state.DoString(content, command); From b7a9575c15191b995c66121c35f6896f9497e5d0 Mon Sep 17 00:00:00 2001 From: Daniel-I-Am Date: Sun, 27 Sep 2020 12:33:42 +0200 Subject: [PATCH 7/7] Trim output to treat `\n` as empty message --- .../LuaProgrammingLanguageInterpreter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs b/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs index 94a0a40..bd55f54 100644 --- a/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs +++ b/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs @@ -40,7 +40,7 @@ namespace ChaosBot.Services.ProgrammingLanguageInterpreter _state.DoString(content, command); LoggingFacade.Trace("Returning Lua output"); - return _outputBuilder.ToString(); + return _outputBuilder.ToString().Trim(); } private void LuaDebugHook(object sender, NLua.Event.DebugHookEventArgs e)