Skip to content

Commit 121c02c

Browse files
committed
javadoc and some readability changes
1 parent f9c3dea commit 121c02c

File tree

3 files changed

+234
-118
lines changed

3 files changed

+234
-118
lines changed

application/src/main/java/org/togetherjava/tjbot/features/tophelper/TopHelpersAssignmentRoutine.java

Lines changed: 79 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import net.dv8tion.jda.api.entities.Role;
77
import net.dv8tion.jda.api.entities.UserSnowflake;
88
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
9+
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
910
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
1011
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
1112
import net.dv8tion.jda.api.interactions.components.ComponentInteraction;
@@ -43,7 +44,18 @@
4344
import java.util.stream.Collectors;
4445
import java.util.stream.Stream;
4546

46-
// TODO Javadoc everywhere
47+
/**
48+
* Semi-automatic routine for assigning and announcing Top Helpers of the month.
49+
* <p>
50+
* The routine triggers once on the 1st of each month, but can also be started manually by calling
51+
* {@link #startDialogFor(Guild)}.
52+
* <p>
53+
* The routine starts a dialog in a configured channel, proposing a list of Top Helpers. A user can
54+
* now choose from a list who to reward with the Top Helper role. These users are now assigned the
55+
* configured Top Helper role, while users who had the role in the past have it removed from them.
56+
* Afterward, one can select from the dialog whether a generic announcement that thanks the selected
57+
* Top Helpers should be posted in a configured channel.
58+
*/
4759
public final class TopHelpersAssignmentRoutine implements Routine, UserInteractor {
4860
private static final Logger logger = LoggerFactory.getLogger(TopHelpersAssignmentRoutine.class);
4961
private static final int CHECK_AT_HOUR = 12;
@@ -58,6 +70,12 @@ public final class TopHelpersAssignmentRoutine implements Routine, UserInteracto
5870
private final Predicate<String> announcementChannelNamePredicate;
5971
private final ComponentIdInteractor componentIdInteractor;
6072

73+
/**
74+
* Creates a new instance.
75+
*
76+
* @param config the config to use
77+
* @param service the service to use to compute Top Helpers
78+
*/
6179
public TopHelpersAssignmentRoutine(Config config, TopHelpersService service) {
6280
this.config = config.getTopHelpers();
6381
this.service = service;
@@ -88,19 +106,30 @@ public void acceptComponentIdGenerator(ComponentIdGenerator generator) {
88106

89107
@Override
90108
public Schedule createSchedule() {
109+
// Routine schedules are hour-based. Ensuring it only triggers once per a month is done
110+
// inside the routine as early-check instead.
91111
return Schedule.atFixedHour(CHECK_AT_HOUR);
92112
}
93113

94114
@Override
95115
public void runRoutine(JDA jda) {
96-
int dayOfMonth = Instant.now().atOffset(ZoneOffset.UTC).getDayOfMonth();
97-
if (dayOfMonth != 1) {
116+
if (!isFirstDayOfMonth()) {
98117
return;
99118
}
100119

101120
jda.getGuilds().forEach(this::startDialogFor);
102121
}
103122

123+
private static boolean isFirstDayOfMonth() {
124+
return Instant.now().atOffset(ZoneOffset.UTC).getDayOfMonth() == 1;
125+
}
126+
127+
/**
128+
* Starts a dialog for semi-automatic Top Helper assignment and announcement. See
129+
* {@link TopHelpersAssignmentRoutine} for details.
130+
*
131+
* @param guild to start the dialog and compute Top Helpers for
132+
*/
104133
public void startDialogFor(Guild guild) {
105134
Optional<TextChannel> assignmentChannel = guild.getTextChannelCache()
106135
.stream()
@@ -116,8 +145,8 @@ private void startDialogIn(TextChannel channel) {
116145
Guild guild = channel.getGuild();
117146

118147
TopHelpersService.TimeRange timeRange = TopHelpersService.TimeRange.ofPreviousMonth();
119-
List<TopHelpersService.TopHelperResult> topHelpers = service
120-
.computeTopHelpersDescending(guild.getIdLong(), timeRange.start(), timeRange.end());
148+
List<TopHelpersService.TopHelperStats> topHelpers =
149+
service.computeTopHelpersDescending(guild, timeRange);
121150

122151
if (topHelpers.isEmpty()) {
123152
channel.sendMessage(
@@ -127,24 +156,21 @@ private void startDialogIn(TextChannel channel) {
127156
return;
128157
}
129158

130-
service.retrieveTopHelperMembers(topHelpers, guild)
131-
.onSuccess(members -> sendSelectionMenu(topHelpers, members, timeRange, channel))
132-
.onError(error -> {
133-
logger.warn("Failed to compute top-helpers for automatic assignment", error);
134-
channel.sendMessage("Wanted to assign Top Helpers, but something went wrong.")
135-
.queue();
136-
});
159+
TopHelpersService.retrieveTopHelperMembers(topHelpers, guild).onError(error -> {
160+
logger.warn("Failed to compute top-helpers for automatic assignment", error);
161+
channel.sendMessage("Wanted to assign Top Helpers, but something went wrong.").queue();
162+
}).onSuccess(members -> sendSelectionMenu(topHelpers, members, timeRange, channel));
137163
}
138164

139-
private void sendSelectionMenu(Collection<TopHelpersService.TopHelperResult> topHelpers,
165+
private void sendSelectionMenu(Collection<TopHelpersService.TopHelperStats> topHelpers,
140166
Collection<? extends Member> members, TopHelpersService.TimeRange timeRange,
141-
TextChannel channel) {
167+
MessageChannel channel) {
142168
String content = """
143169
Starting assignment of Top Helpers for %s:
144170
```java
145171
%s
146172
```""".formatted(timeRange.description(),
147-
service.asAsciiTable(topHelpers, members, false));
173+
TopHelpersService.asAsciiTable(topHelpers, members));
148174

149175
StringSelectMenu.Builder menu =
150176
StringSelectMenu.create(componentIdInteractor.generateComponentId())
@@ -168,7 +194,7 @@ private void sendSelectionMenu(Collection<TopHelpersService.TopHelperResult> top
168194
channel.sendMessage(message).queue();
169195
}
170196

171-
private static SelectOption topHelperToSelectOption(TopHelpersService.TopHelperResult topHelper,
197+
private static SelectOption topHelperToSelectOption(TopHelpersService.TopHelperStats topHelper,
172198
@Nullable Member member) {
173199
String id = Long.toString(topHelper.authorId());
174200

@@ -179,19 +205,6 @@ private static SelectOption topHelperToSelectOption(TopHelpersService.TopHelperR
179205
return SelectOption.of(label, id);
180206
}
181207

182-
@Override
183-
public void onButtonClick(ButtonInteractionEvent event, List<String> args) {
184-
event.deferEdit().queue();
185-
String name = args.getFirst();
186-
187-
switch (name) {
188-
case CANCEL_BUTTON_NAME -> endFlow(event, "cancelled");
189-
case NO_MESSAGE_BUTTON_NAME -> endFlow(event, "not posting an announcement");
190-
case YES_MESSAGE_BUTTON_NAME -> prepareAnnouncement(event, args);
191-
default -> throw new AssertionError("Unknown button name: " + name);
192-
}
193-
}
194-
195208
@Override
196209
public void onStringSelectSelection(StringSelectInteractionEvent event, List<String> args) {
197210
Guild guild = Objects.requireNonNull(event.getGuild());
@@ -214,17 +227,16 @@ public void onStringSelectSelection(StringSelectInteractionEvent event, List<Str
214227
}
215228

216229
event.deferEdit().queue();
217-
guild.findMembersWithRoles(List.of(topHelperRole.orElseThrow()))
230+
guild.findMembersWithRoles(List.of(topHelperRole.orElseThrow())).onError(error -> {
231+
logger.warn("Failed to find existing top-helpers for automatic assignment", error);
232+
event.getHook()
233+
.editOriginal(event.getMessage()
234+
+ "\n❌ Sorry, something went wrong trying to find existing top-helpers.")
235+
.setComponents()
236+
.queue();
237+
})
218238
.onSuccess(currentTopHelpers -> manageTopHelperRole(currentTopHelpers,
219-
selectedTopHelperIds, event, topHelperRole.orElseThrow()))
220-
.onError(error -> {
221-
logger.warn("Failed to find existing top-helpers for automatic assignment", error);
222-
event.getHook()
223-
.editOriginal(event.getMessage()
224-
+ "\n❌ Sorry, something went wrong trying to find existing top-helpers.")
225-
.setComponents()
226-
.queue();
227-
});
239+
selectedTopHelperIds, event, topHelperRole.orElseThrow()));
228240
}
229241

230242
private void manageTopHelperRole(Collection<? extends Member> currentTopHelpers,
@@ -275,20 +287,30 @@ private void reportRoleManageSuccess(StringSelectInteractionEvent event) {
275287
.queue();
276288
}
277289

290+
@Override
291+
public void onButtonClick(ButtonInteractionEvent event, List<String> args) {
292+
event.deferEdit().queue();
293+
String name = args.getFirst();
294+
295+
switch (name) {
296+
case YES_MESSAGE_BUTTON_NAME -> prepareAnnouncement(event, args);
297+
case NO_MESSAGE_BUTTON_NAME -> reportFlowFinished(event, "not posting an announcement");
298+
case CANCEL_BUTTON_NAME -> reportFlowFinished(event, "cancelled");
299+
default -> throw new AssertionError("Unknown button name: " + name);
300+
}
301+
}
302+
278303
private void prepareAnnouncement(ButtonInteractionEvent event, List<String> args) {
279304
List<Long> topHelperIds = args.stream().skip(1).map(Long::parseLong).toList();
280305

281-
event.getGuild()
282-
.retrieveMembersByIds(topHelperIds)
283-
.onSuccess(topHelpers -> postAnnouncement(event, topHelpers))
284-
.onError(error -> {
285-
logger.warn("Failed to retrieve top-helper data for automatic assignment", error);
286-
event.getHook()
287-
.editOriginal(event.getMessage()
288-
+ "\n❌ Sorry, something went wrong trying to retrieve top-helper data.")
289-
.setComponents()
290-
.queue();
291-
});
306+
event.getGuild().retrieveMembersByIds(topHelperIds).onError(error -> {
307+
logger.warn("Failed to retrieve top-helper data for automatic assignment", error);
308+
event.getHook()
309+
.editOriginal(event.getMessage()
310+
+ "\n❌ Sorry, something went wrong trying to retrieve top-helper data.")
311+
.setComponents()
312+
.queue();
313+
}).onSuccess(topHelpers -> postAnnouncement(event, topHelpers));
292314
}
293315

294316
private void postAnnouncement(ButtonInteractionEvent event, List<? extends Member> topHelpers) {
@@ -310,7 +332,9 @@ private void postAnnouncement(ButtonInteractionEvent event, List<? extends Membe
310332
return;
311333
}
312334

313-
Collections.shuffle(topHelpers); // for fairness
335+
// For fairness Top Helpers in the announcement are ordered randomly
336+
Collections.shuffle(topHelpers);
337+
314338
String topHelperList = topHelpers.stream()
315339
.map(Member::getAsMention)
316340
.map(mention -> "* " + mention)
@@ -321,12 +345,12 @@ private void postAnnouncement(ButtonInteractionEvent event, List<? extends Membe
321345

322346
announcementChannel.orElseThrow().sendMessage(announcement).queue();
323347

324-
endFlow(event, "posted an announcement");
348+
reportFlowFinished(event, "posted an announcement");
325349
}
326350

327-
private void endFlow(ComponentInteraction event, String message) {
328-
String content = event.getMessage().getContentRaw() + "\n✅ Okay, " + message
329-
+ ". See you next time 👋";
351+
private void reportFlowFinished(ComponentInteraction event, String phrase) {
352+
String content = "%s%n✅ Okay, %s. See you next time 👋"
353+
.formatted(event.getMessage().getContentRaw(), phrase);
330354
event.getHook().editOriginal(content).setComponents().queue();
331355
}
332356
}

application/src/main/java/org/togetherjava/tjbot/features/tophelper/TopHelpersCommand.java

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.togetherjava.tjbot.features.tophelper;
22

3+
import net.dv8tion.jda.api.entities.Guild;
34
import net.dv8tion.jda.api.entities.Member;
45
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
56
import net.dv8tion.jda.api.interactions.callbacks.IDeferrableCallback;
@@ -26,7 +27,8 @@
2627
import java.util.Objects;
2728

2829
/**
29-
* Command that displays the top helpers of a given time range.
30+
* Command that displays the top helpers of a given time range. Can also be used to start a
31+
* semi-automatic flow to assign Top Helpers.
3032
* <p>
3133
* Top helpers are measured by their message length in help channels, as set by
3234
* {@link TopHelpersMessageListener}.
@@ -57,12 +59,12 @@ public TopHelpersCommand(TopHelpersService service,
5759
.forEach(month -> monthData.addChoice(
5860
month.getDisplayName(TextStyle.FULL_STANDALONE, Locale.US), month.name()));
5961

60-
getData().addSubcommands(
61-
new SubcommandData(SUBCOMMAND_SHOW_NAME,
62-
"Lists top helpers for the last month, or a given month")
63-
.addOptions(monthData),
64-
new SubcommandData(SUBCOMMAND_ASSIGN_NAME,
65-
"Automatically assigns top helpers for the last month"));
62+
var showCommand = new SubcommandData(SUBCOMMAND_SHOW_NAME,
63+
"Lists top helpers for the last month, or a given month")
64+
.addOptions(monthData);
65+
var assignCommand = new SubcommandData(SUBCOMMAND_ASSIGN_NAME,
66+
"Automatically assigns top helpers for the last month");
67+
getData().addSubcommands(showCommand, assignCommand);
6668

6769
this.service = service;
6870
this.assignmentRoutine = assignmentRoutine;
@@ -80,11 +82,12 @@ public void onSlashCommand(SlashCommandInteractionEvent event) {
8082

8183
private void showTopHelpers(SlashCommandInteractionEvent event) {
8284
OptionMapping atMonthData = event.getOption(MONTH_OPTION);
85+
Guild guild = Objects.requireNonNull(event.getGuild());
8386

8487
TopHelpersService.TimeRange timeRange =
85-
TopHelpersService.TimeRange.ofMonth(computeMonth(atMonthData));
86-
List<TopHelpersService.TopHelperResult> topHelpers = service.computeTopHelpersDescending(
87-
event.getGuild().getIdLong(), timeRange.start(), timeRange.end());
88+
TopHelpersService.TimeRange.ofPastMonth(computeMonth(atMonthData));
89+
List<TopHelpersService.TopHelperStats> topHelpers =
90+
service.computeTopHelpersDescending(guild, timeRange);
8891

8992
if (topHelpers.isEmpty()) {
9093
event
@@ -95,7 +98,7 @@ private void showTopHelpers(SlashCommandInteractionEvent event) {
9598
}
9699
event.deferReply().queue();
97100

98-
service.retrieveTopHelperMembers(topHelpers, event.getGuild())
101+
TopHelpersService.retrieveTopHelperMembers(topHelpers, guild)
99102
.onError(error -> handleError(error, event))
100103
.onSuccess(members -> handleTopHelpers(topHelpers, members, timeRange, event));
101104
}
@@ -121,15 +124,15 @@ private static void handleError(Throwable error, IDeferrableCallback event) {
121124
event.getHook().editOriginal("Sorry, something went wrong.").queue();
122125
}
123126

124-
private void handleTopHelpers(Collection<TopHelpersService.TopHelperResult> topHelpers,
127+
private void handleTopHelpers(Collection<TopHelpersService.TopHelperStats> topHelpers,
125128
Collection<? extends Member> members, TopHelpersService.TimeRange timeRange,
126129
IDeferrableCallback event) {
127130
String message = """
128131
```java
129132
// for %s
130133
%s
131134
```""".formatted(timeRange.description(),
132-
service.asAsciiTable(topHelpers, members, true));
135+
TopHelpersService.asAsciiTableWithIds(topHelpers, members));
133136
event.getHook().editOriginal(message).queue();
134137
}
135138
}

0 commit comments

Comments
 (0)