From 471544eb55857276b4ccd1e3286ccdd3e7f82151 Mon Sep 17 00:00:00 2001
From: JJeeff248 <52386683+jjeeff248@users.noreply.github.com>
Date: Mon, 24 Jan 2022 01:14:13 +0100
Subject: [PATCH 1/9] Most of the work done by JJeeff, afterwards some
refactoring by Tijs
---
.../togetherjava/tjbot/commands/Commands.java | 0
.../commands/basic/RoleSelectCommand.java | 282 ++++++++++++++++++
2 files changed, 282 insertions(+)
create mode 100644 application/src/main/java/org/togetherjava/tjbot/commands/Commands.java
create mode 100644 application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java b/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
new file mode 100644
index 0000000000..df591a5e19
--- /dev/null
+++ b/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
@@ -0,0 +1,282 @@
+package org.togetherjava.tjbot.commands.basic;
+
+import net.dv8tion.jda.api.EmbedBuilder;
+import net.dv8tion.jda.api.Permission;
+import net.dv8tion.jda.api.entities.Guild;
+import net.dv8tion.jda.api.entities.Member;
+import net.dv8tion.jda.api.entities.MessageEmbed;
+import net.dv8tion.jda.api.entities.Role;
+import net.dv8tion.jda.api.events.interaction.SelectionMenuEvent;
+import net.dv8tion.jda.api.events.interaction.SlashCommandEvent;
+import net.dv8tion.jda.api.interactions.Interaction;
+import net.dv8tion.jda.api.interactions.commands.OptionMapping;
+import net.dv8tion.jda.api.interactions.commands.OptionType;
+import net.dv8tion.jda.api.interactions.commands.build.OptionData;
+import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
+import net.dv8tion.jda.api.interactions.components.selections.SelectOption;
+import net.dv8tion.jda.api.interactions.components.selections.SelectionMenu;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.togetherjava.tjbot.commands.SlashCommandAdapter;
+import org.togetherjava.tjbot.commands.SlashCommandVisibility;
+
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+
+
+/**
+ * Implements the {@code roleSelect} command.
+ *
+ *
+ * Allows users to select their roles without using reactions, instead it uses selection menus where
+ * you can select multiple roles.
+ * Note: the bot can only use roles below its highest one
+ */
+public class RoleSelectCommand extends SlashCommandAdapter {
+
+ private static final Logger logger = LoggerFactory.getLogger(RoleSelectCommand.class);
+
+ private static final String ALL_OPTION = "all";
+ private static final String CHOOSE_OPTION = "choose";
+
+ private static final String TITLE_OPTION = "title";
+ private static final String DESCRIPTION_OPTION = "description";
+
+ private static final Color embedColor = new Color(24, 221, 136);
+
+ private static final List messageOptions = List.of(
+ new OptionData(OptionType.STRING, TITLE_OPTION, "The title for the message", false),
+ new OptionData(OptionType.STRING, DESCRIPTION_OPTION, "A description for the message",
+ false));
+
+
+ /**
+ * Construct an instance
+ *
+ * @see RoleSelectCommand
+ */
+ public RoleSelectCommand() {
+ super("role-select", "Sends a message where users can select their roles",
+ SlashCommandVisibility.GUILD);
+
+ SubcommandData allRoles =
+ new SubcommandData(ALL_OPTION, "Lists all the rolls in the server for users")
+ .addOptions(messageOptions);
+
+ SubcommandData selectRoles =
+ new SubcommandData(CHOOSE_OPTION, "Choose the roles for users to select")
+ .addOptions(messageOptions);
+
+ getData().addSubcommands(allRoles, selectRoles);
+ }
+
+ @Override
+ public void onSlashCommand(@NotNull final SlashCommandEvent event) {
+ Member member = Objects.requireNonNull(event.getMember(), "Member is null");
+ if (!member.hasPermission(Permission.MANAGE_ROLES)) {
+ event.reply("You dont have the right permissions to use this command")
+ .setEphemeral(true)
+ .queue();
+ return;
+ }
+
+ Member selfMember = Objects.requireNonNull(event.getGuild()).getSelfMember();
+ if (!selfMember.hasPermission(Permission.MANAGE_ROLES)) {
+ event.reply("The bot needs the manage role permissions").setEphemeral(true).queue();
+ logger.warn("The bot needs the manage role permissions");
+ return;
+ }
+
+ SelectionMenu.Builder menu = SelectionMenu.create(generateComponentId(member.getId()));
+ boolean ephemeral = false;
+
+ if (Objects.equals(event.getSubcommandName(), CHOOSE_OPTION)) {
+ addMenuOptions(event, menu, "Select the roles to display", 1);
+ ephemeral = true;
+ } else {
+ addMenuOptions(event, menu, "Select your roles", 0);
+ }
+
+ // Handle Optional arguments
+ OptionMapping titleOption = event.getOption(TITLE_OPTION);
+ OptionMapping descriptionOption = event.getOption(DESCRIPTION_OPTION);
+
+ String title = handleOption(titleOption);
+ String description = handleOption(descriptionOption);
+
+ if (ephemeral) {
+ event.replyEmbeds(makeEmbed(title, description))
+ .addActionRow(menu.build())
+ .setEphemeral(true)
+ .queue();
+ } else {
+ event.getChannel()
+ .sendMessageEmbeds(makeEmbed(title, description))
+ .setActionRow(menu.build())
+ .queue();
+
+ event.reply("Message sent successfully!").setEphemeral(true).queue();
+ }
+ }
+
+ /**
+ * Adds role options to a selection menu
+ *
+ *
+ * @param event the {@link SlashCommandEvent}
+ * @param menu the menu to add options to {@link SelectionMenu.Builder}
+ * @param placeHolder the placeholder for the menu {@link String}
+ * @param minValues the minimum number of selections. nullable {@link Integer}
+ */
+ private static void addMenuOptions(@NotNull final Interaction event,
+ @NotNull final SelectionMenu.Builder menu, @NotNull final String placeHolder,
+ @Nullable final Integer minValues) {
+
+
+ Guild guild = Objects.requireNonNull(event.getGuild(), "The given guild cannot be null");
+
+ Role highestBotRole = guild.getSelfMember().getRoles().get(0);
+ List guildRoles = guild.getRoles();
+
+ Collection roles = new ArrayList<>(
+ guildRoles.subList(guildRoles.indexOf(highestBotRole) + 1, guildRoles.size()));
+
+ if (null != minValues) {
+ menu.setMinValues(minValues);
+ }
+
+ menu.setPlaceholder(placeHolder).setMaxValues(roles.size());
+
+
+ menu.addOptions(roles.stream()
+ .filter(Role::isPublicRole)
+ .filter(role -> !role.getTags().isBot())
+ .map(role -> SelectOption.of(role.getName(), role.getId()))
+ .toList());
+ }
+
+ /**
+ * Creates an embedded message to send with the selection menu
+ *
+ *
+ *
+ *
+ * @param title for the embedded message. nullable {@link String}
+ * @param description for the embedded message. nullable {@link String}
+ * @return the formatted embed {@link MessageEmbed}
+ */
+ private static @NotNull MessageEmbed makeEmbed(@Nullable final String title,
+ @Nullable final CharSequence description) {
+
+ String effectiveTitle = title;
+
+ if (null == effectiveTitle) {
+ effectiveTitle = "Select your roles:";
+ }
+
+ return new EmbedBuilder().setTitle(effectiveTitle)
+ .setDescription(description)
+ .setColor(embedColor)
+ .build();
+ }
+
+ @Override
+ public void onSelectionMenu(@NotNull final SelectionMenuEvent event,
+ @NotNull final List args) {
+ Member member = Objects.requireNonNull(event.getMember(), "Member is null");
+ Guild guild =
+ Objects.requireNonNull(event.getGuild(), "The given Guild guild cannot be null");
+ List selectedOptions = Objects.requireNonNull(event.getSelectedOptions(),
+ "The given selectedOptions cannot be null");
+
+ List selectedRoles = selectedOptions.stream()
+ .map(selectOption -> guild.getRoleById(selectOption.getValue()))
+ .filter(Objects::nonNull)
+ .filter(role -> guild.getSelfMember().canInteract(role))
+ .toList();
+
+ // TODO kinda weird to check it like that?
+ // True if the event option was 'choose'
+ if (event.getMessage().isEphemeral()) {
+
+ SelectionMenu.Builder menu = SelectionMenu.create(generateComponentId(member.getId()))
+ .setPlaceholder("Select your roles")
+ .setMaxValues(selectedRoles.size())
+ .setMinValues(0);
+
+ selectedRoles.forEach(role -> menu.addOption(role.getName(), role.getId()));
+
+ event.getChannel()
+ .sendMessageEmbeds(event.getMessage().getEmbeds().get(0))
+ .setActionRow(menu.build())
+ .queue();
+
+ event.reply("Message sent successfully!").setEphemeral(true).queue();
+ return;
+ }
+
+ List menuOptions =
+ Objects.requireNonNull(event.getInteraction().getComponent()).getOptions();
+
+
+ // TODO weird naming lol
+ Collection additionRoles = new ArrayList<>(selectedRoles.size());
+ Collection removalRoles = new ArrayList<>(selectedRoles.size());
+
+ menuOptions.stream().map(selectedOption -> {
+ // TODO handle in a different way? Because we need the SelectOption it has to be handled
+ // really odd in a Stream
+ Role role = guild.getRoleById(selectedOption.getValue());
+
+ if (null == role) {
+ logger.info(
+ "The {} ({}) role has been removed but is still an option in the selection menu",
+ selectedOption.getLabel(), selectedOption.getValue());
+ return null;
+ }
+
+ return role;
+ }).filter(Objects::nonNull).forEach(role -> {
+ if (selectedRoles.contains(role)) {
+ additionRoles.add(role);
+ } else {
+ removalRoles.add(role);
+ }
+ });
+
+ guild.modifyMemberRoles(member, additionRoles, removalRoles)
+ .flatMap(empty -> event.reply("Updated your roles!").setEphemeral(true))
+ .queue();
+
+ }
+
+ /**
+ * This gets the OptionMapping and returns the value as a string if there is one
+ *
+ *
+ *
+ *
+ * @param option the {@link OptionMapping}
+ * @return the value. nullable {@link String}
+ */
+ @Contract("null -> null")
+ private static @Nullable String handleOption(@Nullable final OptionMapping option) {
+ if (null == option) {
+ return null;
+ }
+
+ if (OptionType.STRING == option.getType()) {
+ return option.getAsString();
+ } else if (OptionType.BOOLEAN == option.getType()) {
+ return option.getAsBoolean() ? "true" : "false";
+ } else {
+ return null;
+ }
+ }
+}
\ No newline at end of file
From 36504b1fc939af8b2b562a27ca9bcff060d497a8 Mon Sep 17 00:00:00 2001
From: Tijs
Date: Mon, 24 Jan 2022 12:17:00 +0100
Subject: [PATCH 2/9] Separated logic into methods + documentation Added role
icon support for emojis
---
.../commands/basic/RoleSelectCommand.java | 168 +++++++++++-------
1 file changed, 105 insertions(+), 63 deletions(-)
diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
index df591a5e19..d865ed3ae8 100644
--- a/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
+++ b/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
@@ -1,11 +1,9 @@
package org.togetherjava.tjbot.commands.basic;
import net.dv8tion.jda.api.EmbedBuilder;
+import net.dv8tion.jda.api.MessageBuilder;
import net.dv8tion.jda.api.Permission;
-import net.dv8tion.jda.api.entities.Guild;
-import net.dv8tion.jda.api.entities.Member;
-import net.dv8tion.jda.api.entities.MessageEmbed;
-import net.dv8tion.jda.api.entities.Role;
+import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.events.interaction.SelectionMenuEvent;
import net.dv8tion.jda.api.events.interaction.SlashCommandEvent;
import net.dv8tion.jda.api.interactions.Interaction;
@@ -13,9 +11,12 @@
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
+import net.dv8tion.jda.api.interactions.components.ActionRow;
+import net.dv8tion.jda.api.interactions.components.ComponentInteraction;
import net.dv8tion.jda.api.interactions.components.selections.SelectOption;
import net.dv8tion.jda.api.interactions.components.selections.SelectionMenu;
import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
@@ -48,7 +49,7 @@ public class RoleSelectCommand extends SlashCommandAdapter {
private static final String TITLE_OPTION = "title";
private static final String DESCRIPTION_OPTION = "description";
- private static final Color embedColor = new Color(24, 221, 136);
+ private static final Color AMBIENT_COLOR = new Color(24, 221, 136, 255);
private static final List messageOptions = List.of(
new OptionData(OptionType.STRING, TITLE_OPTION, "The title for the message", false),
@@ -58,8 +59,6 @@ public class RoleSelectCommand extends SlashCommandAdapter {
/**
* Construct an instance
- *
- * @see RoleSelectCommand
*/
public RoleSelectCommand() {
super("role-select", "Sends a message where users can select their roles",
@@ -110,15 +109,16 @@ public void onSlashCommand(@NotNull final SlashCommandEvent event) {
String title = handleOption(titleOption);
String description = handleOption(descriptionOption);
+ MessageBuilder messageBuilder = new MessageBuilder(makeEmbed(title, description))
+ .setActionRows(ActionRow.of(menu.build()));
+
if (ephemeral) {
- event.replyEmbeds(makeEmbed(title, description))
- .addActionRow(menu.build())
+ event.reply(messageBuilder.build())
.setEphemeral(true)
.queue();
} else {
event.getChannel()
- .sendMessageEmbeds(makeEmbed(title, description))
- .setActionRow(menu.build())
+ .sendMessage(messageBuilder.build())
.queue();
event.reply("Message sent successfully!").setEphemeral(true).queue();
@@ -151,14 +151,22 @@ private static void addMenuOptions(@NotNull final Interaction event,
menu.setMinValues(minValues);
}
- menu.setPlaceholder(placeHolder).setMaxValues(roles.size());
-
-
- menu.addOptions(roles.stream()
- .filter(Role::isPublicRole)
- .filter(role -> !role.getTags().isBot())
- .map(role -> SelectOption.of(role.getName(), role.getId()))
- .toList());
+ menu.setPlaceholder(placeHolder)
+ .setMaxValues(roles.size())
+ .addOptions(roles.stream()
+ .filter(role -> !role.isPublicRole())
+ .filter(role -> !role.getTags().isBot())
+ .map(role -> {
+ RoleIcon roleIcon = role.getIcon();
+
+ if (null == roleIcon || !roleIcon.isEmoji()) {
+ return SelectOption.of(role.getName(), role.getId());
+ } else {
+ return SelectOption.of(role.getName(), role.getId())
+ .withEmoji((Emoji.fromUnicode(roleIcon.getEmoji())));
+ }
+ })
+ .toList());
}
/**
@@ -174,86 +182,120 @@ private static void addMenuOptions(@NotNull final Interaction event,
private static @NotNull MessageEmbed makeEmbed(@Nullable final String title,
@Nullable final CharSequence description) {
- String effectiveTitle = title;
-
- if (null == effectiveTitle) {
- effectiveTitle = "Select your roles:";
- }
+ String effectiveTitle = (null == title) ? "Select your roles:" : title;
return new EmbedBuilder().setTitle(effectiveTitle)
.setDescription(description)
- .setColor(embedColor)
+ .setColor(AMBIENT_COLOR)
.build();
}
@Override
public void onSelectionMenu(@NotNull final SelectionMenuEvent event,
@NotNull final List args) {
- Member member = Objects.requireNonNull(event.getMember(), "Member is null");
- Guild guild =
- Objects.requireNonNull(event.getGuild(), "The given Guild guild cannot be null");
+
+ Guild guild = Objects.requireNonNull(event.getGuild(), "The given guild cannot be null");
List selectedOptions = Objects.requireNonNull(event.getSelectedOptions(),
"The given selectedOptions cannot be null");
+
List selectedRoles = selectedOptions.stream()
- .map(selectOption -> guild.getRoleById(selectOption.getValue()))
+ .map(SelectOption::getValue)
+ .map(guild::getRoleById)
.filter(Objects::nonNull)
.filter(role -> guild.getSelfMember().canInteract(role))
.toList();
- // TODO kinda weird to check it like that?
- // True if the event option was 'choose'
- if (event.getMessage().isEphemeral()) {
-
- SelectionMenu.Builder menu = SelectionMenu.create(generateComponentId(member.getId()))
- .setPlaceholder("Select your roles")
- .setMaxValues(selectedRoles.size())
- .setMinValues(0);
-
- selectedRoles.forEach(role -> menu.addOption(role.getName(), role.getId()));
-
- event.getChannel()
- .sendMessageEmbeds(event.getMessage().getEmbeds().get(0))
- .setActionRow(menu.build())
- .queue();
- event.reply("Message sent successfully!").setEphemeral(true).queue();
- return;
+ if (event.getMessage().isEphemeral()) {
+ handleNewRoleBuilderSelection(event, selectedRoles);
+ } else {
+ handleRoleSelection(event, selectedRoles, guild);
}
+ }
- List menuOptions =
- Objects.requireNonNull(event.getInteraction().getComponent()).getOptions();
-
-
- // TODO weird naming lol
- Collection additionRoles = new ArrayList<>(selectedRoles.size());
- Collection removalRoles = new ArrayList<>(selectedRoles.size());
+ /**
+ * Handles selection of a {@link SelectionMenuEvent}
+ *
+ * @param event the unacknowledged {@link SelectionMenuEvent}
+ * @param selectedRoles the {@link Role roles} selected
+ * @param guild the {@link Guild}
+ */
+ private static void handleRoleSelection(final @NotNull SelectionMenuEvent event,
+ final @NotNull Collection selectedRoles, final Guild guild) {
+ Collection rolesToAdd = new ArrayList<>(selectedRoles.size());
+ Collection rolesToRemove = new ArrayList<>(selectedRoles.size());
- menuOptions.stream().map(selectedOption -> {
- // TODO handle in a different way? Because we need the SelectOption it has to be handled
- // really odd in a Stream
+ event.getInteraction().getComponent().getOptions().stream().map(selectedOption -> {
Role role = guild.getRoleById(selectedOption.getValue());
if (null == role) {
- logger.info(
- "The {} ({}) role has been removed but is still an option in the selection menu",
- selectedOption.getLabel(), selectedOption.getValue());
- return null;
+ handleNullRole(selectedOption);
}
return role;
}).filter(Objects::nonNull).forEach(role -> {
if (selectedRoles.contains(role)) {
- additionRoles.add(role);
+ rolesToAdd.add(role);
} else {
- removalRoles.add(role);
+ rolesToRemove.add(role);
}
});
+ handleRoleModifications(event, event.getMember(), guild, rolesToAdd, rolesToRemove);
+ }
+
+ /**
+ * Handles the selection of the {@link SelectionMenu} if it came from a builder.
+ *
+ * @param event the unacknowledged {@link ComponentInteraction}
+ * @param selectedRoles the {@link Role roles} selected by the {@link User} from the
+ * {@link ComponentInteraction} event
+ */
+ private void handleNewRoleBuilderSelection(@NotNull final ComponentInteraction event,
+ final @NotNull Collection extends Role> selectedRoles) {
+ SelectionMenu.Builder menu =
+ SelectionMenu.create(generateComponentId(event.getUser().getId()))
+ .setPlaceholder("Select your roles")
+ .setMaxValues(selectedRoles.size())
+ .setMinValues(0);
+
+ selectedRoles.forEach(role -> menu.addOption(role.getName(), role.getId()));
+
+ event.getChannel()
+ .sendMessageEmbeds(event.getMessage().getEmbeds().get(0))
+ .setActionRow(menu.build())
+ .queue();
+
+ event.reply("Message sent successfully!").setEphemeral(true).queue();
+ }
+
+ /**
+ * Logs that the role of the given {@link SelectOption} doesn't exist anymore.
+ *
+ * @param selectedOption the {@link SelectOption}
+ */
+ private static void handleNullRole(final @NotNull SelectOption selectedOption) {
+ logger.info(
+ "The {} ({}) role has been removed but is still an option in the selection menu",
+ selectedOption.getLabel(), selectedOption.getValue());
+ }
+
+ /**
+ * Updates the roles of the given member
+ *
+ * @param event an unacknowledged {@link Interaction} event
+ * @param member the member to update the roles of
+ * @param guild what guild to update the roles in
+ * @param additionRoles the roles to add
+ * @param removalRoles the roles to remove
+ */
+ private static void handleRoleModifications(@NotNull final Interaction event,
+ final Member member, final @NotNull Guild guild, final Collection additionRoles,
+ final Collection removalRoles) {
guild.modifyMemberRoles(member, additionRoles, removalRoles)
.flatMap(empty -> event.reply("Updated your roles!").setEphemeral(true))
.queue();
-
}
/**
From 0345cca7d394cfff5d87026fe489bd3ce7f5b9ae Mon Sep 17 00:00:00 2001
From: Tijs
Date: Mon, 24 Jan 2022 22:54:03 +0100
Subject: [PATCH 3/9] Spotless and optimized imports
---
.../tjbot/commands/basic/RoleSelectCommand.java | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
index d865ed3ae8..711f4d0d5d 100644
--- a/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
+++ b/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
@@ -16,7 +16,6 @@
import net.dv8tion.jda.api.interactions.components.selections.SelectOption;
import net.dv8tion.jda.api.interactions.components.selections.SelectionMenu;
import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
@@ -113,13 +112,9 @@ public void onSlashCommand(@NotNull final SlashCommandEvent event) {
.setActionRows(ActionRow.of(menu.build()));
if (ephemeral) {
- event.reply(messageBuilder.build())
- .setEphemeral(true)
- .queue();
+ event.reply(messageBuilder.build()).setEphemeral(true).queue();
} else {
- event.getChannel()
- .sendMessage(messageBuilder.build())
- .queue();
+ event.getChannel().sendMessage(messageBuilder.build()).queue();
event.reply("Message sent successfully!").setEphemeral(true).queue();
}
From 9805e048c4f2938aacf9da06ddc5b6dc2d2df57e Mon Sep 17 00:00:00 2001
From: Tijs
Date: Mon, 24 Jan 2022 22:58:42 +0100
Subject: [PATCH 4/9] I'm crazy, I promise you I applied spotless
---
.../togetherjava/tjbot/commands/basic/RoleSelectCommand.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
index 711f4d0d5d..0de31fbb4c 100644
--- a/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
+++ b/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
@@ -316,4 +316,4 @@ private static void handleRoleModifications(@NotNull final Interaction event,
return null;
}
}
-}
\ No newline at end of file
+}
From ebcc3b9edfafc348e31240ab6eb3f6802326da2c Mon Sep 17 00:00:00 2001
From: Tais993 <49957334+Tais993@users.noreply.github.com>
Date: Tue, 25 Jan 2022 02:41:33 -0800
Subject: [PATCH 5/9] Improved grammer / readability of sentences
With help of @Heatmanofurioso
---
.../togetherjava/tjbot/commands/basic/RoleSelectCommand.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
index 0de31fbb4c..1b5bd77fbd 100644
--- a/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
+++ b/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
@@ -36,7 +36,7 @@
*
* Allows users to select their roles without using reactions, instead it uses selection menus where
* you can select multiple roles.
- * Note: the bot can only use roles below its highest one
+* Note: the bot can only use roles with a position below its highest one
*/
public class RoleSelectCommand extends SlashCommandAdapter {
@@ -289,7 +289,7 @@ private static void handleRoleModifications(@NotNull final Interaction event,
final Member member, final @NotNull Guild guild, final Collection additionRoles,
final Collection removalRoles) {
guild.modifyMemberRoles(member, additionRoles, removalRoles)
- .flatMap(empty -> event.reply("Updated your roles!").setEphemeral(true))
+ .flatMap(empty -> event.reply("Your roles have been updated!").setEphemeral(true))
.queue();
}
From 80a1f8aac89019347d39d223295b52ebb5cdda09 Mon Sep 17 00:00:00 2001
From: Tijs
Date: Wed, 26 Jan 2022 12:23:03 +0100
Subject: [PATCH 6/9] Professional looking title
DUDE I THOUGHT, ITS SUCH A TINY CHANCE, NO WAY ILL FUCK UP SPOTLESS. THEN I DO? ARE YOU SERIOUS? IATRHAWGRTUSEGTH Se UIRSE
---
.../togetherjava/tjbot/commands/basic/RoleSelectCommand.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
index 1b5bd77fbd..3b0e23ee03 100644
--- a/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
+++ b/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
@@ -36,7 +36,7 @@
*
* Allows users to select their roles without using reactions, instead it uses selection menus where
* you can select multiple roles.
-* Note: the bot can only use roles with a position below its highest one
+ * Note: the bot can only use roles with a position below its highest one
*/
public class RoleSelectCommand extends SlashCommandAdapter {
From 0f1ce86f9126881305c036cf58ece3c20fd80ddc Mon Sep 17 00:00:00 2001
From: Tijs
Date: Thu, 27 Jan 2022 13:26:11 +0100
Subject: [PATCH 7/9] Added RoleSelectCommand to Features
---
.../main/java/org/togetherjava/tjbot/commands/Commands.java | 0
.../main/java/org/togetherjava/tjbot/commands/Features.java | 4 +++-
2 files changed, 3 insertions(+), 1 deletion(-)
delete mode 100644 application/src/main/java/org/togetherjava/tjbot/commands/Commands.java
diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java b/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/Features.java b/application/src/main/java/org/togetherjava/tjbot/commands/Features.java
index 393172ed99..eea66b3444 100644
--- a/application/src/main/java/org/togetherjava/tjbot/commands/Features.java
+++ b/application/src/main/java/org/togetherjava/tjbot/commands/Features.java
@@ -3,6 +3,7 @@
import net.dv8tion.jda.api.JDA;
import org.jetbrains.annotations.NotNull;
import org.togetherjava.tjbot.commands.basic.PingCommand;
+import org.togetherjava.tjbot.commands.basic.RoleSelectCommand;
import org.togetherjava.tjbot.commands.basic.VcActivityCommand;
import org.togetherjava.tjbot.commands.free.FreeCommand;
import org.togetherjava.tjbot.commands.mathcommands.TeXCommand;
@@ -78,6 +79,7 @@ public enum Features {
features.add(new AuditCommand(actionsStore));
features.add(new MuteCommand(actionsStore));
features.add(new UnmuteCommand(actionsStore));
+ features.add(new RoleSelectCommand());
features.add(new TopHelpersCommand(database));
// Mixtures
@@ -85,4 +87,4 @@ public enum Features {
return features;
}
-}
+}
\ No newline at end of file
From 958f9cc473829cd944741895aaf6716558fdbd19 Mon Sep 17 00:00:00 2001
From: Tijs
Date: Thu, 27 Jan 2022 13:50:10 +0100
Subject: [PATCH 8/9] SplotlessApply - apply + cry = accurate
---
.../src/main/java/org/togetherjava/tjbot/commands/Features.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/Features.java b/application/src/main/java/org/togetherjava/tjbot/commands/Features.java
index eea66b3444..94c8e74a90 100644
--- a/application/src/main/java/org/togetherjava/tjbot/commands/Features.java
+++ b/application/src/main/java/org/togetherjava/tjbot/commands/Features.java
@@ -87,4 +87,4 @@ public enum Features {
return features;
}
-}
\ No newline at end of file
+}
From 456eb980481f9245390d8035c6f5cfad94e8f3c0 Mon Sep 17 00:00:00 2001
From: Tais993
Date: Mon, 7 Feb 2022 17:43:05 +0100
Subject: [PATCH 9/9] Applied review items
---
.../commands/basic/RoleSelectCommand.java | 91 ++++++++++---------
1 file changed, 50 insertions(+), 41 deletions(-)
diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
index 3b0e23ee03..c7931e86ef 100644
--- a/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
+++ b/application/src/main/java/org/togetherjava/tjbot/commands/basic/RoleSelectCommand.java
@@ -22,12 +22,14 @@
import org.slf4j.LoggerFactory;
import org.togetherjava.tjbot.commands.SlashCommandAdapter;
import org.togetherjava.tjbot.commands.SlashCommandVisibility;
+import org.togetherjava.tjbot.commands.componentids.Lifespan;
import java.awt.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
+import java.util.function.Function;
/**
@@ -38,7 +40,7 @@
* you can select multiple roles.
* Note: the bot can only use roles with a position below its highest one
*/
-public class RoleSelectCommand extends SlashCommandAdapter {
+public final class RoleSelectCommand extends SlashCommandAdapter {
private static final Logger logger = LoggerFactory.getLogger(RoleSelectCommand.class);
@@ -57,7 +59,7 @@ public class RoleSelectCommand extends SlashCommandAdapter {
/**
- * Construct an instance
+ * Construct an instance.
*/
public RoleSelectCommand() {
super("role-select", "Sends a message where users can select their roles",
@@ -74,6 +76,18 @@ public RoleSelectCommand() {
getData().addSubcommands(allRoles, selectRoles);
}
+ @NotNull
+ private static SelectOption mapToSelectOption(@NotNull Role role) {
+ RoleIcon roleIcon = role.getIcon();
+
+ if (null == roleIcon || !roleIcon.isEmoji()) {
+ return SelectOption.of(role.getName(), role.getId());
+ } else {
+ return SelectOption.of(role.getName(), role.getId())
+ .withEmoji((Emoji.fromUnicode(roleIcon.getEmoji())));
+ }
+ }
+
@Override
public void onSlashCommand(@NotNull final SlashCommandEvent event) {
Member member = Objects.requireNonNull(event.getMember(), "Member is null");
@@ -87,16 +101,17 @@ public void onSlashCommand(@NotNull final SlashCommandEvent event) {
Member selfMember = Objects.requireNonNull(event.getGuild()).getSelfMember();
if (!selfMember.hasPermission(Permission.MANAGE_ROLES)) {
event.reply("The bot needs the manage role permissions").setEphemeral(true).queue();
- logger.warn("The bot needs the manage role permissions");
+ logger.error("The bot needs the manage role permissions");
return;
}
- SelectionMenu.Builder menu = SelectionMenu.create(generateComponentId(member.getId()));
- boolean ephemeral = false;
+ SelectionMenu.Builder menu =
+ SelectionMenu.create(generateComponentId(Lifespan.PERMANENT, member.getId()));
+ boolean isEphemeral = false;
- if (Objects.equals(event.getSubcommandName(), CHOOSE_OPTION)) {
+ if (CHOOSE_OPTION.equals(event.getSubcommandName())) {
addMenuOptions(event, menu, "Select the roles to display", 1);
- ephemeral = true;
+ isEphemeral = true;
} else {
addMenuOptions(event, menu, "Select your roles", 0);
}
@@ -111,7 +126,7 @@ public void onSlashCommand(@NotNull final SlashCommandEvent event) {
MessageBuilder messageBuilder = new MessageBuilder(makeEmbed(title, description))
.setActionRows(ActionRow.of(menu.build()));
- if (ephemeral) {
+ if (isEphemeral) {
event.reply(messageBuilder.build()).setEphemeral(true).queue();
} else {
event.getChannel().sendMessage(messageBuilder.build()).queue();
@@ -121,7 +136,7 @@ public void onSlashCommand(@NotNull final SlashCommandEvent event) {
}
/**
- * Adds role options to a selection menu
+ * Adds role options to a selection menu.
*
*
* @param event the {@link SlashCommandEvent}
@@ -133,7 +148,6 @@ private static void addMenuOptions(@NotNull final Interaction event,
@NotNull final SelectionMenu.Builder menu, @NotNull final String placeHolder,
@Nullable final Integer minValues) {
-
Guild guild = Objects.requireNonNull(event.getGuild(), "The given guild cannot be null");
Role highestBotRole = guild.getSelfMember().getRoles().get(0);
@@ -151,24 +165,12 @@ private static void addMenuOptions(@NotNull final Interaction event,
.addOptions(roles.stream()
.filter(role -> !role.isPublicRole())
.filter(role -> !role.getTags().isBot())
- .map(role -> {
- RoleIcon roleIcon = role.getIcon();
-
- if (null == roleIcon || !roleIcon.isEmoji()) {
- return SelectOption.of(role.getName(), role.getId());
- } else {
- return SelectOption.of(role.getName(), role.getId())
- .withEmoji((Emoji.fromUnicode(roleIcon.getEmoji())));
- }
- })
+ .map(RoleSelectCommand::mapToSelectOption)
.toList());
}
/**
- * Creates an embedded message to send with the selection menu
- *
- *
- *
+ * Creates an embedded message to send with the selection menu.
*
* @param title for the embedded message. nullable {@link String}
* @param description for the embedded message. nullable {@link String}
@@ -193,7 +195,6 @@ public void onSelectionMenu(@NotNull final SelectionMenuEvent event,
List selectedOptions = Objects.requireNonNull(event.getSelectedOptions(),
"The given selectedOptions cannot be null");
-
List selectedRoles = selectedOptions.stream()
.map(SelectOption::getValue)
.map(guild::getRoleById)
@@ -210,7 +211,7 @@ public void onSelectionMenu(@NotNull final SelectionMenuEvent event,
}
/**
- * Handles selection of a {@link SelectionMenuEvent}
+ * Handles selection of a {@link SelectionMenuEvent}.
*
* @param event the unacknowledged {@link SelectionMenuEvent}
* @param selectedRoles the {@link Role roles} selected
@@ -221,7 +222,26 @@ private static void handleRoleSelection(final @NotNull SelectionMenuEvent event,
Collection rolesToAdd = new ArrayList<>(selectedRoles.size());
Collection rolesToRemove = new ArrayList<>(selectedRoles.size());
- event.getInteraction().getComponent().getOptions().stream().map(selectedOption -> {
+ event.getInteraction()
+ .getComponent()
+ .getOptions()
+ .stream()
+ .map(roleFromSelectOptionFunction(guild))
+ .filter(Objects::nonNull)
+ .forEach(role -> {
+ if (selectedRoles.contains(role)) {
+ rolesToAdd.add(role);
+ } else {
+ rolesToRemove.add(role);
+ }
+ });
+
+ handleRoleModifications(event, event.getMember(), guild, rolesToAdd, rolesToRemove);
+ }
+
+ @NotNull
+ private static Function roleFromSelectOptionFunction(Guild guild) {
+ return selectedOption -> {
Role role = guild.getRoleById(selectedOption.getValue());
if (null == role) {
@@ -229,15 +249,7 @@ private static void handleRoleSelection(final @NotNull SelectionMenuEvent event,
}
return role;
- }).filter(Objects::nonNull).forEach(role -> {
- if (selectedRoles.contains(role)) {
- rolesToAdd.add(role);
- } else {
- rolesToRemove.add(role);
- }
- });
-
- handleRoleModifications(event, event.getMember(), guild, rolesToAdd, rolesToRemove);
+ };
}
/**
@@ -277,7 +289,7 @@ private static void handleNullRole(final @NotNull SelectOption selectedOption) {
}
/**
- * Updates the roles of the given member
+ * Updates the roles of the given member.
*
* @param event an unacknowledged {@link Interaction} event
* @param member the member to update the roles of
@@ -294,10 +306,7 @@ private static void handleRoleModifications(@NotNull final Interaction event,
}
/**
- * This gets the OptionMapping and returns the value as a string if there is one
- *
- *
- *
+ * This gets the OptionMapping and returns the value as a string if there is one.
*
* @param option the {@link OptionMapping}
* @return the value. nullable {@link String}