77import net .dv8tion .jda .api .EmbedBuilder ;
88import net .dv8tion .jda .api .entities .Guild ;
99import net .dv8tion .jda .api .entities .MessageEmbed ;
10+ import net .dv8tion .jda .api .entities .MessageEmbed .Field ;
1011import net .dv8tion .jda .api .entities .Role ;
1112import net .dv8tion .jda .api .entities .User ;
1213import net .dv8tion .jda .api .events .interaction .command .SlashCommandInteractionEvent ;
1314import net .dv8tion .jda .api .events .interaction .component .ButtonInteractionEvent ;
1415import net .dv8tion .jda .api .interactions .commands .OptionMapping ;
1516import net .dv8tion .jda .api .interactions .commands .OptionType ;
17+ import net .dv8tion .jda .api .interactions .commands .build .OptionData ;
1618import net .dv8tion .jda .api .interactions .commands .build .SubcommandData ;
1719import net .dv8tion .jda .api .interactions .components .ActionRow ;
1820import net .dv8tion .jda .api .interactions .components .buttons .Button ;
1921import net .javadiscord .javabot .annotations .AutoDetectableComponentHandler ;
2022import net .javadiscord .javabot .systems .help .dao .HelpAccountRepository ;
21- import net .javadiscord .javabot .systems .help .model . HelpAccount ;
23+ import net .javadiscord .javabot .systems .help .dao . HelpTransactionRepository ;
2224import net .javadiscord .javabot .util .ExceptionLogger ;
2325import net .javadiscord .javabot .util .Pair ;
2426import net .javadiscord .javabot .util .Responses ;
2830
2931import java .util .List ;
3032import java .util .concurrent .ExecutorService ;
33+ import java .util .function .BiFunction ;
3134
3235/**
3336 * <h3>This class represents the /leaderboard help-experience command.</h3>
@@ -37,24 +40,36 @@ public class ExperienceLeaderboardSubcommand extends SlashCommand.Subcommand imp
3740 private static final int PAGE_SIZE = 5 ;
3841 private final ExecutorService asyncPool ;
3942 private final HelpAccountRepository helpAccountRepository ;
43+ private final HelpTransactionRepository helpTransactionRepository ;
4044
4145 /**
4246 * The constructor of this class, which sets the corresponding {@link SubcommandData}.
4347 * @param helpAccountRepository Dao object that represents the HELP_ACCOUNT SQL Table.
4448 * @param asyncPool the main thread pool for asynchronous operations
49+ * @param helpTransactionRepository Dao object that represents the HELP_TRANSACTIONS SQL Table.
4550 */
46- public ExperienceLeaderboardSubcommand (HelpAccountRepository helpAccountRepository , ExecutorService asyncPool ) {
51+ public ExperienceLeaderboardSubcommand (HelpAccountRepository helpAccountRepository , ExecutorService asyncPool , HelpTransactionRepository helpTransactionRepository ) {
4752 this .asyncPool = asyncPool ;
4853 this .helpAccountRepository = helpAccountRepository ;
54+ this .helpTransactionRepository = helpTransactionRepository ;
4955 setCommandData (new SubcommandData ("help-experience" , "The Help Experience Leaderboard." )
5056 .addOption (OptionType .INTEGER , "page" , "The page of results to show. By default it starts at 1." , false )
57+ .addOptions (new OptionData (OptionType .STRING , "type" , "Type of the help-XP headerboard" , false )
58+ .addChoice ("total" , LeaderboardType .TOTAL .name ())
59+ .addChoice ("last 30 days" , LeaderboardType .MONTH .name ()))
5160 );
5261 }
5362
5463 @ Override
5564 public void handleButton (@ NotNull ButtonInteractionEvent event , Button button ) {
5665 event .deferEdit ().queue ();
5766 String [] id = ComponentIdBuilder .split (event .getComponentId ());
67+ LeaderboardType type ;
68+ if (id .length > 3 ) {
69+ type = LeaderboardType .valueOf (id [3 ]);
70+ } else {
71+ type = LeaderboardType .TOTAL ;
72+ }
5873 asyncPool .execute (() -> {
5974 try {
6075 int page = Integer .parseInt (id [2 ]);
@@ -64,60 +79,96 @@ public void handleButton(@NotNull ButtonInteractionEvent event, Button button) {
6479 } else {
6580 page ++;
6681 }
67- int maxPage = helpAccountRepository .getTotalAccounts () / PAGE_SIZE ;
82+ int totalAccounts = switch (type ) {
83+ case MONTH -> helpTransactionRepository .getNumberOfUsersWithHelpXPInLastMonth ();
84+ case TOTAL -> helpAccountRepository .getTotalAccounts ();
85+ };
86+ int maxPage = totalAccounts / PAGE_SIZE ;
6887 if (page <= 0 ) {
6988 page = maxPage ;
7089 }
7190 if (page > maxPage ) {
7291 page = 1 ;
7392 }
7493 event .getHook ()
75- .editOriginalEmbeds (buildExperienceLeaderboard (event .getGuild (), helpAccountRepository , page ))
76- .setComponents (buildPageControls (page )).queue ();
94+ .editOriginalEmbeds (buildExperienceLeaderboard (event .getGuild (), page , type ))
95+ .setComponents (buildPageControls (page , type )).queue ();
7796 } catch (DataAccessException e ) {
7897 ExceptionLogger .capture (e , ExperienceLeaderboardSubcommand .class .getSimpleName ());
7998 }
8099 });
81100 }
82101
83- private static @ NotNull MessageEmbed buildExperienceLeaderboard (Guild guild , @ NotNull HelpAccountRepository dao , int page ) throws DataAccessException {
84- int maxPage = dao .getTotalAccounts () / PAGE_SIZE ;
85- List <HelpAccount > accounts = dao .getAccounts (Math .min (page , maxPage ), PAGE_SIZE );
102+
103+ private @ NotNull MessageEmbed buildExperienceLeaderboard (Guild guild , int page , LeaderboardType type ) throws DataAccessException {
104+ return switch (type ) {
105+ case TOTAL -> buildGenericExperienceLeaderboard (page , helpAccountRepository .getTotalAccounts (),
106+ "total Leaderboard of help experience" ,
107+ helpAccountRepository ::getAccounts , (position , account ) -> {
108+ Pair <Role , Double > currentRole = account .getCurrentExperienceGoal (guild );
109+ return buildEmbed (guild , position , account .getExperience (), account .getUserId (), currentRole .first () != null ? currentRole .first ().getAsMention () + ": " : "" );
110+ });
111+ case MONTH -> buildGenericExperienceLeaderboard (page , helpTransactionRepository .getNumberOfUsersWithHelpXPInLastMonth (),
112+ """
113+ help experience leaderboard from the last 30 days
114+ This leaderboard does not include experience decay.
115+ """ ,
116+ helpTransactionRepository ::getTotalTransactionWeightsInLastMonth , (position , xpInfo ) -> {
117+ return buildEmbed (guild , position , (double ) xpInfo .second (), xpInfo .first (), "" );
118+ });
119+ };
120+ }
121+
122+ private <T > @ NotNull MessageEmbed buildGenericExperienceLeaderboard (int page , int totalAccounts , String description ,
123+ BiFunction <Integer , Integer , List <T >> accountsReader , BiFunction <Integer , T , MessageEmbed .Field > fieldExtractor ) throws DataAccessException {
124+ int maxPage = totalAccounts / PAGE_SIZE ;
125+ int actualPage = Math .max (1 , Math .min (page , maxPage ));
126+ List <T > accounts = accountsReader .apply (actualPage , PAGE_SIZE );
86127 EmbedBuilder builder = new EmbedBuilder ()
87128 .setTitle ("Experience Leaderboard" )
129+ .setDescription (description )
88130 .setColor (Responses .Type .DEFAULT .getColor ())
89- .setFooter (String .format ("Page %s/%s" , Math .min (page , maxPage ), maxPage ));
90- accounts .forEach (account -> {
91- Pair <Role , Double > currentRole = account .getCurrentExperienceGoal (guild );
92- User user = guild .getJDA ().getUserById (account .getUserId ());
93- builder .addField (
94- String .format ("**%s.** %s" , (accounts .indexOf (account ) + 1 ) + (page - 1 ) * PAGE_SIZE , user == null ? account .getUserId () : UserUtils .getUserTag (user )),
95- String .format ("%s`%.0f XP`\n " , currentRole .first () != null ? currentRole .first ().getAsMention () + ": " : "" , account .getExperience ()),
96- false );
97- });
131+ .setFooter (String .format ("Page %s/%s" , actualPage , maxPage ));
132+ for (int i = 0 ; i < accounts .size (); i ++) {
133+ int position = (i + 1 ) + (actualPage - 1 ) * PAGE_SIZE ;
134+ builder .addField (fieldExtractor .apply (position , accounts .get (i )));
135+ }
98136 return builder .build ();
99137 }
100138
139+ private Field buildEmbed (Guild guild , Integer position , double experience , long userId , String prefix ) {
140+ User user = guild .getJDA ().getUserById (userId );
141+ return new MessageEmbed .Field (
142+ String .format ("**%s.** %s" , position , user == null ? userId : UserUtils .getUserTag (user )),
143+ String .format ("%s`%.0f XP`\n " , prefix , experience ),
144+ false );
145+ }
146+
101147 @ Contract ("_ -> new" )
102- private static @ NotNull ActionRow buildPageControls (int currentPage ) {
148+ private static @ NotNull ActionRow buildPageControls (int currentPage , LeaderboardType type ) {
103149 return ActionRow .of (
104- Button .primary (ComponentIdBuilder .build ("experience-leaderboard" , "left" , currentPage ), "Prev" ),
105- Button .primary (ComponentIdBuilder .build ("experience-leaderboard" , "right" , currentPage ), "Next" )
150+ Button .primary (ComponentIdBuilder .build ("experience-leaderboard" , "left" , currentPage , type . name () ), "Prev" ),
151+ Button .primary (ComponentIdBuilder .build ("experience-leaderboard" , "right" , currentPage , type . name () ), "Next" )
106152 );
107153 }
108154
109155 @ Override
110156 public void execute (@ NotNull SlashCommandInteractionEvent event ) {
111157 int page = event .getOption ("page" , 1 , OptionMapping ::getAsInt );
158+ LeaderboardType type = event .getOption ("type" , LeaderboardType .TOTAL , o ->LeaderboardType .valueOf (o .getAsString ()));
112159 event .deferReply ().queue ();
113160 asyncPool .execute (() -> {
114161 try {
115- event .getHook ().sendMessageEmbeds (buildExperienceLeaderboard (event .getGuild (), helpAccountRepository , page ))
116- .setComponents (buildPageControls (page ))
162+ event .getHook ().sendMessageEmbeds (buildExperienceLeaderboard (event .getGuild (), page , type ))
163+ .setComponents (buildPageControls (page , type ))
117164 .queue ();
118165 }catch (DataAccessException e ) {
119166 ExceptionLogger .capture (e , ExperienceLeaderboardSubcommand .class .getSimpleName ());
120167 }
121168 });
122169 }
170+
171+ private enum LeaderboardType {
172+ TOTAL , MONTH
173+ }
123174}
0 commit comments