diff --git a/src/main/java/wtf/beatrice/hidekobot/HidekoBot.java b/src/main/java/wtf/beatrice/hidekobot/HidekoBot.java index cb8797b..c448b06 100644 --- a/src/main/java/wtf/beatrice/hidekobot/HidekoBot.java +++ b/src/main/java/wtf/beatrice/hidekobot/HidekoBot.java @@ -124,6 +124,7 @@ public class HidekoBot slashCommandListener.registerCommand(new MagicBallCommand()); slashCommandListener.registerCommand(new PingCommand()); slashCommandListener.registerCommand(new SayCommand()); + slashCommandListener.registerCommand(new TriviaCommand()); slashCommandListener.registerCommand(new UrbanDictionaryCommand()); Cache.setSlashCommandListener(slashCommandListener); Cache.setSlashCommandCompletionListener(slashCommandCompletionListener); diff --git a/src/main/java/wtf/beatrice/hidekobot/util/TriviaUtil.java b/src/main/java/wtf/beatrice/hidekobot/commands/base/Trivia.java similarity index 80% rename from src/main/java/wtf/beatrice/hidekobot/util/TriviaUtil.java rename to src/main/java/wtf/beatrice/hidekobot/commands/base/Trivia.java index 49fef41..e33ad32 100644 --- a/src/main/java/wtf/beatrice/hidekobot/util/TriviaUtil.java +++ b/src/main/java/wtf/beatrice/hidekobot/commands/base/Trivia.java @@ -1,19 +1,24 @@ -package wtf.beatrice.hidekobot.util; +package wtf.beatrice.hidekobot.commands.base; +import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent; import net.dv8tion.jda.api.interactions.components.selections.SelectOption; +import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu; import org.apache.commons.text.StringEscapeUtils; import org.json.JSONArray; import org.json.JSONObject; import wtf.beatrice.hidekobot.Cache; +import wtf.beatrice.hidekobot.objects.MessageResponse; +import wtf.beatrice.hidekobot.objects.comparators.TriviaCategoryComparator; import wtf.beatrice.hidekobot.objects.fun.TriviaCategory; import wtf.beatrice.hidekobot.objects.fun.TriviaQuestion; import wtf.beatrice.hidekobot.objects.fun.TriviaScore; import wtf.beatrice.hidekobot.runnables.TriviaTask; +import wtf.beatrice.hidekobot.util.CommandUtil; import java.io.BufferedReader; import java.io.IOException; @@ -27,7 +32,7 @@ import java.util.List; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -public class TriviaUtil +public class Trivia { private final static String triviaLink = "https://opentdb.com/api.php?amount=10&type=multiple&category="; private final static String categoriesLink = "https://opentdb.com/api_category.php"; @@ -43,6 +48,47 @@ public class TriviaUtil public static String getTriviaLink(int categoryId) {return triviaLink + categoryId; } public static String getCategoriesLink() {return categoriesLink; } + public static String getNoDMsError() { + return "\uD83D\uDE22 Sorry! Trivia doesn't work in DMs."; + } + + public static String getTriviaAlreadyRunningError() { + // todo nicer looking + return "Trivia is already running here!"; + } + + public static MessageResponse generateMainScreen() + { + // todo null checks + JSONObject categoriesJson = Trivia.fetchJson(Trivia.getCategoriesLink()); + List categories = Trivia.parseCategories(categoriesJson); + categories.sort(new TriviaCategoryComparator()); + + EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setColor(Cache.getBotColor()); + embedBuilder.setTitle("\uD83C\uDFB2 Trivia"); + embedBuilder.addField("\uD83D\uDCD6 Begin here", + "Select a category from the dropdown menu to start a match!", + false); + embedBuilder.addField("❓ How to play", + "A new question gets posted every few seconds." + + "\nIf you get it right, you earn points!" + + "\nIf you choose a wrong answer, you lose points." + + "\nIf you are unsure, you can wait without answering and your score won't change!", + false); + + StringSelectMenu.Builder menuBuilder = StringSelectMenu.create("trivia_categories"); + + for(TriviaCategory category : categories) + { + String name = category.categoryName(); + int id = category.categoryId(); + menuBuilder.addOption(name, String.valueOf(id)); + } + + return new MessageResponse(null, embedBuilder.build(), menuBuilder.build()); + } + public static JSONObject fetchJson(String link) { try { @@ -206,7 +252,7 @@ public class TriviaUtil Message message = event.getMessage(); MessageChannel channel = message.getChannel(); - if(TriviaUtil.channelsRunningTrivia.contains(channel.getId())) + if(Trivia.channelsRunningTrivia.contains(channel.getId())) { // todo nicer looking // todo: also what if the bot stops (database...?) @@ -228,7 +274,7 @@ public class TriviaUtil TimeUnit.SECONDS); triviaTask.setScheduledFuture(future); - TriviaUtil.channelsRunningTrivia.add(channel.getId()); + Trivia.channelsRunningTrivia.add(channel.getId()); } public enum AnswerType { diff --git a/src/main/java/wtf/beatrice/hidekobot/commands/message/TriviaCommand.java b/src/main/java/wtf/beatrice/hidekobot/commands/message/TriviaCommand.java index 6884d4d..af5e2da 100644 --- a/src/main/java/wtf/beatrice/hidekobot/commands/message/TriviaCommand.java +++ b/src/main/java/wtf/beatrice/hidekobot/commands/message/TriviaCommand.java @@ -1,26 +1,19 @@ package wtf.beatrice.hidekobot.commands.message; -import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import net.dv8tion.jda.api.interactions.components.selections.SelectMenu; -import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.json.JSONObject; import wtf.beatrice.hidekobot.Cache; +import wtf.beatrice.hidekobot.objects.MessageResponse; import wtf.beatrice.hidekobot.objects.commands.CommandCategory; import wtf.beatrice.hidekobot.objects.commands.MessageCommand; -import wtf.beatrice.hidekobot.objects.comparators.TriviaCategoryComparator; -import wtf.beatrice.hidekobot.objects.fun.TriviaCategory; -import wtf.beatrice.hidekobot.runnables.TriviaTask; -import wtf.beatrice.hidekobot.util.TriviaUtil; +import wtf.beatrice.hidekobot.commands.base.Trivia; import java.util.*; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; public class TriviaCommand implements MessageCommand @@ -67,48 +60,22 @@ public class TriviaCommand implements MessageCommand if(!(channel instanceof TextChannel)) { - channel.sendMessage("\uD83D\uDE22 Sorry! Trivia doesn't work in DMs.").queue(); + channel.sendMessage(Trivia.getNoDMsError()).queue(); return; } - if(TriviaUtil.channelsRunningTrivia.contains(channel.getId())) + if(Trivia.channelsRunningTrivia.contains(channel.getId())) { - // todo nicer looking // todo: also what if the bot stops (database...?) // todo: also what if the message is already deleted - Message err = event.getMessage().reply("Trivia is already running here!").complete(); + Message err = event.getMessage().reply(Trivia.getTriviaAlreadyRunningError()).complete(); Cache.getTaskScheduler().schedule(() -> err.delete().queue(), 10, TimeUnit.SECONDS); return; } - // todo null checks - JSONObject categoriesJson = TriviaUtil.fetchJson(TriviaUtil.getCategoriesLink()); - List categories = TriviaUtil.parseCategories(categoriesJson); - categories.sort(new TriviaCategoryComparator()); + MessageResponse response = Trivia.generateMainScreen(); - EmbedBuilder embedBuilder = new EmbedBuilder(); - embedBuilder.setColor(Cache.getBotColor()); - embedBuilder.setTitle("\uD83C\uDFB2 Trivia"); - embedBuilder.addField("\uD83D\uDCD6 Begin here", - "Select a category from the dropdown menu to start a match!", - false); - embedBuilder.addField("❓ How to play", - "A new question gets posted every few seconds." + - "\nIf you get it right, you earn points!" + - "\nIf you choose a wrong answer, you lose points." + - "\nIf you are unsure, you can wait without answering and your score won't change!", - false); - - StringSelectMenu.Builder menuBuilder = StringSelectMenu.create("trivia_categories"); - - for(TriviaCategory category : categories) - { - String name = category.categoryName(); - int id = category.categoryId(); - menuBuilder.addOption(name, String.valueOf(id)); - } - - event.getMessage().replyEmbeds(embedBuilder.build()).addActionRow(menuBuilder.build()).queue(message -> + event.getMessage().replyEmbeds(response.embed()).addActionRow(response.components()).queue(message -> { Cache.getDatabaseSource().trackRanCommandReply(message, event.getAuthor()); Cache.getDatabaseSource().queueDisabling(message); diff --git a/src/main/java/wtf/beatrice/hidekobot/commands/slash/TriviaCommand.java b/src/main/java/wtf/beatrice/hidekobot/commands/slash/TriviaCommand.java new file mode 100644 index 0000000..a18f4dd --- /dev/null +++ b/src/main/java/wtf/beatrice/hidekobot/commands/slash/TriviaCommand.java @@ -0,0 +1,51 @@ +package wtf.beatrice.hidekobot.commands.slash; + +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.build.CommandData; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import org.jetbrains.annotations.NotNull; +import wtf.beatrice.hidekobot.Cache; +import wtf.beatrice.hidekobot.commands.base.Trivia; +import wtf.beatrice.hidekobot.objects.MessageResponse; +import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; + +public class TriviaCommand extends SlashCommandImpl +{ + @Override + public CommandData getSlashCommandData() + { + + return Commands.slash("trivia", + "Start a Trivia session and play with others!"); + } + + @Override + public void runSlashCommand(@NotNull SlashCommandInteractionEvent event) + { + MessageChannel channel = event.getChannel(); + + if(!(channel instanceof TextChannel)) + { + event.reply(Trivia.getNoDMsError()).queue(); + return; + } + + if(Trivia.channelsRunningTrivia.contains(channel.getId())) + { + event.reply(Trivia.getTriviaAlreadyRunningError()).setEphemeral(true).queue(); + return; + } + + MessageResponse response = Trivia.generateMainScreen(); + + event.replyEmbeds(response.embed()).addActionRow(response.components()).queue(interaction -> + { + interaction.retrieveOriginal().queue(message -> { + Cache.getDatabaseSource().trackRanCommandReply(message, event.getUser()); + Cache.getDatabaseSource().queueDisabling(message); + }); + }); + } +} diff --git a/src/main/java/wtf/beatrice/hidekobot/listeners/ButtonInteractionListener.java b/src/main/java/wtf/beatrice/hidekobot/listeners/ButtonInteractionListener.java index 9a05f43..56acde8 100644 --- a/src/main/java/wtf/beatrice/hidekobot/listeners/ButtonInteractionListener.java +++ b/src/main/java/wtf/beatrice/hidekobot/listeners/ButtonInteractionListener.java @@ -5,7 +5,7 @@ import net.dv8tion.jda.api.hooks.ListenerAdapter; import wtf.beatrice.hidekobot.commands.base.CoinFlip; import wtf.beatrice.hidekobot.commands.base.UrbanDictionary; import wtf.beatrice.hidekobot.util.CommandUtil; -import wtf.beatrice.hidekobot.util.TriviaUtil; +import wtf.beatrice.hidekobot.commands.base.Trivia; public class ButtonInteractionListener extends ListenerAdapter { @@ -27,9 +27,9 @@ public class ButtonInteractionListener extends ListenerAdapter case "urban_previouspage" -> UrbanDictionary.changePage(event, UrbanDictionary.ChangeType.PREVIOUS); // trivia - case "trivia_correct" -> TriviaUtil.handleAnswer(event, TriviaUtil.AnswerType.CORRECT); + case "trivia_correct" -> Trivia.handleAnswer(event, Trivia.AnswerType.CORRECT); case "trivia_wrong_1", "trivia_wrong_2", "trivia_wrong_3" -> - TriviaUtil.handleAnswer(event, TriviaUtil.AnswerType.WRONG); + Trivia.handleAnswer(event, Trivia.AnswerType.WRONG); } diff --git a/src/main/java/wtf/beatrice/hidekobot/listeners/SelectMenuInteractionListener.java b/src/main/java/wtf/beatrice/hidekobot/listeners/SelectMenuInteractionListener.java index 3acb9b1..b74b8bb 100644 --- a/src/main/java/wtf/beatrice/hidekobot/listeners/SelectMenuInteractionListener.java +++ b/src/main/java/wtf/beatrice/hidekobot/listeners/SelectMenuInteractionListener.java @@ -2,9 +2,7 @@ package wtf.beatrice.hidekobot.listeners; import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; -import net.dv8tion.jda.api.interactions.components.selections.SelectOption; -import wtf.beatrice.hidekobot.commands.base.CoinFlip; -import wtf.beatrice.hidekobot.util.TriviaUtil; +import wtf.beatrice.hidekobot.commands.base.Trivia; public class SelectMenuInteractionListener extends ListenerAdapter { @@ -15,7 +13,7 @@ public class SelectMenuInteractionListener extends ListenerAdapter switch (event.getComponentId().toLowerCase()) { // trivia - case "trivia_categories" -> TriviaUtil.handleMenuSelection(event); + case "trivia_categories" -> Trivia.handleMenuSelection(event); } } } diff --git a/src/main/java/wtf/beatrice/hidekobot/objects/MessageResponse.java b/src/main/java/wtf/beatrice/hidekobot/objects/MessageResponse.java index da697ca..ae95850 100644 --- a/src/main/java/wtf/beatrice/hidekobot/objects/MessageResponse.java +++ b/src/main/java/wtf/beatrice/hidekobot/objects/MessageResponse.java @@ -1,8 +1,12 @@ package wtf.beatrice.hidekobot.objects; import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.interactions.components.ActionRow; +import net.dv8tion.jda.api.interactions.components.ItemComponent; import org.jetbrains.annotations.Nullable; -public record MessageResponse(@Nullable String content, @Nullable MessageEmbed embed) { +public record MessageResponse(@Nullable String content, + @Nullable MessageEmbed embed, + @Nullable ItemComponent... components) { } diff --git a/src/main/java/wtf/beatrice/hidekobot/runnables/TriviaTask.java b/src/main/java/wtf/beatrice/hidekobot/runnables/TriviaTask.java index 7c0620f..d0bb6b5 100644 --- a/src/main/java/wtf/beatrice/hidekobot/runnables/TriviaTask.java +++ b/src/main/java/wtf/beatrice/hidekobot/runnables/TriviaTask.java @@ -13,7 +13,7 @@ import wtf.beatrice.hidekobot.objects.fun.TriviaQuestion; import wtf.beatrice.hidekobot.objects.fun.TriviaScore; import wtf.beatrice.hidekobot.objects.comparators.TriviaScoreComparator; import wtf.beatrice.hidekobot.util.CommandUtil; -import wtf.beatrice.hidekobot.util.TriviaUtil; +import wtf.beatrice.hidekobot.commands.base.Trivia; import java.util.*; import java.util.concurrent.ScheduledFuture; @@ -39,8 +39,8 @@ public class TriviaTask implements Runnable this.channel = channel; this.category = category; - triviaJson = TriviaUtil.fetchJson(TriviaUtil.getTriviaLink(category.categoryId())); - questions = TriviaUtil.parseQuestions(triviaJson); //todo: null check, rate limiting... + triviaJson = Trivia.fetchJson(Trivia.getTriviaLink(category.categoryId())); + questions = Trivia.parseQuestions(triviaJson); //todo: null check, rate limiting... } public void setScheduledFuture(ScheduledFuture future) @@ -64,7 +64,7 @@ public class TriviaTask implements Runnable // todo: maybe also add who replied correctly as a list // clean the list of people who answered, so they can answer again for the new question - TriviaUtil.channelAndWhoResponded.put(previousMessage.getChannel().getId(), new ArrayList<>()); + Trivia.channelAndWhoResponded.put(previousMessage.getChannel().getId(), new ArrayList<>()); } if(iteration >= questions.size()) @@ -76,7 +76,7 @@ public class TriviaTask implements Runnable int topScore = 0; StringBuilder othersBuilder = new StringBuilder(); - LinkedList triviaScores = TriviaUtil.channelAndScores.get(channel.getId()); + LinkedList triviaScores = Trivia.channelAndScores.get(channel.getId()); if(triviaScores == null) triviaScores = new LinkedList<>(); else triviaScores.sort(new TriviaScoreComparator()); @@ -135,9 +135,9 @@ public class TriviaTask implements Runnable channel.sendMessage(scoreboardText).addEmbeds(scoreboardBuilder.build()).queue(); // remove all cached data - TriviaUtil.channelsRunningTrivia.remove(channel.getId()); - TriviaUtil.channelAndWhoResponded.remove(channel.getId()); - TriviaUtil.channelAndScores.remove(channel.getId()); + Trivia.channelsRunningTrivia.remove(channel.getId()); + Trivia.channelAndWhoResponded.remove(channel.getId()); + Trivia.channelAndScores.remove(channel.getId()); future.cancel(false); // we didn't implement null checks on the future on purpose, because we need to know if we were unable