From 0fc98c5628df71ac7241660c1d0f4a5ec1314cc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B5=D0=BC=D0=B8=D0=B4=D0=BE=D0=B2=20=D0=AF=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=9A=D0=BE=D0=BD=D1=81=D1=82=D0=B0=D0=BD=D1=82?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2=D0=B8=D1=87?= Date: Mon, 28 Jul 2025 10:52:26 +0300 Subject: [PATCH 1/6] Created a Discord Subscription representation as well as a method for creating it in EntityBuilder --- .../entities/subscription/Subscription.java | 127 +++++++ .../subscription/SubscriptionStatus.java | 9 + .../jda/internal/entities/EntityBuilder.java | 352 ++++++++++-------- 3 files changed, 343 insertions(+), 145 deletions(-) create mode 100644 src/main/java/net/dv8tion/jda/api/entities/subscription/Subscription.java create mode 100644 src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionStatus.java diff --git a/src/main/java/net/dv8tion/jda/api/entities/subscription/Subscription.java b/src/main/java/net/dv8tion/jda/api/entities/subscription/Subscription.java new file mode 100644 index 0000000000..8cd76e4bee --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/subscription/Subscription.java @@ -0,0 +1,127 @@ +package net.dv8tion.jda.api.entities.subscription; + +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.utils.EntityString; + +import javax.annotation.Nullable; +import java.time.OffsetDateTime; +import java.util.List; + +/** + * Representation of a Discord Subscription + *
This class is immutable + */ +public class Subscription +{ + private final JDAImpl api; + private final Long id; + private final Long subscriberId; + private final List skuIDs; + private final List entitlementIDs; + private final List renewalSkuIDs; + private final OffsetDateTime currentPeriodStart; + private final OffsetDateTime currentPeriodEnd; + private final OffsetDateTime canceledAt; + private final SubscriptionStatus status; + private final String country; + + public Subscription(final JDAImpl api, final long id, final long subscriberId, final List skuIDs, + final List entitlementIDs, @Nullable final List renewalSkuIDs, + final OffsetDateTime currentPeriodStart, final OffsetDateTime currentPeriodEnd, @Nullable final OffsetDateTime canceledAt, + final SubscriptionStatus status, final String country) + { + + this.api = api; + this.id = id; + this.subscriberId = subscriberId; + this.skuIDs = skuIDs; + this.entitlementIDs = entitlementIDs; + this.renewalSkuIDs = renewalSkuIDs; + this.currentPeriodStart = currentPeriodStart; + this.currentPeriodEnd = currentPeriodEnd; + this.canceledAt = canceledAt; + this.status = status; + this.country = country; + } + + public Long getId() + { + return id; + } + + public Long getSubscriberId() + { + return subscriberId; + } + + public List getSkuIDs() + { + return skuIDs; + } + + public List getEntitlementIDs() + { + return entitlementIDs; + } + + public List getRenewalSkuIDs() + { + return renewalSkuIDs; + } + + public OffsetDateTime getCurrentPeriodStart() + { + return currentPeriodStart; + } + + public OffsetDateTime getCurrentPeriodEnd() + { + return currentPeriodEnd; + } + + public OffsetDateTime getCanceledAt() + { + return canceledAt; + } + + public SubscriptionStatus getStatus() + { + return status; + } + + public String getCountry() + { + return country; + } + + public JDAImpl getApi() + { + return api; + } + + @Override + public int hashCode() + { + return id.hashCode(); + } + + @Override + public boolean equals(Object obj) + { + if(this == obj) + return true; + if(!(obj instanceof Subscription)) + { + return false; + } + Subscription other = (Subscription) obj; + return other.id.equals(this.id); + } + + @Override + public String toString(){ + return new EntityString(this) + .addMetadata("id", id) + .toString(); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionStatus.java b/src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionStatus.java new file mode 100644 index 0000000000..bbf9afdce8 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionStatus.java @@ -0,0 +1,9 @@ +package net.dv8tion.jda.api.entities.subscription; + +/** + * Representation of a Discord Subscription Status + */ +public enum SubscriptionStatus +{ + ACTIVE, ENDING, INACTIVE +} 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 14bb7e5189..4edc29c154 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -48,6 +48,8 @@ import net.dv8tion.jda.api.entities.messages.MessagePoll; import net.dv8tion.jda.api.entities.messages.MessageSnapshot; import net.dv8tion.jda.api.entities.sticker.*; +import net.dv8tion.jda.api.entities.subscription.Subscription; +import net.dv8tion.jda.api.entities.subscription.SubscriptionStatus; import net.dv8tion.jda.api.entities.templates.Template; import net.dv8tion.jda.api.entities.templates.TemplateChannel; import net.dv8tion.jda.api.entities.templates.TemplateGuild; @@ -105,6 +107,7 @@ public class EntityBuilder extends AbstractEntityBuilder public static final String MISSING_USER = "MISSING_USER"; public static final String UNKNOWN_MESSAGE_TYPE = "UNKNOWN_MESSAGE_TYPE"; private static final Set richGameFields; + static { Set tmp = new HashSet<>(); @@ -234,13 +237,13 @@ private void createGuildStickerPass(GuildImpl guildObj, DataArray array) if (object.isNull("id")) { LOG.error("Received GUILD_CREATE with a sticker with a null ID. GuildId: {} JSON: {}", - guildObj.getId(), object); + guildObj.getId(), object); continue; } if (object.getInt("type", -1) != Sticker.Type.GUILD.getId()) { LOG.error("Received GUILD_CREATE with sticker that had an unexpected type. GuildId: {} Type: {} JSON: {}", - guildObj.getId(), object.getInt("type", -1), object); + guildObj.getId(), object.getInt("type", -1), object); continue; } @@ -270,8 +273,8 @@ public SecurityIncidentDetections createSecurityIncidentsDetections(DataObject d return null; return new SecurityIncidentDetections( - timeDmSpamDetected == null ? 0 : Helpers.toTimestamp(timeDmSpamDetected), - timeRaidDetected == null ? 0 : Helpers.toTimestamp(timeRaidDetected) + timeDmSpamDetected == null ? 0 : Helpers.toTimestamp(timeDmSpamDetected), + timeRaidDetected == null ? 0 : Helpers.toTimestamp(timeRaidDetected) ); } @@ -323,12 +326,12 @@ public GuildImpl createGuild(long guildId, DataObject guildJson, TLongObjectMap< .setMaxMembers(maxMembers) .setMaxPresences(maxPresences) .setOwnerId(ownerId) - .setAfkTimeout(Guild.Timeout.fromKey(afkTimeout)) + .setAfkTimeout(Timeout.fromKey(afkTimeout)) .setSecurityIncidentActions(securityIncidentActions) .setSecurityIncidentDetections(securityIncidentDetections) .setVerificationLevel(VerificationLevel.fromKey(verificationLevel)) - .setDefaultNotificationLevel(Guild.NotificationLevel.fromKey(notificationLevel)) - .setExplicitContentLevel(Guild.ExplicitContentLevel.fromKey(explicitContentLevel)) + .setDefaultNotificationLevel(NotificationLevel.fromKey(notificationLevel)) + .setExplicitContentLevel(ExplicitContentLevel.fromKey(explicitContentLevel)) .setRequiredMFALevel(Guild.MFALevel.fromKey(mfaLevel)) .setLocale(DiscordLocale.from(locale)) .setBoostCount(boostCount) @@ -344,9 +347,9 @@ public GuildImpl createGuild(long guildId, DataObject guildJson, TLongObjectMap< } guildObj.setFeatures(featuresArray.map(array -> - array.stream(DataArray::getString) - .map(String::intern) // Prevent allocating the same feature string over and over - .collect(Collectors.toSet()) + array.stream(DataArray::getString) + .map(String::intern) // Prevent allocating the same feature string over and over + .collect(Collectors.toSet()) ).orElse(Collections.emptySet())); SnowflakeCacheViewImpl roleView = guildObj.getRolesView(); @@ -392,7 +395,8 @@ public GuildImpl createGuild(long guildId, DataObject guildJson, TLongObjectMap< LOG.error("Guild is missing a SelfMember. GuildId: {}", guildId); LOG.debug("Guild is missing a SelfMember. GuildId: {} JSON: \n{}", guildId, guildJson); // This is actually a gateway request - guildObj.retrieveMembersByIds(api.getSelfUser().getIdLong()).onSuccess(m -> { + guildObj.retrieveMembersByIds(api.getSelfUser().getIdLong()).onSuccess(m -> + { if (m.isEmpty()) LOG.warn("Was unable to recover SelfMember for guild with id {}. This guild might be corrupted!", guildId); else @@ -475,20 +479,20 @@ 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; + ? new User.Profile(id, user.getString("banner", null), user.getInt("accent_color", User.DEFAULT_ACCENT_COLOR_RAW)) + : null; if (newUser) { // Initial creation userObj.setName(user.getString("username")) - .setGlobalName(user.getString("global_name", null)) - .setDiscriminator(Short.parseShort(user.getString("discriminator", "0"))) - .setAvatarId(user.getString("avatar", null)) - .setBot(user.getBoolean("bot")) - .setSystem(user.getBoolean("system")) - .setFlags(user.getInt("public_flags", 0)) - .setProfile(profile); + .setGlobalName(user.getString("global_name", null)) + .setDiscriminator(Short.parseShort(user.getString("discriminator", "0"))) + .setAvatarId(user.getString("avatar", null)) + .setBot(user.getBoolean("bot")) + .setSystem(user.getBoolean("system")) + .setFlags(user.getInt("public_flags", 0)) + .setProfile(profile); } else { @@ -518,18 +522,18 @@ public void updateUser(UserImpl userObj, DataObject user) { userObj.setName(newName); jda.handleEvent( - new UserUpdateNameEvent( - jda, responseNumber, - userObj, oldName)); + new UserUpdateNameEvent( + jda, responseNumber, + userObj, oldName)); } if (!Objects.equals(oldGlobalName, newGlobalName)) { userObj.setGlobalName(newGlobalName); jda.handleEvent( - new UserUpdateGlobalNameEvent( - jda, responseNumber, - userObj, oldGlobalName)); + new UserUpdateGlobalNameEvent( + jda, responseNumber, + userObj, oldGlobalName)); } if (oldDiscriminator != newDiscriminator) @@ -537,18 +541,18 @@ public void updateUser(UserImpl userObj, DataObject user) String oldDiscrimString = userObj.getDiscriminator(); userObj.setDiscriminator(newDiscriminator); jda.handleEvent( - new UserUpdateDiscriminatorEvent( - jda, responseNumber, - userObj, oldDiscrimString)); + new UserUpdateDiscriminatorEvent( + jda, responseNumber, + userObj, oldDiscrimString)); } if (!Objects.equals(oldAvatar, newAvatar)) { userObj.setAvatarId(newAvatar); jda.handleEvent( - new UserUpdateAvatarEvent( - jda, responseNumber, - userObj, oldAvatar)); + new UserUpdateAvatarEvent( + jda, responseNumber, + userObj, oldAvatar)); } if (oldFlags != newFlags) @@ -556,8 +560,8 @@ public void updateUser(UserImpl userObj, DataObject user) userObj.setFlags(newFlags); jda.handleEvent( new UserUpdateFlagsEvent( - jda, responseNumber, - userObj, User.UserFlag.getFlags(oldFlags))); + jda, responseNumber, + userObj, User.UserFlag.getFlags(oldFlags))); } } @@ -717,9 +721,9 @@ public void updateMember(GuildImpl guild, MemberImpl member, DataObject content, { member.setNickname(newNick); getJDA().handleEvent( - new GuildMemberUpdateNicknameEvent( - getJDA(), responseNumber, - member, oldNick)); + new GuildMemberUpdateNicknameEvent( + getJDA(), responseNumber, + member, oldNick)); } } if (content.hasKey("avatar")) @@ -745,9 +749,9 @@ public void updateMember(GuildImpl guild, MemberImpl member, DataObject content, OffsetDateTime oldTime = member.getTimeBoosted(); member.setBoostDate(epoch); getJDA().handleEvent( - new GuildMemberUpdateBoostTimeEvent( - getJDA(), responseNumber, - member, oldTime)); + new GuildMemberUpdateBoostTimeEvent( + getJDA(), responseNumber, + member, oldTime)); } } @@ -783,9 +787,9 @@ public void updateMember(GuildImpl guild, MemberImpl member, DataObject content, { member.setPending(pending); getJDA().handleEvent( - new GuildMemberUpdatePendingEvent( - getJDA(), responseNumber, - member, oldPending)); + new GuildMemberUpdatePendingEvent( + getJDA(), responseNumber, + member, oldPending)); } } @@ -797,9 +801,9 @@ public void updateMember(GuildImpl guild, MemberImpl member, DataObject content, { member.setFlags(flags); getJDA().handleEvent( - new GuildMemberUpdateFlagsEvent( - getJDA(), responseNumber, - member, Member.MemberFlag.fromRaw(oldFlags))); + new GuildMemberUpdateFlagsEvent( + getJDA(), responseNumber, + member, Member.MemberFlag.fromRaw(oldFlags))); } } @@ -834,16 +838,16 @@ private void updateMemberRoles(MemberImpl member, List newRoles, long resp if (removedRoles.size() > 0) { getJDA().handleEvent( - new GuildMemberRoleRemoveEvent( - getJDA(), responseNumber, - member, removedRoles)); + new GuildMemberRoleRemoveEvent( + getJDA(), responseNumber, + member, removedRoles)); } if (newRoles.size() > 0) { getJDA().handleEvent( - new GuildMemberRoleAddEvent( - getJDA(), responseNumber, - member, newRoles)); + new GuildMemberRoleAddEvent( + getJDA(), responseNumber, + member, newRoles)); } } @@ -916,8 +920,8 @@ public static Activity createActivity(DataObject gameJson) try { type = gameJson.isNull("type") - ? Activity.ActivityType.PLAYING - : Activity.ActivityType.fromKey(Integer.parseInt(gameJson.get("type").toString())); + ? Activity.ActivityType.PLAYING + : Activity.ActivityType.fromKey(Integer.parseInt(gameJson.get("type").toString())); } catch (NumberFormatException e) { @@ -992,8 +996,8 @@ public static Activity createActivity(DataObject gameJson) } return new RichPresenceImpl(type, name, url, - id, emoji, party, details, state, timestamps, syncId, sessionId, flags, - largeImageKey, largeImageText, smallImageKey, smallImageText); + id, emoji, party, details, state, timestamps, syncId, sessionId, flags, + largeImageKey, largeImageText, smallImageKey, smallImageText); } public RichCustomEmojiImpl createEmoji(GuildImpl guildObj, DataObject json) @@ -1107,8 +1111,8 @@ public Category createCategory(GuildImpl guild, DataObject json, long guildId) ChannelCacheViewImpl guildView = guild.getChannelView(); ChannelCacheViewImpl globalView = getJDA().getChannelsView(); try ( - UnlockHook glock = guildView.writeLock(); - UnlockHook jlock = globalView.writeLock()) + UnlockHook glock = guildView.writeLock(); + UnlockHook jlock = globalView.writeLock()) { channel = new CategoryImpl(id, guild); guildView.put(channel); @@ -1356,7 +1360,7 @@ public ThreadMember createThreadMember(ThreadChannelImpl threadChannel, Member m { ThreadMemberImpl threadMember = new ThreadMemberImpl(member, threadChannel); threadMember - .setJoinedTimestamp(Helpers.toTimestamp(json.getString("join_timestamp"))); + .setJoinedTimestamp(Helpers.toTimestamp(json.getString("join_timestamp"))); return threadMember; } @@ -1671,12 +1675,12 @@ private ReceivedMessage createMessage0(DataObject jsonObject, @Nullable MessageC // Message accessories MessageChannel tmpChannel = channel; // because java - final List attachments = map(jsonObject, "attachments", this::createMessageAttachment); - final List embeds = map(jsonObject, "embeds", this::createMessageEmbed); - final List reactions = map(jsonObject, "reactions", (obj) -> createMessageReaction(tmpChannel, channelId, id, obj)); - final List stickers = map(jsonObject, "sticker_items", this::createStickerItem); + final List attachments = map(jsonObject, "attachments", this::createMessageAttachment); + final List embeds = map(jsonObject, "embeds", this::createMessageEmbed); + final List reactions = map(jsonObject, "reactions", (obj) -> createMessageReaction(tmpChannel, channelId, id, obj)); + final List stickers = map(jsonObject, "sticker_items", this::createStickerItem); // Keep the unknown components so the user can read them if they want - final List components = map(jsonObject, "components", (obj) -> Components.parseComponent(MessageTopLevelComponentUnion.class, obj)); + final List components = map(jsonObject, "components", (obj) -> Components.parseComponent(MessageTopLevelComponentUnion.class, obj)); MessagePoll poll = jsonObject.optObject("poll").map(EntityBuilder::createMessagePoll).orElse(null); @@ -1739,7 +1743,7 @@ else if (channel instanceof PrivateChannel) LOG.debug("Received referenced message with unknown type. Type: {}", referenceJson.getInt("type", -1)); else if (MISSING_CHANNEL.equals(ex.getMessage())) LOG.debug("Received referenced message with unknown channel. channel_id: {} Type: {}", - referenceJson.getUnsignedLong("channel_id", 0), referenceJson.getInt("type", -1)); + referenceJson.getUnsignedLong("channel_id", 0), referenceJson.getInt("type", -1)); else throw ex; } @@ -1777,8 +1781,8 @@ else if (MISSING_CHANNEL.equals(ex.getMessage())) // Lazy Mention parsing and caching (includes reply mentions) Mentions mentions = new MessageMentionsImpl( - api, guild, content, mentionsEveryone, - jsonObject.getArray("mentions"), jsonObject.getArray("mention_roles") + api, guild, content, mentionsEveryone, + jsonObject.getArray("mentions"), jsonObject.getArray("mention_roles") ); ThreadChannel startedThread = null; @@ -1829,7 +1833,7 @@ public static MessagePollImpl createMessagePoll(DataObject data) DataObject questionData = data.getObject("question"); DataObject resultsData = data.optObject("results").orElseGet( - () -> DataObject.empty().put("answer_counts", DataArray.empty()) // FIXME: Discord bug + () -> DataObject.empty().put("answer_counts", DataArray.empty()) // FIXME: Discord bug ); boolean isFinalized = resultsData.getBoolean("is_finalized"); @@ -1843,16 +1847,17 @@ public static MessagePollImpl createMessagePoll(DataObject data) questionData.optObject("emoji").map(Emoji::fromData).orElse(null)); List answers = answersData.stream(DataArray::getObject) - .map(answer -> { + .map(answer -> + { long answerId = answer.getLong("answer_id"); DataObject media = answer.getObject("poll_media"); DataObject votes = voteMapping.get(answerId); return new MessagePoll.Answer( - answerId, - media.getString("text"), - media.optObject("emoji").map(Emoji::fromData).orElse(null), - votes != null ? votes.getInt("count") : 0, - votes != null && votes.getBoolean("me_voted") + answerId, + media.getString("text"), + media.optObject("emoji").map(Emoji::fromData).orElse(null), + votes != null ? votes.getInt("count") : 0, + votes != null && votes.getBoolean("me_voted") ); }) .collect(Helpers.toUnmodifiableList()); @@ -1868,7 +1873,7 @@ public MessageReaction createMessageReaction(MessageChannel chan, long channelId obj.optObject("count_details").map(o -> o.getInt("normal", 0)).orElse(0), obj.optObject("count_details").map(o -> o.getInt("burst", 0)).orElse(0), }; - final boolean[] me = new boolean[] { + final boolean[] me = new boolean[]{ obj.getBoolean("me"), // normal obj.getBoolean("me_burst") // super }; @@ -1914,9 +1919,9 @@ public MessageEmbed createMessageEmbed(DataObject content) { DataObject obj = content.getObject("thumbnail"); thumbnail = new Thumbnail(obj.getString("url", null), - obj.getString("proxy_url", null), - obj.getInt("width", -1), - obj.getInt("height", -1)); + obj.getString("proxy_url", null), + obj.getInt("width", -1), + obj.getInt("height", -1)); } final Provider provider; @@ -1928,7 +1933,7 @@ public MessageEmbed createMessageEmbed(DataObject content) { DataObject obj = content.getObject("provider"); provider = new Provider(obj.getString("name", null), - obj.getString("url", null)); + obj.getString("url", null)); } final AuthorInfo author; @@ -1940,9 +1945,9 @@ public MessageEmbed createMessageEmbed(DataObject content) { DataObject obj = content.getObject("author"); author = new AuthorInfo(obj.getString("name", null), - obj.getString("url", null), - obj.getString("icon_url", null), - obj.getString("proxy_icon_url", null)); + obj.getString("url", null), + obj.getString("icon_url", null), + obj.getString("proxy_icon_url", null)); } final VideoInfo video; @@ -1954,9 +1959,9 @@ public MessageEmbed createMessageEmbed(DataObject content) { DataObject obj = content.getObject("video"); video = new VideoInfo(obj.getString("url", null), - obj.getString("proxy_url", null), - obj.getInt("width", -1), - obj.getInt("height", -1)); + obj.getString("proxy_url", null), + obj.getInt("width", -1), + obj.getInt("height", -1)); } final Footer footer; @@ -1968,8 +1973,8 @@ public MessageEmbed createMessageEmbed(DataObject content) { DataObject obj = content.getObject("footer"); footer = new Footer(obj.getString("text", null), - obj.getString("icon_url", null), - obj.getString("proxy_icon_url", null)); + obj.getString("icon_url", null), + obj.getString("proxy_icon_url", null)); } final ImageInfo image; @@ -1981,16 +1986,16 @@ public MessageEmbed createMessageEmbed(DataObject content) { DataObject obj = content.getObject("image"); image = new ImageInfo(obj.getString("url", null), - obj.getString("proxy_url", null), - obj.getInt("width", -1), - obj.getInt("height", -1)); + obj.getString("proxy_url", null), + obj.getInt("width", -1), + obj.getInt("height", -1)); } final List fields = map(content, "fields", (obj) -> - new Field(obj.getString("name", null), - obj.getString("value", null), - obj.getBoolean("inline"), - false) + new Field(obj.getString("name", null), + obj.getString("value", null), + obj.getBoolean("inline"), + false) ); return createMessageEmbed(url, title, description, type, timestamp, @@ -1998,11 +2003,11 @@ public MessageEmbed createMessageEmbed(DataObject content) } public static MessageEmbed createMessageEmbed(String url, String title, String description, EmbedType type, OffsetDateTime timestamp, - int color, Thumbnail thumbnail, Provider siteProvider, AuthorInfo author, - VideoInfo videoInfo, Footer footer, ImageInfo image, List fields) + int color, Thumbnail thumbnail, Provider siteProvider, AuthorInfo author, + VideoInfo videoInfo, Footer footer, ImageInfo image, List fields) { return new MessageEmbed(url, title, description, type, timestamp, - color, thumbnail, siteProvider, author, videoInfo, footer, image, fields); + color, thumbnail, siteProvider, author, videoInfo, footer, image, fields); } public StickerItem createStickerItem(DataObject content) @@ -2040,7 +2045,7 @@ public RichStickerImpl createRichSticker(DataObject content) int sortValue = content.getInt("sort_value", -1); return new StandardStickerImpl(id, format, name, tags, description, packId, sortValue); default: - throw new IllegalArgumentException("Unknown sticker type. Type: " + type +" JSON: " + content); + throw new IllegalArgumentException("Unknown sticker type. Type: " + type + " JSON: " + content); } } @@ -2125,23 +2130,23 @@ public MessageSnapshot createMessageSnapshot(MessageReference messageReference, int flags = jsonObject.getInt("flags", 0); boolean mentionsEveryone = jsonObject.getBoolean("mention_everyone"); - List attachments = map(jsonObject, "attachments", this::createMessageAttachment); - List embeds = map(jsonObject, "embeds", this::createMessageEmbed); - List stickers = map(jsonObject, "sticker_items", this::createStickerItem); + List attachments = map(jsonObject, "attachments", this::createMessageAttachment); + List embeds = map(jsonObject, "embeds", this::createMessageEmbed); + List stickers = map(jsonObject, "sticker_items", this::createStickerItem); // Keep the unknown components so the user can read them if they want - List components = map(jsonObject, "components", (obj) -> Components.parseComponent(MessageTopLevelComponentUnion.class, obj)); + List components = map(jsonObject, "components", (obj) -> Components.parseComponent(MessageTopLevelComponentUnion.class, obj)); Guild guild = messageReference.getGuild(); // Lazy Mention parsing and caching (includes reply mentions) // This only works if the message is from the same guild Mentions mentions = new MessageMentionsImpl( - api, guild instanceof GuildImpl ? (GuildImpl) guild : null, content, mentionsEveryone, - jsonObject.getArray("mentions"), - jsonObject.optArray("mention_roles").orElseGet(DataArray::empty) + api, guild instanceof GuildImpl ? (GuildImpl) guild : null, content, mentionsEveryone, + jsonObject.getArray("mentions"), + jsonObject.optArray("mention_roles").orElseGet(DataArray::empty) ); return new MessageSnapshot( - type, mentions, editTime, content, attachments, embeds, components, stickers, flags + type, mentions, editTime, content, attachments, embeds, components, stickers, flags ); } @@ -2196,10 +2201,10 @@ public WebhookImpl createWebhook(DataObject object, boolean allowMissingChannel) Object avatar = !object.isNull("avatar") ? object.get("avatar") : null; DataObject fakeUser = DataObject.empty() - .put("username", name) - .put("discriminator", "0000") - .put("id", id) - .put("avatar", avatar); + .put("username", name) + .put("discriminator", "0000") + .put("id", id) + .put("avatar", avatar); User defaultUser = createUser(fakeUser); Optional ownerJson = object.optObject("user"); @@ -2362,8 +2367,8 @@ else if (channelType.isGuild()) } return new InviteImpl(getJDA(), code, expanded, inviter, - maxAge, maxUses, temporary, timeCreated, - uses, channel, guild, group, target, type); + maxAge, maxUses, temporary, timeCreated, + uses, channel, guild, group, target, type); } public GuildWelcomeScreen createWelcomeScreen(Guild guild, DataObject object) @@ -2414,14 +2419,14 @@ public Template createTemplate(DataObject object) final List roles = new ArrayList<>(); for (int i = 0; i < roleArray.length(); i++) { - DataObject obj = roleArray.getObject(i); - final long roleId = obj.getLong("id"); - final String roleName = obj.getString("name"); - final int roleColor = obj.getInt("color"); - final boolean hoisted = obj.getBoolean("hoist"); - final boolean mentionable = obj.getBoolean("mentionable"); - final long rawPermissions = obj.getLong("permissions"); - roles.add(new TemplateRole(roleId, roleName, roleColor == 0 ? Role.DEFAULT_COLOR_RAW : roleColor, hoisted, mentionable, rawPermissions)); + DataObject obj = roleArray.getObject(i); + final long roleId = obj.getLong("id"); + final String roleName = obj.getString("name"); + final int roleColor = obj.getInt("color"); + final boolean hoisted = obj.getBoolean("hoist"); + final boolean mentionable = obj.getBoolean("mentionable"); + final long rawPermissions = obj.getLong("permissions"); + roles.add(new TemplateRole(roleId, roleName, roleColor == 0 ? Role.DEFAULT_COLOR_RAW : roleColor, hoisted, mentionable, rawPermissions)); } final List channels = new ArrayList<>(); @@ -2487,8 +2492,8 @@ public ApplicationInfo createApplicationInfo(DataObject object) final ApplicationTeam team = !object.isNull("team") ? createApplicationTeam(object.getObject("team")) : null; final String customAuthUrl = object.getString("custom_install_url", null); final List tags = object.optArray("tags").orElseGet(DataArray::empty) - .stream(DataArray::getString) - .collect(Collectors.toList()); + .stream(DataArray::getString) + .collect(Collectors.toList()); final List redirectUris = object.optArray("redirect_uris").orElseGet(DataArray::empty) .stream(DataArray::getString) .collect(Collectors.toList()); @@ -2498,33 +2503,34 @@ public ApplicationInfo createApplicationInfo(DataObject object) final Optional installParams = object.optObject("install_params"); final long defaultAuthUrlPerms = installParams.map(o -> o.getLong("permissions")) - .orElse(0L); + .orElse(0L); final List defaultAuthUrlScopes = installParams.map(obj -> obj.getArray("scopes") - .stream(DataArray::getString) - .collect(Collectors.toList())) - .orElse(Collections.emptyList()); + .stream(DataArray::getString) + .collect(Collectors.toList())) + .orElse(Collections.emptyList()); final Optional integrationTypesConfigDict = object.optObject("integration_types_config"); final Map integrationTypesConfig = integrationTypesConfigDict - .map(d -> { - final Map map = new EnumMap<>(IntegrationType.class); - for (String key : d.keys()) + .map(d -> { - final DataObject value = d.getObject(key); - - final ApplicationInfo.InstallParameters installParameters = value.optObject("oauth2_install_params") - .map(oauth2InstallParams -> new ApplicationInfoImpl.InstallParametersImpl( - oauth2InstallParams.getArray("scopes").stream(DataArray::getString).collect(Collectors.toList()), - Permission.getPermissions(oauth2InstallParams.getLong("permissions")) - )) - .orElse(null); - - map.put(IntegrationType.fromKey(key), new ApplicationInfoImpl.IntegrationTypeConfigurationImpl(installParameters)); - } - return map; - }) - .orElse(Collections.emptyMap()); + final Map map = new EnumMap<>(IntegrationType.class); + for (String key : d.keys()) + { + final DataObject value = d.getObject(key); + + final ApplicationInfo.InstallParameters installParameters = value.optObject("oauth2_install_params") + .map(oauth2InstallParams -> new ApplicationInfoImpl.InstallParametersImpl( + oauth2InstallParams.getArray("scopes").stream(DataArray::getString).collect(Collectors.toList()), + Permission.getPermissions(oauth2InstallParams.getLong("permissions")) + )) + .orElse(null); + + map.put(IntegrationType.fromKey(key), new ApplicationInfoImpl.IntegrationTypeConfigurationImpl(installParameters)); + } + return map; + }) + .orElse(Collections.emptyMap()); final long approxUserInstallCount = object.getLong("approximate_user_install_count", -1); @@ -2539,7 +2545,8 @@ public ApplicationTeam createApplicationTeam(DataObject object) String iconId = object.getString("icon", null); long id = object.getUnsignedLong("id"); long ownerId = object.getUnsignedLong("owner_user_id", 0); - List members = map(object, "members", (o) -> { + List members = map(object, "members", (o) -> + { DataObject userJson = o.getObject("user"); TeamMember.MembershipState state = TeamMember.MembershipState.fromKey(o.getInt("membership_state")); User user = createUser(userJson); @@ -2596,6 +2603,61 @@ public AuditLogChange createAuditLogChange(DataObject change) return new AuditLogChange(oldValue, newValue, key); } + public Subscription createSubscription(DataObject object) + { + DataArray skuIDs = object.getArray("sku_ids"); + DataArray entitlementsIDs = object.getArray("entitlement_ids"); + DataArray renewalSkuIDs = object.optArray("renewal_sku_ids").orElse(null); + OffsetDateTime canceledAt = object.getOffsetDateTime("canceled_at", null); + + + List mappedSkuIds = mapDataArrayToLongList(skuIDs); + List mappedEntitlementsIds = mapDataArrayToLongList(entitlementsIDs); + List mappedRenewalSkuIds = Optional.ofNullable(renewalSkuIDs) + .map(this::mapDataArrayToLongList) + .orElse(null); + + + return new Subscription( + getJDA(), + object.getUnsignedLong("id"), + object.getUnsignedLong("user_id", 0), + mappedSkuIds, + mappedEntitlementsIds, + mappedRenewalSkuIds, + object.getOffsetDateTime("current_period_start"), + object.getOffsetDateTime("current_period_end"), + canceledAt, + mapJsonStatusToSubscriptionStatus(object.getInt("status")), + object.getString("country?") + ); + } + + public List mapDataArrayToLongList(DataArray dataArray) + { + return IntStream.range(0, dataArray.length()) + .mapToObj(dataArray::getLong) + .collect(Collectors.toList()); + } + + public SubscriptionStatus mapJsonStatusToSubscriptionStatus(int value) + { + SubscriptionStatus status; + switch (value) + { + case 0: + status = SubscriptionStatus.ACTIVE; + break; + case 1: + status = SubscriptionStatus.ENDING; + break; + default: + status = SubscriptionStatus.INACTIVE; + break; + } + return status; + } + public Entitlement createEntitlement(DataObject object) { return new EntitlementImpl( @@ -2620,7 +2682,7 @@ private Map changeToMap(Set changesList) private List map(DataObject jsonObject, String key, Function convert) { - if (jsonObject.isNull(key)) + if (jsonObject.isNull(key)) return Collections.emptyList(); final DataArray arr = jsonObject.getArray(key); From 3e19266fa6f42b68914eb6b01a91cce86f9d1cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B5=D0=BC=D0=B8=D0=B4=D0=BE=D0=B2=20=D0=AF=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=9A=D0=BE=D0=BD=D1=81=D1=82=D0=B0=D0=BD=D1=82?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2=D0=B8=D1=87?= Date: Mon, 28 Jul 2025 13:37:53 +0300 Subject: [PATCH 2/6] Revert EntityBuilder and adds fromKey method to SubscriptionStatus --- .../entities/subscription/Subscription.java | 23 +- .../subscription/SubscriptionStatus.java | 24 +- .../jda/internal/entities/EntityBuilder.java | 347 ++++++++---------- 3 files changed, 194 insertions(+), 200 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/api/entities/subscription/Subscription.java b/src/main/java/net/dv8tion/jda/api/entities/subscription/Subscription.java index 8cd76e4bee..ca8a903553 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/subscription/Subscription.java +++ b/src/main/java/net/dv8tion/jda/api/entities/subscription/Subscription.java @@ -3,6 +3,7 @@ import net.dv8tion.jda.internal.JDAImpl; import net.dv8tion.jda.internal.utils.EntityString; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.time.OffsetDateTime; import java.util.List; @@ -14,8 +15,8 @@ public class Subscription { private final JDAImpl api; - private final Long id; - private final Long subscriberId; + private final long id; + private final long subscriberId; private final List skuIDs; private final List entitlementIDs; private final List renewalSkuIDs; @@ -23,12 +24,11 @@ public class Subscription private final OffsetDateTime currentPeriodEnd; private final OffsetDateTime canceledAt; private final SubscriptionStatus status; - private final String country; - public Subscription(final JDAImpl api, final long id, final long subscriberId, final List skuIDs, - final List entitlementIDs, @Nullable final List renewalSkuIDs, - final OffsetDateTime currentPeriodStart, final OffsetDateTime currentPeriodEnd, @Nullable final OffsetDateTime canceledAt, - final SubscriptionStatus status, final String country) + public Subscription(@Nonnull final JDAImpl api, final long id, final long subscriberId, @Nonnull final List skuIDs, + @Nonnull final List entitlementIDs, @Nullable final List renewalSkuIDs, + @Nonnull final OffsetDateTime currentPeriodStart, @Nonnull final OffsetDateTime currentPeriodEnd, @Nullable final OffsetDateTime canceledAt, + @Nonnull final SubscriptionStatus status) { this.api = api; @@ -41,7 +41,6 @@ public Subscription(final JDAImpl api, final long id, final long subscriberId, f this.currentPeriodEnd = currentPeriodEnd; this.canceledAt = canceledAt; this.status = status; - this.country = country; } public Long getId() @@ -89,10 +88,6 @@ public SubscriptionStatus getStatus() return status; } - public String getCountry() - { - return country; - } public JDAImpl getApi() { @@ -102,7 +97,7 @@ public JDAImpl getApi() @Override public int hashCode() { - return id.hashCode(); + return Long.hashCode(id); } @Override @@ -115,7 +110,7 @@ public boolean equals(Object obj) return false; } Subscription other = (Subscription) obj; - return other.id.equals(this.id); + return other.id == this.id; } @Override diff --git a/src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionStatus.java b/src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionStatus.java index bbf9afdce8..bad88e5298 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionStatus.java +++ b/src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionStatus.java @@ -1,9 +1,31 @@ package net.dv8tion.jda.api.entities.subscription; +import javax.annotation.Nonnull; + /** * Representation of a Discord Subscription Status */ public enum SubscriptionStatus { - ACTIVE, ENDING, INACTIVE + UNKNOWN(-1),ACTIVE(0), ENDING(1), INACTIVE(2); + + private final int id; + + SubscriptionStatus(int id){ + this.id = id; + } + + @Nonnull + public static SubscriptionStatus fromKey(int id){ + for (SubscriptionStatus status : values()){ + if(status.id == id){ + return status; + } + } + return UNKNOWN; + } + + public int getId(){ + return id; + } } 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 4edc29c154..144a18b67d 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -107,7 +107,6 @@ public class EntityBuilder extends AbstractEntityBuilder public static final String MISSING_USER = "MISSING_USER"; public static final String UNKNOWN_MESSAGE_TYPE = "UNKNOWN_MESSAGE_TYPE"; private static final Set richGameFields; - static { Set tmp = new HashSet<>(); @@ -237,13 +236,13 @@ private void createGuildStickerPass(GuildImpl guildObj, DataArray array) if (object.isNull("id")) { LOG.error("Received GUILD_CREATE with a sticker with a null ID. GuildId: {} JSON: {}", - guildObj.getId(), object); + guildObj.getId(), object); continue; } if (object.getInt("type", -1) != Sticker.Type.GUILD.getId()) { LOG.error("Received GUILD_CREATE with sticker that had an unexpected type. GuildId: {} Type: {} JSON: {}", - guildObj.getId(), object.getInt("type", -1), object); + guildObj.getId(), object.getInt("type", -1), object); continue; } @@ -273,8 +272,8 @@ public SecurityIncidentDetections createSecurityIncidentsDetections(DataObject d return null; return new SecurityIncidentDetections( - timeDmSpamDetected == null ? 0 : Helpers.toTimestamp(timeDmSpamDetected), - timeRaidDetected == null ? 0 : Helpers.toTimestamp(timeRaidDetected) + timeDmSpamDetected == null ? 0 : Helpers.toTimestamp(timeDmSpamDetected), + timeRaidDetected == null ? 0 : Helpers.toTimestamp(timeRaidDetected) ); } @@ -326,12 +325,12 @@ public GuildImpl createGuild(long guildId, DataObject guildJson, TLongObjectMap< .setMaxMembers(maxMembers) .setMaxPresences(maxPresences) .setOwnerId(ownerId) - .setAfkTimeout(Timeout.fromKey(afkTimeout)) + .setAfkTimeout(Guild.Timeout.fromKey(afkTimeout)) .setSecurityIncidentActions(securityIncidentActions) .setSecurityIncidentDetections(securityIncidentDetections) .setVerificationLevel(VerificationLevel.fromKey(verificationLevel)) - .setDefaultNotificationLevel(NotificationLevel.fromKey(notificationLevel)) - .setExplicitContentLevel(ExplicitContentLevel.fromKey(explicitContentLevel)) + .setDefaultNotificationLevel(Guild.NotificationLevel.fromKey(notificationLevel)) + .setExplicitContentLevel(Guild.ExplicitContentLevel.fromKey(explicitContentLevel)) .setRequiredMFALevel(Guild.MFALevel.fromKey(mfaLevel)) .setLocale(DiscordLocale.from(locale)) .setBoostCount(boostCount) @@ -347,9 +346,9 @@ public GuildImpl createGuild(long guildId, DataObject guildJson, TLongObjectMap< } guildObj.setFeatures(featuresArray.map(array -> - array.stream(DataArray::getString) - .map(String::intern) // Prevent allocating the same feature string over and over - .collect(Collectors.toSet()) + array.stream(DataArray::getString) + .map(String::intern) // Prevent allocating the same feature string over and over + .collect(Collectors.toSet()) ).orElse(Collections.emptySet())); SnowflakeCacheViewImpl roleView = guildObj.getRolesView(); @@ -479,20 +478,20 @@ 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; + ? new User.Profile(id, user.getString("banner", null), user.getInt("accent_color", User.DEFAULT_ACCENT_COLOR_RAW)) + : null; if (newUser) { // Initial creation userObj.setName(user.getString("username")) - .setGlobalName(user.getString("global_name", null)) - .setDiscriminator(Short.parseShort(user.getString("discriminator", "0"))) - .setAvatarId(user.getString("avatar", null)) - .setBot(user.getBoolean("bot")) - .setSystem(user.getBoolean("system")) - .setFlags(user.getInt("public_flags", 0)) - .setProfile(profile); + .setGlobalName(user.getString("global_name", null)) + .setDiscriminator(Short.parseShort(user.getString("discriminator", "0"))) + .setAvatarId(user.getString("avatar", null)) + .setBot(user.getBoolean("bot")) + .setSystem(user.getBoolean("system")) + .setFlags(user.getInt("public_flags", 0)) + .setProfile(profile); } else { @@ -522,18 +521,18 @@ public void updateUser(UserImpl userObj, DataObject user) { userObj.setName(newName); jda.handleEvent( - new UserUpdateNameEvent( - jda, responseNumber, - userObj, oldName)); + new UserUpdateNameEvent( + jda, responseNumber, + userObj, oldName)); } if (!Objects.equals(oldGlobalName, newGlobalName)) { userObj.setGlobalName(newGlobalName); jda.handleEvent( - new UserUpdateGlobalNameEvent( - jda, responseNumber, - userObj, oldGlobalName)); + new UserUpdateGlobalNameEvent( + jda, responseNumber, + userObj, oldGlobalName)); } if (oldDiscriminator != newDiscriminator) @@ -541,18 +540,18 @@ public void updateUser(UserImpl userObj, DataObject user) String oldDiscrimString = userObj.getDiscriminator(); userObj.setDiscriminator(newDiscriminator); jda.handleEvent( - new UserUpdateDiscriminatorEvent( - jda, responseNumber, - userObj, oldDiscrimString)); + new UserUpdateDiscriminatorEvent( + jda, responseNumber, + userObj, oldDiscrimString)); } if (!Objects.equals(oldAvatar, newAvatar)) { userObj.setAvatarId(newAvatar); jda.handleEvent( - new UserUpdateAvatarEvent( - jda, responseNumber, - userObj, oldAvatar)); + new UserUpdateAvatarEvent( + jda, responseNumber, + userObj, oldAvatar)); } if (oldFlags != newFlags) @@ -560,8 +559,8 @@ public void updateUser(UserImpl userObj, DataObject user) userObj.setFlags(newFlags); jda.handleEvent( new UserUpdateFlagsEvent( - jda, responseNumber, - userObj, User.UserFlag.getFlags(oldFlags))); + jda, responseNumber, + userObj, User.UserFlag.getFlags(oldFlags))); } } @@ -721,9 +720,9 @@ public void updateMember(GuildImpl guild, MemberImpl member, DataObject content, { member.setNickname(newNick); getJDA().handleEvent( - new GuildMemberUpdateNicknameEvent( - getJDA(), responseNumber, - member, oldNick)); + new GuildMemberUpdateNicknameEvent( + getJDA(), responseNumber, + member, oldNick)); } } if (content.hasKey("avatar")) @@ -749,9 +748,9 @@ public void updateMember(GuildImpl guild, MemberImpl member, DataObject content, OffsetDateTime oldTime = member.getTimeBoosted(); member.setBoostDate(epoch); getJDA().handleEvent( - new GuildMemberUpdateBoostTimeEvent( - getJDA(), responseNumber, - member, oldTime)); + new GuildMemberUpdateBoostTimeEvent( + getJDA(), responseNumber, + member, oldTime)); } } @@ -787,9 +786,9 @@ public void updateMember(GuildImpl guild, MemberImpl member, DataObject content, { member.setPending(pending); getJDA().handleEvent( - new GuildMemberUpdatePendingEvent( - getJDA(), responseNumber, - member, oldPending)); + new GuildMemberUpdatePendingEvent( + getJDA(), responseNumber, + member, oldPending)); } } @@ -801,9 +800,9 @@ public void updateMember(GuildImpl guild, MemberImpl member, DataObject content, { member.setFlags(flags); getJDA().handleEvent( - new GuildMemberUpdateFlagsEvent( - getJDA(), responseNumber, - member, Member.MemberFlag.fromRaw(oldFlags))); + new GuildMemberUpdateFlagsEvent( + getJDA(), responseNumber, + member, Member.MemberFlag.fromRaw(oldFlags))); } } @@ -838,16 +837,16 @@ private void updateMemberRoles(MemberImpl member, List newRoles, long resp if (removedRoles.size() > 0) { getJDA().handleEvent( - new GuildMemberRoleRemoveEvent( - getJDA(), responseNumber, - member, removedRoles)); + new GuildMemberRoleRemoveEvent( + getJDA(), responseNumber, + member, removedRoles)); } if (newRoles.size() > 0) { getJDA().handleEvent( - new GuildMemberRoleAddEvent( - getJDA(), responseNumber, - member, newRoles)); + new GuildMemberRoleAddEvent( + getJDA(), responseNumber, + member, newRoles)); } } @@ -920,8 +919,8 @@ public static Activity createActivity(DataObject gameJson) try { type = gameJson.isNull("type") - ? Activity.ActivityType.PLAYING - : Activity.ActivityType.fromKey(Integer.parseInt(gameJson.get("type").toString())); + ? Activity.ActivityType.PLAYING + : Activity.ActivityType.fromKey(Integer.parseInt(gameJson.get("type").toString())); } catch (NumberFormatException e) { @@ -996,8 +995,8 @@ public static Activity createActivity(DataObject gameJson) } return new RichPresenceImpl(type, name, url, - id, emoji, party, details, state, timestamps, syncId, sessionId, flags, - largeImageKey, largeImageText, smallImageKey, smallImageText); + id, emoji, party, details, state, timestamps, syncId, sessionId, flags, + largeImageKey, largeImageText, smallImageKey, smallImageText); } public RichCustomEmojiImpl createEmoji(GuildImpl guildObj, DataObject json) @@ -1111,8 +1110,8 @@ public Category createCategory(GuildImpl guild, DataObject json, long guildId) ChannelCacheViewImpl guildView = guild.getChannelView(); ChannelCacheViewImpl globalView = getJDA().getChannelsView(); try ( - UnlockHook glock = guildView.writeLock(); - UnlockHook jlock = globalView.writeLock()) + UnlockHook glock = guildView.writeLock(); + UnlockHook jlock = globalView.writeLock()) { channel = new CategoryImpl(id, guild); guildView.put(channel); @@ -1360,7 +1359,7 @@ public ThreadMember createThreadMember(ThreadChannelImpl threadChannel, Member m { ThreadMemberImpl threadMember = new ThreadMemberImpl(member, threadChannel); threadMember - .setJoinedTimestamp(Helpers.toTimestamp(json.getString("join_timestamp"))); + .setJoinedTimestamp(Helpers.toTimestamp(json.getString("join_timestamp"))); return threadMember; } @@ -1675,12 +1674,12 @@ private ReceivedMessage createMessage0(DataObject jsonObject, @Nullable MessageC // Message accessories MessageChannel tmpChannel = channel; // because java - final List attachments = map(jsonObject, "attachments", this::createMessageAttachment); - final List embeds = map(jsonObject, "embeds", this::createMessageEmbed); - final List reactions = map(jsonObject, "reactions", (obj) -> createMessageReaction(tmpChannel, channelId, id, obj)); - final List stickers = map(jsonObject, "sticker_items", this::createStickerItem); + final List attachments = map(jsonObject, "attachments", this::createMessageAttachment); + final List embeds = map(jsonObject, "embeds", this::createMessageEmbed); + final List reactions = map(jsonObject, "reactions", (obj) -> createMessageReaction(tmpChannel, channelId, id, obj)); + final List stickers = map(jsonObject, "sticker_items", this::createStickerItem); // Keep the unknown components so the user can read them if they want - final List components = map(jsonObject, "components", (obj) -> Components.parseComponent(MessageTopLevelComponentUnion.class, obj)); + final List components = map(jsonObject, "components", (obj) -> Components.parseComponent(MessageTopLevelComponentUnion.class, obj)); MessagePoll poll = jsonObject.optObject("poll").map(EntityBuilder::createMessagePoll).orElse(null); @@ -1743,7 +1742,7 @@ else if (channel instanceof PrivateChannel) LOG.debug("Received referenced message with unknown type. Type: {}", referenceJson.getInt("type", -1)); else if (MISSING_CHANNEL.equals(ex.getMessage())) LOG.debug("Received referenced message with unknown channel. channel_id: {} Type: {}", - referenceJson.getUnsignedLong("channel_id", 0), referenceJson.getInt("type", -1)); + referenceJson.getUnsignedLong("channel_id", 0), referenceJson.getInt("type", -1)); else throw ex; } @@ -1781,8 +1780,8 @@ else if (MISSING_CHANNEL.equals(ex.getMessage())) // Lazy Mention parsing and caching (includes reply mentions) Mentions mentions = new MessageMentionsImpl( - api, guild, content, mentionsEveryone, - jsonObject.getArray("mentions"), jsonObject.getArray("mention_roles") + api, guild, content, mentionsEveryone, + jsonObject.getArray("mentions"), jsonObject.getArray("mention_roles") ); ThreadChannel startedThread = null; @@ -1833,7 +1832,7 @@ public static MessagePollImpl createMessagePoll(DataObject data) DataObject questionData = data.getObject("question"); DataObject resultsData = data.optObject("results").orElseGet( - () -> DataObject.empty().put("answer_counts", DataArray.empty()) // FIXME: Discord bug + () -> DataObject.empty().put("answer_counts", DataArray.empty()) // FIXME: Discord bug ); boolean isFinalized = resultsData.getBoolean("is_finalized"); @@ -1847,17 +1846,16 @@ public static MessagePollImpl createMessagePoll(DataObject data) questionData.optObject("emoji").map(Emoji::fromData).orElse(null)); List answers = answersData.stream(DataArray::getObject) - .map(answer -> - { + .map(answer -> { long answerId = answer.getLong("answer_id"); DataObject media = answer.getObject("poll_media"); DataObject votes = voteMapping.get(answerId); return new MessagePoll.Answer( - answerId, - media.getString("text"), - media.optObject("emoji").map(Emoji::fromData).orElse(null), - votes != null ? votes.getInt("count") : 0, - votes != null && votes.getBoolean("me_voted") + answerId, + media.getString("text"), + media.optObject("emoji").map(Emoji::fromData).orElse(null), + votes != null ? votes.getInt("count") : 0, + votes != null && votes.getBoolean("me_voted") ); }) .collect(Helpers.toUnmodifiableList()); @@ -1873,7 +1871,7 @@ public MessageReaction createMessageReaction(MessageChannel chan, long channelId obj.optObject("count_details").map(o -> o.getInt("normal", 0)).orElse(0), obj.optObject("count_details").map(o -> o.getInt("burst", 0)).orElse(0), }; - final boolean[] me = new boolean[]{ + final boolean[] me = new boolean[] { obj.getBoolean("me"), // normal obj.getBoolean("me_burst") // super }; @@ -1919,9 +1917,9 @@ public MessageEmbed createMessageEmbed(DataObject content) { DataObject obj = content.getObject("thumbnail"); thumbnail = new Thumbnail(obj.getString("url", null), - obj.getString("proxy_url", null), - obj.getInt("width", -1), - obj.getInt("height", -1)); + obj.getString("proxy_url", null), + obj.getInt("width", -1), + obj.getInt("height", -1)); } final Provider provider; @@ -1933,7 +1931,7 @@ public MessageEmbed createMessageEmbed(DataObject content) { DataObject obj = content.getObject("provider"); provider = new Provider(obj.getString("name", null), - obj.getString("url", null)); + obj.getString("url", null)); } final AuthorInfo author; @@ -1945,9 +1943,9 @@ public MessageEmbed createMessageEmbed(DataObject content) { DataObject obj = content.getObject("author"); author = new AuthorInfo(obj.getString("name", null), - obj.getString("url", null), - obj.getString("icon_url", null), - obj.getString("proxy_icon_url", null)); + obj.getString("url", null), + obj.getString("icon_url", null), + obj.getString("proxy_icon_url", null)); } final VideoInfo video; @@ -1959,9 +1957,9 @@ public MessageEmbed createMessageEmbed(DataObject content) { DataObject obj = content.getObject("video"); video = new VideoInfo(obj.getString("url", null), - obj.getString("proxy_url", null), - obj.getInt("width", -1), - obj.getInt("height", -1)); + obj.getString("proxy_url", null), + obj.getInt("width", -1), + obj.getInt("height", -1)); } final Footer footer; @@ -1973,8 +1971,8 @@ public MessageEmbed createMessageEmbed(DataObject content) { DataObject obj = content.getObject("footer"); footer = new Footer(obj.getString("text", null), - obj.getString("icon_url", null), - obj.getString("proxy_icon_url", null)); + obj.getString("icon_url", null), + obj.getString("proxy_icon_url", null)); } final ImageInfo image; @@ -1986,16 +1984,16 @@ public MessageEmbed createMessageEmbed(DataObject content) { DataObject obj = content.getObject("image"); image = new ImageInfo(obj.getString("url", null), - obj.getString("proxy_url", null), - obj.getInt("width", -1), - obj.getInt("height", -1)); + obj.getString("proxy_url", null), + obj.getInt("width", -1), + obj.getInt("height", -1)); } final List fields = map(content, "fields", (obj) -> - new Field(obj.getString("name", null), - obj.getString("value", null), - obj.getBoolean("inline"), - false) + new Field(obj.getString("name", null), + obj.getString("value", null), + obj.getBoolean("inline"), + false) ); return createMessageEmbed(url, title, description, type, timestamp, @@ -2003,11 +2001,11 @@ public MessageEmbed createMessageEmbed(DataObject content) } public static MessageEmbed createMessageEmbed(String url, String title, String description, EmbedType type, OffsetDateTime timestamp, - int color, Thumbnail thumbnail, Provider siteProvider, AuthorInfo author, - VideoInfo videoInfo, Footer footer, ImageInfo image, List fields) + int color, Thumbnail thumbnail, Provider siteProvider, AuthorInfo author, + VideoInfo videoInfo, Footer footer, ImageInfo image, List fields) { return new MessageEmbed(url, title, description, type, timestamp, - color, thumbnail, siteProvider, author, videoInfo, footer, image, fields); + color, thumbnail, siteProvider, author, videoInfo, footer, image, fields); } public StickerItem createStickerItem(DataObject content) @@ -2045,7 +2043,7 @@ public RichStickerImpl createRichSticker(DataObject content) int sortValue = content.getInt("sort_value", -1); return new StandardStickerImpl(id, format, name, tags, description, packId, sortValue); default: - throw new IllegalArgumentException("Unknown sticker type. Type: " + type + " JSON: " + content); + throw new IllegalArgumentException("Unknown sticker type. Type: " + type +" JSON: " + content); } } @@ -2130,23 +2128,23 @@ public MessageSnapshot createMessageSnapshot(MessageReference messageReference, int flags = jsonObject.getInt("flags", 0); boolean mentionsEveryone = jsonObject.getBoolean("mention_everyone"); - List attachments = map(jsonObject, "attachments", this::createMessageAttachment); - List embeds = map(jsonObject, "embeds", this::createMessageEmbed); - List stickers = map(jsonObject, "sticker_items", this::createStickerItem); + List attachments = map(jsonObject, "attachments", this::createMessageAttachment); + List embeds = map(jsonObject, "embeds", this::createMessageEmbed); + List stickers = map(jsonObject, "sticker_items", this::createStickerItem); // Keep the unknown components so the user can read them if they want - List components = map(jsonObject, "components", (obj) -> Components.parseComponent(MessageTopLevelComponentUnion.class, obj)); + List components = map(jsonObject, "components", (obj) -> Components.parseComponent(MessageTopLevelComponentUnion.class, obj)); Guild guild = messageReference.getGuild(); // Lazy Mention parsing and caching (includes reply mentions) // This only works if the message is from the same guild Mentions mentions = new MessageMentionsImpl( - api, guild instanceof GuildImpl ? (GuildImpl) guild : null, content, mentionsEveryone, - jsonObject.getArray("mentions"), - jsonObject.optArray("mention_roles").orElseGet(DataArray::empty) + api, guild instanceof GuildImpl ? (GuildImpl) guild : null, content, mentionsEveryone, + jsonObject.getArray("mentions"), + jsonObject.optArray("mention_roles").orElseGet(DataArray::empty) ); return new MessageSnapshot( - type, mentions, editTime, content, attachments, embeds, components, stickers, flags + type, mentions, editTime, content, attachments, embeds, components, stickers, flags ); } @@ -2201,10 +2199,10 @@ public WebhookImpl createWebhook(DataObject object, boolean allowMissingChannel) Object avatar = !object.isNull("avatar") ? object.get("avatar") : null; DataObject fakeUser = DataObject.empty() - .put("username", name) - .put("discriminator", "0000") - .put("id", id) - .put("avatar", avatar); + .put("username", name) + .put("discriminator", "0000") + .put("id", id) + .put("avatar", avatar); User defaultUser = createUser(fakeUser); Optional ownerJson = object.optObject("user"); @@ -2367,8 +2365,8 @@ else if (channelType.isGuild()) } return new InviteImpl(getJDA(), code, expanded, inviter, - maxAge, maxUses, temporary, timeCreated, - uses, channel, guild, group, target, type); + maxAge, maxUses, temporary, timeCreated, + uses, channel, guild, group, target, type); } public GuildWelcomeScreen createWelcomeScreen(Guild guild, DataObject object) @@ -2419,14 +2417,14 @@ public Template createTemplate(DataObject object) final List roles = new ArrayList<>(); for (int i = 0; i < roleArray.length(); i++) { - DataObject obj = roleArray.getObject(i); - final long roleId = obj.getLong("id"); - final String roleName = obj.getString("name"); - final int roleColor = obj.getInt("color"); - final boolean hoisted = obj.getBoolean("hoist"); - final boolean mentionable = obj.getBoolean("mentionable"); - final long rawPermissions = obj.getLong("permissions"); - roles.add(new TemplateRole(roleId, roleName, roleColor == 0 ? Role.DEFAULT_COLOR_RAW : roleColor, hoisted, mentionable, rawPermissions)); + DataObject obj = roleArray.getObject(i); + final long roleId = obj.getLong("id"); + final String roleName = obj.getString("name"); + final int roleColor = obj.getInt("color"); + final boolean hoisted = obj.getBoolean("hoist"); + final boolean mentionable = obj.getBoolean("mentionable"); + final long rawPermissions = obj.getLong("permissions"); + roles.add(new TemplateRole(roleId, roleName, roleColor == 0 ? Role.DEFAULT_COLOR_RAW : roleColor, hoisted, mentionable, rawPermissions)); } final List channels = new ArrayList<>(); @@ -2492,8 +2490,8 @@ public ApplicationInfo createApplicationInfo(DataObject object) final ApplicationTeam team = !object.isNull("team") ? createApplicationTeam(object.getObject("team")) : null; final String customAuthUrl = object.getString("custom_install_url", null); final List tags = object.optArray("tags").orElseGet(DataArray::empty) - .stream(DataArray::getString) - .collect(Collectors.toList()); + .stream(DataArray::getString) + .collect(Collectors.toList()); final List redirectUris = object.optArray("redirect_uris").orElseGet(DataArray::empty) .stream(DataArray::getString) .collect(Collectors.toList()); @@ -2503,34 +2501,33 @@ public ApplicationInfo createApplicationInfo(DataObject object) final Optional installParams = object.optObject("install_params"); final long defaultAuthUrlPerms = installParams.map(o -> o.getLong("permissions")) - .orElse(0L); + .orElse(0L); final List defaultAuthUrlScopes = installParams.map(obj -> obj.getArray("scopes") - .stream(DataArray::getString) - .collect(Collectors.toList())) - .orElse(Collections.emptyList()); + .stream(DataArray::getString) + .collect(Collectors.toList())) + .orElse(Collections.emptyList()); final Optional integrationTypesConfigDict = object.optObject("integration_types_config"); final Map integrationTypesConfig = integrationTypesConfigDict - .map(d -> + .map(d -> { + final Map map = new EnumMap<>(IntegrationType.class); + for (String key : d.keys()) { - final Map map = new EnumMap<>(IntegrationType.class); - for (String key : d.keys()) - { - final DataObject value = d.getObject(key); - - final ApplicationInfo.InstallParameters installParameters = value.optObject("oauth2_install_params") - .map(oauth2InstallParams -> new ApplicationInfoImpl.InstallParametersImpl( - oauth2InstallParams.getArray("scopes").stream(DataArray::getString).collect(Collectors.toList()), - Permission.getPermissions(oauth2InstallParams.getLong("permissions")) - )) - .orElse(null); - - map.put(IntegrationType.fromKey(key), new ApplicationInfoImpl.IntegrationTypeConfigurationImpl(installParameters)); - } - return map; - }) - .orElse(Collections.emptyMap()); + final DataObject value = d.getObject(key); + + final ApplicationInfo.InstallParameters installParameters = value.optObject("oauth2_install_params") + .map(oauth2InstallParams -> new ApplicationInfoImpl.InstallParametersImpl( + oauth2InstallParams.getArray("scopes").stream(DataArray::getString).collect(Collectors.toList()), + Permission.getPermissions(oauth2InstallParams.getLong("permissions")) + )) + .orElse(null); + + map.put(IntegrationType.fromKey(key), new ApplicationInfoImpl.IntegrationTypeConfigurationImpl(installParameters)); + } + return map; + }) + .orElse(Collections.emptyMap()); final long approxUserInstallCount = object.getLong("approximate_user_install_count", -1); @@ -2545,8 +2542,7 @@ public ApplicationTeam createApplicationTeam(DataObject object) String iconId = object.getString("icon", null); long id = object.getUnsignedLong("id"); long ownerId = object.getUnsignedLong("owner_user_id", 0); - List members = map(object, "members", (o) -> - { + List members = map(object, "members", (o) -> { DataObject userJson = o.getObject("user"); TeamMember.MembershipState state = TeamMember.MembershipState.fromKey(o.getInt("membership_state")); User user = createUser(userJson); @@ -2603,6 +2599,23 @@ public AuditLogChange createAuditLogChange(DataObject change) return new AuditLogChange(oldValue, newValue, key); } + public Entitlement createEntitlement(DataObject object) + { + return new EntitlementImpl( + getJDA(), + object.getUnsignedLong("id"), + object.getUnsignedLong("sku_id"), + object.getUnsignedLong("application_id"), + object.getUnsignedLong("user_id", 0), + object.getUnsignedLong("guild_id", 0), + Entitlement.EntitlementType.fromKey(object.getInt("type")), + object.getBoolean("deleted"), + object.getOffsetDateTime("starts_at", null), + object.getOffsetDateTime("ends_at", null), + object.getBoolean("consumed", false) + ); + } + public Subscription createSubscription(DataObject object) { DataArray skuIDs = object.getArray("sku_ids"); @@ -2628,8 +2641,7 @@ public Subscription createSubscription(DataObject object) object.getOffsetDateTime("current_period_start"), object.getOffsetDateTime("current_period_end"), canceledAt, - mapJsonStatusToSubscriptionStatus(object.getInt("status")), - object.getString("country?") + SubscriptionStatus.fromKey(object.getInt("status")) ); } @@ -2640,41 +2652,6 @@ public List mapDataArrayToLongList(DataArray dataArray) .collect(Collectors.toList()); } - public SubscriptionStatus mapJsonStatusToSubscriptionStatus(int value) - { - SubscriptionStatus status; - switch (value) - { - case 0: - status = SubscriptionStatus.ACTIVE; - break; - case 1: - status = SubscriptionStatus.ENDING; - break; - default: - status = SubscriptionStatus.INACTIVE; - break; - } - return status; - } - - public Entitlement createEntitlement(DataObject object) - { - return new EntitlementImpl( - getJDA(), - object.getUnsignedLong("id"), - object.getUnsignedLong("sku_id"), - object.getUnsignedLong("application_id"), - object.getUnsignedLong("user_id", 0), - object.getUnsignedLong("guild_id", 0), - Entitlement.EntitlementType.fromKey(object.getInt("type")), - object.getBoolean("deleted"), - object.getOffsetDateTime("starts_at", null), - object.getOffsetDateTime("ends_at", null), - object.getBoolean("consumed", false) - ); - } - private Map changeToMap(Set changesList) { return changesList.stream().collect(Collectors.toMap(AuditLogChange::getKey, UnaryOperator.identity())); @@ -2682,7 +2659,7 @@ private Map changeToMap(Set changesList) private List map(DataObject jsonObject, String key, Function convert) { - if (jsonObject.isNull(key)) + if (jsonObject.isNull(key)) return Collections.emptyList(); final DataArray arr = jsonObject.getArray(key); From f4ef5a71f918c9f865319cd3ca2c21a73b89f058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B5=D0=BC=D0=B8=D0=B4=D0=BE=D0=B2=20=D0=AF=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=9A=D0=BE=D0=BD=D1=81=D1=82=D0=B0=D0=BD=D1=82?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2=D0=B8=D1=87?= Date: Sun, 3 Aug 2025 21:33:23 +0300 Subject: [PATCH 3/6] Add events with subscriptions Add endpoints with subscriptions --- src/main/java/net/dv8tion/jda/api/JDA.java | 27 +++ .../entities/subscription/Subscription.java | 203 +++++++++--------- .../subscription/SubscriptionImpl.java | 114 ++++++++++ .../subscription/SubscriptionStatus.java | 28 ++- .../GenericSubscriptionEvent.java | 36 ++++ .../subscription/SubscriptionCreateEvent.java | 15 ++ .../subscription/SubscriptionDeleteEvent.java | 16 ++ .../subscription/SubscriptionUpdateEvent.java | 16 ++ .../jda/api/hooks/ListenerAdapter.java | 10 + .../net/dv8tion/jda/api/requests/Route.java | 6 + .../net/dv8tion/jda/internal/JDAImpl.java | 15 ++ .../jda/internal/entities/EntityBuilder.java | 14 +- .../handle/SubscriptionCreateHandler.java | 20 ++ .../handle/SubscriptionDeleteHandler.java | 20 ++ .../handle/SubscriptionUpdateHandler.java | 20 ++ .../internal/requests/WebSocketClient.java | 3 + 16 files changed, 453 insertions(+), 110 deletions(-) create mode 100644 src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionImpl.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/subscription/GenericSubscriptionEvent.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionCreateEvent.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionDeleteEvent.java create mode 100644 src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionUpdateEvent.java create mode 100644 src/main/java/net/dv8tion/jda/internal/handle/SubscriptionCreateHandler.java create mode 100644 src/main/java/net/dv8tion/jda/internal/handle/SubscriptionDeleteHandler.java create mode 100644 src/main/java/net/dv8tion/jda/internal/handle/SubscriptionUpdateHandler.java diff --git a/src/main/java/net/dv8tion/jda/api/JDA.java b/src/main/java/net/dv8tion/jda/api/JDA.java index 37554db900..9c3ccd299b 100644 --- a/src/main/java/net/dv8tion/jda/api/JDA.java +++ b/src/main/java/net/dv8tion/jda/api/JDA.java @@ -27,6 +27,7 @@ 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.entities.subscription.Subscription; import net.dv8tion.jda.api.events.GenericEvent; import net.dv8tion.jda.api.hooks.IEventManager; import net.dv8tion.jda.api.interactions.commands.Command; @@ -2122,6 +2123,32 @@ default RestAction deleteTestEntitlement(@Nonnull String entitlementId) @CheckReturnValue RestAction deleteTestEntitlement(long entitlementId); + /** + * Retrieves a List of {@link Subscription} by sku id + * + * @param skuId + * The sku id of the List + * @return {@link RestAction} - Type: {@link Subscription} + *
The list of subscriptions by provided sku id + */ + @Nonnull + @CheckReturnValue + RestAction retrieveSubscriptionsBySkuId(long skuId); + + /** + * Retrieves a {@link Subscription} by its id and sku id + * + * @param skuId + * The sku id of the List where to find subscription + * @param subscriptionId + * The id of the subscription to retrieve + * @return {@link RestAction} - Type: {@link Subscription} + *
The Subscription with the provided id + */ + @Nonnull + @CheckReturnValue + RestAction retrieveSubscriptionBySkuIdAndSubscriptionId(long skuId, long subscriptionId); + /** * Configures the required scopes applied to the {@link #getInviteUrl(Permission...)} and similar methods. *
To use slash commands you must add {@code "applications.commands"} to these scopes. The scope {@code "bot"} is always applied. diff --git a/src/main/java/net/dv8tion/jda/api/entities/subscription/Subscription.java b/src/main/java/net/dv8tion/jda/api/entities/subscription/Subscription.java index ca8a903553..da141d023e 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/subscription/Subscription.java +++ b/src/main/java/net/dv8tion/jda/api/entities/subscription/Subscription.java @@ -1,122 +1,131 @@ package net.dv8tion.jda.api.entities.subscription; -import net.dv8tion.jda.internal.JDAImpl; -import net.dv8tion.jda.internal.utils.EntityString; +import net.dv8tion.jda.api.entities.ISnowflake; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.time.OffsetDateTime; import java.util.List; +import java.util.stream.Collectors; /** * Representation of a Discord Subscription - *
This class is immutable + * + * @see Discord Docs about Subscriptions */ -public class Subscription +public interface Subscription extends ISnowflake { - private final JDAImpl api; - private final long id; - private final long subscriberId; - private final List skuIDs; - private final List entitlementIDs; - private final List renewalSkuIDs; - private final OffsetDateTime currentPeriodStart; - private final OffsetDateTime currentPeriodEnd; - private final OffsetDateTime canceledAt; - private final SubscriptionStatus status; - - public Subscription(@Nonnull final JDAImpl api, final long id, final long subscriberId, @Nonnull final List skuIDs, - @Nonnull final List entitlementIDs, @Nullable final List renewalSkuIDs, - @Nonnull final OffsetDateTime currentPeriodStart, @Nonnull final OffsetDateTime currentPeriodEnd, @Nullable final OffsetDateTime canceledAt, - @Nonnull final SubscriptionStatus status) - { - - this.api = api; - this.id = id; - this.subscriberId = subscriberId; - this.skuIDs = skuIDs; - this.entitlementIDs = entitlementIDs; - this.renewalSkuIDs = renewalSkuIDs; - this.currentPeriodStart = currentPeriodStart; - this.currentPeriodEnd = currentPeriodEnd; - this.canceledAt = canceledAt; - this.status = status; - } - - public Long getId() - { - return id; - } - - public Long getSubscriberId() + /** + * The user who subscribed + * + * @return a use who subscribed + */ + @Nonnull + long getSubscriberIdLong(); + + /** + * The user who subscribed + * + * @return a use who subscribed + */ + @Nonnull + default String getSubscriberId() { - return subscriberId; + return Long.toUnsignedString(getSubscriberIdLong()); } - public List getSkuIDs() + /** + * The sku id's related to this + * + * @return The list of sku id's related to this {@link Subscription} + */ + @Nonnull + List getSkuIdsLong(); + + /** + * The sku id's related to this + * + * @return The list sku id's related to this {@link Subscription} + */ + @Nonnull + default List getSkuIds() { - return skuIDs; + return getSkuIdsLong().stream() + .map(Long::toUnsignedString) + .collect(Collectors.toList()); } - public List getEntitlementIDs() + /** + * The entitlements id's related to this {@link Subscription} + * + * @return The sku id's related to this {@link Subscription} + */ + @Nonnull + List getEntitlementIdsLong(); + + /** + * The entitlements id's related to this {@link Subscription} + * + * @return The entitlements id's related to this {@link Subscription} + */ + @Nonnull + default List getEntitlementIds() { - return entitlementIDs; + return getEntitlementIdsLong().stream() + .map(Long::toUnsignedString) + .collect(Collectors.toList()); } - public List getRenewalSkuIDs() + /** + * The renewal sku id's related to this {@link Subscription} + * + * @return The renewal sku id's related to this {@link Subscription} + */ + @Nullable + List getRenewalSkuIdsLong(); + + /** + * The renewal sku id's related to this {@link Subscription} + * + * @return The renewal sku id's related to this {@link Subscription} + */ + @Nullable + default List getRenewalSkuIds() { - return renewalSkuIDs; + return getRenewalSkuIdsLong().stream() + .map(Long::toUnsignedString) + .collect(Collectors.toList()); } - public OffsetDateTime getCurrentPeriodStart() - { - return currentPeriodStart; - } - - public OffsetDateTime getCurrentPeriodEnd() - { - return currentPeriodEnd; - } - - public OffsetDateTime getCanceledAt() - { - return canceledAt; - } - - public SubscriptionStatus getStatus() - { - return status; - } - - - public JDAImpl getApi() - { - return api; - } - - @Override - public int hashCode() - { - return Long.hashCode(id); - } - - @Override - public boolean equals(Object obj) - { - if(this == obj) - return true; - if(!(obj instanceof Subscription)) - { - return false; - } - Subscription other = (Subscription) obj; - return other.id == this.id; - } - - @Override - public String toString(){ - return new EntityString(this) - .addMetadata("id", id) - .toString(); - } + /** + * The start of period of this {@link Subscription} + * + * @return The start of period of this {@link Subscription} + */ + @Nonnull + OffsetDateTime getCurrentPeriodStart(); + + /** + * The end of period of this {@link Subscription} + * + * @return The end of period of this {@link Subscription} + */ + @Nonnull + OffsetDateTime getCurrentPeriodEnd(); + + /** + * The canceled time of this {@link Subscription} + * + * @return The canceled time of this {@link Subscription} + */ + @Nullable + OffsetDateTime getCanceledAt(); + + /** + * The status of this {@link Subscription} + * + * @return The status of this {@link Subscription} + */ + @Nonnull + SubscriptionStatus getStatus(); } diff --git a/src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionImpl.java b/src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionImpl.java new file mode 100644 index 0000000000..33f93e4b8b --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionImpl.java @@ -0,0 +1,114 @@ +package net.dv8tion.jda.api.entities.subscription; + +import net.dv8tion.jda.internal.utils.EntityString; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.time.OffsetDateTime; +import java.util.List; + + +public class SubscriptionImpl implements Subscription +{ + private final long id; + private final long subscriberId; + private final List skuIDs; + private final List entitlementIDs; + private final List renewalSkuIDs; + private final OffsetDateTime currentPeriodStart; + private final OffsetDateTime currentPeriodEnd; + private final OffsetDateTime canceledAt; + private final SubscriptionStatus status; + + public SubscriptionImpl(final long id, final long subscriberId, @Nonnull final List skuIDs, + @Nonnull final List entitlementIDs, @Nullable final List renewalSkuIDs, + @Nonnull final OffsetDateTime currentPeriodStart, @Nonnull final OffsetDateTime currentPeriodEnd, @Nullable final OffsetDateTime canceledAt, + @Nonnull final SubscriptionStatus status) + { + + this.id = id; + this.subscriberId = subscriberId; + this.skuIDs = skuIDs; + this.entitlementIDs = entitlementIDs; + this.renewalSkuIDs = renewalSkuIDs; + this.currentPeriodStart = currentPeriodStart; + this.currentPeriodEnd = currentPeriodEnd; + this.canceledAt = canceledAt; + this.status = status; + } + + @Nonnull + @Override + public long getIdLong() + { + return id; + } + + public long getSubscriberIdLong() + { + return subscriberId; + } + + public @NotNull List getSkuIdsLong() + { + return skuIDs; + } + + public @NotNull List getEntitlementIdsLong() + { + return entitlementIDs; + } + + public @Nullable List getRenewalSkuIdsLong() + { + return renewalSkuIDs; + } + + public @NotNull OffsetDateTime getCurrentPeriodStart() + { + return currentPeriodStart; + } + + public @NotNull OffsetDateTime getCurrentPeriodEnd() + { + return currentPeriodEnd; + } + + public OffsetDateTime getCanceledAt() + { + return canceledAt; + } + + public @NotNull SubscriptionStatus getStatus() + { + return status; + } + + @Override + public int hashCode() + { + return Long.hashCode(id); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (!(obj instanceof SubscriptionImpl)) + { + return false; + } + SubscriptionImpl other = (SubscriptionImpl) obj; + return other.id == this.id; + } + + @Override + public String toString() + { + return new EntityString(this) + .addMetadata("id", id) + .toString(); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionStatus.java b/src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionStatus.java index bad88e5298..2c7a595c00 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionStatus.java +++ b/src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionStatus.java @@ -4,28 +4,44 @@ /** * Representation of a Discord Subscription Status + * + * @see Discord Docs about Subscription Statuses */ public enum SubscriptionStatus { - UNKNOWN(-1),ACTIVE(0), ENDING(1), INACTIVE(2); + UNKNOWN(-1), ACTIVE(0), ENDING(1), INACTIVE(2); private final int id; - SubscriptionStatus(int id){ + SubscriptionStatus(int id) + { this.id = id; } + /** + * Gets the Subscription status related to the provided key. + *
If an unknown key is provided, this returns {@link #UNKNOWN} + * + * @param key + * The Discord key referencing a Subscription status. + * + * @return The Subscription status that has the key provided, or {@link #UNKNOWN} for unknown key. + */ @Nonnull - public static SubscriptionStatus fromKey(int id){ - for (SubscriptionStatus status : values()){ - if(status.id == id){ + public static SubscriptionStatus fromKey(int key) + { + for (SubscriptionStatus status : values()) + { + if (status.id == key) + { return status; } } return UNKNOWN; } - public int getId(){ + public int getId() + { return id; } } diff --git a/src/main/java/net/dv8tion/jda/api/events/subscription/GenericSubscriptionEvent.java b/src/main/java/net/dv8tion/jda/api/events/subscription/GenericSubscriptionEvent.java new file mode 100644 index 0000000000..705bba2f61 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/subscription/GenericSubscriptionEvent.java @@ -0,0 +1,36 @@ +package net.dv8tion.jda.api.events.subscription; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.subscription.Subscription; +import net.dv8tion.jda.api.events.Event; + +import javax.annotation.Nonnull; + +/** + * Indicates that an {@link Subscription Subscription} was either created, updated, or deleted + * + * @see SubscriptionCreateEvent + * @see SubscriptionUpdateEvent + * @see SubscriptionDeleteEvent + */ +public abstract class GenericSubscriptionEvent extends Event +{ + protected final Subscription subscription; + + protected GenericSubscriptionEvent(@Nonnull JDA api, long responseNumber, @Nonnull Subscription subscription) + { + super(api, responseNumber); + this.subscription = subscription; + } + + /** + * The subscription {@link Subscription} + * + * @return The subscription {@link Subscription} + */ + @Nonnull + public Subscription getSubscription() + { + return subscription; + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionCreateEvent.java b/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionCreateEvent.java new file mode 100644 index 0000000000..2407903222 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionCreateEvent.java @@ -0,0 +1,15 @@ +package net.dv8tion.jda.api.events.subscription; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.subscription.Subscription; + +/** + * Indicate that a Subscription for Premium app was created + */ +public class SubscriptionCreateEvent extends GenericSubscriptionEvent +{ + public SubscriptionCreateEvent(JDA api, long responseNumber, Subscription subscription) + { + super(api, responseNumber, subscription); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionDeleteEvent.java b/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionDeleteEvent.java new file mode 100644 index 0000000000..5d34d2b553 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionDeleteEvent.java @@ -0,0 +1,16 @@ +package net.dv8tion.jda.api.events.subscription; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.subscription.Subscription; +import org.jetbrains.annotations.NotNull; + +/** + * Indicate that a Subscription for Premium app was deleted + */ +public class SubscriptionDeleteEvent extends GenericSubscriptionEvent +{ + public SubscriptionDeleteEvent(@NotNull JDA api, long responseNumber, @NotNull Subscription subscription) + { + super(api, responseNumber, subscription); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionUpdateEvent.java b/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionUpdateEvent.java new file mode 100644 index 0000000000..ccf8fe9b48 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionUpdateEvent.java @@ -0,0 +1,16 @@ +package net.dv8tion.jda.api.events.subscription; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.subscription.Subscription; +import org.jetbrains.annotations.NotNull; + +/** + * Indicates that a Subscription for Premium app was updated + */ +public class SubscriptionUpdateEvent extends GenericSubscriptionEvent +{ + public SubscriptionUpdateEvent(@NotNull JDA api, long responseNumber, @NotNull Subscription subscription) + { + super(api, responseNumber, subscription); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java b/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java index f667acbfe3..cc02a4d6da 100644 --- a/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java +++ b/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java @@ -79,6 +79,10 @@ import net.dv8tion.jda.api.events.sticker.GuildStickerAddedEvent; import net.dv8tion.jda.api.events.sticker.GuildStickerRemovedEvent; import net.dv8tion.jda.api.events.sticker.update.*; +import net.dv8tion.jda.api.events.subscription.GenericSubscriptionEvent; +import net.dv8tion.jda.api.events.subscription.SubscriptionCreateEvent; +import net.dv8tion.jda.api.events.subscription.SubscriptionUpdateEvent; +import net.dv8tion.jda.api.events.subscription.SubscriptionDeleteEvent; import net.dv8tion.jda.api.events.thread.GenericThreadEvent; import net.dv8tion.jda.api.events.thread.ThreadHiddenEvent; import net.dv8tion.jda.api.events.thread.ThreadRevealedEvent; @@ -371,6 +375,11 @@ public void onGuildStickerUpdateTags(@Nonnull GuildStickerUpdateTagsEvent event) public void onGuildStickerUpdateDescription(@Nonnull GuildStickerUpdateDescriptionEvent event) {} public void onGuildStickerUpdateAvailable(@Nonnull GuildStickerUpdateAvailableEvent event) {} + //Subscription events + public void onSubscriptionCreate(@Nonnull SubscriptionCreateEvent event) {} + public void onSubscriptionUpdate(@Nonnull SubscriptionUpdateEvent event) {} + public void onSubscriptionDelete(@Nonnull SubscriptionDeleteEvent event) {} + // Entitlement events public void onEntitlementCreate(@Nonnull EntitlementCreateEvent event) {} public void onEntitlementUpdate(@Nonnull EntitlementUpdateEvent event) {} @@ -420,6 +429,7 @@ public void onGenericScheduledEventGateway(@Nonnull GenericScheduledEventGateway public void onGenericScheduledEventUser(@Nonnull GenericScheduledEventUserEvent event) {} public void onGenericForumTag(@Nonnull GenericForumTagEvent event) {} public void onGenericForumTagUpdate(@Nonnull GenericForumTagUpdateEvent event) {} + public void onGenericSubscription(@Nonnull GenericSubscriptionEvent event) {} private static final MethodHandles.Lookup lookup = MethodHandles.lookup(); private static final ConcurrentMap, MethodHandle> methods = new ConcurrentHashMap<>(); 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 e6b0370f2b..4626151c04 100644 --- a/src/main/java/net/dv8tion/jda/api/requests/Route.java +++ b/src/main/java/net/dv8tion/jda/api/requests/Route.java @@ -102,6 +102,12 @@ public static class Users public static final Route GET_USER = new Route(GET, "users/{user_id}"); } + public static class Sku + { + public static final Route GET_SUBSCRIPTIONS = new Route(GET, "skus/{sku.id}/subscriptions"); + public static final Route GET_SUBSCRIPTION = new Route(GET, "skus/{sku.id}/subscriptions/{subscription_id}"); + } + public static class Guilds { public static final Route GET_GUILD = new Route(GET, "guilds/{guild_id}"); diff --git a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java index 3a4267765d..fe837b75e7 100644 --- a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java @@ -35,6 +35,7 @@ import net.dv8tion.jda.api.entities.sticker.StickerPack; import net.dv8tion.jda.api.entities.sticker.StickerSnowflake; import net.dv8tion.jda.api.entities.sticker.StickerUnion; +import net.dv8tion.jda.api.entities.subscription.Subscription; import net.dv8tion.jda.api.events.GatewayPingEvent; import net.dv8tion.jda.api.events.GenericEvent; import net.dv8tion.jda.api.events.StatusChangeEvent; @@ -1262,6 +1263,20 @@ public RestAction retrieveEntitlementById(long entitlementId) return new RestActionImpl<>(this, Route.Applications.GET_ENTITLEMENT.compile(getSelfUser().getApplicationId(), Long.toUnsignedString(entitlementId))); } + @Nonnull + @Override + public RestAction retrieveSubscriptionsBySkuId(long skuId) + { + return new RestActionImpl<>(this, Route.Sku.GET_SUBSCRIPTIONS.compile(Long.toUnsignedString(skuId))); + } + + @Nonnull + @Override + public RestAction retrieveSubscriptionBySkuIdAndSubscriptionId(long skuId, long subscriptionId) + { + return new RestActionImpl<>(this, Route.Sku.GET_SUBSCRIPTION.compile(Long.toUnsignedString(skuId), Long.toUnsignedString(subscriptionId))); + } + @Nonnull @Override public TestEntitlementCreateAction createTestEntitlement(long skuId, long ownerId, @Nonnull TestEntitlementCreateActionImpl.OwnerType ownerType) 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 144a18b67d..e762917ad2 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -49,6 +49,7 @@ import net.dv8tion.jda.api.entities.messages.MessageSnapshot; import net.dv8tion.jda.api.entities.sticker.*; import net.dv8tion.jda.api.entities.subscription.Subscription; +import net.dv8tion.jda.api.entities.subscription.SubscriptionImpl; import net.dv8tion.jda.api.entities.subscription.SubscriptionStatus; import net.dv8tion.jda.api.entities.templates.Template; import net.dv8tion.jda.api.entities.templates.TemplateChannel; @@ -2624,17 +2625,16 @@ public Subscription createSubscription(DataObject object) OffsetDateTime canceledAt = object.getOffsetDateTime("canceled_at", null); - List mappedSkuIds = mapDataArrayToLongList(skuIDs); - List mappedEntitlementsIds = mapDataArrayToLongList(entitlementsIDs); + List mappedSkuIds = mapToLongList(skuIDs); + List mappedEntitlementsIds = mapToLongList(entitlementsIDs); List mappedRenewalSkuIds = Optional.ofNullable(renewalSkuIDs) - .map(this::mapDataArrayToLongList) + .map(this::mapToLongList) .orElse(null); - return new Subscription( - getJDA(), + return new SubscriptionImpl( object.getUnsignedLong("id"), - object.getUnsignedLong("user_id", 0), + object.getUnsignedLong("user_id"), mappedSkuIds, mappedEntitlementsIds, mappedRenewalSkuIds, @@ -2645,7 +2645,7 @@ public Subscription createSubscription(DataObject object) ); } - public List mapDataArrayToLongList(DataArray dataArray) + public List mapToLongList(DataArray dataArray) { return IntStream.range(0, dataArray.length()) .mapToObj(dataArray::getLong) diff --git a/src/main/java/net/dv8tion/jda/internal/handle/SubscriptionCreateHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/SubscriptionCreateHandler.java new file mode 100644 index 0000000000..4f301e3a39 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/handle/SubscriptionCreateHandler.java @@ -0,0 +1,20 @@ +package net.dv8tion.jda.internal.handle; + +import net.dv8tion.jda.api.events.subscription.SubscriptionCreateEvent; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; + +public class SubscriptionCreateHandler extends SocketHandler +{ + public SubscriptionCreateHandler(JDAImpl api) + { + super(api); + } + + @Override + protected Long handleInternally(DataObject content) + { + getJDA().handleEvent(new SubscriptionCreateEvent(getJDA(), responseNumber, getJDA().getEntityBuilder().createSubscription(content))); + return null; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/handle/SubscriptionDeleteHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/SubscriptionDeleteHandler.java new file mode 100644 index 0000000000..13239ff439 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/handle/SubscriptionDeleteHandler.java @@ -0,0 +1,20 @@ +package net.dv8tion.jda.internal.handle; + +import net.dv8tion.jda.api.events.subscription.SubscriptionDeleteEvent; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; + +public class SubscriptionDeleteHandler extends SocketHandler +{ + public SubscriptionDeleteHandler(JDAImpl api) + { + super(api); + } + + @Override + protected Long handleInternally(DataObject content) + { + getJDA().handleEvent(new SubscriptionDeleteEvent(getJDA(), responseNumber, getJDA().getEntityBuilder().createSubscription(content))); + return null; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/handle/SubscriptionUpdateHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/SubscriptionUpdateHandler.java new file mode 100644 index 0000000000..bdacdafd5f --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/handle/SubscriptionUpdateHandler.java @@ -0,0 +1,20 @@ +package net.dv8tion.jda.internal.handle; + +import net.dv8tion.jda.api.events.subscription.SubscriptionUpdateEvent; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; + +public class SubscriptionUpdateHandler extends SocketHandler +{ + public SubscriptionUpdateHandler(JDAImpl api) + { + super(api); + } + + @Override + protected Long handleInternally(DataObject content) + { + getJDA().handleEvent(new SubscriptionUpdateEvent(getJDA(), responseNumber, getJDA().getEntityBuilder().createSubscription(content))); + return null; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java b/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java index 401bcbf683..5b91aeaaa3 100644 --- a/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java +++ b/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java @@ -1374,6 +1374,9 @@ protected void setupHandlers() handlers.put("CHANNEL_CREATE", new ChannelCreateHandler(api)); handlers.put("CHANNEL_DELETE", new ChannelDeleteHandler(api)); handlers.put("CHANNEL_UPDATE", new ChannelUpdateHandler(api)); + handlers.put("SUBSCRIPTION_CREATE", new SubscriptionCreateHandler(api)); + handlers.put("SUBSCRIPTION_UPDATE", new SubscriptionUpdateHandler(api)); + handlers.put("SUBSCRIPTION_DELETE", new SubscriptionDeleteHandler(api)); handlers.put("ENTITLEMENT_CREATE", new EntitlementCreateHandler(api)); handlers.put("ENTITLEMENT_UPDATE", new EntitlementUpdateHandler(api)); handlers.put("ENTITLEMENT_DELETE", new EntitlementDeleteHandler(api)); From 598add12588303818939c294ed329419c0d13880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B5=D0=BC=D0=B8=D0=B4=D0=BE=D0=B2=20=D0=AF=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=9A=D0=BE=D0=BD=D1=81=D1=82=D0=B0=D0=BD=D1=82?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2=D0=B8=D1=87?= Date: Fri, 8 Aug 2025 16:07:32 +0300 Subject: [PATCH 4/6] Add deserialization --- .../SubscriptionPaginationAction.java | 15 +++ .../SubscriptionPaginationActionImpl.java | 97 +++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 src/main/java/net/dv8tion/jda/api/requests/restaction/pagination/SubscriptionPaginationAction.java create mode 100644 src/main/java/net/dv8tion/jda/internal/requests/restaction/pagination/SubscriptionPaginationActionImpl.java diff --git a/src/main/java/net/dv8tion/jda/api/requests/restaction/pagination/SubscriptionPaginationAction.java b/src/main/java/net/dv8tion/jda/api/requests/restaction/pagination/SubscriptionPaginationAction.java new file mode 100644 index 0000000000..175c2dc764 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/requests/restaction/pagination/SubscriptionPaginationAction.java @@ -0,0 +1,15 @@ +package net.dv8tion.jda.api.requests.restaction.pagination; + +import net.dv8tion.jda.api.entities.UserSnowflake; +import net.dv8tion.jda.internal.entities.subscription.Subscription; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public interface SubscriptionPaginationAction extends PaginationAction +{ + @Nonnull + @CheckReturnValue + SubscriptionPaginationAction user(@Nullable UserSnowflake user); +} diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/pagination/SubscriptionPaginationActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/pagination/SubscriptionPaginationActionImpl.java new file mode 100644 index 0000000000..1babd3da64 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/pagination/SubscriptionPaginationActionImpl.java @@ -0,0 +1,97 @@ +package net.dv8tion.jda.internal.requests.restaction.pagination; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.UserSnowflake; +import net.dv8tion.jda.api.exceptions.ParsingException; +import net.dv8tion.jda.api.requests.Request; +import net.dv8tion.jda.api.requests.Response; +import net.dv8tion.jda.api.requests.Route; +import net.dv8tion.jda.api.requests.restaction.pagination.SubscriptionPaginationAction; +import net.dv8tion.jda.api.utils.data.DataArray; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.entities.EntityBuilder; +import net.dv8tion.jda.internal.entities.subscription.Subscription; +import org.jetbrains.annotations.Nullable; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; + +public class SubscriptionPaginationActionImpl + extends PaginationActionImpl + implements SubscriptionPaginationAction +{ + protected long userId; + + public SubscriptionPaginationActionImpl(JDA api, String skuId) + { + super(api, Route.Sku.GET_SUBSCRIPTIONS.compile(skuId),1,100,100); + this.userId = 0; + } + + @Nonnull + @Override + public EnumSet getSupportedOrders() + { + return EnumSet.of(PaginationOrder.BACKWARD, PaginationOrder.FORWARD); + } + + @Nonnull + @Override + public SubscriptionPaginationAction user(@Nullable UserSnowflake user) + { + if (user == null) + userId = 0; + else + userId = user.getIdLong(); + return this; + } + + @Override + protected Route.CompiledRoute finalizeRoute() + { + Route.CompiledRoute route = super.finalizeRoute(); + + if(userId != 0) + route = route.withQueryParams("user_id",Long.toUnsignedString(userId)); + + return route; + } + + @Override + protected void handleSuccess(Response response, Request> request) + { + DataArray array = response.getArray(); + List subscriptions = new ArrayList<>(array.length()); + EntityBuilder builder = api.getEntityBuilder(); + + for(int i = 0; i < array.length(); i++) + { + try + { + DataObject object = array.getObject(i); + Subscription subscription = builder.createSubscription(object); + subscriptions.add(subscription); + }catch (ParsingException | NullPointerException e) + { + LOG.warn("Encountered an exception in SubscriptionPaginationAction", e); + } + } + if(!subscriptions.isEmpty()) + { + if(useCache) + cached.addAll(subscriptions); + last = subscriptions.get(subscriptions.size() - 1); + lastKey = last.getIdLong(); + } + + request.onSuccess(subscriptions); + } + + @Override + protected long getKey(Subscription it) + { + return it.getIdLong(); + } +} From cca4c550200f156c499ef884816de1ab2b11f7b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B5=D0=BC=D0=B8=D0=B4=D0=BE=D0=B2=20=D0=AF=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=9A=D0=BE=D0=BD=D1=81=D1=82=D0=B0=D0=BD=D1=82?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2=D0=B8=D1=87?= Date: Fri, 8 Aug 2025 16:08:06 +0300 Subject: [PATCH 5/6] Took into account PR's comments --- src/main/java/net/dv8tion/jda/api/JDA.java | 36 ++++++++++++++----- .../GenericSubscriptionEvent.java | 6 ++-- .../subscription/SubscriptionCreateEvent.java | 2 +- .../subscription/SubscriptionDeleteEvent.java | 2 +- .../subscription/SubscriptionUpdateEvent.java | 2 +- .../net/dv8tion/jda/internal/JDAImpl.java | 14 ++++---- .../jda/internal/entities/EntityBuilder.java | 6 ++-- .../entities/subscription/Subscription.java | 10 +++--- .../subscription/SubscriptionImpl.java | 23 ++++++------ .../subscription/SubscriptionStatus.java | 4 +-- 10 files changed, 61 insertions(+), 44 deletions(-) rename src/main/java/net/dv8tion/jda/{api => internal}/entities/subscription/Subscription.java (92%) rename src/main/java/net/dv8tion/jda/{api => internal}/entities/subscription/SubscriptionImpl.java (82%) rename src/main/java/net/dv8tion/jda/{api => internal}/entities/subscription/SubscriptionStatus.java (93%) diff --git a/src/main/java/net/dv8tion/jda/api/JDA.java b/src/main/java/net/dv8tion/jda/api/JDA.java index 9c3ccd299b..dc77c601b9 100644 --- a/src/main/java/net/dv8tion/jda/api/JDA.java +++ b/src/main/java/net/dv8tion/jda/api/JDA.java @@ -27,7 +27,8 @@ 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.entities.subscription.Subscription; +import net.dv8tion.jda.api.requests.restaction.pagination.SubscriptionPaginationAction; +import net.dv8tion.jda.internal.entities.subscription.Subscription; import net.dv8tion.jda.api.events.GenericEvent; import net.dv8tion.jda.api.hooks.IEventManager; import net.dv8tion.jda.api.interactions.commands.Command; @@ -2124,30 +2125,49 @@ default RestAction deleteTestEntitlement(@Nonnull String entitlementId) RestAction deleteTestEntitlement(long entitlementId); /** - * Retrieves a List of {@link Subscription} by sku id + * Retrieves a List of {@link Subscription} by SKU id * * @param skuId - * The sku id of the List + * The SKU id of the List + * + * @return {@link SubscriptionPaginationAction} + */ + @Nonnull + @CheckReturnValue + SubscriptionPaginationAction retrieveSubscriptionsBySkuId(@Nonnull SkuSnowflake skuId); + + /** + * Retrieves a {@link Subscription} by its id and SKU id + * + * @param skuId + * The SKU id of the List where to find subscription + * @param subscriptionId + * The String id of the subscription to retrieve + * * @return {@link RestAction} - Type: {@link Subscription} - *
The list of subscriptions by provided sku id + *
The Subscription with the provided id */ @Nonnull @CheckReturnValue - RestAction retrieveSubscriptionsBySkuId(long skuId); + default RestAction retrieveSubscriptionBySkuId(@Nonnull SkuSnowflake skuId, @Nonnull String subscriptionId) + { + return retrieveSubscriptionBySkuId(skuId, MiscUtil.parseSnowflake(subscriptionId)); + } /** - * Retrieves a {@link Subscription} by its id and sku id + * Retrieves a {@link Subscription} by its id and SKU id * * @param skuId - * The sku id of the List where to find subscription + * The SKU id of the List where to find subscription * @param subscriptionId * The id of the subscription to retrieve + * * @return {@link RestAction} - Type: {@link Subscription} *
The Subscription with the provided id */ @Nonnull @CheckReturnValue - RestAction retrieveSubscriptionBySkuIdAndSubscriptionId(long skuId, long subscriptionId); + RestAction retrieveSubscriptionBySkuId(@Nonnull SkuSnowflake skuId, long subscriptionId); /** * Configures the required scopes applied to the {@link #getInviteUrl(Permission...)} and similar methods. diff --git a/src/main/java/net/dv8tion/jda/api/events/subscription/GenericSubscriptionEvent.java b/src/main/java/net/dv8tion/jda/api/events/subscription/GenericSubscriptionEvent.java index 705bba2f61..06a84a2141 100644 --- a/src/main/java/net/dv8tion/jda/api/events/subscription/GenericSubscriptionEvent.java +++ b/src/main/java/net/dv8tion/jda/api/events/subscription/GenericSubscriptionEvent.java @@ -1,7 +1,7 @@ package net.dv8tion.jda.api.events.subscription; import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.subscription.Subscription; +import net.dv8tion.jda.internal.entities.subscription.Subscription; import net.dv8tion.jda.api.events.Event; import javax.annotation.Nonnull; @@ -24,9 +24,9 @@ protected GenericSubscriptionEvent(@Nonnull JDA api, long responseNumber, @Nonnu } /** - * The subscription {@link Subscription} + * The {@link Subscription} * - * @return The subscription {@link Subscription} + * @return The {@link Subscription} */ @Nonnull public Subscription getSubscription() diff --git a/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionCreateEvent.java b/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionCreateEvent.java index 2407903222..a0df272fba 100644 --- a/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionCreateEvent.java +++ b/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionCreateEvent.java @@ -1,7 +1,7 @@ package net.dv8tion.jda.api.events.subscription; import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.subscription.Subscription; +import net.dv8tion.jda.internal.entities.subscription.Subscription; /** * Indicate that a Subscription for Premium app was created diff --git a/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionDeleteEvent.java b/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionDeleteEvent.java index 5d34d2b553..e424d95353 100644 --- a/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionDeleteEvent.java +++ b/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionDeleteEvent.java @@ -1,7 +1,7 @@ package net.dv8tion.jda.api.events.subscription; import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.subscription.Subscription; +import net.dv8tion.jda.internal.entities.subscription.Subscription; import org.jetbrains.annotations.NotNull; /** diff --git a/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionUpdateEvent.java b/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionUpdateEvent.java index ccf8fe9b48..bace07b1c5 100644 --- a/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionUpdateEvent.java +++ b/src/main/java/net/dv8tion/jda/api/events/subscription/SubscriptionUpdateEvent.java @@ -1,7 +1,7 @@ package net.dv8tion.jda.api.events.subscription; import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.subscription.Subscription; +import net.dv8tion.jda.internal.entities.subscription.Subscription; import org.jetbrains.annotations.NotNull; /** diff --git a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java index fe837b75e7..c834219a3f 100644 --- a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java @@ -35,7 +35,8 @@ import net.dv8tion.jda.api.entities.sticker.StickerPack; import net.dv8tion.jda.api.entities.sticker.StickerSnowflake; import net.dv8tion.jda.api.entities.sticker.StickerUnion; -import net.dv8tion.jda.api.entities.subscription.Subscription; +import net.dv8tion.jda.api.requests.restaction.pagination.SubscriptionPaginationAction; +import net.dv8tion.jda.internal.entities.subscription.Subscription; import net.dv8tion.jda.api.events.GatewayPingEvent; import net.dv8tion.jda.api.events.GenericEvent; import net.dv8tion.jda.api.events.StatusChangeEvent; @@ -76,6 +77,7 @@ import net.dv8tion.jda.internal.requests.*; import net.dv8tion.jda.internal.requests.restaction.*; import net.dv8tion.jda.internal.requests.restaction.pagination.EntitlementPaginationActionImpl; +import net.dv8tion.jda.internal.requests.restaction.pagination.SubscriptionPaginationActionImpl; import net.dv8tion.jda.internal.utils.*; import net.dv8tion.jda.internal.utils.Helpers; import net.dv8tion.jda.internal.utils.cache.AbstractCacheView; @@ -1258,23 +1260,23 @@ public EntitlementPaginationAction retrieveEntitlements() @Nonnull @Override - public RestAction retrieveEntitlementById(long entitlementId) + public RestAction retrieveEntitlementById(@Nonnull long entitlementId) { return new RestActionImpl<>(this, Route.Applications.GET_ENTITLEMENT.compile(getSelfUser().getApplicationId(), Long.toUnsignedString(entitlementId))); } @Nonnull @Override - public RestAction retrieveSubscriptionsBySkuId(long skuId) + public SubscriptionPaginationAction retrieveSubscriptionsBySkuId(@Nonnull SkuSnowflake skuId) { - return new RestActionImpl<>(this, Route.Sku.GET_SUBSCRIPTIONS.compile(Long.toUnsignedString(skuId))); + return new SubscriptionPaginationActionImpl(this, skuId.getId()); } @Nonnull @Override - public RestAction retrieveSubscriptionBySkuIdAndSubscriptionId(long skuId, long subscriptionId) + public RestAction retrieveSubscriptionBySkuId(@Nonnull SkuSnowflake skuId, @Nonnull long subscriptionId) { - return new RestActionImpl<>(this, Route.Sku.GET_SUBSCRIPTION.compile(Long.toUnsignedString(skuId), Long.toUnsignedString(subscriptionId))); + return new RestActionImpl<>(this, Route.Sku.GET_SUBSCRIPTION.compile(Long.toUnsignedString(skuId.getIdLong()), Long.toUnsignedString(subscriptionId))); } @Nonnull 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 e762917ad2..f4ca7e7a87 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -48,9 +48,9 @@ import net.dv8tion.jda.api.entities.messages.MessagePoll; import net.dv8tion.jda.api.entities.messages.MessageSnapshot; import net.dv8tion.jda.api.entities.sticker.*; -import net.dv8tion.jda.api.entities.subscription.Subscription; -import net.dv8tion.jda.api.entities.subscription.SubscriptionImpl; -import net.dv8tion.jda.api.entities.subscription.SubscriptionStatus; +import net.dv8tion.jda.internal.entities.subscription.Subscription; +import net.dv8tion.jda.internal.entities.subscription.SubscriptionImpl; +import net.dv8tion.jda.internal.entities.subscription.SubscriptionStatus; import net.dv8tion.jda.api.entities.templates.Template; import net.dv8tion.jda.api.entities.templates.TemplateChannel; import net.dv8tion.jda.api.entities.templates.TemplateGuild; diff --git a/src/main/java/net/dv8tion/jda/api/entities/subscription/Subscription.java b/src/main/java/net/dv8tion/jda/internal/entities/subscription/Subscription.java similarity index 92% rename from src/main/java/net/dv8tion/jda/api/entities/subscription/Subscription.java rename to src/main/java/net/dv8tion/jda/internal/entities/subscription/Subscription.java index da141d023e..8c3eac1f6b 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/subscription/Subscription.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/subscription/Subscription.java @@ -1,4 +1,4 @@ -package net.dv8tion.jda.api.entities.subscription; +package net.dv8tion.jda.internal.entities.subscription; import net.dv8tion.jda.api.entities.ISnowflake; @@ -35,7 +35,7 @@ default String getSubscriberId() } /** - * The sku id's related to this + * The SKU id's related to this * * @return The list of sku id's related to this {@link Subscription} */ @@ -43,7 +43,7 @@ default String getSubscriberId() List getSkuIdsLong(); /** - * The sku id's related to this + * The SKU id's related to this * * @return The list sku id's related to this {@link Subscription} */ @@ -77,7 +77,7 @@ default List getEntitlementIds() } /** - * The renewal sku id's related to this {@link Subscription} + * The renewal SKU id's related to this {@link Subscription} * * @return The renewal sku id's related to this {@link Subscription} */ @@ -85,7 +85,7 @@ default List getEntitlementIds() List getRenewalSkuIdsLong(); /** - * The renewal sku id's related to this {@link Subscription} + * The renewal SKU id's related to this {@link Subscription} * * @return The renewal sku id's related to this {@link Subscription} */ diff --git a/src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/subscription/SubscriptionImpl.java similarity index 82% rename from src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionImpl.java rename to src/main/java/net/dv8tion/jda/internal/entities/subscription/SubscriptionImpl.java index 33f93e4b8b..c67163c4b2 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/subscription/SubscriptionImpl.java @@ -1,7 +1,6 @@ -package net.dv8tion.jda.api.entities.subscription; +package net.dv8tion.jda.internal.entities.subscription; import net.dv8tion.jda.internal.utils.EntityString; -import org.jetbrains.annotations.NotNull; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -38,24 +37,23 @@ public SubscriptionImpl(final long id, final long subscriberId, @Nonnull final L this.status = status; } - @Nonnull @Override - public long getIdLong() + @Nonnull public long getIdLong() { return id; } - public long getSubscriberIdLong() + public @Nonnull long getSubscriberIdLong() { return subscriberId; } - public @NotNull List getSkuIdsLong() + public @Nonnull List getSkuIdsLong() { return skuIDs; } - public @NotNull List getEntitlementIdsLong() + public @Nonnull List getEntitlementIdsLong() { return entitlementIDs; } @@ -65,22 +63,22 @@ public long getSubscriberIdLong() return renewalSkuIDs; } - public @NotNull OffsetDateTime getCurrentPeriodStart() + public @Nonnull OffsetDateTime getCurrentPeriodStart() { return currentPeriodStart; } - public @NotNull OffsetDateTime getCurrentPeriodEnd() + public @Nonnull OffsetDateTime getCurrentPeriodEnd() { return currentPeriodEnd; } - public OffsetDateTime getCanceledAt() + public @Nullable OffsetDateTime getCanceledAt() { return canceledAt; } - public @NotNull SubscriptionStatus getStatus() + public @Nonnull SubscriptionStatus getStatus() { return status; } @@ -97,9 +95,8 @@ public boolean equals(Object obj) if (this == obj) return true; if (!(obj instanceof SubscriptionImpl)) - { return false; - } + SubscriptionImpl other = (SubscriptionImpl) obj; return other.id == this.id; } diff --git a/src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionStatus.java b/src/main/java/net/dv8tion/jda/internal/entities/subscription/SubscriptionStatus.java similarity index 93% rename from src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionStatus.java rename to src/main/java/net/dv8tion/jda/internal/entities/subscription/SubscriptionStatus.java index 2c7a595c00..ca54166b98 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/subscription/SubscriptionStatus.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/subscription/SubscriptionStatus.java @@ -1,4 +1,4 @@ -package net.dv8tion.jda.api.entities.subscription; +package net.dv8tion.jda.internal.entities.subscription; import javax.annotation.Nonnull; @@ -33,9 +33,7 @@ public static SubscriptionStatus fromKey(int key) for (SubscriptionStatus status : values()) { if (status.id == key) - { return status; - } } return UNKNOWN; } From 670824bb87262562a140eab9895ab2ec3b64e1fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B5=D0=BC=D0=B8=D0=B4=D0=BE=D0=B2=20=D0=AF=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=9A=D0=BE=D0=BD=D1=81=D1=82=D0=B0=D0=BD=D1=82?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2=D0=B8=D1=87?= Date: Tue, 12 Aug 2025 12:06:01 +0300 Subject: [PATCH 6/6] Add javadoc for SubscriptionPaginationAction --- .../SubscriptionPaginationAction.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/main/java/net/dv8tion/jda/api/requests/restaction/pagination/SubscriptionPaginationAction.java b/src/main/java/net/dv8tion/jda/api/requests/restaction/pagination/SubscriptionPaginationAction.java index 175c2dc764..74940a8458 100644 --- a/src/main/java/net/dv8tion/jda/api/requests/restaction/pagination/SubscriptionPaginationAction.java +++ b/src/main/java/net/dv8tion/jda/api/requests/restaction/pagination/SubscriptionPaginationAction.java @@ -1,5 +1,6 @@ package net.dv8tion.jda.api.requests.restaction.pagination; +import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.UserSnowflake; import net.dv8tion.jda.internal.entities.subscription.Subscription; @@ -7,8 +8,32 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +/** + * {@link PaginationAction PaginationAction} that paginates over + * {@link Subscription Subscriptions} returned from the + * List SKU Subscriptions endpoint. + *
This action allows retrieval of subscriptions for a specific SKU, optionally filtered by a user. + * + *

Use {@link #user(UserSnowflake)} to limit results to a specific user, or {@code null} to remove the filter. + *
Results are ordered according to {@link PaginationOrder} and support typical pagination + * parameters such as {@link #limit(int)} and {@link #cache(boolean)}. + * + * @see PaginationAction + * @see Subscription + */ public interface SubscriptionPaginationAction extends PaginationAction { + + /** + * Filter {@link Subscription Subscription}s to retrieve by the given user ID + * + * @param user + * The {@link UserSnowflake UserSnowflake} used to filter or {@code null} to remove user filtering. + * This can be a member or user instance of {@link User#fromId(long)} + * + * @return The current {@link SubscriptionPaginationAction SubscriptionPaginationAction} for chaining convenience + */ @Nonnull @CheckReturnValue SubscriptionPaginationAction user(@Nullable UserSnowflake user);