change how events are handled by using a single class to register listeners onto
This commit is contained in:
1 parent
e0612f90a8
commit
e58854a85c
7 files changed
+92
-476
No files matched your search
@@ -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<AsyncContext> consumer) {
|
||||
reply("Bot is thinking...", consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reply(MessageCreateData messageCreateData, boolean ephemeral, List<LayoutComponent> components, Consumer<AsyncContext> 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<CommandOption> parseOptions(String[] args, List<OptionData> commandOptions) {
|
||||
List<CommandOption> 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<ChannelType> requiredChannelTypes = data.getChannelTypes();
|
||||
List<Command.Choice> 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<Command.Choice> 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<Command.Choice> 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<Command.Choice> 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<Command.Choice> choices) {
|
||||
StringJoiner joiner = new StringJoiner(", ");
|
||||
for (Command.Choice choice : choices) {
|
||||
joiner.add(choice.getAsString());
|
||||
}
|
||||
|
||||
return joiner.toString();
|
||||
}
|
||||
|
||||
private String validTypes(Set<ChannelType> 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();
|
||||
}
|
||||
}
|
||||
@@ -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<CommandOption> parseOptions(String[] args, List<OptionData> 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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user