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/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/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 a9828fd..ac8f6ff 100644
--- a/ChaosBot/Services/ProgrammingLanguageInterpreter/IProgrammingLanguageInterpreter.cs
+++ b/ChaosBot/Services/ProgrammingLanguageInterpreter/IProgrammingLanguageInterpreter.cs
@@ -1,7 +1,10 @@
+using System.Threading;
+using Discord.Commands;
+
namespace ChaosBot.Services.ProgrammingLanguageInterpreter
{
public interface IProgrammingLanguageInterpreter
{
- string Interpret(string content, string command);
+ 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 8da885d..bd55f54 100644
--- a/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs
+++ b/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs
@@ -1,46 +1,64 @@
-using System;
+using System.Linq;
+using System.Reflection;
using System.Text;
-using System.Text.RegularExpressions;
-using Neo.IronLua;
-using NLog;
+using System.Threading;
+using Discord.Commands;
+using NLua;
namespace ChaosBot.Services.ProgrammingLanguageInterpreter
{
internal class LuaProgrammingLanguageInterpreter : IProgrammingLanguageInterpreter
{
- private static readonly ILogger Logger = Program.GetLogger();
private readonly StringBuilder _outputBuilder = new StringBuilder();
-
- public string Interpret(string content, string command)
+ private CancellationToken _ct;
+ private Lua _state;
+
+ public string Interpret(CancellationToken ct, SocketCommandContext context, string content, string command)
{
- using (Lua 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.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");
+ 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);
+
+ LoggingFacade.Trace("Returning Lua output");
+ return _outputBuilder.ToString().Trim();
+ }
+
+ 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.dochunk(content, $"{command}.lua");
-
- return _outputBuilder.ToString();
+ Lua l = (Lua) sender;
+ l.State.Error("Timeout kill");
}
}
- 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)
{
- StringBuilder sb = new StringBuilder();
+ string str = string.Join("\t", from param in parameters select param == null ? string.Empty : param.ToString());
- foreach (object parameter in parameters)
- {
- sb.Append(parameter);
- }
-
- _outputBuilder.Append(sb);
+ _outputBuilder.AppendLine(str);
}
}
}
diff --git a/ChaosBot/Services/ProgrammingLanguageInterpreter/ProgrammingLanguageInterpreterFacade.cs b/ChaosBot/Services/ProgrammingLanguageInterpreter/ProgrammingLanguageInterpreterFacade.cs
index e48d28a..59f85ce 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,23 @@ namespace ChaosBot.Services.ProgrammingLanguageInterpreter
try
{
- string output = interpreter.Interpret(customCommand.Content, customCommand.Command);
+ CancellationTokenSource tokenSource = new CancellationTokenSource();
+ Task task = Task.Run(() =>
+ {
+ Thread.CurrentThread.Priority = ThreadPriority.Lowest;
+ return interpreter.Interpret(tokenSource.Token, context, customCommand.Content, customCommand.Command);
+ }, tokenSource.Token);
+
+ const int timeout = 250;
+ bool isTaskCompleted = task.Wait(TimeSpan.FromMilliseconds(timeout));
+
+ if (!isTaskCompleted)
+ {
+ tokenSource.Cancel();
+ 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);
@@ -29,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;
}