From a2c1944a320c41ba33a2529d7c1e05065e0612ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beatrice=20Dellac=C3=A0?= Date: Sun, 20 Nov 2022 06:04:00 +0100 Subject: [PATCH] Move to class-based command handling Having everything in a single class is bad practice, so different classes for each command were made. --- .../listeners/SlashCommandListener.java | 173 +----------------- .../slashcommands/ClearChatCommand.java | 150 +++++++++++++++ .../slashcommands/CoinFlipCommand.java | 24 +++ .../hidekobot/slashcommands/DieCommand.java | 24 +++ .../hidekobot/slashcommands/PingCommand.java | 12 ++ 5 files changed, 219 insertions(+), 164 deletions(-) create mode 100644 src/main/java/wtf/beatrice/hidekobot/slashcommands/ClearChatCommand.java create mode 100644 src/main/java/wtf/beatrice/hidekobot/slashcommands/CoinFlipCommand.java create mode 100644 src/main/java/wtf/beatrice/hidekobot/slashcommands/DieCommand.java create mode 100644 src/main/java/wtf/beatrice/hidekobot/slashcommands/PingCommand.java diff --git a/src/main/java/wtf/beatrice/hidekobot/listeners/SlashCommandListener.java b/src/main/java/wtf/beatrice/hidekobot/listeners/SlashCommandListener.java index dfc6118..7d690fb 100644 --- a/src/main/java/wtf/beatrice/hidekobot/listeners/SlashCommandListener.java +++ b/src/main/java/wtf/beatrice/hidekobot/listeners/SlashCommandListener.java @@ -12,6 +12,10 @@ import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; import org.jetbrains.annotations.NotNull; import wtf.beatrice.hidekobot.Configuration; import wtf.beatrice.hidekobot.HidekoBot; +import wtf.beatrice.hidekobot.slashcommands.ClearChatCommand; +import wtf.beatrice.hidekobot.slashcommands.CoinFlipCommand; +import wtf.beatrice.hidekobot.slashcommands.DieCommand; +import wtf.beatrice.hidekobot.slashcommands.PingCommand; import wtf.beatrice.hidekobot.utils.RandomUtil; import java.util.ArrayList; @@ -25,170 +29,11 @@ public class SlashCommandListener extends ListenerAdapter @Override public void onSlashCommandInteraction(@NotNull SlashCommandInteractionEvent event) { - if (event.getName().equals("ping")) - { - event.reply("Pong!").queue(); + switch (event.getName().toLowerCase()) { + case "ping" -> new PingCommand(event); + case "die" -> new DieCommand(event); + case "coinflip" -> new CoinFlipCommand(event); + case "clear" -> new ClearChatCommand(event); } - - else if (event.getName().equals("die")) - { - if(Configuration.getBotOwnerId() != event.getMember().getIdLong()) - { - event.reply("Sorry, only the bot owner can run this command!").setEphemeral(true).queue(); - } else { - event.reply("Going to sleep! Cya :sparkles:").queue(); - Executors.newSingleThreadScheduledExecutor().schedule(HidekoBot::shutdown, 3, TimeUnit.SECONDS); - } - } - - else if (event.getName().equals("coinflip")) - { - int rand = RandomUtil.getRandomNumber(0, 1); - String msg; - - if(rand == 1) - { - msg = ":coin: It's **Heads**!"; - } else { - msg = "It's **Tails**! :coin:"; - } - - event.reply(msg).queue(); - } - - - else if (event.getName().equals("clear")) - { - MessageChannel channel = event.getChannel(); - - if(!(channel instanceof TextChannel)) - { - event.reply("Sorry! I can't delete messages here.").queue(); - return; - } - - /* get the amount from the command args. - NULL should not be possible because we specified them as mandatory, - but apparently the mobile app doesn't care and still sends the command if you omit the args. */ - OptionMapping amountMapping = event.getOption("amount"); - int toDeleteAmount = amountMapping == null ? 1 : amountMapping.getAsInt(); - - if(toDeleteAmount <= 0) - { - event.reply("Sorry, I can't delete that amount of messages!").queue(); - } - else { - // answer by saying that the operation has begun. - InteractionHook replyInteraction = event.reply("\uD83D\uDEA7 Clearing...").complete(); - - // int to keep track of how many messages we deleted. - int deleted = 0; - - int limit = 95; //discord limits this method to range 2-100. we set it to 95 to be safe.. - - // increase the count by 1, because we technically aren't clearing the first ID ever - // which is actually the slash command's ID and not a message. - toDeleteAmount++; - - // count how many times we have to iterate this to delete the full messages. - int iterations = toDeleteAmount / limit; - - //if there are some messages left, but less than , we need one more iterations. - int remainder = toDeleteAmount % limit; - if(remainder != 0) iterations++; - - // set the starting point. - long messageId = event.getInteraction().getIdLong(); - - // boolean to see if we're trying to delete more messages than possible. - boolean outOfBounds = false; - - // do iterate. - for(int iteration = 0; iteration < iterations; iteration++) - { - if(outOfBounds) break; - - // set how many messages to delete for this iteration (usually unless there's a remainder) - int iterationSize = limit; - - // if we are at the last iteration... - if(iteration+1 == iterations) - { - // check if we have or fewer messages to delete - if(remainder != 0) iterationSize = remainder; - } - - if(iterationSize == 1) - { - // grab the message - Message toDelete = ((TextChannel)channel).retrieveMessageById(messageId).complete(); - //only delete one message - if(toDelete != null) toDelete.delete().queue(); - else outOfBounds = true; - // increase deleted counter by 1 - deleted++; - } else { - // get the last messages. - MessageHistory.MessageRetrieveAction action = channel.getHistoryBefore(messageId, iterationSize - 1); - // note: first one is the most recent, last one is the oldest message. - List messages = new ArrayList<>(); - // (we are skipping first iteration since it would return an error, given that the id is the slash command and not a message) - if(iteration!=0) messages.add(((TextChannel)channel).retrieveMessageById(messageId).complete()); - messages.addAll(action.complete().getRetrievedHistory()); - - // check if we only have one or zero messages left (trying to delete more than possible) - if(messages.size() <= 1) - { - outOfBounds = true; - } else { - // before deleting, we need to grab the message's id for next iteration. - action = channel.getHistoryBefore(messages.get(messages.size() - 1).getIdLong(), 1); - - List previousMessage = action.complete().getRetrievedHistory(); - - // if that message exists (we are not out of bounds)... store it - if(!previousMessage.isEmpty()) messageId = previousMessage.get(0).getIdLong(); - else outOfBounds = true; - } - - // queue messages for deletion - if(messages.size() == 1) - { - messages.get(0).delete().queue(); - } - else if(!messages.isEmpty()) - { - try { - ((TextChannel) channel).deleteMessages(messages).complete(); - /* alternatively, we could use purgeMessages, which is smarter... - however, it also tries to delete messages older than 2 weeks - which are restricted by discord, and thus has to use - a less efficient way that triggers rate-limiting very quickly. */ - } catch (Exception e) - { - replyInteraction.editOriginal("\uD83D\uDE22 Sorry, it seems like there was an issue! " + e.getMessage()).queue(); - return; // warning: this quits everything. - } - } - - // increase deleted counter by - deleted += messages.size(); - } - } - - // log having deleted the messages. - if(deleted < 1) - { - replyInteraction.editOriginal("\uD83D\uDE22 Couldn't clear any message!").queue(); - } else if(deleted == 1) - { - replyInteraction.editOriginal("✂ Cleared 1 message!").queue(); - } else { - replyInteraction.editOriginal("✂ Cleared " + deleted + " messages!").queue(); - } - } - - } - } } diff --git a/src/main/java/wtf/beatrice/hidekobot/slashcommands/ClearChatCommand.java b/src/main/java/wtf/beatrice/hidekobot/slashcommands/ClearChatCommand.java new file mode 100644 index 0000000..ffd3b96 --- /dev/null +++ b/src/main/java/wtf/beatrice/hidekobot/slashcommands/ClearChatCommand.java @@ -0,0 +1,150 @@ +package wtf.beatrice.hidekobot.slashcommands; + +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageChannel; +import net.dv8tion.jda.api.entities.MessageHistory; +import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.InteractionHook; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class ClearChatCommand +{ + + public ClearChatCommand(@NotNull SlashCommandInteractionEvent event) + { + MessageChannel channel = event.getChannel(); + + if(!(channel instanceof TextChannel)) + { + event.reply("Sorry! I can't delete messages here.").queue(); + return; + } + + /* get the amount from the command args. + NULL should not be possible because we specified them as mandatory, + but apparently the mobile app doesn't care and still sends the command if you omit the args. */ + OptionMapping amountMapping = event.getOption("amount"); + int toDeleteAmount = amountMapping == null ? 1 : amountMapping.getAsInt(); + + if(toDeleteAmount <= 0) + { + event.reply("Sorry, I can't delete that amount of messages!").queue(); + } + else { + // answer by saying that the operation has begun. + InteractionHook replyInteraction = event.reply("\uD83D\uDEA7 Clearing...").complete(); + + // int to keep track of how many messages we deleted. + int deleted = 0; + + int limit = 95; //discord limits this method to range 2-100. we set it to 95 to be safe.. + + // increase the count by 1, because we technically aren't clearing the first ID ever + // which is actually the slash command's ID and not a message. + toDeleteAmount++; + + // count how many times we have to iterate this to delete the full messages. + int iterations = toDeleteAmount / limit; + + //if there are some messages left, but less than , we need one more iterations. + int remainder = toDeleteAmount % limit; + if(remainder != 0) iterations++; + + // set the starting point. + long messageId = event.getInteraction().getIdLong(); + + // boolean to see if we're trying to delete more messages than possible. + boolean outOfBounds = false; + + // do iterate. + for(int iteration = 0; iteration < iterations; iteration++) + { + if(outOfBounds) break; + + // set how many messages to delete for this iteration (usually unless there's a remainder) + int iterationSize = limit; + + // if we are at the last iteration... + if(iteration+1 == iterations) + { + // check if we have or fewer messages to delete + if(remainder != 0) iterationSize = remainder; + } + + if(iterationSize == 1) + { + // grab the message + Message toDelete = ((TextChannel)channel).retrieveMessageById(messageId).complete(); + //only delete one message + if(toDelete != null) toDelete.delete().queue(); + else outOfBounds = true; + // increase deleted counter by 1 + deleted++; + } else { + // get the last messages. + MessageHistory.MessageRetrieveAction action = channel.getHistoryBefore(messageId, iterationSize - 1); + // note: first one is the most recent, last one is the oldest message. + List messages = new ArrayList<>(); + // (we are skipping first iteration since it would return an error, given that the id is the slash command and not a message) + if(iteration!=0) messages.add(((TextChannel)channel).retrieveMessageById(messageId).complete()); + messages.addAll(action.complete().getRetrievedHistory()); + + // check if we only have one or zero messages left (trying to delete more than possible) + if(messages.size() <= 1) + { + outOfBounds = true; + } else { + // before deleting, we need to grab the message's id for next iteration. + action = channel.getHistoryBefore(messages.get(messages.size() - 1).getIdLong(), 1); + + List previousMessage = action.complete().getRetrievedHistory(); + + // if that message exists (we are not out of bounds)... store it + if(!previousMessage.isEmpty()) messageId = previousMessage.get(0).getIdLong(); + else outOfBounds = true; + } + + // queue messages for deletion + if(messages.size() == 1) + { + messages.get(0).delete().queue(); + } + else if(!messages.isEmpty()) + { + try { + ((TextChannel) channel).deleteMessages(messages).complete(); + /* alternatively, we could use purgeMessages, which is smarter... + however, it also tries to delete messages older than 2 weeks + which are restricted by discord, and thus has to use + a less efficient way that triggers rate-limiting very quickly. */ + } catch (Exception e) + { + replyInteraction.editOriginal("\uD83D\uDE22 Sorry, it seems like there was an issue! " + e.getMessage()).queue(); + return; // warning: this quits everything. + } + } + + // increase deleted counter by + deleted += messages.size(); + } + } + + // log having deleted the messages. + if(deleted < 1) + { + replyInteraction.editOriginal("\uD83D\uDE22 Couldn't clear any message!").queue(); + } else if(deleted == 1) + { + replyInteraction.editOriginal("✂ Cleared 1 message!").queue(); + } else { + replyInteraction.editOriginal("✂ Cleared " + deleted + " messages!").queue(); + } + } + + } +} diff --git a/src/main/java/wtf/beatrice/hidekobot/slashcommands/CoinFlipCommand.java b/src/main/java/wtf/beatrice/hidekobot/slashcommands/CoinFlipCommand.java new file mode 100644 index 0000000..ef048e7 --- /dev/null +++ b/src/main/java/wtf/beatrice/hidekobot/slashcommands/CoinFlipCommand.java @@ -0,0 +1,24 @@ +package wtf.beatrice.hidekobot.slashcommands; + +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import org.jetbrains.annotations.NotNull; +import wtf.beatrice.hidekobot.utils.RandomUtil; + +public class CoinFlipCommand +{ + + public CoinFlipCommand(@NotNull SlashCommandInteractionEvent event) + { + int rand = RandomUtil.getRandomNumber(0, 1); + String msg; + + if(rand == 1) + { + msg = ":coin: It's **Heads**!"; + } else { + msg = "It's **Tails**! :coin:"; + } + + event.reply(msg).queue(); + } +} diff --git a/src/main/java/wtf/beatrice/hidekobot/slashcommands/DieCommand.java b/src/main/java/wtf/beatrice/hidekobot/slashcommands/DieCommand.java new file mode 100644 index 0000000..5ef8ba6 --- /dev/null +++ b/src/main/java/wtf/beatrice/hidekobot/slashcommands/DieCommand.java @@ -0,0 +1,24 @@ +package wtf.beatrice.hidekobot.slashcommands; + +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import org.jetbrains.annotations.NotNull; +import wtf.beatrice.hidekobot.Configuration; +import wtf.beatrice.hidekobot.HidekoBot; + +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +public class DieCommand +{ + + public DieCommand(@NotNull SlashCommandInteractionEvent event) + { + if(Configuration.getBotOwnerId() != event.getMember().getIdLong()) + { + event.reply("Sorry, only the bot owner can run this command!").setEphemeral(true).queue(); + } else { + event.reply("Going to sleep! Cya :sparkles:").queue(); + Executors.newSingleThreadScheduledExecutor().schedule(HidekoBot::shutdown, 3, TimeUnit.SECONDS); + } + } +} diff --git a/src/main/java/wtf/beatrice/hidekobot/slashcommands/PingCommand.java b/src/main/java/wtf/beatrice/hidekobot/slashcommands/PingCommand.java new file mode 100644 index 0000000..414f4e2 --- /dev/null +++ b/src/main/java/wtf/beatrice/hidekobot/slashcommands/PingCommand.java @@ -0,0 +1,12 @@ +package wtf.beatrice.hidekobot.slashcommands; + +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import org.jetbrains.annotations.NotNull; + +public class PingCommand +{ + public PingCommand(@NotNull SlashCommandInteractionEvent event) + { + event.reply("Pong!").queue(); + } +}