99import net .dv8tion .jda .api .events .interaction .command .SlashCommandInteractionEvent ;
1010import net .dv8tion .jda .api .interactions .callbacks .IDeferrableCallback ;
1111import net .dv8tion .jda .api .interactions .callbacks .IReplyCallback ;
12+ import net .dv8tion .jda .api .interactions .commands .OptionMapping ;
13+ import net .dv8tion .jda .api .interactions .commands .OptionType ;
14+ import net .dv8tion .jda .api .interactions .commands .build .OptionData ;
1215import org .jetbrains .annotations .NotNull ;
1316import org .jetbrains .annotations .Nullable ;
1417import org .jooq .Records ;
2023import org .togetherjava .tjbot .config .Config ;
2124import org .togetherjava .tjbot .db .Database ;
2225
23- import java .time .Instant ;
24- import java .time .ZoneOffset ;
25- import java .time .ZonedDateTime ;
26+ import java .time .*;
2627import java .time .format .TextStyle ;
27- import java .time .temporal .TemporalAdjusters ;
28- import java .util .Collection ;
29- import java .util .List ;
30- import java .util .Locale ;
31- import java .util .Map ;
28+ import java .util .*;
3229import java .util .function .Function ;
3330import java .util .function .IntFunction ;
3431import java .util .function .Predicate ;
4744public final class TopHelpersCommand extends SlashCommandAdapter {
4845 private static final Logger logger = LoggerFactory .getLogger (TopHelpersCommand .class );
4946 private static final String COMMAND_NAME = "top-helpers" ;
47+ private static final String MONTH_OPTION = "at-month" ;
5048 private static final int TOP_HELPER_LIMIT = 20 ;
5149
5250 private final Database database ;
@@ -59,8 +57,16 @@ public final class TopHelpersCommand extends SlashCommandAdapter {
5957 * @param config the config to use for this
6058 */
6159 public TopHelpersCommand (@ NotNull Database database , @ NotNull Config config ) {
62- super (COMMAND_NAME , "Lists top helpers for the last month" , SlashCommandVisibility .GUILD );
63- // TODO Add options to optionally pick a time range once JDA/Discord offers a date-picker
60+ super (COMMAND_NAME , "Lists top helpers for the last month, or a given month" ,
61+ SlashCommandVisibility .GUILD );
62+
63+ OptionData monthData = new OptionData (OptionType .STRING , MONTH_OPTION ,
64+ "the month to compute for, by default the last month" , false );
65+ Arrays .stream (Month .values ())
66+ .forEach (month -> monthData .addChoice (
67+ month .getDisplayName (TextStyle .FULL_STANDALONE , Locale .US ), month .name ()));
68+ getData ().addOptions (monthData );
69+
6470 hasRequiredRole = Pattern .compile (config .getSoftModerationRolePattern ()).asMatchPredicate ();
6571 this .database = database ;
6672 }
@@ -70,8 +76,9 @@ public void onSlashCommand(@NotNull SlashCommandInteractionEvent event) {
7076 if (!handleHasAuthorRole (event .getMember (), event )) {
7177 return ;
7278 }
79+ OptionMapping atMonthData = event .getOption (MONTH_OPTION );
7380
74- TimeRange timeRange = computeDefaultTimeRange ( );
81+ TimeRange timeRange = computeTimeRange ( computeMonth ( atMonthData ) );
7582 List <TopHelperResult > topHelpers =
7683 computeTopHelpersDescending (event .getGuild ().getIdLong (), timeRange );
7784
@@ -102,16 +109,31 @@ private boolean handleHasAuthorRole(@NotNull Member author, @NotNull IReplyCallb
102109 return false ;
103110 }
104111
105- private static @ NotNull TimeRange computeDefaultTimeRange () {
106- // Last month
107- ZonedDateTime start = Instant .now ()
108- .atZone (ZoneOffset .UTC )
109- .minusMonths (1 )
110- .with (TemporalAdjusters .firstDayOfMonth ());
111- ZonedDateTime end = start .with (TemporalAdjusters .lastDayOfMonth ());
112- String description = start .getMonth ().getDisplayName (TextStyle .FULL_STANDALONE , Locale .US );
112+ private static @ NotNull Month computeMonth (@ Nullable OptionMapping atMonthData ) {
113+ if (atMonthData != null ) {
114+ return Month .valueOf (atMonthData .getAsString ());
115+ }
113116
114- return new TimeRange (start .toInstant (), end .toInstant (), description );
117+ // Previous month
118+ return Instant .now ().atZone (ZoneOffset .UTC ).minusMonths (1 ).getMonth ();
119+ }
120+
121+ private static @ NotNull TimeRange computeTimeRange (@ NotNull Month atMonth ) {
122+ ZonedDateTime now = Instant .now ().atZone (ZoneOffset .UTC );
123+
124+ int atYear = now .getYear ();
125+ // E.g. using November, while it is March 2022, should use November 2021
126+ if (atMonth .compareTo (now .getMonth ()) > 0 ) {
127+ atYear --;
128+ }
129+ YearMonth atYearMonth = YearMonth .of (atYear , atMonth );
130+
131+ Instant start = atYearMonth .atDay (1 ).atTime (LocalTime .MIN ).toInstant (ZoneOffset .UTC );
132+ Instant end = atYearMonth .atEndOfMonth ().atTime (LocalTime .MAX ).toInstant (ZoneOffset .UTC );
133+ String description = "%s %d"
134+ .formatted (atMonth .getDisplayName (TextStyle .FULL_STANDALONE , Locale .US ), atYear );
135+
136+ return new TimeRange (start , end , description );
115137 }
116138
117139 private @ NotNull List <TopHelperResult > computeTopHelpersDescending (long guildId ,
@@ -187,9 +209,11 @@ private static void handleTopHelpers(@NotNull Collection<TopHelperResult> topHel
187209 private record TimeRange (Instant start , Instant end , String description ) {
188210 }
189211
212+
190213 private record TopHelperResult (long authorId , int messageCount ) {
191214 }
192215
216+
193217 private record ColumnSetting (String headerName , HorizontalAlign alignment ) {
194218 }
195219}
0 commit comments