-
Notifications
You must be signed in to change notification settings - Fork 5
feat: add QuizQuestion and TriviaManager classes for Java trivia game #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
492d648
06d3d1c
7d966cc
73890ed
6e09485
ab3d603
607ec27
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| package com.togetherjava.tjplays.listeners.commands; | ||
|
|
||
| import com.togetherjava.tjplays.trivia.QuizQuestion; | ||
| import com.togetherjava.tjplays.trivia.TriviaManager; | ||
|
|
||
| import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; | ||
| import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; | ||
| import net.dv8tion.jda.api.interactions.commands.build.Commands; | ||
| import net.dv8tion.jda.api.interactions.components.buttons.Button; | ||
|
|
||
| import java.util.List; | ||
| import java.util.Optional; | ||
| import java.util.concurrent.ConcurrentHashMap; | ||
|
|
||
| /** | ||
| * Handles the /javaquiz command, presenting Java trivia questions to users. | ||
| * Uses TriviaManager to fetch questions and manages user quiz state. | ||
| * Future contributors can extend this for more quiz features. | ||
| */ | ||
| public final class JavaQuizCommand extends SlashCommand { | ||
| private static final String COMMAND_NAME = "javaquiz"; | ||
|
|
||
| private final TriviaManager triviaManager; | ||
| // messageId -> QuizQuestion | ||
| private final ConcurrentHashMap<String, QuizQuestion> activeQuestions = new ConcurrentHashMap<>(); | ||
| // userId -> messageId | ||
| private final ConcurrentHashMap<String, String> userActiveQuiz = new ConcurrentHashMap<>(); | ||
|
|
||
| /** | ||
| * Constructs a JavaQuizCommand with a TriviaManager instance. | ||
| * @param triviaManager the TriviaManager to use for fetching questions | ||
| */ | ||
| public JavaQuizCommand(TriviaManager triviaManager) { | ||
| super(Commands.slash(COMMAND_NAME, "Get a random Java trivia question")); | ||
| this.triviaManager = triviaManager; | ||
| } | ||
|
|
||
| @Override | ||
| public void onSlashCommand(SlashCommandInteractionEvent event) { | ||
| String userId = event.getUser().getId(); | ||
| // Prevent new quiz if user has an active one | ||
| if (userActiveQuiz.containsKey(userId)) { | ||
| event.reply("You already have an active quiz. Please answer it before starting a new one.").setEphemeral(true).queue(); | ||
| return; | ||
| } | ||
|
|
||
| event.deferReply().queue(); | ||
|
|
||
| Optional<QuizQuestion> question = triviaManager.fetchRandomQuestion(); | ||
| if (question.isEmpty()) { | ||
| event.getHook().editOriginal("Could not fetch a quiz question. Try again later.").queue(); | ||
| return; | ||
| } | ||
|
|
||
| QuizQuestion quizQuestion = question.get(); | ||
| List<String> choices = quizQuestion.getChoices(); | ||
|
|
||
| String quizContent = String.format("**Java Quiz**\n\n%s\n\n%s", | ||
| quizQuestion.getQuestion(), | ||
| java.util.stream.IntStream.range(0, choices.size()) | ||
| .mapToObj(i -> String.format("`%d`) %s", i + 1, choices.get(i))) | ||
| .collect(java.util.stream.Collectors.joining("\n")) | ||
| ); | ||
|
|
||
| String messageId = event.getHook().editOriginal(quizContent) | ||
| .setActionRow( | ||
| Button.primary(COMMAND_NAME + "-1-" + userId, "1"), | ||
| Button.primary(COMMAND_NAME + "-2-" + userId, "2"), | ||
| Button.primary(COMMAND_NAME + "-3-" + userId, "3"), | ||
| Button.primary(COMMAND_NAME + "-4-" + userId, "4") | ||
| ) | ||
| .complete() | ||
| .getId(); | ||
|
|
||
| activeQuestions.put(messageId, quizQuestion); | ||
| userActiveQuiz.put(userId, messageId); | ||
| } | ||
|
|
||
| @Override | ||
| public void onButtonInteraction(ButtonInteractionEvent event) { | ||
| String buttonId = event.getButton().getId(); | ||
| if (buttonId == null || !buttonId.startsWith(COMMAND_NAME)) { | ||
| return; | ||
| } | ||
|
|
||
| if (!buttonId.contains(event.getUser().getId())) { | ||
| event.reply("This isn't your quiz!").setEphemeral(true).queue(); | ||
| return; | ||
| } | ||
|
|
||
| String userId = event.getUser().getId(); | ||
| QuizQuestion quizQuestion = activeQuestions.remove(event.getMessageId()); | ||
| // Remove user's active quiz | ||
| userActiveQuiz.remove(userId); | ||
| if (quizQuestion == null) { | ||
| event.reply("This quiz has already been answered.").setEphemeral(true).queue(); | ||
| return; | ||
| } | ||
|
|
||
| int chosen = Character.getNumericValue(buttonId.charAt(COMMAND_NAME.length() + 1)) - 1; | ||
| boolean correct = chosen == quizQuestion.getCorrectAnswerIndex(); | ||
|
|
||
| String correctAnswer = quizQuestion.getChoices().get(quizQuestion.getCorrectAnswerIndex()); | ||
| String result = correct | ||
| ? "Correct! The answer is: **" + correctAnswer + "**" | ||
| : "Wrong! The correct answer was: **" + correctAnswer + "**"; | ||
|
|
||
| event.editMessage(event.getMessage().getContentRaw() + "\n\n" + result) | ||
| .setActionRow( | ||
| Button.primary(COMMAND_NAME + "-1-" + userId, "1").asDisabled(), | ||
| Button.primary(COMMAND_NAME + "-2-" + userId, "2").asDisabled(), | ||
| Button.primary(COMMAND_NAME + "-3-" + userId, "3").asDisabled(), | ||
| Button.primary(COMMAND_NAME + "-4-" + userId, "4").asDisabled() | ||
| ) | ||
| .queue(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| package com.togetherjava.tjplays.trivia; | ||
|
|
||
| import com.fasterxml.jackson.annotation.JsonCreator; | ||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||
| import java.util.List; | ||
|
|
||
| /** | ||
| * Represents a single quiz question for Java trivia. | ||
| * Used by TriviaManager and JavaQuizCommand to display questions and validate answers. | ||
| * Future contributors can use this for any quiz/trivia feature needing a question, choices, and answer index. | ||
| */ | ||
| public record QuizQuestion( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this is public-facing, it should have a JavaDoc explaining what it is, where it's used in and where it could be used by future contributors. |
||
| @JsonProperty("question") String question, | ||
| @JsonProperty("choices") List<String> choices, | ||
| @JsonProperty("correct") int correctAnswerIndex | ||
| ) { | ||
| @JsonCreator | ||
| public QuizQuestion {} | ||
|
|
||
| public String getQuestion() { | ||
| return question; | ||
| } | ||
|
|
||
| public List<String> getChoices() { | ||
| return choices; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the index of the correct answer in the choices list. | ||
| */ | ||
| public int getCorrectAnswerIndex() { | ||
| return correctAnswerIndex; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| package com.togetherjava.tjplays.trivia; | ||
|
|
||
| import com.togetherjava.tjplays.services.chatgpt.ChatGptService; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import java.util.Optional; | ||
|
|
||
| /** | ||
| * Manages Java trivia questions using ChatGptService. | ||
| * Used by quiz commands to fetch questions for users. | ||
| * Future contributors can use this for any trivia/quiz feature. | ||
| */ | ||
| public class TriviaManager { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this is public-facing, it should have a JavaDoc explaining what it is, where it's used in and where it could be used by future contributors. |
||
| private final ChatGptService chatGptService; | ||
| private final ObjectMapper mapper = new ObjectMapper(); | ||
|
|
||
| public TriviaManager(ChatGptService chatGptService) { | ||
| this.chatGptService = chatGptService; | ||
| } | ||
|
|
||
| public Optional<QuizQuestion> fetchRandomQuestion() { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To respect the principle of least privilege, let's reduce this method's visibility into |
||
| String prompt = "Provide one Java trivia question as a JSON object like" | ||
| + " {\"question\":\"...\",\"choices\":[\"A\",\"B\",\"C\",\"D\"],\"correct\":<index>}"; | ||
| Optional<String> raw = chatGptService.ask(prompt, "java quiz"); | ||
| if (raw.isEmpty()) { | ||
| return Optional.empty(); | ||
| } | ||
| try { | ||
| QuizQuestion question = mapper.readValue(raw.get(), QuizQuestion.class); | ||
| return Optional.of(question); | ||
| } catch (Exception e) { | ||
| return Optional.empty(); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once again, a JavaDoc would be useful here for future contributors as this is
publicfacing.