Implement profile banner grabber command

This commit is contained in:
Bea 2022-12-25 01:48:31 +01:00
parent 7f7ada9b9e
commit 00c46c1396
9 changed files with 299 additions and 80 deletions

View File

@ -5,7 +5,7 @@ import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.OnlineStatus; import net.dv8tion.jda.api.OnlineStatus;
import net.dv8tion.jda.api.requests.GatewayIntent; import net.dv8tion.jda.api.requests.GatewayIntent;
import sun.misc.Signal; 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.message.HelloCommand;
import wtf.beatrice.hidekobot.commands.slash.*; import wtf.beatrice.hidekobot.commands.slash.*;
import wtf.beatrice.hidekobot.datasources.ConfigurationSource; import wtf.beatrice.hidekobot.datasources.ConfigurationSource;
@ -110,9 +110,13 @@ public class HidekoBot
SlashCommandListener slashCommandListener = new SlashCommandListener(); SlashCommandListener slashCommandListener = new SlashCommandListener();
SlashCommandCompletionListener slashCommandCompletionListener = new SlashCommandCompletionListener(); SlashCommandCompletionListener slashCommandCompletionListener = new SlashCommandCompletionListener();
AvatarCommand avatarCommand = new AvatarCommand(); AvatarCommand avatarCommand = new AvatarCommand();
AvatarCommandCompleter avatarCommandCompleter = new AvatarCommandCompleter(avatarCommand); ProfileImageCommandCompleter avatarCommandCompleter = new ProfileImageCommandCompleter(avatarCommand);
slashCommandListener.registerCommand(avatarCommand); slashCommandListener.registerCommand(avatarCommand);
slashCommandCompletionListener.registerCommandCompleter(avatarCommandCompleter); 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 BotInfoCommand());
slashCommandListener.registerCommand(new ClearCommand()); slashCommandListener.registerCommand(new ClearCommand());
slashCommandListener.registerCommand(new CoinFlipCommand()); slashCommandListener.registerCommand(new CoinFlipCommand());
@ -134,6 +138,7 @@ public class HidekoBot
messageCommandListener.registerCommand(new HelloCommand()); messageCommandListener.registerCommand(new HelloCommand());
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.AliasCommand()); 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.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.BotInfoCommand());
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.CoinFlipCommand()); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.CoinFlipCommand());
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.ClearCommand()); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.ClearCommand());

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -10,10 +10,10 @@ import wtf.beatrice.hidekobot.objects.commands.SlashCommand;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class AvatarCommandCompleter extends SlashArgumentsCompleterImpl public class ProfileImageCommandCompleter extends SlashArgumentsCompleterImpl
{ {
public AvatarCommandCompleter(SlashCommand parentCommand) { public ProfileImageCommandCompleter(SlashCommand parentCommand) {
super(parentCommand); super(parentCommand);
} }

View File

@ -2,14 +2,14 @@ package wtf.beatrice.hidekobot.commands.message;
import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.Mentions; 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.entities.User;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.HidekoBot; 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.CommandCategory;
import wtf.beatrice.hidekobot.objects.commands.MessageCommand; import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
@ -69,7 +69,7 @@ public class AvatarCommand implements MessageCommand
for (String arg : args) { for (String arg : args) {
try { try {
int givenRes = Integer.parseInt(arg); int givenRes = Integer.parseInt(arg);
resolution = Avatar.parseResolution(givenRes); resolution = ProfileImage.parseResolution(givenRes);
resFound = true; resFound = true;
break; break;
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
@ -77,7 +77,7 @@ public class AvatarCommand implements MessageCommand
} }
// fallback in case we didn't find any specified resolution // 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 // check if someone is mentioned
Mentions mentions = event.getMessage().getMentions(); Mentions mentions = event.getMessage().getMentions();
@ -94,7 +94,13 @@ public class AvatarCommand implements MessageCommand
if(user == null) user = event.getAuthor(); if(user == null) user = event.getAuthor();
// send a response // send a response
MessageEmbed embed = Avatar.buildEmbed(resolution, user); MessageResponse response = ProfileImage.buildResponse(resolution, user, ProfileImage.ImageType.AVATAR);
event.getMessage().replyEmbeds(embed).queue(); if(response.content() != null)
{
event.getMessage().reply(response.content()).queue();
} else if(response.embed() != null)
{
event.getMessage().replyEmbeds(response.embed()).queue();
}
} }
} }

View File

@ -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<String> getCommandLabels() {
return new LinkedList<>(Collections.singletonList("banner"));
}
@Nullable
@Override
public List<Permission> 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();
}
}
}

View File

@ -48,8 +48,8 @@ public class HelloCommand implements MessageCommand
@Override @Override
public void runCommand(MessageReceivedEvent event, String label, String[] args) public void runCommand(MessageReceivedEvent event, String label, String[] args)
{ {
String senderId = event.getMessage().getAuthor().getId(); String sender = event.getMessage().getAuthor().getAsMention();
event.getMessage().reply("Hi, <@" + senderId + ">! :sparkles:").queue(); event.getMessage().reply("Hi, " + sender + "! :sparkles:").queue();
} }
} }

View File

@ -1,6 +1,5 @@
package wtf.beatrice.hidekobot.commands.slash; 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.entities.User;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.OptionMapping; 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.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.Commands;
import org.jetbrains.annotations.NotNull; 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; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
public class AvatarCommand extends SlashCommandImpl public class AvatarCommand extends SlashCommandImpl
@ -42,12 +42,18 @@ public class AvatarCommand extends SlashCommandImpl
OptionMapping sizeArg = event.getOption("size"); OptionMapping sizeArg = event.getOption("size");
if(sizeArg != null) if(sizeArg != null)
{ {
resolution = Avatar.parseResolution(sizeArg.getAsInt()); resolution = ProfileImage.parseResolution(sizeArg.getAsInt());
} else { } else {
resolution = Avatar.parseResolution(512); resolution = ProfileImage.parseResolution(512);
} }
MessageEmbed embed = Avatar.buildEmbed(resolution, user); MessageResponse response = ProfileImage.buildResponse(resolution, user, ProfileImage.ImageType.AVATAR);
event.getHook().editOriginalEmbeds(embed).queue(); if(response.content() != null)
{
event.getHook().editOriginal(response.content()).queue();
} else if(response.embed() != null)
{
event.getHook().editOriginalEmbeds(response.embed()).queue();
}
} }
} }

View File

@ -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();
}
}
}