diff --git a/.classpath b/.classpath
index b116255..7c63137 100644
--- a/.classpath
+++ b/.classpath
@@ -13,5 +13,6 @@
+
diff --git a/lib/luaj-jse-3.0.1.jar b/lib/luaj-jse-3.0.1.jar
new file mode 100644
index 0000000..e125860
Binary files /dev/null and b/lib/luaj-jse-3.0.1.jar differ
diff --git a/src/core/ObjectBuilderFactory.java b/src/core/ObjectBuilderFactory.java
index e4f806d..ada6602 100644
--- a/src/core/ObjectBuilderFactory.java
+++ b/src/core/ObjectBuilderFactory.java
@@ -6,6 +6,7 @@ import java.util.List;
import java.util.concurrent.Semaphore;
import commands.*;
+import core.lua.PluginManager;
import dataStructures.*;
import net.dv8tion.jda.core.entities.Emote;
import net.dv8tion.jda.core.entities.Member;
@@ -38,6 +39,9 @@ public class ObjectBuilderFactory
// RPManger for tracking RP system
private static RPManager rpManager;
+ // Plugin manager
+ private static PluginManager pluginManager;
+
// Localization classes - these are singletons, but should be initialized before almost all other
// things so their inclusion in the factory is to ensure they're started at the correct time.
@SuppressWarnings("unused") private static LocStrings locStrings;
@@ -408,6 +412,16 @@ public class ObjectBuilderFactory
return rpManager;
}
+ public static PluginManager ConstructPluginManager()
+ {
+ LazyInit();
+
+ if(pluginManager == null)
+ pluginManager = new PluginManager("./plugins/");
+
+ return pluginManager;
+ }
+
public static Integer GetGuildCount()
{ synchronized(guildCache)
{
diff --git a/src/core/lua/Plugin.java b/src/core/lua/Plugin.java
index 4dc989a..9053ef6 100644
--- a/src/core/lua/Plugin.java
+++ b/src/core/lua/Plugin.java
@@ -1,8 +1,34 @@
package core.lua;
-// To promote flexibility, plugins are lua file with predefined callbacks.
-// They are executed between the initial built-in preprocessing and the command parsing.
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.stream.Stream;
+
+//To promote flexibility, plugins are lua file with predefined callbacks.
+//They are executed between the initial built-in preprocessing and the command parsing.
public class Plugin
{
+ public Path filepath;
+ public String contents;
+ public Plugin(Path filepath)
+ {
+ this.filepath = filepath;
+
+ StringBuilder contentBuilder = new StringBuilder();
+
+ try
+ {
+ Stream stream = Files.lines(filepath, StandardCharsets.UTF_8);
+ stream.forEach(str -> contentBuilder.append(str).append("\n"));
+ stream.close();
+ contents = contentBuilder.toString();
+ }
+ catch (IOException e)
+ {
+ PluginLog.Error(e.getMessage());
+ }
+ }
}
diff --git a/src/core/lua/PluginLoader.java b/src/core/lua/PluginLoader.java
new file mode 100644
index 0000000..35db1a0
--- /dev/null
+++ b/src/core/lua/PluginLoader.java
@@ -0,0 +1,42 @@
+package core.lua;
+
+import org.luaj.vm2.Globals;
+import org.luaj.vm2.LuaValue;
+import org.luaj.vm2.lib.jse.JsePlatform;
+
+// This class wraps the lua layer - provides all the processing and function
+// calls to the lua binding.
+public class PluginLoader
+{
+ private Globals globals;
+
+ public PluginLoader()
+ {
+ globals = JsePlatform.standardGlobals();
+ PluginLog.Log("Created new PluginLoader lua environment");
+ }
+
+ public String Process(Plugin toProcess, String args)
+ {
+ globals.load(toProcess.contents).call();
+ LuaValue pluginFunc = globals.get("plugin");
+
+ String output = null;
+
+ try
+ {
+ LuaValue res = pluginFunc.call(LuaValue.valueOf(args));
+
+ if(res.isnil())
+ output = null;
+ else
+ output = res.toString();
+ }
+ catch(Exception e)
+ {
+ output = null;
+ }
+
+ return output;
+ }
+}
diff --git a/src/core/lua/PluginLog.java b/src/core/lua/PluginLog.java
new file mode 100644
index 0000000..8b084c7
--- /dev/null
+++ b/src/core/lua/PluginLog.java
@@ -0,0 +1,11 @@
+package core.lua;
+
+import utils.GlobalLog;
+import utils.LogFilter;
+
+public class PluginLog
+{
+ public static void Log(String s) { GlobalLog.Log(LogFilter.Plugin, s); }
+ public static void Warn(String s) { GlobalLog.Warn(LogFilter.Plugin, s); }
+ public static void Error(String s) { GlobalLog.Error(LogFilter.Plugin, s); }
+}
diff --git a/src/core/lua/PluginManager.java b/src/core/lua/PluginManager.java
index 3cc08cf..07937fe 100644
--- a/src/core/lua/PluginManager.java
+++ b/src/core/lua/PluginManager.java
@@ -1,8 +1,64 @@
package core.lua;
-// Reads in, handles, and manipulates plugins. Plugins are
-// loaded in the order they appear in the folder.
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+//Reads in, handles, and manipulates plugins. Plugins are
+//loaded in the order they appear in the folder.
public class PluginManager
{
+ public PluginLoader pluginLoader;
+ public final String pluginFolder;
+ public ArrayList plugins;
+ public void AddPlugin(Path path)
+ {
+ plugins.add(new Plugin(path));
+ }
+
+ public PluginManager(String folder)
+ {
+ this.pluginFolder = folder;
+ plugins = new ArrayList();
+ this.pluginLoader = new PluginLoader();
+
+ try
+ {
+ try (Stream paths = Files.walk(Paths.get(this.pluginFolder)))
+ {
+ paths.filter(Files::isRegularFile).forEach((path)->{ AddPlugin(path); });
+ }
+ }
+ catch(Exception e)
+ {
+ PluginLog.Error(e.getMessage());
+ }
+ }
+
+ public String CallAll(String input)
+ {
+ for(int i = 0; i < plugins.size(); ++i)
+ {
+ Plugin script = plugins.get(i);
+ String out = pluginLoader.Process(script, input);
+
+ if(out != null)
+ {
+ PluginLog.Log("Executed plugin at " + script.filepath);
+ return out;
+ }
+ }
+
+ return null;
+ }
+
+ public void PrintAll()
+ {
+ for(int i = 0; i < plugins.size(); ++i)
+ PluginLog.Log(plugins.get(i).contents.toString());
+ }
}
diff --git a/src/main/Main.java b/src/main/Main.java
index 1ca3707..5d79ca8 100644
--- a/src/main/Main.java
+++ b/src/main/Main.java
@@ -2,6 +2,7 @@ package main;
import javax.security.auth.login.LoginException;
import core.*;
+import core.lua.PluginManager;
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
import dataStructures.KittyRole;
@@ -22,14 +23,15 @@ import net.dv8tion.jda.core.*;
@SuppressWarnings("unused")
public class Main extends ListenerAdapter
{
- // Variables and stuff
+ // Variables and bot specific objects
private static JDA kitty;
- private static CommandManager commandManager;
- private static CommandEnabler commandEnabler;
private static DatabaseManager databaseManager;
+ private static CommandEnabler commandEnabler;
+ private static CommandManager commandManager;
private static Stats stats;
private static RPManager rpManager;
-
+ private static PluginManager pluginManager;
+
// Main test location
public static void main(String[] args) throws InterruptedException, LoginException, Exception
{
@@ -37,8 +39,9 @@ public class Main extends ListenerAdapter
databaseManager = ObjectBuilderFactory.ConstructDatabaseManager();
commandEnabler = ObjectBuilderFactory.ConstructCommandEnabler();
commandManager = ObjectBuilderFactory.ConstructCommandManager(commandEnabler);
- rpManager = ObjectBuilderFactory.ConstructRPManager();
stats = ObjectBuilderFactory.ConstructStats(commandManager);
+ rpManager = ObjectBuilderFactory.ConstructRPManager();
+ pluginManager = ObjectBuilderFactory.ConstructPluginManager();
// Bot startup
kitty = new JDABuilder(AccountType.BOT).setToken(Ref.TestToken).buildBlocking();
@@ -61,19 +64,25 @@ public class Main extends ListenerAdapter
// Specialized uncached objects
Response response = new Response(event, kitty);
UserInput input = new UserInput(event, guild);
-
+
// Tweak object construction as necessary
if(!PostProcessSetup(event, user, guild, channel, response, input))
return;
-
+
// Track beans!
- user.ChangeBeans(1);
+ user.ChangeBeans(1);
// RP logging system
RPManager.instance.addLine(channel, user, input);
- // Issue the command
- commandManager.InvokeOnNewThread(guild, channel, user, input, response);
+ // Run plugins right before invoking the commands but after all other setup
+ String output = pluginManager.CallAll(input.message);
+
+ // Spin up the command if no plugins ran. Otherwise, send a response.
+ if(output == null)
+ commandManager.InvokeOnNewThread(guild, channel, user, input, response);
+ else
+ response.Call(output);
// Run any upkeep we need to
PerCommandUpkeep();
diff --git a/src/utils/LogFilter.java b/src/utils/LogFilter.java
index 22742fe..c0ad6e3 100644
--- a/src/utils/LogFilter.java
+++ b/src/utils/LogFilter.java
@@ -3,7 +3,7 @@ package utils;
public enum LogFilter
{
// Assign numbers as flags, so we can | ('or') them together as necessary
- Debug(0), Command(1), Core(2), Util(4), Database(8), Response(16), Network(32), Strings(64);
+ Debug(0), Command(1), Core(2), Util(4), Database(8), Response(16), Network(32), Strings(64), Plugin(128);
private final int value;
private LogFilter(int value)