From 1685e9ae2781d50308f2b140906295851cbebffe Mon Sep 17 00:00:00 2001 From: Andre601 Date: Sun, 28 Apr 2024 00:40:29 +0200 Subject: [PATCH 1/5] Create Context Menu Actions for application accept/deny Also change command system to custom implementation for role support. --- src/main/java/io/codemc/bot/CodeMCBot.java | 8 +- .../io/codemc/bot/commands/BotCommand.java | 100 +++++ .../codemc/bot/commands/CmdApplication.java | 62 ++-- .../io/codemc/bot/commands/CmdDisable.java | 29 +- .../java/io/codemc/bot/commands/CmdMsg.java | 51 ++- .../io/codemc/bot/commands/CmdSubmit.java | 13 +- .../codemc/bot/listeners/ModalListener.java | 343 ++++++++++-------- .../io/codemc/bot/menu/ApplicationMenu.java | 135 +++++++ .../java/io/codemc/bot/utils/CommandUtil.java | 4 +- .../java/io/codemc/bot/utils/Constants.java | 5 +- 10 files changed, 538 insertions(+), 212 deletions(-) create mode 100644 src/main/java/io/codemc/bot/commands/BotCommand.java create mode 100644 src/main/java/io/codemc/bot/menu/ApplicationMenu.java diff --git a/src/main/java/io/codemc/bot/CodeMCBot.java b/src/main/java/io/codemc/bot/CodeMCBot.java index 6332c53..2d819a8 100644 --- a/src/main/java/io/codemc/bot/CodeMCBot.java +++ b/src/main/java/io/codemc/bot/CodeMCBot.java @@ -25,6 +25,7 @@ import io.codemc.bot.commands.CmdMsg; import io.codemc.bot.commands.CmdSubmit; import io.codemc.bot.listeners.ModalListener; +import io.codemc.bot.menu.ApplicationMenu; import io.codemc.bot.utils.Constants; import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.entities.Activity; @@ -62,7 +63,12 @@ private void start(String token) throws LoginException{ new CmdDisable(), new CmdMsg(), new CmdSubmit() - ).forceGuildOnly(Constants.SERVER) + ) + .addContextMenus( + new ApplicationMenu.Accept(), + new ApplicationMenu.Deny() + ) + .forceGuildOnly(Constants.SERVER) .build(); JDABuilder.createDefault(token) diff --git a/src/main/java/io/codemc/bot/commands/BotCommand.java b/src/main/java/io/codemc/bot/commands/BotCommand.java new file mode 100644 index 0000000..4be7ada --- /dev/null +++ b/src/main/java/io/codemc/bot/commands/BotCommand.java @@ -0,0 +1,100 @@ +/* + * Copyright 2024 CodeMC.io + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package io.codemc.bot.commands; + +import com.jagrosh.jdautilities.command.SlashCommand; +import com.jagrosh.jdautilities.command.SlashCommandEvent; +import io.codemc.bot.utils.CommandUtil; +import io.codemc.bot.utils.Constants; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.interactions.InteractionHook; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public abstract class BotCommand extends SlashCommand{ + + protected List allowedRoles = new ArrayList<>(); + protected boolean hasModalReply = false; + + @Override + public void execute(SlashCommandEvent event){ + System.out.println("Custom BotCommand called!"); + Guild guild = event.getGuild(); + if(guild == null){ + CommandUtil.EmbedReply.fromCommandEvent(event) + .withError("Command can only be executed in a Server!") + .send(); + return; + } + + if(!guild.getId().equals(Constants.SERVER)){ + CommandUtil.EmbedReply.fromCommandEvent(event) + .withError("Unable to find CodeMC Server!") + .send(); + return; + } + + Member member = event.getMember(); + if(member == null){ + CommandUtil.EmbedReply.fromCommandEvent(event) + .withError("Unable to retrieve Member from Event!") + .send(); + return; + } + + if(!allowedRoles.isEmpty()){ + boolean hasRole = false; + for(Role role : member.getRoles()){ + if(allowedRoles.contains(role.getIdLong())){ + hasRole = true; + break; + } + } + + if(!hasRole){ + List names = guild.getRoles().stream() + .filter(role -> allowedRoles.contains(role.getIdLong())) + .map(Role::getName) + .collect(Collectors.toList()); + + CommandUtil.EmbedReply.fromCommandEvent(event) + .withError( + "You don't have any of the allowed roles to execute this command.", + "Allowed Roles: " + String.join(", ", names) + ) + .send(); + return; + } + } + + if(hasModalReply){ + withModalReply(event); + }else{ + event.deferReply(true).queue(hook -> withHookReply(hook, event, guild, member)); + } + } + + public abstract void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild guild, Member member); + + public abstract void withModalReply(SlashCommandEvent event); +} diff --git a/src/main/java/io/codemc/bot/commands/CmdApplication.java b/src/main/java/io/codemc/bot/commands/CmdApplication.java index 0971b85..923d218 100644 --- a/src/main/java/io/codemc/bot/commands/CmdApplication.java +++ b/src/main/java/io/codemc/bot/commands/CmdApplication.java @@ -23,7 +23,6 @@ import io.codemc.bot.utils.CommandUtil; import io.codemc.bot.utils.Constants; import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageEmbed; @@ -43,15 +42,16 @@ import java.util.List; import java.util.regex.Pattern; -public class CmdApplication extends SlashCommand{ +public class CmdApplication extends BotCommand{ public CmdApplication(){ this.name = "application"; this.help = "Accept or deny applications."; - this.userPermissions = new Permission[]{ - Permission.MANAGE_SERVER - }; + this.allowedRoles = Arrays.asList( + Constants.ROLE_ADMINISTRATOR, + Constants.ROLE_MODERATOR + ); this.children = new SlashCommand[]{ new Accept(), @@ -60,14 +60,12 @@ public CmdApplication(){ } @Override - protected void execute(SlashCommandEvent event){} + public void withModalReply(SlashCommandEvent event){} - private static void handle(InteractionHook hook, Guild guild, long messageId, String str, boolean accepted){ - if(guild == null || !guild.getId().equals(Constants.SERVER)){ - CommandUtil.EmbedReply.fromHook(hook).withError("Unable to retrieve Server!").send(); - return; - } - + @Override + public void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild guild, Member member){} + + public static void handle(InteractionHook hook, Guild guild, long messageId, String str, boolean accepted){ TextChannel requestChannel = guild.getTextChannelById(Constants.REQUEST_ACCESS); if(requestChannel == null){ CommandUtil.EmbedReply.fromHook(hook).withError("Unable to retrieve `request-access` channel.").send(); @@ -95,8 +93,6 @@ private static void handle(InteractionHook hook, Guild guild, long messageId, St return; } - System.out.println("Embed Footer text: " + userId); - String userLink = null; String repoLink = null; for(MessageEmbed.Field field : embed.getFields()){ @@ -193,7 +189,7 @@ private static MessageCreateData getMessage(String userId, String userLink, Stri .build(); } - private static class Accept extends SlashCommand{ + private static class Accept extends BotCommand{ private final Pattern projectUrlPattern = Pattern.compile("^https://ci\\.codemc\\.io/job/[a-zA-Z0-9-]+/job/[a-zA-Z0-9-_.]+/?$"); @@ -201,9 +197,10 @@ public Accept(){ this.name = "accept"; this.help = "Accept an application"; - this.userPermissions = new Permission[]{ - Permission.MANAGE_SERVER - }; + this.allowedRoles = Arrays.asList( + Constants.ROLE_ADMINISTRATOR, + Constants.ROLE_MODERATOR + ); this.options = Arrays.asList( new OptionData(OptionType.STRING, "id", "The message id of the application.").setRequired(true), @@ -212,7 +209,10 @@ public Accept(){ } @Override - protected void execute(SlashCommandEvent event){ + public void withModalReply(SlashCommandEvent event){} + + @Override + public void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild guild, Member member){ long messageId = event.getOption("id", -1L, option -> { try{ return Long.parseLong(option.getAsString()); @@ -223,32 +223,33 @@ protected void execute(SlashCommandEvent event){ String projectUrl = event.getOption("project-url", null, OptionMapping::getAsString); if(messageId == -1L || projectUrl == null){ - CommandUtil.EmbedReply.fromCommandEvent(event).withError( + CommandUtil.EmbedReply.fromHook(hook).withError( "Message ID or Project URL were not present!" ).send(); return; } if(!projectUrlPattern.matcher(projectUrl).matches()){ - CommandUtil.EmbedReply.fromCommandEvent(event).withError( + CommandUtil.EmbedReply.fromHook(hook).withError( "The provided Project URL did not match the pattern `https://ci.codemc.io/job//job/`!" ).send(); return; } - event.deferReply(true).queue(hook -> handle(hook, event.getGuild(), messageId, projectUrl, true)); + handle(hook, guild, messageId, projectUrl, true); } } - private static class Deny extends SlashCommand{ + private static class Deny extends BotCommand{ public Deny(){ this.name = "deny"; this.help = "Deny an application"; - this.userPermissions = new Permission[]{ - Permission.MANAGE_SERVER - }; + this.allowedRoles = Arrays.asList( + Constants.ROLE_ADMINISTRATOR, + Constants.ROLE_MODERATOR + ); this.options = Arrays.asList( new OptionData(OptionType.STRING, "id", "The message id of the application.").setRequired(true), @@ -257,7 +258,10 @@ public Deny(){ } @Override - protected void execute(SlashCommandEvent event){ + public void withModalReply(SlashCommandEvent event){} + + @Override + public void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild guild, Member member){ long messageId = event.getOption("id", -1L, option -> { try{ return Long.parseLong(option.getAsString()); @@ -268,13 +272,13 @@ protected void execute(SlashCommandEvent event){ String reason = event.getOption("reason", null, OptionMapping::getAsString); if(messageId == -1L || reason == null){ - CommandUtil.EmbedReply.fromCommandEvent(event).withError( + CommandUtil.EmbedReply.fromHook(hook).withError( "Message ID or Reason were not present!" ).send(); return; } - event.deferReply(true).queue(hook -> handle(hook, event.getGuild(), messageId, reason, false)); + handle(hook, guild, messageId, reason, false); } } } diff --git a/src/main/java/io/codemc/bot/commands/CmdDisable.java b/src/main/java/io/codemc/bot/commands/CmdDisable.java index 27ee9f9..7dd1c53 100644 --- a/src/main/java/io/codemc/bot/commands/CmdDisable.java +++ b/src/main/java/io/codemc/bot/commands/CmdDisable.java @@ -18,31 +18,38 @@ package io.codemc.bot.commands; -import com.jagrosh.jdautilities.command.SlashCommand; import com.jagrosh.jdautilities.command.SlashCommandEvent; -import net.dv8tion.jda.api.Permission; +import io.codemc.bot.utils.CommandUtil; +import io.codemc.bot.utils.Constants; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.interactions.InteractionHook; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class CmdDisable extends SlashCommand{ +import java.util.Collections; + +public class CmdDisable extends BotCommand{ private final Logger logger = LoggerFactory.getLogger("Shutdown"); public CmdDisable(){ this.name = "disable"; this.help = "Disables the bot."; - - this.userPermissions = new Permission[]{ - Permission.MANAGE_SERVER - }; + + this.allowedRoles = Collections.singletonList( + Constants.ROLE_ADMINISTRATOR + ); } @Override - protected void execute(SlashCommandEvent event){ - event.reply("Disabling bot...").setEphemeral(true).queue(m -> { - logger.info("Received disable command by {}.", event.getUser().getEffectiveName()); - logger.info("Disabling bot..."); + public void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild guild, Member member){ + hook.editOriginalEmbeds(CommandUtil.getEmbed().setColor(0x00FF00).setDescription("Bot disabled!").build()).queue(h -> { + logger.info("Bot disabled by {}", event.getUser().getEffectiveName()); System.exit(0); }); } + + @Override + public void withModalReply(SlashCommandEvent event){} } diff --git a/src/main/java/io/codemc/bot/commands/CmdMsg.java b/src/main/java/io/codemc/bot/commands/CmdMsg.java index 5574c3a..632d0f4 100644 --- a/src/main/java/io/codemc/bot/commands/CmdMsg.java +++ b/src/main/java/io/codemc/bot/commands/CmdMsg.java @@ -21,11 +21,14 @@ import com.jagrosh.jdautilities.command.SlashCommand; import com.jagrosh.jdautilities.command.SlashCommandEvent; import io.codemc.bot.utils.CommandUtil; -import net.dv8tion.jda.api.Permission; +import io.codemc.bot.utils.Constants; +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.ChannelType; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.interactions.InteractionHook; import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.OptionData; @@ -35,16 +38,17 @@ import net.dv8tion.jda.api.interactions.modals.Modal; import java.util.Arrays; +import java.util.Collections; -public class CmdMsg extends SlashCommand{ +public class CmdMsg extends BotCommand{ public CmdMsg(){ this.name = "msg"; this.help = "Sends a message in a specified channel or edits one."; - - this.userPermissions = new Permission[]{ - Permission.MANAGE_SERVER - }; + + this.allowedRoles = Collections.singletonList( + Constants.ROLE_ADMINISTRATOR + ); this.children = new SlashCommand[]{ new Post(), @@ -53,17 +57,21 @@ public CmdMsg(){ } @Override - protected void execute(SlashCommandEvent event){} + public void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild guild, Member member){} - private static class Post extends SlashCommand{ + @Override + public void withModalReply(SlashCommandEvent event){} + + private static class Post extends BotCommand{ public Post(){ this.name = "send"; this.help = "Sends a message as the Bot."; - this.userPermissions = new Permission[]{ - Permission.MANAGE_SERVER - }; + this.allowedRoles = Collections.singletonList( + Constants.ROLE_ADMINISTRATOR + ); + this.hasModalReply = true; this.options = Arrays.asList( new OptionData(OptionType.CHANNEL, "channel", "The channel to sent the message in.") @@ -74,7 +82,12 @@ public Post(){ } @Override - protected void execute(SlashCommandEvent event){ + public void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild guild, Member member){ + + } + + @Override + public void withModalReply(SlashCommandEvent event){ TextChannel channel = event.getOption("channel", null, option -> option.getAsChannel().asTextChannel()); boolean asEmbed = event.getOption("embed", false, OptionMapping::getAsBoolean); @@ -98,15 +111,16 @@ protected void execute(SlashCommandEvent event){ } } - private static class Edit extends SlashCommand{ + private static class Edit extends BotCommand{ public Edit(){ this.name = "edit"; this.help = "Edit an existing message of the bot."; - this.userPermissions = new Permission[]{ - Permission.MANAGE_SERVER - }; + this.allowedRoles = Collections.singletonList( + Constants.ROLE_ADMINISTRATOR + ); + this.hasModalReply = true; this.options = Arrays.asList( new OptionData(OptionType.CHANNEL, "channel", "The channel to edit the message in.") @@ -119,7 +133,10 @@ public Edit(){ } @Override - protected void execute(SlashCommandEvent event){ + public void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild guild, Member member){} + + @Override + public void withModalReply(SlashCommandEvent event){ TextChannel channel = event.getOption("channel", null, option -> option.getAsChannel().asTextChannel()); long messageId = event.getOption("id", -1L, option -> { try{ diff --git a/src/main/java/io/codemc/bot/commands/CmdSubmit.java b/src/main/java/io/codemc/bot/commands/CmdSubmit.java index 394012e..71b4ffd 100644 --- a/src/main/java/io/codemc/bot/commands/CmdSubmit.java +++ b/src/main/java/io/codemc/bot/commands/CmdSubmit.java @@ -18,23 +18,30 @@ package io.codemc.bot.commands; -import com.jagrosh.jdautilities.command.SlashCommand; import com.jagrosh.jdautilities.command.SlashCommandEvent; +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.interactions.InteractionHook; import net.dv8tion.jda.api.interactions.components.ActionRow; 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.Modal; -public class CmdSubmit extends SlashCommand{ +public class CmdSubmit extends BotCommand{ public CmdSubmit(){ this.name = "submit"; this.help = "Submit a request to join the CodeMC CI with a project."; + + this.hasModalReply = true; } @Override - protected void execute(SlashCommandEvent event){ + public void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild guild, Member member){} + + @Override + public void withModalReply(SlashCommandEvent event){ TextInput userLink = TextInput.create("userlink", "User Link", TextInputStyle.SHORT) .setPlaceholder("https://github.com/CodeMC") .setRequired(true) diff --git a/src/main/java/io/codemc/bot/listeners/ModalListener.java b/src/main/java/io/codemc/bot/listeners/ModalListener.java index 1b9ca0c..c87c857 100644 --- a/src/main/java/io/codemc/bot/listeners/ModalListener.java +++ b/src/main/java/io/codemc/bot/listeners/ModalListener.java @@ -18,6 +18,7 @@ package io.codemc.bot.listeners; +import io.codemc.bot.commands.CmdApplication; import io.codemc.bot.utils.CommandUtil; import io.codemc.bot.utils.Constants; import net.dv8tion.jda.api.entities.Guild; @@ -58,176 +59,222 @@ public void onModalInteraction(@NotNull ModalInteractionEvent event){ return; } - if(event.getModalId().equals("submit")){ - event.deferReply(true).queue(hook -> { - - String userLink = value(event, "userlink"); - String repoLink = value(event, "repolink"); - String description = value(event, "description"); - - if(nullOrEmpty(userLink, repoLink, description)){ - CommandUtil.EmbedReply.fromHook(hook).withError( - "User Link, Repository Link or Description was not present!" - ).send(); - return; - } - - Matcher userMatcher = userLinkPattern.matcher(userLink); - Matcher repoMatcher = repoLinkPattern.matcher(repoLink); - - if(!userMatcher.matches() || !repoMatcher.matches()){ - CommandUtil.EmbedReply.fromHook(hook).withError( - "The provided User or Repository link does not match a valid GitHub URL.", - "Make sure the patterns are `https://github.com/` and `https://github.com//` respectively." - ).send(); - return; - } - - String username = String.format("[`%s`](%s)", userMatcher.group("user"), userLink); - String repo = String.format("[`%s/%s`](%s)", repoMatcher.group("user"), repoMatcher.group("repo"), repoLink); - String submitter = String.format("`%s` (%s)", event.getUser().getEffectiveName(), event.getUser().getAsMention()); - - TextChannel requestChannel = guild.getTextChannelById(Constants.REQUEST_ACCESS); - if(requestChannel == null){ - CommandUtil.EmbedReply.fromHook(hook).withError( - "Unable to retrieve `request-access` channel!" - ).send(); - return; - } - - MessageEmbed embed = CommandUtil.getEmbed() - .addField("User/Organisation:", username, true) - .addField("Repository:", repo, true) - .addField("Submitted by:", submitter, true) - .addField("Description", description, false) - .setFooter(event.getUser().getId()) - .setTimestamp(Instant.now()) - .build(); - - requestChannel.sendMessageEmbeds(embed).queue( - message -> { - CommandUtil.EmbedReply.fromHook(hook).withMessage( - "[Request sent!](" + message.getJumpUrl() + ")" - ).asSuccess().send(); + String[] args = event.getModalId().split(":"); + + switch(args[0]){ + case "submit": + event.deferReply(true).queue(hook -> { + + String userLink = value(event, "userlink"); + String repoLink = value(event, "repolink"); + String description = value(event, "description"); + + if(nullOrEmpty(userLink, repoLink, description)){ + CommandUtil.EmbedReply.fromHook(hook).withError( + "User Link, Repository Link or Description was not present!" + ).send(); + return; + } + + Matcher userMatcher = userLinkPattern.matcher(userLink); + Matcher repoMatcher = repoLinkPattern.matcher(repoLink); + + if(!userMatcher.matches() || !repoMatcher.matches()){ + CommandUtil.EmbedReply.fromHook(hook).withError( + "The provided User or Repository link does not match a valid GitHub URL.", + "Make sure the patterns are `https://github.com/` and `https://github.com//` respectively." + ).send(); + return; + } + + String username = String.format("[`%s`](%s)", userMatcher.group("user"), userLink); + String repo = String.format("[`%s/%s`](%s)", repoMatcher.group("user"), repoMatcher.group("repo"), repoLink); + String submitter = String.format("`%s` (%s)", event.getUser().getEffectiveName(), event.getUser().getAsMention()); + + TextChannel requestChannel = guild.getTextChannelById(Constants.REQUEST_ACCESS); + if(requestChannel == null){ + CommandUtil.EmbedReply.fromHook(hook).withError( + "Unable to retrieve `request-access` channel!" + ).send(); + return; + } + + MessageEmbed embed = CommandUtil.getEmbed() + .addField("User/Organisation:", username, true) + .addField("Repository:", repo, true) + .addField("Submitted by:", submitter, true) + .addField("Description", description, false) + .setFooter(event.getUser().getId()) + .setTimestamp(Instant.now()) + .build(); + + requestChannel.sendMessageEmbeds(embed).queue( + message -> { + CommandUtil.EmbedReply.fromHook(hook).withMessage( + "[Request sent!](" + message.getJumpUrl() + ")" + ).asSuccess().send(); + + RestAction.allOf( + message.createThreadChannel("Access Request - " + event.getUser().getName()), + message.addReaction(Emoji.fromCustom("like", 935126958193405962L, false)), + message.addReaction(Emoji.fromCustom("dislike", 935126958235344927L, false)) + ).queue(); + + logger.info("[Access Request] User {} requested access to the CI.", event.getUser().getEffectiveName()); + }, + e -> CommandUtil.EmbedReply.fromHook(hook).withError( + "Error while submitting request!", + "Reported Error: " + e.getMessage() + ).send() + ); + }); + break; + case "message": + event.deferReply(true).queue(hook -> { + if(args.length < 4){ + CommandUtil.EmbedReply.fromHook(hook) + .withError("Invalid Modal data. Expected `>=4` but received `" + args.length + "`!") + .send(); + return; + } + + TextChannel channel = guild.getTextChannelById(args[2]); + if(channel == null){ + CommandUtil.EmbedReply.fromHook(hook) + .withError("Received invalid Text Channel.") + .send(); + return; + } + + String text = value(event, "message"); + if(text == null || text.isEmpty()){ + CommandUtil.EmbedReply.fromHook(hook) + .withError("Received invalid Message to sent/edit.") + .send(); + return; + } + + if(!channel.canTalk()){ + CommandUtil.EmbedReply.fromHook(hook) + .withError("I lack the permission to see and/or write in " + channel.getAsMention() + ".") + .send(); + return; + } + + boolean asEmbed = Boolean.parseBoolean(args[3]); + + if(args[1].equals("post")){ + if(asEmbed){ + channel.sendMessageEmbeds(CommandUtil.getEmbed().setDescription(text).build()).queue( + message -> sendConfirmation(hook, message, false), + e -> CommandUtil.EmbedReply.fromHook(hook) + .withError("Unable to sent message. Reason: " + e.getMessage()) + .send() + ); + }else{ + channel.sendMessage(text).queue( + message -> sendConfirmation(hook, message, false), + e -> CommandUtil.EmbedReply.fromHook(hook) + .withError("Unable to sent message. Reason: " + e.getMessage()) + .send() + ); + } + }else if(args[1].equals("edit")){ + if(args.length == 4){ + CommandUtil.EmbedReply.fromHook(hook) + .withError("Received invalid Modal data. Expected `>4` but got `=4`") + .send(); + return; + } - RestAction.allOf( - message.createThreadChannel("Access Request - " + event.getUser().getName()), - message.addReaction(Emoji.fromCustom("like", 935126958193405962L, false)), - message.addReaction(Emoji.fromCustom("dislike", 935126958235344927L, false)) - ).queue(); + long messageId; + try{ + messageId = Long.parseLong(args[4]); + }catch(NumberFormatException ex){ + messageId = -1L; + } + + if(messageId == -1L){ + CommandUtil.EmbedReply.fromHook(hook) + .withError("Received invalid message ID `" + args[4] + "`.") + .send(); + return; + } - logger.info("[Access Request] User {} requested access to the CI.", event.getUser().getEffectiveName()); - }, - e -> CommandUtil.EmbedReply.fromHook(hook).withError( - "Error while submitting request!", - "Reported Error: " + e.getMessage() - ).send() - ); - }); - }else - if(event.getModalId().startsWith("message:")){ - event.deferReply(true).queue(hook -> { - String[] args = event.getModalId().split(":"); - if(args.length < 4){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Invalid Modal data. Expected `>=4` but received `" + args.length + "`!") - .send(); - return; - } - - TextChannel channel = guild.getTextChannelById(args[2]); - if(channel == null){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Received invalid Text Channel.") - .send(); - return; - } - - String text = value(event, "message"); - if(text == null || text.isEmpty()){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Received invalid Message to sent/edit.") - .send(); - return; - } - - if(!channel.canTalk()){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("I lack the permission to see and/or write in " + channel.getAsMention() + ".") - .send(); - return; - } - - boolean asEmbed = Boolean.parseBoolean(args[3]); - - if(args[1].equals("post")){ - if(asEmbed){ - channel.sendMessageEmbeds(CommandUtil.getEmbed().setDescription(text).build()).queue( - message -> sendConfirmation(hook, message, false), - e -> CommandUtil.EmbedReply.fromHook(hook) - .withError("Unable to sent message. Reason: " + e.getMessage()) - .send() + channel.retrieveMessageById(messageId).queue( + message -> { + if(asEmbed){ + message.editMessageEmbeds(CommandUtil.getEmbed().setDescription(text).build()).setReplace(true).queue( + m -> sendConfirmation(hook, m, true), + e -> CommandUtil.EmbedReply.fromHook(hook) + .withError("Unable to edit message. Reason: " + e.getMessage()) + .send() + ); + }else{ + message.editMessage(text).setReplace(true).queue( + m -> sendConfirmation(hook, m, true), + e -> CommandUtil.EmbedReply.fromHook(hook) + .withError("Unable to edit message. Reason: " + e.getMessage()) + .send() + ); + } + } ); }else{ - channel.sendMessage(text).queue( - message -> sendConfirmation(hook, message, false), - e -> CommandUtil.EmbedReply.fromHook(hook) - .withError("Unable to sent message. Reason: " + e.getMessage()) - .send() - ); + CommandUtil.EmbedReply.fromHook(hook) + .withError("Received Unknown Message type: `" + args[1] + "`.") + .send(); + } + }); + case "application": + event.deferReply(true).queue(hook -> { + if(args.length < 3){ + CommandUtil.EmbedReply.fromHook(hook) + .withError("Invalid Modal data. Expected `=3` but received `" + args.length + "`!") + .send(); + return; } - }else - if(args[1].equals("edit")){ - if(args.length == 4){ + + if(!args[1].equals("accepted") && !args[1].equals("denied")){ CommandUtil.EmbedReply.fromHook(hook) - .withError("Received invalid Modal data. Expected `>4` but got `=4`") + .withError("Received unknown Application type. Expected `accepted` or `denied` but received `" + args[1] + "`.") .send(); return; } long messageId; try{ - messageId = Long.parseLong(args[4]); + messageId = Long.parseLong(args[2]); }catch(NumberFormatException ex){ messageId = -1L; } if(messageId == -1L){ CommandUtil.EmbedReply.fromHook(hook) - .withError("Received invalid message ID `" + args[4] + "`.") + .withError("Received Invalid Message ID. Expected number but got `" + args[2] + "` instead!") .send(); return; } - channel.retrieveMessageById(messageId).queue( - message -> { - if(asEmbed){ - message.editMessageEmbeds(CommandUtil.getEmbed().setDescription(text).build()).setReplace(true).queue( - m -> sendConfirmation(hook, m, true), - e -> CommandUtil.EmbedReply.fromHook(hook) - .withError("Unable to edit message. Reason: " + e.getMessage()) - .send() - ); - }else{ - message.editMessage(text).setReplace(true).queue( - m -> sendConfirmation(hook, m, true), - e -> CommandUtil.EmbedReply.fromHook(hook) - .withError("Unable to edit message. Reason: " + e.getMessage()) - .send() - ); - } - } - ); - }else{ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Received Unknown Message type: `" + args[1] + "`.") - .send(); - } - }); - }else{ - CommandUtil.EmbedReply.fromModalEvent(event) - .withError("Received unknwon Modal Data: `"+ event.getModalId() +"`") - .send(); + boolean accepted = args[1].equals("accepted"); + + String text = value(event, "text"); + if(text == null || text.isEmpty()){ + CommandUtil.EmbedReply.fromHook(hook) + .withError("Received invalid " + (accepted ? "Project URL" : "Reason") + ". Text was empty/null.") + .send(); + return; + } + + CmdApplication.handle(hook, guild, messageId, text, args[1].equals("accepted")); + }); + break; + + default: + CommandUtil.EmbedReply.fromModalEvent(event) + .withError("Received Modal with unknown ID `" + event.getModalId() + "`.") + .send(); + break; } } diff --git a/src/main/java/io/codemc/bot/menu/ApplicationMenu.java b/src/main/java/io/codemc/bot/menu/ApplicationMenu.java new file mode 100644 index 0000000..16092be --- /dev/null +++ b/src/main/java/io/codemc/bot/menu/ApplicationMenu.java @@ -0,0 +1,135 @@ +/* + * Copyright 2024 CodeMC.io + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package io.codemc.bot.menu; + +import com.jagrosh.jdautilities.command.MessageContextMenu; +import com.jagrosh.jdautilities.command.MessageContextMenuEvent; +import io.codemc.bot.utils.Constants; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; +import net.dv8tion.jda.api.interactions.components.ActionRow; +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.Modal; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class ApplicationMenu{ + + private static final List allowedRoles = Arrays.asList( + Constants.ROLE_ADMINISTRATOR, + Constants.ROLE_MODERATOR + ); + + private static void handleEvent(MessageContextMenuEvent event, boolean accepted){ + Guild guild = event.getGuild(); + if(guild == null || !guild.getId().equals(Constants.SERVER)){ + event.reply("This Context Menu Action may only work in the CodeMC Server!") + .setEphemeral(true) + .queue(); + return; + } + + MessageChannel channel = event.getChannel(); + if(channel == null || !channel.getId().equals(Constants.REQUEST_ACCESS)){ + event.reply("This Context Menu Action may only work in the request-access channel of the CodeMC Server!") + .setEphemeral(true) + .queue(); + return; + } + + Member member = event.getMember(); + if(member == null){ + event.reply("Unable to retrieve Member!").setEphemeral(true).queue(); + return; + } + + List roleIds = member.getRoles().stream().map(Role::getIdLong).collect(Collectors.toList()); + boolean hasRole = false; + for(long roleId : roleIds){ + if(allowedRoles.contains(roleId)){ + hasRole = true; + break; + } + } + + if(!hasRole){ + List roleNames = guild.getRoles().stream() + .filter(role -> allowedRoles.contains(role.getIdLong())) + .map(Role::getName) + .collect(Collectors.toList()); + + event.reply( + "You do not have any of the allowed roles to execute this Context Menu Action.\n" + + "Allowed Roles: " + String.join(", ", roleNames) + ).queue(); + return; + } + + TextInput input; + if(accepted){ + input = TextInput.create("text", "Project URL", TextInputStyle.SHORT) + .setRequired(true) + .setPlaceholder("https://ci.codemc.io/job/CodeMC/job/CodeMC-Discord-Bot/") + .build(); + }else{ + input = TextInput.create("text", "Denial Reason", TextInputStyle.PARAGRAPH) + .setRequired(true) + .setPlaceholder("The project was denied because ...") + .build(); + } + + Modal modal = Modal.create( + "application:" + (accepted ? "accepted" : "denied") + ":" + event.getTarget().getId(), + accepted ? "Accept Application" : "Deny Application" + ) + .addComponents(ActionRow.of(input)) + .build(); + + event.replyModal(modal).queue(); + } + + public static class Accept extends MessageContextMenu{ + + public Accept(){ + this.name = "Accept this application"; + } + + @Override + protected void execute(MessageContextMenuEvent event){ + handleEvent(event, true); + } + } + + public static class Deny extends MessageContextMenu{ + + public Deny(){ + this.name = "Deny this application"; + } + + @Override + protected void execute(MessageContextMenuEvent event){ + handleEvent(event, false); + } + } +} diff --git a/src/main/java/io/codemc/bot/utils/CommandUtil.java b/src/main/java/io/codemc/bot/utils/CommandUtil.java index 6ee8e08..a83cd50 100644 --- a/src/main/java/io/codemc/bot/utils/CommandUtil.java +++ b/src/main/java/io/codemc/bot/utils/CommandUtil.java @@ -100,10 +100,10 @@ public EmbedReply withIssue(String... lines){ public void send(){ if(commandEvent != null){ - commandEvent.replyEmbeds(builder.build()).queue(); + commandEvent.replyEmbeds(builder.build()).setEphemeral(true).queue(); }else if(modalEvent != null){ - modalEvent.replyEmbeds(builder.build()).queue(); + modalEvent.replyEmbeds(builder.build()).setEphemeral(true).queue(); }else if(hook != null){ hook.editOriginalEmbeds(builder.build()).queue(); diff --git a/src/main/java/io/codemc/bot/utils/Constants.java b/src/main/java/io/codemc/bot/utils/Constants.java index f1b0438..2726621 100644 --- a/src/main/java/io/codemc/bot/utils/Constants.java +++ b/src/main/java/io/codemc/bot/utils/Constants.java @@ -29,7 +29,10 @@ public class Constants{ public static final String REJECTED_REQUESTS = "800423355551449098"; // Role IDs - public static final String ROLE_AUTHOR = "405918641859723294"; + public static final long ROLE_ADMINISTRATOR = 405917902865170453L; + public static final long ROLE_MODERATOR = 659568973079379971L; + public static final long ROLE_CONTRIBUTOR = 1233119938621997097L; + public static final long ROLE_AUTHOR = 405918641859723294L; // Result messages for applications public static final String ACCEPTED_MSG = From 00adc413b6e86f065c30270d65b3164608791149 Mon Sep 17 00:00:00 2001 From: Andre601 Date: Sun, 28 Apr 2024 16:14:41 +0200 Subject: [PATCH 2/5] Implement configuration system (+ reload command) --- build.gradle | 3 +- src/main/java/io/codemc/bot/CodeMCBot.java | 106 +++-- .../io/codemc/bot/commands/BotCommand.java | 1 - .../codemc/bot/commands/CmdApplication.java | 49 +-- .../io/codemc/bot/commands/CmdDisable.java | 10 +- .../java/io/codemc/bot/commands/CmdMsg.java | 25 +- .../io/codemc/bot/commands/CmdReload.java | 59 +++ .../io/codemc/bot/commands/CmdSubmit.java | 2 +- .../io/codemc/bot/config/ConfigHandler.java | 93 +++++ .../codemc/bot/listeners/ModalListener.java | 390 +++++++++--------- .../io/codemc/bot/menu/ApplicationMenu.java | 53 +-- .../java/io/codemc/bot/utils/CommandUtil.java | 10 + src/main/resources/config.json | 47 +++ 13 files changed, 533 insertions(+), 315 deletions(-) create mode 100644 src/main/java/io/codemc/bot/commands/CmdReload.java create mode 100644 src/main/java/io/codemc/bot/config/ConfigHandler.java create mode 100644 src/main/resources/config.json diff --git a/build.gradle b/build.gradle index 3671d80..0479852 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,7 @@ plugins { } group 'io.codemc' -version '2.0.0' +version '2.1.0' compileJava.options.encoding('UTF-8') @@ -42,6 +42,7 @@ dependencies { } implementation group: 'pw.chew', name: 'jda-chewtils-commons', version: '2.0-SNAPSHOT' implementation group: 'pw.chew', name: 'jda-chewtils-command', version: '2.0-SNAPSHOT' + implementation group: 'org.spongepowered', name: 'configurate-gson', version: '4.1.2' } artifacts { diff --git a/src/main/java/io/codemc/bot/CodeMCBot.java b/src/main/java/io/codemc/bot/CodeMCBot.java index 2d819a8..f5aacbd 100644 --- a/src/main/java/io/codemc/bot/CodeMCBot.java +++ b/src/main/java/io/codemc/bot/CodeMCBot.java @@ -18,15 +18,11 @@ package io.codemc.bot; -import com.jagrosh.jdautilities.command.CommandClient; import com.jagrosh.jdautilities.command.CommandClientBuilder; -import io.codemc.bot.commands.CmdApplication; -import io.codemc.bot.commands.CmdDisable; -import io.codemc.bot.commands.CmdMsg; -import io.codemc.bot.commands.CmdSubmit; +import io.codemc.bot.commands.*; +import io.codemc.bot.config.ConfigHandler; import io.codemc.bot.listeners.ModalListener; import io.codemc.bot.menu.ApplicationMenu; -import io.codemc.bot.utils.Constants; import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.entities.Activity; import net.dv8tion.jda.api.requests.GatewayIntent; @@ -35,42 +31,82 @@ import org.slf4j.LoggerFactory; import javax.security.auth.login.LoginException; +import java.util.List; public class CodeMCBot{ - private final Logger LOG = LoggerFactory.getLogger(CodeMCBot.class); + private final Logger logger = LoggerFactory.getLogger(CodeMCBot.class); + private final ConfigHandler configHandler = new ConfigHandler(); public static void main(String[] args){ try{ - new CodeMCBot().start(args[0]); + new CodeMCBot().start(); }catch(LoginException ex){ - new CodeMCBot().LOG.error("Unable to login to Discord!", ex); + new CodeMCBot().logger.error("Unable to login to Discord!", ex); } } - private void start(String token) throws LoginException{ - CommandClient commandClient = new CommandClientBuilder() - .setOwnerId( - "204232208049766400" // Andre_601#0601 - ) - .setCoOwnerIds( - "143088571656437760", // sgdc3#0001 - "282975975954710528" // tr7zw#4005 - ) - .setActivity(null) - .addSlashCommands( - new CmdApplication(), - new CmdDisable(), - new CmdMsg(), - new CmdSubmit() - ) - .addContextMenus( - new ApplicationMenu.Accept(), - new ApplicationMenu.Deny() - ) - .forceGuildOnly(Constants.SERVER) - .build(); + private void start() throws LoginException{ + if(!configHandler.loadConfig()){ + logger.warn("Unable to load config.json! See previous logs for any errors."); + System.exit(1); + return; + } + String token = configHandler.getString("bot_token"); + if(token == null || token.isEmpty()){ + logger.warn("Received invalid Bot Token!"); + System.exit(1); + return; + } + + long owner = configHandler.getLong("users", "owner"); + if(owner == -1L){ + logger.warn("Unable to retrieve Owner ID. This value is required!"); + System.exit(1); + return; + } + + long guildId = configHandler.getLong("server"); + if(guildId == -1L){ + logger.warn("Unable to retrieve Server ID. This value is required!"); + System.exit(1); + return; + } + + CommandClientBuilder clientBuilder = new CommandClientBuilder().setActivity(null).forceGuildOnly(guildId); + + clientBuilder.setOwnerId(owner); + + List coOwners = configHandler.getLongList("users", "co_owners"); + + if(coOwners != null && !coOwners.isEmpty()){ + logger.info("Adding {} Co-Owner(s) to the bot.", coOwners.size()); + // Annoying, but setCoOwnerIds has no overload with a Collection... + long[] coOwnerIds = new long[coOwners.size()]; + for(int i = 0; i < coOwnerIds.length; i++){ + coOwnerIds[i] = coOwners.get(i); + } + + clientBuilder.setCoOwnerIds(coOwnerIds); + } + + logger.info("Adding commands..."); + clientBuilder.addSlashCommands( + new CmdApplication(this), + new CmdDisable(this), + new CmdMsg(this), + new CmdReload(this), + new CmdSubmit() + ); + + logger.info("Adding Context Menus..."); + clientBuilder.addContextMenus( + new ApplicationMenu.Accept(this), + new ApplicationMenu.Deny(this) + ); + + logger.info("Starting bot..."); JDABuilder.createDefault(token) .enableIntents( GatewayIntent.GUILD_MEMBERS, @@ -83,9 +119,13 @@ private void start(String token) throws LoginException{ "Applications" )) .addEventListeners( - commandClient, - new ModalListener() + clientBuilder.build(), + new ModalListener(this) ) .build(); } + + public ConfigHandler getConfigHandler(){ + return configHandler; + } } diff --git a/src/main/java/io/codemc/bot/commands/BotCommand.java b/src/main/java/io/codemc/bot/commands/BotCommand.java index 4be7ada..852605f 100644 --- a/src/main/java/io/codemc/bot/commands/BotCommand.java +++ b/src/main/java/io/codemc/bot/commands/BotCommand.java @@ -38,7 +38,6 @@ public abstract class BotCommand extends SlashCommand{ @Override public void execute(SlashCommandEvent event){ - System.out.println("Custom BotCommand called!"); Guild guild = event.getGuild(); if(guild == null){ CommandUtil.EmbedReply.fromCommandEvent(event) diff --git a/src/main/java/io/codemc/bot/commands/CmdApplication.java b/src/main/java/io/codemc/bot/commands/CmdApplication.java index 923d218..7f6d20d 100644 --- a/src/main/java/io/codemc/bot/commands/CmdApplication.java +++ b/src/main/java/io/codemc/bot/commands/CmdApplication.java @@ -20,6 +20,7 @@ import com.jagrosh.jdautilities.command.SlashCommand; import com.jagrosh.jdautilities.command.SlashCommandEvent; +import io.codemc.bot.CodeMCBot; import io.codemc.bot.utils.CommandUtil; import io.codemc.bot.utils.Constants; import net.dv8tion.jda.api.EmbedBuilder; @@ -44,18 +45,15 @@ public class CmdApplication extends BotCommand{ - public CmdApplication(){ + public CmdApplication(CodeMCBot bot){ this.name = "application"; this.help = "Accept or deny applications."; - this.allowedRoles = Arrays.asList( - Constants.ROLE_ADMINISTRATOR, - Constants.ROLE_MODERATOR - ); + this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "application"); this.children = new SlashCommand[]{ - new Accept(), - new Deny() + new Accept(bot), + new Deny(bot) }; } @@ -65,8 +63,8 @@ public void withModalReply(SlashCommandEvent event){} @Override public void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild guild, Member member){} - public static void handle(InteractionHook hook, Guild guild, long messageId, String str, boolean accepted){ - TextChannel requestChannel = guild.getTextChannelById(Constants.REQUEST_ACCESS); + public static void handle(CodeMCBot bot, InteractionHook hook, Guild guild, long messageId, String str, boolean accepted){ + TextChannel requestChannel = guild.getTextChannelById(bot.getConfigHandler().getLong("channel", "request_access")); if(requestChannel == null){ CommandUtil.EmbedReply.fromHook(hook).withError("Unable to retrieve `request-access` channel.").send(); return; @@ -112,7 +110,10 @@ public static void handle(InteractionHook hook, Guild guild, long messageId, Str return; } - TextChannel channel = guild.getTextChannelById(accepted ? Constants.ACCEPTED_REQUESTS : Constants.REJECTED_REQUESTS); + TextChannel channel = guild.getTextChannelById(accepted + ? bot.getConfigHandler().getLong("channels", "accepted_requests") + : bot.getConfigHandler().getLong("channels", "rejected_requests") + ); if(channel == null){ CommandUtil.EmbedReply.fromHook(hook) .withError("Unable to retrieve `" + (accepted ? "accepted" : "rejected") + "-requests` channel.") @@ -140,7 +141,7 @@ public static void handle(InteractionHook hook, Guild guild, long messageId, Str return; } - Role authorRole = guild.getRoleById(Constants.ROLE_AUTHOR); + Role authorRole = guild.getRoleById(bot.getConfigHandler().getLong("author_role")); if(authorRole == null){ CommandUtil.EmbedReply.fromHook(hook) .withError("Unable to retrieve Author Role!") @@ -193,14 +194,15 @@ private static class Accept extends BotCommand{ private final Pattern projectUrlPattern = Pattern.compile("^https://ci\\.codemc\\.io/job/[a-zA-Z0-9-]+/job/[a-zA-Z0-9-_.]+/?$"); - public Accept(){ + private final CodeMCBot bot; + + public Accept(CodeMCBot bot){ + this.bot = bot; + this.name = "accept"; this.help = "Accept an application"; - this.allowedRoles = Arrays.asList( - Constants.ROLE_ADMINISTRATOR, - Constants.ROLE_MODERATOR - ); + this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "application"); this.options = Arrays.asList( new OptionData(OptionType.STRING, "id", "The message id of the application.").setRequired(true), @@ -236,20 +238,21 @@ public void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild g return; } - handle(hook, guild, messageId, projectUrl, true); + handle(bot, hook, guild, messageId, projectUrl, true); } } private static class Deny extends BotCommand{ - public Deny(){ + private final CodeMCBot bot; + + public Deny(CodeMCBot bot){ + this.bot = bot; + this.name = "deny"; this.help = "Deny an application"; - this.allowedRoles = Arrays.asList( - Constants.ROLE_ADMINISTRATOR, - Constants.ROLE_MODERATOR - ); + this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "application"); this.options = Arrays.asList( new OptionData(OptionType.STRING, "id", "The message id of the application.").setRequired(true), @@ -278,7 +281,7 @@ public void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild g return; } - handle(hook, guild, messageId, reason, false); + handle(bot, hook, guild, messageId, reason, false); } } } diff --git a/src/main/java/io/codemc/bot/commands/CmdDisable.java b/src/main/java/io/codemc/bot/commands/CmdDisable.java index 7dd1c53..804e5ea 100644 --- a/src/main/java/io/codemc/bot/commands/CmdDisable.java +++ b/src/main/java/io/codemc/bot/commands/CmdDisable.java @@ -19,27 +19,23 @@ package io.codemc.bot.commands; import com.jagrosh.jdautilities.command.SlashCommandEvent; +import io.codemc.bot.CodeMCBot; import io.codemc.bot.utils.CommandUtil; -import io.codemc.bot.utils.Constants; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.interactions.InteractionHook; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Collections; - public class CmdDisable extends BotCommand{ private final Logger logger = LoggerFactory.getLogger("Shutdown"); - public CmdDisable(){ + public CmdDisable(CodeMCBot bot){ this.name = "disable"; this.help = "Disables the bot."; - this.allowedRoles = Collections.singletonList( - Constants.ROLE_ADMINISTRATOR - ); + this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "disable"); } @Override diff --git a/src/main/java/io/codemc/bot/commands/CmdMsg.java b/src/main/java/io/codemc/bot/commands/CmdMsg.java index 632d0f4..2f83eea 100644 --- a/src/main/java/io/codemc/bot/commands/CmdMsg.java +++ b/src/main/java/io/codemc/bot/commands/CmdMsg.java @@ -20,8 +20,8 @@ import com.jagrosh.jdautilities.command.SlashCommand; import com.jagrosh.jdautilities.command.SlashCommandEvent; +import io.codemc.bot.CodeMCBot; import io.codemc.bot.utils.CommandUtil; -import io.codemc.bot.utils.Constants; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; @@ -38,21 +38,18 @@ import net.dv8tion.jda.api.interactions.modals.Modal; import java.util.Arrays; -import java.util.Collections; public class CmdMsg extends BotCommand{ - public CmdMsg(){ + public CmdMsg(CodeMCBot bot){ this.name = "msg"; this.help = "Sends a message in a specified channel or edits one."; - this.allowedRoles = Collections.singletonList( - Constants.ROLE_ADMINISTRATOR - ); + this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "msg"); this.children = new SlashCommand[]{ - new Post(), - new Edit() + new Post(bot), + new Edit(bot) }; } @@ -64,13 +61,11 @@ public void withModalReply(SlashCommandEvent event){} private static class Post extends BotCommand{ - public Post(){ + public Post(CodeMCBot bot){ this.name = "send"; this.help = "Sends a message as the Bot."; - this.allowedRoles = Collections.singletonList( - Constants.ROLE_ADMINISTRATOR - ); + this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "msg"); this.hasModalReply = true; this.options = Arrays.asList( @@ -113,13 +108,11 @@ public void withModalReply(SlashCommandEvent event){ private static class Edit extends BotCommand{ - public Edit(){ + public Edit(CodeMCBot bot){ this.name = "edit"; this.help = "Edit an existing message of the bot."; - this.allowedRoles = Collections.singletonList( - Constants.ROLE_ADMINISTRATOR - ); + this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "msg"); this.hasModalReply = true; this.options = Arrays.asList( diff --git a/src/main/java/io/codemc/bot/commands/CmdReload.java b/src/main/java/io/codemc/bot/commands/CmdReload.java new file mode 100644 index 0000000..f3227a9 --- /dev/null +++ b/src/main/java/io/codemc/bot/commands/CmdReload.java @@ -0,0 +1,59 @@ +/* + * Copyright 2024 CodeMC.io + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package io.codemc.bot.commands; + +import com.jagrosh.jdautilities.command.SlashCommandEvent; +import io.codemc.bot.CodeMCBot; +import io.codemc.bot.utils.CommandUtil; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.interactions.InteractionHook; + +public class CmdReload extends BotCommand{ + + private final CodeMCBot bot; + + public CmdReload(CodeMCBot bot){ + this.bot = bot; + + this.name = "reload"; + this.help = "Reloads the configuration."; + + this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "reload"); + } + + @Override + public void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild guild, Member member){ + boolean success = bot.getConfigHandler().reloadConfig(); + + if(success){ + CommandUtil.EmbedReply.fromHook(hook) + .withMessage("Reload success!") + .asSuccess() + .send(); + }else{ + CommandUtil.EmbedReply.fromHook(hook) + .withError("There was an issue while reloading the configuration! Check console.") + .send(); + } + } + + @Override + public void withModalReply(SlashCommandEvent event){} +} diff --git a/src/main/java/io/codemc/bot/commands/CmdSubmit.java b/src/main/java/io/codemc/bot/commands/CmdSubmit.java index 71b4ffd..414c4b0 100644 --- a/src/main/java/io/codemc/bot/commands/CmdSubmit.java +++ b/src/main/java/io/codemc/bot/commands/CmdSubmit.java @@ -51,7 +51,7 @@ public void withModalReply(SlashCommandEvent event){ .setRequired(true) .build(); TextInput description = TextInput.create("description", "Description", TextInputStyle.PARAGRAPH) - .setPlaceholder("Duscird Vit fir tge CideNC Sercver.") + .setPlaceholder("Discord Bot for the CodeMC Server.") .setRequired(true) .setMaxLength(MessageEmbed.VALUE_MAX_LENGTH) .build(); diff --git a/src/main/java/io/codemc/bot/config/ConfigHandler.java b/src/main/java/io/codemc/bot/config/ConfigHandler.java new file mode 100644 index 0000000..02170b4 --- /dev/null +++ b/src/main/java/io/codemc/bot/config/ConfigHandler.java @@ -0,0 +1,93 @@ +/* + * Copyright 2024 CodeMC.io + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package io.codemc.bot.config; + +import io.codemc.bot.CodeMCBot; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.gson.GsonConfigurationLoader; +import org.spongepowered.configurate.serialize.SerializationException; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.util.Collections; +import java.util.List; + +public class ConfigHandler{ + + private final Logger logger = LoggerFactory.getLogger(ConfigHandler.class); + private final File file = new File("./config.json"); + + private ConfigurationNode node = null; + + public ConfigHandler(){} + + public boolean loadConfig(){ + logger.info("Loading config.json..."); + + if(!file.exists()){ + try(InputStream stream = CodeMCBot.class.getResourceAsStream("/config.json")){ + if(stream == null){ + logger.warn("Unable to create config.json! InputStream was null."); + return false; + } + + Files.copy(stream, file.toPath()); + logger.info("Successfully created config.json!"); + }catch(IOException ex){ + logger.warn("Encountered IOException while creating config.json!", ex); + return false; + } + } + + return reloadConfig(); + } + + public boolean reloadConfig(){ + GsonConfigurationLoader loader = GsonConfigurationLoader.builder() + .file(file) + .build(); + + try{ + return (node = loader.load()) != null; + }catch(IOException ex){ + logger.warn("Encountered IOException while loading Configuration!", ex); + return false; + } + } + + public String getString(Object... path){ + return node.node(path).getString(""); + } + + public long getLong(Object... path){ + return node.node(path).getLong(-1L); + } + + public List getLongList(Object... path){ + try{ + return node.node(path).getList(Long.class); + }catch(SerializationException ex){ + return Collections.emptyList(); + } + } +} diff --git a/src/main/java/io/codemc/bot/listeners/ModalListener.java b/src/main/java/io/codemc/bot/listeners/ModalListener.java index c87c857..6904d1f 100644 --- a/src/main/java/io/codemc/bot/listeners/ModalListener.java +++ b/src/main/java/io/codemc/bot/listeners/ModalListener.java @@ -18,6 +18,7 @@ package io.codemc.bot.listeners; +import io.codemc.bot.CodeMCBot; import io.codemc.bot.commands.CmdApplication; import io.codemc.bot.utils.CommandUtil; import io.codemc.bot.utils.Constants; @@ -46,6 +47,12 @@ public class ModalListener extends ListenerAdapter{ private final Logger logger = LoggerFactory.getLogger(ModalListener.class); + private final CodeMCBot bot; + + public ModalListener(CodeMCBot bot){ + this.bot = bot; + } + @Override public void onModalInteraction(@NotNull ModalInteractionEvent event){ if(!event.isFromGuild()) @@ -62,219 +69,213 @@ public void onModalInteraction(@NotNull ModalInteractionEvent event){ String[] args = event.getModalId().split(":"); switch(args[0]){ - case "submit": - event.deferReply(true).queue(hook -> { - - String userLink = value(event, "userlink"); - String repoLink = value(event, "repolink"); - String description = value(event, "description"); - - if(nullOrEmpty(userLink, repoLink, description)){ - CommandUtil.EmbedReply.fromHook(hook).withError( - "User Link, Repository Link or Description was not present!" - ).send(); - return; - } - - Matcher userMatcher = userLinkPattern.matcher(userLink); - Matcher repoMatcher = repoLinkPattern.matcher(repoLink); - - if(!userMatcher.matches() || !repoMatcher.matches()){ - CommandUtil.EmbedReply.fromHook(hook).withError( - "The provided User or Repository link does not match a valid GitHub URL.", - "Make sure the patterns are `https://github.com/` and `https://github.com//` respectively." - ).send(); - return; - } - - String username = String.format("[`%s`](%s)", userMatcher.group("user"), userLink); - String repo = String.format("[`%s/%s`](%s)", repoMatcher.group("user"), repoMatcher.group("repo"), repoLink); - String submitter = String.format("`%s` (%s)", event.getUser().getEffectiveName(), event.getUser().getAsMention()); - - TextChannel requestChannel = guild.getTextChannelById(Constants.REQUEST_ACCESS); - if(requestChannel == null){ - CommandUtil.EmbedReply.fromHook(hook).withError( - "Unable to retrieve `request-access` channel!" - ).send(); - return; - } - - MessageEmbed embed = CommandUtil.getEmbed() - .addField("User/Organisation:", username, true) - .addField("Repository:", repo, true) - .addField("Submitted by:", submitter, true) - .addField("Description", description, false) - .setFooter(event.getUser().getId()) - .setTimestamp(Instant.now()) - .build(); - - requestChannel.sendMessageEmbeds(embed).queue( - message -> { - CommandUtil.EmbedReply.fromHook(hook).withMessage( - "[Request sent!](" + message.getJumpUrl() + ")" - ).asSuccess().send(); - - RestAction.allOf( - message.createThreadChannel("Access Request - " + event.getUser().getName()), - message.addReaction(Emoji.fromCustom("like", 935126958193405962L, false)), - message.addReaction(Emoji.fromCustom("dislike", 935126958235344927L, false)) - ).queue(); - - logger.info("[Access Request] User {} requested access to the CI.", event.getUser().getEffectiveName()); - }, - e -> CommandUtil.EmbedReply.fromHook(hook).withError( - "Error while submitting request!", - "Reported Error: " + e.getMessage() - ).send() - ); - }); - break; - case "message": - event.deferReply(true).queue(hook -> { - if(args.length < 4){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Invalid Modal data. Expected `>=4` but received `" + args.length + "`!") - .send(); - return; - } - - TextChannel channel = guild.getTextChannelById(args[2]); - if(channel == null){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Received invalid Text Channel.") - .send(); - return; - } - - String text = value(event, "message"); - if(text == null || text.isEmpty()){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Received invalid Message to sent/edit.") - .send(); - return; - } - - if(!channel.canTalk()){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("I lack the permission to see and/or write in " + channel.getAsMention() + ".") - .send(); - return; - } - - boolean asEmbed = Boolean.parseBoolean(args[3]); - - if(args[1].equals("post")){ - if(asEmbed){ - channel.sendMessageEmbeds(CommandUtil.getEmbed().setDescription(text).build()).queue( - message -> sendConfirmation(hook, message, false), - e -> CommandUtil.EmbedReply.fromHook(hook) - .withError("Unable to sent message. Reason: " + e.getMessage()) - .send() - ); - }else{ - channel.sendMessage(text).queue( - message -> sendConfirmation(hook, message, false), - e -> CommandUtil.EmbedReply.fromHook(hook) - .withError("Unable to sent message. Reason: " + e.getMessage()) - .send() - ); - } - }else if(args[1].equals("edit")){ - if(args.length == 4){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Received invalid Modal data. Expected `>4` but got `=4`") - .send(); - return; - } - - long messageId; - try{ - messageId = Long.parseLong(args[4]); - }catch(NumberFormatException ex){ - messageId = -1L; - } + case "submit" -> event.deferReply(true).queue(hook -> { + String userLink = value(event, "userlink"); + String repoLink = value(event, "repolink"); + String description = value(event, "description"); + + if(userLink == null || userLink.isEmpty() || repoLink == null || repoLink.isEmpty() || description == null || description.isEmpty()){ + CommandUtil.EmbedReply.fromHook(hook).withError( + "User Link, Repository Link or Description was not present!" + ).send(); + return; + } + + Matcher userMatcher = userLinkPattern.matcher(userLink); + Matcher repoMatcher = repoLinkPattern.matcher(repoLink); + + if(!userMatcher.matches() || !repoMatcher.matches()){ + CommandUtil.EmbedReply.fromHook(hook).withError( + "The provided User or Repository link does not match a valid GitHub URL.", + "Make sure the patterns are `https://github.com/` and `https://github.com//` respectively." + ).send(); + return; + } + + String username = String.format("[`%s`](%s)", userMatcher.group("user"), userLink); + String repo = String.format("[`%s/%s`](%s)", repoMatcher.group("user"), repoMatcher.group("repo"), repoLink); + String submitter = String.format("`%s` (%s)", event.getUser().getEffectiveName(), event.getUser().getAsMention()); + + TextChannel requestChannel = guild.getTextChannelById(Constants.REQUEST_ACCESS); + if(requestChannel == null){ + CommandUtil.EmbedReply.fromHook(hook).withError( + "Unable to retrieve `request-access` channel!" + ).send(); + return; + } + + MessageEmbed embed = CommandUtil.getEmbed() + .addField("User/Organisation:", username, true) + .addField("Repository:", repo, true) + .addField("Submitted by:", submitter, true) + .addField("Description", description, false) + .setFooter(event.getUser().getId()) + .setTimestamp(Instant.now()) + .build(); + + requestChannel.sendMessageEmbeds(embed).queue( + message -> { + CommandUtil.EmbedReply.fromHook(hook).withMessage( + "[Request sent!](" + message.getJumpUrl() + ")" + ).asSuccess().send(); - if(messageId == -1L){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Received invalid message ID `" + args[4] + "`.") - .send(); - return; - } + RestAction.allOf( + message.createThreadChannel("Access Request - " + event.getUser().getName()), + message.addReaction(Emoji.fromCustom("like", 935126958193405962L, false)), + message.addReaction(Emoji.fromCustom("dislike", 935126958235344927L, false)) + ).queue(); - channel.retrieveMessageById(messageId).queue( - message -> { - if(asEmbed){ - message.editMessageEmbeds(CommandUtil.getEmbed().setDescription(text).build()).setReplace(true).queue( - m -> sendConfirmation(hook, m, true), - e -> CommandUtil.EmbedReply.fromHook(hook) - .withError("Unable to edit message. Reason: " + e.getMessage()) - .send() - ); - }else{ - message.editMessage(text).setReplace(true).queue( - m -> sendConfirmation(hook, m, true), - e -> CommandUtil.EmbedReply.fromHook(hook) - .withError("Unable to edit message. Reason: " + e.getMessage()) - .send() - ); - } - } + logger.info("[Access Request] User {} requested access to the CI.", event.getUser().getEffectiveName()); + }, + e -> CommandUtil.EmbedReply.fromHook(hook).withError( + "Error while submitting request!", + "Reported Error: " + e.getMessage() + ).send() + ); + }); + + case "message" -> event.deferReply(true).queue(hook -> { + if(args.length < 4){ + CommandUtil.EmbedReply.fromHook(hook) + .withError("Invalid Modal data. Expected `>=4` but received `" + args.length + "`!") + .send(); + return; + } + + TextChannel channel = guild.getTextChannelById(args[2]); + if(channel == null){ + CommandUtil.EmbedReply.fromHook(hook) + .withError("Received invalid Text Channel.") + .send(); + return; + } + + String text = value(event, "message"); + if(text == null || text.isEmpty()){ + CommandUtil.EmbedReply.fromHook(hook) + .withError("Received invalid Message to sent/edit.") + .send(); + return; + } + + if(!channel.canTalk()){ + CommandUtil.EmbedReply.fromHook(hook) + .withError("I lack the permission to see and/or write in " + channel.getAsMention() + ".") + .send(); + return; + } + + boolean asEmbed = Boolean.parseBoolean(args[3]); + + if(args[1].equals("post")){ + if(asEmbed){ + channel.sendMessageEmbeds(CommandUtil.getEmbed().setDescription(text).build()).queue( + message -> sendConfirmation(hook, message, false), + e -> CommandUtil.EmbedReply.fromHook(hook) + .withError("Unable to sent message. Reason: " + e.getMessage()) + .send() ); }else{ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Received Unknown Message type: `" + args[1] + "`.") - .send(); - } - }); - case "application": - event.deferReply(true).queue(hook -> { - if(args.length < 3){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Invalid Modal data. Expected `=3` but received `" + args.length + "`!") - .send(); - return; + channel.sendMessage(text).queue( + message -> sendConfirmation(hook, message, false), + e -> CommandUtil.EmbedReply.fromHook(hook) + .withError("Unable to sent message. Reason: " + e.getMessage()) + .send() + ); } - - if(!args[1].equals("accepted") && !args[1].equals("denied")){ + }else if(args[1].equals("edit")){ + if(args.length == 4){ CommandUtil.EmbedReply.fromHook(hook) - .withError("Received unknown Application type. Expected `accepted` or `denied` but received `" + args[1] + "`.") + .withError("Received invalid Modal data. Expected `>4` but got `=4`") .send(); return; } long messageId; try{ - messageId = Long.parseLong(args[2]); + messageId = Long.parseLong(args[4]); }catch(NumberFormatException ex){ messageId = -1L; } if(messageId == -1L){ CommandUtil.EmbedReply.fromHook(hook) - .withError("Received Invalid Message ID. Expected number but got `" + args[2] + "` instead!") + .withError("Received invalid message ID `" + args[4] + "`.") .send(); return; } - boolean accepted = args[1].equals("accepted"); - - String text = value(event, "text"); - if(text == null || text.isEmpty()){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Received invalid " + (accepted ? "Project URL" : "Reason") + ". Text was empty/null.") - .send(); - return; - } - - CmdApplication.handle(hook, guild, messageId, text, args[1].equals("accepted")); - }); - break; + channel.retrieveMessageById(messageId).queue( + message -> { + if(asEmbed){ + message.editMessageEmbeds(CommandUtil.getEmbed().setDescription(text).build()).setReplace(true).queue( + m -> sendConfirmation(hook, m, true), + e -> CommandUtil.EmbedReply.fromHook(hook) + .withError("Unable to edit message. Reason: " + e.getMessage()) + .send() + ); + }else{ + message.editMessage(text).setReplace(true).queue( + m -> sendConfirmation(hook, m, true), + e -> CommandUtil.EmbedReply.fromHook(hook) + .withError("Unable to edit message. Reason: " + e.getMessage()) + .send() + ); + } + } + ); + }else{ + CommandUtil.EmbedReply.fromHook(hook) + .withError("Received Unknown Message type: `" + args[1] + "`.") + .send(); + } + }); + + case "application" -> event.deferReply(true).queue(hook -> { + if(args.length < 3){ + CommandUtil.EmbedReply.fromHook(hook) + .withError("Invalid Modal data. Expected `=3` but received `" + args.length + "`!") + .send(); + return; + } + + if(!args[1].equals("accepted") && !args[1].equals("denied")){ + CommandUtil.EmbedReply.fromHook(hook) + .withError("Received unknown Application type. Expected `accepted` or `denied` but received `" + args[1] + "`.") + .send(); + return; + } + + long messageId; + try{ + messageId = Long.parseLong(args[2]); + }catch(NumberFormatException ex){ + messageId = -1L; + } + + if(messageId == -1L){ + CommandUtil.EmbedReply.fromHook(hook) + .withError("Received Invalid Message ID. Expected number but got `" + args[2] + "` instead!") + .send(); + return; + } + + boolean accepted = args[1].equals("accepted"); + + String text = value(event, "text"); + if(text == null || text.isEmpty()){ + CommandUtil.EmbedReply.fromHook(hook) + .withError("Received invalid " + (accepted ? "Project URL" : "Reason") + ". Text was empty/null.") + .send(); + return; + } + + CmdApplication.handle(bot, hook, guild, messageId, text, accepted); + }); - default: - CommandUtil.EmbedReply.fromModalEvent(event) - .withError("Received Modal with unknown ID `" + event.getModalId() + "`.") - .send(); - break; + default -> CommandUtil.EmbedReply.fromModalEvent(event) + .withError("Received Modal with unknown ID `" + event.getModalId() + "`.") + .send(); } } @@ -287,15 +288,6 @@ private void sendConfirmation(InteractionHook hook, Message message, boolean edi logger.info("[Message] User {} {} a Message as the Bot.", hook.getInteraction().getUser().getEffectiveName(), edit ? "edited" : "sent"); } - private boolean nullOrEmpty(String... values){ - for(String value : values){ - if(value == null || value.isEmpty()) - return true; - } - - return false; - } - private String value(ModalInteractionEvent event, String id){ ModalMapping value = event.getValue(id); if(value == null) diff --git a/src/main/java/io/codemc/bot/menu/ApplicationMenu.java b/src/main/java/io/codemc/bot/menu/ApplicationMenu.java index 16092be..3f23703 100644 --- a/src/main/java/io/codemc/bot/menu/ApplicationMenu.java +++ b/src/main/java/io/codemc/bot/menu/ApplicationMenu.java @@ -20,28 +20,19 @@ import com.jagrosh.jdautilities.command.MessageContextMenu; import com.jagrosh.jdautilities.command.MessageContextMenuEvent; +import io.codemc.bot.CodeMCBot; +import io.codemc.bot.utils.CommandUtil; import io.codemc.bot.utils.Constants; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.interactions.components.ActionRow; 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.Modal; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - public class ApplicationMenu{ - - private static final List allowedRoles = Arrays.asList( - Constants.ROLE_ADMINISTRATOR, - Constants.ROLE_MODERATOR - ); - - private static void handleEvent(MessageContextMenuEvent event, boolean accepted){ + private static void handleEvent(CodeMCBot bot, MessageContextMenuEvent event, boolean accepted){ Guild guild = event.getGuild(); if(guild == null || !guild.getId().equals(Constants.SERVER)){ event.reply("This Context Menu Action may only work in the CodeMC Server!") @@ -64,24 +55,10 @@ private static void handleEvent(MessageContextMenuEvent event, boolean accepted) return; } - List roleIds = member.getRoles().stream().map(Role::getIdLong).collect(Collectors.toList()); - boolean hasRole = false; - for(long roleId : roleIds){ - if(allowedRoles.contains(roleId)){ - hasRole = true; - break; - } - } - - if(!hasRole){ - List roleNames = guild.getRoles().stream() - .filter(role -> allowedRoles.contains(role.getIdLong())) - .map(Role::getName) - .collect(Collectors.toList()); - - event.reply( - "You do not have any of the allowed roles to execute this Context Menu Action.\n" + - "Allowed Roles: " + String.join(", ", roleNames) + if(!CommandUtil.hasRole(member, bot.getConfigHandler().getLongList("allowed_roles", "application"))){ + event.replyEmbeds(CommandUtil.getEmbed().setColor(0xFF0000) + .setDescription("You do not have permissions to use this Context Menu Action!") + .build() ).queue(); return; } @@ -111,25 +88,33 @@ private static void handleEvent(MessageContextMenuEvent event, boolean accepted) public static class Accept extends MessageContextMenu{ - public Accept(){ + private final CodeMCBot bot; + + public Accept(CodeMCBot bot){ + this.bot = bot; + this.name = "Accept this application"; } @Override protected void execute(MessageContextMenuEvent event){ - handleEvent(event, true); + handleEvent(bot, event, true); } } public static class Deny extends MessageContextMenu{ - public Deny(){ + private final CodeMCBot bot; + + public Deny(CodeMCBot bot){ + this.bot = bot; + this.name = "Deny this application"; } @Override protected void execute(MessageContextMenuEvent event){ - handleEvent(event, false); + handleEvent(bot, event, false); } } } diff --git a/src/main/java/io/codemc/bot/utils/CommandUtil.java b/src/main/java/io/codemc/bot/utils/CommandUtil.java index a83cd50..b806c9a 100644 --- a/src/main/java/io/codemc/bot/utils/CommandUtil.java +++ b/src/main/java/io/codemc/bot/utils/CommandUtil.java @@ -21,10 +21,13 @@ import ch.qos.logback.classic.Logger; import com.jagrosh.jdautilities.command.SlashCommandEvent; import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.interactions.InteractionHook; import org.slf4j.LoggerFactory; +import java.util.List; + public class CommandUtil{ private static final Logger LOG = (Logger)LoggerFactory.getLogger(CommandUtil.class); @@ -35,6 +38,13 @@ public static EmbedBuilder getEmbed(){ return new EmbedBuilder().setColor(0x0172BA); } + public static boolean hasRole(Member member, List roleIds){ + return member.getRoles().stream() + .filter(role -> roleIds.contains(role.getIdLong())) + .findFirst() + .orElse(null) != null; + } + public static class EmbedReply { private final SlashCommandEvent commandEvent; diff --git a/src/main/resources/config.json b/src/main/resources/config.json new file mode 100644 index 0000000..b834ba3 --- /dev/null +++ b/src/main/resources/config.json @@ -0,0 +1,47 @@ +{ + "bot_token": "TOKEN", + "server": 405915656039694336, + "channels": { + "request_access": 1233971297185431582, + "accepted_requests": 784119059138478080, + "rejected_requests": 800423355551449098 + }, + "author_role": 405918641859723294, + "allowed_roles": { + "application": [ + 405917902865170453, + 659568973079379971, + 1233971297185431582 + ], + "disable": [ + 405917902865170453 + ], + "msg": [ + 405917902865170453 + ], + "reload": [ + 405917902865170453 + ] + }, + "users": { + "owner": 204232208049766400, + "co_owners": [ + 143088571656437760, + 282975975954710528 + ] + }, + "messages": { + "accepted": [ + "Your request has been **accepted**!", + "You will now be able to login with your GitHub Account and access the approved Repository on the CI.", + "", + "Remember to [visit our Documentation](https://docs.codemc.io) and [Read our FAQ](https://docs.codemc.io/faq) to learn how to setup automatic builds!" + ], + "denied": [ + "Your request has been **rejected**!", + "The reason for the denial is stated below.", + "", + "You may re-apply unless mentioned otherwise in the Reason." + ] + } +} \ No newline at end of file From 59b350ec2c384d4326961aaf386fc6afe47fd42b Mon Sep 17 00:00:00 2001 From: Andre601 Date: Sun, 28 Apr 2024 16:29:10 +0200 Subject: [PATCH 3/5] Remove Constants.java --- gradle/wrapper/gradle-wrapper.properties | 3 +- src/main/java/io/codemc/bot/CodeMCBot.java | 2 +- .../io/codemc/bot/commands/BotCommand.java | 40 +++++---------- .../codemc/bot/commands/CmdApplication.java | 20 ++++---- .../io/codemc/bot/commands/CmdDisable.java | 2 + .../java/io/codemc/bot/commands/CmdMsg.java | 6 +++ .../io/codemc/bot/commands/CmdReload.java | 4 +- .../io/codemc/bot/commands/CmdSubmit.java | 5 +- .../io/codemc/bot/config/ConfigHandler.java | 8 +++ .../codemc/bot/listeners/ModalListener.java | 5 +- .../io/codemc/bot/menu/ApplicationMenu.java | 5 +- .../java/io/codemc/bot/utils/CommandUtil.java | 4 ++ .../java/io/codemc/bot/utils/Constants.java | 49 ------------------- 13 files changed, 55 insertions(+), 98 deletions(-) delete mode 100644 src/main/java/io/codemc/bot/utils/Constants.java diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 69a9715..a344743 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Sun Apr 28 16:10:49 CEST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/io/codemc/bot/CodeMCBot.java b/src/main/java/io/codemc/bot/CodeMCBot.java index f5aacbd..0e70bed 100644 --- a/src/main/java/io/codemc/bot/CodeMCBot.java +++ b/src/main/java/io/codemc/bot/CodeMCBot.java @@ -97,7 +97,7 @@ private void start() throws LoginException{ new CmdDisable(this), new CmdMsg(this), new CmdReload(this), - new CmdSubmit() + new CmdSubmit(this) ); logger.info("Adding Context Menus..."); diff --git a/src/main/java/io/codemc/bot/commands/BotCommand.java b/src/main/java/io/codemc/bot/commands/BotCommand.java index 852605f..58dee56 100644 --- a/src/main/java/io/codemc/bot/commands/BotCommand.java +++ b/src/main/java/io/codemc/bot/commands/BotCommand.java @@ -20,22 +20,26 @@ import com.jagrosh.jdautilities.command.SlashCommand; import com.jagrosh.jdautilities.command.SlashCommandEvent; +import io.codemc.bot.CodeMCBot; import io.codemc.bot.utils.CommandUtil; -import io.codemc.bot.utils.Constants; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.interactions.InteractionHook; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; public abstract class BotCommand extends SlashCommand{ protected List allowedRoles = new ArrayList<>(); protected boolean hasModalReply = false; + public final CodeMCBot bot; + + public BotCommand(CodeMCBot bot){ + this.bot = bot; + } + @Override public void execute(SlashCommandEvent event){ Guild guild = event.getGuild(); @@ -46,7 +50,7 @@ public void execute(SlashCommandEvent event){ return; } - if(!guild.getId().equals(Constants.SERVER)){ + if(guild.getIdLong() != bot.getConfigHandler().getLong("server")){ CommandUtil.EmbedReply.fromCommandEvent(event) .withError("Unable to find CodeMC Server!") .send(); @@ -61,29 +65,11 @@ public void execute(SlashCommandEvent event){ return; } - if(!allowedRoles.isEmpty()){ - boolean hasRole = false; - for(Role role : member.getRoles()){ - if(allowedRoles.contains(role.getIdLong())){ - hasRole = true; - break; - } - } - - if(!hasRole){ - List names = guild.getRoles().stream() - .filter(role -> allowedRoles.contains(role.getIdLong())) - .map(Role::getName) - .collect(Collectors.toList()); - - CommandUtil.EmbedReply.fromCommandEvent(event) - .withError( - "You don't have any of the allowed roles to execute this command.", - "Allowed Roles: " + String.join(", ", names) - ) - .send(); - return; - } + if(!CommandUtil.hasRole(member, allowedRoles)){ + CommandUtil.EmbedReply.fromCommandEvent(event) + .withError("You lack the permissions required to use this command!") + .send(); + return; } if(hasModalReply){ diff --git a/src/main/java/io/codemc/bot/commands/CmdApplication.java b/src/main/java/io/codemc/bot/commands/CmdApplication.java index 7f6d20d..f459e78 100644 --- a/src/main/java/io/codemc/bot/commands/CmdApplication.java +++ b/src/main/java/io/codemc/bot/commands/CmdApplication.java @@ -22,7 +22,6 @@ import com.jagrosh.jdautilities.command.SlashCommandEvent; import io.codemc.bot.CodeMCBot; import io.codemc.bot.utils.CommandUtil; -import io.codemc.bot.utils.Constants; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; @@ -46,6 +45,8 @@ public class CmdApplication extends BotCommand{ public CmdApplication(CodeMCBot bot){ + super(bot); + this.name = "application"; this.help = "Accept or deny applications."; @@ -121,7 +122,7 @@ public static void handle(CodeMCBot bot, InteractionHook hook, Guild guild, long return; } - channel.sendMessage(getMessage(userId, userLink, repoLink, str, accepted)).queue(m -> { + channel.sendMessage(getMessage(bot, userId, userLink, repoLink, str, accepted)).queue(m -> { ThreadChannel thread = message.getStartedThread(); if(thread != null && !thread.isArchived()){ thread.getManager().setArchived(true) @@ -175,10 +176,13 @@ public static void handle(CodeMCBot bot, InteractionHook hook, Guild guild, long }); } - private static MessageCreateData getMessage(String userId, String userLink, String repoLink, String str, boolean accepted){ + private static MessageCreateData getMessage(CodeMCBot bot, String userId, String userLink, String repoLink, String str, boolean accepted){ + + String msg = String.join("\n", bot.getConfigHandler().getStringList("messages", (accepted ? "accepted" : "denied"))); + MessageEmbed embed = new EmbedBuilder() .setColor(accepted ? 0x00FF00 : 0xFF0000) - .setDescription(accepted ? Constants.ACCEPTED_MSG : Constants.REJECTED_MSG) + .setDescription(msg) .addField("User/Organisation:", userLink, true) .addField("Repository:", repoLink, true) .addField(accepted ? "New Project:" : "Reason:", str, false) @@ -194,10 +198,8 @@ private static class Accept extends BotCommand{ private final Pattern projectUrlPattern = Pattern.compile("^https://ci\\.codemc\\.io/job/[a-zA-Z0-9-]+/job/[a-zA-Z0-9-_.]+/?$"); - private final CodeMCBot bot; - public Accept(CodeMCBot bot){ - this.bot = bot; + super(bot); this.name = "accept"; this.help = "Accept an application"; @@ -244,10 +246,8 @@ public void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild g private static class Deny extends BotCommand{ - private final CodeMCBot bot; - public Deny(CodeMCBot bot){ - this.bot = bot; + super(bot); this.name = "deny"; this.help = "Deny an application"; diff --git a/src/main/java/io/codemc/bot/commands/CmdDisable.java b/src/main/java/io/codemc/bot/commands/CmdDisable.java index 804e5ea..da3007f 100644 --- a/src/main/java/io/codemc/bot/commands/CmdDisable.java +++ b/src/main/java/io/codemc/bot/commands/CmdDisable.java @@ -32,6 +32,8 @@ public class CmdDisable extends BotCommand{ private final Logger logger = LoggerFactory.getLogger("Shutdown"); public CmdDisable(CodeMCBot bot){ + super(bot); + this.name = "disable"; this.help = "Disables the bot."; diff --git a/src/main/java/io/codemc/bot/commands/CmdMsg.java b/src/main/java/io/codemc/bot/commands/CmdMsg.java index 2f83eea..75c9e0c 100644 --- a/src/main/java/io/codemc/bot/commands/CmdMsg.java +++ b/src/main/java/io/codemc/bot/commands/CmdMsg.java @@ -42,6 +42,8 @@ public class CmdMsg extends BotCommand{ public CmdMsg(CodeMCBot bot){ + super(bot); + this.name = "msg"; this.help = "Sends a message in a specified channel or edits one."; @@ -62,6 +64,8 @@ public void withModalReply(SlashCommandEvent event){} private static class Post extends BotCommand{ public Post(CodeMCBot bot){ + super(bot); + this.name = "send"; this.help = "Sends a message as the Bot."; @@ -109,6 +113,8 @@ public void withModalReply(SlashCommandEvent event){ private static class Edit extends BotCommand{ public Edit(CodeMCBot bot){ + super(bot); + this.name = "edit"; this.help = "Edit an existing message of the bot."; diff --git a/src/main/java/io/codemc/bot/commands/CmdReload.java b/src/main/java/io/codemc/bot/commands/CmdReload.java index f3227a9..a923280 100644 --- a/src/main/java/io/codemc/bot/commands/CmdReload.java +++ b/src/main/java/io/codemc/bot/commands/CmdReload.java @@ -27,10 +27,8 @@ public class CmdReload extends BotCommand{ - private final CodeMCBot bot; - public CmdReload(CodeMCBot bot){ - this.bot = bot; + super(bot); this.name = "reload"; this.help = "Reloads the configuration."; diff --git a/src/main/java/io/codemc/bot/commands/CmdSubmit.java b/src/main/java/io/codemc/bot/commands/CmdSubmit.java index 414c4b0..b005a5b 100644 --- a/src/main/java/io/codemc/bot/commands/CmdSubmit.java +++ b/src/main/java/io/codemc/bot/commands/CmdSubmit.java @@ -19,6 +19,7 @@ package io.codemc.bot.commands; import com.jagrosh.jdautilities.command.SlashCommandEvent; +import io.codemc.bot.CodeMCBot; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageEmbed; @@ -30,7 +31,9 @@ public class CmdSubmit extends BotCommand{ - public CmdSubmit(){ + public CmdSubmit(CodeMCBot bot){ + super(bot); + this.name = "submit"; this.help = "Submit a request to join the CodeMC CI with a project."; diff --git a/src/main/java/io/codemc/bot/config/ConfigHandler.java b/src/main/java/io/codemc/bot/config/ConfigHandler.java index 02170b4..68f0f29 100644 --- a/src/main/java/io/codemc/bot/config/ConfigHandler.java +++ b/src/main/java/io/codemc/bot/config/ConfigHandler.java @@ -90,4 +90,12 @@ public List getLongList(Object... path){ return Collections.emptyList(); } } + + public List getStringList(Object... path){ + try{ + return node.node(path).getList(String.class); + }catch(SerializationException ex){ + return Collections.emptyList(); + } + } } diff --git a/src/main/java/io/codemc/bot/listeners/ModalListener.java b/src/main/java/io/codemc/bot/listeners/ModalListener.java index 6904d1f..ad62e73 100644 --- a/src/main/java/io/codemc/bot/listeners/ModalListener.java +++ b/src/main/java/io/codemc/bot/listeners/ModalListener.java @@ -21,7 +21,6 @@ import io.codemc.bot.CodeMCBot; import io.codemc.bot.commands.CmdApplication; import io.codemc.bot.utils.CommandUtil; -import io.codemc.bot.utils.Constants; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.MessageEmbed; @@ -59,7 +58,7 @@ public void onModalInteraction(@NotNull ModalInteractionEvent event){ return; Guild guild = event.getGuild(); - if(guild == null || !guild.getId().equals(Constants.SERVER)){ + if(guild == null || guild.getIdLong() != bot.getConfigHandler().getLong("server")){ CommandUtil.EmbedReply.fromModalEvent(event) .withError("Unable to retrieve CodeMC Server!") .send(); @@ -96,7 +95,7 @@ public void onModalInteraction(@NotNull ModalInteractionEvent event){ String repo = String.format("[`%s/%s`](%s)", repoMatcher.group("user"), repoMatcher.group("repo"), repoLink); String submitter = String.format("`%s` (%s)", event.getUser().getEffectiveName(), event.getUser().getAsMention()); - TextChannel requestChannel = guild.getTextChannelById(Constants.REQUEST_ACCESS); + TextChannel requestChannel = guild.getTextChannelById(bot.getConfigHandler().getLong("channels", "request_access")); if(requestChannel == null){ CommandUtil.EmbedReply.fromHook(hook).withError( "Unable to retrieve `request-access` channel!" diff --git a/src/main/java/io/codemc/bot/menu/ApplicationMenu.java b/src/main/java/io/codemc/bot/menu/ApplicationMenu.java index 3f23703..2e921cd 100644 --- a/src/main/java/io/codemc/bot/menu/ApplicationMenu.java +++ b/src/main/java/io/codemc/bot/menu/ApplicationMenu.java @@ -22,7 +22,6 @@ import com.jagrosh.jdautilities.command.MessageContextMenuEvent; import io.codemc.bot.CodeMCBot; import io.codemc.bot.utils.CommandUtil; -import io.codemc.bot.utils.Constants; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; @@ -34,7 +33,7 @@ public class ApplicationMenu{ private static void handleEvent(CodeMCBot bot, MessageContextMenuEvent event, boolean accepted){ Guild guild = event.getGuild(); - if(guild == null || !guild.getId().equals(Constants.SERVER)){ + if(guild == null || guild.getIdLong() != bot.getConfigHandler().getLong("server")){ event.reply("This Context Menu Action may only work in the CodeMC Server!") .setEphemeral(true) .queue(); @@ -42,7 +41,7 @@ private static void handleEvent(CodeMCBot bot, MessageContextMenuEvent event, bo } MessageChannel channel = event.getChannel(); - if(channel == null || !channel.getId().equals(Constants.REQUEST_ACCESS)){ + if(channel == null || channel.getIdLong() != bot.getConfigHandler().getLong("channels", "request_access")){ event.reply("This Context Menu Action may only work in the request-access channel of the CodeMC Server!") .setEphemeral(true) .queue(); diff --git a/src/main/java/io/codemc/bot/utils/CommandUtil.java b/src/main/java/io/codemc/bot/utils/CommandUtil.java index b806c9a..f0e36d7 100644 --- a/src/main/java/io/codemc/bot/utils/CommandUtil.java +++ b/src/main/java/io/codemc/bot/utils/CommandUtil.java @@ -38,7 +38,11 @@ public static EmbedBuilder getEmbed(){ return new EmbedBuilder().setColor(0x0172BA); } + @SuppressWarnings("BooleanMethodIsAlwaysInverted") public static boolean hasRole(Member member, List roleIds){ + if(roleIds.isEmpty()) + return true; + return member.getRoles().stream() .filter(role -> roleIds.contains(role.getIdLong())) .findFirst() diff --git a/src/main/java/io/codemc/bot/utils/Constants.java b/src/main/java/io/codemc/bot/utils/Constants.java deleted file mode 100644 index 2726621..0000000 --- a/src/main/java/io/codemc/bot/utils/Constants.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2021 CodeMC.io - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.codemc.bot.utils; - -public class Constants{ - - // Server ID - public static final String SERVER = "405915656039694336"; - - // Channel IDs - public static final String REQUEST_ACCESS = "782998340559306792"; - public static final String ACCEPTED_REQUESTS = "784119059138478080"; - public static final String REJECTED_REQUESTS = "800423355551449098"; - - // Role IDs - public static final long ROLE_ADMINISTRATOR = 405917902865170453L; - public static final long ROLE_MODERATOR = 659568973079379971L; - public static final long ROLE_CONTRIBUTOR = 1233119938621997097L; - public static final long ROLE_AUTHOR = 405918641859723294L; - - // Result messages for applications - public static final String ACCEPTED_MSG = - "Your request has been **accepted**!\n" + - "You will now be able to login with your GitHub Account and access the approved Repository on the CI.\n" + - "\n" + - "Remember to [visit our Documentation](https://docs.codemc.io) and [read our FAQ](https://docs.codemc.io/faq) " + - "to know how to setup automatic builds!"; - public static final String REJECTED_MSG = - "Your request has been **rejected**.\n" + - "The reason for this can be found below.\n" + - "\n" + - "You may re-apply for access unless mentioned so in the reason."; -} From af8206338b658c90c8aa596639408707bb334ce8 Mon Sep 17 00:00:00 2001 From: Andre601 Date: Thu, 16 May 2024 14:47:24 +0200 Subject: [PATCH 4/5] Apply suggested code changes --- .gitignore | 10 +++- build.gradle | 7 ++- gradle/wrapper/gradle-wrapper.properties | 2 +- .../io/codemc/bot/menu/ApplicationMenu.java | 4 +- src/main/resources/config.json | 49 +++++-------------- src/main/resources/config.json.sample | 47 ++++++++++++++++++ 6 files changed, 75 insertions(+), 44 deletions(-) create mode 100644 src/main/resources/config.json.sample diff --git a/.gitignore b/.gitignore index 0591374..7e8f5e6 100644 --- a/.gitignore +++ b/.gitignore @@ -133,4 +133,12 @@ fabric.properties # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* -# End of https://www.toptal.com/developers/gitignore/api/intellij,java \ No newline at end of file +# End of https://www.toptal.com/developers/gitignore/api/intellij,java +/.idea/copyright/*.xml +/.idea/.gitignore +/.idea/compiler.xml +/.idea/gradle.xml +/.idea/jarRepositories.xml +/.idea/misc.xml +/.idea/uiDesigner.xml +/.idea/vcs.xml diff --git a/build.gradle b/build.gradle index 0479852..8ff0474 100644 --- a/build.gradle +++ b/build.gradle @@ -17,10 +17,9 @@ */ plugins { - id 'java' - id 'application' - id 'com.github.johnrengelman.shadow' version '5.2.0' - id 'idea' + id "java" + id "application" + id "com.github.johnrengelman.shadow" version "8.1.1" } group 'io.codemc' diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a344743..b38ed1f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Sun Apr 28 16:10:49 CEST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/io/codemc/bot/menu/ApplicationMenu.java b/src/main/java/io/codemc/bot/menu/ApplicationMenu.java index 2e921cd..0241cca 100644 --- a/src/main/java/io/codemc/bot/menu/ApplicationMenu.java +++ b/src/main/java/io/codemc/bot/menu/ApplicationMenu.java @@ -85,7 +85,7 @@ private static void handleEvent(CodeMCBot bot, MessageContextMenuEvent event, bo event.replyModal(modal).queue(); } - public static class Accept extends MessageContextMenu{ + public static final class Accept extends MessageContextMenu{ private final CodeMCBot bot; @@ -101,7 +101,7 @@ protected void execute(MessageContextMenuEvent event){ } } - public static class Deny extends MessageContextMenu{ + public static final class Deny extends MessageContextMenu{ private final CodeMCBot bot; diff --git a/src/main/resources/config.json b/src/main/resources/config.json index b834ba3..f14ee16 100644 --- a/src/main/resources/config.json +++ b/src/main/resources/config.json @@ -1,47 +1,24 @@ { "bot_token": "TOKEN", - "server": 405915656039694336, + "server": 0, "channels": { - "request_access": 1233971297185431582, - "accepted_requests": 784119059138478080, - "rejected_requests": 800423355551449098 + "request_access": 0, + "accepted_requests": 0, + "rejected_requests": 0 }, - "author_role": 405918641859723294, + "author_role": 0, "allowed_roles": { - "application": [ - 405917902865170453, - 659568973079379971, - 1233971297185431582 - ], - "disable": [ - 405917902865170453 - ], - "msg": [ - 405917902865170453 - ], - "reload": [ - 405917902865170453 - ] + "application": [], + "disable": [], + "msg": [], + "reload": [] }, "users": { - "owner": 204232208049766400, - "co_owners": [ - 143088571656437760, - 282975975954710528 - ] + "owner": 0, + "co_owners": [] }, "messages": { - "accepted": [ - "Your request has been **accepted**!", - "You will now be able to login with your GitHub Account and access the approved Repository on the CI.", - "", - "Remember to [visit our Documentation](https://docs.codemc.io) and [Read our FAQ](https://docs.codemc.io/faq) to learn how to setup automatic builds!" - ], - "denied": [ - "Your request has been **rejected**!", - "The reason for the denial is stated below.", - "", - "You may re-apply unless mentioned otherwise in the Reason." - ] + "accepted": [], + "denied": [] } } \ No newline at end of file diff --git a/src/main/resources/config.json.sample b/src/main/resources/config.json.sample new file mode 100644 index 0000000..b834ba3 --- /dev/null +++ b/src/main/resources/config.json.sample @@ -0,0 +1,47 @@ +{ + "bot_token": "TOKEN", + "server": 405915656039694336, + "channels": { + "request_access": 1233971297185431582, + "accepted_requests": 784119059138478080, + "rejected_requests": 800423355551449098 + }, + "author_role": 405918641859723294, + "allowed_roles": { + "application": [ + 405917902865170453, + 659568973079379971, + 1233971297185431582 + ], + "disable": [ + 405917902865170453 + ], + "msg": [ + 405917902865170453 + ], + "reload": [ + 405917902865170453 + ] + }, + "users": { + "owner": 204232208049766400, + "co_owners": [ + 143088571656437760, + 282975975954710528 + ] + }, + "messages": { + "accepted": [ + "Your request has been **accepted**!", + "You will now be able to login with your GitHub Account and access the approved Repository on the CI.", + "", + "Remember to [visit our Documentation](https://docs.codemc.io) and [Read our FAQ](https://docs.codemc.io/faq) to learn how to setup automatic builds!" + ], + "denied": [ + "Your request has been **rejected**!", + "The reason for the denial is stated below.", + "", + "You may re-apply unless mentioned otherwise in the Reason." + ] + } +} \ No newline at end of file From c8bf9a48055cd6e556a4d6ec23c547841ecfeb10 Mon Sep 17 00:00:00 2001 From: Andre601 Date: Sun, 28 Jul 2024 22:52:51 +0200 Subject: [PATCH 5/5] Update a lot of stuff. --- build.gradle | 8 +- src/main/java/io/codemc/bot/CodeMCBot.java | 9 +- .../io/codemc/bot/commands/BotCommand.java | 16 +- .../codemc/bot/commands/CmdApplication.java | 66 +++---- .../io/codemc/bot/commands/CmdDisable.java | 2 +- .../java/io/codemc/bot/commands/CmdMsg.java | 14 +- .../io/codemc/bot/commands/CmdReload.java | 11 +- .../io/codemc/bot/commands/CmdSubmit.java | 14 +- .../codemc/bot/listeners/ButtonListener.java | 115 ++++++++++++ .../codemc/bot/listeners/ModalListener.java | 165 ++++++++---------- .../io/codemc/bot/menu/ApplicationMenu.java | 119 ------------- .../java/io/codemc/bot/utils/CommandUtil.java | 79 +++------ src/main/resources/config.json | 14 +- src/main/resources/config.json.sample | 42 +++-- 14 files changed, 314 insertions(+), 360 deletions(-) create mode 100644 src/main/java/io/codemc/bot/listeners/ButtonListener.java delete mode 100644 src/main/java/io/codemc/bot/menu/ApplicationMenu.java diff --git a/build.gradle b/build.gradle index 8ff0474..f349a3a 100644 --- a/build.gradle +++ b/build.gradle @@ -31,16 +31,16 @@ repositories { mavenCentral() maven { url = 'https://jitpack.io' } maven { url = 'https://repo.codemc.io/repository/maven-public' } - maven { url = 'https://m2.chew.pro/snapshots' } + maven { url = 'https://m2.chew.pro/releases' } } dependencies { implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.4.14' - implementation(group: 'net.dv8tion', name: 'JDA', version:'5.0.0-beta.23'){ + implementation(group: 'net.dv8tion', name: 'JDA', version:'5.0.0'){ exclude(module: 'opus-java') } - implementation group: 'pw.chew', name: 'jda-chewtils-commons', version: '2.0-SNAPSHOT' - implementation group: 'pw.chew', name: 'jda-chewtils-command', version: '2.0-SNAPSHOT' + implementation group: 'pw.chew', name: 'jda-chewtils-commons', version: '2.0' + implementation group: 'pw.chew', name: 'jda-chewtils-command', version: '2.0' implementation group: 'org.spongepowered', name: 'configurate-gson', version: '4.1.2' } diff --git a/src/main/java/io/codemc/bot/CodeMCBot.java b/src/main/java/io/codemc/bot/CodeMCBot.java index 0e70bed..0bcdc0d 100644 --- a/src/main/java/io/codemc/bot/CodeMCBot.java +++ b/src/main/java/io/codemc/bot/CodeMCBot.java @@ -21,8 +21,8 @@ import com.jagrosh.jdautilities.command.CommandClientBuilder; import io.codemc.bot.commands.*; import io.codemc.bot.config.ConfigHandler; +import io.codemc.bot.listeners.ButtonListener; import io.codemc.bot.listeners.ModalListener; -import io.codemc.bot.menu.ApplicationMenu; import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.entities.Activity; import net.dv8tion.jda.api.requests.GatewayIntent; @@ -100,12 +100,6 @@ private void start() throws LoginException{ new CmdSubmit(this) ); - logger.info("Adding Context Menus..."); - clientBuilder.addContextMenus( - new ApplicationMenu.Accept(this), - new ApplicationMenu.Deny(this) - ); - logger.info("Starting bot..."); JDABuilder.createDefault(token) .enableIntents( @@ -120,6 +114,7 @@ private void start() throws LoginException{ )) .addEventListeners( clientBuilder.build(), + new ButtonListener(this), new ModalListener(this) ) .build(); diff --git a/src/main/java/io/codemc/bot/commands/BotCommand.java b/src/main/java/io/codemc/bot/commands/BotCommand.java index 58dee56..81a5609 100644 --- a/src/main/java/io/codemc/bot/commands/BotCommand.java +++ b/src/main/java/io/codemc/bot/commands/BotCommand.java @@ -44,30 +44,30 @@ public BotCommand(CodeMCBot bot){ public void execute(SlashCommandEvent event){ Guild guild = event.getGuild(); if(guild == null){ - CommandUtil.EmbedReply.fromCommandEvent(event) - .withError("Command can only be executed in a Server!") + CommandUtil.EmbedReply.from(event) + .error("Command can only be executed in a Server!") .send(); return; } if(guild.getIdLong() != bot.getConfigHandler().getLong("server")){ - CommandUtil.EmbedReply.fromCommandEvent(event) - .withError("Unable to find CodeMC Server!") + CommandUtil.EmbedReply.from(event) + .error("Unable to find CodeMC Server!") .send(); return; } Member member = event.getMember(); if(member == null){ - CommandUtil.EmbedReply.fromCommandEvent(event) - .withError("Unable to retrieve Member from Event!") + CommandUtil.EmbedReply.from(event) + .error("Unable to retrieve Member from Event!") .send(); return; } if(!CommandUtil.hasRole(member, allowedRoles)){ - CommandUtil.EmbedReply.fromCommandEvent(event) - .withError("You lack the permissions required to use this command!") + CommandUtil.EmbedReply.from(event) + .error("You lack the permissions required to use this command!") .send(); return; } diff --git a/src/main/java/io/codemc/bot/commands/CmdApplication.java b/src/main/java/io/codemc/bot/commands/CmdApplication.java index f459e78..301ff45 100644 --- a/src/main/java/io/codemc/bot/commands/CmdApplication.java +++ b/src/main/java/io/codemc/bot/commands/CmdApplication.java @@ -50,7 +50,7 @@ public CmdApplication(CodeMCBot bot){ this.name = "application"; this.help = "Accept or deny applications."; - this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "application"); + this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "commands", "application"); this.children = new SlashCommand[]{ new Accept(bot), @@ -67,28 +67,26 @@ public void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild g public static void handle(CodeMCBot bot, InteractionHook hook, Guild guild, long messageId, String str, boolean accepted){ TextChannel requestChannel = guild.getTextChannelById(bot.getConfigHandler().getLong("channel", "request_access")); if(requestChannel == null){ - CommandUtil.EmbedReply.fromHook(hook).withError("Unable to retrieve `request-access` channel.").send(); + CommandUtil.EmbedReply.from(hook).error("Unable to retrieve `request-access` channel.").send(); return; } requestChannel.retrieveMessageById(messageId).queue(message -> { List embeds = message.getEmbeds(); if(embeds.isEmpty()){ - CommandUtil.EmbedReply.fromHook(hook).withError("Provided message does not have any embeds.").send(); + CommandUtil.EmbedReply.from(hook).error("Provided message does not have any embeds.").send(); return; } MessageEmbed embed = embeds.get(0); if(embed.getFooter() == null || embed.getFields().isEmpty()){ - CommandUtil.EmbedReply.fromHook(hook).withError( - "Embed does not have a footer or any Embed Fields." - ).send(); + CommandUtil.EmbedReply.from(hook).error("Embed does not have a footer or any Embed Fields.").send(); return; } String userId = embed.getFooter().getText(); if(userId == null || userId.isEmpty()){ - CommandUtil.EmbedReply.fromHook(hook).withError("Embed does not have a valid footer.").send(); + CommandUtil.EmbedReply.from(hook).error("Embed does not have a valid footer.").send(); return; } @@ -107,7 +105,7 @@ public static void handle(CodeMCBot bot, InteractionHook hook, Guild guild, long } if(userLink == null || repoLink == null){ - CommandUtil.EmbedReply.fromHook(hook).withError("Embed does not have any valid Fields.").send(); + CommandUtil.EmbedReply.from(hook).error("Embed does not have any valid Fields.").send(); return; } @@ -116,8 +114,8 @@ public static void handle(CodeMCBot bot, InteractionHook hook, Guild guild, long : bot.getConfigHandler().getLong("channels", "rejected_requests") ); if(channel == null){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Unable to retrieve `" + (accepted ? "accepted" : "rejected") + "-requests` channel.") + CommandUtil.EmbedReply.from(hook) + .error("Unable to retrieve `" + (accepted ? "accepted" : "rejected") + "-requests` channel.") .send(); return; } @@ -135,24 +133,23 @@ public static void handle(CodeMCBot bot, InteractionHook hook, Guild guild, long Member member = guild.getMemberById(userId); if(!accepted){ - CommandUtil.EmbedReply.fromHook(hook) - .withMessage("Denied Application of " + (member == null ? "Unknown" : member.getUser().getEffectiveName()) + "!") - .asSuccess() + CommandUtil.EmbedReply.from(hook) + .success("Denied Application of " + (member == null ? "Unknown" : member.getUser().getEffectiveName()) + "!") .send(); return; } Role authorRole = guild.getRoleById(bot.getConfigHandler().getLong("author_role")); if(authorRole == null){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Unable to retrieve Author Role!") + CommandUtil.EmbedReply.from(hook) + .error("Unable to retrieve Author Role!") .send(); return; } if(member == null){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Unable to apply Role. Member not found!") + CommandUtil.EmbedReply.from(hook) + .error("Unable to apply Role. Member not found!") .send(); return; } @@ -160,15 +157,14 @@ public static void handle(CodeMCBot bot, InteractionHook hook, Guild guild, long guild.addRoleToMember(member, authorRole) .reason("[Access Request] Application accepted.") .queue( - v -> CommandUtil.EmbedReply.fromHook(hook) - .withMessage("Accepted application of " + member.getUser().getEffectiveName() + "!") - .asSuccess() + v -> CommandUtil.EmbedReply.from(hook) + .success("Accepted application of " + member.getUser().getEffectiveName() + "!") .send(), new ErrorHandler() .handle( ErrorResponse.MISSING_PERMISSIONS, - e -> CommandUtil.EmbedReply.fromHook(hook) - .withIssue("I lack the `Manage Roles` permission to apply the role.") + e -> CommandUtil.EmbedReply.from(hook) + .appendWarning("I lack the `Manage Roles` permission to apply the role.") .send() ) ); @@ -204,7 +200,7 @@ public Accept(CodeMCBot bot){ this.name = "accept"; this.help = "Accept an application"; - this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "application"); + this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "commands", "application"); this.options = Arrays.asList( new OptionData(OptionType.STRING, "id", "The message id of the application.").setRequired(true), @@ -227,16 +223,14 @@ public void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild g String projectUrl = event.getOption("project-url", null, OptionMapping::getAsString); if(messageId == -1L || projectUrl == null){ - CommandUtil.EmbedReply.fromHook(hook).withError( - "Message ID or Project URL were not present!" - ).send(); + CommandUtil.EmbedReply.from(hook).error("Message ID or Project URL were not present!").send(); return; } if(!projectUrlPattern.matcher(projectUrl).matches()){ - CommandUtil.EmbedReply.fromHook(hook).withError( - "The provided Project URL did not match the pattern `https://ci.codemc.io/job//job/`!" - ).send(); + CommandUtil.EmbedReply.from(hook).error( + "The provided Project URL did not match the pattern `https://ci.codemc.io/job//job/`!") + .send(); return; } @@ -252,7 +246,7 @@ public Deny(CodeMCBot bot){ this.name = "deny"; this.help = "Deny an application"; - this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "application"); + this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "commands", "application"); this.options = Arrays.asList( new OptionData(OptionType.STRING, "id", "The message id of the application.").setRequired(true), @@ -265,19 +259,11 @@ public void withModalReply(SlashCommandEvent event){} @Override public void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild guild, Member member){ - long messageId = event.getOption("id", -1L, option -> { - try{ - return Long.parseLong(option.getAsString()); - }catch(NumberFormatException ex){ - return -1L; - } - }); + long messageId = event.getOption("id", -1L, OptionMapping::getAsLong); String reason = event.getOption("reason", null, OptionMapping::getAsString); if(messageId == -1L || reason == null){ - CommandUtil.EmbedReply.fromHook(hook).withError( - "Message ID or Reason were not present!" - ).send(); + CommandUtil.EmbedReply.from(hook).error("Message ID or Reason were not present!").send(); return; } diff --git a/src/main/java/io/codemc/bot/commands/CmdDisable.java b/src/main/java/io/codemc/bot/commands/CmdDisable.java index da3007f..12121f3 100644 --- a/src/main/java/io/codemc/bot/commands/CmdDisable.java +++ b/src/main/java/io/codemc/bot/commands/CmdDisable.java @@ -37,7 +37,7 @@ public CmdDisable(CodeMCBot bot){ this.name = "disable"; this.help = "Disables the bot."; - this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "disable"); + this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "commands", "disable"); } @Override diff --git a/src/main/java/io/codemc/bot/commands/CmdMsg.java b/src/main/java/io/codemc/bot/commands/CmdMsg.java index 75c9e0c..e99e35c 100644 --- a/src/main/java/io/codemc/bot/commands/CmdMsg.java +++ b/src/main/java/io/codemc/bot/commands/CmdMsg.java @@ -47,7 +47,7 @@ public CmdMsg(CodeMCBot bot){ this.name = "msg"; this.help = "Sends a message in a specified channel or edits one."; - this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "msg"); + this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "commands", "msg"); this.children = new SlashCommand[]{ new Post(bot), @@ -69,7 +69,7 @@ public Post(CodeMCBot bot){ this.name = "send"; this.help = "Sends a message as the Bot."; - this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "msg"); + this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "commands", "msg"); this.hasModalReply = true; this.options = Arrays.asList( @@ -91,9 +91,7 @@ public void withModalReply(SlashCommandEvent event){ boolean asEmbed = event.getOption("embed", false, OptionMapping::getAsBoolean); if(channel == null){ - CommandUtil.EmbedReply.fromCommandEvent(event) - .withError("Received invalid Channel input.") - .send(); + CommandUtil.EmbedReply.from(event).error("Received invalid Channel input.").send(); return; } @@ -118,7 +116,7 @@ public Edit(CodeMCBot bot){ this.name = "edit"; this.help = "Edit an existing message of the bot."; - this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "msg"); + this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "commands", "msg"); this.hasModalReply = true; this.options = Arrays.asList( @@ -147,9 +145,7 @@ public void withModalReply(SlashCommandEvent event){ boolean asEmbed = event.getOption("embed", false, OptionMapping::getAsBoolean); if(channel == null || messageId == -1L){ - CommandUtil.EmbedReply.fromCommandEvent(event) - .withError("Received invalid Channel or Message ID.") - .send(); + CommandUtil.EmbedReply.from(event).error("Received invalid Channel or Message ID.").send(); return; } diff --git a/src/main/java/io/codemc/bot/commands/CmdReload.java b/src/main/java/io/codemc/bot/commands/CmdReload.java index a923280..cbf29ee 100644 --- a/src/main/java/io/codemc/bot/commands/CmdReload.java +++ b/src/main/java/io/codemc/bot/commands/CmdReload.java @@ -33,7 +33,7 @@ public CmdReload(CodeMCBot bot){ this.name = "reload"; this.help = "Reloads the configuration."; - this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "reload"); + this.allowedRoles = bot.getConfigHandler().getLongList("allowed_roles", "commands", "reload"); } @Override @@ -41,13 +41,10 @@ public void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild g boolean success = bot.getConfigHandler().reloadConfig(); if(success){ - CommandUtil.EmbedReply.fromHook(hook) - .withMessage("Reload success!") - .asSuccess() - .send(); + CommandUtil.EmbedReply.from(hook).success("Reload success!").send(); }else{ - CommandUtil.EmbedReply.fromHook(hook) - .withError("There was an issue while reloading the configuration! Check console.") + CommandUtil.EmbedReply.from(hook).error( + "There was an issue while reloading the configuration! Check console.") .send(); } } diff --git a/src/main/java/io/codemc/bot/commands/CmdSubmit.java b/src/main/java/io/codemc/bot/commands/CmdSubmit.java index b005a5b..93afebc 100644 --- a/src/main/java/io/codemc/bot/commands/CmdSubmit.java +++ b/src/main/java/io/codemc/bot/commands/CmdSubmit.java @@ -45,14 +45,17 @@ public void withHookReply(InteractionHook hook, SlashCommandEvent event, Guild g @Override public void withModalReply(SlashCommandEvent event){ - TextInput userLink = TextInput.create("userlink", "User Link", TextInputStyle.SHORT) - .setPlaceholder("https://github.com/CodeMC") + TextInput user = TextInput.create("user", "GitHub Username", TextInputStyle.SHORT) + .setPlaceholder("CodeMC") .setRequired(true) .build(); - TextInput repoLink = TextInput.create("repolink", "Repository Link", TextInputStyle.SHORT) - .setPlaceholder("https://github.com/CodeMC/Bot") + TextInput repo = TextInput.create("repo", "Repository Name", TextInputStyle.SHORT) + .setPlaceholder("Bot") .setRequired(true) .build(); + TextInput repoLink = TextInput.create("repoLink", "Repository Link (Leave blank if on GitHub)", TextInputStyle.SHORT) + .setPlaceholder("https://git.example.com/CodeMC/Bot") + .build(); TextInput description = TextInput.create("description", "Description", TextInputStyle.PARAGRAPH) .setPlaceholder("Discord Bot for the CodeMC Server.") .setRequired(true) @@ -61,7 +64,8 @@ public void withModalReply(SlashCommandEvent event){ Modal modal = Modal.create("submit", "Join Request") .addComponents( - ActionRow.of(userLink), + ActionRow.of(user), + ActionRow.of(repo), ActionRow.of(repoLink), ActionRow.of(description) ) diff --git a/src/main/java/io/codemc/bot/listeners/ButtonListener.java b/src/main/java/io/codemc/bot/listeners/ButtonListener.java new file mode 100644 index 0000000..42cfe58 --- /dev/null +++ b/src/main/java/io/codemc/bot/listeners/ButtonListener.java @@ -0,0 +1,115 @@ +/* + * Copyright 2024 CodeMC.io + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package io.codemc.bot.listeners; + +import io.codemc.bot.CodeMCBot; +import io.codemc.bot.commands.CmdApplication; +import io.codemc.bot.utils.CommandUtil; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class ButtonListener extends ListenerAdapter{ + + private final CodeMCBot bot; + + public ButtonListener(CodeMCBot bot){ + this.bot = bot; + } + + @Override + public void onButtonInteraction(@NotNull ButtonInteractionEvent event){ + Guild guild = event.getGuild(); + if(!event.isFromGuild() || guild == null){ + CommandUtil.EmbedReply.from(event).error("Buttons only work on the CodeMC Server!").send(); + return; + } + + if(event.getButton().getId() == null){ + event.deferReply().queue(); + return; + } + + List acceptApplicationRoles = bot.getConfigHandler().getLongList("allowed_roles", "applications", "accept"); + List denyApplicationRoles = bot.getConfigHandler().getLongList("allowed_roles", "applications", "deny"); + + if(acceptApplicationRoles.isEmpty() || denyApplicationRoles.isEmpty()){ + CommandUtil.EmbedReply.from(event).error("No roles for accepting or denying applications set!").send(); + return; + } + + Member member = event.getMember(); + if(member == null){ + CommandUtil.EmbedReply.from(event).error("Cannot get Member from Server!").send(); + return; + } + + String[] values = event.getButton().getId().split(":"); + if(values.length < 4 || !values[0].equals("application")){ + CommandUtil.EmbedReply.from(event).error("Received non-application button event!").send(); + return; + } + + if(!values[1].equals("accept") && !values[1].equals("deny")){ + CommandUtil.EmbedReply.from(event).error( + "Received unknown Button Application type.", + "Expected `accept` or `deny` but got " + values[1] + "." + ).send(); + return; + } + + List roleIds = member.getRoles().stream() + .map(Role::getIdLong) + .toList(); + + if(values[1].equals("accept")){ + if(lacksRole(roleIds, acceptApplicationRoles)){ + CommandUtil.EmbedReply.from(event).error("You lack permissions to perform this action.").send(); + return; + } + + event.deferReply(true).queue( + // TODO: Add project URL here (Maybe move application handling from CmdApplication) + hook -> CmdApplication.handle(bot, hook, guild, event.getMessageIdLong(), "", true) + ); + }else{ + if(lacksRole(roleIds, denyApplicationRoles)){ + CommandUtil.EmbedReply.from(event).error("You lack permissions to perform this action.").send(); + return; + } + + event.deferReply(true).queue( + // TODO: Add project URL here (Maybe move application handling from CmdApplication) + hook -> CmdApplication.handle(bot, hook, guild, event.getMessageIdLong(), "", false) + ); + } + } + + private boolean lacksRole(List roleIds, List allowedRoleIds){ + if(roleIds.isEmpty()) + return true; + + return roleIds.stream().anyMatch(allowedRoleIds::contains); + } +} diff --git a/src/main/java/io/codemc/bot/listeners/ModalListener.java b/src/main/java/io/codemc/bot/listeners/ModalListener.java index ad62e73..e4b864c 100644 --- a/src/main/java/io/codemc/bot/listeners/ModalListener.java +++ b/src/main/java/io/codemc/bot/listeners/ModalListener.java @@ -29,21 +29,18 @@ import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.interactions.InteractionHook; +import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.interactions.modals.ModalMapping; import net.dv8tion.jda.api.requests.RestAction; +import net.dv8tion.jda.api.utils.MarkdownUtil; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.time.Instant; -import java.util.regex.Matcher; -import java.util.regex.Pattern; public class ModalListener extends ListenerAdapter{ - private final Pattern userLinkPattern = Pattern.compile("^https://github\\.com/(?[a-zA-Z0-9-]+)/?$"); - private final Pattern repoLinkPattern = Pattern.compile("^https://github\\.com/(?[a-zA-Z0-9-]+)/(?[a-zA-Z0-9-_.]+)/?$"); - private final Logger logger = LoggerFactory.getLogger(ModalListener.class); private final CodeMCBot bot; @@ -59,107 +56,96 @@ public void onModalInteraction(@NotNull ModalInteractionEvent event){ Guild guild = event.getGuild(); if(guild == null || guild.getIdLong() != bot.getConfigHandler().getLong("server")){ - CommandUtil.EmbedReply.fromModalEvent(event) - .withError("Unable to retrieve CodeMC Server!") - .send(); + CommandUtil.EmbedReply.from(event).error("Unable to retrieve CodeMC Server!").send(); return; } String[] args = event.getModalId().split(":"); switch(args[0]){ - case "submit" -> event.deferReply(true).queue(hook -> { - String userLink = value(event, "userlink"); - String repoLink = value(event, "repolink"); + case "submit" -> event.deferReply(true).queue(hook -> { + String user = value(event, "user"); + String repo = value(event, "repo"); String description = value(event, "description"); - if(userLink == null || userLink.isEmpty() || repoLink == null || repoLink.isEmpty() || description == null || description.isEmpty()){ - CommandUtil.EmbedReply.fromHook(hook).withError( - "User Link, Repository Link or Description was not present!" - ).send(); - return; - } - - Matcher userMatcher = userLinkPattern.matcher(userLink); - Matcher repoMatcher = repoLinkPattern.matcher(repoLink); - - if(!userMatcher.matches() || !repoMatcher.matches()){ - CommandUtil.EmbedReply.fromHook(hook).withError( - "The provided User or Repository link does not match a valid GitHub URL.", - "Make sure the patterns are `https://github.com/` and `https://github.com//` respectively." - ).send(); + if(user == null || user.isEmpty() || repo == null || repo.isEmpty() || description == null || description.isEmpty()){ + CommandUtil.EmbedReply.from(hook).error( + "The option User, Repo and/or Description was not set properly!") + .send(); return; } - String username = String.format("[`%s`](%s)", userMatcher.group("user"), userLink); - String repo = String.format("[`%s/%s`](%s)", repoMatcher.group("user"), repoMatcher.group("repo"), repoLink); - String submitter = String.format("`%s` (%s)", event.getUser().getEffectiveName(), event.getUser().getAsMention()); - TextChannel requestChannel = guild.getTextChannelById(bot.getConfigHandler().getLong("channels", "request_access")); if(requestChannel == null){ - CommandUtil.EmbedReply.fromHook(hook).withError( - "Unable to retrieve `request-access` channel!" - ).send(); + CommandUtil.EmbedReply.from(hook).error("Unable to retrieve `request-access` channel!").send(); return; } + String repoLinkValue = value(event, "repoLink"); + if(repoLinkValue == null || repoLinkValue.isEmpty()) + repoLinkValue = "https://github.com/" + user + "/" + repo; + + String userLink = MarkdownUtil.maskedLink(user, "https://github.com/" + user); + String repoLink = MarkdownUtil.maskedLink(repo, repoLinkValue); + String submitter = String.format("`%s` (%s)", event.getUser().getEffectiveName(), event.getUser().getAsMention()); + MessageEmbed embed = CommandUtil.getEmbed() - .addField("User/Organisation:", username, true) - .addField("Repository:", repo, true) + .addField("User/Organisation:", userLink, true) + .addField("Repository:", repoLink, true) .addField("Submitted by:", submitter, true) .addField("Description", description, false) .setFooter(event.getUser().getId()) .setTimestamp(Instant.now()) .build(); - requestChannel.sendMessageEmbeds(embed).queue( - message -> { - CommandUtil.EmbedReply.fromHook(hook).withMessage( - "[Request sent!](" + message.getJumpUrl() + ")" - ).asSuccess().send(); - - RestAction.allOf( - message.createThreadChannel("Access Request - " + event.getUser().getName()), - message.addReaction(Emoji.fromCustom("like", 935126958193405962L, false)), - message.addReaction(Emoji.fromCustom("dislike", 935126958235344927L, false)) - ).queue(); - - logger.info("[Access Request] User {} requested access to the CI.", event.getUser().getEffectiveName()); - }, - e -> CommandUtil.EmbedReply.fromHook(hook).withError( - "Error while submitting request!", - "Reported Error: " + e.getMessage() - ).send() + requestChannel.sendMessageEmbeds(embed) + .setActionRow( + Button.success("application:accept:" + user + ":" + repo, "Accept"), + Button.danger("application:deny:" + user + ":" + repo, "Deny") + ).queue( + message -> { + CommandUtil.EmbedReply.from(hook).success( + "[Request sent!](" + message.getJumpUrl() + ")") + .send(); + + RestAction.allOf( + message.createThreadChannel("Access Request - " + event.getUser().getName()), + message.addReaction(Emoji.fromCustom("like", 935126958193405962L, false)), + message.addReaction(Emoji.fromCustom("dislike", 935126958235344927L, false)) + ).queue(); + + logger.info("[Access Request] User {} requested access to the CI.", event.getUser().getEffectiveName()); + }, + e -> CommandUtil.EmbedReply.from(hook).error( + "Error while submitting request!", + "Reported Error: " + e.getMessage() + ).send() ); }); case "message" -> event.deferReply(true).queue(hook -> { if(args.length < 4){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Invalid Modal data. Expected `>=4` but received `" + args.length + "`!") + CommandUtil.EmbedReply.from(hook) + .error("Invalid Modal data. Expected `4+` arguments but received `" + args.length + "`!") .send(); return; } TextChannel channel = guild.getTextChannelById(args[2]); if(channel == null){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Received invalid Text Channel.") - .send(); + CommandUtil.EmbedReply.from(hook).error("Received invalid Text Channel.").send(); return; } String text = value(event, "message"); if(text == null || text.isEmpty()){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Received invalid Message to sent/edit.") - .send(); + CommandUtil.EmbedReply.from(hook).error("Received invalid Message to sent/edit.").send(); return; } if(!channel.canTalk()){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("I lack the permission to see and/or write in " + channel.getAsMention() + ".") + CommandUtil.EmbedReply.from(hook) + .error("I lack the permission to see and/or write in " + channel.getAsMention() + ".") .send(); return; } @@ -170,22 +156,22 @@ public void onModalInteraction(@NotNull ModalInteractionEvent event){ if(asEmbed){ channel.sendMessageEmbeds(CommandUtil.getEmbed().setDescription(text).build()).queue( message -> sendConfirmation(hook, message, false), - e -> CommandUtil.EmbedReply.fromHook(hook) - .withError("Unable to sent message. Reason: " + e.getMessage()) + e -> CommandUtil.EmbedReply.from(hook) + .error("Unable to sent message. Reason: " + e.getMessage()) .send() ); }else{ channel.sendMessage(text).queue( message -> sendConfirmation(hook, message, false), - e -> CommandUtil.EmbedReply.fromHook(hook) - .withError("Unable to sent message. Reason: " + e.getMessage()) + e -> CommandUtil.EmbedReply.from(hook) + .error("Unable to sent message. Reason: " + e.getMessage()) .send() ); } }else if(args[1].equals("edit")){ if(args.length == 4){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Received invalid Modal data. Expected `>4` but got `=4`") + CommandUtil.EmbedReply.from(hook) + .error("Received invalid Modal data. Expected `>4` but got `=4`") .send(); return; } @@ -198,8 +184,8 @@ public void onModalInteraction(@NotNull ModalInteractionEvent event){ } if(messageId == -1L){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Received invalid message ID `" + args[4] + "`.") + CommandUtil.EmbedReply.from(hook) + .error("Received invalid message ID `" + args[4] + "`.") .send(); return; } @@ -209,38 +195,38 @@ public void onModalInteraction(@NotNull ModalInteractionEvent event){ if(asEmbed){ message.editMessageEmbeds(CommandUtil.getEmbed().setDescription(text).build()).setReplace(true).queue( m -> sendConfirmation(hook, m, true), - e -> CommandUtil.EmbedReply.fromHook(hook) - .withError("Unable to edit message. Reason: " + e.getMessage()) + e -> CommandUtil.EmbedReply.from(hook) + .error("Unable to edit message. Reason: " + e.getMessage()) .send() ); }else{ message.editMessage(text).setReplace(true).queue( m -> sendConfirmation(hook, m, true), - e -> CommandUtil.EmbedReply.fromHook(hook) - .withError("Unable to edit message. Reason: " + e.getMessage()) + e -> CommandUtil.EmbedReply.from(hook) + .error("Unable to edit message. Reason: " + e.getMessage()) .send() ); } } ); }else{ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Received Unknown Message type: `" + args[1] + "`.") + CommandUtil.EmbedReply.from(hook) + .error("Received Unknown Message type: `" + args[1] + "`.") .send(); } }); case "application" -> event.deferReply(true).queue(hook -> { if(args.length < 3){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Invalid Modal data. Expected `=3` but received `" + args.length + "`!") + CommandUtil.EmbedReply.from(hook) + .error("Invalid Modal data. Expected `3` args but received `" + args.length + "`!") .send(); return; } if(!args[1].equals("accepted") && !args[1].equals("denied")){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Received unknown Application type. Expected `accepted` or `denied` but received `" + args[1] + "`.") + CommandUtil.EmbedReply.from(hook) + .error("Received unknown Application type. Expected `accepted` or `denied` but received `" + args[1] + "`.") .send(); return; } @@ -253,8 +239,8 @@ public void onModalInteraction(@NotNull ModalInteractionEvent event){ } if(messageId == -1L){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Received Invalid Message ID. Expected number but got `" + args[2] + "` instead!") + CommandUtil.EmbedReply.from(hook) + .error("Received Invalid Message ID. Expected number but got `" + args[2] + "` instead!") .send(); return; } @@ -263,8 +249,8 @@ public void onModalInteraction(@NotNull ModalInteractionEvent event){ String text = value(event, "text"); if(text == null || text.isEmpty()){ - CommandUtil.EmbedReply.fromHook(hook) - .withError("Received invalid " + (accepted ? "Project URL" : "Reason") + ". Text was empty/null.") + CommandUtil.EmbedReply.from(hook) + .error("Received invalid " + (accepted ? "Project URL" : "Reason") + ". Text was empty/null.") .send(); return; } @@ -272,16 +258,15 @@ public void onModalInteraction(@NotNull ModalInteractionEvent event){ CmdApplication.handle(bot, hook, guild, messageId, text, accepted); }); - default -> CommandUtil.EmbedReply.fromModalEvent(event) - .withError("Received Modal with unknown ID `" + event.getModalId() + "`.") + default -> CommandUtil.EmbedReply.from(event) + .error("Received Modal with unknown ID `" + event.getModalId() + "`.") .send(); } } private void sendConfirmation(InteractionHook hook, Message message, boolean edit){ - CommandUtil.EmbedReply.fromHook(hook) - .withMessage(String.format("[%s](%s)", edit ? "Message edited!" : "Message sent!", message.getJumpUrl())) - .asSuccess() + CommandUtil.EmbedReply.from(hook) + .success(String.format("[%s](%s)", edit ? "Message edited!" : "Message sent!", message.getJumpUrl())) .send(); logger.info("[Message] User {} {} a Message as the Bot.", hook.getInteraction().getUser().getEffectiveName(), edit ? "edited" : "sent"); diff --git a/src/main/java/io/codemc/bot/menu/ApplicationMenu.java b/src/main/java/io/codemc/bot/menu/ApplicationMenu.java deleted file mode 100644 index 0241cca..0000000 --- a/src/main/java/io/codemc/bot/menu/ApplicationMenu.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2024 CodeMC.io - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.codemc.bot.menu; - -import com.jagrosh.jdautilities.command.MessageContextMenu; -import com.jagrosh.jdautilities.command.MessageContextMenuEvent; -import io.codemc.bot.CodeMCBot; -import io.codemc.bot.utils.CommandUtil; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; -import net.dv8tion.jda.api.interactions.components.ActionRow; -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.Modal; - -public class ApplicationMenu{ - private static void handleEvent(CodeMCBot bot, MessageContextMenuEvent event, boolean accepted){ - Guild guild = event.getGuild(); - if(guild == null || guild.getIdLong() != bot.getConfigHandler().getLong("server")){ - event.reply("This Context Menu Action may only work in the CodeMC Server!") - .setEphemeral(true) - .queue(); - return; - } - - MessageChannel channel = event.getChannel(); - if(channel == null || channel.getIdLong() != bot.getConfigHandler().getLong("channels", "request_access")){ - event.reply("This Context Menu Action may only work in the request-access channel of the CodeMC Server!") - .setEphemeral(true) - .queue(); - return; - } - - Member member = event.getMember(); - if(member == null){ - event.reply("Unable to retrieve Member!").setEphemeral(true).queue(); - return; - } - - if(!CommandUtil.hasRole(member, bot.getConfigHandler().getLongList("allowed_roles", "application"))){ - event.replyEmbeds(CommandUtil.getEmbed().setColor(0xFF0000) - .setDescription("You do not have permissions to use this Context Menu Action!") - .build() - ).queue(); - return; - } - - TextInput input; - if(accepted){ - input = TextInput.create("text", "Project URL", TextInputStyle.SHORT) - .setRequired(true) - .setPlaceholder("https://ci.codemc.io/job/CodeMC/job/CodeMC-Discord-Bot/") - .build(); - }else{ - input = TextInput.create("text", "Denial Reason", TextInputStyle.PARAGRAPH) - .setRequired(true) - .setPlaceholder("The project was denied because ...") - .build(); - } - - Modal modal = Modal.create( - "application:" + (accepted ? "accepted" : "denied") + ":" + event.getTarget().getId(), - accepted ? "Accept Application" : "Deny Application" - ) - .addComponents(ActionRow.of(input)) - .build(); - - event.replyModal(modal).queue(); - } - - public static final class Accept extends MessageContextMenu{ - - private final CodeMCBot bot; - - public Accept(CodeMCBot bot){ - this.bot = bot; - - this.name = "Accept this application"; - } - - @Override - protected void execute(MessageContextMenuEvent event){ - handleEvent(bot, event, true); - } - } - - public static final class Deny extends MessageContextMenu{ - - private final CodeMCBot bot; - - public Deny(CodeMCBot bot){ - this.bot = bot; - - this.name = "Deny this application"; - } - - @Override - protected void execute(MessageContextMenuEvent event){ - handleEvent(bot, event, false); - } - } -} diff --git a/src/main/java/io/codemc/bot/utils/CommandUtil.java b/src/main/java/io/codemc/bot/utils/CommandUtil.java index f0e36d7..2107837 100644 --- a/src/main/java/io/codemc/bot/utils/CommandUtil.java +++ b/src/main/java/io/codemc/bot/utils/CommandUtil.java @@ -23,6 +23,7 @@ import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; +import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.interactions.InteractionHook; import org.slf4j.LoggerFactory; @@ -49,80 +50,54 @@ public static boolean hasRole(Member member, List roleIds){ .orElse(null) != null; } - public static class EmbedReply { + public static class EmbedReply { - private final SlashCommandEvent commandEvent; - private final ModalInteractionEvent modalEvent; - private final InteractionHook hook; + private final T type; private final EmbedBuilder builder = new EmbedBuilder(); - private EmbedReply(SlashCommandEvent commandEvent){ - this.commandEvent = commandEvent; - this.modalEvent = null; - this.hook = null; + private EmbedReply(T type){ + this.type = type; } - private EmbedReply(InteractionHook hook){ - this.commandEvent = null; - this.modalEvent = null; - this.hook = hook; + public static EmbedReply from(T type){ + return new EmbedReply<>(type); } - private EmbedReply(ModalInteractionEvent modalEvent){ - this.commandEvent = null; - this.modalEvent = modalEvent; - this.hook = null; - } - - public static EmbedReply fromCommandEvent(SlashCommandEvent event){ - return new EmbedReply(event); - } - - public static EmbedReply fromModalEvent(ModalInteractionEvent event){ - return new EmbedReply(event); - } - - public static EmbedReply fromHook(InteractionHook hook){ - return new EmbedReply(hook); - } - - public EmbedReply withMessage(String... lines){ - builder.setDescription(String.join("\n", lines)); - return this; - } - - public EmbedReply asSuccess(){ - builder.setColor(0x00FF00); + public EmbedReply success(String... lines){ + builder.setDescription(String.join("\n", lines)) + .setColor(0x00FF00); return this; } - public EmbedReply withError(String... lines){ - builder.setColor(0xFF0000) - .setDescription( - "There was an error while trying to handle the command!\n" + - "If this error persists, report it to Andre_601#0601" - ) - .addField("Error:", String.join("\n", lines), false); + public EmbedReply appendWarning(String... lines){ + builder.addField("Warning:", String.join("\n", lines), false) + .setColor(0xFFC800); return this; } - public EmbedReply withIssue(String... lines){ - builder.setColor(0xFFC800) - .addField("Warning:", String.join("\n", lines), false); + public EmbedReply error(String... lines){ + builder.setDescription( + "There was an error while trying to handle an action!\n" + + "If this error persists, report it to the Bot owner!") + .addField("Error:", String.join("\n", lines), false) + .setColor(0xFF0000); return this; } public void send(){ - if(commandEvent != null){ + if(type instanceof SlashCommandEvent commandEvent){ commandEvent.replyEmbeds(builder.build()).setEphemeral(true).queue(); }else - if(modalEvent != null){ + if(type instanceof ModalInteractionEvent modalEvent){ modalEvent.replyEmbeds(builder.build()).setEphemeral(true).queue(); }else - if(hook != null){ - hook.editOriginalEmbeds(builder.build()).queue(); + if(type instanceof ButtonInteractionEvent buttonEvent){ + buttonEvent.replyEmbeds(builder.build()).queue(); + }else + if(type instanceof InteractionHook hook){ + hook.editOriginal(EmbedBuilder.ZERO_WIDTH_SPACE).setEmbeds(builder.build()).queue(); }else{ - LOG.error("Received EmbedReply class with neither SlashCommandEvent, ModalInteractionEvent nor InteractionHook set!"); + LOG.error("Received unknown Type {} for EmbedReply!", type); } } } diff --git a/src/main/resources/config.json b/src/main/resources/config.json index f14ee16..2208375 100644 --- a/src/main/resources/config.json +++ b/src/main/resources/config.json @@ -8,10 +8,16 @@ }, "author_role": 0, "allowed_roles": { - "application": [], - "disable": [], - "msg": [], - "reload": [] + "applications": { + "accept": [], + "deny": [] + }, + "commands": { + "application": [], + "disable": [], + "msg": [], + "reload": [] + } }, "users": { "owner": 0, diff --git a/src/main/resources/config.json.sample b/src/main/resources/config.json.sample index b834ba3..70b2ba6 100644 --- a/src/main/resources/config.json.sample +++ b/src/main/resources/config.json.sample @@ -8,20 +8,34 @@ }, "author_role": 405918641859723294, "allowed_roles": { - "application": [ - 405917902865170453, - 659568973079379971, - 1233971297185431582 - ], - "disable": [ - 405917902865170453 - ], - "msg": [ - 405917902865170453 - ], - "reload": [ - 405917902865170453 - ] + "applications": { + "accept": [ + 405917902865170453, + 659568973079379971, + 1233971297185431582 + ], + "deny": [ + 405917902865170453, + 659568973079379971, + 1233971297185431582 + ] + } + "commands": { + "application": [ + 405917902865170453, + 659568973079379971, + 1233971297185431582 + ], + "disable": [ + 405917902865170453 + ], + "msg": [ + 405917902865170453 + ], + "reload": [ + 405917902865170453 + ] + } }, "users": { "owner": 204232208049766400,