From e58854a85c9541604f9b52bd6fd95101d0cde10e Mon Sep 17 00:00:00 2001 From: anthorob Date: Tue, 31 Dec 2024 20:45:16 -0500 Subject: [PATCH] change how events are handled by using a single class to register listeners onto --- .../context/MessageCommandContext.java | 338 ------------------ .../context/SlashCommandContext.java | 72 ---- .../command/AbstractMessageMenuCommand.java | 46 +++ .../core/command/AbstractUserMenuCommand.java | 46 +++ .../interaction/ButtonInteractionHook.java | 22 -- .../interaction/ModalInteractionHook.java | 22 -- .../hooks/message/EmojiReactionHook.java | 22 -- 7 files changed, 92 insertions(+), 476 deletions(-) delete mode 100644 src/main/java/me/bobert/netneutrality/context/MessageCommandContext.java delete mode 100644 src/main/java/me/bobert/netneutrality/context/SlashCommandContext.java create mode 100644 src/main/java/me/bobert/netneutrality/core/command/AbstractMessageMenuCommand.java create mode 100644 src/main/java/me/bobert/netneutrality/core/command/AbstractUserMenuCommand.java delete mode 100644 src/main/java/me/bobert/netneutrality/hooks/interaction/ButtonInteractionHook.java delete mode 100644 src/main/java/me/bobert/netneutrality/hooks/interaction/ModalInteractionHook.java delete mode 100644 src/main/java/me/bobert/netneutrality/hooks/message/EmojiReactionHook.java diff --git a/src/main/java/me/bobert/netneutrality/context/MessageCommandContext.java b/src/main/java/me/bobert/netneutrality/context/MessageCommandContext.java deleted file mode 100644 index b319386..0000000 --- a/src/main/java/me/bobert/netneutrality/context/MessageCommandContext.java +++ /dev/null @@ -1,338 +0,0 @@ -package me.bobert.netneutrality.context; -/* - * Copyright (c) anthorob - * - * https://discord.gg/qUa6zNFZsd - */ - -import me.bobert.netneutrality.core.command.options.CommandOption; -import me.bobert.netneutrality.core.command.options.MessageCommandOption; -import me.bobert.netneutrality.core.context.CommandContext; -import me.bobert.netneutrality.utils.Settings; -import net.dv8tion.jda.api.entities.*; -import net.dv8tion.jda.api.entities.channel.ChannelType; -import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; -import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import net.dv8tion.jda.api.interactions.InteractionHook; -import net.dv8tion.jda.api.interactions.commands.Command; -import net.dv8tion.jda.api.interactions.commands.OptionType; -import net.dv8tion.jda.api.interactions.commands.build.OptionData; -import net.dv8tion.jda.api.interactions.components.LayoutComponent; -import net.dv8tion.jda.api.interactions.modals.Modal; -import net.dv8tion.jda.api.utils.messages.MessageCreateData; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.StringJoiner; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -public class MessageCommandContext extends CommandContext { - private final MessageReceivedEvent event; - private final Message message; - - public MessageCommandContext(MessageReceivedEvent event) { - super(event.getJDA(), event.isFromGuild() ? event.getGuild() : null, event.getChannel(), event.getMember(), event.getAuthor()); - this.event = event; - this.message = event.getMessage(); - } - - @Override - public MessageReceivedEvent getEvent() { - return event; - } - - @Override - public String getId() { - return String.format("%s:%s:%s:%s", - getGuild() == null ? "PrivateMessage" : getGuild().getId(), - getChannel().getId(), - getDiscordUser().getId(), - message.getId()); - } - - @Override - public ChannelType getType() { - return event.getChannelType(); - } - - public Message getMessage() { - return message; - } - - @Override - public boolean isAcknowledged() { - return true; - } - - @Override - public boolean isFromGuild() { - return event.isFromGuild(); - } - - @Override - public InteractionHook getInteractionHook() { - return null; - } - - @Override - public void defer(boolean ephemeral, Consumer consumer) { - reply("Bot is thinking...", consumer); - } - - @Override - public void reply(MessageCreateData messageCreateData, boolean ephemeral, List components, Consumer consumer) { - if (ephemeral) { - sendMessageInPrivate(messageCreateData, components); - return; - } - - if (getChannel() != null && getChannel().canTalk()) { - message.reply(messageCreateData).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message))); - } - } - - @Override - public void reply(Modal modal) { - - } - - @Override - public void ack(boolean ephemeral) { - - } - - @Override - public void deleteMessage() { - if (message != null && getType() != ChannelType.PRIVATE) { - message.delete().queueAfter(1, TimeUnit.SECONDS); - } - } - - @Override - public List parseOptions(String[] args, List commandOptions) { - List options = new ArrayList<>(); - Mentions mentions = message.getMentions(); - int roleMentionIndex = 0; - int userMentionIndex = 0; - int memberMentionIndex = 0; - int channelMentionIndex = 0; - int index = 0; - - for (OptionData data : commandOptions) { - OptionType type = data.getType(); - Set requiredChannelTypes = data.getChannelTypes(); - List choices = data.getChoices(); - Object value = null; - - try { - switch (type) { - case STRING -> { - String str = args[index]; - - if ((str.contains("<@") || str.contains("<#")) && str.contains(">")) { - invalidateContext(data, "expected a string at position " + (index + 1) + " but a role/user/channel was provided"); - break; - } - - if (!choices.isEmpty()) { - List matched = choices.stream().filter(choice -> choice.getAsString().equalsIgnoreCase(str)).toList(); - if (matched.isEmpty()) { - invalidateContext(data, "not a valid choice, valid choices are: " + validChoices(choices)); - } - } - - Integer min = data.getMinLength(); - Integer max = data.getMaxLength(); - - if (min != null && str.length() < min) { - invalidateContext(data, "minimum length for argument is " + min); - } - - if (max != null && str.length() > max) { - invalidateContext(data, "maximum length for argument is " + max); - } - - value = str; - } - case INTEGER -> { - long num = Long.parseLong(args[index]); - if (!choices.isEmpty()) { - List matched = choices.stream().filter(choice -> choice.getAsLong() == num).toList(); - if (matched.isEmpty()) { - invalidateContext(data, "not a valid choice, valid choices are: " + validChoices(choices)); - } - } - value = num; - - Number max = data.getMaxValue(); - Number min = data.getMinValue(); - - if (min != null && num < min.longValue()) { - value = min.longValue(); - } else if (max != null && num > max.longValue()) { - value = max.longValue(); - } - - value = num; - } - case BOOLEAN -> { - String str = args[index]; - if (str.equalsIgnoreCase("true") || str.equalsIgnoreCase("false")) { - value = Boolean.parseBoolean(str); - break; - } - - invalidateContext(data, "expected true or false"); - } - case NUMBER -> { - double num = Long.parseLong(args[index]); - if (!choices.isEmpty()) { - List matched = choices.stream().filter(choice -> choice.getAsDouble() == num).toList(); - if (matched.isEmpty()) { - invalidateContext(data, "not a valid choice, valid choices are: " + validChoices(choices)); - break; - } - } - - Number max = data.getMaxValue(); - Number min = data.getMinValue(); - value = num; - - if (min != null && num < min.doubleValue()) { - value = min.doubleValue(); - } else if (max != null && num > max.doubleValue()) { - value = max.doubleValue(); - } - } - case ROLE -> { - if (mentions.getRoles().size() > roleMentionIndex) { - Role role = mentions.getRoles().get(roleMentionIndex++); - if (role.getGuild().getId().equals(getGuild().getId())) { - value = role; - } - - break; - } - - invalidateContext(data, "provided argument at position " + (index + 1) + " is not a valid role."); - } - case USER -> { - if (mentions.getMembers().size() > memberMentionIndex) { - Member member = mentions.getMembers().get(memberMentionIndex++); - if (member.getGuild().getId().equals(getGuild().getId())) { - value = member; - break; - } - } - - if (mentions.getUsers().size() > userMentionIndex) { - value = mentions.getUsers().get(userMentionIndex++); - break; - } - - invalidateContext(data, "provided argument at position " + (index + 1) + " is not a valid user."); - } - case MENTIONABLE -> { - if (mentions.getRoles().size() > roleMentionIndex) { - Role role = mentions.getRoles().get(roleMentionIndex++); - if (role.getGuild().getId().equals(getGuild().getId())) { - value = role; - } - - break; - } - - if (mentions.getMembers().size() > memberMentionIndex) { - Member member = mentions.getMembers().get(memberMentionIndex++); - if (member.getGuild().getId().equals(getGuild().getId())) { - value = member; - } - - break; - } - - if (mentions.getUsers().size() > userMentionIndex) { - value = mentions.getUsers().get(userMentionIndex++); - break; - } - - invalidateContext(data, "provided argument at position " + (index + 1) + " is not a valid user or role."); - } - case CHANNEL -> { - if (mentions.getChannels().size() > channelMentionIndex) { - GuildChannel channel = mentions.getChannels().get(channelMentionIndex++); - - if (!requiredChannelTypes.contains(channel.getType())) { - invalidateContext(data, "not a valid channel type at index " + (index + 1) + - " , valid types are: " + validTypes(requiredChannelTypes)); - break; - } - - if (channel.getGuild().getId().equals(getGuild().getId())) { - value = channel; - break; - } - } - - invalidateContext(data, "provided argument at position " + (index + 1) + " is not a valid channel."); - } - } - } catch (IndexOutOfBoundsException e) { - if (data.isRequired()) { - // This should never happen because required options are validated before calling parseOptions() - throw new IllegalArgumentException("Required option " + data.getName() + " was not found at index " + index); - } - } catch (NumberFormatException ignored) { - invalidateContext(data, "the argument provided is not a number."); - } - - if (value != null) { - options.add(new MessageCommandOption(data, value)); - } - - index++; - } - - return options; - } - - private String validChoices(List choices) { - StringJoiner joiner = new StringJoiner(", "); - for (Command.Choice choice : choices) { - joiner.add(choice.getAsString()); - } - - return joiner.toString(); - } - - private String validTypes(Set types) { - StringJoiner joiner = new StringJoiner(", "); - for (ChannelType type : types) { - joiner.add(type.name()); - } - - return joiner.toString(); - } - - @Override - public String parseCommandName() { - String rawMessage = message.getContentRaw().trim(); - String prefix = Settings.getInstance().getPrefix(); - - if (rawMessage.startsWith(prefix)) { - return getFullCommand().split(" ", 2)[0]; - } - - return null; - } - - @Override - public String getFullCommand() { - String rawMessage = message.getContentRaw().trim(); - String prefix = Settings.getInstance().getPrefix(); - - return rawMessage.replaceFirst("^[" + prefix + "\\s]+", "").trim(); - } -} diff --git a/src/main/java/me/bobert/netneutrality/context/SlashCommandContext.java b/src/main/java/me/bobert/netneutrality/context/SlashCommandContext.java deleted file mode 100644 index 275a9c1..0000000 --- a/src/main/java/me/bobert/netneutrality/context/SlashCommandContext.java +++ /dev/null @@ -1,72 +0,0 @@ -package me.bobert.netneutrality.context; -/* - * Copyright (c) anthorob - * - * https://discord.gg/qUa6zNFZsd - */ - -import me.bobert.netneutrality.core.command.options.CommandOption; -import me.bobert.netneutrality.core.command.options.SlashCommandOption; -import me.bobert.netneutrality.core.context.CommandContext; -import me.bobert.netneutrality.core.context.InteractionContext; -import net.dv8tion.jda.api.entities.channel.ChannelType; -import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; -import net.dv8tion.jda.api.interactions.commands.build.OptionData; -import net.dv8tion.jda.api.interactions.modals.Modal; - -import java.util.List; -import java.util.stream.Collectors; - -public class SlashCommandContext extends CommandContext implements InteractionContext { - private final SlashCommandInteractionEvent event; - - public SlashCommandContext(SlashCommandInteractionEvent event) { - super(event.getJDA(), event.getGuild(), event.getChannel(), event.getMember(), event.getUser()); - this.event = event; - } - - @Override - public SlashCommandInteractionEvent getEvent() { - return event; - } - - @Override - public ChannelType getType() { - return event.getChannelType(); - } - - @Override - public void deleteMessage() { - // not implemented - } - - @Override - public void reply(Modal modal) { - event.replyModal(modal).queue(); - } - - @Override - public List parseOptions(String[] args, List commandOptions) { - return event.getOptions().stream().map(SlashCommandOption::new).collect(Collectors.toUnmodifiableList()); - } - - @Override - public String parseCommandName() { - return event.getName(); - } - - @Override - public String getFullCommand() { - return event.getFullCommandName(); - } - - @Override - public String getSubcommandGroup() { - return event.getSubcommandGroup(); - } - - @Override - public String getSubcommand() { - return event.getSubcommandName(); - } -} diff --git a/src/main/java/me/bobert/netneutrality/core/command/AbstractMessageMenuCommand.java b/src/main/java/me/bobert/netneutrality/core/command/AbstractMessageMenuCommand.java new file mode 100644 index 0000000..3ea7a84 --- /dev/null +++ b/src/main/java/me/bobert/netneutrality/core/command/AbstractMessageMenuCommand.java @@ -0,0 +1,46 @@ +package me.bobert.netneutrality.core.command; + +/* + * Copyright (c) anthorob + * + * https://discord.gg/qUa6zNFZsd + */ + +import me.bobert.netneutrality.context.command.MessageMenuContext; +import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder; +import me.bobert.netneutrality.core.context.CommandContext; +import net.dv8tion.jda.api.entities.Message; + +public abstract class AbstractMessageMenuCommand extends AbstractCommand { + @Override + public String getDescription() { + return null; + } + + @Override + public boolean isSlashCommandOnly() { + return false; + } + + @Override + public void registerCommandOptions(CommandOptionsBuilder builder) { + // no options for context commands + } + + @Override + public Type getType() { + return Type.MESSAGE; + } + + @Override + public CommandExecutionResult execute(CommandContext context) { + if (context instanceof MessageMenuContext mc) { + Message message = mc.getTarget(); + return execute(message, context); + } + + throw new IllegalArgumentException("Wrong context type for this command"); + } + + public abstract CommandExecutionResult execute(Message target, CommandContext context); +} diff --git a/src/main/java/me/bobert/netneutrality/core/command/AbstractUserMenuCommand.java b/src/main/java/me/bobert/netneutrality/core/command/AbstractUserMenuCommand.java new file mode 100644 index 0000000..c9b2625 --- /dev/null +++ b/src/main/java/me/bobert/netneutrality/core/command/AbstractUserMenuCommand.java @@ -0,0 +1,46 @@ +package me.bobert.netneutrality.core.command; + +/* + * Copyright (c) anthorob + * + * https://discord.gg/qUa6zNFZsd + */ + +import me.bobert.netneutrality.context.command.UserMenuContext; +import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder; +import me.bobert.netneutrality.core.context.CommandContext; +import net.dv8tion.jda.api.entities.User; + +public abstract class AbstractUserMenuCommand extends AbstractCommand { + @Override + public String getDescription() { + return null; + } + + @Override + public boolean isSlashCommandOnly() { + return false; + } + + @Override + public void registerCommandOptions(CommandOptionsBuilder builder) { + // no options for context commands + } + + @Override + public Type getType() { + return Type.USER; + } + + @Override + public CommandExecutionResult execute(CommandContext context) { + if (context instanceof UserMenuContext uc) { + User user = uc.getTarget(); + return execute(user, context); + } + + throw new IllegalArgumentException("Wrong context type for this command"); + } + + public abstract CommandExecutionResult execute(User target, CommandContext context); +} diff --git a/src/main/java/me/bobert/netneutrality/hooks/interaction/ButtonInteractionHook.java b/src/main/java/me/bobert/netneutrality/hooks/interaction/ButtonInteractionHook.java deleted file mode 100644 index 14aa283..0000000 --- a/src/main/java/me/bobert/netneutrality/hooks/interaction/ButtonInteractionHook.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.bobert.netneutrality.hooks.interaction; -/* - * Copyright (c) anthorob - * - * https://discord.gg/qUa6zNFZsd - */ - -import me.bobert.netneutrality.context.tasks.ButtonTaskContext; -import me.bobert.netneutrality.core.tasks.TaskManager; -import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; -import net.dv8tion.jda.api.hooks.ListenerAdapter; -import org.jetbrains.annotations.NotNull; - -public class ButtonInteractionHook extends ListenerAdapter { - - @Override - public void onButtonInteraction(@NotNull ButtonInteractionEvent event) { - ButtonTaskContext context = new ButtonTaskContext(event); - - TaskManager.getInstance().handle(context); - } -} diff --git a/src/main/java/me/bobert/netneutrality/hooks/interaction/ModalInteractionHook.java b/src/main/java/me/bobert/netneutrality/hooks/interaction/ModalInteractionHook.java deleted file mode 100644 index 203af42..0000000 --- a/src/main/java/me/bobert/netneutrality/hooks/interaction/ModalInteractionHook.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.bobert.netneutrality.hooks.interaction; -/* - * Copyright (c) anthorob - * - * https://discord.gg/qUa6zNFZsd - */ - -import me.bobert.netneutrality.context.tasks.ModalTaskContext; -import me.bobert.netneutrality.core.tasks.TaskManager; -import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; -import net.dv8tion.jda.api.hooks.ListenerAdapter; -import org.jetbrains.annotations.NotNull; - -public class ModalInteractionHook extends ListenerAdapter { - - @Override - public void onModalInteraction(@NotNull ModalInteractionEvent event) { - ModalTaskContext context = new ModalTaskContext(event); - - TaskManager.getInstance().handle(context); - } -} diff --git a/src/main/java/me/bobert/netneutrality/hooks/message/EmojiReactionHook.java b/src/main/java/me/bobert/netneutrality/hooks/message/EmojiReactionHook.java deleted file mode 100644 index 7fb8483..0000000 --- a/src/main/java/me/bobert/netneutrality/hooks/message/EmojiReactionHook.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.bobert.netneutrality.hooks.message; -/* - * Copyright (c) anthorob - * - * https://discord.gg/qUa6zNFZsd - */ - -import me.bobert.netneutrality.context.tasks.EmojiTaskContext; -import me.bobert.netneutrality.core.tasks.TaskManager; -import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; -import net.dv8tion.jda.api.hooks.ListenerAdapter; -import org.jetbrains.annotations.NotNull; - -public class EmojiReactionHook extends ListenerAdapter { - - @Override - public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { - EmojiTaskContext context = new EmojiTaskContext(event); - TaskManager.getInstance().handle(context); - } - -}