Fix internal lua functionality #patch

This commit is contained in:
Daniel_I_Am 2020-09-27 12:38:29 +02:00
commit a08ba9ea33
No known key found for this signature in database
GPG Key ID: 80C428FCC9743E84
6 changed files with 72 additions and 33 deletions

View File

@ -16,9 +16,9 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.6" />
<PackageReference Include="NeoLua" Version="1.3.11" />
<PackageReference Include="NLog" Version="4.7.2" />
<PackageReference Include="NLog.Extensions.Logging" Version="1.6.4" />
<PackageReference Include="NLua" Version="1.5.1" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.1.2" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql.Design" Version="1.1.2" />
</ItemGroup>

View File

@ -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;
}

View File

@ -6,7 +6,6 @@ using ChaosBot.Services;
using ChaosBot.Services.ProgrammingLanguageInterpreter;
using Discord;
using Discord.Commands;
using Neo.IronLua;
namespace ChaosBot.Discord.Services
{

View File

@ -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);
}
}

View File

@ -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();
private CancellationToken _ct;
private Lua _state;
public string Interpret(string content, string command)
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)
{
// 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();
if (_ct.IsCancellationRequested)
{
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);
}
}
}

View File

@ -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<string> 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;
}