From 3bb4fa8b0f787b5440a7fb9a6fee67d4b7fe66bc Mon Sep 17 00:00:00 2001 From: Len <40720638+destro174@users.noreply.github.com> Date: Fri, 9 Sep 2022 15:21:09 +0200 Subject: [PATCH 01/27] Update build.gradle.kts --- build.gradle.kts | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 078cd2a..bc91869 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -38,22 +38,18 @@ tasks { } } -// create("relocateJars") { -// target = shadowJar.get() -// prefix = "${project.name}.lib" -// } -// -// shadowJar { -// dependsOn(getByName("relocateJars") as ConfigureShadowRelocation) -// archiveFileName.set("${project.name}-${project.version}.jar") -// minimize() -// configurations = listOf(project.configurations.shadow.get()) -// } -// + shadowJar { + archiveFileName.set(rootProject.name + ".jar") + } + build { dependsOn(shadowJar) } + jar { + enabled = false + } + } dependencies { From d371b44a66dfffecae8415510d75da1c39ad4328 Mon Sep 17 00:00:00 2001 From: Len <40720638+destro174@users.noreply.github.com> Date: Fri, 9 Sep 2022 15:31:14 +0200 Subject: [PATCH 02/27] Update JDA --- build.gradle.kts | 2 +- .../commandManager/commands/AddCommand/CommandManage.java | 4 ++-- .../commands/AddCommand/SubCommandGroupSet.java | 2 +- .../commands/AddCommand/SubCommandGroupUnset.java | 2 +- .../java/com/alttd/commandManager/commands/CommandHelp.java | 4 ++-- .../commandManager/commands/PollCommand/CommandPoll.java | 4 ++-- src/main/java/com/alttd/util/OptionMappingParsing.java | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index bc91869..8c0033e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -54,7 +54,7 @@ tasks { dependencies { // JDA - implementation("net.dv8tion:JDA:5.0.0-alpha.10") { + implementation("net.dv8tion:JDA:5.0.0-alpha.18") { exclude("opus-java") // exclude audio } // MySQL diff --git a/src/main/java/com/alttd/commandManager/commands/AddCommand/CommandManage.java b/src/main/java/com/alttd/commandManager/commands/AddCommand/CommandManage.java index 0a8a304..28b9bab 100644 --- a/src/main/java/com/alttd/commandManager/commands/AddCommand/CommandManage.java +++ b/src/main/java/com/alttd/commandManager/commands/AddCommand/CommandManage.java @@ -52,7 +52,7 @@ public class CommandManage extends DiscordCommand { .addOption(OptionType.CHANNEL, "channel", "Channel to disable this command in", true) .addOption(OptionType.STRING, "command", "Name of the command to disable", true, true) ); - slashCommandData.setDefaultEnabled(true); +// slashCommandData.setDefaultEnabled(true); Util.registerSubOptions(subOptionsMap, new SubCommandDisable(commandManager, null, this), new SubCommandEnable(commandManager, null, this)); @@ -70,7 +70,7 @@ public class CommandManage extends DiscordCommand { event.replyEmbeds(Util.guildOnlyCommand(getName())).setEphemeral(true).queue(); return; } - if (PermissionManager.getInstance().hasPermission(event.getTextChannel(), event.getIdLong(), Util.getGroupIds(event.getMember()), getPermission())) { + if (PermissionManager.getInstance().hasPermission(event.getChannel().asTextChannel(), event.getIdLong(), Util.getGroupIds(event.getMember()), getPermission())) { event.replyEmbeds(Util.noPermission(getName())).setEphemeral(true).queue(); return; } diff --git a/src/main/java/com/alttd/commandManager/commands/AddCommand/SubCommandGroupSet.java b/src/main/java/com/alttd/commandManager/commands/AddCommand/SubCommandGroupSet.java index f672bdf..9055897 100644 --- a/src/main/java/com/alttd/commandManager/commands/AddCommand/SubCommandGroupSet.java +++ b/src/main/java/com/alttd/commandManager/commands/AddCommand/SubCommandGroupSet.java @@ -28,7 +28,7 @@ public class SubCommandGroupSet extends SubCommandGroup { @Override public void execute(SlashCommandInteractionEvent event) { - if (PermissionManager.getInstance().hasPermission(event.getTextChannel(), event.getIdLong(), Util.getGroupIds(event.getMember()), getPermission())) { + if (PermissionManager.getInstance().hasPermission(event.getChannel().asTextChannel(), event.getIdLong(), Util.getGroupIds(event.getMember()), getPermission())) { event.replyEmbeds(Util.noPermission(getName())).setEphemeral(true).queue(); return; } diff --git a/src/main/java/com/alttd/commandManager/commands/AddCommand/SubCommandGroupUnset.java b/src/main/java/com/alttd/commandManager/commands/AddCommand/SubCommandGroupUnset.java index 26fc722..6e9e845 100644 --- a/src/main/java/com/alttd/commandManager/commands/AddCommand/SubCommandGroupUnset.java +++ b/src/main/java/com/alttd/commandManager/commands/AddCommand/SubCommandGroupUnset.java @@ -28,7 +28,7 @@ public class SubCommandGroupUnset extends SubCommandGroup { @Override public void execute(SlashCommandInteractionEvent event) { - if (PermissionManager.getInstance().hasPermission(event.getTextChannel(), event.getIdLong(), Util.getGroupIds(event.getMember()), getPermission())) { + if (PermissionManager.getInstance().hasPermission(event.getChannel().asTextChannel(), event.getIdLong(), Util.getGroupIds(event.getMember()), getPermission())) { event.replyEmbeds(Util.noPermission(getName())).setEphemeral(true).queue(); return; } diff --git a/src/main/java/com/alttd/commandManager/commands/CommandHelp.java b/src/main/java/com/alttd/commandManager/commands/CommandHelp.java index a256070..19efa52 100644 --- a/src/main/java/com/alttd/commandManager/commands/CommandHelp.java +++ b/src/main/java/com/alttd/commandManager/commands/CommandHelp.java @@ -45,14 +45,14 @@ public class CommandHelp extends DiscordCommand { public void execute(SlashCommandInteractionEvent event) { PermissionManager permissionManager = PermissionManager.getInstance(); - if (permissionManager.hasPermission(event.getTextChannel(), event.getIdLong(), Util.getGroupIds(event.getMember()), getPermission())) { + if (permissionManager.hasPermission(event.getChannel().asTextChannel(), event.getIdLong(), Util.getGroupIds(event.getMember()), getPermission())) { event.replyEmbeds(Util.noPermission(getName())).setEphemeral(true).queue(); return; } StringBuilder helpMessage = new StringBuilder(); List options = event.getOptions(); - TextChannel textChannel = event.getTextChannel(); + TextChannel textChannel = event.getChannel().asTextChannel(); if (options.size() == 0) { commandManager.getCommands(textChannel).stream() .filter(command -> permissionManager.hasPermission( diff --git a/src/main/java/com/alttd/commandManager/commands/PollCommand/CommandPoll.java b/src/main/java/com/alttd/commandManager/commands/PollCommand/CommandPoll.java index dbd0dc5..dc83025 100644 --- a/src/main/java/com/alttd/commandManager/commands/PollCommand/CommandPoll.java +++ b/src/main/java/com/alttd/commandManager/commands/PollCommand/CommandPoll.java @@ -53,7 +53,7 @@ public class CommandPoll extends DiscordCommand { new SubcommandData("results", "Get the results for a poll") .addOption(OptionType.CHANNEL, "channel", "Channel this poll is in", true) .addOption(OptionType.INTEGER, "message_id", "Id of the poll you want the results for", true)); - slashCommandData.setDefaultEnabled(true); +// slashCommandData.setDefaultEnabled(true); Util.registerSubOptions(subOptionsMap, new SubCommandAdd(null,this), new SubCommandAddButton(null, this), @@ -77,7 +77,7 @@ public class CommandPoll extends DiscordCommand { event.replyEmbeds(Util.guildOnlyCommand(getName())).setEphemeral(true).queue(); return; } - if (PermissionManager.getInstance().hasPermission(event.getTextChannel(), event.getIdLong(), Util.getGroupIds(event.getMember()), getPermission())) { + if (PermissionManager.getInstance().hasPermission(event.getChannel().asTextChannel(), event.getIdLong(), Util.getGroupIds(event.getMember()), getPermission())) { event.replyEmbeds(Util.noPermission(getName())).setEphemeral(true).queue(); return; } diff --git a/src/main/java/com/alttd/util/OptionMappingParsing.java b/src/main/java/com/alttd/util/OptionMappingParsing.java index b3f7c93..086c237 100644 --- a/src/main/java/com/alttd/util/OptionMappingParsing.java +++ b/src/main/java/com/alttd/util/OptionMappingParsing.java @@ -16,7 +16,7 @@ public class OptionMappingParsing { public static GuildMessageChannel getGuildChannel(String optionName, SlashCommandInteractionEvent event, String commandName) { OptionMapping optionMappingChannel = event.getInteraction().getOption(optionName); - GuildMessageChannel messageChannel = optionMappingChannel == null ? null : optionMappingChannel.getAsMessageChannel(); + GuildMessageChannel messageChannel = optionMappingChannel == null ? null : optionMappingChannel.getAsChannel().asGuildMessageChannel(); if (messageChannel == null) event.replyEmbeds(Util.invalidCommand(commandName, "Not a valid text channel or didn't give input for " + optionName, event.getInteraction())).setEphemeral(true).queue(); return messageChannel; From 1157b90d51af2a5b88fffa8a420fe3460242447c Mon Sep 17 00:00:00 2001 From: Len <40720638+destro174@users.noreply.github.com> Date: Fri, 9 Sep 2022 16:15:13 +0200 Subject: [PATCH 03/27] Add lombok --- build.gradle.kts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 8c0033e..15783af 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -63,4 +63,7 @@ dependencies { // Configurate implementation("org.spongepowered:configurate-yaml:4.1.2") + + compileOnly("org.projectlombok:lombok:1.18.24") + annotationProcessor("org.projectlombok:lombok:1.18.24") } \ No newline at end of file From 8d883b1e88896a95dee4b14812b484f13482c3e9 Mon Sep 17 00:00:00 2001 From: Len <40720638+destro174@users.noreply.github.com> Date: Fri, 9 Sep 2022 17:32:40 +0200 Subject: [PATCH 04/27] Changes to AbstractConfig.java --- .../java/com/alttd/config/AbstractConfig.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/alttd/config/AbstractConfig.java b/src/main/java/com/alttd/config/AbstractConfig.java index feb1fcd..3289e8f 100644 --- a/src/main/java/com/alttd/config/AbstractConfig.java +++ b/src/main/java/com/alttd/config/AbstractConfig.java @@ -22,14 +22,14 @@ import java.util.Map; import java.util.regex.Pattern; @SuppressWarnings({"unused", "SameParameterValue"}) -abstract class AbstractConfig { +public abstract class AbstractConfig { private static final Pattern PATH_PATTERN = Pattern.compile("\\."); private static final String HEADER = ""; private YamlConfigurationLoader configLoader; private ConfigurationNode config; - AbstractConfig(String filename) { + protected AbstractConfig(String filename) { init(new File(new File(AltitudeBot.getInstance().getDataFolder()).getParentFile(), filename), filename); } @@ -60,7 +60,7 @@ abstract class AbstractConfig { } } - void readConfig(Class clazz, Object instance) { + protected void readConfig(Class clazz, Object instance) { for (Method method : clazz.getDeclaredMethods()) { if (Modifier.isPrivate(method.getModifiers())) { if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) { @@ -80,7 +80,7 @@ abstract class AbstractConfig { save(); } - private void save() { + protected void save() { try { configLoader.save(config); } catch (IOException ex) { @@ -101,6 +101,17 @@ abstract class AbstractConfig { } } + protected void update(String path, Object def) { + if(config.node(splitPath(path)).virtual()) { + set(path, def); + return; + } + try { + config.node(splitPath(path)).set(def); + } catch (SerializationException e) { + } + } + protected void setString(String path, String def) { try { if(config.node(splitPath(path)).virtual()) From 9ab689c0b8a7200fa6e22faa1d51b2bba82c3931 Mon Sep 17 00:00:00 2001 From: Len <40720638+destro174@users.noreply.github.com> Date: Sat, 10 Sep 2022 00:19:38 +0200 Subject: [PATCH 05/27] Work on RequestManager, a new way to create and manage requests. --- src/main/java/com/alttd/AltitudeBot.java | 6 +- .../commandManager/listeners/JDAListener.java | 29 +++++ src/main/java/com/alttd/request/Request.java | 84 +++++++++++++ .../java/com/alttd/request/RequestConfig.java | 58 +++++++++ .../com/alttd/request/RequestManager.java | 110 ++++++++++++++++++ src/main/java/com/alttd/util/Pair.java | 21 ++++ 6 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/alttd/request/Request.java create mode 100644 src/main/java/com/alttd/request/RequestConfig.java create mode 100644 src/main/java/com/alttd/request/RequestManager.java create mode 100644 src/main/java/com/alttd/util/Pair.java diff --git a/src/main/java/com/alttd/AltitudeBot.java b/src/main/java/com/alttd/AltitudeBot.java index c865c87..78a89ab 100644 --- a/src/main/java/com/alttd/AltitudeBot.java +++ b/src/main/java/com/alttd/AltitudeBot.java @@ -8,6 +8,7 @@ import com.alttd.console.ConsoleCommandManager; import com.alttd.database.Database; import com.alttd.database.DatabaseTables; import com.alttd.permissions.PermissionManager; +import com.alttd.request.RequestManager; import com.alttd.util.Logger; import com.mysql.cj.log.Log; import net.dv8tion.jda.api.JDA; @@ -40,11 +41,13 @@ public class AltitudeBot { Logger.info("Starting bot..."); initConfigs(); try { - jda = JDABuilder.createDefault(SettingsConfig.TOKEN).build(); + jda = JDABuilder.createDefault(SettingsConfig.TOKEN).build().awaitReady(); } catch (LoginException e) { Logger.info("Unable to log in, shutting down (check token in settings.yml)."); exit(1); Logger.exception(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); } DatabaseTables.createTables(Database.getDatabase().getConnection()); ConsoleCommandManager.startConsoleCommands(jda); @@ -55,6 +58,7 @@ public class AltitudeBot { } catch (IllegalArgumentException e) { Logger.exception(e); } + RequestManager.init(); initListeners(); //TODO init permissionManager } diff --git a/src/main/java/com/alttd/commandManager/listeners/JDAListener.java b/src/main/java/com/alttd/commandManager/listeners/JDAListener.java index 9438808..b4a7814 100644 --- a/src/main/java/com/alttd/commandManager/listeners/JDAListener.java +++ b/src/main/java/com/alttd/commandManager/listeners/JDAListener.java @@ -1,9 +1,14 @@ package com.alttd.commandManager.listeners; import com.alttd.commandManager.CommandManager; +import com.alttd.request.RequestManager; import com.alttd.util.Logger; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.events.ReadyEvent; +import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; +import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; +import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import org.jetbrains.annotations.NotNull; @@ -21,4 +26,28 @@ public class JDAListener extends ListenerAdapter { jda.addEventListener(new CommandManager(jda)); } + @Override + public void onSelectMenuInteraction(@NotNull SelectMenuInteractionEvent event) { + String s = event.getComponentId(); + if (s.startsWith("request:")) { + RequestManager.onSelectMenuInteraction(event); + } + } + + @Override + public void onModalInteraction(@NotNull ModalInteractionEvent event) { + String s = event.getModalId(); + if (s.startsWith("request:")) { + RequestManager.onModalInteractionEvent(event); + } + } + + @Override + public void onButtonInteraction(ButtonInteractionEvent event) { + String s = event.getComponentId(); + if (s.startsWith("request:")) { + RequestManager.onButtonInteractionEvent(event); + } + } + } diff --git a/src/main/java/com/alttd/request/Request.java b/src/main/java/com/alttd/request/Request.java new file mode 100644 index 0000000..b20bf62 --- /dev/null +++ b/src/main/java/com/alttd/request/Request.java @@ -0,0 +1,84 @@ +package com.alttd.request; + +import com.alttd.AltitudeBot; +import com.alttd.util.Pair; +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.entities.ThreadChannel; +import net.dv8tion.jda.api.interactions.components.ActionRow; +import net.dv8tion.jda.api.interactions.components.Modal; +import net.dv8tion.jda.api.interactions.components.buttons.Button; +import net.dv8tion.jda.api.interactions.components.text.TextInput; +import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; +import net.dv8tion.jda.api.requests.restaction.ThreadChannelAction; + +import java.awt.*; + +@AllArgsConstructor +public class Request { + + @Getter + private String id, category, channel, name, title, description, message; + + public Modal modal(Member member) { + TextInput requestTitle = TextInput + .create("title", title, TextInputStyle.PARAGRAPH) + .build(); + + TextInput requestMessage = TextInput + .create("request", message.replaceAll("%member%", member.getEffectiveName()).subSequence(0, 45).toString(), TextInputStyle.PARAGRAPH) + .build(); + + return Modal.create("request:" + id, name) + .addActionRow(requestTitle) + .addActionRow(requestMessage) + .build(); + } + + public void createThread(Member member, String title, String request) { + TextChannel channel = AltitudeBot.getInstance().getJDA().getGuildById(RequestConfig.REQUEST_GUILD_ID).getTextChannelById(getChannel()); + ThreadChannelAction threadChannelAction = channel.createThreadChannel(title); + threadChannelAction.queue(threadChannel -> { + threadChannel.addThreadMember(member).queue(); + sendEmbed(threadChannel, title, request); + channel.deleteMessageById(threadChannel.getId()).queue(); + // TODO store the request somewhere so it can be grabbed later + }); + } + + public void sendEmbed(ThreadChannel channel, String title, String request) { +// Pair pair = getRequestEmbed(channel.getId(), title, request); + // pairs are not really possible here :( + EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setTitle("title") + .addField(getName(), request, false) + .setColor(new Color(41, 43, 47)); + channel.sendMessageEmbeds(embedBuilder.build()).queue(message1 -> + channel.editMessageEmbedsById(message1.getId(), embedBuilder.build()) + .setActionRow( + Button.primary("request:" + getId() + ":" + channel.getId() + ":" + message1.getId() + ":progress", "in progress"), + Button.success("request:" + getId() + ":" + channel.getId() + ":" + message1.getId() + ":complete", "complete"), + Button.danger("request:" + getId() + ":" + channel.getId() + ":" + message1.getId() + ":denied", "denied") + ).queue() + ); + } + + public Pair getRequestEmbed(String channellId, String title, String request) { + EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setTitle("title") + .addField(getName(), request, false) + .setColor(new Color(41, 43, 47)); + + ActionRow actionRow = ActionRow.of( + Button.primary("request:" + getId() + ":" + channellId + ":progress", "in progress"), + Button.success("request:" + getId() + ":" + channellId + ":complete", "complete"), + Button.danger("request:" + getId() + ":" + channellId + ":denied", "denied") + ); + + return new Pair<>(embedBuilder, actionRow); + } + +} diff --git a/src/main/java/com/alttd/request/RequestConfig.java b/src/main/java/com/alttd/request/RequestConfig.java new file mode 100644 index 0000000..ce450aa --- /dev/null +++ b/src/main/java/com/alttd/request/RequestConfig.java @@ -0,0 +1,58 @@ +package com.alttd.request; + +import com.alttd.config.AbstractConfig; +import com.alttd.util.Logger; + +import java.util.ArrayList; +import java.util.List; + +public class RequestConfig extends AbstractConfig { + + static RequestConfig requestConfig; + + public RequestConfig() { + super("requests.yml"); + } + + public static void reload() { + requestConfig = new RequestConfig(); + requestConfig.readConfig(RequestConfig.class, requestConfig); + } + + public static String REQUEST_GUILD_ID = "776590138296893480"; + public static String REQUEST_CATEGORY = "776590138296893481"; + public static String REQUEST_CHANNEL = "1017787342561476709"; + public static String REQUEST_MESSAGE = ""; + private void settings() { + REQUEST_GUILD_ID = requestConfig.getString("request.guild", REQUEST_GUILD_ID); + REQUEST_CATEGORY = requestConfig.getString("request.category", REQUEST_CATEGORY); + REQUEST_CHANNEL = requestConfig.getString("request.channel", REQUEST_CHANNEL); + REQUEST_MESSAGE = requestConfig.getString("request.message", REQUEST_MESSAGE); + } + + public static void setRequestMessage(String messageId) { + REQUEST_MESSAGE = messageId; + requestConfig.update("request.message", REQUEST_MESSAGE); + requestConfig.save(); + } + + public static final List requests = new ArrayList<>(); + private void loadRequests() { + requests.clear(); + requestConfig.getNode("types").childrenMap().forEach((key, value) -> { + String id = key.toString(); + String category = value.node("category").getString(); + String channel = value.node("channel").getString(); + String name = value.node("name").getString(); + String title = value.node("title").getString(); + String description = value.node("description").getString(); + String message = value.node("message").getString(); + if (id == null || category == null || channel == null || name == null || description == null || message == null) { + Logger.warning("Requests are set up incorrectly!"); + } else { + requests.add(new Request(id, category, channel, name, title, description, message)); + } + }); + } + +} diff --git a/src/main/java/com/alttd/request/RequestManager.java b/src/main/java/com/alttd/request/RequestManager.java new file mode 100644 index 0000000..a6f9c9e --- /dev/null +++ b/src/main/java/com/alttd/request/RequestManager.java @@ -0,0 +1,110 @@ +package com.alttd.request; + +import com.alttd.AltitudeBot; +import com.alttd.util.Pair; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.entities.ThreadChannel; +import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; +import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; +import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent; +import net.dv8tion.jda.api.interactions.components.selections.SelectMenu; + +import java.awt.*; + +public class RequestManager { + + public static void init() { + RequestConfig.reload(); + if (RequestConfig.REQUEST_MESSAGE == null || RequestConfig.REQUEST_MESSAGE.isEmpty()) + sendRequestMessage(); + } + + public static Pair getRequestEmbed() { + EmbedBuilder embedBuilder = new EmbedBuilder(); + SelectMenu.Builder selectMenuBuilder = SelectMenu.create("request:create"); + embedBuilder.setDescription("Select an option below to open a request!\n") + .setTitle("Create a new request.") + .setColor(new Color(41, 43, 47)); + + for (Request request : RequestConfig.requests) { + embedBuilder.addField(request.getName(), request.getDescription(), false); + selectMenuBuilder.addOption(request.getName(), "request:open:" + request.getId(), request.getDescription(), null); + } + + return new Pair<>(embedBuilder, selectMenuBuilder); + } + + public static void sendRequestMessage() { + TextChannel channel = AltitudeBot.getInstance().getJDA().getGuildById(RequestConfig.REQUEST_GUILD_ID).getTextChannelById(RequestConfig.REQUEST_CHANNEL); + Pair pair = getRequestEmbed(); + channel.sendMessageEmbeds(pair.getValue0().build()).setActionRow( + pair.getValue1().build() + ).queue(m -> RequestConfig.setRequestMessage(m.getId())); + } + + public static void updateRequestMessage() { + TextChannel channel = AltitudeBot.getInstance().getJDA().getGuildById(RequestConfig.REQUEST_GUILD_ID).getTextChannelById(RequestConfig.REQUEST_CHANNEL); + Pair pair = getRequestEmbed(); + channel.editMessageEmbedsById(RequestConfig.REQUEST_MESSAGE, pair.getValue0().build()) + .setActionRow( + pair.getValue1().build() + ).queue(m -> RequestConfig.setRequestMessage(m.getId())); + } + + public static Request getRequestById(String id) { + return RequestConfig.requests.stream().filter(request -> request.getId().equalsIgnoreCase(id)).findFirst().orElse(null); + } + + public static void onSelectMenuInteraction(SelectMenuInteractionEvent event) { + String[] actions = event.getComponentId().split(":"); + if (actions[1].equals("create")) { + String[] selection = event.getSelectedOptions().get(0).getValue().split(":"); + if (selection[0].equals("request") && selection[1].equals("open")) { + String id = selection[2]; + event.replyModal(getRequestById(id).modal(event.getMember())).queue(); + updateRequestMessage(); // You can't use a select menu option twice in a row, updating it fixes that. + } + } + } + + public static void onModalInteractionEvent(ModalInteractionEvent event) { + String s = event.getModalId(); + String[] strings = s.split(":", 2); + getRequestById(strings[1]).createThread(event.getMember(), event.getValue("title").getAsString(), event.getValue("request").getAsString()); + event.reply("Thanks for your request!").setEphemeral(true).queue(); + } + + public static void onButtonInteractionEvent(ButtonInteractionEvent event) { + String s = event.getComponentId(); + String[] strings = s.split(":", 5); + String requestId = strings[1]; + String threadId = strings[2]; + String messageId = strings[3]; + String type = strings[4]; // progress, complete, denied + + // TODO update the stored request in the database + switch (type) { + case "denied" -> { + // TODO open a new modal to input a reason? + // could also do this by command? + event.reply("This request has been denied by " + event.getMember().getAsMention()).queue(); + ThreadChannel threadChannel = AltitudeBot.getInstance().getJDA().getGuildById(RequestConfig.REQUEST_GUILD_ID).getThreadChannelById(threadId); + threadChannel.getManager().setArchived(true).setLocked(true).queue(); + } + case "complete" -> { + // TODO open a new modal to input a reason? + // could also do this by command? + event.reply("This request has been completed by " + event.getMember().getAsMention()).queue(); + ThreadChannel threadChannel = AltitudeBot.getInstance().getJDA().getGuildById(RequestConfig.REQUEST_GUILD_ID).getThreadChannelById(threadId); + threadChannel.getManager().setArchived(true).setLocked(true).queue(); + } + case "progress" -> { + // TODO open a new modal to input a reason? + // edit the message to show who is working on it? + } + } + + } + +} diff --git a/src/main/java/com/alttd/util/Pair.java b/src/main/java/com/alttd/util/Pair.java new file mode 100644 index 0000000..987cfc7 --- /dev/null +++ b/src/main/java/com/alttd/util/Pair.java @@ -0,0 +1,21 @@ +package com.alttd.util; + +public class Pair { + + private final X x; + private final Y y; + + public Pair(X x, Y y) { + this.x = x; + this.y = y; + } + + public X getValue0() { + return x; + } + + public Y getValue1() { + return y; + } +} + From 26dfc0013c874aee4411893c22e8bad4f1b7cdd3 Mon Sep 17 00:00:00 2001 From: Len <40720638+destro174@users.noreply.github.com> Date: Sat, 10 Sep 2022 00:34:53 +0200 Subject: [PATCH 06/27] Add an example config. --- example.requests.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 example.requests.yml diff --git a/example.requests.yml b/example.requests.yml new file mode 100644 index 0000000..322beab --- /dev/null +++ b/example.requests.yml @@ -0,0 +1,34 @@ +request: + guild: '776590138296893480' + category: '776590138296893481' + channel: '1017787342561476709' + message: '1017839462111256667' +types: + bug-report: + category: '776590138296893481' + channel: '820222354180800525' + name: Bug report + title: What should the title be? + description: Report a new bug. + message: Describe the bug. + act-request: + category: '776590138296893481' + channel: '820222354180800525' + name: Act request + title: What should the title be? + description: Make a new act request. + message: Describe the act request. + admin-act-request: + category: '776590138296893481' + channel: '820222354180800525' + name: Admin act request + title: What should the title be? + description: Make a new admin act request. + message: Describe the admin act request. + feature-request: + category: '776590138296893481' + channel: '820222354180800525' + name: Feature request + title: What should the title be? + description: Make a new feature request. + message: Describe the feature request. From ddcd1643c2d4cfaa6dd9abd4c1346b2dff05a99a Mon Sep 17 00:00:00 2001 From: Len <40720638+destro174@users.noreply.github.com> Date: Sat, 10 Sep 2022 10:40:32 +0200 Subject: [PATCH 07/27] Update Request.java --- src/main/java/com/alttd/request/Request.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/alttd/request/Request.java b/src/main/java/com/alttd/request/Request.java index b20bf62..f7716cf 100644 --- a/src/main/java/com/alttd/request/Request.java +++ b/src/main/java/com/alttd/request/Request.java @@ -19,17 +19,19 @@ import java.awt.*; @AllArgsConstructor public class Request { - + // TODO check if all labels on the modal are max 45 in length @Getter private String id, category, channel, name, title, description, message; public Modal modal(Member member) { TextInput requestTitle = TextInput - .create("title", title, TextInputStyle.PARAGRAPH) + .create("title", title, TextInputStyle.SHORT) + .setPlaceholder(id) + .setRequired(false) .build(); TextInput requestMessage = TextInput - .create("request", message.replaceAll("%member%", member.getEffectiveName()).subSequence(0, 45).toString(), TextInputStyle.PARAGRAPH) + .create("request", message, TextInputStyle.PARAGRAPH) .build(); return Modal.create("request:" + id, name) @@ -40,10 +42,12 @@ public class Request { public void createThread(Member member, String title, String request) { TextChannel channel = AltitudeBot.getInstance().getJDA().getGuildById(RequestConfig.REQUEST_GUILD_ID).getTextChannelById(getChannel()); - ThreadChannelAction threadChannelAction = channel.createThreadChannel(title); + if (title == null || title.isEmpty()) title = id; + String finalTitle = title; + ThreadChannelAction threadChannelAction = channel.createThreadChannel(finalTitle); threadChannelAction.queue(threadChannel -> { threadChannel.addThreadMember(member).queue(); - sendEmbed(threadChannel, title, request); + sendEmbed(threadChannel, finalTitle, request); channel.deleteMessageById(threadChannel.getId()).queue(); // TODO store the request somewhere so it can be grabbed later }); @@ -53,7 +57,7 @@ public class Request { // Pair pair = getRequestEmbed(channel.getId(), title, request); // pairs are not really possible here :( EmbedBuilder embedBuilder = new EmbedBuilder(); - embedBuilder.setTitle("title") + embedBuilder.setTitle(title) .addField(getName(), request, false) .setColor(new Color(41, 43, 47)); channel.sendMessageEmbeds(embedBuilder.build()).queue(message1 -> From 68b1366e03153492cd9abfde1a00d01b973d4c0d Mon Sep 17 00:00:00 2001 From: Len <40720638+destro174@users.noreply.github.com> Date: Fri, 16 Sep 2022 08:43:23 +0200 Subject: [PATCH 08/27] Use JDA forum branch --- build.gradle.kts | 3 ++- settings.gradle.kts | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index c985d92..dfa16ca 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -58,7 +58,8 @@ tasks { dependencies { // JDA - implementation("net.dv8tion:JDA:5.0.0-alpha.19") { +// implementation("net.dv8tion:JDA:5.0.0-alpha.19") { + implementation("com.github.DV8FromTheWorld:JDA:1f9c577") { exclude("opus-java") // exclude audio } // MySQL diff --git a/settings.gradle.kts b/settings.gradle.kts index 6fb6767..d6d98f5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,6 +10,8 @@ dependencyResolutionManagement { maven("https://repo.spongepowered.org/maven") // MySQL maven("https://jcenter.bintray.com") + //jitpack + maven("https://jitpack.io") } repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) } From 649d5e73c2bf0a2b7242ba0cb7161091e43ddc42 Mon Sep 17 00:00:00 2001 From: Len <40720638+destro174@users.noreply.github.com> Date: Fri, 16 Sep 2022 09:17:52 +0200 Subject: [PATCH 09/27] Missed merging in lombok --- build.gradle.kts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 72c3c70..5f85c5b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -64,4 +64,8 @@ dependencies { // Configurate implementation("org.spongepowered:configurate-yaml:4.1.2") + + + compileOnly("org.projectlombok:lombok:1.18.24") + annotationProcessor("org.projectlombok:lombok:1.18.24") } \ No newline at end of file From f7c531d6ba5840613d2c7c8474714cb4f4fbe515 Mon Sep 17 00:00:00 2001 From: Len <40720638+destro174@users.noreply.github.com> Date: Fri, 16 Sep 2022 09:20:05 +0200 Subject: [PATCH 10/27] Update broken JDA stuff --- src/main/java/com/alttd/AltitudeBot.java | 10 ++++++---- src/main/java/com/alttd/request/Request.java | 4 ++-- src/main/java/com/alttd/request/RequestManager.java | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/alttd/AltitudeBot.java b/src/main/java/com/alttd/AltitudeBot.java index 498b5e0..62ac4c1 100644 --- a/src/main/java/com/alttd/AltitudeBot.java +++ b/src/main/java/com/alttd/AltitudeBot.java @@ -6,13 +6,19 @@ import com.alttd.console.ConsoleCommandManager; import com.alttd.database.Database; import com.alttd.database.DatabaseTables; import com.alttd.listeners.JDAListener; +import com.alttd.request.RequestManager; import com.alttd.util.Logger; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.OnlineStatus; +import net.dv8tion.jda.api.entities.Activity; +import javax.security.auth.login.LoginException; import java.io.File; import java.net.URISyntaxException; +import static java.lang.System.exit; + public class AltitudeBot { private JDA jda; @@ -34,10 +40,6 @@ public class AltitudeBot { jda = JDABuilder.createDefault(SettingsConfig.TOKEN).build(); try { jda = JDABuilder.createDefault(SettingsConfig.TOKEN).build().awaitReady(); - } catch (LoginException e) { - Logger.info("Unable to log in, shutting down (check token in settings.yml)."); - exit(1); - Logger.exception(e); } catch (InterruptedException e) { throw new RuntimeException(e); } diff --git a/src/main/java/com/alttd/request/Request.java b/src/main/java/com/alttd/request/Request.java index f7716cf..f22c2df 100644 --- a/src/main/java/com/alttd/request/Request.java +++ b/src/main/java/com/alttd/request/Request.java @@ -6,8 +6,8 @@ import lombok.AllArgsConstructor; import lombok.Getter; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.TextChannel; -import net.dv8tion.jda.api.entities.ThreadChannel; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.dv8tion.jda.api.interactions.components.ActionRow; import net.dv8tion.jda.api.interactions.components.Modal; import net.dv8tion.jda.api.interactions.components.buttons.Button; diff --git a/src/main/java/com/alttd/request/RequestManager.java b/src/main/java/com/alttd/request/RequestManager.java index a6f9c9e..d2c0651 100644 --- a/src/main/java/com/alttd/request/RequestManager.java +++ b/src/main/java/com/alttd/request/RequestManager.java @@ -3,8 +3,8 @@ package com.alttd.request; import com.alttd.AltitudeBot; import com.alttd.util.Pair; import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.entities.TextChannel; -import net.dv8tion.jda.api.entities.ThreadChannel; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent; From b31a5ba0df5584f40e3d66a3ae212930bee30c61 Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Fri, 16 Sep 2022 18:57:45 +0200 Subject: [PATCH 11/27] Moved RequestManager.init to the onReady listener --- src/main/java/com/alttd/AltitudeBot.java | 20 ------------------- .../java/com/alttd/listeners/JDAListener.java | 2 +- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/main/java/com/alttd/AltitudeBot.java b/src/main/java/com/alttd/AltitudeBot.java index 62ac4c1..1927481 100644 --- a/src/main/java/com/alttd/AltitudeBot.java +++ b/src/main/java/com/alttd/AltitudeBot.java @@ -6,19 +6,13 @@ import com.alttd.console.ConsoleCommandManager; import com.alttd.database.Database; import com.alttd.database.DatabaseTables; import com.alttd.listeners.JDAListener; -import com.alttd.request.RequestManager; import com.alttd.util.Logger; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; -import net.dv8tion.jda.api.OnlineStatus; -import net.dv8tion.jda.api.entities.Activity; -import javax.security.auth.login.LoginException; import java.io.File; import java.net.URISyntaxException; -import static java.lang.System.exit; - public class AltitudeBot { private JDA jda; @@ -38,21 +32,8 @@ public class AltitudeBot { initConfigs(); ConsoleCommandManager.startConsoleCommands(jda); jda = JDABuilder.createDefault(SettingsConfig.TOKEN).build(); - try { - jda = JDABuilder.createDefault(SettingsConfig.TOKEN).build().awaitReady(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } DatabaseTables.createTables(Database.getDatabase().getConnection()); ConsoleCommandManager.startConsoleCommands(jda); - try { - jda.getPresence().setPresence( - OnlineStatus.valueOf(SettingsConfig.STATUS), - Activity.listening(SettingsConfig.ACTIVITY)); - } catch (IllegalArgumentException e) { - Logger.exception(e); - } - RequestManager.init(); // try { // jda.getPresence().setPresence( // OnlineStatus.valueOf(SettingsConfig.STATUS), @@ -61,7 +42,6 @@ public class AltitudeBot { // Logger.exception(e); // } initListeners(); - //TODO init permissionManager } private void initListeners() { diff --git a/src/main/java/com/alttd/listeners/JDAListener.java b/src/main/java/com/alttd/listeners/JDAListener.java index 67ec41b..ee7bb99 100644 --- a/src/main/java/com/alttd/listeners/JDAListener.java +++ b/src/main/java/com/alttd/listeners/JDAListener.java @@ -10,7 +10,6 @@ import net.dv8tion.jda.api.events.ReadyEvent; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent; -import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import org.jetbrains.annotations.NotNull; @@ -29,6 +28,7 @@ public class JDAListener extends ListenerAdapter { ModalManager modalManager = new ModalManager(buttonManager); CommandManager commandManager = new CommandManager(jda, modalManager); jda.addEventListener(buttonManager, modalManager, commandManager); +// RequestManager.init(); } @Override From 2676acc20528baaec614c70521220390bdf9a620 Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Fri, 16 Sep 2022 18:58:06 +0200 Subject: [PATCH 12/27] Made suggestions work with forums and added support for forum channels to other commands --- .../ButtonSuggestionReviewAccept.java | 90 ++++++++++++++----- .../ButtonSuggestionReviewDeny.java | 45 +++++++--- .../commands/CommandSetOutputChannel.java | 12 +-- .../com/alttd/database/DatabaseTables.java | 1 + .../CommandOutputChannels.java | 48 +++++++--- .../modalManager/modals/ModalEvidence.java | 13 ++- .../modalManager/modals/ModalSuggestion.java | 11 ++- 7 files changed, 161 insertions(+), 59 deletions(-) diff --git a/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java b/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java index 7f2a3c1..4231747 100644 --- a/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java +++ b/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java @@ -8,10 +8,14 @@ import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.MessageEmbed; -import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; +import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.requests.RestAction; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; import java.awt.*; import java.util.List; @@ -26,16 +30,6 @@ public class ButtonSuggestionReviewAccept extends DiscordButton { @Override public void execute(ButtonInteractionEvent event) { Message message = event.getMessage(); - long suggestionChannelId = CommandOutputChannels.getOutputChannel(message.getGuild().getIdLong(), OutputType.SUGGESTION); - long modLogChannelId = CommandOutputChannels.getOutputChannel(message.getGuild().getIdLong(), OutputType.MOD_LOG); - - List embeds = message.getEmbeds(); - if (embeds.size() != 1) { - event.replyEmbeds(Util.genericErrorEmbed("Error", "This message contains no embeds, can't be a suggestion")) - .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); - return; - } - Guild guild = event.getGuild(); if (guild == null) { event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to retrieve guild")) @@ -43,16 +37,9 @@ public class ButtonSuggestionReviewAccept extends DiscordButton { return; } - GuildMessageChannel suggestionChannel = guild.getChannelById(GuildMessageChannel.class, suggestionChannelId); - if (suggestionChannel == null) { - event.replyEmbeds(Util.genericErrorEmbed("Error", "This server does not have a valid suggestion channel")) - .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); - return; - } - - GuildMessageChannel modLogChannel = guild.getChannelById(GuildMessageChannel.class, modLogChannelId); - if (modLogChannel == null) { - event.replyEmbeds(Util.genericErrorEmbed("Error", "This server does not have a valid suggestion channel")) + List embeds = message.getEmbeds(); + if (embeds.size() != 1) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This message contains no embeds, can't be a suggestion")) .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); return; } @@ -65,21 +52,76 @@ public class ButtonSuggestionReviewAccept extends DiscordButton { return; } + GuildChannel suggestionGuildChannel = CommandOutputChannels.getOutputChannel(message.getGuild(), OutputType.SUGGESTION); + GuildChannel modLogGuildChannel = CommandOutputChannels.getOutputChannel(message.getGuild(), OutputType.MOD_LOG); + + TextChannel modLogChannel = validModLogChannel(event, modLogGuildChannel); + if (modLogChannel == null) + return; + + if (suggestionGuildChannel == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This server does not have a valid suggestion channel.")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + MessageEmbed suggestionMessage = new EmbedBuilder(reviewMessage) .clearFields() .setColor(Color.GRAY) .setTitle(fields.get(0).getName()) .setDescription(fields.get(0).getValue()) .build(); + + if (suggestionGuildChannel instanceof ForumChannel forumChannel) { + sendSuggestionInForum(forumChannel, modLogChannel, fields.get(0), suggestionMessage, event); + } else if (suggestionGuildChannel instanceof TextChannel forumChannel) { + sendSuggestionEmbed(forumChannel, modLogChannel, suggestionMessage, event); + } else { + event.replyEmbeds(Util.genericErrorEmbed("Error", suggestionGuildChannel.getType().name() + " is not a valid suggestion channel")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + } + } + + private TextChannel validModLogChannel(ButtonInteractionEvent event, GuildChannel guildChannel) { + if (guildChannel == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This server does not have a valid mod log channel")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return null; + } + + if (!(guildChannel instanceof TextChannel channel)) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "A mod log channel can't be of type: " + guildChannel.getType().name())) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return null; + } + return channel; + } + + public void sendSuggestionEmbed(TextChannel suggestionChannel, TextChannel modLog, MessageEmbed suggestionMessage, ButtonInteractionEvent event) { suggestionChannel.sendMessageEmbeds(suggestionMessage).queue(success -> { - message.delete().queue(RestAction.getDefaultSuccess(), Util::handleFailure); + event.getMessage().delete().queue(RestAction.getDefaultSuccess(), Util::handleFailure); event.replyEmbeds(Util.genericSuccessEmbed("Success", "The suggestion was accepted and posted in the suggestion channel")).setEphemeral(true).queue(); - modLogChannel.sendMessageEmbeds(new EmbedBuilder(suggestionMessage).addField("Accepted", event.getUser().getAsMention(), false).setColor(Color.GREEN).build()) - .queue(RestAction.getDefaultSuccess(), Util::handleFailure); + sendModLog(modLog, suggestionMessage, event); }, failure -> event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to send suggestion to the suggestion channel")) .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure)); } + public void sendSuggestionInForum(ForumChannel forumChannel, TextChannel modLog, MessageEmbed.Field field, MessageEmbed suggestionMessage, ButtonInteractionEvent event) { + MessageCreateData messageCreateData = new MessageCreateBuilder().addContent(field.getValue()).build(); + + forumChannel.createForumPost(field.getName(), messageCreateData).queue(success -> { + event.getMessage().delete().queue(RestAction.getDefaultSuccess(), Util::handleFailure); + event.replyEmbeds(Util.genericSuccessEmbed("Success", "The suggestion was accepted and posted in the suggestion channel")).setEphemeral(true).queue(); + sendModLog(modLog, suggestionMessage, event); + }, failure -> event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to send suggestion to the suggestion channel")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure)); + } + + public void sendModLog(TextChannel modLog, MessageEmbed suggestionMessage, ButtonInteractionEvent event) { + modLog.sendMessageEmbeds(new EmbedBuilder(suggestionMessage).addField("Accepted", event.getUser().getAsMention(), false).setColor(Color.GREEN).build()) + .queue(RestAction.getDefaultSuccess(), Util::handleFailure); + } + @Override public Button getButton() { return Button.success(getButtonId(), "Accept Suggestion"); diff --git a/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewDeny.java b/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewDeny.java index 48b149c..74dd9d9 100644 --- a/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewDeny.java +++ b/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewDeny.java @@ -8,9 +8,11 @@ import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.MessageEmbed; -import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.interactions.components.buttons.Button; +import net.dv8tion.jda.api.requests.RestAction; import java.awt.*; import java.util.List; @@ -25,30 +27,30 @@ public class ButtonSuggestionReviewDeny extends DiscordButton { @Override public void execute(ButtonInteractionEvent event) { Message message = event.getMessage(); - long channelId = CommandOutputChannels.getOutputChannel(message.getGuild().getIdLong(), OutputType.MOD_LOG); + GuildChannel guildChannel = CommandOutputChannels.getOutputChannel(message.getGuild(), OutputType.MOD_LOG); + TextChannel channel = validModLogChannel(event, guildChannel); + if (channel == null) + return; List embeds = message.getEmbeds(); if (embeds.size() != 1) { - event.replyEmbeds(Util.genericErrorEmbed("Error", "This message contains no embeds, can't be a suggestion")).setEphemeral(true).queue(); + event.replyEmbeds(Util.genericErrorEmbed("Error", "This message contains no embeds, can't be a suggestion")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); return; } Guild guild = event.getGuild(); if (guild == null) { - event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to retrieve guild")).setEphemeral(true).queue(); - return; - } - - GuildMessageChannel channel = guild.getChannelById(GuildMessageChannel.class, channelId); - if (channel == null) { - event.replyEmbeds(Util.genericErrorEmbed("Error", "This server does not have a valid mod log channel")).setEphemeral(true).queue(); + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to retrieve guild")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); return; } MessageEmbed reviewMessage = embeds.get(0); List fields = reviewMessage.getFields(); if (fields.size() != 1) { - event.replyEmbeds(Util.genericErrorEmbed("Error", "This message's embed does not contain a field, can't be a suggestion")).setEphemeral(true).queue(); + event.replyEmbeds(Util.genericErrorEmbed("Error", "This message's embed does not contain a field, can't be a suggestion")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); return; } @@ -60,12 +62,29 @@ public class ButtonSuggestionReviewDeny extends DiscordButton { .build(); channel.sendMessageEmbeds(suggestionMessage).queue(success -> { message.delete().queue(); - event.replyEmbeds(Util.genericSuccessEmbed("Success", "The suggestion was denied and logged")).setEphemeral(true).queue(); + event.replyEmbeds(Util.genericSuccessEmbed("Success", "The suggestion was denied and logged")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); }, failure -> { - event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to send suggestion to the suggestion channel")).setEphemeral(true).queue(); + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to send suggestion to the suggestion channel")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); }); } + private TextChannel validModLogChannel(ButtonInteractionEvent event, GuildChannel guildChannel) { + if (guildChannel == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This server does not have a valid mod log channel")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return null; + } + + if (!(guildChannel instanceof TextChannel channel)) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "A mod log channel can't be of type: " + guildChannel.getType().name())) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return null; + } + return channel; + } + @Override public Button getButton() { return Button.danger(getButtonId(), "Deny Suggestion"); diff --git a/src/main/java/com/alttd/commandManager/commands/CommandSetOutputChannel.java b/src/main/java/com/alttd/commandManager/commands/CommandSetOutputChannel.java index f44dbe1..75a21c8 100644 --- a/src/main/java/com/alttd/commandManager/commands/CommandSetOutputChannel.java +++ b/src/main/java/com/alttd/commandManager/commands/CommandSetOutputChannel.java @@ -8,6 +8,7 @@ import com.alttd.util.Util; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -70,10 +71,11 @@ public class CommandSetOutputChannel extends DiscordCommand { return; } - GuildChannelUnion channel = option.getAsChannel(); - switch (channel.getType()) { - case TEXT, NEWS, GUILD_NEWS_THREAD, GUILD_PUBLIC_THREAD, GUILD_PRIVATE_THREAD -> { - boolean success = CommandOutputChannels.setOutputChannel(guild.getIdLong(), outputType, channel.getIdLong()); + ChannelType channelType = option.getChannelType(); + switch (channelType) { + case TEXT, NEWS, GUILD_NEWS_THREAD, GUILD_PUBLIC_THREAD, GUILD_PRIVATE_THREAD, FORUM -> { + GuildChannelUnion channel = option.getAsChannel(); + boolean success = CommandOutputChannels.setOutputChannel(guild.getIdLong(), outputType, channel.getIdLong(), channelType); if (success) event.replyEmbeds(Util.genericSuccessEmbed("Success", "Set channel " + channel.getAsMention() + " as the output channel for " + outputType.name() + ".")) .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); @@ -81,7 +83,7 @@ public class CommandSetOutputChannel extends DiscordCommand { event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to store the new channel output in the database")) .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); } - default -> event.replyEmbeds(Util.genericErrorEmbed("Error", "The channel type " + channel.getType().name() + " is not a valid output channel type")) + default -> event.replyEmbeds(Util.genericErrorEmbed("Error", "The channel type " + channelType.name() + " is not a valid output channel type")) .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); } diff --git a/src/main/java/com/alttd/database/DatabaseTables.java b/src/main/java/com/alttd/database/DatabaseTables.java index 683b837..546dd78 100644 --- a/src/main/java/com/alttd/database/DatabaseTables.java +++ b/src/main/java/com/alttd/database/DatabaseTables.java @@ -74,6 +74,7 @@ public class DatabaseTables { "guild BIGINT NOT NULL, " + "output_type VARCHAR(64) NOT NULL, " + "channel BIGINT NOT NULL, " + + "channel_type VARCHAR(64) NOT NULL, " + "PRIMARY KEY (guild, output_type, channel)" + ")"; try { diff --git a/src/main/java/com/alttd/database/queries/commandOutputChannels/CommandOutputChannels.java b/src/main/java/com/alttd/database/queries/commandOutputChannels/CommandOutputChannels.java index b1a38a3..b15946b 100644 --- a/src/main/java/com/alttd/database/queries/commandOutputChannels/CommandOutputChannels.java +++ b/src/main/java/com/alttd/database/queries/commandOutputChannels/CommandOutputChannels.java @@ -2,6 +2,9 @@ package com.alttd.database.queries.commandOutputChannels; import com.alttd.database.Database; import com.alttd.util.Logger; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.channel.ChannelType; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -9,14 +12,15 @@ import java.sql.SQLException; public class CommandOutputChannels { - public static boolean setOutputChannel(long guildId, OutputType outputType, long channelId) { - String sql = "INSERT INTO output_channels (guild, output_type, channel) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE channel = ?"; + public static boolean setOutputChannel(long guildId, OutputType outputType, long channelId, ChannelType channelType) { + String sql = "INSERT INTO output_channels (guild, output_type, channel, channel_type) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE channel = ?"; try { PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql); preparedStatement.setLong(1, guildId); preparedStatement.setString(2, outputType.name()); preparedStatement.setLong(3, channelId); - preparedStatement.setLong(4, channelId); + preparedStatement.setString(4, channelType.name()); + preparedStatement.setLong(5, channelId); return preparedStatement.executeUpdate() == 1; } catch (SQLException e) { @@ -27,26 +31,46 @@ public class CommandOutputChannels { /** * Retrieve the channelId of the channel in the specified guild for the specified output type - * @param guildId id of the guild to check in + * @param guild guild to get the channel for * @param outputType output type to check for * @return long channel id or 0 if it errors or can't be found */ - public static long getOutputChannel(long guildId, OutputType outputType) { - String sql = "SELECT channel FROM output_channels WHERE guild = ? AND output_type = ?"; + public static GuildChannel getOutputChannel(Guild guild, OutputType outputType) { + String sql = "SELECT channel, channel_type FROM output_channels WHERE guild = ? AND output_type = ?"; try { PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql); - preparedStatement.setLong(1, guildId); + preparedStatement.setLong(1, guild.getIdLong()); preparedStatement.setString(2, outputType.name()); ResultSet resultSet = preparedStatement.executeQuery(); - if (resultSet.next()) - return resultSet.getLong("channel"); - else - return 0L; + if (resultSet.next()) { + String stringChannelType = resultSet.getString("channel_type"); + ChannelType channelType; + try { + channelType = ChannelType.valueOf(stringChannelType); + } catch (IllegalArgumentException exception) { + return null; + } + long channelId = resultSet.getLong("channel"); + switch (channelType) { + case TEXT, NEWS -> { + return guild.getTextChannelById(channelId); + } + case GUILD_NEWS_THREAD, GUILD_PUBLIC_THREAD, GUILD_PRIVATE_THREAD -> { + return guild.getThreadChannelById(channelId); + } + case FORUM -> { + return guild.getForumChannelById(channelId); + } + default -> { + return null; + } + } + } } catch (SQLException e) { Logger.exception(e); - return 0L; } + return null; } } diff --git a/src/main/java/com/alttd/modalManager/modals/ModalEvidence.java b/src/main/java/com/alttd/modalManager/modals/ModalEvidence.java index 0ad1f2c..fb7a06a 100644 --- a/src/main/java/com/alttd/modalManager/modals/ModalEvidence.java +++ b/src/main/java/com/alttd/modalManager/modals/ModalEvidence.java @@ -8,6 +8,7 @@ import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.interactions.components.ActionRow; @@ -48,9 +49,15 @@ public class ModalEvidence extends DiscordModal { return; } - GuildMessageChannel channel = guild.getChannelById(GuildMessageChannel.class, CommandOutputChannels.getOutputChannel(guild.getIdLong(), OutputType.EVIDENCE)); - if (channel == null) { - event.replyEmbeds(Util.genericErrorEmbed("Error", "This guild does not have a suggestion review channel or it's not the right channel type")) + GuildChannel outputChannel = CommandOutputChannels.getOutputChannel(guild, OutputType.EVIDENCE); + if (outputChannel == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This guild does not have an evidence channel or it's not the right channel type")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + if (!(outputChannel instanceof GuildMessageChannel channel)) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Invalid Evidence channel type: " + outputChannel.getType())) .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); return; } diff --git a/src/main/java/com/alttd/modalManager/modals/ModalSuggestion.java b/src/main/java/com/alttd/modalManager/modals/ModalSuggestion.java index ce113d0..3fd9ce1 100644 --- a/src/main/java/com/alttd/modalManager/modals/ModalSuggestion.java +++ b/src/main/java/com/alttd/modalManager/modals/ModalSuggestion.java @@ -10,6 +10,7 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.interactions.components.ActionRow; @@ -54,13 +55,19 @@ public class ModalSuggestion extends DiscordModal { return; } - GuildMessageChannel channel = guild.getChannelById(GuildMessageChannel.class, CommandOutputChannels.getOutputChannel(guild.getIdLong(), OutputType.SUGGESTION_REVIEW)); - if (channel == null) { + GuildChannel outputChannel = CommandOutputChannels.getOutputChannel(guild, OutputType.SUGGESTION_REVIEW); + if (outputChannel == null) { event.replyEmbeds(Util.genericErrorEmbed("Error", "This guild does not have a suggestion review channel or it's not the right channel type")) .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); return; } + if (!(outputChannel instanceof GuildMessageChannel channel)) { + event.replyEmbeds(Util.genericErrorEmbed("Error", outputChannel.getType().name() + " is not a valid suggestion review channel type")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + Member member = event.getMember(); if (member == null) { event.replyEmbeds(Util.genericErrorEmbed("Error", "This command should only be executed from a guild")) From 2083f8ff355700af9f3dc9315ccaff446eaebc30 Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Fri, 16 Sep 2022 21:34:57 +0200 Subject: [PATCH 13/27] Added a way to reply to suggestions --- .../ButtonSuggestionReviewAccept.java | 2 +- .../alttd/commandManager/CommandManager.java | 5 +- .../commands/AddCommand/CommandManage.java | 7 +- .../commands/AddCommand/SubCommandEnable.java | 92 ++++++++++++++++--- .../ContextMenuManager.java | 76 +++++++++++++++ .../DiscordContextMenu.java | 17 ++++ .../ContextMenuRespondSuggestion.java | 90 ++++++++++++++++++ .../java/com/alttd/listeners/JDAListener.java | 6 +- .../com/alttd/modalManager/ModalManager.java | 4 +- .../modals/ModalReplySuggestion.java | 90 ++++++++++++++++++ 10 files changed, 367 insertions(+), 22 deletions(-) create mode 100644 src/main/java/com/alttd/contextMenuManager/ContextMenuManager.java create mode 100644 src/main/java/com/alttd/contextMenuManager/DiscordContextMenu.java create mode 100644 src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuRespondSuggestion.java create mode 100644 src/main/java/com/alttd/modalManager/modals/ModalReplySuggestion.java diff --git a/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java b/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java index 4231747..f048635 100644 --- a/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java +++ b/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java @@ -107,7 +107,7 @@ public class ButtonSuggestionReviewAccept extends DiscordButton { } public void sendSuggestionInForum(ForumChannel forumChannel, TextChannel modLog, MessageEmbed.Field field, MessageEmbed suggestionMessage, ButtonInteractionEvent event) { - MessageCreateData messageCreateData = new MessageCreateBuilder().addContent(field.getValue()).build(); + MessageCreateData messageCreateData = new MessageCreateBuilder().addContent(field.getValue() + "\u200B").build(); forumChannel.createForumPost(field.getName(), messageCreateData).queue(success -> { event.getMessage().delete().queue(RestAction.getDefaultSuccess(), Util::handleFailure); diff --git a/src/main/java/com/alttd/commandManager/CommandManager.java b/src/main/java/com/alttd/commandManager/CommandManager.java index b685d21..c186f27 100644 --- a/src/main/java/com/alttd/commandManager/CommandManager.java +++ b/src/main/java/com/alttd/commandManager/CommandManager.java @@ -3,6 +3,7 @@ package com.alttd.commandManager; import com.alttd.commandManager.commands.AddCommand.CommandManage; import com.alttd.commandManager.commands.*; import com.alttd.commandManager.commands.PollCommand.CommandPoll; +import com.alttd.contextMenuManager.ContextMenuManager; import com.alttd.database.Database; import com.alttd.modalManager.ModalManager; import com.alttd.util.Logger; @@ -29,12 +30,12 @@ public class CommandManager extends ListenerAdapter { private final List commands; private final HashMap> commandList = new HashMap<>(); - public CommandManager(JDA jda, ModalManager modalManager) { + public CommandManager(JDA jda, ModalManager modalManager, ContextMenuManager contextMenuManager) { commandList.put("manage", new ArrayList<>(List.of(new ScopeInfo(CommandScope.GLOBAL, 0)))); loadCommands(); Logger.info("Loading commands..."); commands = List.of( - new CommandManage(jda, this), + new CommandManage(jda, this, contextMenuManager), new CommandHelp(jda, this), new CommandPoll(jda, this), new CommandSuggestion(jda, modalManager, this), diff --git a/src/main/java/com/alttd/commandManager/commands/AddCommand/CommandManage.java b/src/main/java/com/alttd/commandManager/commands/AddCommand/CommandManage.java index 546c85c..e08839b 100644 --- a/src/main/java/com/alttd/commandManager/commands/AddCommand/CommandManage.java +++ b/src/main/java/com/alttd/commandManager/commands/AddCommand/CommandManage.java @@ -4,6 +4,7 @@ import com.alttd.commandManager.CommandManager; import com.alttd.commandManager.DiscordCommand; import com.alttd.commandManager.SubCommand; import com.alttd.commandManager.SubOption; +import com.alttd.contextMenuManager.ContextMenuManager; import com.alttd.util.Logger; import com.alttd.util.Util; import net.dv8tion.jda.api.JDA; @@ -23,7 +24,7 @@ public class CommandManage extends DiscordCommand { private final HashMap subOptionsMap = new HashMap<>(); private final CommandData commandData; - public CommandManage(JDA jda, CommandManager commandManager) { + public CommandManage(JDA jda, CommandManager commandManager, ContextMenuManager contextMenuManager) { commandData = Commands.slash(getName(), "Enable commands and assign permissions") .addSubcommands( new SubcommandData("enable", "Enable a command in a channel") @@ -33,8 +34,8 @@ public class CommandManage extends DiscordCommand { ); commandData.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR)); Util.registerSubOptions(subOptionsMap, - new SubCommandEnable(commandManager, null, this), - new SubCommandEnable(commandManager, null, this) + new SubCommandEnable(commandManager, contextMenuManager, null, this), + new SubCommandDisable(commandManager, null, this) ); Util.registerCommand(commandManager, jda, commandData, getName()); } diff --git a/src/main/java/com/alttd/commandManager/commands/AddCommand/SubCommandEnable.java b/src/main/java/com/alttd/commandManager/commands/AddCommand/SubCommandEnable.java index aa7ab7d..b9c7705 100644 --- a/src/main/java/com/alttd/commandManager/commands/AddCommand/SubCommandEnable.java +++ b/src/main/java/com/alttd/commandManager/commands/AddCommand/SubCommandEnable.java @@ -1,20 +1,19 @@ package com.alttd.commandManager.commands.AddCommand; import com.alttd.commandManager.*; +import com.alttd.contextMenuManager.ContextMenuManager; +import com.alttd.contextMenuManager.DiscordContextMenu; import com.alttd.database.Database; import com.alttd.templates.Parser; import com.alttd.templates.Template; import com.alttd.util.Logger; import com.alttd.util.Util; -import com.google.protobuf.GeneratedMessageV3; -import com.mysql.cj.log.Log; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.interactions.commands.OptionMapping; import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; @@ -23,10 +22,12 @@ import java.util.stream.Collectors; public class SubCommandEnable extends SubCommand { private final CommandManager commandManager; + private final ContextMenuManager contextMenuManager; - protected SubCommandEnable(CommandManager commandManager, SubCommandGroup parentGroup, DiscordCommand parent) { + protected SubCommandEnable(CommandManager commandManager, ContextMenuManager contextMenuManager, SubCommandGroup parentGroup, DiscordCommand parent) { super(parentGroup, parent); this.commandManager = commandManager; + this.contextMenuManager = contextMenuManager; } @Override @@ -50,11 +51,22 @@ public class SubCommandEnable extends SubCommand { String commandName = option.getAsString(); DiscordCommand command = commandManager.getCommand(commandName); - if (command == null) { - event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to find a command called [" + commandName + "].")).setEphemeral(true).queue(); + if (command != null) { + tryEnableCommand(command, guild, commandName, event); return; } + DiscordContextMenu contextMenu = contextMenuManager.getContext(commandName); + if (contextMenu != null) { + tryEnableContextMenu(contextMenu, guild, commandName, event); + //todo stuff + return; + } + + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to find a command called [" + commandName + "].")).setEphemeral(true).queue(); + } + + private void tryEnableCommand(DiscordCommand command, Guild guild, String commandName, SlashCommandInteractionEvent event) { if (enableCommand(command, guild.getIdLong())) { Util.registerCommand(guild, command.getCommandData(), command.getName()); event.replyEmbeds(Util.genericSuccessEmbed("Enabled command", @@ -71,6 +83,23 @@ public class SubCommandEnable extends SubCommand { } } + private void tryEnableContextMenu(DiscordContextMenu contextMenu, Guild guild, String commandName, SlashCommandInteractionEvent event) { + if (enableContextMenu(contextMenu, guild.getIdLong())) { + Util.registerCommand(guild, contextMenu.getUserContextInteraction(), contextMenu.getContextMenuId()); + event.replyEmbeds(Util.genericSuccessEmbed("Enabled command", + Parser.parse("Successfully enabled in !", + Template.of("command", commandName.toLowerCase()), + Template.of("guild", guild.getName()) + ))).setEphemeral(true).queue(); + } else { + event.replyEmbeds(Util.genericErrorEmbed("Failed to enable command", + Parser.parse("Unable to enable in , is it already enabled?", + Template.of("command", commandName.toLowerCase()), + Template.of("guild", guild.getName()) + ))).setEphemeral(true).queue(); + } + } + private boolean enableCommand(DiscordCommand command, long guildId) { if (!commandManager.enableCommand(command.getName(), new ScopeInfo(CommandScope.GUILD, guildId))) return false; @@ -100,6 +129,35 @@ public class SubCommandEnable extends SubCommand { return true; } + private boolean enableContextMenu(DiscordContextMenu contextMenu, long guildId) { + if (!commandManager.enableCommand(contextMenu.getContextMenuId(), new ScopeInfo(CommandScope.GUILD, guildId))) + return false; + String sql = "INSERT INTO commands (command_name, scope, location_id) VALUES(?, ?, ?)"; + PreparedStatement statement = null; + + try { + statement = Database.getDatabase().getConnection().prepareStatement(sql); + statement.setString(1, contextMenu.getContextMenuId()); + statement.setString(2, "GUILD"); + statement.setLong(3, guildId); + if (statement.executeUpdate() == 0) { + Logger.warning("Unable to enable command: % for guild: %", contextMenu.getContextMenuId(), String.valueOf(guildId)); + return false; + } + } catch (SQLException exception) { + Logger.sql(exception); + return false; + } finally { + try { + if (statement != null) + statement.close(); + } catch (SQLException exception) { + Logger.sql(exception); + } + } + return true; + } + @Override public void suggest(CommandAutoCompleteInteractionEvent event) { OptionMapping option = event.getOption("command"); @@ -110,13 +168,21 @@ public class SubCommandEnable extends SubCommand { } String commandName = option.getAsString().toLowerCase(); ScopeInfo scopeInfo = new ScopeInfo(CommandScope.GLOBAL, event.getGuild().getIdLong()); - event.replyChoiceStrings(commandManager.getCommands().stream() - .map(DiscordCommand::getName) - .filter(name -> name.toLowerCase().startsWith(commandName)) - .filter(name -> !commandManager.getActiveLocations(name).contains(scopeInfo)) - .limit(25) - .collect(Collectors.toList())) - .queue(); + List collect = commandManager.getCommands().stream() + .map(DiscordCommand::getName) + .filter(name -> name.toLowerCase().startsWith(commandName)) + .filter(name -> !commandManager.getActiveLocations(name).contains(scopeInfo)) + .limit(25) + .collect(Collectors.toList()); + + collect.addAll(contextMenuManager.getContexts().stream() + .map(DiscordContextMenu::getContextMenuId) + .filter(name -> name.toLowerCase().startsWith(commandName)) + .filter(name -> !commandManager.getActiveLocations(name).contains(scopeInfo)) + .limit(25) + .collect(Collectors.toList())); + + event.replyChoiceStrings(collect).queue(); } @Override diff --git a/src/main/java/com/alttd/contextMenuManager/ContextMenuManager.java b/src/main/java/com/alttd/contextMenuManager/ContextMenuManager.java new file mode 100644 index 0000000..94e2ab6 --- /dev/null +++ b/src/main/java/com/alttd/contextMenuManager/ContextMenuManager.java @@ -0,0 +1,76 @@ +package com.alttd.contextMenuManager; + +import com.alttd.contextMenuManager.contextMenus.ContextMenuRespondSuggestion; +import com.alttd.modalManager.ModalManager; +import com.alttd.util.Util; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent; +import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.api.requests.RestAction; + +import javax.annotation.Nonnull; +import java.awt.*; +import java.util.List; +import java.util.Optional; + +public class ContextMenuManager extends ListenerAdapter { + + private final List contextMenus; + + public ContextMenuManager(ModalManager modalManager) { + contextMenus = List.of( + new ContextMenuRespondSuggestion(modalManager) + ); + } + + public DiscordContextMenu getContext(String name) { + for (DiscordContextMenu contextMenu : contextMenus) { + if (contextMenu.getContextMenuId().equalsIgnoreCase(name)) + return contextMenu; + } + return null; + } + + public List getContexts() { + return contextMenus; + } + + @Override + public void onUserContextInteraction(@Nonnull UserContextInteractionEvent event) { + String name = event.getInteraction().getName(); + Optional first = contextMenus.stream() + .filter(discordModal -> discordModal.getContextMenuId().equalsIgnoreCase(name)) + .findFirst(); + if (first.isEmpty()) { + event.replyEmbeds(new EmbedBuilder() + .setTitle("Invalid command") + .setDescription("Unable to process user context interaction with id: [" + name + "].") + .setColor(Color.RED) + .build()) + .setEphemeral(true) + .queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + first.get().execute(event); + } + + @Override + public void onMessageContextInteraction(@Nonnull MessageContextInteractionEvent event) { + String name = event.getInteraction().getName(); + Optional first = contextMenus.stream() + .filter(discordModal -> discordModal.getContextMenuId().equalsIgnoreCase(name)) + .findFirst(); + if (first.isEmpty()) { + event.replyEmbeds(new EmbedBuilder() + .setTitle("Invalid command") + .setDescription("Unable to process user context interaction with id: [" + name + "].") + .setColor(Color.RED) + .build()) + .setEphemeral(true) + .queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + first.get().execute(event); + } +} diff --git a/src/main/java/com/alttd/contextMenuManager/DiscordContextMenu.java b/src/main/java/com/alttd/contextMenuManager/DiscordContextMenu.java new file mode 100644 index 0000000..c01f5ce --- /dev/null +++ b/src/main/java/com/alttd/contextMenuManager/DiscordContextMenu.java @@ -0,0 +1,17 @@ +package com.alttd.contextMenuManager; + +import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent; +import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.build.CommandData; + +public abstract class DiscordContextMenu { + + public abstract String getContextMenuId(); + + public abstract void execute(UserContextInteractionEvent event); + + public abstract void execute(MessageContextInteractionEvent event); + + public abstract CommandData getUserContextInteraction(); + +} diff --git a/src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuRespondSuggestion.java b/src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuRespondSuggestion.java new file mode 100644 index 0000000..7e63c3e --- /dev/null +++ b/src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuRespondSuggestion.java @@ -0,0 +1,90 @@ +package com.alttd.contextMenuManager.contextMenus; + +import com.alttd.contextMenuManager.DiscordContextMenu; +import com.alttd.database.queries.commandOutputChannels.CommandOutputChannels; +import com.alttd.database.queries.commandOutputChannels.OutputType; +import com.alttd.modalManager.ModalManager; +import com.alttd.modalManager.modals.ModalReplySuggestion; +import com.alttd.util.Util; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.channel.ChannelType; +import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import net.dv8tion.jda.api.entities.channel.unions.IThreadContainerUnion; +import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion; +import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent; +import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; +import net.dv8tion.jda.api.interactions.commands.build.CommandData; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import net.dv8tion.jda.api.interactions.components.Modal; +import net.dv8tion.jda.api.requests.RestAction; + +public class ContextMenuRespondSuggestion extends DiscordContextMenu { + + private final ModalManager modalManager; + + public ContextMenuRespondSuggestion(ModalManager modalManager) { + this.modalManager = modalManager; + } + + @Override + public String getContextMenuId() { + return "Respond To Suggestion"; + } + + @Override + public void execute(UserContextInteractionEvent event) { + event.getInteraction().replyEmbeds(Util.genericErrorEmbed("Error", "This interaction should have been a message interaction")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + } + + @Override + public void execute(MessageContextInteractionEvent event) { + Message message = event.getInteraction().getTarget(); + if (!isSuggestion(message)) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This is not a suggestion")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + Modal replySuggestion = modalManager.getModalFor("reply_suggestion"); + if (replySuggestion == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to find reply suggestion modal")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + ModalReplySuggestion.putMessage(event.getUser().getIdLong(), message); //TODO find a better way to do this + event.replyModal(replySuggestion).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + } + + @Override + public CommandData getUserContextInteraction() { + return Commands.message(getContextMenuId()) + .setGuildOnly(true) + .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR)); + } + + public boolean isSuggestion(Message message) { + GuildChannel channel = CommandOutputChannels.getOutputChannel(message.getGuild(), OutputType.SUGGESTION); + if (channel == null) + return false; + MessageChannelUnion messageChannel = message.getChannel(); + if (channel.getType().equals(ChannelType.FORUM)) { + if (messageChannel.getType() != ChannelType.GUILD_PUBLIC_THREAD) { + return false; + } + ThreadChannel threadChannel = messageChannel.asThreadChannel(); + IThreadContainerUnion parentChannel = threadChannel.getParentChannel(); + if (!parentChannel.getType().equals(ChannelType.FORUM)) + return false; + + return message.getIdLong() == messageChannel.getIdLong() && message.getAuthor().equals(message.getJDA().getSelfUser()); + } else { + return channel.equals(messageChannel); + } + + } +} diff --git a/src/main/java/com/alttd/listeners/JDAListener.java b/src/main/java/com/alttd/listeners/JDAListener.java index ee7bb99..6efd4bb 100644 --- a/src/main/java/com/alttd/listeners/JDAListener.java +++ b/src/main/java/com/alttd/listeners/JDAListener.java @@ -2,6 +2,7 @@ package com.alttd.listeners; import com.alttd.buttonManager.ButtonManager; import com.alttd.commandManager.CommandManager; +import com.alttd.contextMenuManager.ContextMenuManager; import com.alttd.modalManager.ModalManager; import com.alttd.request.RequestManager; import com.alttd.util.Logger; @@ -26,8 +27,9 @@ public class JDAListener extends ListenerAdapter { Logger.info("JDA ready to register commands."); ButtonManager buttonManager = new ButtonManager(); ModalManager modalManager = new ModalManager(buttonManager); - CommandManager commandManager = new CommandManager(jda, modalManager); - jda.addEventListener(buttonManager, modalManager, commandManager); + ContextMenuManager contextMenuManager = new ContextMenuManager(modalManager); + CommandManager commandManager = new CommandManager(jda, modalManager, contextMenuManager); + jda.addEventListener(buttonManager, modalManager, commandManager, contextMenuManager); // RequestManager.init(); } diff --git a/src/main/java/com/alttd/modalManager/ModalManager.java b/src/main/java/com/alttd/modalManager/ModalManager.java index 2cb30d1..1a3a608 100644 --- a/src/main/java/com/alttd/modalManager/ModalManager.java +++ b/src/main/java/com/alttd/modalManager/ModalManager.java @@ -2,6 +2,7 @@ package com.alttd.modalManager; import com.alttd.buttonManager.ButtonManager; import com.alttd.modalManager.modals.ModalEvidence; +import com.alttd.modalManager.modals.ModalReplySuggestion; import com.alttd.modalManager.modals.ModalSuggestion; import com.alttd.util.Util; import net.dv8tion.jda.api.EmbedBuilder; @@ -23,7 +24,8 @@ public class ModalManager extends ListenerAdapter { public ModalManager(ButtonManager buttonManager) { modals = List.of( new ModalSuggestion(buttonManager), - new ModalEvidence()); + new ModalEvidence(), + new ModalReplySuggestion()); } @Override diff --git a/src/main/java/com/alttd/modalManager/modals/ModalReplySuggestion.java b/src/main/java/com/alttd/modalManager/modals/ModalReplySuggestion.java new file mode 100644 index 0000000..5d76ea9 --- /dev/null +++ b/src/main/java/com/alttd/modalManager/modals/ModalReplySuggestion.java @@ -0,0 +1,90 @@ +package com.alttd.modalManager.modals; + +import com.alttd.modalManager.DiscordModal; +import com.alttd.util.Util; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; +import net.dv8tion.jda.api.interactions.components.ActionRow; +import net.dv8tion.jda.api.interactions.components.Modal; +import net.dv8tion.jda.api.interactions.components.text.TextInput; +import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; +import net.dv8tion.jda.api.interactions.modals.ModalMapping; +import net.dv8tion.jda.api.requests.RestAction; + +import java.util.HashMap; + +public class ModalReplySuggestion extends DiscordModal { + + private static final HashMap userToMessageMap = new HashMap<>(); + + public static synchronized void putMessage(long userId, Message message) { + userToMessageMap.put(userId, message); + } + + private static synchronized Message pullMessage(long userId) { + return userToMessageMap.remove(userId); + } + + @Override + public String getModalId() { + return "reply_suggestion"; + } + + @Override + public void execute(ModalInteractionEvent event) { + ModalMapping modalMapping = event.getInteraction().getValue("response"); + if (modalMapping == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to find response in modal")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + String response = modalMapping.getAsString(); + if (response.isEmpty()) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Response in modal is empty")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + Member member = event.getMember(); + if (member == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This modal only works from within a guild")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + Message message = pullMessage(member.getIdLong()); + if (message == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to find a message for this modal")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + String[] split = message.getContentRaw().split("\u200B"); + if (split.length == 0) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "The suggestion to be edited has no content")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + event.editMessage(split[0] + "\u200B\n\n" + "Response by: " + member.getAsMention() + response) + .queue(success -> event.replyEmbeds(Util.genericSuccessEmbed("Success", "Responded to the suggestion!")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure), + failure -> event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to edit the suggestion")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure)); + } + + @Override + public Modal getModal() { + TextInput body = TextInput.create("response", "Response", TextInputStyle.PARAGRAPH) + .setPlaceholder("Response...") + .setRequiredRange(10, 1024) + .setRequired(true) + .build(); + + return Modal.create(getModalId(), "Suggestion Response") + .addActionRows(ActionRow.of(body)) + .build(); + } +} From fa3ee5e6bc970e73ded9fdc215683922c7e984b6 Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Fri, 16 Sep 2022 21:57:15 +0200 Subject: [PATCH 14/27] Fixed editing event instead of message --- .../com/alttd/modalManager/modals/ModalReplySuggestion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/alttd/modalManager/modals/ModalReplySuggestion.java b/src/main/java/com/alttd/modalManager/modals/ModalReplySuggestion.java index 5d76ea9..1c0422f 100644 --- a/src/main/java/com/alttd/modalManager/modals/ModalReplySuggestion.java +++ b/src/main/java/com/alttd/modalManager/modals/ModalReplySuggestion.java @@ -68,7 +68,7 @@ public class ModalReplySuggestion extends DiscordModal { return; } - event.editMessage(split[0] + "\u200B\n\n" + "Response by: " + member.getAsMention() + response) + message.editMessage(split[0] + "\u200B\n\n" + "**Response by: " + member.getAsMention() + "**\n" + response.replaceAll("\u200B", "")) .queue(success -> event.replyEmbeds(Util.genericSuccessEmbed("Success", "Responded to the suggestion!")) .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure), failure -> event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to edit the suggestion")) From d4411af654c0da5003fd40eb9c52c09456195112 Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Fri, 16 Sep 2022 21:57:33 +0200 Subject: [PATCH 15/27] Filter out zero width spaces to prevent ppl messing with the bot --- .../java/com/alttd/modalManager/modals/ModalSuggestion.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/alttd/modalManager/modals/ModalSuggestion.java b/src/main/java/com/alttd/modalManager/modals/ModalSuggestion.java index 3fd9ce1..55a9a7d 100644 --- a/src/main/java/com/alttd/modalManager/modals/ModalSuggestion.java +++ b/src/main/java/com/alttd/modalManager/modals/ModalSuggestion.java @@ -45,8 +45,8 @@ public class ModalSuggestion extends DiscordModal { .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); return; } - String title = modalMappings.get(0).getAsString(); - String desc = modalMappings.get(1).getAsString(); + String title = modalMappings.get(0).getAsString().replaceAll("\u200B", ""); + String desc = modalMappings.get(1).getAsString().replaceAll("\u200B", ""); Guild guild = event.getGuild(); if (guild == null) { From ce8cabdbc999f41391970cdc3c9174b70449715e Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Fri, 16 Sep 2022 21:57:46 +0200 Subject: [PATCH 16/27] Updated to the latest commit for the forum branch --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 5f85c5b..df16528 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -55,7 +55,7 @@ tasks { dependencies { // JDA // implementation("net.dv8tion:JDA:5.0.0-alpha.19") { - implementation("com.github.DV8FromTheWorld:JDA:1f9c577") { + implementation("com.github.DV8FromTheWorld:JDA:7d09a14") { exclude("opus-java") // exclude audio } // MySQL From 40b126bbe84b4705accf2379bcb865f447772b55 Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Fri, 16 Sep 2022 22:05:48 +0200 Subject: [PATCH 17/27] Made response italic --- .../com/alttd/modalManager/modals/ModalReplySuggestion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/alttd/modalManager/modals/ModalReplySuggestion.java b/src/main/java/com/alttd/modalManager/modals/ModalReplySuggestion.java index 1c0422f..421a5aa 100644 --- a/src/main/java/com/alttd/modalManager/modals/ModalReplySuggestion.java +++ b/src/main/java/com/alttd/modalManager/modals/ModalReplySuggestion.java @@ -68,7 +68,7 @@ public class ModalReplySuggestion extends DiscordModal { return; } - message.editMessage(split[0] + "\u200B\n\n" + "**Response by: " + member.getAsMention() + "**\n" + response.replaceAll("\u200B", "")) + message.editMessage(split[0] + "\u200B\n\n" + "**Response by: " + member.getAsMention() + "**\n_" + response.replaceAll("\u200B", "") + "_") .queue(success -> event.replyEmbeds(Util.genericSuccessEmbed("Success", "Responded to the suggestion!")) .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure), failure -> event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to edit the suggestion")) From accd875eaaa6f6ef087d1c0fb41a35333723c38c Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Fri, 16 Sep 2022 22:52:31 +0200 Subject: [PATCH 18/27] Added a toggle role command and a command to manage the roles that can be toggled --- .../alttd/commandManager/CommandManager.java | 5 +- .../commands/CommandSetToggleableRoles.java | 147 ++++++++++++++++++ .../commands/CommandToggleRole.java | 108 +++++++++++++ .../com/alttd/database/DatabaseTables.java | 18 ++- .../queries/QueriesToggleableRoles.java | 67 ++++++++ 5 files changed, 342 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/alttd/commandManager/commands/CommandSetToggleableRoles.java create mode 100644 src/main/java/com/alttd/commandManager/commands/CommandToggleRole.java create mode 100644 src/main/java/com/alttd/database/queries/QueriesToggleableRoles.java diff --git a/src/main/java/com/alttd/commandManager/CommandManager.java b/src/main/java/com/alttd/commandManager/CommandManager.java index c186f27..a03bc7e 100644 --- a/src/main/java/com/alttd/commandManager/CommandManager.java +++ b/src/main/java/com/alttd/commandManager/CommandManager.java @@ -34,6 +34,7 @@ public class CommandManager extends ListenerAdapter { commandList.put("manage", new ArrayList<>(List.of(new ScopeInfo(CommandScope.GLOBAL, 0)))); loadCommands(); Logger.info("Loading commands..."); + CommandSetToggleableRoles commandSetToggleableRoles = new CommandSetToggleableRoles(jda, this); commands = List.of( new CommandManage(jda, this, contextMenuManager), new CommandHelp(jda, this), @@ -43,7 +44,9 @@ public class CommandManager extends ListenerAdapter { new CommandUpdateCommands(jda, this), new CommandEvidence(jda, modalManager, this), new CommandFlag(jda, this), - new CommandHistory(jda, this)); + new CommandHistory(jda, this), + commandSetToggleableRoles, + new CommandToggleRole(commandSetToggleableRoles, jda, this)); } @Override diff --git a/src/main/java/com/alttd/commandManager/commands/CommandSetToggleableRoles.java b/src/main/java/com/alttd/commandManager/commands/CommandSetToggleableRoles.java new file mode 100644 index 0000000..5179d1b --- /dev/null +++ b/src/main/java/com/alttd/commandManager/commands/CommandSetToggleableRoles.java @@ -0,0 +1,147 @@ +package com.alttd.commandManager.commands; + +import com.alttd.commandManager.CommandManager; +import com.alttd.commandManager.DiscordCommand; +import com.alttd.database.queries.QueriesToggleableRoles; +import com.alttd.util.Util; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.CommandData; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import net.dv8tion.jda.api.requests.RestAction; + +import java.awt.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.stream.Collectors; + +public class CommandSetToggleableRoles extends DiscordCommand { + + private final HashMap> guildToRolesMap; + private final CommandData commandData; + + public CommandSetToggleableRoles(JDA jda, CommandManager commandManager) { + guildToRolesMap = QueriesToggleableRoles.getToggleableRoles(); + + commandData = Commands.slash(getName(), "Set which roles can be toggled") + .addOption(OptionType.ROLE, "role", "The role you want to toggle on/off", false) + .setDefaultPermissions(DefaultMemberPermissions.DISABLED); + + Util.registerCommand(commandManager, jda, commandData, getName()); + } + + @Override + public String getName() { + return "settoggleableroles"; + } + + @Override + public void execute(SlashCommandInteractionEvent event) { + Guild guild = event.getGuild(); + if (guild == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This command has to be ran in a guild")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + List options = event.getInteraction().getOptions(); + if (options.size() == 0) { + String toggleableRoles = getToggleableRoles(guild); + MessageEmbed messageEmbed = new EmbedBuilder() + .setTitle("Active roles") + .setColor(Color.GREEN) + .setDescription(toggleableRoles) + .build(); + + event.replyEmbeds(messageEmbed).setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + OptionMapping optionMapping = options.get(0); + Role role = optionMapping.getAsRole(); + if (containsRole(role)) { + if (!QueriesToggleableRoles.removeRoleToggleable(role)) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to remove role from the database")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + removeRole(role); + event.replyEmbeds(Util.genericSuccessEmbed("Success", "Removed " + role.getAsMention() + " from the toggleable roles")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + } else { + if (role.hasPermission(Permission.ADMINISTRATOR) || + role.hasPermission(Permission.MANAGE_ROLES) || + role.hasPermission(Permission.MANAGE_CHANNEL) || + role.hasPermission(Permission.MANAGE_THREADS) || + role.hasPermission(Permission.MANAGE_WEBHOOKS) || + role.hasPermission(Permission.MANAGE_SERVER) || + role.hasPermission(Permission.MANAGE_PERMISSIONS) || + role.hasPermission(Permission.MESSAGE_MANAGE) || + role.hasPermission(Permission.MODERATE_MEMBERS)) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "For safety reason this bot can not add roles which have a manage or moderator permission")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + if (!QueriesToggleableRoles.addRoleToggleable(role)) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to store role in the database")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + addRole(role); + event.replyEmbeds(Util.genericSuccessEmbed("Success", "Added " + role.getAsMention() + " to the toggleable roles")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + } + } + + private void addRole(Role role) { + long guild = role.getGuild().getIdLong(); + HashSet set = guildToRolesMap.getOrDefault(guild, new HashSet<>()); + set.add(role.getIdLong()); + guildToRolesMap.put(guild, set); + } + + private void removeRole(Role role) { + long guild = role.getGuild().getIdLong(); + HashSet set = guildToRolesMap.getOrDefault(guild, new HashSet<>()); + if (set.isEmpty()) + return; + set.remove(role.getIdLong()); + guildToRolesMap.put(guild, set); + } + + public boolean containsRole(Role role) { + return guildToRolesMap.getOrDefault(role.getGuild().getIdLong(), new HashSet<>()).contains(role.getIdLong()); + } + + public String getToggleableRoles(Guild guild) { + HashSet roleIds = guildToRolesMap.get(guild.getIdLong()); + return guild.getRoles().stream() + .filter(role -> roleIds.contains(role.getIdLong())) + .map(Role::getAsMention) + .collect(Collectors.joining("\n")); + } + + @Override + public void suggest(CommandAutoCompleteInteractionEvent event) { + event.replyChoices(Collections.emptyList()).queue(); + } + + @Override + public String getHelpMessage() { + return null; + } + + @Override + public CommandData getCommandData() { + return commandData; + } +} diff --git a/src/main/java/com/alttd/commandManager/commands/CommandToggleRole.java b/src/main/java/com/alttd/commandManager/commands/CommandToggleRole.java new file mode 100644 index 0000000..2d7f261 --- /dev/null +++ b/src/main/java/com/alttd/commandManager/commands/CommandToggleRole.java @@ -0,0 +1,108 @@ +package com.alttd.commandManager.commands; + +import com.alttd.commandManager.CommandManager; +import com.alttd.commandManager.DiscordCommand; +import com.alttd.util.Util; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.CommandData; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import net.dv8tion.jda.api.requests.RestAction; + +import java.awt.*; +import java.util.Collections; +import java.util.List; + +public class CommandToggleRole extends DiscordCommand { + + private final CommandSetToggleableRoles commandSetToggleableRoles; + private final CommandData commandData; + + public CommandToggleRole(CommandSetToggleableRoles commandSetToggleableRoles, JDA jda, CommandManager commandManager) { + this.commandSetToggleableRoles = commandSetToggleableRoles; + commandData = Commands.slash(getName(), "Toggle a role") + .addOption(OptionType.ROLE, "role", "The role you want to toggle on/off (run the command without this option to see all available roles)", false) + .setDefaultPermissions(DefaultMemberPermissions.ENABLED); + + Util.registerCommand(commandManager, jda, commandData, getName()); + } + + @Override + public String getName() { + return "togglerole"; + } + + @Override + public void execute(SlashCommandInteractionEvent event) { + Guild guild = event.getGuild(); + if (guild == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This command has to be ran in a guild")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + List options = event.getInteraction().getOptions(); + if (options.size() == 0) { + String toggleableRoles = commandSetToggleableRoles.getToggleableRoles(guild); + MessageEmbed messageEmbed = new EmbedBuilder() + .setTitle("Toggleable roles") + .setColor(Color.GREEN) + .setDescription(toggleableRoles) + .build(); + + event.replyEmbeds(messageEmbed).setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + OptionMapping optionMapping = options.get(0); + Role role = optionMapping.getAsRole(); + if (!commandSetToggleableRoles.containsRole(role)) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This role is not toggleable!")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + Member member = event.getMember(); + if (member == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This command has to be ran in a guild")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + + if (member.getRoles().contains(role)) { + guild.removeRoleFromMember(member, role).queue(success -> + event.replyEmbeds(Util.genericSuccessEmbed("Role removed", "You no longer have " + role.getAsMention() + ".")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure), + error -> event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to manage your roles.")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure)); + } else { + guild.addRoleToMember(member, role).queue(success -> + event.replyEmbeds(Util.genericSuccessEmbed("Role add", "You now have " + role.getAsMention() + ".")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure), + error -> event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to manage your roles.")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure)); + } + } + + @Override + public void suggest(CommandAutoCompleteInteractionEvent event) { + event.replyChoices(Collections.emptyList()).queue(); + } + + @Override + public String getHelpMessage() { + return null; + } + + @Override + public CommandData getCommandData() { + return commandData; + } +} diff --git a/src/main/java/com/alttd/database/DatabaseTables.java b/src/main/java/com/alttd/database/DatabaseTables.java index 546dd78..d1c0b3f 100644 --- a/src/main/java/com/alttd/database/DatabaseTables.java +++ b/src/main/java/com/alttd/database/DatabaseTables.java @@ -65,7 +65,7 @@ public class DatabaseTables { connection.prepareStatement(sql).executeUpdate(); } catch (SQLException e) { Logger.sql(e); - Logger.severe("Unable to create polls table, shutting down..."); + Logger.severe("Unable to create commands table, shutting down..."); } } @@ -81,7 +81,21 @@ public class DatabaseTables { connection.prepareStatement(sql).executeUpdate(); } catch (SQLException e) { Logger.sql(e); - Logger.severe("Unable to create polls table, shutting down..."); + Logger.severe("Unable to create output channel table, shutting down..."); + } + } + + private void createToggleableRolesTable() { + String sql = "CREATE TABLE IF NOT EXISTS toggleable_roles(" + + "guild BIGINT NOT NULL, " + + "role BIGINT NOT NULL, " + + "PRIMARY KEY (guild, role)" + + ")"; + try { + connection.prepareStatement(sql).executeUpdate(); + } catch (SQLException e) { + Logger.sql(e); + Logger.severe("Unable to create toggleable roles table, shutting down..."); } } diff --git a/src/main/java/com/alttd/database/queries/QueriesToggleableRoles.java b/src/main/java/com/alttd/database/queries/QueriesToggleableRoles.java new file mode 100644 index 0000000..e9464dd --- /dev/null +++ b/src/main/java/com/alttd/database/queries/QueriesToggleableRoles.java @@ -0,0 +1,67 @@ +package com.alttd.database.queries; + +import com.alttd.database.Database; +import net.dv8tion.jda.api.entities.Role; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.HashSet; + +public class QueriesToggleableRoles { + + public static boolean addRoleToggleable(Role role) { + String sql = "INSERT INTO toggleable_roles (guild, role) VALUES (?, ?)"; + try { + PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql); + + preparedStatement.setLong(1, role.getGuild().getIdLong()); + preparedStatement.setLong(2, role.getIdLong()); + + return preparedStatement.executeUpdate() == 1; + } catch (SQLException exception) { + exception.printStackTrace(); + } + return false; + } + + public static boolean removeRoleToggleable(Role role) { + String sql = "DELETE FROM toggleable_roles WHERE guild = ? AND role = ?"; + try { + PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql); + + preparedStatement.setLong(1, role.getGuild().getIdLong()); + preparedStatement.setLong(2, role.getIdLong()); + + return preparedStatement.executeUpdate() == 1; + } catch (SQLException exception) { + exception.printStackTrace(); + } + return false; + } + + public static HashMap> getToggleableRoles() { + String sql = "SELECT * FROM toggleable_roles"; + try { + HashMap> map = new HashMap<>(); + PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql); + + ResultSet resultSet = preparedStatement.executeQuery(); + + while (resultSet.next()) { + long guild = resultSet.getLong("guild"); + long role = resultSet.getLong("role"); + HashSet roles = map.getOrDefault(guild, new HashSet<>()); + roles.add(role); + map.put(guild, roles); + } + + return map; + } catch (SQLException exception) { + exception.printStackTrace(); + } + return null; + } + +} From 6b8ec12d48f9a7eaa2a44f266c76367bdc5307de Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Fri, 16 Sep 2022 23:45:31 +0200 Subject: [PATCH 19/27] Added the player's name in the suggestion they made --- .../ButtonSuggestionReviewAccept.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java b/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java index f048635..85a42a7 100644 --- a/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java +++ b/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java @@ -65,15 +65,23 @@ public class ButtonSuggestionReviewAccept extends DiscordButton { return; } + String mentionMember = reviewMessage.getDescription(); + if (mentionMember == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "This message contains no description, can't be a suggestion")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + return; + } + MessageEmbed suggestionMessage = new EmbedBuilder(reviewMessage) .clearFields() .setColor(Color.GRAY) .setTitle(fields.get(0).getName()) .setDescription(fields.get(0).getValue()) + .addField("Suggestion by", mentionMember, false) .build(); if (suggestionGuildChannel instanceof ForumChannel forumChannel) { - sendSuggestionInForum(forumChannel, modLogChannel, fields.get(0), suggestionMessage, event); + sendSuggestionInForum(forumChannel, modLogChannel, fields.get(0), suggestionMessage, mentionMember, event); } else if (suggestionGuildChannel instanceof TextChannel forumChannel) { sendSuggestionEmbed(forumChannel, modLogChannel, suggestionMessage, event); } else { @@ -106,8 +114,8 @@ public class ButtonSuggestionReviewAccept extends DiscordButton { .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure)); } - public void sendSuggestionInForum(ForumChannel forumChannel, TextChannel modLog, MessageEmbed.Field field, MessageEmbed suggestionMessage, ButtonInteractionEvent event) { - MessageCreateData messageCreateData = new MessageCreateBuilder().addContent(field.getValue() + "\u200B").build(); + public void sendSuggestionInForum(ForumChannel forumChannel, TextChannel modLog, MessageEmbed.Field field, MessageEmbed suggestionMessage, String mentionMember, ButtonInteractionEvent event) { + MessageCreateData messageCreateData = new MessageCreateBuilder().addContent("**Suggestion by: " + mentionMember + "**\n\n" + field.getValue() + "\u200B").build(); forumChannel.createForumPost(field.getName(), messageCreateData).queue(success -> { event.getMessage().delete().queue(RestAction.getDefaultSuccess(), Util::handleFailure); From 7dd9bbcdd2bd1373d3312745ff8041527eed39fb Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Sat, 17 Sep 2022 02:20:47 +0200 Subject: [PATCH 20/27] Add reactions to suggestions when posted --- .../buttons/suggestionReview/ButtonSuggestionReviewAccept.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java b/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java index 85a42a7..be63c1d 100644 --- a/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java +++ b/src/main/java/com/alttd/buttonManager/buttons/suggestionReview/ButtonSuggestionReviewAccept.java @@ -11,6 +11,7 @@ import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.requests.RestAction; @@ -121,6 +122,8 @@ public class ButtonSuggestionReviewAccept extends DiscordButton { event.getMessage().delete().queue(RestAction.getDefaultSuccess(), Util::handleFailure); event.replyEmbeds(Util.genericSuccessEmbed("Success", "The suggestion was accepted and posted in the suggestion channel")).setEphemeral(true).queue(); sendModLog(modLog, suggestionMessage, event); + success.getMessage().addReaction(Emoji.fromUnicode("\uD83D\uDC4D")).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + success.getMessage().addReaction(Emoji.fromUnicode("\uD83D\uDC4E")).queue(RestAction.getDefaultSuccess(), Util::handleFailure); }, failure -> event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to send suggestion to the suggestion channel")) .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure)); } From a49bbb987b156122481a5a74275fd4cbcd9eea48 Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Sat, 17 Sep 2022 03:10:43 +0200 Subject: [PATCH 21/27] Fixed when console manager gets called --- src/main/java/com/alttd/AltitudeBot.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/alttd/AltitudeBot.java b/src/main/java/com/alttd/AltitudeBot.java index 1927481..554f58a 100644 --- a/src/main/java/com/alttd/AltitudeBot.java +++ b/src/main/java/com/alttd/AltitudeBot.java @@ -30,10 +30,9 @@ public class AltitudeBot { private void start() { Logger.info("Starting bot..."); initConfigs(); - ConsoleCommandManager.startConsoleCommands(jda); jda = JDABuilder.createDefault(SettingsConfig.TOKEN).build(); - DatabaseTables.createTables(Database.getDatabase().getConnection()); ConsoleCommandManager.startConsoleCommands(jda); + DatabaseTables.createTables(Database.getDatabase().getConnection()); // try { // jda.getPresence().setPresence( // OnlineStatus.valueOf(SettingsConfig.STATUS), From 83bb43f696c4e2bae11f4cb3a2a6c14c952a8402 Mon Sep 17 00:00:00 2001 From: Stijn Date: Wed, 21 Sep 2022 18:52:59 +0200 Subject: [PATCH 22/27] Added guildOnly to commands that need it --- .../commandManager/commands/AddCommand/CommandManage.java | 5 +++-- .../com/alttd/commandManager/commands/CommandEvidence.java | 5 ++++- .../java/com/alttd/commandManager/commands/CommandFlag.java | 3 ++- .../java/com/alttd/commandManager/commands/CommandHelp.java | 5 ++++- .../com/alttd/commandManager/commands/CommandHistory.java | 3 ++- .../commandManager/commands/CommandSetOutputChannel.java | 3 ++- .../com/alttd/commandManager/commands/CommandSuggestion.java | 5 ++++- .../alttd/commandManager/commands/CommandUpdateCommands.java | 3 ++- .../commandManager/commands/PollCommand/CommandPoll.java | 5 +++-- 9 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/alttd/commandManager/commands/AddCommand/CommandManage.java b/src/main/java/com/alttd/commandManager/commands/AddCommand/CommandManage.java index e08839b..f1a64d2 100644 --- a/src/main/java/com/alttd/commandManager/commands/AddCommand/CommandManage.java +++ b/src/main/java/com/alttd/commandManager/commands/AddCommand/CommandManage.java @@ -31,8 +31,9 @@ public class CommandManage extends DiscordCommand { .addOption(OptionType.STRING, "command", "Name of the command to enable", true, true), new SubcommandData("disable", "Disable a command") .addOption(OptionType.STRING, "command", "Name of the command to disable", true, true) - ); - commandData.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR)); + ) + .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR)) + .setGuildOnly(true); Util.registerSubOptions(subOptionsMap, new SubCommandEnable(commandManager, contextMenuManager, null, this), new SubCommandDisable(commandManager, null, this) diff --git a/src/main/java/com/alttd/commandManager/commands/CommandEvidence.java b/src/main/java/com/alttd/commandManager/commands/CommandEvidence.java index ccc366b..7dfc7bd 100644 --- a/src/main/java/com/alttd/commandManager/commands/CommandEvidence.java +++ b/src/main/java/com/alttd/commandManager/commands/CommandEvidence.java @@ -8,6 +8,7 @@ import com.alttd.util.Util; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.components.Modal; @@ -23,7 +24,9 @@ public class CommandEvidence extends DiscordCommand { public CommandEvidence(JDA jda, ModalManager modalManager, CommandManager commandManager) { this.modalManager = modalManager; - commandData = Commands.slash(getName(), "Open suggestion form."); + commandData = Commands.slash(getName(), "Open suggestion form.") + .setDefaultPermissions(DefaultMemberPermissions.DISABLED) + .setGuildOnly(true); Util.registerCommand(commandManager, jda, commandData, getName()); } diff --git a/src/main/java/com/alttd/commandManager/commands/CommandFlag.java b/src/main/java/com/alttd/commandManager/commands/CommandFlag.java index bbc9b47..034e995 100644 --- a/src/main/java/com/alttd/commandManager/commands/CommandFlag.java +++ b/src/main/java/com/alttd/commandManager/commands/CommandFlag.java @@ -31,7 +31,8 @@ public class CommandFlag extends DiscordCommand { public CommandFlag(JDA jda, CommandManager commandManager) { this.commandData = Commands.slash(getName(), "Show flags for a user") .addOption(OptionType.STRING, "user", "The user to show flags for", true) - .setDefaultPermissions(DefaultMemberPermissions.ENABLED); + .setDefaultPermissions(DefaultMemberPermissions.ENABLED) + .setGuildOnly(true); Util.registerCommand(commandManager, jda, commandData, getName()); } diff --git a/src/main/java/com/alttd/commandManager/commands/CommandHelp.java b/src/main/java/com/alttd/commandManager/commands/CommandHelp.java index 88b0d27..0e2c7ad 100644 --- a/src/main/java/com/alttd/commandManager/commands/CommandHelp.java +++ b/src/main/java/com/alttd/commandManager/commands/CommandHelp.java @@ -11,6 +11,7 @@ import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.interactions.AutoCompleteQuery; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.CommandData; @@ -30,7 +31,9 @@ public class CommandHelp extends DiscordCommand { this.commandManager = commandManager; commandData = Commands.slash(getName(), "Show info about all commands or a specific command.") - .addOption(OptionType.STRING, "command", "Command to get more info about", true , true); + .addOption(OptionType.STRING, "command", "Command to get more info about", true , true) + .setDefaultPermissions(DefaultMemberPermissions.ENABLED) + .setGuildOnly(true); Util.registerCommand(commandManager, jda, commandData, getName()); } diff --git a/src/main/java/com/alttd/commandManager/commands/CommandHistory.java b/src/main/java/com/alttd/commandManager/commands/CommandHistory.java index aacd21e..7dcf957 100644 --- a/src/main/java/com/alttd/commandManager/commands/CommandHistory.java +++ b/src/main/java/com/alttd/commandManager/commands/CommandHistory.java @@ -33,7 +33,8 @@ public class CommandHistory extends DiscordCommand { this.commandData = Commands.slash(getName(), "Show history for a user") .addOption(OptionType.STRING, "user", "The user to show history for", true) .addOption(OptionType.STRING, "type", "The type of punishment to show", false, true) - .setDefaultPermissions(DefaultMemberPermissions.ENABLED); + .setDefaultPermissions(DefaultMemberPermissions.ENABLED) + .setGuildOnly(true); Util.registerCommand(commandManager, jda, commandData, getName()); } diff --git a/src/main/java/com/alttd/commandManager/commands/CommandSetOutputChannel.java b/src/main/java/com/alttd/commandManager/commands/CommandSetOutputChannel.java index 75a21c8..a0220a5 100644 --- a/src/main/java/com/alttd/commandManager/commands/CommandSetOutputChannel.java +++ b/src/main/java/com/alttd/commandManager/commands/CommandSetOutputChannel.java @@ -33,7 +33,8 @@ public class CommandSetOutputChannel extends DiscordCommand { commandData = Commands.slash(getName(), "Set up output channels") .addOption(OptionType.STRING, "type", "The type of output channel", true, true) .addOption(OptionType.CHANNEL, "channel", "The channel the specified output should go into", true) - .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR)); + .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR)) + .setGuildOnly(true); Util.registerCommand(commandManager, jda, commandData, getName()); } diff --git a/src/main/java/com/alttd/commandManager/commands/CommandSuggestion.java b/src/main/java/com/alttd/commandManager/commands/CommandSuggestion.java index 9054267..0b68407 100644 --- a/src/main/java/com/alttd/commandManager/commands/CommandSuggestion.java +++ b/src/main/java/com/alttd/commandManager/commands/CommandSuggestion.java @@ -8,6 +8,7 @@ import com.alttd.util.Util; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.components.Modal; @@ -25,7 +26,9 @@ public class CommandSuggestion extends DiscordCommand { this.commandManager = commandManager; this.modalManager = modalManager; - commandData = Commands.slash(getName(), "Open suggestion form."); + commandData = Commands.slash(getName(), "Open suggestion form.") + .setGuildOnly(true) + .setDefaultPermissions(DefaultMemberPermissions.ENABLED); Util.registerCommand(commandManager, jda, commandData, getName()); } diff --git a/src/main/java/com/alttd/commandManager/commands/CommandUpdateCommands.java b/src/main/java/com/alttd/commandManager/commands/CommandUpdateCommands.java index 158b500..cad39c0 100644 --- a/src/main/java/com/alttd/commandManager/commands/CommandUpdateCommands.java +++ b/src/main/java/com/alttd/commandManager/commands/CommandUpdateCommands.java @@ -26,7 +26,8 @@ public class CommandUpdateCommands extends DiscordCommand { public CommandUpdateCommands(JDA jda, CommandManager commandManager) { this.commandManager = commandManager; this.commandData = Commands.slash(getName(), "Updates all commands for this bot in this guild") - .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR)); + .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR)) + .setGuildOnly(true); Util.registerCommand(commandManager, jda, commandData, getName()); } diff --git a/src/main/java/com/alttd/commandManager/commands/PollCommand/CommandPoll.java b/src/main/java/com/alttd/commandManager/commands/PollCommand/CommandPoll.java index 9aa4cba..bd66efc 100644 --- a/src/main/java/com/alttd/commandManager/commands/PollCommand/CommandPoll.java +++ b/src/main/java/com/alttd/commandManager/commands/PollCommand/CommandPoll.java @@ -53,8 +53,9 @@ public class CommandPoll extends DiscordCommand { .addOption(OptionType.STRING, "message_id", "Id of the poll you're closing", true), new SubcommandData("results", "Get the results for a poll") .addOption(OptionType.CHANNEL, "channel", "Channel this poll is in", true) - .addOption(OptionType.STRING, "message_id", "Id of the poll you want the results for", true)); - commandData.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR)); + .addOption(OptionType.STRING, "message_id", "Id of the poll you want the results for", true)) + .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR)) + .setGuildOnly(true); Util.registerSubOptions(subOptionsMap, new SubCommandAdd(null,this), new SubCommandAddButton(null, this), From d8102e4baad67cca6b9e568cbb03270501977e40 Mon Sep 17 00:00:00 2001 From: Stijn Date: Wed, 21 Sep 2022 21:49:42 +0200 Subject: [PATCH 23/27] Added reminders (in a new table for now) --- .../alttd/buttonManager/ButtonManager.java | 6 +- .../remindMeConfirm/ButtonRemindMeCancel.java | 25 +++ .../ButtonRemindMeConfirm.java | 81 +++++++++ .../alttd/commandManager/CommandManager.java | 3 +- .../commands/CommandRemindMe.java | 161 +++++++++++++++++- .../com/alttd/database/DatabaseTables.java | 22 +++ .../QueriesReminders/QueriesReminders.java | 90 ++++++++++ .../queries/QueriesReminders/Reminder.java | 33 ++++ .../java/com/alttd/listeners/JDAListener.java | 5 + .../com/alttd/modalManager/ModalManager.java | 4 +- .../modalManager/modals/ModalRemindMe.java | 134 +++++++++++++++ .../alttd/reminders/ReminderScheduler.java | 114 +++++++++++++ 12 files changed, 674 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/ButtonRemindMeCancel.java create mode 100644 src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/ButtonRemindMeConfirm.java create mode 100644 src/main/java/com/alttd/database/queries/QueriesReminders/QueriesReminders.java create mode 100644 src/main/java/com/alttd/database/queries/QueriesReminders/Reminder.java create mode 100644 src/main/java/com/alttd/modalManager/modals/ModalRemindMe.java create mode 100644 src/main/java/com/alttd/reminders/ReminderScheduler.java diff --git a/src/main/java/com/alttd/buttonManager/ButtonManager.java b/src/main/java/com/alttd/buttonManager/ButtonManager.java index 607113f..2516ac4 100644 --- a/src/main/java/com/alttd/buttonManager/ButtonManager.java +++ b/src/main/java/com/alttd/buttonManager/ButtonManager.java @@ -1,5 +1,7 @@ package com.alttd.buttonManager; +import com.alttd.buttonManager.buttons.remindMeConfirm.ButtonRemindMeCancel; +import com.alttd.buttonManager.buttons.remindMeConfirm.ButtonRemindMeConfirm; import com.alttd.buttonManager.buttons.suggestionReview.ButtonSuggestionReviewAccept; import com.alttd.buttonManager.buttons.suggestionReview.ButtonSuggestionReviewDeny; import com.alttd.util.Util; @@ -22,7 +24,9 @@ public class ButtonManager extends ListenerAdapter { public ButtonManager() { buttons = List.of( new ButtonSuggestionReviewAccept(), - new ButtonSuggestionReviewDeny()); + new ButtonSuggestionReviewDeny(), + new ButtonRemindMeCancel(), + new ButtonRemindMeConfirm()); } @Override diff --git a/src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/ButtonRemindMeCancel.java b/src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/ButtonRemindMeCancel.java new file mode 100644 index 0000000..cce9259 --- /dev/null +++ b/src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/ButtonRemindMeCancel.java @@ -0,0 +1,25 @@ +package com.alttd.buttonManager.buttons.remindMeConfirm; + +import com.alttd.buttonManager.DiscordButton; +import com.alttd.util.Util; +import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; +import net.dv8tion.jda.api.interactions.components.buttons.Button; + +public class ButtonRemindMeCancel extends DiscordButton { + @Override + public String getButtonId() { + return "remind_me_cancel"; + } + + @Override + public void execute(ButtonInteractionEvent event) { + ButtonRemindMeConfirm.removeReminder(event.getUser().getIdLong()); + event.replyEmbeds(Util.genericSuccessEmbed("Success", "Cancelled your reminder!")) + .setEphemeral(true).queue(); + } + + @Override + public Button getButton() { + return Button.danger(getButtonId(), "Cancel"); + } +} diff --git a/src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/ButtonRemindMeConfirm.java b/src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/ButtonRemindMeConfirm.java new file mode 100644 index 0000000..0b0ea5b --- /dev/null +++ b/src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/ButtonRemindMeConfirm.java @@ -0,0 +1,81 @@ +package com.alttd.buttonManager.buttons.remindMeConfirm; + +import com.alttd.buttonManager.DiscordButton; +import com.alttd.database.queries.QueriesReminders.QueriesReminders; +import com.alttd.database.queries.QueriesReminders.Reminder; +import com.alttd.reminders.ReminderScheduler; +import com.alttd.util.Util; +import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; +import net.dv8tion.jda.api.interactions.components.buttons.Button; + +import java.util.HashMap; + +public class ButtonRemindMeConfirm extends DiscordButton { + + private static final HashMap unconfirmedReminders = new HashMap<>(); + + public static synchronized void putReminder(long id, Reminder reminder) { + unconfirmedReminders.put(id, reminder); + } + + public static synchronized Reminder removeReminder(long id) { + return unconfirmedReminders.get(id); + } + + @Override + public String getButtonId() { + return "remind_me_confirm"; + } + + @Override + public void execute(ButtonInteractionEvent event) { + Reminder reminder = removeReminder(event.getUser().getIdLong()); + + if (storeReminder(reminder, event)) { + event.replyEmbeds(Util.genericSuccessEmbed("Success", "Your reminder was successfully created!")) + .setEphemeral(true).queue(); + } + } + + private boolean storeReminder(Reminder reminder, ButtonInteractionEvent event) { + if (reminder == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to retrieve reminder data for this button")) + .setEphemeral(true).queue(); + return false; + } + int id = QueriesReminders.storeReminder(reminder); + if (id == 0) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to store reminder in the database")) + .setEphemeral(true).queue(); + return false; + } + + reminder = new Reminder( + id, + reminder.title(), + reminder.description(), + reminder.userId(), + reminder.guildId(), + reminder.channelId(), + reminder.messageId(), + reminder.shouldRepeat(), + reminder.creationDate(), + reminder.remindDate()); + + ReminderScheduler instance = ReminderScheduler.getInstance(event.getJDA()); + if (instance == null) { + QueriesReminders.removeReminder(reminder.id()); + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to start reminder, removing it from the database...")) + .setEphemeral(true).queue(); + return false; + } + + instance.addReminder(reminder); + return true; + } + + @Override + public Button getButton() { + return Button.success(getButtonId(), "Confirm"); + } +} diff --git a/src/main/java/com/alttd/commandManager/CommandManager.java b/src/main/java/com/alttd/commandManager/CommandManager.java index a03bc7e..4d152f2 100644 --- a/src/main/java/com/alttd/commandManager/CommandManager.java +++ b/src/main/java/com/alttd/commandManager/CommandManager.java @@ -46,7 +46,8 @@ public class CommandManager extends ListenerAdapter { new CommandFlag(jda, this), new CommandHistory(jda, this), commandSetToggleableRoles, - new CommandToggleRole(commandSetToggleableRoles, jda, this)); + new CommandToggleRole(commandSetToggleableRoles, jda, this), + new CommandRemindMe(jda, this, modalManager)); } @Override diff --git a/src/main/java/com/alttd/commandManager/commands/CommandRemindMe.java b/src/main/java/com/alttd/commandManager/commands/CommandRemindMe.java index 11c95cc..3ffafad 100644 --- a/src/main/java/com/alttd/commandManager/commands/CommandRemindMe.java +++ b/src/main/java/com/alttd/commandManager/commands/CommandRemindMe.java @@ -1,4 +1,163 @@ package com.alttd.commandManager.commands; -public class CommandRemindMe { +import com.alttd.commandManager.CommandManager; +import com.alttd.commandManager.DiscordCommand; +import com.alttd.modalManager.ModalManager; +import com.alttd.modalManager.modals.ModalRemindMe; +import com.alttd.util.Util; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion; +import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.CommandData; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import net.dv8tion.jda.api.interactions.components.Modal; + +import java.util.Calendar; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +public class CommandRemindMe extends DiscordCommand { + + private final CommandData commandData; + private final ModalManager modalManager; + + public CommandRemindMe(JDA jda, CommandManager commandManager, ModalManager modalManager) { + this.modalManager = modalManager; + commandData = Commands.slash(getName(), "Create a reminder") + .addOption(OptionType.CHANNEL, "channel", "The channel to send the reminder in", true) + .addOption(OptionType.STRING, "fromnow", "How long from now the reminder should send", true, true) + .setDefaultPermissions(DefaultMemberPermissions.ENABLED) + .setGuildOnly(true); + + Util.registerCommand(commandManager, jda, commandData, getName()); + } + + @Override + public String getName() { + return "remindme"; + } + + @Override + public void execute(SlashCommandInteractionEvent event) { + TextChannel channel = getValidChannel( + event.getInteraction().getOption("channel", OptionMapping::getAsChannel), event); + if (channel == null) + return; + + Long fromNow = getFromNow(event.getInteraction().getOption("fromnow", OptionMapping::getAsString), event); + if (fromNow == null) + return; + + Modal modal = modalManager.getModalFor("remindme"); + if (modal == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to retrieve remind me modal")) + .setEphemeral(true).queue(); + return; + } + + ModalRemindMe.putData(event.getIdLong(), channel, fromNow); + event.replyModal(modal).queue(); + } + + private TextChannel getValidChannel(GuildChannelUnion channel, SlashCommandInteractionEvent event) { + if (channel == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Couldn't find channel")) + .setEphemeral(true).queue(); + return null; + } + + if (!(channel instanceof TextChannel textChannel)) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Not a valid TextChannel")) + .setEphemeral(true).queue(); + return null; + } + + if (!textChannel.canTalk()) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "I can't talk in this channel")) + .setEphemeral(true).queue(); + return null; + } + return textChannel; + } + + private Long getFromNow(String fromNow, SlashCommandInteractionEvent event) { + if (fromNow == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Couldn't find from now option")) + .setEphemeral(true).queue(); + return null; + } + + if (!fromNow.matches("[1-9][0-9]*[dmy]")) { + return fromNowTimestamp(fromNow, event); + } + + int i; + try { + i = Integer.parseInt(fromNow.substring(0, fromNow.length() - 1)); + } catch (NumberFormatException e) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Invalid number")) + .setEphemeral(true).queue(); + return null; + } + + switch (fromNow.substring(fromNow.length() - 1)) { + case "d" -> { + return TimeUnit.DAYS.toMillis(i) + new Date().getTime(); + } + case "m" -> { + Calendar instance = Calendar.getInstance(); + instance.setTime(new Date()); + instance.add(Calendar.MONTH, i); + return instance.getTimeInMillis(); + } + case "y" -> { + Calendar instance = Calendar.getInstance(); + instance.setTime(new Date()); + instance.add(Calendar.YEAR, i); + return instance.getTimeInMillis(); + } + default -> { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Invalid format? This shouldn't be possible...")) + .setEphemeral(true).queue(); + return null; + } + } + } + + private Long fromNowTimestamp(String fromNow, SlashCommandInteractionEvent event) { + if (!fromNow.matches("t:[1-9][0-9]*")) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Invalid from now format ex: `1d`")) + .setEphemeral(true).queue(); + return null; + } + long l; + try { + l = Long.parseLong(fromNow.substring(2)); + } catch (NumberFormatException e) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Invalid number")) + .setEphemeral(true).queue(); + return null; + } + return l; + } + + @Override + public void suggest(CommandAutoCompleteInteractionEvent event) { + //TODO implement suggest + } + + @Override + public String getHelpMessage() { + return null; + } + + @Override + public CommandData getCommandData() { + return commandData; + } } diff --git a/src/main/java/com/alttd/database/DatabaseTables.java b/src/main/java/com/alttd/database/DatabaseTables.java index d1c0b3f..4150d5f 100644 --- a/src/main/java/com/alttd/database/DatabaseTables.java +++ b/src/main/java/com/alttd/database/DatabaseTables.java @@ -99,6 +99,28 @@ public class DatabaseTables { } } + private void createReminderTable() { + String sql = "CREATE TABLE IF NOT EXISTS new_reminders(" + + "id INT NOT NULL AUTO_INCREMENT, " + + "title VARCHAR(256) NOT NULL, " + + "description VARCHAR(4096) NOT NULL, " + + "user_id LONG NOT NULL, " + + "guild_id LONG NOT NULL, " + + "channel_id LONG NOT NULL, " + + "message_id LONG NOT NULL, " + + "should_repeat TINYINT(1) NOT NULL, " + + "creation_date LONG NOT NULL, " + + "remind_date LONG NOT NULL, " + + "PRIMARY KEY (id)" + + ")"; + try { + connection.prepareStatement(sql).executeUpdate(); + } catch (SQLException e) { + Logger.sql(e); + Logger.severe("Unable to create reminders table, shutting down..."); + } + } + public static void createTables(Connection connection) { if (instance == null) instance = new DatabaseTables(connection); diff --git a/src/main/java/com/alttd/database/queries/QueriesReminders/QueriesReminders.java b/src/main/java/com/alttd/database/queries/QueriesReminders/QueriesReminders.java new file mode 100644 index 0000000..fba207c --- /dev/null +++ b/src/main/java/com/alttd/database/queries/QueriesReminders/QueriesReminders.java @@ -0,0 +1,90 @@ +package com.alttd.database.queries.QueriesReminders; + +import com.alttd.database.Database; +import com.alttd.util.Logger; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; + +public class QueriesReminders { + + public static int storeReminder(Reminder reminder) { + String sql = "INSERT INTO new_reminders " + + "(title, description, user_id, guild_id, channel_id, message_id, should_repeat, creation_date, remind_date) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + try { + PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + + preparedStatement.setString(1, reminder.title()); + preparedStatement.setString(2, reminder.description()); + preparedStatement.setLong(3, reminder.userId()); + preparedStatement.setLong(4, reminder.guildId()); + preparedStatement.setLong(5, reminder.channelId()); + preparedStatement.setLong(6, 0); + preparedStatement.setInt(7, reminder.shouldRepeat() ? 1 : 0); + preparedStatement.setLong(8, reminder.creationDate()); + preparedStatement.setLong(9, reminder.remindDate()); + + if (preparedStatement.executeUpdate() == 1) { + ResultSet generatedKeys = preparedStatement.getGeneratedKeys(); + if (generatedKeys.next()) { + return generatedKeys.getInt(1); + } + } + + return -1; + } catch (SQLException e) { + Logger.exception(e); + } + return -1; + } + + public static boolean removeReminder(int id) { + String sql = "DELETE FROM new_reminders WHERE id = ?"; + try { + PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql); + + preparedStatement.setInt(1, id); + + return preparedStatement.executeUpdate() == 1; + } catch (SQLException e) { + Logger.exception(e); + } + return false; + } + + public static ArrayList getReminders() { + String sql = "SELECT * FROM new_reminders"; + try { + ArrayList reminders = new ArrayList<>(); + PreparedStatement preparedStatement = Database.getDatabase().getConnection().prepareStatement(sql); + + ResultSet resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + reminders.add(getReminder(resultSet)); + } + return reminders; + } catch (SQLException e) { + Logger.exception(e); + } + return null; + } + + private static Reminder getReminder(ResultSet resultSet) throws SQLException { + int id = resultSet.getInt("id"); + String title = resultSet.getString("title"); + String desc = resultSet.getString("description"); + long userId = resultSet.getLong("user_id"); + long guildId = resultSet.getLong("guild_id"); + long channelId = resultSet.getLong("channel_id"); + long messageId = resultSet.getLong("message_id"); + boolean shouldRepeat = resultSet.getInt("should_repeat") == 1; + long creationDate = resultSet.getLong("creation_date"); + long remindDate = resultSet.getLong("remind_date"); + return new Reminder(id, title, desc, userId, guildId, channelId, messageId, shouldRepeat, creationDate, remindDate); + } + +} diff --git a/src/main/java/com/alttd/database/queries/QueriesReminders/Reminder.java b/src/main/java/com/alttd/database/queries/QueriesReminders/Reminder.java new file mode 100644 index 0000000..6cb9934 --- /dev/null +++ b/src/main/java/com/alttd/database/queries/QueriesReminders/Reminder.java @@ -0,0 +1,33 @@ +package com.alttd.database.queries.QueriesReminders; + +import com.alttd.util.Logger; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; + +public record Reminder (int id, String title, String description, long userId, long guildId, long channelId, + long messageId, boolean shouldRepeat, long creationDate, long remindDate) { + public TextChannel getChannel(JDA jda) { + Guild guildById = getGuild(jda); + if (guildById == null) + return null; + + TextChannel textChannelById = guildById.getTextChannelById(this.channelId); + if (textChannelById == null) { + Logger.warning("Unable to find text channel for reminder, text channel id: [" + channelId + "]"); + return null; + } + + return textChannelById; + } + + public Guild getGuild(JDA jda) { + Guild guildById = jda.getGuildById(guildId); + if (guildById == null) { + Logger.warning("Unable to find guild for reminder, guild id: [" + guildId + "]"); + return null; + } + + return guildById; + } +} diff --git a/src/main/java/com/alttd/listeners/JDAListener.java b/src/main/java/com/alttd/listeners/JDAListener.java index 6efd4bb..d117d58 100644 --- a/src/main/java/com/alttd/listeners/JDAListener.java +++ b/src/main/java/com/alttd/listeners/JDAListener.java @@ -4,6 +4,7 @@ import com.alttd.buttonManager.ButtonManager; import com.alttd.commandManager.CommandManager; import com.alttd.contextMenuManager.ContextMenuManager; import com.alttd.modalManager.ModalManager; +import com.alttd.reminders.ReminderScheduler; import com.alttd.request.RequestManager; import com.alttd.util.Logger; import net.dv8tion.jda.api.JDA; @@ -30,6 +31,10 @@ public class JDAListener extends ListenerAdapter { ContextMenuManager contextMenuManager = new ContextMenuManager(modalManager); CommandManager commandManager = new CommandManager(jda, modalManager, contextMenuManager); jda.addEventListener(buttonManager, modalManager, commandManager, contextMenuManager); + ReminderScheduler reminderScheduler = ReminderScheduler.getInstance(jda); + if (reminderScheduler == null) { + Logger.severe("Unable to start reminder scheduler!"); + } // RequestManager.init(); } diff --git a/src/main/java/com/alttd/modalManager/ModalManager.java b/src/main/java/com/alttd/modalManager/ModalManager.java index 1a3a608..ac2d6b7 100644 --- a/src/main/java/com/alttd/modalManager/ModalManager.java +++ b/src/main/java/com/alttd/modalManager/ModalManager.java @@ -2,6 +2,7 @@ package com.alttd.modalManager; import com.alttd.buttonManager.ButtonManager; import com.alttd.modalManager.modals.ModalEvidence; +import com.alttd.modalManager.modals.ModalRemindMe; import com.alttd.modalManager.modals.ModalReplySuggestion; import com.alttd.modalManager.modals.ModalSuggestion; import com.alttd.util.Util; @@ -25,7 +26,8 @@ public class ModalManager extends ListenerAdapter { modals = List.of( new ModalSuggestion(buttonManager), new ModalEvidence(), - new ModalReplySuggestion()); + new ModalReplySuggestion(), + new ModalRemindMe(buttonManager)); } @Override diff --git a/src/main/java/com/alttd/modalManager/modals/ModalRemindMe.java b/src/main/java/com/alttd/modalManager/modals/ModalRemindMe.java new file mode 100644 index 0000000..4286cae --- /dev/null +++ b/src/main/java/com/alttd/modalManager/modals/ModalRemindMe.java @@ -0,0 +1,134 @@ +package com.alttd.modalManager.modals; + +import com.alttd.buttonManager.ButtonManager; +import com.alttd.buttonManager.buttons.remindMeConfirm.ButtonRemindMeConfirm; +import com.alttd.database.queries.QueriesReminders.Reminder; +import com.alttd.modalManager.DiscordModal; +import com.alttd.util.Util; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; +import net.dv8tion.jda.api.interactions.components.ActionRow; +import net.dv8tion.jda.api.interactions.components.Modal; +import net.dv8tion.jda.api.interactions.components.buttons.Button; +import net.dv8tion.jda.api.interactions.components.text.TextInput; +import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; +import net.dv8tion.jda.api.interactions.modals.ModalMapping; +import net.dv8tion.jda.api.requests.RestAction; +import net.dv8tion.jda.api.utils.TimeUtil; + +import java.util.Date; +import java.util.HashMap; + +public class ModalRemindMe extends DiscordModal { + + private static final HashMap userToRemindMeMap = new HashMap<>(); + + public static synchronized void putData(long userId, TextChannel channel, long timestamp) { + userToRemindMeMap.put(userId, new RemindMeData(channel, timestamp)); + } + + private static synchronized RemindMeData pullData(long userId) { + return userToRemindMeMap.remove(userId); + } + + private final ButtonManager buttonManager; + + public ModalRemindMe(ButtonManager buttonManager) { + this.buttonManager = buttonManager; + } + + @Override + public String getModalId() { + return "remindme"; + } + + @Override + public void execute(ModalInteractionEvent event) { + String title = getValidString(event.getValue("title"), event); + if (title == null) + return; + + String desc = getValidString(event.getValue("description"), event); + if (desc == null) + return; + + long userId = event.getUser().getIdLong(); + RemindMeData remindMeData = pullData(userId); + if (remindMeData == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Couldn't find the data from the command that triggered this modal")) + .setEphemeral(true).queue(); + return; + } + + Reminder reminder = new Reminder( + -1, + title, + desc, + userId, + remindMeData.textChannel.getGuild().getIdLong(), + remindMeData.textChannel.getIdLong(), + 0, + false, + new Date().getTime(), + remindMeData.timestamp); + + Button remindMeConfirm = buttonManager.getButtonFor("remind_me_confirm"); + Button remindMeCancel = buttonManager.getButtonFor("remind_me_cancel"); + if (remindMeConfirm == null || remindMeCancel == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to retrieve continue/cancel buttons")) + .setEphemeral(true).queue(); + return; + } + + long discordTimestamp = TimeUtil.getDiscordTimestamp(reminder.creationDate()); + MessageEmbed messageEmbed = new EmbedBuilder() + .setTitle(reminder.title()) + .setDescription(reminder.description()) + .setFooter("Requested ") + .build(); + ButtonRemindMeConfirm.putReminder(userId, reminder); + event.replyEmbeds(messageEmbed).queue(message -> + message.editOriginalComponents().setActionRow(remindMeConfirm, remindMeCancel) + .queue(RestAction.getDefaultSuccess(), Util::handleFailure)); + } + + public String getValidString(ModalMapping modalMapping, ModalInteractionEvent event) { + if (modalMapping == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Couldn't find modal")) + .setEphemeral(true).queue(); + return null; + } + + String string = modalMapping.getAsString(); + if (string.isEmpty()) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Couldn't find contents of modal")) + .setEphemeral(true).queue(); + return null; + } + + return string; + } + + @Override + public Modal getModal() { + TextInput title = TextInput.create("title", "Title", TextInputStyle.SHORT) + .setPlaceholder("reminder title:") + .setRequiredRange(1, 256) + .setRequired(true) + .build(); + + TextInput desc = TextInput.create("description", "Description", TextInputStyle.PARAGRAPH) + .setPlaceholder("optional reminder description:") + .setRequiredRange(1, 4096) + .setRequired(false) + .build(); + + return Modal.create(getModalId(), "Remind Me") + .addActionRows(ActionRow.of(title), ActionRow.of(desc)) + .build(); + } + + private record RemindMeData(TextChannel textChannel, long timestamp) {} +} diff --git a/src/main/java/com/alttd/reminders/ReminderScheduler.java b/src/main/java/com/alttd/reminders/ReminderScheduler.java new file mode 100644 index 0000000..79cb195 --- /dev/null +++ b/src/main/java/com/alttd/reminders/ReminderScheduler.java @@ -0,0 +1,114 @@ +package com.alttd.reminders; + +import com.alttd.database.queries.QueriesReminders.QueriesReminders; +import com.alttd.database.queries.QueriesReminders.Reminder; +import com.alttd.util.Logger; +import com.alttd.util.Util; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.requests.RestAction; +import net.dv8tion.jda.api.utils.TimeUtil; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Date; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class ReminderScheduler { + + private static ReminderScheduler instance = null; + private final ArrayList reminders; + private Reminder nextReminder; + private final JDA jda; + + private ReminderScheduler(JDA jda) { + instance = this; + this.jda = jda; + reminders = QueriesReminders.getReminders(); + if (reminders == null) { + Logger.severe("Unable to retrieve reminders"); + instance = null; + return; + } + reminders.sort(Comparator.comparingLong(Reminder::remindDate)); + if (reminders.size() == 0) + nextReminder = null; + else + nextReminder = reminders.get(0); + ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); + scheduledExecutorService.schedule(new ReminderRun(), 1, TimeUnit.MINUTES); + + } + + public static ReminderScheduler getInstance(JDA jda) { + if (instance == null) + instance = new ReminderScheduler(jda); + return instance; + } + + public synchronized void addReminder(Reminder reminder) { + reminders.add(reminder); + reminders.sort(Comparator.comparingLong(Reminder::remindDate)); + nextReminder = reminders.get(0); + } + + public synchronized void removeReminder(Reminder reminder) { + reminders.remove(reminder); + if (reminders.size() == 0) + nextReminder = null; + else + nextReminder = reminders.get(0); + QueriesReminders.removeReminder(reminder.id()); + } + + private class ReminderRun implements Runnable { + + @Override + public void run() { + long time = new Date().getTime(); + while (nextReminder != null && time > nextReminder.remindDate()) { + //TODO run reminder + TextChannel channel = nextReminder.getChannel(jda); + if (channel == null || !channel.canTalk()) { + Logger.warning("Unable to run reminder: " + nextReminder.id() + + "\ntitle: [" + nextReminder.title() + + "]\ndescription: [" + nextReminder.description() + "]"); + return; + } + sendEmbed(nextReminder, channel); + removeReminder(nextReminder); + } + } + + private void sendEmbed(Reminder reminder, TextChannel channel) { + long discordTimestamp = TimeUtil.getDiscordTimestamp(reminder.creationDate()); + EmbedBuilder embedBuilder = new EmbedBuilder() + .setTitle(reminder.title()) + .setDescription(reminder.description()) + .setFooter("Requested "); + Guild guild = reminder.getGuild(jda); + if (guild == null) { + sendEmbed(reminder, channel, embedBuilder); + return; + } + guild.retrieveMemberById(reminder.userId()).queue( + member -> sendEmbed(reminder, channel, embedBuilder, member), + failed -> sendEmbed(reminder, channel, embedBuilder)); + } + + private void sendEmbed(Reminder reminder, TextChannel channel, EmbedBuilder embedBuilder, Member member) { + embedBuilder.setAuthor(member.getEffectiveName(), null, member.getEffectiveAvatarUrl()); + channel.sendMessageEmbeds(embedBuilder.build()).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + } + + private void sendEmbed(Reminder reminder, TextChannel channel, EmbedBuilder embedBuilder) { + embedBuilder.setAuthor(reminder.userId() + ""); + channel.sendMessageEmbeds(embedBuilder.build()).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + } + } +} From 3c4213f1889abcc1b3744b1bd3ade8005698de2d Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Wed, 21 Sep 2022 23:24:18 +0200 Subject: [PATCH 24/27] Fixed spelling --- .../java/com/alttd/modalManager/modals/ModalSuggestion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/alttd/modalManager/modals/ModalSuggestion.java b/src/main/java/com/alttd/modalManager/modals/ModalSuggestion.java index 55a9a7d..9928fe2 100644 --- a/src/main/java/com/alttd/modalManager/modals/ModalSuggestion.java +++ b/src/main/java/com/alttd/modalManager/modals/ModalSuggestion.java @@ -108,7 +108,7 @@ public class ModalSuggestion extends DiscordModal { @Override public Modal getModal() { TextInput title = TextInput.create("title", "Title", TextInputStyle.SHORT) - .setPlaceholder("You suggestion in one sentence") + .setPlaceholder("Your suggestion in one sentence") .setRequiredRange(10, 100) .setRequired(true) .build(); From be890af51fca7c4285d59933f1c67767daa71b88 Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Wed, 21 Sep 2022 23:43:24 +0200 Subject: [PATCH 25/27] Fixed buttons not removing after clicking them Added hour option Fixed reminder not working without description Fixed using snowflake instead of timestamp for relative time --- .../remindMeConfirm/ButtonRemindMeCancel.java | 13 +++++++- .../ButtonRemindMeConfirm.java | 17 ++++++---- .../remindMeConfirm/HookAndReminder.java | 8 +++++ .../commands/CommandRemindMe.java | 29 ++++++++++++++-- .../modalManager/modals/ModalRemindMe.java | 33 ++++++++++--------- .../alttd/reminders/ReminderScheduler.java | 5 ++- 6 files changed, 76 insertions(+), 29 deletions(-) create mode 100644 src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/HookAndReminder.java diff --git a/src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/ButtonRemindMeCancel.java b/src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/ButtonRemindMeCancel.java index cce9259..65351e2 100644 --- a/src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/ButtonRemindMeCancel.java +++ b/src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/ButtonRemindMeCancel.java @@ -1,10 +1,15 @@ package com.alttd.buttonManager.buttons.remindMeConfirm; import com.alttd.buttonManager.DiscordButton; +import com.alttd.database.queries.QueriesReminders.Reminder; import com.alttd.util.Util; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.interactions.components.buttons.Button; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + public class ButtonRemindMeCancel extends DiscordButton { @Override public String getButtonId() { @@ -13,9 +18,15 @@ public class ButtonRemindMeCancel extends DiscordButton { @Override public void execute(ButtonInteractionEvent event) { - ButtonRemindMeConfirm.removeReminder(event.getUser().getIdLong()); + HookAndReminder hookAndReminder = ButtonRemindMeConfirm.removeReminder(event.getUser().getIdLong()); + if (hookAndReminder == null) { + event.replyEmbeds(Util.genericErrorEmbed("Error", "Your reminder was already cancelled!")) + .setEphemeral(true).queue(); + return; + } event.replyEmbeds(Util.genericSuccessEmbed("Success", "Cancelled your reminder!")) .setEphemeral(true).queue(); + hookAndReminder.interactionHook().editOriginalComponents(List.of()).queue(); } @Override diff --git a/src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/ButtonRemindMeConfirm.java b/src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/ButtonRemindMeConfirm.java index 0b0ea5b..7076c00 100644 --- a/src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/ButtonRemindMeConfirm.java +++ b/src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/ButtonRemindMeConfirm.java @@ -6,20 +6,22 @@ import com.alttd.database.queries.QueriesReminders.Reminder; import com.alttd.reminders.ReminderScheduler; import com.alttd.util.Util; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; +import net.dv8tion.jda.api.interactions.InteractionHook; import net.dv8tion.jda.api.interactions.components.buttons.Button; import java.util.HashMap; +import java.util.List; public class ButtonRemindMeConfirm extends DiscordButton { - private static final HashMap unconfirmedReminders = new HashMap<>(); + private static final HashMap unconfirmedReminders = new HashMap<>(); - public static synchronized void putReminder(long id, Reminder reminder) { - unconfirmedReminders.put(id, reminder); + public static synchronized void putReminder(long id, InteractionHook defer, Reminder reminder) { + unconfirmedReminders.put(id, new HookAndReminder(reminder, defer)); } - public static synchronized Reminder removeReminder(long id) { - return unconfirmedReminders.get(id); + public static synchronized HookAndReminder removeReminder(long id) { + return unconfirmedReminders.remove(id); } @Override @@ -29,12 +31,13 @@ public class ButtonRemindMeConfirm extends DiscordButton { @Override public void execute(ButtonInteractionEvent event) { - Reminder reminder = removeReminder(event.getUser().getIdLong()); + HookAndReminder hookAndReminder = removeReminder(event.getUser().getIdLong()); - if (storeReminder(reminder, event)) { + if (storeReminder(hookAndReminder.reminder(), event)) { event.replyEmbeds(Util.genericSuccessEmbed("Success", "Your reminder was successfully created!")) .setEphemeral(true).queue(); } + hookAndReminder.interactionHook().editOriginalComponents(List.of()).queue(); } private boolean storeReminder(Reminder reminder, ButtonInteractionEvent event) { diff --git a/src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/HookAndReminder.java b/src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/HookAndReminder.java new file mode 100644 index 0000000..bc8945f --- /dev/null +++ b/src/main/java/com/alttd/buttonManager/buttons/remindMeConfirm/HookAndReminder.java @@ -0,0 +1,8 @@ +package com.alttd.buttonManager.buttons.remindMeConfirm; + +import com.alttd.database.queries.QueriesReminders.Reminder; +import net.dv8tion.jda.api.interactions.InteractionHook; + +record HookAndReminder(Reminder reminder, InteractionHook interactionHook) { + +} diff --git a/src/main/java/com/alttd/commandManager/commands/CommandRemindMe.java b/src/main/java/com/alttd/commandManager/commands/CommandRemindMe.java index 3ffafad..1debb02 100644 --- a/src/main/java/com/alttd/commandManager/commands/CommandRemindMe.java +++ b/src/main/java/com/alttd/commandManager/commands/CommandRemindMe.java @@ -10,6 +10,7 @@ import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.AutoCompleteQuery; import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.OptionType; @@ -18,7 +19,9 @@ import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.components.Modal; import java.util.Calendar; +import java.util.Collections; import java.util.Date; +import java.util.List; import java.util.concurrent.TimeUnit; public class CommandRemindMe extends DiscordCommand { @@ -60,7 +63,7 @@ public class CommandRemindMe extends DiscordCommand { return; } - ModalRemindMe.putData(event.getIdLong(), channel, fromNow); + ModalRemindMe.putData(event.getUser().getIdLong(), channel, fromNow); event.replyModal(modal).queue(); } @@ -92,7 +95,7 @@ public class CommandRemindMe extends DiscordCommand { return null; } - if (!fromNow.matches("[1-9][0-9]*[dmy]")) { + if (!fromNow.matches("[1-9][0-9]*[hdmy]")) { return fromNowTimestamp(fromNow, event); } @@ -106,6 +109,9 @@ public class CommandRemindMe extends DiscordCommand { } switch (fromNow.substring(fromNow.length() - 1)) { + case "h" -> { + return TimeUnit.HOURS.toMillis(i) + new Date().getTime(); + } case "d" -> { return TimeUnit.DAYS.toMillis(i) + new Date().getTime(); } @@ -148,7 +154,24 @@ public class CommandRemindMe extends DiscordCommand { @Override public void suggest(CommandAutoCompleteInteractionEvent event) { - //TODO implement suggest + AutoCompleteQuery focusedOption = event.getFocusedOption(); + if (!focusedOption.getName().equals("fromnow")) { + event.replyChoices(Collections.emptyList()).queue(); + return; + } + + String value = focusedOption.getValue(); + if (value.isBlank()) { + event.replyChoiceStrings(List.of("1h", "1d", "1w", "1y", "t:" + new Date().getTime())).queue(); + return; + } + + if (value.matches("[0-9]+")) + event.replyChoiceStrings(List.of(value + "h", value + "d", value + "w", value + "y")).queue(); + else if (value.startsWith("t:")) + event.replyChoiceStrings(List.of(value)).queue(); + else + event.replyChoices(Collections.emptyList()).queue(); } @Override diff --git a/src/main/java/com/alttd/modalManager/modals/ModalRemindMe.java b/src/main/java/com/alttd/modalManager/modals/ModalRemindMe.java index 4286cae..01d8186 100644 --- a/src/main/java/com/alttd/modalManager/modals/ModalRemindMe.java +++ b/src/main/java/com/alttd/modalManager/modals/ModalRemindMe.java @@ -20,6 +20,7 @@ import net.dv8tion.jda.api.utils.TimeUtil; import java.util.Date; import java.util.HashMap; +import java.util.concurrent.TimeUnit; public class ModalRemindMe extends DiscordModal { @@ -46,13 +47,13 @@ public class ModalRemindMe extends DiscordModal { @Override public void execute(ModalInteractionEvent event) { - String title = getValidString(event.getValue("title"), event); + String title = getValidString(event.getValue("title"), event, true); if (title == null) return; - String desc = getValidString(event.getValue("description"), event); + String desc = getValidString(event.getValue("description"), event, false); if (desc == null) - return; + desc = ""; long userId = event.getUser().getIdLong(); RemindMeData remindMeData = pullData(userId); @@ -82,19 +83,20 @@ public class ModalRemindMe extends DiscordModal { return; } - long discordTimestamp = TimeUtil.getDiscordTimestamp(reminder.creationDate()); MessageEmbed messageEmbed = new EmbedBuilder() .setTitle(reminder.title()) .setDescription(reminder.description()) - .setFooter("Requested ") + .appendDescription("\n\nWill remind ") .build(); - ButtonRemindMeConfirm.putReminder(userId, reminder); - event.replyEmbeds(messageEmbed).queue(message -> - message.editOriginalComponents().setActionRow(remindMeConfirm, remindMeCancel) - .queue(RestAction.getDefaultSuccess(), Util::handleFailure)); + event.deferReply().setEphemeral(true).queue(defer -> { + ButtonRemindMeConfirm.putReminder(userId, defer, reminder); + defer.editOriginalEmbeds(messageEmbed).queue(message -> + defer.editOriginalComponents().setActionRow(remindMeConfirm, remindMeCancel) + .queue(RestAction.getDefaultSuccess(), Util::handleFailure)); + }); } - public String getValidString(ModalMapping modalMapping, ModalInteractionEvent event) { + public String getValidString(ModalMapping modalMapping, ModalInteractionEvent event, boolean required) { if (modalMapping == null) { event.replyEmbeds(Util.genericErrorEmbed("Error", "Couldn't find modal")) .setEphemeral(true).queue(); @@ -103,8 +105,9 @@ public class ModalRemindMe extends DiscordModal { String string = modalMapping.getAsString(); if (string.isEmpty()) { - event.replyEmbeds(Util.genericErrorEmbed("Error", "Couldn't find contents of modal")) - .setEphemeral(true).queue(); + if (required) + event.replyEmbeds(Util.genericErrorEmbed("Error", "Couldn't find contents of modal")) + .setEphemeral(true).queue(); return null; } @@ -114,14 +117,14 @@ public class ModalRemindMe extends DiscordModal { @Override public Modal getModal() { TextInput title = TextInput.create("title", "Title", TextInputStyle.SHORT) - .setPlaceholder("reminder title:") + .setPlaceholder("reminder title") .setRequiredRange(1, 256) .setRequired(true) .build(); TextInput desc = TextInput.create("description", "Description", TextInputStyle.PARAGRAPH) - .setPlaceholder("optional reminder description:") - .setRequiredRange(1, 4096) + .setPlaceholder("optional reminder description") + .setRequiredRange(1, 4000) .setRequired(false) .build(); diff --git a/src/main/java/com/alttd/reminders/ReminderScheduler.java b/src/main/java/com/alttd/reminders/ReminderScheduler.java index 79cb195..89113b2 100644 --- a/src/main/java/com/alttd/reminders/ReminderScheduler.java +++ b/src/main/java/com/alttd/reminders/ReminderScheduler.java @@ -41,7 +41,7 @@ public class ReminderScheduler { else nextReminder = reminders.get(0); ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); - scheduledExecutorService.schedule(new ReminderRun(), 1, TimeUnit.MINUTES); + scheduledExecutorService.scheduleWithFixedDelay(new ReminderRun(), 0, 1, TimeUnit.MINUTES); } @@ -86,11 +86,10 @@ public class ReminderScheduler { } private void sendEmbed(Reminder reminder, TextChannel channel) { - long discordTimestamp = TimeUtil.getDiscordTimestamp(reminder.creationDate()); EmbedBuilder embedBuilder = new EmbedBuilder() .setTitle(reminder.title()) .setDescription(reminder.description()) - .setFooter("Requested "); + .appendDescription("\n\nRequested "); Guild guild = reminder.getGuild(jda); if (guild == null) { sendEmbed(reminder, channel, embedBuilder); From f7a936e9a83c52a966de04cd92b73399181d3f8e Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Thu, 22 Sep 2022 00:11:02 +0200 Subject: [PATCH 26/27] Fixed auto complete suggesting 1w instead of 1m --- .../com/alttd/commandManager/commands/CommandRemindMe.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/alttd/commandManager/commands/CommandRemindMe.java b/src/main/java/com/alttd/commandManager/commands/CommandRemindMe.java index 1debb02..1b5da6e 100644 --- a/src/main/java/com/alttd/commandManager/commands/CommandRemindMe.java +++ b/src/main/java/com/alttd/commandManager/commands/CommandRemindMe.java @@ -162,12 +162,12 @@ public class CommandRemindMe extends DiscordCommand { String value = focusedOption.getValue(); if (value.isBlank()) { - event.replyChoiceStrings(List.of("1h", "1d", "1w", "1y", "t:" + new Date().getTime())).queue(); + event.replyChoiceStrings(List.of("1h", "1d", "1m", "1y", "t:" + new Date().getTime())).queue(); return; } if (value.matches("[0-9]+")) - event.replyChoiceStrings(List.of(value + "h", value + "d", value + "w", value + "y")).queue(); + event.replyChoiceStrings(List.of(value + "h", value + "d", value + "m", value + "y")).queue(); else if (value.startsWith("t:")) event.replyChoiceStrings(List.of(value)).queue(); else From 7a76ff9d14c2efca373a98a366cbc504cede61d1 Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Thu, 29 Sep 2022 19:01:30 +0200 Subject: [PATCH 27/27] Update to jda alpha 20 (has forums support) --- build.gradle.kts | 3 +-- settings.gradle.kts | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index df16528..c3f603e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -54,8 +54,7 @@ tasks { dependencies { // JDA -// implementation("net.dv8tion:JDA:5.0.0-alpha.19") { - implementation("com.github.DV8FromTheWorld:JDA:7d09a14") { + implementation("net.dv8tion:JDA:5.0.0-alpha.20") { exclude("opus-java") // exclude audio } // MySQL diff --git a/settings.gradle.kts b/settings.gradle.kts index d6d98f5..6fb6767 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,8 +10,6 @@ dependencyResolutionManagement { maven("https://repo.spongepowered.org/maven") // MySQL maven("https://jcenter.bintray.com") - //jitpack - maven("https://jitpack.io") } repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) }