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 PingCommand());
slashCommandListener.registerCommand(new SayCommand());
slashCommandListener.registerCommand(new TriviaCommand());
slashCommandListener.registerCommand(new UrbanDictionaryCommand());
Cache.setSlashCommandListener(slashCommandListener);
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.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<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)
{
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 {

View File

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

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

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

View File

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

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.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<TriviaScore> triviaScores = TriviaUtil.channelAndScores.get(channel.getId());
LinkedList<TriviaScore> 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