From f0d9cc5f897bea57080e4acc4c3c13191e7c460a Mon Sep 17 00:00:00 2001 From: "Tobias Burdow [Kaleidox]" Date: Mon, 3 Feb 2020 10:26:30 +0100 Subject: [PATCH 1/2] Added FuzzySearch field --- build.gradle | 2 ++ .../javacord/util/commands/CommandHandler.java | 16 ++++++++++++++++ .../util/model/command/SelfFuzzyComparable.java | 16 ++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 src/main/java/org/comroid/javacord/util/model/command/SelfFuzzyComparable.java diff --git a/build.gradle b/build.gradle index 941c6fe..564df74 100644 --- a/build.gradle +++ b/build.gradle @@ -60,6 +60,8 @@ repositories.jcenter() dependencies { api 'org.apache.logging.log4j:log4j-api:2.13.0' + + implementation 'me.xdrop:fuzzywuzzy:1.2.0' compileOnly 'org.javacord:javacord-api:3.0.5' compileOnly 'org.javacord:javacord-core:3.0.5' diff --git a/src/main/java/org/comroid/javacord/util/commands/CommandHandler.java b/src/main/java/org/comroid/javacord/util/commands/CommandHandler.java index f05ea69..a43e5bb 100644 --- a/src/main/java/org/comroid/javacord/util/commands/CommandHandler.java +++ b/src/main/java/org/comroid/javacord/util/commands/CommandHandler.java @@ -26,6 +26,7 @@ import org.comroid.javacord.util.model.command.SelfBotOwnerIgnorable; import org.comroid.javacord.util.model.command.SelfCommandChannelable; import org.comroid.javacord.util.model.command.SelfCustomPrefixable; +import org.comroid.javacord.util.model.command.SelfFuzzyComparable; import org.comroid.javacord.util.model.command.SelfMultiCommandRegisterable; import org.comroid.javacord.util.model.command.SelfUnknownCommandRespondable; import org.comroid.javacord.util.ui.embed.DefaultEmbedFactory; @@ -58,6 +59,7 @@ import org.javacord.core.util.logging.LoggerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Range; import static java.lang.System.arraycopy; import static java.lang.reflect.Modifier.isStatic; @@ -68,6 +70,7 @@ public final class CommandHandler implements SelfCommandChannelable, SelfCustomPrefixable, SelfBotOwnerIgnorable, + SelfFuzzyComparable, SelfUnknownCommandRespondable { private static final Logger logger = LoggerUtil.getLogger(CommandHandler.class); static final String NO_GROUP = "@NoGroup#"; @@ -85,6 +88,7 @@ public final class CommandHandler implements private long[] serverBlacklist; private boolean ignoreBotOwnerPermissions; private boolean respondToUnknownCommand; + private @Nullable Function fuzzyMatchingThresholdFunction; public CommandHandler(DiscordApi api) { this(api, false); @@ -328,6 +332,18 @@ public boolean doesRespondToUnknownCommands() { return respondToUnknownCommand; } + @Override + public CommandHandler withFuzzyMatchingThreshold(@Nullable Function fuzzyMatchingThresholdFunction) { + this.fuzzyMatchingThresholdFunction = fuzzyMatchingThresholdFunction; + + return this; + } + + @Override + public Optional> getFuzzyMatchingThreshold() { + return Optional.ofNullable(fuzzyMatchingThresholdFunction); + } + private void extractCommandRep(@Nullable Object invocationTarget, Method... methods) { for (Method method : methods) { Command cmd = method.getAnnotation(Command.class); diff --git a/src/main/java/org/comroid/javacord/util/model/command/SelfFuzzyComparable.java b/src/main/java/org/comroid/javacord/util/model/command/SelfFuzzyComparable.java new file mode 100644 index 0000000..6af1fe3 --- /dev/null +++ b/src/main/java/org/comroid/javacord/util/model/command/SelfFuzzyComparable.java @@ -0,0 +1,16 @@ +package org.comroid.javacord.util.model.command; + +import java.util.Optional; +import java.util.function.Function; + +import org.jetbrains.annotations.Nullable; + +public interface SelfFuzzyComparable> { + Self withFuzzyMatchingThreshold(@Nullable Function fuzzyMatchingThresholdFunction); + + Optional> getFuzzyMatchingThreshold(); + + default Self removeFuzzyMatchingThreshold() { + return withFuzzyMatchingThreshold(null); + } +} From 1d85b22fb07e08039e19cb3e38be39431e76d191 Mon Sep 17 00:00:00 2001 From: "Tobias Burdow [Kaleidox]" Date: Mon, 3 Feb 2020 10:47:54 +0100 Subject: [PATCH 2/2] Implemented Fuzzy Matching --- .../util/commands/CommandHandler.java | 57 +++++++++++++------ 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/comroid/javacord/util/commands/CommandHandler.java b/src/main/java/org/comroid/javacord/util/commands/CommandHandler.java index a43e5bb..8b491e0 100644 --- a/src/main/java/org/comroid/javacord/util/commands/CommandHandler.java +++ b/src/main/java/org/comroid/javacord/util/commands/CommandHandler.java @@ -37,6 +37,8 @@ import org.comroid.javacord.util.ui.messages.paging.PagedMessage; import org.comroid.javacord.util.ui.reactions.InfoReaction; +import me.xdrop.fuzzywuzzy.FuzzySearch; +import me.xdrop.fuzzywuzzy.model.ExtractedResult; import org.apache.logging.log4j.Logger; import org.javacord.api.DiscordApi; import org.javacord.api.entity.DiscordEntity; @@ -59,7 +61,6 @@ import org.javacord.core.util.logging.LoggerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.Range; import static java.lang.System.arraycopy; import static java.lang.reflect.Modifier.isStatic; @@ -335,7 +336,7 @@ public boolean doesRespondToUnknownCommands() { @Override public CommandHandler withFuzzyMatchingThreshold(@Nullable Function fuzzyMatchingThresholdFunction) { this.fuzzyMatchingThresholdFunction = fuzzyMatchingThresholdFunction; - + return this; } @@ -478,27 +479,21 @@ private void handleCommand(final Message message, final TextChannel channel, fin CommandRepresentation cmd; String[] split = splitContent(content); final String[] commandName = new String[1]; + String[] args; + Optional serverOptional = commandParams.getServer(); if (usedPrefix.matches("^(.*\\s.*)+$")) { - cmd = commands.entrySet() - .stream() - .filter(entry -> entry.getKey() - .toLowerCase() - .equals((commandName[0] = split[1]).substring(usedPrefix.length()).toLowerCase())) - .findFirst() - .map(Map.Entry::getValue) - .orElse(null); + final String cmdQuery = (commandName[0] = split[1]).substring(usedPrefix.length()).toLowerCase(); + + cmd = findCommand(serverOptional.orElse(null), cmdQuery); + args = new String[split.length - 2]; arraycopy(split, 2, args, 0, args.length); } else { - cmd = commands.entrySet() - .stream() - .filter(entry -> entry.getKey() - .toLowerCase() - .equals((commandName[0] = split[0]).substring(usedPrefix.length()).toLowerCase())) - .findFirst() - .map(Map.Entry::getValue) - .orElse(null); + final String cmdQuery = (commandName[0] = split[0]).substring(usedPrefix.length()).toLowerCase(); + + cmd = findCommand(serverOptional.orElse(null), cmdQuery); + args = new String[split.length - 1]; arraycopy(split, 1, args, 0, args.length); } @@ -573,6 +568,32 @@ else if (!message.isPrivateMessage() && !cmd.enableServerChat) else doInvoke(cmd, commandParams, channel, message); } + private @Nullable CommandRepresentation findCommand(@Nullable Server server, String cmdQuery) { + CommandRepresentation yield = null; + + if (fuzzyMatchingThresholdFunction != null && server != null) { + final List extractedResults = FuzzySearch.extractTop( + cmdQuery, + commands.keySet(), + fuzzyMatchingThresholdFunction.apply(server.getId()) + ); + + if (extractedResults.size() == 1) + yield = commands.get(extractedResults.get(0).getString()); + } else { + yield = commands.entrySet() + .stream() + .filter(entry -> entry.getKey() + .toLowerCase() + .equals(cmdQuery)) + .findFirst() + .map(Map.Entry::getValue) + .orElse(null); + } + + return yield; + } + private @Nullable String extractUsedPrefix(final Message message) { String content = message.getContent(); int usedPrefix = -1;