From fe4959d9b210e42e08e344e8e0c91b98c61747df Mon Sep 17 00:00:00 2001 From: Daedalus <16168171+RedDaedalus@users.noreply.github.com> Date: Sun, 7 Nov 2021 12:51:56 -0700 Subject: [PATCH] Implement user banner & banner color (#1736) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Florian Spieß --- .../net/dv8tion/jda/api/entities/User.java | 101 ++++++++++++++++++ .../jda/internal/entities/EntityBuilder.java | 7 +- .../jda/internal/entities/UserById.java | 8 ++ .../jda/internal/entities/UserImpl.java | 29 +++++ 4 files changed, 144 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/dv8tion/jda/api/entities/User.java b/src/main/java/net/dv8tion/jda/api/entities/User.java index bd0521cb8a..d3da3ca139 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/User.java +++ b/src/main/java/net/dv8tion/jda/api/entities/User.java @@ -28,6 +28,7 @@ import javax.annotation.CheckReturnValue; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.awt.Color; import java.util.Collection; import java.util.EnumSet; import java.util.List; @@ -84,6 +85,11 @@ public interface User extends IMentionable String AVATAR_URL = "https://cdn.discordapp.com/avatars/%s/%s.%s"; /** Template for {@link #getDefaultAvatarUrl()} */ String DEFAULT_AVATAR_URL = "https://cdn.discordapp.com/embed/avatars/%s.png"; + /** Template for {@link Profile#getBannerUrl()} */ + String BANNER_URL = "https://cdn.discordapp.com/banners/%s/%s.%s"; + + /** Used to keep consistency between color values used in the API */ + int DEFAULT_ACCENT_COLOR_RAW = 0x1FFFFFFF; // java.awt.Color fills the MSB with FF, we just use 1F to provide better consistency /** * Creates a User instance which only wraps an ID. @@ -220,6 +226,21 @@ default String getEffectiveAvatarUrl() return avatarUrl == null ? getDefaultAvatarUrl() : avatarUrl; } + /** + * Loads the user's {@link User.Profile} data. + * Returns a completed RestAction if this User has been retrieved using {@link JDA#retrieveUserById(long)}. + * + * @throws UnsupportedOperationException + * If this User was created with {@link #fromId(long)} + * + * @return {@link RestAction} - Type: {@link User.Profile} + * + * @since 4.3.0 + */ + @Nonnull + @CheckReturnValue + RestAction retrieveProfile(); + /** * The "tag" for this user *

This is the equivalent of calling {@link java.lang.String#format(String, Object...) String.format}("%#s", user) @@ -344,6 +365,86 @@ default String getEffectiveAvatarUrl() */ int getFlagsRaw(); + /** + * Represents the information contained in a {@link User User}'s profile. + * + * @since 4.3.0 + */ + class Profile + { + private final long userId; + private final String bannerId; + private final int accentColor; + + public Profile(long userId, String bannerId, int accentColor) + { + this.userId = userId; + this.bannerId = bannerId; + this.accentColor = accentColor; + } + + /** + * The Discord Id for this user's banner image. + * If the user has not set a banner, this will return null. + * + * @return Possibly-null String containing the {@link User User} banner id. + */ + @Nullable + public String getBannerId() + { + return bannerId; + } + + /** + * The URL for the user's banner image. + * If the user has not set a banner, this will return null. + * + * @return Possibly-null String containing the {@link User User} banner url. + * + * @see User#BANNER_URL + */ + @Nullable + public String getBannerUrl() + { + return bannerId == null ? null : String.format(BANNER_URL, Long.toUnsignedString(userId), bannerId, bannerId.startsWith("a_") ? "gif" : "png"); + } + + /** + * The user's accent color. + * If the user has not set an accent color, this will return null. + * The automatically calculated color is not returned. + * The accent color is not shown in the client if the user has set a banner. + * + * @return Possibly-null {@link java.awt.Color} containing the {@link User User} accent color. + */ + @Nullable + public Color getAccentColor() + { + return accentColor == DEFAULT_ACCENT_COLOR_RAW ? null : new Color(accentColor); + } + + /** + * The raw RGB value of this user's accent color. + *
Defaults to {@link #DEFAULT_ACCENT_COLOR_RAW} if this user's banner color is not available. + * + * @return The raw RGB color value or {@link User#DEFAULT_ACCENT_COLOR_RAW} + */ + public int getAccentColorRaw() + { + return accentColor; + } + + @Override + public String toString() + { + return "UserProfile(" + + "userId=" + userId + + ", bannerId='" + bannerId + "'" + + ", accentColor=" + accentColor + + ')'; + } + } + /** * Represents the bit offsets used by Discord for public flags */ diff --git a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java index 29bc42738c..e390777970 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -333,6 +333,10 @@ public UserImpl createUser(DataObject user) } } + User.Profile profile = user.hasKey("banner") + ? new User.Profile(id, user.getString("banner", null), user.getInt("accent_color", User.DEFAULT_ACCENT_COLOR_RAW)) + : null; + if (newUser) { // Initial creation @@ -341,7 +345,8 @@ public UserImpl createUser(DataObject user) .setAvatarId(user.getString("avatar", null)) .setBot(user.getBoolean("bot")) .setSystem(user.getBoolean("system")) - .setFlags(user.getInt("public_flags", 0)); + .setFlags(user.getInt("public_flags", 0)) + .setProfile(profile); } else { diff --git a/src/main/java/net/dv8tion/jda/internal/entities/UserById.java b/src/main/java/net/dv8tion/jda/internal/entities/UserById.java index c40649b5af..e1801d37e1 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/UserById.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/UserById.java @@ -102,6 +102,14 @@ public String getAvatarId() return null; } + @Nonnull + @Override + public RestAction retrieveProfile() + { + unsupported(); + return null; + } + @Nonnull @Override public String getDefaultAvatarId() diff --git a/src/main/java/net/dv8tion/jda/internal/entities/UserImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/UserImpl.java index 35f1a81b12..ba42b3e590 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/UserImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/UserImpl.java @@ -41,6 +41,7 @@ public class UserImpl extends UserById implements User protected short discriminator; protected String name; protected String avatarId; + protected Profile profile; protected long privateChannel = 0L; protected boolean bot; protected boolean system; @@ -73,6 +74,28 @@ public String getAvatarId() return avatarId; } + @Nonnull + @Override + public RestAction retrieveProfile() + { + return new DeferredRestAction<>(getJDA(), Profile.class, this::getProfile, () -> { + Route.CompiledRoute route = Route.Users.GET_USER.compile(getId()); + return new RestActionImpl<>(getJDA(), route, (response, request) -> { + DataObject json = response.getObject(); + + String bannerId = json.getString("banner", null); + int accentColor = json.getInt("accent_color", User.DEFAULT_ACCENT_COLOR_RAW); + + return new Profile(getIdLong(), bannerId, accentColor); + }); + }); + } + + public Profile getProfile() + { + return profile; + } + @Nonnull @Override public String getDefaultAvatarId() @@ -182,6 +205,12 @@ public UserImpl setAvatarId(String avatarId) return this; } + public UserImpl setProfile(Profile profile) + { + this.profile = profile; + return this; + } + public UserImpl setPrivateChannel(PrivateChannel privateChannel) { if (privateChannel != null)