From 9945ecac3f43c0ca15ae3d2344761de3ad8a1596 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Wed, 4 Sep 2024 14:42:29 +0200 Subject: [PATCH 01/11] Add changes by @Andre601 --- src/main/java/net/dv8tion/jda/api/JDA.java | 39 ++++ .../api/entities/emoji/ApplicationEmoji.java | 62 +++++++ .../api/managers/ApplicationEmojiManager.java | 83 +++++++++ .../net/dv8tion/jda/api/requests/Route.java | 5 + .../net/dv8tion/jda/internal/JDAImpl.java | 70 ++++++++ .../jda/internal/entities/EntityBuilder.java | 11 ++ .../entities/emoji/ApplicationEmojiImpl.java | 168 ++++++++++++++++++ .../managers/ApplicationEmojiManagerImpl.java | 74 ++++++++ 8 files changed, 512 insertions(+) create mode 100644 src/main/java/net/dv8tion/jda/api/entities/emoji/ApplicationEmoji.java create mode 100644 src/main/java/net/dv8tion/jda/api/managers/ApplicationEmojiManager.java create mode 100644 src/main/java/net/dv8tion/jda/internal/entities/emoji/ApplicationEmojiImpl.java create mode 100644 src/main/java/net/dv8tion/jda/internal/managers/ApplicationEmojiManagerImpl.java diff --git a/src/main/java/net/dv8tion/jda/api/JDA.java b/src/main/java/net/dv8tion/jda/api/JDA.java index f71d50dbb5..ed52f9acff 100644 --- a/src/main/java/net/dv8tion/jda/api/JDA.java +++ b/src/main/java/net/dv8tion/jda/api/JDA.java @@ -23,6 +23,7 @@ import net.dv8tion.jda.api.entities.channel.concrete.PrivateChannel; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; +import net.dv8tion.jda.api.entities.emoji.ApplicationEmoji; import net.dv8tion.jda.api.entities.emoji.RichCustomEmoji; import net.dv8tion.jda.api.entities.sticker.*; import net.dv8tion.jda.api.events.GenericEvent; @@ -1750,6 +1751,44 @@ default List getEmojisByName(@Nonnull String name, boolean igno return getEmojiCache().getElementsByName(name, ignoreCase); } + @Nonnull + @CheckReturnValue + RestAction createApplicationEmoji(@Nonnull String name, @Nonnull Icon icon); + + @CheckReturnValue + RestAction> retrieveApplicationEmojis(); + + @Nullable + @CheckReturnValue + RestAction retrieveApplicationEmojiById(long emojiId); + + @Nullable + @CheckReturnValue + default RestAction retrieveApplicationEmojiById(@Nonnull String emojiId) + { + return retrieveApplicationEmojiById(MiscUtil.parseSnowflake(emojiId)); + } + + @Nullable + @CheckReturnValue + RestAction updateApplicationEmojiName(long emojiId, @Nonnull String name); + + @Nullable + @CheckReturnValue + default RestAction updateApplicationEmojiName(@Nonnull String emojiId, @Nonnull String name) + { + return updateApplicationEmojiName(MiscUtil.parseSnowflake(emojiId), name); + } + + @CheckReturnValue + RestAction deleteApplicationEmojiById(long emojiId); + + @CheckReturnValue + default RestAction deleteApplicationEmojiById(@Nonnull String emojiId) + { + return deleteApplicationEmojiById(MiscUtil.parseSnowflake(emojiId)); + } + /** * Attempts to retrieve a {@link Sticker} object based on the provided snowflake reference. *
This works for both {@link StandardSticker} and {@link GuildSticker}, and you can resolve them using the provided {@link StickerUnion}. diff --git a/src/main/java/net/dv8tion/jda/api/entities/emoji/ApplicationEmoji.java b/src/main/java/net/dv8tion/jda/api/entities/emoji/ApplicationEmoji.java new file mode 100644 index 0000000000..94a0f503c1 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/emoji/ApplicationEmoji.java @@ -0,0 +1,62 @@ +package net.dv8tion.jda.api.entities.emoji; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.managers.ApplicationEmojiManager; +import net.dv8tion.jda.api.requests.RestAction; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Represents a Custom Emoji hosted on the Bot Account. + * + *

This does not represent unicode emojis like they are used in the official client! + * The format {@code :smiley:} is a client-side alias which is replaced by the unicode emoji, not a custom emoji. + * + * @see JDA#retrieveApplicationEmojiById(long) + * @see JDA#retrieveApplicationEmojis() + */ +public interface ApplicationEmoji extends CustomEmoji +{ + /** + * The {@link net.dv8tion.jda.api.JDA JDA} instance of this emoji + * + * @return The JDA instance of this emoji + */ + @Nonnull + JDA getJDA(); + + /** + * The user who created this emoji + * + * @return The user who created this emoji + */ + @Nullable + User getOwner(); + + /** + * Deletes this emoji. + * + *

Possible ErrorResponses include: + *

    + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_EMOJI UNKNOWN_EMOJI} + *
    If this emoji was already removed
  • + *
+ * + * @return {@link net.dv8tion.jda.api.requests.RestAction RestAction} + * The RestAction to delete this emoji. + */ + RestAction delete(); + + /** + * The {@link ApplicationEmojiManager Manager} for this emoji, used to modify + * properties of the emoji like name. + * + * @return The ApplicationEmojiManager for this emoji + */ + @Nonnull + @CheckReturnValue + ApplicationEmojiManager getManager(); +} diff --git a/src/main/java/net/dv8tion/jda/api/managers/ApplicationEmojiManager.java b/src/main/java/net/dv8tion/jda/api/managers/ApplicationEmojiManager.java new file mode 100644 index 0000000000..df70f8bec3 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/managers/ApplicationEmojiManager.java @@ -0,0 +1,83 @@ +package net.dv8tion.jda.api.managers; + +import net.dv8tion.jda.api.entities.emoji.ApplicationEmoji; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; + +/** + * Manager providing functionality to update the name field for an {@link ApplicationEmoji}. + * + *

Example + *

{@code
+ * manager.setName("minn")
+ *        .queue();
+ * }
+ * + * @see ApplicationEmoji#getManager() + */ +public interface ApplicationEmojiManager extends Manager +{ + /** Used to reset the name field */ + long NAME = 1; + + /** + * Resets the fields specified by the provided bit-flag pattern. + * You can specify a combination by using a bitwise OR concat of the flag constants. + * + *

Flag Constants: + *

    + *
  • {@link #NAME}
  • + *
+ * + * @param fields + * Integer value containing the flags to reset. + * + * @return ApplicationEmojiManager for chaining convenience. + */ + @Nonnull + @Override + ApplicationEmojiManager reset(long fields); + + /** + * Resets the fields specified by the provided bit-flag patterns. + * + *

Flag Constants: + *

    + *
  • {@link #NAME}
  • + *
+ * + * @param fields + * Integer values containing the flags to reset. + * + * @return ApplicationEmojiManager for chaining convenience. + */ + @Nonnull + @Override + ApplicationEmojiManager reset(long... fields); + + /** + * The target {@link ApplicationEmoji} that will be modified by this Manager + * + * @return The target emoji + */ + @Nonnull + ApplicationEmoji getEmoji(); + + /** + * Sets the name of the selected {@link ApplicationEmoji}. + * + *

An emoji name must be between 2-32 characters long! + *
Emoji names may only be populated with alphanumeric (with underscore and dash). + * + *

Example: {@code tatDab} or {@code fmgSUP} + * + * @param name + * The new name for the selected {@link ApplicationEmoji} + * + * @return ApplicationEmojiManager for chaining convenience. + */ + @Nonnull + @CheckReturnValue + ApplicationEmojiManager setName(@Nonnull String name); +} diff --git a/src/main/java/net/dv8tion/jda/api/requests/Route.java b/src/main/java/net/dv8tion/jda/api/requests/Route.java index 0e165f9f85..b92d493cf2 100644 --- a/src/main/java/net/dv8tion/jda/api/requests/Route.java +++ b/src/main/java/net/dv8tion/jda/api/requests/Route.java @@ -50,6 +50,11 @@ public static class Applications public static final Route CONSUME_ENTITLEMENT = new Route(POST, "applications/{application_id}/entitlements/{entitlement_id}/consume"); public static final Route CREATE_TEST_ENTITLEMENT = new Route(POST, "applications/{application_id}/entitlements"); public static final Route DELETE_TEST_ENTITLEMENT = new Route(DELETE, "applications/{application_id}/entitlements/{entitlement_id}"); + public static final Route GET_APPLICATION_EMOJIS = new Route(GET, "applications/{application_id}/emojis"); + public static final Route GET_APPLICATION_EMOJI = new Route(GET, "applications/{application_id}/emojis/{emoji_id}"); + public static final Route CREATE_APPLICATION_EMOJI = new Route(POST, "applications/{application_id}/emojis"); + public static final Route MODIFY_APPLICATION_EMOJI = new Route(PATCH, "applications/{application_id}/emojis/{emoji_id}"); + public static final Route DELETE_APPLICATION_EMOJI = new Route(DELETE, "applications/{application_id}/emojis/{emoji_id}"); } public static class Interactions diff --git a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java index 8ef202f4d3..662b2c5a39 100644 --- a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java @@ -29,6 +29,7 @@ import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.concrete.*; import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import net.dv8tion.jda.api.entities.emoji.ApplicationEmoji; import net.dv8tion.jda.api.entities.emoji.RichCustomEmoji; import net.dv8tion.jda.api.entities.sticker.StickerPack; import net.dv8tion.jda.api.entities.sticker.StickerSnowflake; @@ -82,10 +83,12 @@ import net.dv8tion.jda.internal.utils.config.ThreadingConfig; import okhttp3.OkHttpClient; import okhttp3.RequestBody; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.MDC; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.time.OffsetDateTime; import java.util.*; import java.util.concurrent.*; @@ -675,6 +678,73 @@ public SnowflakeCacheView getEmojiCache() return CacheView.allSnowflakes(() -> guildCache.stream().map(Guild::getEmojiCache)); } + @Nonnull + @Override + public RestAction createApplicationEmoji(@Nonnull String name, @Nonnull Icon icon) + { + Checks.inRange(name, 2, 32, "Emoji name"); + Checks.notNull(icon, "Emoji icon"); + + DataObject body = DataObject.empty(); + body.put("name", name); + body.put("image", icon.getEncoding()); + + final Route.CompiledRoute route = Route.Applications.CREATE_APPLICATION_EMOJI.compile(getSelfUser().getApplicationId()); + return new RestActionImpl<>(this, route, body, (response, request) -> + { + final DataObject obj = response.getObject(); + return entityBuilder.createApplicationEmoji(this, obj); + }); + } + + @Override + public RestAction> retrieveApplicationEmojis() + { + Route.CompiledRoute route = Route.Applications.GET_APPLICATION_EMOJIS.compile(getSelfUser().getApplicationId()); + return new RestActionImpl<>(this, route, (response, request) -> + { + DataArray emojis = response.getObject().getArray("items"); + List list = new ArrayList<>(emojis.length()); + for (int i = 0; i < emojis.length(); i++) + { + list.add(entityBuilder.createApplicationEmoji(this, emojis.getObject(i))); + } + + return Collections.unmodifiableList(list); + }); + } + + @Nullable + @Override + public RestAction retrieveApplicationEmojiById(long emojiId) + { + Route.CompiledRoute route = Route.Applications.GET_APPLICATION_EMOJI.compile(getSelfUser().getApplicationId(), String.valueOf(emojiId)); + return new RestActionImpl<>(this, route, + (response, request) -> entityBuilder.createApplicationEmoji(this, response.getObject()) + ); + } + + @Nullable + @Override + public RestAction updateApplicationEmojiName(long emojiId, @NotNull String name) + { + Checks.inRange(name, 2, 32, "Emoji name"); + + Route.CompiledRoute route = Route.Applications.MODIFY_APPLICATION_EMOJI.compile(getSelfUser().getApplicationId(), String.valueOf(emojiId)); + DataObject body = DataObject.empty(); + body.put("name", name); + return new RestActionImpl<>(this, route, body, + (response, request) -> entityBuilder.createApplicationEmoji(this, response.getObject()) + ); + } + + @Override + public RestAction deleteApplicationEmojiById(long emojiId) + { + Route.CompiledRoute route = Route.Applications.DELETE_APPLICATION_EMOJI.compile(getSelfUser().getApplicationId()); + return new RestActionImpl<>(this, route); + } + @Nonnull @Override public RestAction retrieveSticker(@Nonnull StickerSnowflake sticker) 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 2a8341eb23..3410300520 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -66,6 +66,7 @@ import net.dv8tion.jda.internal.entities.channel.mixin.attribute.IPermissionContainerMixin; import net.dv8tion.jda.internal.entities.channel.mixin.attribute.IPostContainerMixin; import net.dv8tion.jda.internal.entities.channel.mixin.middleman.AudioChannelMixin; +import net.dv8tion.jda.internal.entities.emoji.ApplicationEmojiImpl; import net.dv8tion.jda.internal.entities.emoji.CustomEmojiImpl; import net.dv8tion.jda.internal.entities.emoji.RichCustomEmojiImpl; import net.dv8tion.jda.internal.entities.emoji.UnicodeEmojiImpl; @@ -1020,6 +1021,16 @@ public RichCustomEmojiImpl createEmoji(GuildImpl guildObj, DataObject json) .setAvailable(json.getBoolean("available", true)); } + public ApplicationEmojiImpl createApplicationEmoji(JDAImpl api, DataObject json) + { + final long emojiId = json.getLong("id"); + // Nullable when creating emojis, at least + final User user = json.optObject("user").map(this::createUser).orElse(null); + return new ApplicationEmojiImpl(emojiId, api, user) + .setAnimated(json.getBoolean("animated")) + .setName(json.getString("name")); + } + public ScheduledEvent createScheduledEvent(GuildImpl guild, DataObject json) { final long id = json.getLong("id"); diff --git a/src/main/java/net/dv8tion/jda/internal/entities/emoji/ApplicationEmojiImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/emoji/ApplicationEmojiImpl.java new file mode 100644 index 0000000000..c532282ecb --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/entities/emoji/ApplicationEmojiImpl.java @@ -0,0 +1,168 @@ +package net.dv8tion.jda.internal.entities.emoji; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.entities.emoji.ApplicationEmoji; +import net.dv8tion.jda.api.entities.emoji.CustomEmoji; +import net.dv8tion.jda.api.entities.emoji.EmojiUnion; +import net.dv8tion.jda.api.entities.emoji.UnicodeEmoji; +import net.dv8tion.jda.api.managers.ApplicationEmojiManager; +import net.dv8tion.jda.api.requests.RestAction; +import net.dv8tion.jda.api.requests.Route; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.managers.ApplicationEmojiManagerImpl; +import net.dv8tion.jda.internal.requests.RestActionImpl; +import net.dv8tion.jda.internal.utils.EntityString; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class ApplicationEmojiImpl implements ApplicationEmoji, EmojiUnion +{ + private final long id; + private final JDAImpl api; + private final User owner; + + boolean animated = false; + private String name; + + public ApplicationEmojiImpl(long id, JDAImpl api, User owner) + { + this.id = id; + this.api = api; + this.owner = owner; + } + + @Nonnull + @Override + public Type getType() + { + return Type.CUSTOM; + } + + @Nonnull + @Override + public String getAsReactionCode() + { + return name + ":" + id; + } + + @Nonnull + @Override + public DataObject toData() + { + return DataObject.empty() + .put("name", name) + .put("animated", animated) + .put("id", id); + } + + @Nonnull + @Override + public String getName() + { + return name; + } + + @Override + public long getIdLong() + { + return id; + } + + @Nonnull + @Override + public JDA getJDA() + { + return api; + } + + @Nullable + @Override + public User getOwner() + { + return owner; + } + + @Nonnull + @Override + public ApplicationEmojiManager getManager() + { + return new ApplicationEmojiManagerImpl(this); + } + + @Override + public boolean isAnimated() + { + return animated; + } + + @Override + public RestAction delete() + { + Route.CompiledRoute route = Route.Applications.DELETE_APPLICATION_EMOJI.compile(getJDA().getSelfUser().getApplicationId(), getId()); + return new RestActionImpl<>(getJDA(), route); + } + + // -- Setters -- + + public ApplicationEmojiImpl setName(String name) + { + this.name = name; + return this; + } + + public ApplicationEmojiImpl setAnimated(boolean animated) + { + this.animated = animated; + return this; + } + + // -- Object overrides -- + + @Override + public boolean equals(Object obj) + { + if (obj == this) + return true; + if (!(obj instanceof CustomEmoji)) + return false; + + CustomEmoji other = (CustomEmoji) obj; + return this.id == other.getIdLong(); + } + + @Override + public int hashCode() + { + return Long.hashCode(id); + } + + @Override + public String toString() + { + return new EntityString(this) + .setName(name) + .toString(); + } + + public ApplicationEmojiImpl copy() + { + return new ApplicationEmojiImpl(id, api, owner).setAnimated(animated).setName(name); + } + + @Nonnull + @Override + public UnicodeEmoji asUnicode() + { + throw new IllegalStateException("Cannot convert CustomEmoji to UnicodeEmoji!"); + } + + @Nonnull + @Override + public CustomEmoji asCustom() + { + return this; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/managers/ApplicationEmojiManagerImpl.java b/src/main/java/net/dv8tion/jda/internal/managers/ApplicationEmojiManagerImpl.java new file mode 100644 index 0000000000..0fdb3b9b17 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/managers/ApplicationEmojiManagerImpl.java @@ -0,0 +1,74 @@ +package net.dv8tion.jda.internal.managers; + +import net.dv8tion.jda.api.entities.emoji.ApplicationEmoji; +import net.dv8tion.jda.api.managers.ApplicationEmojiManager; +import net.dv8tion.jda.api.requests.Route; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.utils.Checks; +import okhttp3.RequestBody; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; + +public class ApplicationEmojiManagerImpl extends ManagerBase implements ApplicationEmojiManager +{ + protected final ApplicationEmoji emoji; + + protected String name; + + public ApplicationEmojiManagerImpl(ApplicationEmoji emoji) + { + super(emoji.getJDA(), Route.Applications.MODIFY_APPLICATION_EMOJI.compile(emoji.getJDA().getSelfUser().getApplicationId(), emoji.getId())); + this.emoji = emoji; + } + + @NotNull + @Override + public ApplicationEmoji getEmoji() + { + return emoji; + } + + @Nonnull + @Override + @CheckReturnValue + public ApplicationEmojiManagerImpl reset(long fields) + { + super.reset(fields); + if ((fields & NAME) == NAME) + this.name = null; + return this; + } + + @Nonnull + @Override + @CheckReturnValue + public ApplicationEmojiManagerImpl reset(long... fields) + { + super.reset(fields); + return this; + } + + @NotNull + @Override + public ApplicationEmojiManager setName(@NotNull String name) + { + Checks.notBlank(name, "Name"); + name = name.trim(); + Checks.inRange(name, 2, 32, "Name"); + this.name = name; + set |= NAME; + return this; + } + + @Override + protected RequestBody finalizeData() + { + DataObject object = DataObject.empty(); + if (shouldUpdate(NAME)) + object.put("name", name); + reset(); + return getRequestBody(object); + } +} From 9a1d2fd439e8fbd96b2b4da47656e2aec0754053 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Wed, 4 Sep 2024 14:52:15 +0200 Subject: [PATCH 02/11] Fix merge request comments --- src/main/java/net/dv8tion/jda/api/JDA.java | 33 ++++++++++--------- .../api/entities/emoji/ApplicationEmoji.java | 6 ++++ .../net/dv8tion/jda/internal/JDAImpl.java | 19 ++++++----- .../entities/emoji/ApplicationEmojiImpl.java | 3 +- .../managers/ApplicationEmojiManagerImpl.java | 5 ++- 5 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/api/JDA.java b/src/main/java/net/dv8tion/jda/api/JDA.java index ed52f9acff..c557909898 100644 --- a/src/main/java/net/dv8tion/jda/api/JDA.java +++ b/src/main/java/net/dv8tion/jda/api/JDA.java @@ -1755,40 +1755,43 @@ default List getEmojisByName(@Nonnull String name, boolean igno @CheckReturnValue RestAction createApplicationEmoji(@Nonnull String name, @Nonnull Icon icon); + @Nonnull @CheckReturnValue RestAction> retrieveApplicationEmojis(); - @Nullable - @CheckReturnValue - RestAction retrieveApplicationEmojiById(long emojiId); - - @Nullable + @Nonnull @CheckReturnValue - default RestAction retrieveApplicationEmojiById(@Nonnull String emojiId) + default RestAction retrieveApplicationEmojiById(long emojiId) { - return retrieveApplicationEmojiById(MiscUtil.parseSnowflake(emojiId)); + return retrieveApplicationEmojiById(String.valueOf(emojiId)); } - @Nullable + @Nonnull @CheckReturnValue - RestAction updateApplicationEmojiName(long emojiId, @Nonnull String name); + RestAction retrieveApplicationEmojiById(@Nonnull String emojiId); - @Nullable + @Nonnull @CheckReturnValue - default RestAction updateApplicationEmojiName(@Nonnull String emojiId, @Nonnull String name) + default RestAction updateApplicationEmojiName(long emojiId, @Nonnull String name) { - return updateApplicationEmojiName(MiscUtil.parseSnowflake(emojiId), name); + return updateApplicationEmojiName(String.valueOf(emojiId), name); } + @Nonnull @CheckReturnValue - RestAction deleteApplicationEmojiById(long emojiId); + RestAction updateApplicationEmojiName(@Nonnull String emojiId, @Nonnull String name); + @Nonnull @CheckReturnValue - default RestAction deleteApplicationEmojiById(@Nonnull String emojiId) + default RestAction deleteApplicationEmojiById(long emojiId) { - return deleteApplicationEmojiById(MiscUtil.parseSnowflake(emojiId)); + return deleteApplicationEmojiById(String.valueOf(emojiId)); } + @Nonnull + @CheckReturnValue + RestAction deleteApplicationEmojiById(@Nonnull String emojiId); + /** * Attempts to retrieve a {@link Sticker} object based on the provided snowflake reference. *
This works for both {@link StandardSticker} and {@link GuildSticker}, and you can resolve them using the provided {@link StickerUnion}. diff --git a/src/main/java/net/dv8tion/jda/api/entities/emoji/ApplicationEmoji.java b/src/main/java/net/dv8tion/jda/api/entities/emoji/ApplicationEmoji.java index 94a0f503c1..59c8ae1eb0 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/emoji/ApplicationEmoji.java +++ b/src/main/java/net/dv8tion/jda/api/entities/emoji/ApplicationEmoji.java @@ -1,6 +1,7 @@ package net.dv8tion.jda.api.entities.emoji; import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.Icon; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.managers.ApplicationEmojiManager; import net.dv8tion.jda.api.requests.RestAction; @@ -15,6 +16,7 @@ *

This does not represent unicode emojis like they are used in the official client! * The format {@code :smiley:} is a client-side alias which is replaced by the unicode emoji, not a custom emoji. * + * @see JDA#createApplicationEmoji(String, Icon) * @see JDA#retrieveApplicationEmojiById(long) * @see JDA#retrieveApplicationEmojis() */ @@ -31,6 +33,8 @@ public interface ApplicationEmoji extends CustomEmoji /** * The user who created this emoji * + *

This returns null, right after creating the emoji. + * * @return The user who created this emoji */ @Nullable @@ -48,6 +52,8 @@ public interface ApplicationEmoji extends CustomEmoji * @return {@link net.dv8tion.jda.api.requests.RestAction RestAction} * The RestAction to delete this emoji. */ + @Nonnull + @CheckReturnValue RestAction delete(); /** diff --git a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java index 662b2c5a39..03bd0af4ae 100644 --- a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java @@ -697,6 +697,7 @@ public RestAction createApplicationEmoji(@Nonnull String name, }); } + @Nonnull @Override public RestAction> retrieveApplicationEmojis() { @@ -714,23 +715,24 @@ public RestAction> retrieveApplicationEmojis() }); } - @Nullable + @Nonnull @Override - public RestAction retrieveApplicationEmojiById(long emojiId) + public RestAction retrieveApplicationEmojiById(@Nonnull String emojiId) { - Route.CompiledRoute route = Route.Applications.GET_APPLICATION_EMOJI.compile(getSelfUser().getApplicationId(), String.valueOf(emojiId)); + Route.CompiledRoute route = Route.Applications.GET_APPLICATION_EMOJI.compile(getSelfUser().getApplicationId(), emojiId); return new RestActionImpl<>(this, route, (response, request) -> entityBuilder.createApplicationEmoji(this, response.getObject()) ); } - @Nullable + @Nonnull @Override - public RestAction updateApplicationEmojiName(long emojiId, @NotNull String name) + public RestAction updateApplicationEmojiName(@Nonnull String emojiId, @NotNull String name) { Checks.inRange(name, 2, 32, "Emoji name"); + Checks.matches(name, Checks.ALPHANUMERIC_WITH_DASH, "Emoji name"); - Route.CompiledRoute route = Route.Applications.MODIFY_APPLICATION_EMOJI.compile(getSelfUser().getApplicationId(), String.valueOf(emojiId)); + Route.CompiledRoute route = Route.Applications.MODIFY_APPLICATION_EMOJI.compile(getSelfUser().getApplicationId(), emojiId); DataObject body = DataObject.empty(); body.put("name", name); return new RestActionImpl<>(this, route, body, @@ -738,10 +740,11 @@ public RestAction updateApplicationEmojiName(long emojiId, @No ); } + @Nonnull @Override - public RestAction deleteApplicationEmojiById(long emojiId) + public RestAction deleteApplicationEmojiById(@Nonnull String emojiId) { - Route.CompiledRoute route = Route.Applications.DELETE_APPLICATION_EMOJI.compile(getSelfUser().getApplicationId()); + Route.CompiledRoute route = Route.Applications.DELETE_APPLICATION_EMOJI.compile(getSelfUser().getApplicationId(), emojiId); return new RestActionImpl<>(this, route); } diff --git a/src/main/java/net/dv8tion/jda/internal/entities/emoji/ApplicationEmojiImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/emoji/ApplicationEmojiImpl.java index c532282ecb..2cfdf0086c 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/emoji/ApplicationEmojiImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/emoji/ApplicationEmojiImpl.java @@ -98,6 +98,7 @@ public boolean isAnimated() return animated; } + @Nonnull @Override public RestAction delete() { @@ -156,7 +157,7 @@ public ApplicationEmojiImpl copy() @Override public UnicodeEmoji asUnicode() { - throw new IllegalStateException("Cannot convert CustomEmoji to UnicodeEmoji!"); + throw new IllegalStateException("Cannot convert ApplicationEmoji to UnicodeEmoji!"); } @Nonnull diff --git a/src/main/java/net/dv8tion/jda/internal/managers/ApplicationEmojiManagerImpl.java b/src/main/java/net/dv8tion/jda/internal/managers/ApplicationEmojiManagerImpl.java index 0fdb3b9b17..43bc851f95 100644 --- a/src/main/java/net/dv8tion/jda/internal/managers/ApplicationEmojiManagerImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/managers/ApplicationEmojiManagerImpl.java @@ -54,9 +54,8 @@ public ApplicationEmojiManagerImpl reset(long... fields) @Override public ApplicationEmojiManager setName(@NotNull String name) { - Checks.notBlank(name, "Name"); - name = name.trim(); - Checks.inRange(name, 2, 32, "Name"); + Checks.inRange(name, 2, 32, "Emoji name"); + Checks.matches(name, Checks.ALPHANUMERIC_WITH_DASH, "Emoji name"); this.name = name; set |= NAME; return this; From 234d3983e1c818a0047b47794b364106df3008e4 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Wed, 4 Sep 2024 15:48:56 +0200 Subject: [PATCH 03/11] Remove unused import --- src/main/java/net/dv8tion/jda/internal/JDAImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java index 03bd0af4ae..2a61e5fa2d 100644 --- a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java @@ -88,7 +88,6 @@ import org.slf4j.MDC; import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.time.OffsetDateTime; import java.util.*; import java.util.concurrent.*; From e091914750e1f3ca6554f0510e138c12b3db7a51 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Wed, 4 Sep 2024 16:47:29 +0200 Subject: [PATCH 04/11] Remove duplicate methods to align with normal Guild emoji handling Add java doc to JDA application emoji methods --- src/main/java/net/dv8tion/jda/api/JDA.java | 59 ++++++++++++------- .../dv8tion/jda/api/entities/emoji/Emoji.java | 5 +- .../net/dv8tion/jda/internal/JDAImpl.java | 24 -------- 3 files changed, 40 insertions(+), 48 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/api/JDA.java b/src/main/java/net/dv8tion/jda/api/JDA.java index c557909898..ce4d595a9d 100644 --- a/src/main/java/net/dv8tion/jda/api/JDA.java +++ b/src/main/java/net/dv8tion/jda/api/JDA.java @@ -1751,14 +1751,40 @@ default List getEmojisByName(@Nonnull String name, boolean igno return getEmojiCache().getElementsByName(name, ignoreCase); } + /** + * Creates a new {@link ApplicationEmoji} for this JDA. + * + *

Note that a JDA is limited to 2000 normal/animated emojis. + * + * @param name + * The name for the new emoji (2-30 characters) + * @param icon + * The {@link Icon} for the new emoji + */ @Nonnull @CheckReturnValue RestAction createApplicationEmoji(@Nonnull String name, @Nonnull Icon icon); + /** + * Retrieves a list of Application Emojis together with their respective creators. + * + * @return {@link RestAction RestAction} - Type: List of {@link ApplicationEmoji} + */ @Nonnull @CheckReturnValue RestAction> retrieveApplicationEmojis(); + /** + * Retrieves an application emoji together with its respective creator. + * + * @param emojiId + * The emoji id + * + * @throws IllegalArgumentException + * If the provided id is not a valid snowflake + * + * @return {@link RestAction RestAction} - Type: {@link ApplicationEmoji} + */ @Nonnull @CheckReturnValue default RestAction retrieveApplicationEmojiById(long emojiId) @@ -1766,32 +1792,21 @@ default RestAction retrieveApplicationEmojiById(long emojiId) return retrieveApplicationEmojiById(String.valueOf(emojiId)); } + /** + * Retrieves an application emoji together with its respective creator. + * + * @param emojiId + * The emoji id + * + * @throws IllegalArgumentException + * If the provided id is not a valid snowflake + * + * @return {@link RestAction RestAction} - Type: {@link ApplicationEmoji} + */ @Nonnull @CheckReturnValue RestAction retrieveApplicationEmojiById(@Nonnull String emojiId); - @Nonnull - @CheckReturnValue - default RestAction updateApplicationEmojiName(long emojiId, @Nonnull String name) - { - return updateApplicationEmojiName(String.valueOf(emojiId), name); - } - - @Nonnull - @CheckReturnValue - RestAction updateApplicationEmojiName(@Nonnull String emojiId, @Nonnull String name); - - @Nonnull - @CheckReturnValue - default RestAction deleteApplicationEmojiById(long emojiId) - { - return deleteApplicationEmojiById(String.valueOf(emojiId)); - } - - @Nonnull - @CheckReturnValue - RestAction deleteApplicationEmojiById(@Nonnull String emojiId); - /** * Attempts to retrieve a {@link Sticker} object based on the provided snowflake reference. *
This works for both {@link StandardSticker} and {@link GuildSticker}, and you can resolve them using the provided {@link StickerUnion}. diff --git a/src/main/java/net/dv8tion/jda/api/entities/emoji/Emoji.java b/src/main/java/net/dv8tion/jda/api/entities/emoji/Emoji.java index 542b7a87f0..60d7666a40 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/emoji/Emoji.java +++ b/src/main/java/net/dv8tion/jda/api/entities/emoji/Emoji.java @@ -233,9 +233,10 @@ enum Type */ UNICODE, /** - * Custom Guild Emoji. + * Custom Guild Emoji or Custom Application Emoji. *
This represents emojis which were created by users and added to a guild. + *
This can also represent emojis which were created by users and added to a jda. */ - CUSTOM, + CUSTOM } } diff --git a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java index 2a61e5fa2d..41fee52ddf 100644 --- a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java @@ -83,7 +83,6 @@ import net.dv8tion.jda.internal.utils.config.ThreadingConfig; import okhttp3.OkHttpClient; import okhttp3.RequestBody; -import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.MDC; @@ -724,29 +723,6 @@ public RestAction retrieveApplicationEmojiById(@Nonnull String ); } - @Nonnull - @Override - public RestAction updateApplicationEmojiName(@Nonnull String emojiId, @NotNull String name) - { - Checks.inRange(name, 2, 32, "Emoji name"); - Checks.matches(name, Checks.ALPHANUMERIC_WITH_DASH, "Emoji name"); - - Route.CompiledRoute route = Route.Applications.MODIFY_APPLICATION_EMOJI.compile(getSelfUser().getApplicationId(), emojiId); - DataObject body = DataObject.empty(); - body.put("name", name); - return new RestActionImpl<>(this, route, body, - (response, request) -> entityBuilder.createApplicationEmoji(this, response.getObject()) - ); - } - - @Nonnull - @Override - public RestAction deleteApplicationEmojiById(@Nonnull String emojiId) - { - Route.CompiledRoute route = Route.Applications.DELETE_APPLICATION_EMOJI.compile(getSelfUser().getApplicationId(), emojiId); - return new RestActionImpl<>(this, route); - } - @Nonnull @Override public RestAction retrieveSticker(@Nonnull StickerSnowflake sticker) From 9d48ce5e26b9abbc99079d1333c32f18d37c0bd5 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Wed, 4 Sep 2024 17:06:10 +0200 Subject: [PATCH 05/11] Update JavaDoc and add comma back after Emoji.Type.CUSTOM --- src/main/java/net/dv8tion/jda/api/JDA.java | 4 ++-- src/main/java/net/dv8tion/jda/api/entities/emoji/Emoji.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/api/JDA.java b/src/main/java/net/dv8tion/jda/api/JDA.java index ce4d595a9d..ac1bbc144b 100644 --- a/src/main/java/net/dv8tion/jda/api/JDA.java +++ b/src/main/java/net/dv8tion/jda/api/JDA.java @@ -1752,9 +1752,9 @@ default List getEmojisByName(@Nonnull String name, boolean igno } /** - * Creates a new {@link ApplicationEmoji} for this JDA. + * Creates a new {@link ApplicationEmoji} for this bot. * - *

Note that a JDA is limited to 2000 normal/animated emojis. + *

Note that the bot is limited to 2000 Application Emojis (normal and animated). * * @param name * The name for the new emoji (2-30 characters) diff --git a/src/main/java/net/dv8tion/jda/api/entities/emoji/Emoji.java b/src/main/java/net/dv8tion/jda/api/entities/emoji/Emoji.java index 60d7666a40..5d14b908d3 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/emoji/Emoji.java +++ b/src/main/java/net/dv8tion/jda/api/entities/emoji/Emoji.java @@ -237,6 +237,6 @@ enum Type *
This represents emojis which were created by users and added to a guild. *
This can also represent emojis which were created by users and added to a jda. */ - CUSTOM + CUSTOM, } } From 1596e1a9a1da356b7729d6767c4ce911fb086efe Mon Sep 17 00:00:00 2001 From: yoyosource Date: Mon, 7 Oct 2024 08:51:25 +0200 Subject: [PATCH 06/11] Fix some Peer-Review messages --- src/main/java/net/dv8tion/jda/api/JDA.java | 9 ++--- .../api/entities/emoji/ApplicationEmoji.java | 20 +++++++++- .../jda/api/entities/emoji/CustomEmoji.java | 2 + .../dv8tion/jda/api/entities/emoji/Emoji.java | 2 +- .../jda/api/entities/emoji/EmojiUnion.java | 25 +++++++++++- .../api/managers/ApplicationEmojiManager.java | 18 ++++++++- .../net/dv8tion/jda/internal/JDAImpl.java | 3 +- .../jda/internal/entities/EntityBuilder.java | 5 +-- .../jda/internal/entities/GuildImpl.java | 2 +- .../entities/emoji/ApplicationEmojiImpl.java | 40 +++++++++++++++---- .../entities/emoji/CustomEmojiImpl.java | 16 ++++++++ .../entities/emoji/RichCustomEmojiImpl.java | 15 +++++++ .../entities/emoji/UnicodeEmojiImpl.java | 16 ++++++++ .../managers/ApplicationEmojiManagerImpl.java | 18 ++++++++- 14 files changed, 167 insertions(+), 24 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/api/JDA.java b/src/main/java/net/dv8tion/jda/api/JDA.java index ac1bbc144b..dc3df23993 100644 --- a/src/main/java/net/dv8tion/jda/api/JDA.java +++ b/src/main/java/net/dv8tion/jda/api/JDA.java @@ -1754,10 +1754,10 @@ default List getEmojisByName(@Nonnull String name, boolean igno /** * Creates a new {@link ApplicationEmoji} for this bot. * - *

Note that the bot is limited to 2000 Application Emojis (normal and animated). + *

Note that the bot is limited to {@value ApplicationEmoji#APPLICATION_EMOJI_CAP} Application Emojis (normal and animated). * * @param name - * The name for the new emoji (2-30 characters) + * The name for the new emoji (2-{@value CustomEmoji#EMOJI_NAME_MAX_LENGTH} characters) * @param icon * The {@link Icon} for the new emoji */ @@ -1780,16 +1780,13 @@ default List getEmojisByName(@Nonnull String name, boolean igno * @param emojiId * The emoji id * - * @throws IllegalArgumentException - * If the provided id is not a valid snowflake - * * @return {@link RestAction RestAction} - Type: {@link ApplicationEmoji} */ @Nonnull @CheckReturnValue default RestAction retrieveApplicationEmojiById(long emojiId) { - return retrieveApplicationEmojiById(String.valueOf(emojiId)); + return retrieveApplicationEmojiById(Long.toUnsignedString(emojiId)); } /** diff --git a/src/main/java/net/dv8tion/jda/api/entities/emoji/ApplicationEmoji.java b/src/main/java/net/dv8tion/jda/api/entities/emoji/ApplicationEmoji.java index 59c8ae1eb0..1b00e8c4fa 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/emoji/ApplicationEmoji.java +++ b/src/main/java/net/dv8tion/jda/api/entities/emoji/ApplicationEmoji.java @@ -1,3 +1,19 @@ +/* + * Copyright 2024 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.dv8tion.jda.api.entities.emoji; import net.dv8tion.jda.api.JDA; @@ -22,6 +38,8 @@ */ public interface ApplicationEmoji extends CustomEmoji { + int APPLICATION_EMOJI_CAP = 2000; + /** * The {@link net.dv8tion.jda.api.JDA JDA} instance of this emoji * @@ -33,8 +51,6 @@ public interface ApplicationEmoji extends CustomEmoji /** * The user who created this emoji * - *

This returns null, right after creating the emoji. - * * @return The user who created this emoji */ @Nullable diff --git a/src/main/java/net/dv8tion/jda/api/entities/emoji/CustomEmoji.java b/src/main/java/net/dv8tion/jda/api/entities/emoji/CustomEmoji.java index 3b4a7abc36..12ef5df24a 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/emoji/CustomEmoji.java +++ b/src/main/java/net/dv8tion/jda/api/entities/emoji/CustomEmoji.java @@ -38,6 +38,8 @@ */ public interface CustomEmoji extends Emoji, IMentionable { + int EMOJI_NAME_MAX_LENGTH = 32 + /** Template for {@link #getImageUrl()} */ String ICON_URL = "https://cdn.discordapp.com/emojis/%s.%s"; diff --git a/src/main/java/net/dv8tion/jda/api/entities/emoji/Emoji.java b/src/main/java/net/dv8tion/jda/api/entities/emoji/Emoji.java index 5d14b908d3..8faeed59f6 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/emoji/Emoji.java +++ b/src/main/java/net/dv8tion/jda/api/entities/emoji/Emoji.java @@ -235,7 +235,7 @@ enum Type /** * Custom Guild Emoji or Custom Application Emoji. *
This represents emojis which were created by users and added to a guild. - *
This can also represent emojis which were created by users and added to a jda. + *
This can also represent emojis which were created and owned by a specific application. */ CUSTOM, } diff --git a/src/main/java/net/dv8tion/jda/api/entities/emoji/EmojiUnion.java b/src/main/java/net/dv8tion/jda/api/entities/emoji/EmojiUnion.java index 18ae0f55cb..9f487c90bb 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/emoji/EmojiUnion.java +++ b/src/main/java/net/dv8tion/jda/api/entities/emoji/EmojiUnion.java @@ -22,7 +22,8 @@ * Represents possible {@link Emoji} types. * *

This delegates the emoji methods for some concrete emoji type, - * but can be converted to a concrete type using either {@link #asUnicode()} or {@link #asCustom()}. + * but can be converted to a concrete type using either {@link #asUnicode()}, + * {@link #asCustom()}, {@link #asRich()} or {@link #asApplication()}. */ public interface EmojiUnion extends Emoji { @@ -47,4 +48,26 @@ public interface EmojiUnion extends Emoji */ @Nonnull CustomEmoji asCustom(); + + /** + * Returns the underlying {@link RichCustomEmoji} if applicable. + * + * @throws IllegalStateException + * If this is not a {@link RichCustomEmoji} + * + * @return The {@link RichCustomEmoji} + */ + @Nonnull + RichCustomEmoji asRich(); + + /** + * Returns the underlying {@link ApplicationEmoji} if applicable. + * + * @throws IllegalStateException + * If this is not a {@link ApplicationEmoji} + * + * @return The {@link ApplicationEmoji} + */ + @Nonnull + ApplicationEmoji asApplication(); } diff --git a/src/main/java/net/dv8tion/jda/api/managers/ApplicationEmojiManager.java b/src/main/java/net/dv8tion/jda/api/managers/ApplicationEmojiManager.java index df70f8bec3..cb819ef55a 100644 --- a/src/main/java/net/dv8tion/jda/api/managers/ApplicationEmojiManager.java +++ b/src/main/java/net/dv8tion/jda/api/managers/ApplicationEmojiManager.java @@ -1,3 +1,19 @@ +/* + * Copyright 2024 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.dv8tion.jda.api.managers; import net.dv8tion.jda.api.entities.emoji.ApplicationEmoji; @@ -67,7 +83,7 @@ public interface ApplicationEmojiManager extends Managername of the selected {@link ApplicationEmoji}. * - *

An emoji name must be between 2-32 characters long! + *

An emoji name must be between 2-{@value CustomEmoji#EMOJI_NAME_MAX_LENGTH} characters long! *
Emoji names may only be populated with alphanumeric (with underscore and dash). * *

Example: {@code tatDab} or {@code fmgSUP} diff --git a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java index 41fee52ddf..ed08af4550 100644 --- a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java @@ -680,7 +680,7 @@ public SnowflakeCacheView getEmojiCache() @Override public RestAction createApplicationEmoji(@Nonnull String name, @Nonnull Icon icon) { - Checks.inRange(name, 2, 32, "Emoji name"); + Checks.inRange(name, 2, CustomEmoji.EMOJI_NAME_MAX_LENGTH, "Emoji name"); Checks.notNull(icon, "Emoji icon"); DataObject body = DataObject.empty(); @@ -717,6 +717,7 @@ public RestAction> retrieveApplicationEmojis() @Override public RestAction retrieveApplicationEmojiById(@Nonnull String emojiId) { + Checks.isSnowflake(emojiId); Route.CompiledRoute route = Route.Applications.GET_APPLICATION_EMOJI.compile(getSelfUser().getApplicationId(), emojiId); return new RestActionImpl<>(this, route, (response, request) -> entityBuilder.createApplicationEmoji(this, response.getObject()) 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 3410300520..7fb94a3abe 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -1023,9 +1023,8 @@ public RichCustomEmojiImpl createEmoji(GuildImpl guildObj, DataObject json) public ApplicationEmojiImpl createApplicationEmoji(JDAImpl api, DataObject json) { - final long emojiId = json.getLong("id"); - // Nullable when creating emojis, at least - final User user = json.optObject("user").map(this::createUser).orElse(null); + final long emojiId = json.getUnsignedLong("id"); + final User user = createUser(json.getObject("user")); return new ApplicationEmojiImpl(emojiId, api, user) .setAnimated(json.getBoolean("animated")) .setName(json.getString("name")); diff --git a/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java index 0afde36341..1d07e7f538 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java @@ -1875,7 +1875,7 @@ public RoleAction createRole() public AuditableRestAction createEmoji(@Nonnull String name, @Nonnull Icon icon, @Nonnull Role... roles) { checkPermission(Permission.MANAGE_GUILD_EXPRESSIONS); - Checks.inRange(name, 2, 32, "Emoji name"); + Checks.inRange(name, 2, CustomEmoji.EMOJI_NAME_MAX_LENGTH, "Emoji name"); Checks.notNull(icon, "Emoji icon"); Checks.notNull(roles, "Roles"); diff --git a/src/main/java/net/dv8tion/jda/internal/entities/emoji/ApplicationEmojiImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/emoji/ApplicationEmojiImpl.java index 2cfdf0086c..b5a6c5782b 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/emoji/ApplicationEmojiImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/emoji/ApplicationEmojiImpl.java @@ -1,9 +1,26 @@ +/* + * Copyright 2024 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.dv8tion.jda.internal.entities.emoji; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.emoji.ApplicationEmoji; import net.dv8tion.jda.api.entities.emoji.CustomEmoji; +import net.dv8tion.jda.api.entities.emoji.RichCustomEmoji; import net.dv8tion.jda.api.entities.emoji.EmojiUnion; import net.dv8tion.jda.api.entities.emoji.UnicodeEmoji; import net.dv8tion.jda.api.managers.ApplicationEmojiManager; @@ -127,10 +144,10 @@ public boolean equals(Object obj) { if (obj == this) return true; - if (!(obj instanceof CustomEmoji)) + if (!(obj instanceof ApplicationEmojiImpl)) return false; - CustomEmoji other = (CustomEmoji) obj; + ApplicationEmojiImpl other = (ApplicationEmojiImpl) obj; return this.id == other.getIdLong(); } @@ -148,11 +165,6 @@ public String toString() .toString(); } - public ApplicationEmojiImpl copy() - { - return new ApplicationEmojiImpl(id, api, owner).setAnimated(animated).setName(name); - } - @Nonnull @Override public UnicodeEmoji asUnicode() @@ -166,4 +178,18 @@ public CustomEmoji asCustom() { return this; } + + @Nonnull + @Override + public RichCustomEmoji asRich() + { + throw new IllegalStateException("Cannot convert ApplicationEmoji to RichCustomEmoji!"); + } + + @Nonnull + @Override + public ApplicationEmoji asApplication() + { + return this; + } } diff --git a/src/main/java/net/dv8tion/jda/internal/entities/emoji/CustomEmojiImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/emoji/CustomEmojiImpl.java index 9f036acbc5..5ae051710a 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/emoji/CustomEmojiImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/emoji/CustomEmojiImpl.java @@ -16,7 +16,9 @@ package net.dv8tion.jda.internal.entities.emoji; +import net.dv8tion.jda.api.entities.emoji.ApplicationEmoji; import net.dv8tion.jda.api.entities.emoji.CustomEmoji; +import net.dv8tion.jda.api.entities.emoji.RichCustomEmoji; import net.dv8tion.jda.api.entities.emoji.EmojiUnion; import net.dv8tion.jda.api.entities.emoji.UnicodeEmoji; import net.dv8tion.jda.api.utils.data.DataObject; @@ -125,4 +127,18 @@ public CustomEmoji asCustom() { return this; } + + @Nonnull + @Override + public RichCustomEmoji asRich() + { + throw new IllegalStateException("Cannot convert CustomEmoji to RichCustomEmoji!"); + } + + @Nonnull + @Override + public ApplicationEmoji asApplication() + { + throw new IllegalStateException("Cannot convert CustomEmoji to ApplicationEmoji!"); + } } diff --git a/src/main/java/net/dv8tion/jda/internal/entities/emoji/RichCustomEmojiImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/emoji/RichCustomEmojiImpl.java index 28c2921847..3330e1d34e 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/emoji/RichCustomEmojiImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/emoji/RichCustomEmojiImpl.java @@ -19,6 +19,7 @@ import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.entities.emoji.ApplicationEmoji; import net.dv8tion.jda.api.entities.emoji.CustomEmoji; import net.dv8tion.jda.api.entities.emoji.EmojiUnion; import net.dv8tion.jda.api.entities.emoji.RichCustomEmoji; @@ -279,4 +280,18 @@ public CustomEmoji asCustom() { return this; } + + @Nonnull + @Override + public RichCustomEmoji asRich() + { + return this; + } + + @Nonnull + @Override + public ApplicationEmoji asApplication() + { + throw new IllegalStateException("Cannot convert RichCustomEmoji to ApplicationEmoji!"); + } } diff --git a/src/main/java/net/dv8tion/jda/internal/entities/emoji/UnicodeEmojiImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/emoji/UnicodeEmojiImpl.java index 71eda48bcd..aecf0f63a7 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/emoji/UnicodeEmojiImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/emoji/UnicodeEmojiImpl.java @@ -16,7 +16,9 @@ package net.dv8tion.jda.internal.entities.emoji; +import net.dv8tion.jda.api.entities.emoji.ApplicationEmoji; import net.dv8tion.jda.api.entities.emoji.CustomEmoji; +import net.dv8tion.jda.api.entities.emoji.RichCustomEmoji; import net.dv8tion.jda.api.entities.emoji.EmojiUnion; import net.dv8tion.jda.api.entities.emoji.UnicodeEmoji; import net.dv8tion.jda.api.utils.data.DataObject; @@ -100,4 +102,18 @@ public CustomEmoji asCustom() { throw new IllegalStateException("Cannot convert UnicodeEmoji into CustomEmoji!"); } + + @Nonnull + @Override + public RichCustomEmoji asRich() + { + throw new IllegalStateException("Cannot convert UnicodeEmoji into RichCustomEmoji!"); + } + + @Nonnull + @Override + public ApplicationEmoji asApplication() + { + throw new IllegalStateException("Cannot convert UnicodeEmoji to ApplicationEmoji!"); + } } diff --git a/src/main/java/net/dv8tion/jda/internal/managers/ApplicationEmojiManagerImpl.java b/src/main/java/net/dv8tion/jda/internal/managers/ApplicationEmojiManagerImpl.java index 43bc851f95..cf7741e2fc 100644 --- a/src/main/java/net/dv8tion/jda/internal/managers/ApplicationEmojiManagerImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/managers/ApplicationEmojiManagerImpl.java @@ -1,3 +1,19 @@ +/* + * Copyright 2024 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.dv8tion.jda.internal.managers; import net.dv8tion.jda.api.entities.emoji.ApplicationEmoji; @@ -54,7 +70,7 @@ public ApplicationEmojiManagerImpl reset(long... fields) @Override public ApplicationEmojiManager setName(@NotNull String name) { - Checks.inRange(name, 2, 32, "Emoji name"); + Checks.inRange(name, 2, CustomEmoji.EMOJI_NAME_MAX_LENGTH, "Emoji name"); Checks.matches(name, Checks.ALPHANUMERIC_WITH_DASH, "Emoji name"); this.name = name; set |= NAME; From 613ecdb6a625324427b171db5036ba10b689731b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Sun, 3 Nov 2024 14:36:50 +0100 Subject: [PATCH 07/11] Add missing semi-colon --- .../java/net/dv8tion/jda/api/entities/emoji/CustomEmoji.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/dv8tion/jda/api/entities/emoji/CustomEmoji.java b/src/main/java/net/dv8tion/jda/api/entities/emoji/CustomEmoji.java index 12ef5df24a..b3e1183d0f 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/emoji/CustomEmoji.java +++ b/src/main/java/net/dv8tion/jda/api/entities/emoji/CustomEmoji.java @@ -38,7 +38,7 @@ */ public interface CustomEmoji extends Emoji, IMentionable { - int EMOJI_NAME_MAX_LENGTH = 32 + int EMOJI_NAME_MAX_LENGTH = 32; /** Template for {@link #getImageUrl()} */ String ICON_URL = "https://cdn.discordapp.com/emojis/%s.%s"; From 31ef278bc39cb22387d9f1b342f9a6f59fd043ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Sun, 3 Nov 2024 14:41:56 +0100 Subject: [PATCH 08/11] Fix missing imports and failing tests --- src/main/java/net/dv8tion/jda/api/JDA.java | 1 + .../net/dv8tion/jda/api/managers/ApplicationEmojiManager.java | 3 +++ src/main/java/net/dv8tion/jda/internal/JDAImpl.java | 1 + src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java | 1 + .../jda/internal/managers/ApplicationEmojiManagerImpl.java | 1 + 5 files changed, 7 insertions(+) diff --git a/src/main/java/net/dv8tion/jda/api/JDA.java b/src/main/java/net/dv8tion/jda/api/JDA.java index c82d09d45c..a974c20031 100644 --- a/src/main/java/net/dv8tion/jda/api/JDA.java +++ b/src/main/java/net/dv8tion/jda/api/JDA.java @@ -24,6 +24,7 @@ import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.entities.emoji.ApplicationEmoji; +import net.dv8tion.jda.api.entities.emoji.CustomEmoji; import net.dv8tion.jda.api.entities.emoji.RichCustomEmoji; import net.dv8tion.jda.api.entities.sticker.*; import net.dv8tion.jda.api.events.GenericEvent; diff --git a/src/main/java/net/dv8tion/jda/api/managers/ApplicationEmojiManager.java b/src/main/java/net/dv8tion/jda/api/managers/ApplicationEmojiManager.java index cb819ef55a..699587801b 100644 --- a/src/main/java/net/dv8tion/jda/api/managers/ApplicationEmojiManager.java +++ b/src/main/java/net/dv8tion/jda/api/managers/ApplicationEmojiManager.java @@ -17,6 +17,7 @@ package net.dv8tion.jda.api.managers; import net.dv8tion.jda.api.entities.emoji.ApplicationEmoji; +import net.dv8tion.jda.api.entities.emoji.CustomEmoji; import javax.annotation.CheckReturnValue; import javax.annotation.Nonnull; @@ -53,6 +54,7 @@ public interface ApplicationEmojiManager extends Manager Date: Sun, 3 Nov 2024 14:44:26 +0100 Subject: [PATCH 09/11] Add missing docs and improve checks --- src/main/java/net/dv8tion/jda/api/JDA.java | 5 +++++ .../dv8tion/jda/api/managers/ApplicationEmojiManager.java | 3 +++ src/main/java/net/dv8tion/jda/internal/JDAImpl.java | 1 + 3 files changed, 9 insertions(+) diff --git a/src/main/java/net/dv8tion/jda/api/JDA.java b/src/main/java/net/dv8tion/jda/api/JDA.java index a974c20031..7f50e3b45c 100644 --- a/src/main/java/net/dv8tion/jda/api/JDA.java +++ b/src/main/java/net/dv8tion/jda/api/JDA.java @@ -1761,6 +1761,11 @@ default List getEmojisByName(@Nonnull String name, boolean igno * The name for the new emoji (2-{@value CustomEmoji#EMOJI_NAME_MAX_LENGTH} characters) * @param icon * The {@link Icon} for the new emoji + * + * @throws IllegalArgumentException + * If null is provided or the name is not alphanumeric or not between 2 and {@value CustomEmoji#EMOJI_NAME_MAX_LENGTH} characters long + * + * @return {@link RestAction} - Type: {@link ApplicationEmoji} */ @Nonnull @CheckReturnValue diff --git a/src/main/java/net/dv8tion/jda/api/managers/ApplicationEmojiManager.java b/src/main/java/net/dv8tion/jda/api/managers/ApplicationEmojiManager.java index 699587801b..1d8e1def62 100644 --- a/src/main/java/net/dv8tion/jda/api/managers/ApplicationEmojiManager.java +++ b/src/main/java/net/dv8tion/jda/api/managers/ApplicationEmojiManager.java @@ -94,6 +94,9 @@ public interface ApplicationEmojiManager extends Manager getEmojiCache() public RestAction createApplicationEmoji(@Nonnull String name, @Nonnull Icon icon) { Checks.inRange(name, 2, CustomEmoji.EMOJI_NAME_MAX_LENGTH, "Emoji name"); + Checks.matches(name, Checks.ALPHANUMERIC_WITH_DASH, "Emoji name"); Checks.notNull(icon, "Emoji icon"); DataObject body = DataObject.empty(); From 1231f7945c739713138198a1d49c7f77231e5583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Sun, 3 Nov 2024 14:59:51 +0100 Subject: [PATCH 10/11] Add tests for creating application emoji --- .../CreateApplicationEmojiTest.java | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/test/java/net/dv8tion/jda/test/restaction/CreateApplicationEmojiTest.java diff --git a/src/test/java/net/dv8tion/jda/test/restaction/CreateApplicationEmojiTest.java b/src/test/java/net/dv8tion/jda/test/restaction/CreateApplicationEmojiTest.java new file mode 100644 index 0000000000..7281970fbc --- /dev/null +++ b/src/test/java/net/dv8tion/jda/test/restaction/CreateApplicationEmojiTest.java @@ -0,0 +1,95 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.test.restaction; + +import net.dv8tion.jda.api.entities.Icon; +import net.dv8tion.jda.api.requests.Method; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.entities.SelfUserImpl; +import net.dv8tion.jda.test.Constants; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.junit.jupiter.params.provider.Arguments.arguments; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@SuppressWarnings("ResultOfMethodCallIgnored") +public class CreateApplicationEmojiTest extends RestActionTest +{ + private static final String EXAMPLE_NAME = "thinking"; + private static final Icon EXAMPLE_ICON = Icon.from(new byte[]{1, 2, 3}); + + @BeforeEach + void setupMocks() + { + when(jda.createApplicationEmoji(any(), any())).thenCallRealMethod(); + when(jda.getSelfUser()).thenReturn(new SelfUserImpl(Constants.BUTLER_USER_ID, jda)); + } + + @MethodSource + @ParameterizedTest + void testNullArguments(String name, Icon icon) + { + assertThatIllegalArgumentException() + .isThrownBy(() -> jda.createApplicationEmoji(name, icon)); + } + + static Stream testNullArguments() + { + return Stream.of( + arguments(null, null), + arguments(null, EXAMPLE_ICON), + arguments(EXAMPLE_NAME, null) + ); + } + + @Test + void testWrongNameFormat() + { + assertThatIllegalArgumentException() + .isThrownBy(() -> jda.createApplicationEmoji("test with spaces", EXAMPLE_ICON)) + .withMessageContaining("must match regex"); + } + + @Test + void testWrongNameLength() + { + assertThatIllegalArgumentException() + .isThrownBy(() -> jda.createApplicationEmoji(StringUtils.repeat('a', 33), EXAMPLE_ICON)) + .withMessageContaining("must be between 2 and 32 characters long"); + } + + @Test + void testValidEmoji() + { + assertThatRequestFrom(jda.createApplicationEmoji(EXAMPLE_NAME, EXAMPLE_ICON)) + .hasMethod(Method.POST) + .hasCompiledRoute("applications/" + Constants.BUTLER_USER_ID + "/emojis") + .hasBodyEqualTo(DataObject.empty() + .put("name", EXAMPLE_NAME) + .put("image", EXAMPLE_ICON.getEncoding())) + .whenQueueCalled(); + } +} From ffd2e94946fcc02440caccc4b1a866dff1bd85be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Sun, 3 Nov 2024 15:02:38 +0100 Subject: [PATCH 11/11] Handle errors for retrieving application emojis --- src/main/java/net/dv8tion/jda/internal/JDAImpl.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java index 1755f01502..9c1be36130 100644 --- a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java @@ -708,7 +708,14 @@ public RestAction> retrieveApplicationEmojis() List list = new ArrayList<>(emojis.length()); for (int i = 0; i < emojis.length(); i++) { - list.add(entityBuilder.createApplicationEmoji(this, emojis.getObject(i))); + try + { + list.add(entityBuilder.createApplicationEmoji(this, emojis.getObject(i))); + } + catch (ParsingException e) + { + LOG.error("Failed to parse application emoji with JSON: {}", emojis.getObject(i), e); + } } return Collections.unmodifiableList(list);