Skip to content

Commit b720cb3

Browse files
Merge pull request #386 from Java-Discord/dynxsty/qotw_improvements
Improved QOTW Reviews
2 parents 98a06b6 + 05229fd commit b720cb3

File tree

7 files changed

+143
-28
lines changed

7 files changed

+143
-28
lines changed

src/main/java/net/javadiscord/javabot/data/config/guild/HelpConfig.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,6 @@
1414
@Data
1515
@EqualsAndHashCode(callSuper = true)
1616
public class HelpConfig extends GuildConfigItem {
17-
/**
18-
* The id of the help overview channel.
19-
*/
20-
private Map<String, Long> helpOverviewMessageIds = Map.of();
21-
2217
private long helpForumChannelId = 0;
2318

2419
/**

src/main/java/net/javadiscord/javabot/systems/notification/QOTWNotificationService.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import net.javadiscord.javabot.data.config.SystemsConfig;
99
import net.javadiscord.javabot.systems.qotw.QOTWPointsService;
1010
import net.javadiscord.javabot.systems.qotw.model.QOTWAccount;
11+
import net.javadiscord.javabot.systems.qotw.submissions.SubmissionStatus;
1112
import net.javadiscord.javabot.util.Responses;
1213
import org.jetbrains.annotations.NotNull;
1314
import org.springframework.dao.DataAccessException;
@@ -48,8 +49,19 @@ public void sendAccountIncrementedNotification() {
4849
notificationService.withUser(user).sendDirectMessage(c -> c.sendMessageEmbeds(buildAccountIncrementEmbed(account.getPoints())));
4950
}
5051

51-
public void sendSubmissionDeclinedEmbed() {
52-
notificationService.withUser(user).sendDirectMessage(c -> c.sendMessageEmbeds(buildSubmissionDeclinedEmbed()));
52+
/**
53+
* Sends a "your submission was declined"-notification to the {@link QOTWNotificationService#user}.
54+
*
55+
* @param status The {@link SubmissionStatus}. Must NOT be {@link SubmissionStatus#ACCEPT} or {@link SubmissionStatus#ACCEPT_BEST}
56+
*/
57+
public void sendSubmissionDeclinedEmbed(SubmissionStatus status) {
58+
String reason = switch (status) {
59+
case DECLINE_WRONG_ANSWER -> "Wrong answer";
60+
case DECLINE_TOO_SHORT -> "Too short";
61+
case DECLINE_EMPTY -> "Empty submission";
62+
default -> throw new IllegalArgumentException("Invalid decline status: " + status);
63+
};
64+
notificationService.withUser(user).sendDirectMessage(c -> c.sendMessageEmbeds(buildSubmissionDeclinedEmbed(reason)));
5365
}
5466

5567
private @NotNull EmbedBuilder buildQOTWNotificationEmbed() {
@@ -80,14 +92,14 @@ public void sendSubmissionDeclinedEmbed() {
8092
.build();
8193
}
8294

83-
private @NotNull MessageEmbed buildSubmissionDeclinedEmbed() {
95+
private @NotNull MessageEmbed buildSubmissionDeclinedEmbed(String reason) {
8496
return this.buildQOTWNotificationEmbed()
8597
.setColor(Responses.Type.ERROR.getColor())
8698
.setDescription(String.format("""
8799
Hey %s,
88-
Your QOTW-Submission was **declined**.
100+
Your QOTW-Submission was **declined** for the following reason: `%s`.
89101
However, you can try your luck again next week!""",
90-
user.getAsMention()))
102+
user.getAsMention(), reason))
91103
.build();
92104
}
93105
}

src/main/java/net/javadiscord/javabot/systems/qotw/jobs/QOTWCloseSubmissionsJob.java

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,35 @@
88
import net.dv8tion.jda.api.entities.Message;
99
import net.dv8tion.jda.api.entities.MessageEmbed;
1010
import net.dv8tion.jda.api.entities.MessageHistory;
11+
import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel;
1112
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
1213
import net.dv8tion.jda.api.interactions.components.ActionRow;
1314
import net.dv8tion.jda.api.interactions.components.buttons.Button;
15+
import net.dv8tion.jda.api.interactions.components.selections.SelectOption;
16+
import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu;
1417
import net.dv8tion.jda.api.requests.restaction.ForumPostAction;
1518
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
1619
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
1720
import net.javadiscord.javabot.data.config.BotConfig;
1821
import net.javadiscord.javabot.data.config.GuildConfig;
22+
import net.javadiscord.javabot.data.config.SystemsConfig;
1923
import net.javadiscord.javabot.data.config.guild.QOTWConfig;
2024
import net.javadiscord.javabot.systems.notification.NotificationService;
2125
import net.javadiscord.javabot.systems.qotw.dao.QuestionQueueRepository;
2226
import net.javadiscord.javabot.systems.qotw.model.QOTWQuestion;
27+
import net.javadiscord.javabot.systems.qotw.model.QOTWSubmission;
28+
import net.javadiscord.javabot.systems.qotw.submissions.SubmissionStatus;
2329
import org.jetbrains.annotations.NotNull;
2430
import org.springframework.scheduling.annotation.Scheduled;
2531
import org.springframework.stereotype.Service;
32+
import xyz.dynxsty.dih4jda.util.ComponentIdBuilder;
2633

34+
import javax.annotation.Nonnull;
2735
import java.sql.SQLException;
2836
import java.util.Collections;
2937
import java.util.List;
3038
import java.util.Optional;
3139
import java.util.concurrent.ExecutorService;
32-
import java.util.stream.Collectors;
3340

3441
/**
3542
* Job which disables the Submission button.
@@ -65,15 +72,22 @@ public void execute() throws SQLException {
6572
questionMessage.editMessageComponents(ActionRow.of(Button.secondary("qotw-submission:closed", "Submissions closed").asDisabled())).queue();
6673
notificationService.withGuild(guild)
6774
.sendToModerationLog(log ->
68-
log.sendMessageFormat("%s%nIt's review time! There are **%s** threads to review:%n%n%s",
75+
log.sendMessageFormat("%s%nIt's review time! There are **%s** threads to review",
6976
qotwConfig.getQOTWReviewRole().getAsMention(),
70-
qotwConfig.getSubmissionChannel().getThreadChannels().size(),
71-
qotwConfig.getSubmissionChannel().getThreadChannels().stream()
72-
.map(t -> "> %s (%d message(s))".formatted(t.getAsMention(), t.getMessageCount() - 2)) // NOTE: Subtract 2 messages which are send by the bot
73-
.collect(Collectors.joining("\n")))
77+
qotwConfig.getSubmissionChannel().getThreadChannels().size())
7478
);
75-
qotwConfig.getSubmissionChannel().getThreadChannels().forEach(t ->
76-
t.getManager().setName(SUBMISSION_PENDING + t.getName()).queue());
79+
for (ThreadChannel submission : qotwConfig.getSubmissionChannel().getThreadChannels()) {
80+
submission.getManager().setName(SUBMISSION_PENDING + submission.getName()).queue();
81+
// remove the author
82+
final QOTWSubmission s = new QOTWSubmission(submission);
83+
s.retrieveAuthor(author -> {
84+
submission.removeThreadMember(author).queue();
85+
notificationService.withGuild(guild).sendToModerationLog(log ->
86+
log.sendMessage("%s by %s".formatted(submission.getAsMention(), author.getAsMention()))
87+
.addActionRow(buildSubmissionSelectMenu(jda, submission.getIdLong()))
88+
);
89+
});
90+
}
7791
if (qotwConfig.getSubmissionsForumChannel() == null) continue;
7892
asyncPool.execute(() -> {
7993
MessageEmbed embed = questionMessage.getEmbeds().get(0);
@@ -94,6 +108,29 @@ public void execute() throws SQLException {
94108
}
95109
}
96110

111+
private @Nonnull StringSelectMenu buildSubmissionSelectMenu(JDA jda, long threadId) {
112+
final SystemsConfig.EmojiConfig emojiConfig = botConfig.getSystems().getEmojiConfig();
113+
return StringSelectMenu.create(ComponentIdBuilder.build("qotw-submission-select", "review", threadId))
114+
.setPlaceholder("Select an action for this submission")
115+
.addOptions(
116+
SelectOption.of("Accept (Best Answer)", SubmissionStatus.ACCEPT_BEST.name())
117+
.withDescription("The submission is correct and is considered to be among the \"best answers\"")
118+
.withEmoji(emojiConfig.getSuccessEmote(jda)),
119+
SelectOption.of("Accept", SubmissionStatus.ACCEPT.name())
120+
.withDescription("The overall submission is correct")
121+
.withEmoji(emojiConfig.getSuccessEmote(jda)),
122+
SelectOption.of("Decline: Wrong Answer", SubmissionStatus.DECLINE_WRONG_ANSWER.name())
123+
.withDescription("The submission is simply wrong or falsely explained")
124+
.withEmoji(emojiConfig.getFailureEmote(jda)),
125+
SelectOption.of("Decline: Too short", SubmissionStatus.DECLINE_TOO_SHORT.name())
126+
.withDescription("The submission is way to short in comparison to other submissions")
127+
.withEmoji(emojiConfig.getFailureEmote(jda)),
128+
SelectOption.of("Decline: Empty Submission", SubmissionStatus.DECLINE_EMPTY.name())
129+
.withDescription("The submission was empty")
130+
.withEmoji(emojiConfig.getFailureEmote(jda))
131+
).build();
132+
}
133+
97134
private @NotNull MessageEmbed buildQuestionEmbed(@NotNull QOTWQuestion question) {
98135
return new EmbedBuilder()
99136
.setTitle("Question of the Week #" + question.getQuestionNumber())

src/main/java/net/javadiscord/javabot/systems/qotw/submissions/SubmissionInteractionManager.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package net.javadiscord.javabot.systems.qotw.submissions;
22

3+
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
4+
import xyz.dynxsty.dih4jda.interactions.components.StringSelectMenuHandler;
35
import xyz.dynxsty.dih4jda.util.ComponentIdBuilder;
46
import xyz.dynxsty.dih4jda.interactions.components.ButtonHandler;
57

@@ -13,14 +15,15 @@
1315
import net.javadiscord.javabot.systems.qotw.dao.QuestionQueueRepository;
1416
import org.jetbrains.annotations.NotNull;
1517

18+
import java.util.List;
1619
import java.util.concurrent.ExecutorService;
1720

1821
/**
1922
* Handles all interactions regarding the QOTW Submission System.
2023
*/
2124
@AutoDetectableComponentHandler({"qotw-submission","qotw-submission-select"})
2225
@RequiredArgsConstructor
23-
public class SubmissionInteractionManager implements ButtonHandler {
26+
public class SubmissionInteractionManager implements ButtonHandler, StringSelectMenuHandler {
2427
private final QOTWPointsService pointsService;
2528
private final NotificationService notificationService;
2629
private final BotConfig botConfig;
@@ -36,4 +39,13 @@ public void handleButton(@NotNull ButtonInteractionEvent event, Button button) {
3639
case "delete" -> manager.handleThreadDeletion(event);
3740
}
3841
}
42+
43+
@Override
44+
public void handleStringSelectMenu(@NotNull StringSelectInteractionEvent event, @NotNull List<String> values) {
45+
SubmissionManager manager = new SubmissionManager(botConfig.get(event.getGuild()).getQotwConfig(), pointsService, questionQueueRepository, notificationService, asyncPool);
46+
String[] id = ComponentIdBuilder.split(event.getComponentId());
47+
switch (id[1]) {
48+
case "review" -> manager.handleSelectReview(event, id[2]);
49+
}
50+
}
3951
}

src/main/java/net/javadiscord/javabot/systems/qotw/submissions/SubmissionManager.java

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import net.dv8tion.jda.api.entities.channel.ChannelType;
1212
import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel;
1313
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
14+
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
1415
import net.dv8tion.jda.api.interactions.InteractionHook;
1516
import net.dv8tion.jda.api.interactions.components.ActionRow;
1617
import net.dv8tion.jda.api.interactions.components.buttons.Button;
@@ -99,6 +100,52 @@ public List<QOTWSubmission> getActiveSubmissions() {
99100
.map(QOTWSubmission::new).toList();
100101
}
101102

103+
/**
104+
* Handles a submission review using a {@link net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu}.
105+
*
106+
* @param event The {@link StringSelectInteractionEvent} that was fired.
107+
* @param threadId The submissions' thread-id.
108+
*/
109+
public void handleSelectReview(StringSelectInteractionEvent event, String threadId) {
110+
if (event.getGuild() == null) {
111+
Responses.replyGuildOnly(event).queue();
112+
return;
113+
}
114+
final ThreadChannel submissionThread = event.getGuild().getThreadChannelById(threadId);
115+
if (submissionThread == null) {
116+
Responses.error(event, "Could not find submission thread!").queue();
117+
return;
118+
}
119+
if (submissionThread.getParentChannel().getIdLong() != config.getSubmissionChannelId()) {
120+
Responses.error(event, "The selected thread is not a submission channel!").queue();
121+
return;
122+
}
123+
if (event.getValues().size() != 1) {
124+
Responses.error(event, "Please select an action!").queue();
125+
return;
126+
}
127+
final SubmissionStatus status = SubmissionStatus.valueOf(event.getValues().get(0));
128+
event.deferReply().queue();
129+
final QOTWSubmission submission = new QOTWSubmission(submissionThread);
130+
submission.retrieveAuthor(author -> {
131+
switch (status) {
132+
case ACCEPT_BEST -> acceptSubmission(event.getHook(), submissionThread, author, true);
133+
case ACCEPT -> acceptSubmission(event.getHook(), submissionThread, author, false);
134+
default -> declineSubmission(event.getHook(), submissionThread, author, status);
135+
}
136+
if (config.getSubmissionChannel().getThreadChannels().size() <= 1) {
137+
Optional<ThreadChannel> newestPostOptional = config.getSubmissionsForumChannel().getThreadChannels()
138+
.stream().max(Comparator.comparing(ThreadChannel::getTimeCreated));
139+
newestPostOptional.ifPresent(p -> {
140+
p.getManager().setAppliedTags().queue();
141+
notificationService.withGuild(config.getGuild()).sendToModerationLog(log -> log.sendMessageFormat("All submissions have been reviewed!"));
142+
});
143+
}
144+
event.getMessage().editMessageComponents(ActionRow.of(Button.secondary("dummy", "%s by %s".formatted(status.getVerb(), event.getUser().getAsTag())))).queue();
145+
Responses.info(event, "Review done!", "Successfully reviewed %s! (`%s`)", submissionThread.getAsMention(), status).queue();
146+
});
147+
}
148+
102149
/**
103150
* Handles the "Delete Submission" Button.
104151
*
@@ -151,10 +198,10 @@ public void acceptSubmission(InteractionHook hook, @NotNull ThreadChannel thread
151198
boolean lastMessage = messages.indexOf(message) + 1 == messages.size();
152199
if (message.getAuthor().isBot() || message.getType() != MessageType.DEFAULT) continue;
153200
if (message.getContentRaw().length() > 2000) {
154-
WebhookUtil.mirrorMessageToWebhook(wh, message, message.getContentRaw().substring(0, 2000), newestPost.getIdLong(), null, null);
155-
WebhookUtil.mirrorMessageToWebhook(wh, message, message.getContentRaw().substring(2000), newestPost.getIdLong(), null, lastMessage ? List.of(buildAuthorEmbed(author, bestAnswer)) : null);
201+
WebhookUtil.mirrorMessageToWebhook(wh, message, message.getContentRaw().substring(0, 2000), newestPost.getIdLong(), null, null).join();
202+
WebhookUtil.mirrorMessageToWebhook(wh, message, message.getContentRaw().substring(2000), newestPost.getIdLong(), null, lastMessage ? List.of(buildAuthorEmbed(author, bestAnswer)) : null).join();
156203
} else {
157-
WebhookUtil.mirrorMessageToWebhook(wh, message, message.getContentRaw(), newestPost.getIdLong(), null, lastMessage ? List.of(buildAuthorEmbed(author, bestAnswer)) : null);
204+
WebhookUtil.mirrorMessageToWebhook(wh, message, message.getContentRaw(), newestPost.getIdLong(), null, lastMessage ? List.of(buildAuthorEmbed(author, bestAnswer)) : null).join();
158205
}
159206
}
160207
}));
@@ -168,12 +215,13 @@ public void acceptSubmission(InteractionHook hook, @NotNull ThreadChannel thread
168215
* @param hook The {@link net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent} that was fired.
169216
* @param thread The submission's {@link ThreadChannel}.
170217
* @param author The submissions' author.
218+
* @param status The {@link SubmissionStatus}.
171219
*/
172-
public void declineSubmission(InteractionHook hook, @NotNull ThreadChannel thread, User author) {
220+
public void declineSubmission(InteractionHook hook, @NotNull ThreadChannel thread, User author, SubmissionStatus status) {
173221
thread.getManager().setName(SUBMISSION_DECLINED + thread.getName().substring(1)).queue();
174-
notificationService.withQOTW(thread.getGuild(), author).sendSubmissionDeclinedEmbed();
222+
notificationService.withQOTW(thread.getGuild(), author).sendSubmissionDeclinedEmbed(status);
175223
Responses.success(hook, "Submission Declined", "Successfully declined submission by " + author.getAsMention()).queue();
176-
notificationService.withQOTW(thread.getGuild()).sendSubmissionActionNotification(author, new QOTWSubmission(thread), SubmissionStatus.DECLINE);
224+
notificationService.withQOTW(thread.getGuild()).sendSubmissionActionNotification(author, new QOTWSubmission(thread), status);
177225
thread.getManager().setLocked(true).setArchived(true).queue();
178226
}
179227

src/main/java/net/javadiscord/javabot/systems/qotw/submissions/SubmissionStatus.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,17 @@ public enum SubmissionStatus {
1313
*/
1414
ACCEPT("accepted"),
1515
/**
16-
* The submission got declined.
16+
* The submission got declined as it was simply wrong.
1717
*/
18-
DECLINE("declined");
18+
DECLINE_WRONG_ANSWER("declined (wrong answer)"),
19+
/**
20+
* The submission got declined as it was too short compared to other submissions.
21+
*/
22+
DECLINE_TOO_SHORT("declined (too short)"),
23+
/**
24+
* The submission got declined as it was simply empty.
25+
*/
26+
DECLINE_EMPTY("declined (empty)");
1927

2028
private final String verb;
2129

src/main/java/net/javadiscord/javabot/systems/qotw/submissions/subcommands/QOTWReviewSubcommand.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import net.javadiscord.javabot.systems.qotw.dao.QuestionQueueRepository;
1515
import net.javadiscord.javabot.systems.qotw.model.QOTWSubmission;
1616
import net.javadiscord.javabot.systems.qotw.submissions.SubmissionManager;
17+
import net.javadiscord.javabot.systems.qotw.submissions.SubmissionStatus;
1718
import net.javadiscord.javabot.util.Responses;
1819
import org.jetbrains.annotations.NotNull;
1920
import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand;
@@ -82,7 +83,9 @@ public void execute(@NotNull SlashCommandInteractionEvent event) {
8283
if (state.contains("ACCEPT")) {
8384
manager.acceptSubmission(event.getHook(), submissionThread, author, state.equals("ACCEPT_BEST"));
8485
} else {
85-
manager.declineSubmission(event.getHook(), submissionThread, author);
86+
// just do a "wrong answer" for now. this command is going to be removed
87+
// in the near future anyway
88+
manager.declineSubmission(event.getHook(), submissionThread, author, SubmissionStatus.DECLINE_WRONG_ANSWER);
8689
}
8790
if (qotwConfig.getSubmissionChannel().getThreadChannels().size() <= 1) {
8891
Optional<ThreadChannel> newestPostOptional = qotwConfig.getSubmissionsForumChannel().getThreadChannels()

0 commit comments

Comments
 (0)