Skip to content

Commit 55a2d3b

Browse files
Merge pull request #338 from danthe1st/qotw-view
Add /qotw-view command
2 parents 667da34 + b29f2fd commit 55a2d3b

File tree

12 files changed

+439
-34
lines changed

12 files changed

+439
-34
lines changed

src/main/java/net/javadiscord/javabot/Bot.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import net.javadiscord.javabot.systems.moderation.report.ReportManager;
2828
import net.javadiscord.javabot.systems.moderation.server_lock.ServerLockManager;
2929
import net.javadiscord.javabot.systems.qotw.commands.questions_queue.AddQuestionSubcommand;
30+
import net.javadiscord.javabot.systems.qotw.commands.view.QOTWQuerySubcommand;
3031
import net.javadiscord.javabot.systems.qotw.submissions.SubmissionInteractionManager;
3132
import net.javadiscord.javabot.systems.staff_commands.self_roles.SelfRoleInteractionManager;
3233
import net.javadiscord.javabot.systems.staff_commands.embeds.AddEmbedFieldSubcommand;
@@ -167,7 +168,8 @@ private static void addComponentHandler(@NotNull DIH4JDA dih4jda) {
167168
List.of("resolve-report"), new ReportManager(),
168169
List.of("self-role"), new SelfRoleInteractionManager(),
169170
List.of("qotw-submission"), new SubmissionInteractionManager(),
170-
List.of("help-channel", "help-thank"), new HelpChannelInteractionManager()
171+
List.of("help-channel", "help-thank"), new HelpChannelInteractionManager(),
172+
List.of("qotw-list-questions"),new QOTWQuerySubcommand()
171173
));
172174
dih4jda.addModalHandlers(Map.of(
173175
List.of("qotw-add-question"), new AddQuestionSubcommand(),

src/main/java/net/javadiscord/javabot/systems/qotw/QOTWUserReminderJob.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class QOTWUserReminderJob extends DiscordApiJob {
2525
protected void execute(JobExecutionContext context, @NotNull JDA jda) throws JobExecutionException {
2626
for (Guild guild : jda.getGuilds()) {
2727
QOTWConfig config = Bot.getConfig().get(guild).getQotwConfig();
28-
List<QOTWSubmission> submissions = new SubmissionManager(config).getActiveSubmissionThreads();
28+
List<QOTWSubmission> submissions = new SubmissionManager(config).getActiveSubmissionThreads(guild.getIdLong());
2929
for (QOTWSubmission submission : submissions) {
3030
UserPreferenceManager manager = new UserPreferenceManager(Bot.getDataSource());
3131
UserPreference preference = manager.getOrCreate(submission.getAuthorId(), Preference.QOTW_REMINDER);

src/main/java/net/javadiscord/javabot/systems/qotw/commands/QOTWCommand.java renamed to src/main/java/net/javadiscord/javabot/systems/qotw/commands/QOTWAdminCommand.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515
import java.util.Set;
1616

1717
/**
18-
* Represents the `/qotw` command. This holds administrative commands for managing the Question of the Week.
18+
* Represents the `/qotw-admin` command. This holds administrative commands for managing the Question of the Week.
1919
*/
20-
public class QOTWCommand extends SlashCommand {
20+
public class QOTWAdminCommand extends SlashCommand {
2121
/**
2222
* This classes constructor which sets the {@link net.dv8tion.jda.api.interactions.commands.build.SlashCommandData} and
2323
* adds the corresponding {@link net.dv8tion.jda.api.interactions.commands.Command.SubcommandGroup}s.
2424
*/
25-
public QOTWCommand() {
26-
setSlashCommandData(Commands.slash("qotw", "Administrative tools for managing the Question of the Week.")
25+
public QOTWAdminCommand() {
26+
setSlashCommandData(Commands.slash("qotw-admin", "Administrative tools for managing the Question of the Week.")
2727
.setDefaultPermissions(DefaultMemberPermissions.DISABLED)
2828
.setGuildOnly(true)
2929
);
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package net.javadiscord.javabot.systems.qotw.commands.view;
2+
3+
import java.util.List;
4+
import java.util.stream.Collectors;
5+
6+
import com.dynxsty.dih4jda.interactions.commands.SlashCommand;
7+
8+
import net.dv8tion.jda.api.EmbedBuilder;
9+
import net.dv8tion.jda.api.entities.TextChannel;
10+
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
11+
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
12+
import net.dv8tion.jda.api.interactions.commands.OptionType;
13+
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
14+
import net.javadiscord.javabot.Bot;
15+
import net.javadiscord.javabot.data.h2db.DbActions;
16+
import net.javadiscord.javabot.systems.qotw.submissions.SubmissionStatus;
17+
import net.javadiscord.javabot.systems.qotw.submissions.dao.QOTWSubmissionRepository;
18+
import net.javadiscord.javabot.systems.qotw.submissions.model.QOTWSubmission;
19+
import net.javadiscord.javabot.systems.qotw.submissions.subcommands.MarkBestAnswerSubcommand;
20+
import net.javadiscord.javabot.util.Responses;
21+
22+
/**
23+
* Represents the `/qotw-view list-answers` subcommand. It allows for listing answers to a specific QOTW.
24+
*/
25+
public class QOTWListAnswersSubcommand extends SlashCommand.Subcommand{
26+
public QOTWListAnswersSubcommand() {
27+
setSubcommandData(new SubcommandData("list-answers", "Lists answers to (previous) questions of the week")
28+
.addOption(OptionType.INTEGER, "question", "The question number",true));
29+
}
30+
31+
@Override
32+
public void execute(SlashCommandInteractionEvent event) {
33+
if (!event.isFromGuild()) {
34+
Responses.replyGuildOnly(event).setEphemeral(true).queue();
35+
return;
36+
}
37+
OptionMapping questionNumOption = event.getOption("question");
38+
if (questionNumOption == null) {
39+
Responses.replyMissingArguments(event);
40+
return;
41+
}
42+
int questionNum = questionNumOption.getAsInt();
43+
44+
event.deferReply(true).queue();
45+
DbActions.doAsyncDaoAction(QOTWSubmissionRepository::new, repo -> {
46+
List<QOTWSubmission> submissions = repo.getSubmissionsByQuestionNumber(event.getGuild().getIdLong(), questionNum);
47+
EmbedBuilder eb = new EmbedBuilder()
48+
.setDescription("**Answers of Question of the week #" + questionNum + "**\n")
49+
.setColor(Responses.Type.DEFAULT.getColor())
50+
.setFooter("Results may not be accurate due to historic data.");
51+
TextChannel submissionChannel = Bot.getConfig().get(event.getGuild()).getQotwConfig().getSubmissionChannel();
52+
String allAnswers = submissions
53+
.stream()
54+
.filter(submission -> isSubmissionVisible(submission, event.getUser().getIdLong()))
55+
.filter(submission -> submission.getQuestionNumber() == questionNum)
56+
.map(s -> (isBestAnswer(submissionChannel,s) ?
57+
"best " : s.getStatus() == SubmissionStatus.ACCEPTED ? "accepted " : "")+
58+
"Answer by <@" + s.getAuthorId() + ">")
59+
.collect(Collectors.joining("\n"));
60+
if (allAnswers.isEmpty()) {
61+
allAnswers = "No accepted answers found.";
62+
}
63+
eb.appendDescription(allAnswers);
64+
event.getHook().sendMessageEmbeds(eb.build()).queue();
65+
});
66+
}
67+
68+
/**
69+
* Checks whether a submission is visible to a specific user.
70+
* @param submission the {@link QOTWSubmission} to be checked
71+
* @param viewerID the user to check against
72+
* @return {@code true} if the submission is visible, else {@code false}}
73+
*/
74+
public static boolean isSubmissionVisible(QOTWSubmission submission, long viewerID) {
75+
return submission.getStatus() == SubmissionStatus.ACCEPTED || submission.getAuthorId() == viewerID;
76+
}
77+
78+
private boolean isBestAnswer(TextChannel submissionChannel, QOTWSubmission submission) {
79+
return submissionChannel
80+
.retrieveArchivedPrivateThreadChannels()
81+
.stream()
82+
.filter(t -> t.getIdLong() == submission.getThreadId())
83+
.findAny()
84+
.map(t -> MarkBestAnswerSubcommand.isSubmissionThreadABestAnswer(t))
85+
.orElse(false);
86+
}
87+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package net.javadiscord.javabot.systems.qotw.commands.view;
2+
3+
import java.sql.SQLException;
4+
import java.util.Comparator;
5+
import java.util.List;
6+
7+
import javax.annotation.Nonnull;
8+
9+
import org.jetbrains.annotations.NotNull;
10+
11+
import com.dynxsty.dih4jda.interactions.ComponentIdBuilder;
12+
import com.dynxsty.dih4jda.interactions.commands.SlashCommand;
13+
import com.dynxsty.dih4jda.interactions.components.ButtonHandler;
14+
15+
import net.dv8tion.jda.api.EmbedBuilder;
16+
import net.dv8tion.jda.api.entities.MessageEmbed;
17+
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
18+
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
19+
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
20+
import net.dv8tion.jda.api.interactions.commands.OptionType;
21+
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
22+
import net.dv8tion.jda.api.interactions.components.ActionRow;
23+
import net.dv8tion.jda.api.interactions.components.buttons.Button;
24+
import net.javadiscord.javabot.data.h2db.DbActions;
25+
import net.javadiscord.javabot.data.h2db.DbHelper;
26+
import net.javadiscord.javabot.systems.qotw.dao.QuestionQueueRepository;
27+
import net.javadiscord.javabot.systems.qotw.model.QOTWQuestion;
28+
import net.javadiscord.javabot.util.Responses;
29+
30+
/**
31+
* Represents the `/qotw-view query` subcommand. It allows for listing filtering QOTWs.
32+
*/
33+
public class QOTWQuerySubcommand extends SlashCommand.Subcommand implements ButtonHandler{
34+
35+
private static final int MAX_BUTTON_QUERY_LENGTH = 10;
36+
private static final int PAGE_LIMIT = 20;
37+
38+
/**
39+
* The constructor of this class, which sets the corresponding {@link SubcommandData}.
40+
*/
41+
public QOTWQuerySubcommand() {
42+
setSubcommandData(new SubcommandData("list-questions", "Lists previous questions of the week")
43+
.addOption(OptionType.STRING, "query", "Only queries questions that contain a specific query", false)
44+
.addOption(OptionType.INTEGER, "page", "The page to show, starting with 1", false));
45+
}
46+
47+
@Override
48+
public void execute(SlashCommandInteractionEvent event) {
49+
if(!event.isFromGuild()) {
50+
Responses.replyGuildOnly(event).setEphemeral(true).queue();
51+
return;
52+
}
53+
String query = event.getOption("query", "", OptionMapping::getAsString);
54+
int page = event.getOption("page", 1, OptionMapping::getAsInt) -1;
55+
if (page < 0) {
56+
Responses.error(event, "Invalid page - must be >= 1").queue();
57+
return;
58+
}
59+
event.deferReply(true).queue();
60+
DbActions.doAsyncDaoAction(QuestionQueueRepository::new, repo-> {
61+
MessageEmbed embed = buildListQuestionsEmbed(repo, event.getGuild().getIdLong(), query, page);
62+
event.getHook()
63+
.sendMessageEmbeds(embed)
64+
.addActionRows(buildPageControls(query, page, embed))
65+
.queue();
66+
});
67+
}
68+
69+
@Override
70+
public void handleButton(@Nonnull ButtonInteractionEvent event, @Nonnull Button button) {
71+
event.deferEdit().queue();
72+
String[] id = ComponentIdBuilder.split(event.getComponentId());
73+
int page = Integer.parseInt(id[1]);
74+
String query = id.length == 2 ? "" : id[2];
75+
if (page < 0) {
76+
Responses.error(event.getHook(), "Invalid page - must be >= 1").queue();
77+
return;
78+
}
79+
DbHelper.doDaoAction(QuestionQueueRepository::new, repo -> {
80+
MessageEmbed embed = buildListQuestionsEmbed(repo, event.getGuild().getIdLong(), query, page);
81+
event.getHook()
82+
.editOriginalEmbeds(embed)
83+
.setActionRows(buildPageControls(query, page, embed))
84+
.queue();
85+
});
86+
}
87+
88+
@NotNull
89+
private ActionRow buildPageControls(String query, int page, MessageEmbed embed) {
90+
if (query.length() > MAX_BUTTON_QUERY_LENGTH) {
91+
query = query.substring(0,MAX_BUTTON_QUERY_LENGTH);
92+
}
93+
return ActionRow.of(
94+
Button.primary(ComponentIdBuilder.build("qotw-list-questions", page - 1 + "", query), "Previous Page")
95+
.withDisabled(page <= 0),
96+
Button.primary(ComponentIdBuilder.build("qotw-list-questions", page + 1 + "", query), "Next Page")
97+
.withDisabled(embed.getFields().size() < PAGE_LIMIT)
98+
);
99+
}
100+
101+
private MessageEmbed buildListQuestionsEmbed(QuestionQueueRepository repo, long guildId, String query, int page) throws SQLException {
102+
List<QOTWQuestion> questions = repo.getUsedQuestionsWithQuery(guildId, query, page*PAGE_LIMIT, PAGE_LIMIT);
103+
EmbedBuilder eb = new EmbedBuilder();
104+
eb.setDescription("**Questions of the week"+(query.isEmpty()?"":" matching '"+query+"'")+"**");
105+
questions
106+
.stream()
107+
.sorted(Comparator.comparingInt(QOTWQuestion::getQuestionNumber))
108+
.map(q -> new MessageEmbed.Field("Question #" + q.getQuestionNumber(), q.getText(), true))
109+
.forEach(eb::addField);
110+
if(eb.getFields().isEmpty()) {
111+
eb.appendDescription("\nNo questions found");
112+
if(page != 0) {
113+
eb.appendDescription(" on this page");
114+
}
115+
}
116+
eb.setFooter("Page " + (page+1));
117+
eb.setColor(Responses.Type.DEFAULT.getColor());
118+
return eb.build();
119+
}
120+
121+
122+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package net.javadiscord.javabot.systems.qotw.commands.view;
2+
3+
import org.jetbrains.annotations.NotNull;
4+
5+
import com.dynxsty.dih4jda.interactions.commands.SlashCommand;
6+
import net.dv8tion.jda.api.EmbedBuilder;
7+
import net.dv8tion.jda.api.entities.MessageEmbed;
8+
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
9+
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
10+
import net.dv8tion.jda.api.interactions.commands.OptionType;
11+
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
12+
import net.javadiscord.javabot.Bot;
13+
import net.javadiscord.javabot.data.h2db.DbActions;
14+
import net.javadiscord.javabot.systems.qotw.submissions.dao.QOTWSubmissionRepository;
15+
import net.javadiscord.javabot.systems.qotw.submissions.model.QOTWSubmission;
16+
import net.javadiscord.javabot.util.MessageActionUtils;
17+
import net.javadiscord.javabot.util.Responses;
18+
19+
/**
20+
* Represents the `/qotw-view answer` subcommand. It allows for viewing an answer to a QOTW.
21+
*/
22+
public class QOTWViewAnswerSubcommand extends SlashCommand.Subcommand{
23+
24+
/**
25+
* The constructor of this class, which sets the corresponding {@link SubcommandData}.
26+
*/
27+
public QOTWViewAnswerSubcommand() {
28+
setSubcommandData(new SubcommandData("answer", "Views the content of an answer to the Question of the Week")
29+
.addOption(OptionType.INTEGER, "question", "The question number the answer has been submitted to", true)
30+
.addOption(OptionType.USER, "answerer", "The user who answered the question", true));
31+
}
32+
33+
@Override
34+
public void execute(SlashCommandInteractionEvent event) {
35+
if (!event.isFromGuild()) {
36+
Responses.replyGuildOnly(event).setEphemeral(true).queue();
37+
return;
38+
}
39+
OptionMapping questionOption = event.getOption("question");
40+
if (questionOption == null) {
41+
Responses.replyMissingArguments(event);
42+
return;
43+
}
44+
OptionMapping answerOwnerOption = event.getOption("answerer");
45+
if (answerOwnerOption == null) {
46+
Responses.error(event, "The answerer option is missing.").queue();
47+
return;
48+
}
49+
event.deferReply(true).queue();
50+
DbActions.doAsyncDaoAction(QOTWSubmissionRepository::new, repo -> {
51+
QOTWSubmission submission = repo.getSubmissionByQuestionNumberAndAuthorID(event.getGuild().getIdLong(), questionOption.getAsInt(), answerOwnerOption.getAsUser().getIdLong());
52+
if (submission == null || !QOTWListAnswersSubcommand.isSubmissionVisible(submission, event.getUser().getIdLong())) {
53+
Responses.error(event.getHook(), "No answer to the question was found from the specific user.").queue();
54+
return;
55+
}
56+
Bot.getConfig().get(event.getGuild()).getQotwConfig()
57+
.getSubmissionChannel().retrieveArchivedPrivateThreadChannels().queue(threadChannels->{
58+
threadChannels
59+
.stream()
60+
.filter(c->c.getIdLong() == submission.getThreadId())
61+
.findAny()
62+
.ifPresentOrElse(submissionChannel->
63+
submissionChannel.getHistoryFromBeginning(100).queue(history->
64+
MessageActionUtils.copyMessagesToNewThread(event.getGuildChannel().asStandardGuildMessageChannel(),
65+
buildQOTWInfoEmbed(submission, event.getMember()==null?event.getUser().getName():event.getMember().getEffectiveName()),
66+
"QOTW #"+submission.getQuestionNumber(),
67+
history.getRetrievedHistory(),
68+
() -> Responses.success(event.getHook(), "View Answer", "Answer copied successfully").setEphemeral(true))),
69+
() -> Responses.error(event.getHook(), "The QOTW submission thread was not found.").queue());
70+
});
71+
});
72+
}
73+
74+
private @NotNull MessageEmbed buildQOTWInfoEmbed(QOTWSubmission submission, String requester) {
75+
return new EmbedBuilder()
76+
.setTitle("Answer to Question of the Week #" + submission.getQuestionNumber())
77+
.setDescription("Answer by <@" + submission.getAuthorId() + ">")
78+
.setFooter("Requested by " + requester)
79+
.setColor(Responses.Type.DEFAULT.getColor())
80+
.build();
81+
}
82+
83+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package net.javadiscord.javabot.systems.qotw.commands.view;
2+
3+
import com.dynxsty.dih4jda.interactions.commands.SlashCommand;
4+
5+
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
6+
import net.dv8tion.jda.api.interactions.commands.build.Commands;
7+
8+
/**
9+
* Represents the `/qotw-view` command.
10+
* It allows to view previous QOTWs and their answers.
11+
*/
12+
public class QOTWViewCommand extends SlashCommand{
13+
/**
14+
* This classes constructor which sets the {@link net.dv8tion.jda.api.interactions.commands.build.SlashCommandData} and
15+
* adds the corresponding {@link net.dv8tion.jda.api.interactions.commands.Command.SubcommandGroup}s.
16+
*/
17+
public QOTWViewCommand() {
18+
setSlashCommandData(Commands.slash("qotw-view", "Query questions of the week and their answers")
19+
.setDefaultPermissions(DefaultMemberPermissions.ENABLED)
20+
.setGuildOnly(true));
21+
addSubcommands(new QOTWQuerySubcommand(), new QOTWListAnswersSubcommand(), new QOTWViewAnswerSubcommand());
22+
23+
}
24+
}

0 commit comments

Comments
 (0)