package io.shockah.skylark.groovy; import java.util.Collection; import java.util.Map; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer; import org.codehaus.groovy.control.customizers.ImportCustomizer; import org.kohsuke.groovy.sandbox.GroovyInterceptor; import org.kohsuke.groovy.sandbox.SandboxTransformer; import com.github.kevinsawicki.http.HttpRequest; import com.google.common.collect.ImmutableMap; import groovy.lang.Binding; import groovy.lang.GroovyShell; import groovy.transform.TimedInterrupt; import io.shockah.json.JSONList; import io.shockah.json.JSONObject; import io.shockah.skylark.commands.CommandCall; import io.shockah.skylark.commands.CommandsPlugin; import io.shockah.skylark.event.GenericUserMessageEvent; import io.shockah.skylark.factoids.FactoidType; import io.shockah.skylark.factoids.FactoidsPlugin; import io.shockah.skylark.func.Func1; import io.shockah.skylark.plugin.Plugin; import io.shockah.skylark.plugin.PluginManager; public class GroovyPlugin extends Plugin { public static final int TIMEOUT = 30; @Dependency protected CommandsPlugin commandsPlugin; @Dependency("io.shockah.skylark.factoids") protected Plugin factoidsOptionalPlugin; private GroovyCommand command; private Object factoidType; public GroovyPlugin(PluginManager manager, Info info) { super(manager, info); } @Override protected void onLoad() { commandsPlugin.addNamedCommand(command = new GroovyCommand(this)); } @Override protected void onAllPluginsLoaded() { if (factoidsOptionalPlugin != null) { FactoidsPlugin factoidsPlugin = (FactoidsPlugin)factoidsOptionalPlugin; FactoidType factoidType = new GroovyFactoidType(this); this.factoidType = factoidType; factoidsPlugin.addType(factoidType); } } @Override protected void onUnload() { commandsPlugin.removeNamedCommand(command); if (factoidsOptionalPlugin != null) { FactoidsPlugin factoidsPlugin = (FactoidsPlugin)factoidsOptionalPlugin; factoidsPlugin.removeType((FactoidType)factoidType); } } public GroovyShell getShell(GenericUserMessageEvent event) { return getShell(new GroovySandboxImpl(), event); } public GroovyShell getShell(Map<String, Object> variables, GenericUserMessageEvent event) { return getShell(variables, new GroovySandboxImpl(), event); } public GroovyShell getShell(GroovyInterceptor sandbox, GenericUserMessageEvent event) { return getShell(new Binding(), sandbox, event); } public GroovyShell getShell(Map<String, Object> variables, GroovyInterceptor sandbox, GenericUserMessageEvent event) { return getShell(new Binding(variables), sandbox, event); } private Func1<String, Object> getEvalFunction(Binding binding, GroovyInterceptor sandbox, GenericUserMessageEvent event) { return input -> getShell(binding, sandbox, event).evaluate(input); } private GroovyShell getShell(Binding binding, GroovyInterceptor sandbox, GenericUserMessageEvent event) { binding.setVariable("eval", getEvalFunction(binding, sandbox, event)); binding.setVariable("commands", new DynamicCommandHandler(this, event)); CompilerConfiguration cc = new CompilerConfiguration(); cc.addCompilationCustomizers( new SandboxTransformer(), new ASTTransformationCustomizer(ImmutableMap.of("value", TIMEOUT), TimedInterrupt.class), new ImportCustomizer() .addStarImports("java.lang.reflect") .addImports(HttpRequest.class.getName()) .addImports(CommandCall.class.getName()) ); GroovyShell shell = new GroovyShell(manager.pluginClassLoader, binding, cc); sandbox.register(); return shell; } @SuppressWarnings("unchecked") public Object turnIntoJSONValue(Object o) { if (o instanceof Map<?, ?>) { Map<String, Object> map = (Map<String, Object>)o; JSONObject j = new JSONObject(); for (Map.Entry<String, Object> entry : map.entrySet()) { j.put(entry.getKey(), turnIntoJSONValue(entry.getValue())); } return j; } else if (o instanceof Collection<?>) { Collection<Object> collection = (Collection<Object>)o; JSONList<Object> jList = new JSONList<>(); for (Object element : collection) { jList.add(turnIntoJSONValue(element)); } return jList; } else { return o; } } }