diff --git a/build.gradle b/build.gradle index 8bbffd5c..a0afdc29 100644 --- a/build.gradle +++ b/build.gradle @@ -58,6 +58,7 @@ subprojects { repositories { mavenLocal() mavenCentral() + jcenter() maven { url = 'https://oss.sonatype.org/content/repositories/snapshots' diff --git a/cloud-discord/cloud-jda/build.gradle b/cloud-discord/cloud-jda/build.gradle new file mode 100644 index 00000000..4f898f27 --- /dev/null +++ b/cloud-discord/cloud-jda/build.gradle @@ -0,0 +1,4 @@ +dependencies { + api project(':cloud-core') + compileOnly 'net.dv8tion:JDA:4.2.0_207' +} diff --git a/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/JDACommandListener.java b/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/JDACommandListener.java new file mode 100644 index 00000000..d504ee8b --- /dev/null +++ b/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/JDACommandListener.java @@ -0,0 +1,125 @@ +// +// MIT License +// +// Copyright (c) 2020 Alexander Söderberg & Contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +package cloud.commandframework.jda; + +import cloud.commandframework.exceptions.ArgumentParseException; +import cloud.commandframework.exceptions.InvalidCommandSenderException; +import cloud.commandframework.exceptions.InvalidSyntaxException; +import cloud.commandframework.exceptions.NoPermissionException; +import cloud.commandframework.exceptions.NoSuchCommandException; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * JDA Command Listener + * + * @param Command sender type + */ +public class JDACommandListener extends ListenerAdapter { + + private static final String MESSAGE_INVALID_SYNTAX = "Invalid Command Syntax. Correct command syntax is: "; + private static final String MESSAGE_NO_PERMS = "I'm sorry, but you do not have permission to perform this command. " + + "Please contact the server administrators if you believe that this is in error."; + private static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; + + private final JDACommandManager commandManager; + + /** + * Construct a new JDA Command Listener + * + * @param commandManager Command Manager instance + */ + public JDACommandListener(final @NonNull JDACommandManager commandManager) { + this.commandManager = commandManager; + } + + @Override + public final void onMessageReceived(final @NonNull MessageReceivedEvent event) { + Message message = event.getMessage(); + C sender = commandManager.getCommandSenderMapper().apply(event); + + if (commandManager.getBotId() == event.getAuthor().getIdLong()) { + return; + } + + String prefix = commandManager.getPrefixMapper().apply(sender); + String content = message.getContentRaw(); + + if (!content.startsWith(prefix)) { + return; + } + + content = content.substring(prefix.length()); + + commandManager.executeCommand(sender, content) + .whenComplete((commandResult, throwable) -> { + if (throwable == null) { + return; + } + + if (throwable instanceof InvalidSyntaxException) { + commandManager.handleException(sender, + InvalidSyntaxException.class, + (InvalidSyntaxException) throwable, (c, e) -> { + sendMessage(event, + MESSAGE_INVALID_SYNTAX + prefix + ((InvalidSyntaxException) throwable) + .getCorrectSyntax()); + }); + } else if (throwable instanceof InvalidCommandSenderException) { + commandManager.handleException(sender, + InvalidCommandSenderException.class, + (InvalidCommandSenderException) throwable, (c, e) -> + sendMessage(event, throwable.getMessage()) + ); + } else if (throwable instanceof NoPermissionException) { + commandManager.handleException(sender, + NoPermissionException.class, + (NoPermissionException) throwable, (c, e) -> + sendMessage(event, MESSAGE_NO_PERMS) + ); + } else if (throwable instanceof NoSuchCommandException) { + commandManager.handleException(sender, + NoSuchCommandException.class, + (NoSuchCommandException) throwable, (c, e) -> + sendMessage(event, MESSAGE_UNKNOWN_COMMAND) + ); + } else if (throwable instanceof ArgumentParseException) { + commandManager.handleException(sender, ArgumentParseException.class, + (ArgumentParseException) throwable, (c, e) -> { + sendMessage(event, + "Invalid Command Argument: " + throwable.getCause() + .getMessage()); + }); + } else { + sendMessage(event, throwable.getMessage()); + } + }); + } + + private void sendMessage(final @NonNull MessageReceivedEvent event, final @NonNull String message) { + event.getChannel().sendMessage(message).queue(); + } +} diff --git a/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/JDACommandManager.java b/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/JDACommandManager.java new file mode 100644 index 00000000..9a1d8f49 --- /dev/null +++ b/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/JDACommandManager.java @@ -0,0 +1,124 @@ +// +// MIT License +// +// Copyright (c) 2020 Alexander Söderberg & Contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +package cloud.commandframework.jda; + +import cloud.commandframework.CommandManager; +import cloud.commandframework.CommandTree; +import cloud.commandframework.execution.CommandExecutionCoordinator; +import cloud.commandframework.internal.CommandRegistrationHandler; +import cloud.commandframework.meta.CommandMeta; +import cloud.commandframework.meta.SimpleCommandMeta; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.function.Function; + +/** + * Command manager for use with JDA + * + * @param Command sender type + */ +public class JDACommandManager extends CommandManager { + private final long botId; + + private final Function<@NonNull C, @NonNull String> prefixMapper; + private final Function<@NonNull MessageReceivedEvent, @NonNull C> commandSenderMapper; + private final Function<@NonNull C, @NonNull MessageReceivedEvent> backwardsCommandSenderMapper; + + /** + * final + * Construct a new JDA Command Manager + * + * @param jda JDA instance to register against + * @param prefixMapper Function that maps the sender to a command prefix string + * @param commandExecutionCoordinator Coordination provider + * @param commandSenderMapper Function that maps {@link MessageReceivedEvent} to the command sender type + * @param backwardsCommandSenderMapper Function that maps the command sender type to {@link MessageReceivedEvent} + * @throws InterruptedException If the jda instance does not ready correctly + */ + public JDACommandManager(final @NonNull JDA jda, + final @NonNull Function<@NonNull C, @NonNull String> prefixMapper, + final @NonNull Function, CommandExecutionCoordinator> commandExecutionCoordinator, + final @NonNull Function<@NonNull MessageReceivedEvent, @NonNull C> commandSenderMapper, + final @NonNull Function<@NonNull C, @NonNull MessageReceivedEvent> backwardsCommandSenderMapper) + throws InterruptedException { + super(commandExecutionCoordinator, CommandRegistrationHandler.nullCommandRegistrationHandler()); + this.prefixMapper = prefixMapper; + this.commandSenderMapper = commandSenderMapper; + this.backwardsCommandSenderMapper = backwardsCommandSenderMapper; + jda.addEventListener(new JDACommandListener<>(this)); + jda.awaitReady(); + this.botId = jda.getSelfUser().getIdLong(); + } + + /** + * Get the prefix mapper + * + * @return Prefix mapper + */ + public final @NonNull Function getPrefixMapper() { + return prefixMapper; + } + + /** + * Get the command sender mapper + * + * @return Command sender mapper + */ + public final @NonNull Function<@NonNull MessageReceivedEvent, @NonNull C> getCommandSenderMapper() { + return this.commandSenderMapper; + } + + /** + * Get the bots discord id + * + * @return Bots discord id + */ + public final long getBotId() { + return botId; + } + + @Override + public final boolean hasPermission(final @NonNull C sender, final @NonNull String permission) { + if (permission.isEmpty()) { + return true; + } + + MessageReceivedEvent message = backwardsCommandSenderMapper.apply(sender); + Member member = message.getMember(); + if (member == null) { + return false; + } + + return member.hasPermission(Permission.valueOf(permission)); + } + + @Override + public final @NonNull CommandMeta createDefaultCommandMeta() { + return SimpleCommandMeta.empty(); + } +} diff --git a/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/JDACommandSender.java b/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/JDACommandSender.java new file mode 100644 index 00000000..969acc6a --- /dev/null +++ b/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/JDACommandSender.java @@ -0,0 +1,67 @@ +// +// MIT License +// +// Copyright (c) 2020 Alexander Söderberg & Contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +package cloud.commandframework.jda; + +import net.dv8tion.jda.api.entities.ChannelType; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Wrapper for {@link MessageReceivedEvent} + */ +public class JDACommandSender { + private final MessageReceivedEvent event; + + /** + * Construct a JDA Command Sender using an event + * + * @param event Message Received Event + */ + public JDACommandSender(final @NonNull MessageReceivedEvent event) { + this.event = event; + } + + /** + * Get the {@link MessageReceivedEvent} + * + * @return Message Received Event + */ + public @NonNull MessageReceivedEvent getEvent() { + return event; + } + + /** + * Create a JDA Command Sender from a {@link MessageReceivedEvent} + * + * @param event Message Received Event + * @return Constructed JDA Command Sender + */ + public static JDACommandSender of(final @NonNull MessageReceivedEvent event) { + if (event.isFromType(ChannelType.PRIVATE)) { + return new JDAPrivateSender(event); + } + + return new JDAGuildSender(event); + } +} diff --git a/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/JDAGuildSender.java b/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/JDAGuildSender.java new file mode 100644 index 00000000..6c9bb691 --- /dev/null +++ b/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/JDAGuildSender.java @@ -0,0 +1,37 @@ +// +// MIT License +// +// Copyright (c) 2020 Alexander Söderberg & Contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +package cloud.commandframework.jda; + +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Guild specific JDA Command Sender + */ +public class JDAGuildSender extends JDACommandSender { + + JDAGuildSender(final @NonNull MessageReceivedEvent event) { + super(event); + } +} diff --git a/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/JDAPrivateSender.java b/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/JDAPrivateSender.java new file mode 100644 index 00000000..5c793318 --- /dev/null +++ b/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/JDAPrivateSender.java @@ -0,0 +1,37 @@ +// +// MIT License +// +// Copyright (c) 2020 Alexander Söderberg & Contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +package cloud.commandframework.jda; + +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Private message specific JDA Command Sender + */ +public class JDAPrivateSender extends JDACommandSender { + + JDAPrivateSender(final @NonNull MessageReceivedEvent event) { + super(event); + } +} diff --git a/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/package-info.java b/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/package-info.java new file mode 100644 index 00000000..528bca2e --- /dev/null +++ b/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/package-info.java @@ -0,0 +1,28 @@ +// +// MIT License +// +// Copyright (c) 2020 Alexander Söderberg & Contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** + * cloud implementation for Bukkit 1.8-1.16 + */ +package cloud.commandframework.jda; diff --git a/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/parsers/UserArgument.java b/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/parsers/UserArgument.java new file mode 100644 index 00000000..6ff5c9c6 --- /dev/null +++ b/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/parsers/UserArgument.java @@ -0,0 +1,279 @@ +// +// MIT License +// +// Copyright (c) 2020 Alexander Söderberg & Contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +package cloud.commandframework.jda.parsers; + +import cloud.commandframework.arguments.CommandArgument; +import cloud.commandframework.arguments.parser.ArgumentParseResult; +import cloud.commandframework.arguments.parser.ArgumentParser; +import cloud.commandframework.context.CommandContext; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.User; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; + +/** + * Command Argument for {@link User} + * + * @param Command sender type + */ +@SuppressWarnings("unused") +public final class UserArgument extends CommandArgument { + private final List modes; + + private UserArgument(final boolean required, final @NonNull String name, + final @NonNull JDA jda, final @NonNull List modes) { + super(required, name, new UserParser<>(jda, modes), User.class); + this.modes = modes; + } + + /** + * Create a new builder + * + * @param name Name of the component + * @param jda JDA instance + * @param Command sender type + * @return Created builder + */ + public static @NonNull Builder newBuilder(final @NonNull String name, final @NonNull JDA jda) { + return new Builder<>(name, jda); + } + + /** + * Create a new required command component + * + * @param name Component name + * @param jda JDA instance + * @param Command sender type + * @return Created component + */ + public static @NonNull CommandArgument of(final @NonNull String name, final @NonNull JDA jda) { + return UserArgument.newBuilder(name, jda).asRequired().build(); + } + + /** + * Create a new optional command component + * + * @param name Component name + * @param jda JDA instance + * @param Command sender type + * @return Created component + */ + public static @NonNull CommandArgument optional(final @NonNull String name, final @NonNull JDA jda) { + return UserArgument.newBuilder(name, jda).asOptional().build(); + } + + + public enum ParserMode { + MENTION, + ID, + NAME + } + + /** + * Get the modes enabled on the parser + * + * @return List of Modes + */ + public @NotNull List getModes() { + return modes; + } + + public static final class Builder extends CommandArgument.Builder { + private final JDA jda; + private List modes = new ArrayList<>(); + + protected Builder(final @NonNull String name, final @NonNull JDA jda) { + super(User.class, name); + this.jda = jda; + } + + /** + * Set the modes for the parsers to use + * + * @param modes List of Modes + * @return Builder instance + */ + public @NonNull Builder withParsers(final @NonNull List modes) { + this.modes = modes; + return this; + } + + /** + * Builder a new example component + * + * @return Constructed component + */ + @Override + public @NonNull UserArgument build() { + return new UserArgument<>(this.isRequired(), this.getName(), jda, modes); + } + + } + + + public static final class UserParser implements ArgumentParser { + private final JDA jda; + private final List modes; + + private UserParser(final @NonNull JDA jda, final @NonNull List modes) { + this.jda = jda; + this.modes = modes; + } + + @Override + public @NonNull ArgumentParseResult parse( + final @NonNull CommandContext commandContext, + final @NonNull Queue<@NonNull String> inputQueue) { + final String input = inputQueue.peek(); + if (input == null) { + return ArgumentParseResult.failure(new NullPointerException("No input was provided")); + } + + Exception exception = null; + + if (modes.contains(ParserMode.MENTION)) { + if (input.endsWith(">")) { + String id; + if (input.startsWith("<@!")) { + id = input.substring(3, input.length() - 1); + } else { + id = input.substring(2, input.length() - 1); + } + + try { + final ArgumentParseResult result = userFromId(input, id); + inputQueue.remove(); + return result; + } catch (UserNotFoundParseException | NumberFormatException e) { + exception = e; + } + } + } + + if (modes.contains(ParserMode.ID)) { + try { + final ArgumentParseResult result = userFromId(input, input); + inputQueue.remove(); + return result; + } catch (UserNotFoundParseException | NumberFormatException e) { + exception = e; + } + } + + if (modes.contains(ParserMode.NAME)) { + List users = jda.getUsersByName(input, true); + + if (users.size() == 0) { + exception = new UserNotFoundParseException(input); + } else if (users.size() > 1) { + exception = new TooManyUsersFoundParseException(input); + } else { + inputQueue.remove(); + return ArgumentParseResult.success(users.get(0)); + } + } + + assert exception != null; + return ArgumentParseResult.failure(exception); + } + + @Override + public boolean isContextFree() { + return true; + } + + private @NonNull ArgumentParseResult userFromId(final @NonNull String input, final @NonNull String id) + throws UserNotFoundParseException, NumberFormatException { + User user = jda.getUserById(id); + + if (user == null) { + throw new UserNotFoundParseException(input); + } else { + return ArgumentParseResult.success(user); + } + } + } + + + public static class UserParseException extends IllegalArgumentException { + + private final String input; + + /** + * Construct a new UUID parse exception + * + * @param input String input + */ + public UserParseException(final @NonNull String input) { + this.input = input; + } + + /** + * Get the users input + * + * @return Users input + */ + public final @NonNull String getInput() { + return input; + } + } + + + public static final class TooManyUsersFoundParseException extends UserParseException { + /** + * Construct a new UUID parse exception + * + * @param input String input + */ + public TooManyUsersFoundParseException(final @NonNull String input) { + super(input); + } + + @Override + public @NonNull String getMessage() { + return String.format("Too many users found for '%s'.", getInput()); + } + } + + + public static final class UserNotFoundParseException extends UserParseException { + /** + * Construct a new UUID parse exception + * + * @param input String input + */ + public UserNotFoundParseException(final @NonNull String input) { + super(input); + } + + @Override + public @NonNull String getMessage() { + return String.format("User not found for '%s'.", getInput()); + } + } +} diff --git a/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/parsers/package-info.java b/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/parsers/package-info.java new file mode 100644 index 00000000..867e44b9 --- /dev/null +++ b/cloud-discord/cloud-jda/src/main/java/cloud/commandframework/jda/parsers/package-info.java @@ -0,0 +1,28 @@ +// +// MIT License +// +// Copyright (c) 2020 Alexander Söderberg & Contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** + * JDA specific command arguments + */ +package cloud.commandframework.jda.parsers; diff --git a/settings.gradle b/settings.gradle index a715227d..b64670ff 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,6 +10,7 @@ include(':cloud-bungee') include(':cloud-velocity') include(':cloud-minecraft-extras') include(':cloud-cloudburst') +include(':cloud-jda') project(':cloud-bukkit').projectDir = file('cloud-minecraft/cloud-bukkit') project(':cloud-paper').projectDir = file('cloud-minecraft/cloud-paper') project(':cloud-brigadier').projectDir = file('cloud-minecraft/cloud-brigadier') @@ -17,3 +18,4 @@ project(':cloud-bungee').projectDir = file('cloud-minecraft/cloud-bungee') project(':cloud-velocity').projectDir = file('cloud-minecraft/cloud-velocity') project(':cloud-minecraft-extras').projectDir = file('cloud-minecraft/cloud-minecraft-extras') project(':cloud-cloudburst').projectDir = file('cloud-minecraft/cloud-cloudburst') +project(':cloud-jda').projectDir = file('cloud-discord/cloud-jda')