diff --git a/dcf.properties.example b/dcf.properties.example index df538af..aa51b9a 100644 --- a/dcf.properties.example +++ b/dcf.properties.example @@ -13,4 +13,7 @@ discord.token = tokenGoesHere # ... or username/password combination if you don't use bot mode # comment out discord.token if using us/pw tuple #discord.username = email -#discord.password = superSecretPassword \ No newline at end of file +#discord.password = superSecretPassword + +# Command prefix +commands.prefix = ! \ No newline at end of file diff --git a/src/main/java/net/pingex/dcf/commands/BasicParser.java b/src/main/java/net/pingex/dcf/commands/BasicParser.java new file mode 100644 index 0000000..1e7f725 --- /dev/null +++ b/src/main/java/net/pingex/dcf/commands/BasicParser.java @@ -0,0 +1,48 @@ +package net.pingex.dcf.commands; + +import net.pingex.dcf.core.Configuration; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Basic command parser. + */ +public class BasicParser implements ICommandParser +{ + private Pattern commandPattern = Pattern.compile("^" + Configuration.COMMAND_PREFIX + "([:\\w]+)(?: (.*))?$"); + private Pattern argsPattern = Pattern.compile("([^\"]\\S*|\".+?\")\\s*"); + + @Override + public boolean checkSyntax(String input) + { + return commandPattern.matcher(input).matches(); + } + + @Override + public String parseCommand(String input) throws ParserException + { + Matcher m = commandPattern.matcher(input); + if(!m.matches()) throw new ParserException("String cannot be parsed for a command."); + return m.group(1); + } + + @Override + public List parseArguments(String input) throws ParserException + { + Matcher mc = commandPattern.matcher(input); + if(!mc.matches()) throw new ParserException("String cannot be parsed for a command."); + + // CASE: Command has no arg + if(mc.group(2) == null) return Collections.emptyList(); + + // CASE: Command has args + Matcher ma = argsPattern.matcher(mc.group(2)); + List argsOut = new ArrayList<>(); + while(ma.find()) argsOut.add(ma.group(1).replace("\"", "")); + return argsOut; + } +} diff --git a/src/main/java/net/pingex/dcf/commands/CommandHandler.java b/src/main/java/net/pingex/dcf/commands/CommandHandler.java new file mode 100644 index 0000000..742d729 --- /dev/null +++ b/src/main/java/net/pingex/dcf/commands/CommandHandler.java @@ -0,0 +1,45 @@ +package net.pingex.dcf.commands; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import sx.blah.discord.handle.impl.events.MessageReceivedEvent; + +import java.util.List; + +/** + * Landing class for a MRE + */ +public class CommandHandler +{ + /** + * The one and only valid parser for now. + */ + public static final ICommandParser PARSER = new BasicParser(); + + /** + * Logger + */ + private static final Logger LOGGER = LogManager.getLogger(CommandHandler.class); + + /** + * Landing method for a MRE + */ + public static void handle(MessageReceivedEvent event) + { + String command; + List arguments; + + try + { + command = PARSER.parseCommand(event.getMessage().getContent()); + arguments = PARSER.parseArguments(event.getMessage().getContent()); + } + catch(ParserException e) + { + return; // Not a command + } + LOGGER.debug("Attempting to run command {} by user #{}.", command, event.getMessage().getAuthor().getID()); + + + } +} diff --git a/src/main/java/net/pingex/dcf/commands/ICommandParser.java b/src/main/java/net/pingex/dcf/commands/ICommandParser.java new file mode 100644 index 0000000..be433d3 --- /dev/null +++ b/src/main/java/net/pingex/dcf/commands/ICommandParser.java @@ -0,0 +1,32 @@ +package net.pingex.dcf.commands; + +import java.util.List; + +/** + * General interface for a command parser. + */ +public interface ICommandParser +{ + /** + * Checks the syntax of a command. + * @param input Input String, ie. the message content. + * @return `true` if the string is a command, `false` otherwise. + */ + boolean checkSyntax(String input); + + /** + * Gives the command part of a command string. + * @param input Input String, ie. the message content. + * @return The command part of the inputted string. + * @throws ParserException whether the inputted string cannot be parsed for a command. + */ + String parseCommand(String input) throws ParserException; + + /** + * Gives the arguments list of a command string. + * @param input Input String, ie. the message content. + * @return The arguments list of the inputted string. + * @throws ParserException whether the inputted string cannot be parsed for a command. + */ + List parseArguments(String input) throws ParserException; +} diff --git a/src/main/java/net/pingex/dcf/commands/ParserException.java b/src/main/java/net/pingex/dcf/commands/ParserException.java new file mode 100644 index 0000000..e33a7de --- /dev/null +++ b/src/main/java/net/pingex/dcf/commands/ParserException.java @@ -0,0 +1,17 @@ +package net.pingex.dcf.commands; + +/** + * Thrown when the parser cannot parse. + */ +public class ParserException extends Exception +{ + public ParserException() + { + super(); + } + + public ParserException(String message) + { + super(message); + } +} diff --git a/src/main/java/net/pingex/dcf/commands/package-info.java b/src/main/java/net/pingex/dcf/commands/package-info.java new file mode 100644 index 0000000..3c6952d --- /dev/null +++ b/src/main/java/net/pingex/dcf/commands/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains everything related to commands parsing and executing. + */ +package net.pingex.dcf.commands; \ No newline at end of file diff --git a/src/main/java/net/pingex/dcf/core/Configuration.java b/src/main/java/net/pingex/dcf/core/Configuration.java index 74d6296..ea53e9b 100644 --- a/src/main/java/net/pingex/dcf/core/Configuration.java +++ b/src/main/java/net/pingex/dcf/core/Configuration.java @@ -89,6 +89,11 @@ public class Configuration */ public static String DATA_DIR = "data"; + /** + * Command prefix string + */ + public static String COMMAND_PREFIX = "!"; + /** * Tells if the bot is configured to connect using a token or an username/password tuple. * @return Whether the main connection is a bot, or not @@ -106,6 +111,7 @@ public class Configuration BOT_NAME = store.getString("general.bot_name", BOT_NAME); PLUGINS_DIR = store.getString("general.plugins_dir", PLUGINS_DIR); DATA_DIR = store.getString("general.storage_dir", DATA_DIR); + COMMAND_PREFIX = store.getString("commands.prefix", COMMAND_PREFIX); // Validate main connection username/password or token if(isConnectionToken()) diff --git a/src/main/java/net/pingex/dcf/core/CoreEventsHandler.java b/src/main/java/net/pingex/dcf/core/CoreEventsHandler.java index d342ba2..0eba31b 100644 --- a/src/main/java/net/pingex/dcf/core/CoreEventsHandler.java +++ b/src/main/java/net/pingex/dcf/core/CoreEventsHandler.java @@ -1,5 +1,6 @@ package net.pingex.dcf.core; +import net.pingex.dcf.commands.CommandHandler; import net.pingex.dcf.modularity.PluginWrapper; import net.pingex.dcf.modularity.events.EventManager; import org.apache.logging.log4j.LogManager; @@ -53,6 +54,7 @@ public class CoreEventsHandler public static void onMessageReceived(MessageReceivedEvent event) { LOGGER.trace("Received message from channel #{}.", event.getMessage().getChannel().getID()); + CommandHandler.handle(event); } @EventSubscriber @@ -92,7 +94,7 @@ public class CoreEventsHandler public static void pluginRunning(PluginWrapper plugin) { EventManager.getInstance().checkAndRegister(plugin); // Hook: event handlers - // TODO: Hook: commands + // TODO: Hook: register commands (if implements appropriate interface) } /** @@ -102,6 +104,7 @@ public class CoreEventsHandler public static void pluginStopped(PluginWrapper plugin) { EventManager.getInstance().unregister(plugin); // Hook: event handlers + // TODO: Hook: unregister commands } /**