diff --git a/src/main/java/me/chayapak1/chomens_bot/Bot.java b/src/main/java/me/chayapak1/chomens_bot/Bot.java index 0387a4b..472ac51 100644 --- a/src/main/java/me/chayapak1/chomens_bot/Bot.java +++ b/src/main/java/me/chayapak1/chomens_bot/Bot.java @@ -66,6 +66,7 @@ public class Bot { public BossbarManagerPlugin bossbar; public MusicPlayerPlugin music; public TPSPlugin tps; + public EvalPlugin eval; public TrustedPlugin trusted; public BruhifyPlugin bruhify; public CloopPlugin cloop; @@ -119,6 +120,7 @@ public class Bot { this.bossbar = new BossbarManagerPlugin(this); this.music = new MusicPlayerPlugin(this); this.tps = new TPSPlugin(this); + this.eval = new EvalPlugin(this); this.trusted = new TrustedPlugin(this); this.bruhify = new BruhifyPlugin(this); this.cloop = new CloopPlugin(this); diff --git a/src/main/java/me/chayapak1/chomens_bot/Configuration.java b/src/main/java/me/chayapak1/chomens_bot/Configuration.java index da9f69c..cd4bca2 100644 --- a/src/main/java/me/chayapak1/chomens_bot/Configuration.java +++ b/src/main/java/me/chayapak1/chomens_bot/Configuration.java @@ -26,6 +26,8 @@ public class Configuration { public IRC irc = new IRC(); public Music music = new Music(); + public Eval eval = new Eval(); + public ColorPalette colorPalette = new ColorPalette(); public String ownerName = "chayapak"; // mabe mabe @@ -125,6 +127,10 @@ public class Configuration { public String error = "#FF0000"; } + public static class Eval { + public String address = "ws://localhost:3069"; + } + public static class SelfCare { public int delay = 225; diff --git a/src/main/java/me/chayapak1/chomens_bot/commands/EvalCommand.java b/src/main/java/me/chayapak1/chomens_bot/commands/EvalCommand.java new file mode 100644 index 0000000..6aa2b54 --- /dev/null +++ b/src/main/java/me/chayapak1/chomens_bot/commands/EvalCommand.java @@ -0,0 +1,58 @@ +package me.chayapak1.chomens_bot.commands; + +import me.chayapak1.chomens_bot.Bot; +import me.chayapak1.chomens_bot.command.Command; +import me.chayapak1.chomens_bot.command.CommandContext; +import me.chayapak1.chomens_bot.command.CommandException; +import me.chayapak1.chomens_bot.command.TrustLevel; +import me.chayapak1.chomens_bot.data.eval.EvalOutput; +import me.chayapak1.chomens_bot.util.ColorUtilities; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; + +import java.util.concurrent.CompletableFuture; + +public class EvalCommand extends Command { + public EvalCommand () { + super( + "eval", + "Evaluate JavaScript codes on a separate Node.JS server running isolated-vm", + new String[] { "run ", "reset" }, + new String[] {}, + TrustLevel.PUBLIC, + false + ); + } + + @Override + public Component execute(CommandContext context) throws CommandException { + final Bot bot = context.bot; + + if (!bot.eval.connected) throw new CommandException(Component.text("Eval server is not online")); + + final String action = context.getString(false, true); + + switch (action) { + case "run" -> { + final String command = context.getString(true, true); + + final CompletableFuture future = bot.eval.run(command); + + future.thenApply((output) -> { + if (output.isError()) context.sendOutput(Component.text(output.output()).color(NamedTextColor.RED)); + else context.sendOutput(Component.text(output.output())); + + return output; + }); + } + case "reset" -> { + bot.eval.reset(); + + return Component.text("Reset the eval worker").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor)); + } + default -> throw new CommandException(Component.text("Invalid action")); + } + + return null; + } +} diff --git a/src/main/java/me/chayapak1/chomens_bot/data/eval/EvalFunction.java b/src/main/java/me/chayapak1/chomens_bot/data/eval/EvalFunction.java new file mode 100644 index 0000000..6ebadab --- /dev/null +++ b/src/main/java/me/chayapak1/chomens_bot/data/eval/EvalFunction.java @@ -0,0 +1,29 @@ +package me.chayapak1.chomens_bot.data.eval; + +import me.chayapak1.chomens_bot.Bot; + +public class EvalFunction { + public final String name; + + protected final Bot bot; + + public EvalFunction ( + String name, + Bot bot + ) { + this.name = name; + this.bot = bot; + } + + public Output execute (Object ...args) { return null; } + + public static class Output { + public final String message; + public final boolean parseJSON; + + public Output (String message, boolean parseJSON) { + this.message = message; + this.parseJSON = parseJSON; + } + } +} diff --git a/src/main/java/me/chayapak1/chomens_bot/data/EvalOutput.java b/src/main/java/me/chayapak1/chomens_bot/data/eval/EvalOutput.java similarity index 62% rename from src/main/java/me/chayapak1/chomens_bot/data/EvalOutput.java rename to src/main/java/me/chayapak1/chomens_bot/data/eval/EvalOutput.java index d2ad351..bb83b49 100644 --- a/src/main/java/me/chayapak1/chomens_bot/data/EvalOutput.java +++ b/src/main/java/me/chayapak1/chomens_bot/data/eval/EvalOutput.java @@ -1,4 +1,4 @@ -package me.chayapak1.chomens_bot.data; +package me.chayapak1.chomens_bot.data.eval; public record EvalOutput ( boolean isError, diff --git a/src/main/java/me/chayapak1/chomens_bot/evalFunctions/ChatFunction.java b/src/main/java/me/chayapak1/chomens_bot/evalFunctions/ChatFunction.java new file mode 100644 index 0000000..e764ecc --- /dev/null +++ b/src/main/java/me/chayapak1/chomens_bot/evalFunctions/ChatFunction.java @@ -0,0 +1,19 @@ +package me.chayapak1.chomens_bot.evalFunctions; + +import me.chayapak1.chomens_bot.Bot; +import me.chayapak1.chomens_bot.data.eval.EvalFunction; + +public class ChatFunction extends EvalFunction { + public ChatFunction (Bot bot) { + super("chat", bot); + } + + @Override + public Output execute(Object... args) { + final String message = (String) args[0]; + + bot.chat.send(message); + + return null; + } +} diff --git a/src/main/java/me/chayapak1/chomens_bot/evalFunctions/CoreFunction.java b/src/main/java/me/chayapak1/chomens_bot/evalFunctions/CoreFunction.java new file mode 100644 index 0000000..46c0029 --- /dev/null +++ b/src/main/java/me/chayapak1/chomens_bot/evalFunctions/CoreFunction.java @@ -0,0 +1,19 @@ +package me.chayapak1.chomens_bot.evalFunctions; + +import me.chayapak1.chomens_bot.Bot; +import me.chayapak1.chomens_bot.data.eval.EvalFunction; + +public class CoreFunction extends EvalFunction { + public CoreFunction (Bot bot) { + super("core", bot); + } + + @Override + public Output execute(Object... args) { + final String command = (String) args[0]; + + bot.core.run(command); + + return null; + } +} diff --git a/src/main/java/me/chayapak1/chomens_bot/evalFunctions/CorePlaceBlockFunction.java b/src/main/java/me/chayapak1/chomens_bot/evalFunctions/CorePlaceBlockFunction.java new file mode 100644 index 0000000..9078208 --- /dev/null +++ b/src/main/java/me/chayapak1/chomens_bot/evalFunctions/CorePlaceBlockFunction.java @@ -0,0 +1,19 @@ +package me.chayapak1.chomens_bot.evalFunctions; + +import me.chayapak1.chomens_bot.Bot; +import me.chayapak1.chomens_bot.data.eval.EvalFunction; + +public class CorePlaceBlockFunction extends EvalFunction { + public CorePlaceBlockFunction (Bot bot) { + super("corePlaceBlock", bot); + } + + @Override + public Output execute(Object... args) { + final String command = (String) args[0]; + + bot.core.runPlaceBlock(command); + + return null; + } +} diff --git a/src/main/java/me/chayapak1/chomens_bot/evalFunctions/GetPlayerListFunction.java b/src/main/java/me/chayapak1/chomens_bot/evalFunctions/GetPlayerListFunction.java new file mode 100644 index 0000000..3481bf9 --- /dev/null +++ b/src/main/java/me/chayapak1/chomens_bot/evalFunctions/GetPlayerListFunction.java @@ -0,0 +1,35 @@ +package me.chayapak1.chomens_bot.evalFunctions; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import me.chayapak1.chomens_bot.Bot; +import me.chayapak1.chomens_bot.data.PlayerEntry; +import me.chayapak1.chomens_bot.data.eval.EvalFunction; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; + +import java.util.List; + +public class GetPlayerListFunction extends EvalFunction { + public GetPlayerListFunction(Bot bot) { + super("getPlayerList", bot); + } + + @Override + public Output execute(Object... args) { + final List list = bot.players.list; + + final JsonArray array = new JsonArray(); + + for (PlayerEntry entry : list) { + final JsonObject object = new JsonObject(); + + object.addProperty("uuid", entry.profile.getIdAsString()); + object.addProperty("username", entry.profile.getName()); + if (entry.displayName != null) object.addProperty("displayName", GsonComponentSerializer.gson().serialize(entry.displayName)); + + array.add(object); + } + + return new Output(array.toString(), true); + } +} diff --git a/src/main/java/me/chayapak1/chomens_bot/plugins/CommandHandlerPlugin.java b/src/main/java/me/chayapak1/chomens_bot/plugins/CommandHandlerPlugin.java index 4d54242..51bda2e 100644 --- a/src/main/java/me/chayapak1/chomens_bot/plugins/CommandHandlerPlugin.java +++ b/src/main/java/me/chayapak1/chomens_bot/plugins/CommandHandlerPlugin.java @@ -48,6 +48,7 @@ public class CommandHandlerPlugin { registerCommand(new ClearChatQueueCommand()); registerCommand(new FilterCommand()); registerCommand(new MailCommand()); + registerCommand(new EvalCommand()); registerCommand(new InfoCommand()); registerCommand(new ConsoleCommand()); registerCommand(new PCrashCommand()); diff --git a/src/main/java/me/chayapak1/chomens_bot/plugins/EvalPlugin.java b/src/main/java/me/chayapak1/chomens_bot/plugins/EvalPlugin.java new file mode 100644 index 0000000..f483d1c --- /dev/null +++ b/src/main/java/me/chayapak1/chomens_bot/plugins/EvalPlugin.java @@ -0,0 +1,105 @@ +package me.chayapak1.chomens_bot.plugins; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import io.socket.client.IO; +import io.socket.client.Socket; +import me.chayapak1.chomens_bot.Bot; +import me.chayapak1.chomens_bot.data.eval.EvalFunction; +import me.chayapak1.chomens_bot.data.eval.EvalOutput; +import me.chayapak1.chomens_bot.evalFunctions.ChatFunction; +import me.chayapak1.chomens_bot.evalFunctions.CoreFunction; +import me.chayapak1.chomens_bot.evalFunctions.CorePlaceBlockFunction; +import me.chayapak1.chomens_bot.evalFunctions.GetPlayerListFunction; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +public class EvalPlugin { + public static final String BRIDGE_PREFIX = "function:"; + + public boolean connected = false; + + private Socket socket = null; + + private int transactionId = 0; + + private final Map> futures = new HashMap<>(); + + public final List functions = new ArrayList<>(); + + private final Gson gson = new Gson(); + + public EvalPlugin (Bot bot) { + functions.add(new CoreFunction(bot)); + functions.add(new CorePlaceBlockFunction(bot)); + functions.add(new ChatFunction(bot)); + functions.add(new GetPlayerListFunction(bot)); + + try { + socket = IO.socket(bot.config.eval.address); + } catch (Exception e) { + e.printStackTrace(); + } + + socket.on(Socket.EVENT_CONNECT, (args) -> { + connected = true; + + final JsonArray array = new JsonArray(); + + for (EvalFunction function : functions) array.add(function.name); + + socket.emit( + "setFunctions", + gson.toJson(array) + ); + }); + socket.on(Socket.EVENT_DISCONNECT, (args) -> connected = false); + socket.on(Socket.EVENT_CONNECT_ERROR, (args) -> connected = false); + + for (EvalFunction function : functions) { + socket.on(BRIDGE_PREFIX + function.name, args -> { + final EvalFunction.Output output = function.execute(args); + + if (output == null) return; + + socket.emit("functionOutput:" + function.name, output.message, output.parseJSON); + }); + } + + socket.on("codeOutput", (args) -> { + final int id = (int) args[0]; + final boolean isError = (boolean) args[1]; + final String output = (String) args[2]; + + final CompletableFuture future = futures.get(id); + + future.complete(new EvalOutput(isError, output)); + }); + + socket.connect(); + } + + public CompletableFuture run (String code) { + final CompletableFuture future = new CompletableFuture<>(); + + if (!connected) return null; + + socket.emit("runCode", transactionId, code); + + futures.put(transactionId, future); + + transactionId++; + + return future; + } + + public void reset () { + if (!connected) return; + + socket.emit("reset"); + } +} diff --git a/src/main/resources/default-config.yml b/src/main/resources/default-config.yml index 3445cad..21423b5 100644 --- a/src/main/resources/default-config.yml +++ b/src/main/resources/default-config.yml @@ -94,6 +94,10 @@ core: # PLEASE give valid JSON component here else the core don't refill at all customName: '[{"text":"ChomeNS ","color":"yellow"},{"text":"Core","color":"green"},{"text":"™","color":"gold"}]' +# https://code.chipmunk.land/ChomeNS/eval-server +eval: + address: 'ws://localhost:3069' + selfCare: delay: 225 # NOTE: self care does NOT use chat queue