using System; using System.Linq; using System.Reflection; using System.Text; using System.Text.Json; using System.Threading; using Discord.Commands; using NLua; namespace ChaosBot.Services.ProgrammingLanguageInterpreter { internal class LuaProgrammingLanguageInterpreter : IProgrammingLanguageInterpreter { private readonly StringBuilder _outputBuilder = new StringBuilder(); private CancellationToken _ct; private Lua _state; public string Interpret(CancellationToken ct, LimitedSocketCommandContext context, string content, string command) { 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) { Lua l = (Lua) sender; l.State.Error("Timeout kill"); } } // 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("\t", from param in parameters select param == null ? string.Empty : param.ToString()); _outputBuilder.AppendLine(str); } } }