1313import net .dv8tion .jda .api .entities .emoji .EmojiUnion ;
1414import net .dv8tion .jda .api .events .interaction .ModalInteractionEvent ;
1515import net .dv8tion .jda .api .events .interaction .command .SlashCommandInteractionEvent ;
16- import net .dv8tion .jda .api .events .interaction .component .ButtonInteractionEvent ;
1716import net .dv8tion .jda .api .events .interaction .component .StringSelectInteractionEvent ;
1817import net .dv8tion .jda .api .interactions .commands .CommandInteraction ;
18+ import net .dv8tion .jda .api .interactions .commands .OptionMapping ;
19+ import net .dv8tion .jda .api .interactions .commands .OptionType ;
20+ import net .dv8tion .jda .api .interactions .commands .build .SlashCommandData ;
1921import net .dv8tion .jda .api .interactions .components .ActionRow ;
20- import net .dv8tion .jda .api .interactions .components .buttons .Button ;
2122import net .dv8tion .jda .api .interactions .components .selections .SelectOption ;
2223import net .dv8tion .jda .api .interactions .components .selections .StringSelectMenu ;
2324import net .dv8tion .jda .api .interactions .components .text .TextInput ;
3839import java .time .Duration ;
3940import java .time .Instant ;
4041import java .time .OffsetDateTime ;
42+ import java .util .HashMap ;
4143import java .util .List ;
44+ import java .util .Map ;
4245import java .util .Optional ;
4346import java .util .concurrent .TimeUnit ;
4447import java .util .function .Predicate ;
4548import java .util .regex .Pattern ;
49+ import java .util .stream .IntStream ;
4650
4751/**
4852 * Represents a command to create an application form for members to apply for roles.
@@ -59,6 +63,8 @@ public class ApplicationCreateCommand extends SlashCommandAdapter {
5963 private static final int APPLICATION_SUBMIT_COOLDOWN = 5 ;
6064 private static final String DEFAULT_QUESTION =
6165 "What makes you a valuable addition to the team? 😎" ;
66+ private static final int OPTIONAL_ROLES_AMOUNT = 5 ;
67+ private static final String ROLE_COMPONENT_ID_HEADER = "application-create" ;
6268
6369 private final Cache <Member , OffsetDateTime > applicationSubmitCooldown ;
6470 private final Predicate <String > applicationChannelPattern ;
@@ -82,6 +88,24 @@ public ApplicationCreateCommand(Config config) {
8288 this .applicationSubmitCooldown = Caffeine .newBuilder ()
8389 .expireAfterWrite (APPLICATION_SUBMIT_COOLDOWN , TimeUnit .MINUTES )
8490 .build ();
91+
92+ generateRoleOptions (getData ());
93+ }
94+
95+ /**
96+ * Populates a {@link SlashCommandData} object with the proper arguments.
97+ *
98+ * @param data the object to populate
99+ */
100+ private void generateRoleOptions (SlashCommandData data ) {
101+ IntStream .range (0 , OPTIONAL_ROLES_AMOUNT ).forEach (index -> {
102+ int renderNumber = index + 1 ;
103+
104+ data .addOption (OptionType .STRING , "title" + renderNumber , "The title of the role" );
105+ data .addOption (OptionType .STRING , "description" + renderNumber ,
106+ "The description of the role" );
107+ data .addOption (OptionType .STRING , "emoji" + renderNumber , "The emoji of the role" );
108+ });
85109 }
86110
87111 @ Override
@@ -90,22 +114,15 @@ public void onSlashCommand(SlashCommandInteractionEvent event) {
90114 return ;
91115 }
92116
93- sendMenu (event );
94- }
95-
96- @ Override
97- public void onButtonClick (ButtonInteractionEvent event , List <String > args ) {
98- User user = event .getUser ();
99- StringSelectMenu .Builder menu = StringSelectMenu
100- .create (generateComponentId (Lifespan .REGULAR , event .getUser ().getId ()))
101- .setPlaceholder ("Select role to apply for" );
102-
103- config .applyRoleConfig ()
104- .stream ()
105- .map (option -> mapToSelectOption (user , option ))
106- .forEach (menu ::addOptions );
117+ long incorrectArgsCount = getIncorrectRoleArgsCount (event .getInteraction ().getOptions ());
118+ if (incorrectArgsCount > 0 ) {
119+ event .reply ("Missing information for %d roles." .formatted (incorrectArgsCount ))
120+ .setEphemeral (true )
121+ .queue ();
122+ return ;
123+ }
107124
108- event . reply ( "" ). addActionRow ( menu . build ()). setEphemeral ( true ). queue ( );
125+ sendMenu ( event );
109126 }
110127
111128 /**
@@ -172,6 +189,58 @@ public void onStringSelectSelection(StringSelectInteractionEvent event, List<Str
172189 event .replyModal (modal ).queue ();
173190 }
174191
192+ /**
193+ * Checks a given list of passed arguments (from a user) and calculates how many roles have
194+ * missing data.
195+ *
196+ * @param args the list of passed arguments
197+ * @return the amount of roles with missing data
198+ */
199+ private static long getIncorrectRoleArgsCount (final List <OptionMapping > args ) {
200+ final Map <Character , Integer > frequencyMap = new HashMap <>();
201+
202+ args .stream ()
203+ .map (OptionMapping ::getName )
204+ .map (name -> name .charAt (name .length () - 1 ))
205+ .forEach (number -> frequencyMap .merge (number , 1 , Integer ::sum ));
206+
207+ return frequencyMap .values ().stream ().filter (value -> value != 3 ).count ();
208+ }
209+
210+ /**
211+ * Populates a {@link StringSelectMenu.Builder} with application roles.
212+ *
213+ * @param menuBuilder the menu builder to populate
214+ * @param args the arguments which contain data about the roles
215+ */
216+ private void addRolesToMenu (StringSelectMenu .Builder menuBuilder ,
217+ final List <OptionMapping > args ) {
218+ final Map <Character , MenuRole > roles = new HashMap <>();
219+
220+ args .forEach (arg -> {
221+ final String name = arg .getName ();
222+ final String argValue = arg .getAsString ();
223+ final char roleId = name .charAt (name .length () - 1 );
224+ MenuRole role = roles .computeIfAbsent (roleId , k -> new MenuRole ());
225+
226+ if (name .startsWith ("title" )) {
227+ String value = generateComponentId (ROLE_COMPONENT_ID_HEADER , argValue );
228+
229+ role .setValue (value );
230+ role .setLabel (argValue );
231+ } else if (name .startsWith ("description" )) {
232+ role .setDescription (argValue );
233+ } else if (name .startsWith ("emoji" )) {
234+ role .setEmoji (Emoji .fromFormatted (argValue ));
235+ }
236+ });
237+
238+ roles .values ().forEach (role -> {
239+ menuBuilder .addOption (role .getLabel (), role .getValue (), role .getDescription (),
240+ role .getEmoji ());
241+ });
242+ }
243+
175244 @ Override
176245 public void onModalSubmitted (ModalInteractionEvent event , List <String > args ) {
177246 Guild guild = event .getGuild ();
@@ -282,10 +351,14 @@ private void sendApplicationResult(final ModalInteractionEvent event, List<Strin
282351 private void sendMenu (final CommandInteraction event ) {
283352 MessageEmbed embed = createApplicationEmbed ();
284353
285- String buttonComponentId = generateComponentId (Lifespan .PERMANENT , event .getUser ().getId ());
286- Button button = Button .primary (buttonComponentId , "Check openings" );
354+ StringSelectMenu .Builder menuBuilder = StringSelectMenu
355+ .create (generateComponentId (Lifespan .REGULAR , event .getUser ().getId ()))
356+ .setPlaceholder ("Select role to apply for" )
357+ .setRequiredRange (1 , 1 );
358+
359+ addRolesToMenu (menuBuilder , event .getOptions ());
287360
288- event .replyEmbeds (embed ).addActionRow (button ).queue ();
361+ event .replyEmbeds (embed ).addActionRow (menuBuilder . build () ).queue ();
289362 }
290363
291364 private static MessageEmbed createApplicationEmbed () {
@@ -297,4 +370,50 @@ private static MessageEmbed createApplicationEmbed() {
297370 .setColor (AMBIENT_COLOR )
298371 .build ();
299372 }
373+
374+ /**
375+ * Wrapper class which represents a menu role for the application create command.
376+ * <p>
377+ * The reason this exists is due to the fact that {@link StringSelectMenu.Builder} does not have
378+ * a method which takes emojis as input as of writing this, so we have to elegantly pass in
379+ * custom data from this POJO.
380+ */
381+ private static class MenuRole {
382+ private String label ;
383+ private String value ;
384+ private String description ;
385+ private Emoji emoji ;
386+
387+ public String getLabel () {
388+ return label ;
389+ }
390+
391+ public void setLabel (String label ) {
392+ this .label = label ;
393+ }
394+
395+ public String getValue () {
396+ return value ;
397+ }
398+
399+ public void setValue (String value ) {
400+ this .value = value ;
401+ }
402+
403+ public String getDescription () {
404+ return description ;
405+ }
406+
407+ public void setDescription (String description ) {
408+ this .description = description ;
409+ }
410+
411+ public Emoji getEmoji () {
412+ return emoji ;
413+ }
414+
415+ public void setEmoji (Emoji emoji ) {
416+ this .emoji = emoji ;
417+ }
418+ }
300419}
0 commit comments