From 00c46c1396a48e59df6c3494f24a9ca4d52457ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beatrice=20Dellac=C3=A0?= Date: Sun, 25 Dec 2022 01:48:31 +0100 Subject: [PATCH] Implement profile banner grabber command --- .../wtf/beatrice/hidekobot/HidekoBot.java | 9 +- .../hidekobot/commands/base/Avatar.java | 62 ----------- .../hidekobot/commands/base/ProfileImage.java | 102 +++++++++++++++++ ...java => ProfileImageCommandCompleter.java} | 4 +- .../commands/message/AvatarCommand.java | 18 ++- .../commands/message/BannerCommand.java | 103 ++++++++++++++++++ .../commands/message/HelloCommand.java | 4 +- .../commands/slash/AvatarCommand.java | 18 ++- .../commands/slash/BannerCommand.java | 59 ++++++++++ 9 files changed, 299 insertions(+), 80 deletions(-) delete mode 100644 src/main/java/wtf/beatrice/hidekobot/commands/base/Avatar.java create mode 100644 src/main/java/wtf/beatrice/hidekobot/commands/base/ProfileImage.java rename src/main/java/wtf/beatrice/hidekobot/commands/completer/{AvatarCommandCompleter.java => ProfileImageCommandCompleter.java} (88%) create mode 100644 src/main/java/wtf/beatrice/hidekobot/commands/message/BannerCommand.java create mode 100644 src/main/java/wtf/beatrice/hidekobot/commands/slash/BannerCommand.java diff --git a/src/main/java/wtf/beatrice/hidekobot/HidekoBot.java b/src/main/java/wtf/beatrice/hidekobot/HidekoBot.java index c448b06..3f943ce 100644 --- a/src/main/java/wtf/beatrice/hidekobot/HidekoBot.java +++ b/src/main/java/wtf/beatrice/hidekobot/HidekoBot.java @@ -5,7 +5,7 @@ import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.OnlineStatus; import net.dv8tion.jda.api.requests.GatewayIntent; import sun.misc.Signal; -import wtf.beatrice.hidekobot.commands.completer.AvatarCommandCompleter; +import wtf.beatrice.hidekobot.commands.completer.ProfileImageCommandCompleter; import wtf.beatrice.hidekobot.commands.message.HelloCommand; import wtf.beatrice.hidekobot.commands.slash.*; import wtf.beatrice.hidekobot.datasources.ConfigurationSource; @@ -110,9 +110,13 @@ public class HidekoBot SlashCommandListener slashCommandListener = new SlashCommandListener(); SlashCommandCompletionListener slashCommandCompletionListener = new SlashCommandCompletionListener(); AvatarCommand avatarCommand = new AvatarCommand(); - AvatarCommandCompleter avatarCommandCompleter = new AvatarCommandCompleter(avatarCommand); + ProfileImageCommandCompleter avatarCommandCompleter = new ProfileImageCommandCompleter(avatarCommand); slashCommandListener.registerCommand(avatarCommand); slashCommandCompletionListener.registerCommandCompleter(avatarCommandCompleter); + BannerCommand bannerCommand = new BannerCommand(); + ProfileImageCommandCompleter bannerCommandCompleter = new ProfileImageCommandCompleter(bannerCommand); + slashCommandListener.registerCommand(bannerCommand); + slashCommandCompletionListener.registerCommandCompleter(bannerCommandCompleter); slashCommandListener.registerCommand(new BotInfoCommand()); slashCommandListener.registerCommand(new ClearCommand()); slashCommandListener.registerCommand(new CoinFlipCommand()); @@ -134,6 +138,7 @@ public class HidekoBot messageCommandListener.registerCommand(new HelloCommand()); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.AliasCommand()); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.AvatarCommand()); + messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.BannerCommand()); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.BotInfoCommand()); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.CoinFlipCommand()); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.ClearCommand()); diff --git a/src/main/java/wtf/beatrice/hidekobot/commands/base/Avatar.java b/src/main/java/wtf/beatrice/hidekobot/commands/base/Avatar.java deleted file mode 100644 index 2515269..0000000 --- a/src/main/java/wtf/beatrice/hidekobot/commands/base/Avatar.java +++ /dev/null @@ -1,62 +0,0 @@ -package wtf.beatrice.hidekobot.commands.base; - -import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.entities.MessageEmbed; -import net.dv8tion.jda.api.entities.User; -import wtf.beatrice.hidekobot.Cache; - -public class Avatar -{ - public static int parseResolution(int resolution) - { - int[] acceptedSizes = Cache.getSupportedAvatarResolutions(); - - // method to find closest value to accepted values - int distance = Math.abs(acceptedSizes[0] - resolution); - int idx = 0; - for(int c = 1; c < acceptedSizes.length; c++){ - int cdistance = Math.abs(acceptedSizes[c] - resolution); - if(cdistance < distance){ - idx = c; - distance = cdistance; - } - } - - return acceptedSizes[idx]; - } - - public static MessageEmbed buildEmbed(int resolution, User user) - { - int[] acceptedSizes = Cache.getSupportedAvatarResolutions(); - EmbedBuilder embedBuilder = new EmbedBuilder(); - - embedBuilder.setColor(Cache.getBotColor()); - embedBuilder.setTitle("Profile picture"); - - embedBuilder.addField("User", "<@" + user.getId() + ">", false); - - embedBuilder.addField("Current resolution", resolution + " × " + resolution, false); - - // string builder to create a string that links to all available resolutions - StringBuilder links = new StringBuilder(); - for(int pos = 0; pos < acceptedSizes.length; pos++) - { - int currSize = acceptedSizes[pos]; - - String currLink = user.getEffectiveAvatar().getUrl(currSize); - - links.append("[").append(currSize).append("px](").append(currLink).append(")"); - if(pos + 1 != acceptedSizes.length) // don't add a separator on the last iteration - { - links.append(" | "); - } - } - - embedBuilder.addField("Available resolutions", links.toString(), false); - - - - embedBuilder.setImage(user.getEffectiveAvatar().getUrl(resolution)); - return embedBuilder.build(); - } -} diff --git a/src/main/java/wtf/beatrice/hidekobot/commands/base/ProfileImage.java b/src/main/java/wtf/beatrice/hidekobot/commands/base/ProfileImage.java new file mode 100644 index 0000000..7cfb1ba --- /dev/null +++ b/src/main/java/wtf/beatrice/hidekobot/commands/base/ProfileImage.java @@ -0,0 +1,102 @@ +package wtf.beatrice.hidekobot.commands.base; + +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.utils.ImageProxy; +import wtf.beatrice.hidekobot.Cache; +import wtf.beatrice.hidekobot.objects.MessageResponse; + +public class ProfileImage +{ + public static int parseResolution(int resolution) + { + int[] acceptedSizes = Cache.getSupportedAvatarResolutions(); + + // method to find closest value to accepted values + int distance = Math.abs(acceptedSizes[0] - resolution); + int idx = 0; + for(int c = 1; c < acceptedSizes.length; c++){ + int cdistance = Math.abs(acceptedSizes[c] - resolution); + if(cdistance < distance){ + idx = c; + distance = cdistance; + } + } + + return acceptedSizes[idx]; + } + + public static MessageResponse buildResponse(int resolution, User user, ImageType imageType) + { + String imageTypeName = imageType.name().toLowerCase(); + String resolutionString; + String imageLink = null; + + User.Profile userProfile = user.retrieveProfile().complete(); + ImageProxy bannerProxy = userProfile.getBanner(); + + if(imageType == ImageType.AVATAR) + { + resolutionString = resolution + " × " + resolution; + imageLink = user.getEffectiveAvatar().getUrl(resolution); + } else { + int verticalRes = 361 * resolution / 1024; + resolutionString = resolution + " × " + verticalRes; + if(bannerProxy != null) + imageLink = bannerProxy.getUrl(resolution); + } + + int[] acceptedSizes = Cache.getSupportedAvatarResolutions(); + EmbedBuilder embedBuilder = new EmbedBuilder(); + + + embedBuilder.setColor(Cache.getBotColor()); + embedBuilder.setTitle("Profile " + imageTypeName); + + embedBuilder.addField("User", user.getAsMention(), false); + + embedBuilder.addField("Current resolution", resolutionString, false); + + // string builder to create a string that links to all available resolutions + StringBuilder links = new StringBuilder(); + for(int pos = 0; pos < acceptedSizes.length; pos++) + { + int currSize = acceptedSizes[pos]; + + String currLink; + if(imageType == ImageType.AVATAR) + { + currLink = user.getEffectiveAvatar().getUrl(currSize); + } else { + if(bannerProxy == null) break; + currLink = bannerProxy.getUrl(currSize); + } + + links.append("**[").append(currSize).append("px](").append(currLink).append(")**"); + if(pos + 1 != acceptedSizes.length) // don't add a separator on the last iteration + { + links.append(" | "); + } + } + + embedBuilder.addField("Available resolutions", links.toString(), false); + + + + if(imageLink != null) + embedBuilder.setImage(imageLink); + + + if(imageLink == null) { + String error = "I couldn't find " + user.getAsMention() + "'s " + imageTypeName + "!"; + return new MessageResponse(error, null); + } else { + return new MessageResponse(null, embedBuilder.build()); + } + } + + public enum ImageType + { + AVATAR, BANNER; + } +} diff --git a/src/main/java/wtf/beatrice/hidekobot/commands/completer/AvatarCommandCompleter.java b/src/main/java/wtf/beatrice/hidekobot/commands/completer/ProfileImageCommandCompleter.java similarity index 88% rename from src/main/java/wtf/beatrice/hidekobot/commands/completer/AvatarCommandCompleter.java rename to src/main/java/wtf/beatrice/hidekobot/commands/completer/ProfileImageCommandCompleter.java index 331b07f..f530eff 100644 --- a/src/main/java/wtf/beatrice/hidekobot/commands/completer/AvatarCommandCompleter.java +++ b/src/main/java/wtf/beatrice/hidekobot/commands/completer/ProfileImageCommandCompleter.java @@ -10,10 +10,10 @@ import wtf.beatrice.hidekobot.objects.commands.SlashCommand; import java.util.ArrayList; import java.util.List; -public class AvatarCommandCompleter extends SlashArgumentsCompleterImpl +public class ProfileImageCommandCompleter extends SlashArgumentsCompleterImpl { - public AvatarCommandCompleter(SlashCommand parentCommand) { + public ProfileImageCommandCompleter(SlashCommand parentCommand) { super(parentCommand); } diff --git a/src/main/java/wtf/beatrice/hidekobot/commands/message/AvatarCommand.java b/src/main/java/wtf/beatrice/hidekobot/commands/message/AvatarCommand.java index 8db4e4d..de7a3eb 100644 --- a/src/main/java/wtf/beatrice/hidekobot/commands/message/AvatarCommand.java +++ b/src/main/java/wtf/beatrice/hidekobot/commands/message/AvatarCommand.java @@ -2,14 +2,14 @@ package wtf.beatrice.hidekobot.commands.message; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Mentions; -import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.HidekoBot; -import wtf.beatrice.hidekobot.commands.base.Avatar; +import wtf.beatrice.hidekobot.commands.base.ProfileImage; +import wtf.beatrice.hidekobot.objects.MessageResponse; import wtf.beatrice.hidekobot.objects.commands.CommandCategory; import wtf.beatrice.hidekobot.objects.commands.MessageCommand; @@ -69,7 +69,7 @@ public class AvatarCommand implements MessageCommand for (String arg : args) { try { int givenRes = Integer.parseInt(arg); - resolution = Avatar.parseResolution(givenRes); + resolution = ProfileImage.parseResolution(givenRes); resFound = true; break; } catch (NumberFormatException ignored) { @@ -77,7 +77,7 @@ public class AvatarCommand implements MessageCommand } // fallback in case we didn't find any specified resolution - if(!resFound) resolution = Avatar.parseResolution(512); + if(!resFound) resolution = ProfileImage.parseResolution(512); // check if someone is mentioned Mentions mentions = event.getMessage().getMentions(); @@ -94,7 +94,13 @@ public class AvatarCommand implements MessageCommand if(user == null) user = event.getAuthor(); // send a response - MessageEmbed embed = Avatar.buildEmbed(resolution, user); - event.getMessage().replyEmbeds(embed).queue(); + MessageResponse response = ProfileImage.buildResponse(resolution, user, ProfileImage.ImageType.AVATAR); + if(response.content() != null) + { + event.getMessage().reply(response.content()).queue(); + } else if(response.embed() != null) + { + event.getMessage().replyEmbeds(response.embed()).queue(); + } } } diff --git a/src/main/java/wtf/beatrice/hidekobot/commands/message/BannerCommand.java b/src/main/java/wtf/beatrice/hidekobot/commands/message/BannerCommand.java new file mode 100644 index 0000000..f0f7858 --- /dev/null +++ b/src/main/java/wtf/beatrice/hidekobot/commands/message/BannerCommand.java @@ -0,0 +1,103 @@ +package wtf.beatrice.hidekobot.commands.message; + +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Mentions; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import wtf.beatrice.hidekobot.HidekoBot; +import wtf.beatrice.hidekobot.commands.base.ProfileImage; +import wtf.beatrice.hidekobot.objects.MessageResponse; +import wtf.beatrice.hidekobot.objects.commands.CommandCategory; +import wtf.beatrice.hidekobot.objects.commands.MessageCommand; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class BannerCommand implements MessageCommand +{ + + @Override + public LinkedList getCommandLabels() { + return new LinkedList<>(Collections.singletonList("banner")); + } + + @Nullable + @Override + public List getPermissions() { + return null; // anyone can use it + } + + @Override + public boolean passRawArgs() { + return false; + } + + @NotNull + @Override + public String getDescription() { + return "Get someone's profile banner, or your own."; + } + + @Nullable + @Override + public String getUsage() { + return "[mentioned user] [resolution]"; + } + + @NotNull + @Override + public CommandCategory getCategory() { + return CommandCategory.TOOLS; + } + + @Override + public void runCommand(MessageReceivedEvent event, String label, String[] args) + { + User user; + int resolution = -1; + + // we have no specific order for user and resolution, so let's try parsing any arg as resolution + // (mentions are handled differently by a specific method) + boolean resFound = false; + + for (String arg : args) { + try { + int givenRes = Integer.parseInt(arg); + resolution = ProfileImage.parseResolution(givenRes); + resFound = true; + break; + } catch (NumberFormatException ignored) { + } + } + + // fallback in case we didn't find any specified resolution + if(!resFound) resolution = ProfileImage.parseResolution(512); + + // check if someone is mentioned + Mentions mentions = event.getMessage().getMentions(); + if(mentions.getMentions().isEmpty()) + { + user = event.getAuthor(); + } else + { + String mentionedId = mentions.getMentions().get(0).getId(); + user = HidekoBot.getAPI().retrieveUserById(mentionedId).complete(); + } + + // in case of issues, fallback to the sender + if(user == null) user = event.getAuthor(); + + // send a response + MessageResponse response = ProfileImage.buildResponse(resolution, user, ProfileImage.ImageType.BANNER); + if(response.content() != null) + { + event.getMessage().reply(response.content()).queue(); + } else if(response.embed() != null) + { + event.getMessage().replyEmbeds(response.embed()).queue(); + } + } +} diff --git a/src/main/java/wtf/beatrice/hidekobot/commands/message/HelloCommand.java b/src/main/java/wtf/beatrice/hidekobot/commands/message/HelloCommand.java index 62390ae..dae4c11 100644 --- a/src/main/java/wtf/beatrice/hidekobot/commands/message/HelloCommand.java +++ b/src/main/java/wtf/beatrice/hidekobot/commands/message/HelloCommand.java @@ -48,8 +48,8 @@ public class HelloCommand implements MessageCommand @Override public void runCommand(MessageReceivedEvent event, String label, String[] args) { - String senderId = event.getMessage().getAuthor().getId(); - event.getMessage().reply("Hi, <@" + senderId + ">! :sparkles:").queue(); + String sender = event.getMessage().getAuthor().getAsMention(); + event.getMessage().reply("Hi, " + sender + "! :sparkles:").queue(); } } diff --git a/src/main/java/wtf/beatrice/hidekobot/commands/slash/AvatarCommand.java b/src/main/java/wtf/beatrice/hidekobot/commands/slash/AvatarCommand.java index 5cb06d7..3698278 100644 --- a/src/main/java/wtf/beatrice/hidekobot/commands/slash/AvatarCommand.java +++ b/src/main/java/wtf/beatrice/hidekobot/commands/slash/AvatarCommand.java @@ -1,6 +1,5 @@ package wtf.beatrice.hidekobot.commands.slash; -import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.interactions.commands.OptionMapping; @@ -8,7 +7,8 @@ import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.Commands; import org.jetbrains.annotations.NotNull; -import wtf.beatrice.hidekobot.commands.base.Avatar; +import wtf.beatrice.hidekobot.commands.base.ProfileImage; +import wtf.beatrice.hidekobot.objects.MessageResponse; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; public class AvatarCommand extends SlashCommandImpl @@ -42,12 +42,18 @@ public class AvatarCommand extends SlashCommandImpl OptionMapping sizeArg = event.getOption("size"); if(sizeArg != null) { - resolution = Avatar.parseResolution(sizeArg.getAsInt()); + resolution = ProfileImage.parseResolution(sizeArg.getAsInt()); } else { - resolution = Avatar.parseResolution(512); + resolution = ProfileImage.parseResolution(512); } - MessageEmbed embed = Avatar.buildEmbed(resolution, user); - event.getHook().editOriginalEmbeds(embed).queue(); + MessageResponse response = ProfileImage.buildResponse(resolution, user, ProfileImage.ImageType.AVATAR); + if(response.content() != null) + { + event.getHook().editOriginal(response.content()).queue(); + } else if(response.embed() != null) + { + event.getHook().editOriginalEmbeds(response.embed()).queue(); + } } } diff --git a/src/main/java/wtf/beatrice/hidekobot/commands/slash/BannerCommand.java b/src/main/java/wtf/beatrice/hidekobot/commands/slash/BannerCommand.java new file mode 100644 index 0000000..eb4569a --- /dev/null +++ b/src/main/java/wtf/beatrice/hidekobot/commands/slash/BannerCommand.java @@ -0,0 +1,59 @@ +package wtf.beatrice.hidekobot.commands.slash; + +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.CommandData; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import org.jetbrains.annotations.NotNull; +import wtf.beatrice.hidekobot.commands.base.ProfileImage; +import wtf.beatrice.hidekobot.objects.MessageResponse; +import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; + +public class BannerCommand extends SlashCommandImpl +{ + @Override + public CommandData getSlashCommandData() { + return Commands.slash("banner", "Get someone's profile banner.") + .addOption(OptionType.USER, "user", "User you want to grab the banner of.") + .addOption(OptionType.INTEGER, "size", "The size of the returned image.", + false, + true); + } + + @Override + public void runSlashCommand(@NotNull SlashCommandInteractionEvent event) + { + // defer reply because this might take a moment + event.deferReply().queue(); + + User user; + int resolution; + + OptionMapping userArg = event.getOption("user"); + if(userArg != null) + { + user = userArg.getAsUser(); + } else { + user = event.getUser(); + } + + OptionMapping sizeArg = event.getOption("size"); + if(sizeArg != null) + { + resolution = ProfileImage.parseResolution(sizeArg.getAsInt()); + } else { + resolution = ProfileImage.parseResolution(512); + } + + MessageResponse response = ProfileImage.buildResponse(resolution, user, ProfileImage.ImageType.BANNER); + if(response.content() != null) + { + event.getHook().editOriginal(response.content()).queue(); + } else if(response.embed() != null) + { + event.getHook().editOriginalEmbeds(response.embed()).queue(); + } + } +}