Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Application hosted Emoji support #2712

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions src/main/java/net/dv8tion/jda/api/JDA.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1747,6 +1748,44 @@ default List<RichCustomEmoji> getEmojisByName(@Nonnull String name, boolean igno
return getEmojiCache().getElementsByName(name, ignoreCase);
}

@Nonnull
@CheckReturnValue
RestAction<ApplicationEmoji> createApplicationEmoji(@Nonnull String name, @Nonnull Icon icon);

@CheckReturnValue
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@CheckReturnValue
@Nonnull
@CheckReturnValue

RestAction<List<ApplicationEmoji>> retrieveApplicationEmojis();

@Nullable
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Nullable
@Nonnull

@CheckReturnValue
RestAction<ApplicationEmoji> retrieveApplicationEmojiById(long emojiId);

@Nullable
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Nullable
@Nonnull

@CheckReturnValue
default RestAction<ApplicationEmoji> retrieveApplicationEmojiById(@Nonnull String emojiId)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The String overload should be implemented, and the long overload should be the default method, to avoid converting twice

{
return retrieveApplicationEmojiById(MiscUtil.parseSnowflake(emojiId));
}

@Nullable
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Nullable
@Nonnull

@CheckReturnValue
RestAction<ApplicationEmoji> updateApplicationEmojiName(long emojiId, @Nonnull String name);

@Nullable
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Nullable
@Nonnull

@CheckReturnValue
default RestAction<ApplicationEmoji> updateApplicationEmojiName(@Nonnull String emojiId, @Nonnull String name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The String overload should be implemented, and the long overload should be the default method, to avoid converting twice

{
return updateApplicationEmojiName(MiscUtil.parseSnowflake(emojiId), name);
}

@CheckReturnValue
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@CheckReturnValue
@Nonnull
@CheckReturnValue

RestAction<Void> deleteApplicationEmojiById(long emojiId);

@CheckReturnValue
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@CheckReturnValue
@Nonnull
@CheckReturnValue

default RestAction<Void> deleteApplicationEmojiById(@Nonnull String emojiId)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The String overload should be implemented, and the long overload should be the default method, to avoid converting twice

{
return deleteApplicationEmojiById(MiscUtil.parseSnowflake(emojiId));
}

/**
* Attempts to retrieve a {@link Sticker} object based on the provided snowflake reference.
* <br>This works for both {@link StandardSticker} and {@link GuildSticker}, and you can resolve them using the provided {@link StickerUnion}.
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
*
* <p><b>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.</b>
*
* @see JDA#retrieveApplicationEmojiById(long)
* @see JDA#retrieveApplicationEmojis()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps also add JDA#createApplicationEmoji(...)

*/
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably tell when this is null, which to my knowledge is only after creating it

*
* @return The user who created this emoji
*/
@Nullable
User getOwner();

/**
* Deletes this emoji.
*
* <p>Possible ErrorResponses include:
* <ul>
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_EMOJI UNKNOWN_EMOJI}
* <br>If this emoji was already removed</li>
* </ul>
*
* @return {@link net.dv8tion.jda.api.requests.RestAction RestAction}
* The RestAction to delete this emoji.
*/
RestAction<Void> delete();
Comment on lines +50 to +51
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
*/
RestAction<Void> delete();
*/
@Nonnull
@CheckReturnValue
RestAction<Void> 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();
}
Original file line number Diff line number Diff line change
@@ -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}.
*
* <p><b>Example</b>
* <pre>{@code
* manager.setName("minn")
* .queue();
* }</pre>
*
* @see ApplicationEmoji#getManager()
*/
public interface ApplicationEmojiManager extends Manager<ApplicationEmojiManager>
{
/** 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.
*
* <p><b>Flag Constants:</b>
* <ul>
* <li>{@link #NAME}</li>
* </ul>
*
* @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.
*
* <p><b>Flag Constants:</b>
* <ul>
* <li>{@link #NAME}</li>
* </ul>
*
* @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 <b><u>name</u></b> of the selected {@link ApplicationEmoji}.
*
* <p>An emoji name <b>must</b> be between 2-32 characters long!
* <br>Emoji names may only be populated with alphanumeric (with underscore and dash).
*
* <p><b>Example</b>: {@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);
}
5 changes: 5 additions & 0 deletions src/main/java/net/dv8tion/jda/api/requests/Route.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
70 changes: 70 additions & 0 deletions src/main/java/net/dv8tion/jda/internal/JDAImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -82,6 +83,8 @@
import net.dv8tion.jda.internal.utils.config.ThreadingConfig;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.MDC;

Expand Down Expand Up @@ -675,6 +678,73 @@ public SnowflakeCacheView<RichCustomEmoji> getEmojiCache()
return CacheView.allSnowflakes(() -> guildCache.stream().map(Guild::getEmojiCache));
}

@Nonnull
@Override
public RestAction<ApplicationEmoji> 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<List<ApplicationEmoji>> 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<ApplicationEmoji> 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<ApplicationEmoji> 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<ApplicationEmoji> 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<Void> deleteApplicationEmojiById(long emojiId)
{
Route.CompiledRoute route = Route.Applications.DELETE_APPLICATION_EMOJI.compile(getSelfUser().getApplicationId());
return new RestActionImpl<>(this, route);
}

@Nonnull
@Override
public RestAction<StickerUnion> retrieveSticker(@Nonnull StickerSnowflake sticker)
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,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;
Expand Down Expand Up @@ -1015,6 +1016,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");
Expand Down
Loading
Loading