Make trivia support slash commands too

This commit is contained in:
Bea 2022-12-21 23:31:12 +01:00
parent 26f1c880ea
commit 50196bb8f9
8 changed files with 127 additions and 60 deletions

View File

@ -124,6 +124,7 @@ public class HidekoBot
slashCommandListener.registerCommand(new MagicBallCommand()); slashCommandListener.registerCommand(new MagicBallCommand());
slashCommandListener.registerCommand(new PingCommand()); slashCommandListener.registerCommand(new PingCommand());
slashCommandListener.registerCommand(new SayCommand()); slashCommandListener.registerCommand(new SayCommand());
slashCommandListener.registerCommand(new TriviaCommand());
slashCommandListener.registerCommand(new UrbanDictionaryCommand()); slashCommandListener.registerCommand(new UrbanDictionaryCommand());
Cache.setSlashCommandListener(slashCommandListener); Cache.setSlashCommandListener(slashCommandListener);
Cache.setSlashCommandCompletionListener(slashCommandCompletionListener); Cache.setSlashCommandCompletionListener(slashCommandCompletionListener);

View File

@ -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.Message;
import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; 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.ButtonInteractionEvent;
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent; 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.SelectOption;
import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu;
import org.apache.commons.text.StringEscapeUtils; import org.apache.commons.text.StringEscapeUtils;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import wtf.beatrice.hidekobot.Cache; 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.TriviaCategory;
import wtf.beatrice.hidekobot.objects.fun.TriviaQuestion; import wtf.beatrice.hidekobot.objects.fun.TriviaQuestion;
import wtf.beatrice.hidekobot.objects.fun.TriviaScore; import wtf.beatrice.hidekobot.objects.fun.TriviaScore;
import wtf.beatrice.hidekobot.runnables.TriviaTask; import wtf.beatrice.hidekobot.runnables.TriviaTask;
import wtf.beatrice.hidekobot.util.CommandUtil;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
@ -27,7 +32,7 @@ import java.util.List;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; 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 triviaLink = "https://opentdb.com/api.php?amount=10&type=multiple&category=";
private final static String categoriesLink = "https://opentdb.com/api_category.php"; 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 getTriviaLink(int categoryId) {return triviaLink + categoryId; }
public static String getCategoriesLink() {return categoriesLink; } 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<TriviaCategory> 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) public static JSONObject fetchJson(String link)
{ {
try { try {
@ -206,7 +252,7 @@ public class TriviaUtil
Message message = event.getMessage(); Message message = event.getMessage();
MessageChannel channel = message.getChannel(); MessageChannel channel = message.getChannel();
if(TriviaUtil.channelsRunningTrivia.contains(channel.getId())) if(Trivia.channelsRunningTrivia.contains(channel.getId()))
{ {
// todo nicer looking // todo nicer looking
// todo: also what if the bot stops (database...?) // todo: also what if the bot stops (database...?)
@ -228,7 +274,7 @@ public class TriviaUtil
TimeUnit.SECONDS); TimeUnit.SECONDS);
triviaTask.setScheduledFuture(future); triviaTask.setScheduledFuture(future);
TriviaUtil.channelsRunningTrivia.add(channel.getId()); Trivia.channelsRunningTrivia.add(channel.getId());
} }
public enum AnswerType { public enum AnswerType {

View File

@ -1,26 +1,19 @@
package wtf.beatrice.hidekobot.commands.message; package wtf.beatrice.hidekobot.commands.message;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 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.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.json.JSONObject;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.objects.MessageResponse;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory; import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
import wtf.beatrice.hidekobot.objects.commands.MessageCommand; import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
import wtf.beatrice.hidekobot.objects.comparators.TriviaCategoryComparator; import wtf.beatrice.hidekobot.commands.base.Trivia;
import wtf.beatrice.hidekobot.objects.fun.TriviaCategory;
import wtf.beatrice.hidekobot.runnables.TriviaTask;
import wtf.beatrice.hidekobot.util.TriviaUtil;
import java.util.*; import java.util.*;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class TriviaCommand implements MessageCommand public class TriviaCommand implements MessageCommand
@ -67,48 +60,22 @@ public class TriviaCommand implements MessageCommand
if(!(channel instanceof TextChannel)) if(!(channel instanceof TextChannel))
{ {
channel.sendMessage("\uD83D\uDE22 Sorry! Trivia doesn't work in DMs.").queue(); channel.sendMessage(Trivia.getNoDMsError()).queue();
return; 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 bot stops (database...?)
// todo: also what if the message is already deleted // 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); Cache.getTaskScheduler().schedule(() -> err.delete().queue(), 10, TimeUnit.SECONDS);
return; return;
} }
// todo null checks MessageResponse response = Trivia.generateMainScreen();
JSONObject categoriesJson = TriviaUtil.fetchJson(TriviaUtil.getCategoriesLink());
List<TriviaCategory> categories = TriviaUtil.parseCategories(categoriesJson);
categories.sort(new TriviaCategoryComparator());
EmbedBuilder embedBuilder = new EmbedBuilder(); event.getMessage().replyEmbeds(response.embed()).addActionRow(response.components()).queue(message ->
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 ->
{ {
Cache.getDatabaseSource().trackRanCommandReply(message, event.getAuthor()); Cache.getDatabaseSource().trackRanCommandReply(message, event.getAuthor());
Cache.getDatabaseSource().queueDisabling(message); Cache.getDatabaseSource().queueDisabling(message);

View File

@ -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);
});
});
}
}

View File

@ -5,7 +5,7 @@ import net.dv8tion.jda.api.hooks.ListenerAdapter;
import wtf.beatrice.hidekobot.commands.base.CoinFlip; import wtf.beatrice.hidekobot.commands.base.CoinFlip;
import wtf.beatrice.hidekobot.commands.base.UrbanDictionary; import wtf.beatrice.hidekobot.commands.base.UrbanDictionary;
import wtf.beatrice.hidekobot.util.CommandUtil; import wtf.beatrice.hidekobot.util.CommandUtil;
import wtf.beatrice.hidekobot.util.TriviaUtil; import wtf.beatrice.hidekobot.commands.base.Trivia;
public class ButtonInteractionListener extends ListenerAdapter public class ButtonInteractionListener extends ListenerAdapter
{ {
@ -27,9 +27,9 @@ public class ButtonInteractionListener extends ListenerAdapter
case "urban_previouspage" -> UrbanDictionary.changePage(event, UrbanDictionary.ChangeType.PREVIOUS); case "urban_previouspage" -> UrbanDictionary.changePage(event, UrbanDictionary.ChangeType.PREVIOUS);
// trivia // 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" -> case "trivia_wrong_1", "trivia_wrong_2", "trivia_wrong_3" ->
TriviaUtil.handleAnswer(event, TriviaUtil.AnswerType.WRONG); Trivia.handleAnswer(event, Trivia.AnswerType.WRONG);
} }

View File

@ -2,9 +2,7 @@ package wtf.beatrice.hidekobot.listeners;
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.dv8tion.jda.api.interactions.components.selections.SelectOption; import wtf.beatrice.hidekobot.commands.base.Trivia;
import wtf.beatrice.hidekobot.commands.base.CoinFlip;
import wtf.beatrice.hidekobot.util.TriviaUtil;
public class SelectMenuInteractionListener extends ListenerAdapter public class SelectMenuInteractionListener extends ListenerAdapter
{ {
@ -15,7 +13,7 @@ public class SelectMenuInteractionListener extends ListenerAdapter
switch (event.getComponentId().toLowerCase()) { switch (event.getComponentId().toLowerCase()) {
// trivia // trivia
case "trivia_categories" -> TriviaUtil.handleMenuSelection(event); case "trivia_categories" -> Trivia.handleMenuSelection(event);
} }
} }
} }

View File

@ -1,8 +1,12 @@
package wtf.beatrice.hidekobot.objects; package wtf.beatrice.hidekobot.objects;
import net.dv8tion.jda.api.entities.MessageEmbed; 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; 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) {
} }

View File

@ -13,7 +13,7 @@ import wtf.beatrice.hidekobot.objects.fun.TriviaQuestion;
import wtf.beatrice.hidekobot.objects.fun.TriviaScore; import wtf.beatrice.hidekobot.objects.fun.TriviaScore;
import wtf.beatrice.hidekobot.objects.comparators.TriviaScoreComparator; import wtf.beatrice.hidekobot.objects.comparators.TriviaScoreComparator;
import wtf.beatrice.hidekobot.util.CommandUtil; 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.*;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
@ -39,8 +39,8 @@ public class TriviaTask implements Runnable
this.channel = channel; this.channel = channel;
this.category = category; this.category = category;
triviaJson = TriviaUtil.fetchJson(TriviaUtil.getTriviaLink(category.categoryId())); triviaJson = Trivia.fetchJson(Trivia.getTriviaLink(category.categoryId()));
questions = TriviaUtil.parseQuestions(triviaJson); //todo: null check, rate limiting... questions = Trivia.parseQuestions(triviaJson); //todo: null check, rate limiting...
} }
public void setScheduledFuture(ScheduledFuture<?> future) public void setScheduledFuture(ScheduledFuture<?> future)
@ -64,7 +64,7 @@ public class TriviaTask implements Runnable
// todo: maybe also add who replied correctly as a list // 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 // 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()) if(iteration >= questions.size())
@ -76,7 +76,7 @@ public class TriviaTask implements Runnable
int topScore = 0; int topScore = 0;
StringBuilder othersBuilder = new StringBuilder(); StringBuilder othersBuilder = new StringBuilder();
LinkedList<TriviaScore> triviaScores = TriviaUtil.channelAndScores.get(channel.getId()); LinkedList<TriviaScore> triviaScores = Trivia.channelAndScores.get(channel.getId());
if(triviaScores == null) triviaScores = new LinkedList<>(); if(triviaScores == null) triviaScores = new LinkedList<>();
else triviaScores.sort(new TriviaScoreComparator()); else triviaScores.sort(new TriviaScoreComparator());
@ -135,9 +135,9 @@ public class TriviaTask implements Runnable
channel.sendMessage(scoreboardText).addEmbeds(scoreboardBuilder.build()).queue(); channel.sendMessage(scoreboardText).addEmbeds(scoreboardBuilder.build()).queue();
// remove all cached data // remove all cached data
TriviaUtil.channelsRunningTrivia.remove(channel.getId()); Trivia.channelsRunningTrivia.remove(channel.getId());
TriviaUtil.channelAndWhoResponded.remove(channel.getId()); Trivia.channelAndWhoResponded.remove(channel.getId());
TriviaUtil.channelAndScores.remove(channel.getId()); Trivia.channelAndScores.remove(channel.getId());
future.cancel(false); future.cancel(false);
// we didn't implement null checks on the future on purpose, because we need to know if we were unable // we didn't implement null checks on the future on purpose, because we need to know if we were unable