|
| 1 | +package org.togetherjava.tjbot.commands.moderation; |
| 2 | + |
| 3 | +import net.dv8tion.jda.api.EmbedBuilder; |
| 4 | +import net.dv8tion.jda.api.entities.GuildVoiceState; |
| 5 | +import net.dv8tion.jda.api.entities.Member; |
| 6 | +import net.dv8tion.jda.api.entities.Role; |
| 7 | +import net.dv8tion.jda.api.entities.User; |
| 8 | +import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; |
| 9 | +import net.dv8tion.jda.api.interactions.commands.OptionMapping; |
| 10 | +import net.dv8tion.jda.api.interactions.commands.OptionType; |
| 11 | +import net.dv8tion.jda.api.interactions.components.Button; |
| 12 | +import net.dv8tion.jda.api.interactions.components.ButtonStyle; |
| 13 | +import net.dv8tion.jda.api.requests.restaction.interactions.ReplyAction; |
| 14 | +import org.jetbrains.annotations.NotNull; |
| 15 | +import org.togetherjava.tjbot.commands.SlashCommandAdapter; |
| 16 | +import org.togetherjava.tjbot.commands.SlashCommandVisibility; |
| 17 | +import org.togetherjava.tjbot.commands.utils.DiscordClientAction; |
| 18 | + |
| 19 | +import javax.annotation.CheckReturnValue; |
| 20 | +import java.awt.*; |
| 21 | +import java.time.Instant; |
| 22 | +import java.time.OffsetDateTime; |
| 23 | +import java.time.format.DateTimeFormatter; |
| 24 | +import java.util.Collection; |
| 25 | +import java.util.Objects; |
| 26 | +import java.util.stream.Collectors; |
| 27 | + |
| 28 | +/** |
| 29 | + * This command allows you to look up user (or member) info. |
| 30 | + */ |
| 31 | +@SuppressWarnings("ClassWithoutLogger") |
| 32 | +public final class WhoIsCommand extends SlashCommandAdapter { |
| 33 | + private static final String USER_OPTION = "user"; |
| 34 | + private static final String SHOW_SERVER_INFO_OPTION = "show_server_specific_info"; |
| 35 | + |
| 36 | + private static final String USER_PROFILE_PICTURE_SIZE = "4096"; |
| 37 | + |
| 38 | + // Sun, December 11, 2016, 13:36:30 |
| 39 | + private static final DateTimeFormatter DATE_TIME_FORMAT = |
| 40 | + DateTimeFormatter.ofPattern("E, MMMM d, u, HH:mm:ss"); |
| 41 | + |
| 42 | + /** |
| 43 | + * Creates an instance. |
| 44 | + */ |
| 45 | + public WhoIsCommand() { |
| 46 | + super("whois", "Provides info about the given user", SlashCommandVisibility.GUILD); |
| 47 | + |
| 48 | + getData().addOption(OptionType.USER, USER_OPTION, "the user to look up", true) |
| 49 | + .addOption(OptionType.BOOLEAN, SHOW_SERVER_INFO_OPTION, |
| 50 | + "Whenever to show info that is specific to this server, such as their roles. This is true by default.", |
| 51 | + false); |
| 52 | + } |
| 53 | + |
| 54 | + @Override |
| 55 | + public void onSlashCommand(@NotNull final SlashCommandEvent event) { |
| 56 | + OptionMapping userOption = Objects.requireNonNull(event.getOption(USER_OPTION), |
| 57 | + "The given user option cannot be null"); |
| 58 | + OptionMapping showServerSpecificInfoOption = event.getOption(SHOW_SERVER_INFO_OPTION); |
| 59 | + |
| 60 | + User user = userOption.getAsUser(); |
| 61 | + Member member = userOption.getAsMember(); |
| 62 | + |
| 63 | + boolean showServerSpecificInfo = null != member && (null == showServerSpecificInfoOption |
| 64 | + || showServerSpecificInfoOption.getAsBoolean()); |
| 65 | + |
| 66 | + user.retrieveProfile().flatMap((User.Profile profile) -> { |
| 67 | + if (showServerSpecificInfo) { |
| 68 | + return handleWhoIsMember(event, member, profile); |
| 69 | + } else { |
| 70 | + return handleWhoIsUser(event, user, profile); |
| 71 | + } |
| 72 | + }).queue(); |
| 73 | + } |
| 74 | + |
| 75 | + @CheckReturnValue |
| 76 | + private static @NotNull ReplyAction handleWhoIsUser(final @NotNull SlashCommandEvent event, |
| 77 | + final @NotNull User user, final @NotNull User.Profile profile) { |
| 78 | + |
| 79 | + StringBuilder descriptionBuilder = |
| 80 | + new StringBuilder().append(userIdentificationToStringItem(user)) |
| 81 | + .append("\n**Is bot:** ") |
| 82 | + .append(user.isBot()) |
| 83 | + .append(userFlagsToStringItem(user.getFlags())) |
| 84 | + .append("\n**Registration date:** ") |
| 85 | + .append(DATE_TIME_FORMAT.format(user.getTimeCreated())); |
| 86 | + |
| 87 | + EmbedBuilder embedBuilder = |
| 88 | + generateEmbedBuilder(event, user, profile, profile.getAccentColor()).setAuthor( |
| 89 | + user.getName(), user.getEffectiveAvatarUrl(), user.getEffectiveAvatarUrl()) |
| 90 | + .setDescription(descriptionBuilder); |
| 91 | + |
| 92 | + return event.replyEmbeds(embedBuilder.build()) |
| 93 | + .addActionRow(Button.of(ButtonStyle.LINK, "discord://-/users/" + user.getId(), |
| 94 | + "Click to see profile")); |
| 95 | + } |
| 96 | + |
| 97 | + @CheckReturnValue |
| 98 | + private static @NotNull ReplyAction handleWhoIsMember(final @NotNull SlashCommandEvent event, |
| 99 | + final @NotNull Member member, final @NotNull User.Profile profile) { |
| 100 | + User user = member.getUser(); |
| 101 | + |
| 102 | + Color memberColor = member.getColor(); |
| 103 | + Color effectiveColor = (null == memberColor) ? profile.getAccentColor() : memberColor; |
| 104 | + |
| 105 | + StringBuilder descriptionBuilder = |
| 106 | + new StringBuilder().append(userIdentificationToStringItem(user)) |
| 107 | + .append(voiceStateToStringItem(member)) |
| 108 | + .append("\n**Is bot:** ") |
| 109 | + .append(user.isBot()) |
| 110 | + .append(possibleBoosterToStringItem(member)) |
| 111 | + .append(userFlagsToStringItem(user.getFlags())) |
| 112 | + .append("\n**Join date:** ") |
| 113 | + .append(DATE_TIME_FORMAT.format(member.getTimeJoined())) |
| 114 | + .append("\n**Registration date:** ") |
| 115 | + .append(DATE_TIME_FORMAT.format(user.getTimeCreated())) |
| 116 | + .append("\n**Roles:** ") |
| 117 | + .append(formatRoles(member)); |
| 118 | + |
| 119 | + EmbedBuilder embedBuilder = generateEmbedBuilder(event, user, profile, effectiveColor) |
| 120 | + .setAuthor(member.getEffectiveName(), member.getEffectiveAvatarUrl(), |
| 121 | + member.getEffectiveAvatarUrl()) |
| 122 | + .setDescription(descriptionBuilder); |
| 123 | + |
| 124 | + return event.replyEmbeds(embedBuilder.build()) |
| 125 | + .addActionRow(DiscordClientAction.General.USER.asLinkButton("Click to see profile!", user.getId())); |
| 126 | + } |
| 127 | + |
| 128 | + private static @NotNull String voiceStateToStringItem(@NotNull final Member member) { |
| 129 | + GuildVoiceState voiceState = Objects.requireNonNull(member.getVoiceState(), |
| 130 | + "The given voiceState cannot be null"); |
| 131 | + if (voiceState.inVoiceChannel()) { |
| 132 | + return "\n**In voicechannel:** " + (voiceState.getChannel().getAsMention()); |
| 133 | + } else { |
| 134 | + return ""; |
| 135 | + } |
| 136 | + } |
| 137 | + |
| 138 | + |
| 139 | + /** |
| 140 | + * Generates whois embed based on the given parameters. |
| 141 | + * |
| 142 | + * @param event the {@link SlashCommandEvent} |
| 143 | + * @param user the {@link User} getting whois'd |
| 144 | + * @param profile the {@link net.dv8tion.jda.api.entities.User.Profile} of the whois'd user |
| 145 | + * @param effectiveColor the {@link Color} that the embed will become |
| 146 | + * @return the generated {@link EmbedBuilder} |
| 147 | + */ |
| 148 | + private static @NotNull EmbedBuilder generateEmbedBuilder( |
| 149 | + @NotNull final SlashCommandEvent event, @NotNull final User user, |
| 150 | + final @NotNull User.Profile profile, final Color effectiveColor) { |
| 151 | + |
| 152 | + EmbedBuilder embedBuilder = new EmbedBuilder().setThumbnail(user.getEffectiveAvatarUrl()) |
| 153 | + .setColor(effectiveColor) |
| 154 | + .setFooter("Requested by " + event.getUser().getAsTag(), |
| 155 | + event.getMember().getEffectiveAvatarUrl()) |
| 156 | + .setTimestamp(Instant.now()); |
| 157 | + |
| 158 | + if (null != profile.getBannerId()) { |
| 159 | + embedBuilder.setImage(profile.getBannerUrl() + "?size=" + USER_PROFILE_PICTURE_SIZE); |
| 160 | + } |
| 161 | + |
| 162 | + return embedBuilder; |
| 163 | + } |
| 164 | + |
| 165 | + /** |
| 166 | + * Handles boosting properties of a {@link Member} |
| 167 | + * |
| 168 | + * @param member the {@link Member} to take the booster properties from |
| 169 | + * @return user readable {@link String} |
| 170 | + */ |
| 171 | + private static @NotNull String possibleBoosterToStringItem(final @NotNull Member member) { |
| 172 | + OffsetDateTime timeBoosted = member.getTimeBoosted(); |
| 173 | + if (null != timeBoosted) { |
| 174 | + return "\n**Is booster:** true \n**Boosting since:** " |
| 175 | + + DATE_TIME_FORMAT.format(timeBoosted); |
| 176 | + } else { |
| 177 | + return "\n**Is booster:** false"; |
| 178 | + } |
| 179 | + } |
| 180 | + |
| 181 | + /** |
| 182 | + * Handles the user's identifying properties (such as ID, tag) |
| 183 | + * |
| 184 | + * @param user the {@link User} to take the identifiers from |
| 185 | + * @return user readable {@link StringBuilder} |
| 186 | + */ |
| 187 | + private static @NotNull StringBuilder userIdentificationToStringItem(final @NotNull User user) { |
| 188 | + return new StringBuilder("**Mention:** ").append(user.getAsMention()) |
| 189 | + .append("\n**Tag:** ") |
| 190 | + .append(user.getAsTag()) |
| 191 | + .append("\n**ID:** ") |
| 192 | + .append(user.getId()); |
| 193 | + } |
| 194 | + |
| 195 | + /** |
| 196 | + * Formats the roles into a user readable {@link String} |
| 197 | + * |
| 198 | + * @param member member to take the Roles from |
| 199 | + * @return user readable {@link String} of the roles |
| 200 | + */ |
| 201 | + private static String formatRoles(final @NotNull Member member) { |
| 202 | + return member.getRoles().stream().map(Role::getAsMention).collect(Collectors.joining(", ")); |
| 203 | + } |
| 204 | + |
| 205 | + /** |
| 206 | + * Formats Hypesquad and the flags |
| 207 | + * |
| 208 | + * @param flags the {@link Collection} of {@link net.dv8tion.jda.api.entities.User.UserFlag} |
| 209 | + * (recommend {@link java.util.EnumSet} |
| 210 | + * @return user readable {@link StringBuilder} |
| 211 | + */ |
| 212 | + private static @NotNull StringBuilder userFlagsToStringItem( |
| 213 | + final @NotNull Collection<User.UserFlag> flags) { |
| 214 | + String formattedFlags = formatUserFlags(flags); |
| 215 | + |
| 216 | + if (formattedFlags.isBlank()) { |
| 217 | + return hypeSquadToStringItem(flags); |
| 218 | + } else { |
| 219 | + return hypeSquadToStringItem(flags).append("\n**Flags:** ") |
| 220 | + .append(formatUserFlags(flags)); |
| 221 | + } |
| 222 | + } |
| 223 | + |
| 224 | + /** |
| 225 | + * Formats user readable Hypesquad item |
| 226 | + * |
| 227 | + * @param flags the {@link Collection} of {@link net.dv8tion.jda.api.entities.User.UserFlag} |
| 228 | + * (recommend {@link java.util.EnumSet} |
| 229 | + * @return user readable {@link StringBuilder} |
| 230 | + */ |
| 231 | + private static @NotNull StringBuilder hypeSquadToStringItem( |
| 232 | + final @NotNull Collection<User.UserFlag> flags) { |
| 233 | + StringBuilder stringBuilder = new StringBuilder("**\nHypesquad:** "); |
| 234 | + |
| 235 | + if (flags.contains(User.UserFlag.HYPESQUAD_BALANCE)) { |
| 236 | + stringBuilder.append(User.UserFlag.HYPESQUAD_BALANCE.getName()); |
| 237 | + } else if (flags.contains(User.UserFlag.HYPESQUAD_BRAVERY)) { |
| 238 | + stringBuilder.append(User.UserFlag.HYPESQUAD_BRAVERY.getName()); |
| 239 | + } else if (flags.contains(User.UserFlag.HYPESQUAD_BRILLIANCE)) { |
| 240 | + stringBuilder.append(User.UserFlag.HYPESQUAD_BRILLIANCE.getName()); |
| 241 | + } else { |
| 242 | + stringBuilder.append("joined none"); |
| 243 | + } |
| 244 | + |
| 245 | + return stringBuilder; |
| 246 | + } |
| 247 | + |
| 248 | + /** |
| 249 | + * Formats the flags into a user readable {@link String}, filters Hypesquad relating flags |
| 250 | + * |
| 251 | + * @param flags the {@link Collection} of {@link net.dv8tion.jda.api.entities.User.UserFlag} |
| 252 | + * (recommend {@link java.util.EnumSet} |
| 253 | + * @return the user readable string |
| 254 | + */ |
| 255 | + @NotNull |
| 256 | + private static String formatUserFlags(final @NotNull Collection<User.UserFlag> flags) { |
| 257 | + return flags.stream() |
| 258 | + .map(User.UserFlag::getName) |
| 259 | + .filter(name -> (name.contains("Hypesquad"))) |
| 260 | + .collect(Collectors.joining(", ")); |
| 261 | + } |
| 262 | +} |
0 commit comments