Make trivia loop through all questions
This commit is contained in:
parent
0be4389448
commit
c9082e84cc
@ -1,25 +1,22 @@
|
|||||||
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.Channel;
|
|
||||||
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.events.message.MessageReceivedEvent;
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
|
||||||
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.TriviaQuestion;
|
|
||||||
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.runnables.TriviaTask;
|
||||||
import wtf.beatrice.hidekobot.util.TriviaUtil;
|
import wtf.beatrice.hidekobot.util.TriviaUtil;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
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
|
||||||
@ -62,11 +59,11 @@ public class TriviaCommand implements MessageCommand
|
|||||||
@Override
|
@Override
|
||||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
{
|
{
|
||||||
Channel channel = event.getChannel();
|
MessageChannel channel = event.getChannel();
|
||||||
|
|
||||||
if(!(channel instanceof TextChannel))
|
if(!(channel instanceof TextChannel))
|
||||||
{
|
{
|
||||||
event.getMessage().reply("\uD83D\uDE22 Sorry! Trivia doesn't work in DMs.").queue();
|
channel.sendMessage("\uD83D\uDE22 Sorry! Trivia doesn't work in DMs.").queue();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,45 +81,15 @@ public class TriviaCommand implements MessageCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
JSONObject triviaJson = TriviaUtil.fetchTrivia();
|
TriviaTask triviaTask = new TriviaTask(event.getAuthor(), channel);
|
||||||
List<TriviaQuestion> questions = TriviaUtil.getQuestions(triviaJson); //todo null check, rate limiting...
|
ScheduledFuture<?> future =
|
||||||
TriviaQuestion first = questions.get(0);
|
Cache.getTaskScheduler().scheduleAtFixedRate(triviaTask,
|
||||||
|
0,
|
||||||
|
10,
|
||||||
|
TimeUnit.SECONDS);
|
||||||
|
triviaTask.setScheduledFuture(future);
|
||||||
|
|
||||||
List<Button> answerButtons = new ArrayList<>();
|
TriviaUtil.channelsRunningTrivia.add(channel.getId());
|
||||||
|
|
||||||
Button correctAnswerButton = Button.primary("trivia_correct", first.correctAnswer());
|
|
||||||
answerButtons.add(correctAnswerButton);
|
|
||||||
|
|
||||||
int i = 0; // we need to add a number because buttons can't have the same id
|
|
||||||
for(String wrongAnswer : first.wrongAnswers())
|
|
||||||
{
|
|
||||||
i++;
|
|
||||||
Button wrongAnswerButton = Button.primary("trivia_wrong_" + i, wrongAnswer);
|
|
||||||
answerButtons.add(wrongAnswerButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
Collections.shuffle(answerButtons);
|
|
||||||
|
|
||||||
EmbedBuilder embedBuilder = new EmbedBuilder();
|
|
||||||
|
|
||||||
embedBuilder.setColor(Cache.getBotColor());
|
|
||||||
embedBuilder.setTitle("Trivia");
|
|
||||||
|
|
||||||
embedBuilder.addField("Question", first.question(), false);
|
|
||||||
|
|
||||||
Message botMessage = event.getChannel()
|
|
||||||
.sendMessageEmbeds(embedBuilder.build())
|
|
||||||
.setActionRow(answerButtons)
|
|
||||||
.complete();
|
|
||||||
|
|
||||||
Cache.getDatabaseSource().trackRanCommandReply(botMessage, event.getAuthor());
|
|
||||||
// todo: ^ we should get rid of this tracking, since we don't need to know who started the trivia.
|
|
||||||
// todo: however, for now, that's the only way to avoid a thread-locking scenario as some data is
|
|
||||||
// todo: only stored in that table. this should be solved when we merge / fix the two main tables.
|
|
||||||
// todo: then, we can remove this instruction.
|
|
||||||
Cache.getDatabaseSource().queueDisabling(botMessage);
|
|
||||||
|
|
||||||
TriviaUtil.channelsRunningTrivia.add(event.getChannel().getId());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import net.dv8tion.jda.api.requests.RestAction;
|
|||||||
import wtf.beatrice.hidekobot.Cache;
|
import wtf.beatrice.hidekobot.Cache;
|
||||||
import wtf.beatrice.hidekobot.HidekoBot;
|
import wtf.beatrice.hidekobot.HidekoBot;
|
||||||
import wtf.beatrice.hidekobot.datasources.DatabaseSource;
|
import wtf.beatrice.hidekobot.datasources.DatabaseSource;
|
||||||
|
import wtf.beatrice.hidekobot.util.CommandUtil;
|
||||||
import wtf.beatrice.hidekobot.util.Logger;
|
import wtf.beatrice.hidekobot.util.Logger;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@ -63,93 +64,9 @@ public class ExpiredMessageTask implements Runnable {
|
|||||||
if(now.isAfter(expiryDate))
|
if(now.isAfter(expiryDate))
|
||||||
{
|
{
|
||||||
if(Cache.isVerbose()) logger.log("expired: " + messageId);
|
if(Cache.isVerbose()) logger.log("expired: " + messageId);
|
||||||
disableExpired(messageId);
|
CommandUtil.disableExpired(messageId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void disableExpired(String messageId)
|
|
||||||
{
|
|
||||||
String channelId = databaseSource.getQueuedExpiringMessageChannel(messageId);
|
|
||||||
|
|
||||||
// todo: warning, the following method + related if check are thread-locking.
|
|
||||||
// todo: we should probably merge the two tables somehow, since they have redundant information.
|
|
||||||
ChannelType msgChannelType = databaseSource.getTrackedMessageChannelType(messageId);
|
|
||||||
|
|
||||||
MessageChannel textChannel = null;
|
|
||||||
|
|
||||||
// this should never happen, but only message channels are supported.
|
|
||||||
if(!msgChannelType.isMessage())
|
|
||||||
{
|
|
||||||
databaseSource.untrackExpiredMessage(messageId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if this is a DM
|
|
||||||
if(!(msgChannelType.isGuild()))
|
|
||||||
{
|
|
||||||
String userId = databaseSource.getTrackedReplyUserId(messageId);
|
|
||||||
User user = userId == null ? null : HidekoBot.getAPI().retrieveUserById(userId).complete();
|
|
||||||
if(user == null)
|
|
||||||
{
|
|
||||||
// if user is not found, consider it expired
|
|
||||||
// (deleted profile, or blocked the bot)
|
|
||||||
databaseSource.untrackExpiredMessage(messageId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
textChannel = user.openPrivateChannel().complete();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
String guildId = databaseSource.getQueuedExpiringMessageGuild(messageId);
|
|
||||||
Guild guild = guildId == null ? null : HidekoBot.getAPI().getGuildById(guildId);
|
|
||||||
if(guild == null)
|
|
||||||
{
|
|
||||||
// if guild is not found, consider it expired
|
|
||||||
// (server was deleted or bot was kicked)
|
|
||||||
databaseSource.untrackExpiredMessage(messageId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
textChannel = guild.getTextChannelById(channelId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(textChannel == null)
|
|
||||||
{
|
|
||||||
// if channel is not found, count it as expired
|
|
||||||
// (channel was deleted or bot permissions restricted)
|
|
||||||
databaseSource.untrackExpiredMessage(messageId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RestAction<Message> retrieveAction = textChannel.retrieveMessageById(messageId);
|
|
||||||
|
|
||||||
|
|
||||||
if(Cache.isVerbose()) logger.log("cleaning up: " + messageId);
|
|
||||||
|
|
||||||
retrieveAction.queue(
|
|
||||||
message -> {
|
|
||||||
if(message == null)
|
|
||||||
{
|
|
||||||
databaseSource.untrackExpiredMessage(messageId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<LayoutComponent> components = message.getComponents();
|
|
||||||
List<LayoutComponent> newComponents = new ArrayList<>();
|
|
||||||
for (LayoutComponent component : components)
|
|
||||||
{
|
|
||||||
component = component.asDisabled();
|
|
||||||
newComponents.add(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
message.editMessageComponents(newComponents).queue();
|
|
||||||
databaseSource.untrackExpiredMessage(messageId);
|
|
||||||
},
|
|
||||||
|
|
||||||
(error) -> {
|
|
||||||
databaseSource.untrackExpiredMessage(messageId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,107 @@
|
|||||||
package wtf.beatrice.hidekobot.runnables;
|
package wtf.beatrice.hidekobot.runnables;
|
||||||
|
|
||||||
|
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.interactions.components.buttons.Button;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import wtf.beatrice.hidekobot.Cache;
|
||||||
|
import wtf.beatrice.hidekobot.objects.TriviaQuestion;
|
||||||
|
import wtf.beatrice.hidekobot.util.CommandUtil;
|
||||||
|
import wtf.beatrice.hidekobot.util.TriviaUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
|
||||||
public class TriviaTask implements Runnable
|
public class TriviaTask implements Runnable
|
||||||
{
|
{
|
||||||
|
|
||||||
|
private final User author;
|
||||||
|
private final MessageChannel channel;
|
||||||
|
|
||||||
|
private Message previousMessage = null;
|
||||||
|
|
||||||
|
private final JSONObject triviaJson;
|
||||||
|
private final List<TriviaQuestion> questions;
|
||||||
|
|
||||||
|
ScheduledFuture<?> future = null;
|
||||||
|
|
||||||
|
private int iteration = 0;
|
||||||
|
|
||||||
|
public TriviaTask(User author, MessageChannel channel)
|
||||||
|
{
|
||||||
|
this.author = author;
|
||||||
|
this.channel = channel;
|
||||||
|
|
||||||
|
triviaJson = TriviaUtil.fetchTrivia();
|
||||||
|
questions = TriviaUtil.getQuestions(triviaJson); //todo: null check, rate limiting...
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScheduledFuture(ScheduledFuture<?> future)
|
||||||
|
{
|
||||||
|
this.future = future;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
||||||
|
if(previousMessage != null)
|
||||||
|
{
|
||||||
|
// todo: we shouldn't use this method, since it messes with the database...
|
||||||
|
CommandUtil.disableExpired(previousMessage.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(iteration >= questions.size())
|
||||||
|
{
|
||||||
|
// todo: nicer-looking embed with stats
|
||||||
|
channel.sendMessage("Trivia session is over!").queue();
|
||||||
|
|
||||||
|
TriviaUtil.channelsRunningTrivia.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
|
||||||
|
// to cancel it (and console errors should make it clear enough).
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriviaQuestion currentTriviaQuestion = questions.get(iteration);
|
||||||
|
|
||||||
|
List<Button> answerButtons = new ArrayList<>();
|
||||||
|
|
||||||
|
Button correctAnswerButton = Button.primary("trivia_correct", currentTriviaQuestion.correctAnswer());
|
||||||
|
answerButtons.add(correctAnswerButton);
|
||||||
|
|
||||||
|
int i = 0; // we need to add a number because buttons can't have the same id
|
||||||
|
for(String wrongAnswer : currentTriviaQuestion.wrongAnswers())
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
Button wrongAnswerButton = Button.primary("trivia_wrong_" + i, wrongAnswer);
|
||||||
|
answerButtons.add(wrongAnswerButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.shuffle(answerButtons);
|
||||||
|
|
||||||
|
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||||
|
|
||||||
|
embedBuilder.setColor(Cache.getBotColor());
|
||||||
|
embedBuilder.setTitle("Trivia (" + (iteration+1) + "/" + questions.size() + ")");
|
||||||
|
|
||||||
|
embedBuilder.addField("Question", currentTriviaQuestion.question(), false);
|
||||||
|
|
||||||
|
previousMessage = channel
|
||||||
|
.sendMessageEmbeds(embedBuilder.build())
|
||||||
|
.setActionRow(answerButtons)
|
||||||
|
.complete();
|
||||||
|
|
||||||
|
Cache.getDatabaseSource().trackRanCommandReply(previousMessage, author);
|
||||||
|
// todo: ^ we should get rid of this tracking, since we don't need to know who started the trivia.
|
||||||
|
// todo: however, for now, that's the only way to avoid a thread-locking scenario as some data is
|
||||||
|
// todo: only stored in that table. this should be solved when we merge / fix the two main tables.
|
||||||
|
// todo: then, we can remove this instruction.
|
||||||
|
Cache.getDatabaseSource().queueDisabling(previousMessage);
|
||||||
|
|
||||||
|
iteration++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
package wtf.beatrice.hidekobot.util;
|
package wtf.beatrice.hidekobot.util;
|
||||||
|
|
||||||
import net.dv8tion.jda.api.JDA;
|
import net.dv8tion.jda.api.JDA;
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||||
|
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.interactions.commands.Command;
|
import net.dv8tion.jda.api.interactions.commands.Command;
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
|
||||||
|
import net.dv8tion.jda.api.requests.RestAction;
|
||||||
import wtf.beatrice.hidekobot.Cache;
|
import wtf.beatrice.hidekobot.Cache;
|
||||||
import wtf.beatrice.hidekobot.HidekoBot;
|
import wtf.beatrice.hidekobot.HidekoBot;
|
||||||
|
import wtf.beatrice.hidekobot.datasources.DatabaseSource;
|
||||||
import wtf.beatrice.hidekobot.objects.commands.SlashCommand;
|
import wtf.beatrice.hidekobot.objects.commands.SlashCommand;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -134,4 +142,96 @@ public class CommandUtil
|
|||||||
logger.log("Commands updated. New total: " + allCommands.size() + ".");
|
logger.log("Commands updated. New total: " + allCommands.size() + ".");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to disable all buttons from an expired message.
|
||||||
|
*
|
||||||
|
* @param messageId the message id to disable.
|
||||||
|
*/
|
||||||
|
public static void disableExpired(String messageId)
|
||||||
|
{
|
||||||
|
DatabaseSource databaseSource = Cache.getDatabaseSource();
|
||||||
|
|
||||||
|
String channelId = databaseSource.getQueuedExpiringMessageChannel(messageId);
|
||||||
|
|
||||||
|
// todo: warning, the following method + related if check are thread-locking.
|
||||||
|
// todo: we should probably merge the two tables somehow, since they have redundant information.
|
||||||
|
ChannelType msgChannelType = databaseSource.getTrackedMessageChannelType(messageId);
|
||||||
|
|
||||||
|
MessageChannel textChannel = null;
|
||||||
|
|
||||||
|
// this should never happen, but only message channels are supported.
|
||||||
|
if(!msgChannelType.isMessage())
|
||||||
|
{
|
||||||
|
databaseSource.untrackExpiredMessage(messageId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this is a DM
|
||||||
|
if(!(msgChannelType.isGuild()))
|
||||||
|
{
|
||||||
|
String userId = databaseSource.getTrackedReplyUserId(messageId);
|
||||||
|
User user = userId == null ? null : HidekoBot.getAPI().retrieveUserById(userId).complete();
|
||||||
|
if(user == null)
|
||||||
|
{
|
||||||
|
// if user is not found, consider it expired
|
||||||
|
// (deleted profile, or blocked the bot)
|
||||||
|
databaseSource.untrackExpiredMessage(messageId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
textChannel = user.openPrivateChannel().complete();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
String guildId = databaseSource.getQueuedExpiringMessageGuild(messageId);
|
||||||
|
Guild guild = guildId == null ? null : HidekoBot.getAPI().getGuildById(guildId);
|
||||||
|
if(guild == null)
|
||||||
|
{
|
||||||
|
// if guild is not found, consider it expired
|
||||||
|
// (server was deleted or bot was kicked)
|
||||||
|
databaseSource.untrackExpiredMessage(messageId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
textChannel = guild.getTextChannelById(channelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(textChannel == null)
|
||||||
|
{
|
||||||
|
// if channel is not found, count it as expired
|
||||||
|
// (channel was deleted or bot permissions restricted)
|
||||||
|
databaseSource.untrackExpiredMessage(messageId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RestAction<Message> retrieveAction = textChannel.retrieveMessageById(messageId);
|
||||||
|
|
||||||
|
|
||||||
|
if(Cache.isVerbose()) logger.log("cleaning up: " + messageId);
|
||||||
|
|
||||||
|
retrieveAction.queue(
|
||||||
|
message -> {
|
||||||
|
if(message == null)
|
||||||
|
{
|
||||||
|
databaseSource.untrackExpiredMessage(messageId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<LayoutComponent> components = message.getComponents();
|
||||||
|
List<LayoutComponent> newComponents = new ArrayList<>();
|
||||||
|
for (LayoutComponent component : components)
|
||||||
|
{
|
||||||
|
component = component.asDisabled();
|
||||||
|
newComponents.add(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
message.editMessageComponents(newComponents).queue();
|
||||||
|
databaseSource.untrackExpiredMessage(messageId);
|
||||||
|
},
|
||||||
|
|
||||||
|
(error) -> {
|
||||||
|
databaseSource.untrackExpiredMessage(messageId);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user