From 92bcc32bc3cc36183ebd940ad46505fa380745cc Mon Sep 17 00:00:00 2001 From: Tobias123567 <42582100+Tobias123567@users.noreply.github.com> Date: Sun, 12 May 2024 15:58:55 +0200 Subject: [PATCH] Add missing features relating to premium app subscriptions (#2667) --- src/main/java/net/dv8tion/jda/api/JDA.java | 111 ++++++++++++++ .../dv8tion/jda/api/entities/Entitlement.java | 54 ++++++- .../net/dv8tion/jda/api/requests/Route.java | 4 + .../TestEntitlementCreateAction.java | 137 ++++++++++++++++++ .../net/dv8tion/jda/internal/JDAImpl.java | 34 ++++- .../internal/entities/EntitlementImpl.java | 28 +++- .../jda/internal/entities/EntityBuilder.java | 4 +- .../TestEntitlementCreateActionImpl.java | 91 ++++++++++++ 8 files changed, 451 insertions(+), 12 deletions(-) create mode 100644 src/main/java/net/dv8tion/jda/api/requests/restaction/TestEntitlementCreateAction.java create mode 100644 src/main/java/net/dv8tion/jda/internal/requests/restaction/TestEntitlementCreateActionImpl.java diff --git a/src/main/java/net/dv8tion/jda/api/JDA.java b/src/main/java/net/dv8tion/jda/api/JDA.java index 631e965fb9..e5fb70beee 100644 --- a/src/main/java/net/dv8tion/jda/api/JDA.java +++ b/src/main/java/net/dv8tion/jda/api/JDA.java @@ -1899,6 +1899,117 @@ default List getEmojisByName(@Nonnull String name, boolean igno @CheckReturnValue EntitlementPaginationAction retrieveEntitlements(); + /** + * Retrieves an {@link Entitlement} by its id. + * + * @param entitlementId + * The id of the entitlement to retrieve + * + * @throws IllegalArgumentException + * If the provided id is not a valid snowflake + * + * @return {@link RestAction} - Type: {@link Entitlement} + *
The entitlement with the provided id + */ + @Nonnull + @CheckReturnValue + default RestAction retrieveEntitlementById(@Nonnull String entitlementId) + { + return retrieveEntitlementById(MiscUtil.parseSnowflake(entitlementId)); + } + + /** + * Retrieves an {@link Entitlement} by its id. + * + * @param entitlementId + * The id of the entitlement to retrieve + * + * @return {@link RestAction} - Type: {@link Entitlement} + *
The entitlement with the provided id + */ + @Nonnull + @CheckReturnValue + RestAction retrieveEntitlementById(long entitlementId); + + /** + * Constructs a new {@link Entitlement Entitlement} with the skuId and the type. + *
Use the returned {@link TestEntitlementCreateAction TestEntitlementCreateAction} to provide more details. + * + * @param skuId + * The id of the SKU the entitlement is for + * + * @param ownerId + * The id of the owner of the entitlement + * + * @param ownerType + * The type of the owner of the entitlement + * + * @throws IllegalArgumentException + * If the provided skuId or ownerId is not a valid snowflake + * + * @return {@link TestEntitlementCreateAction TestEntitlementCreateAction} + *
Allows for setting various details for the resulting Entitlement + */ + @Nonnull + @CheckReturnValue + default TestEntitlementCreateAction createTestEntitlement(@Nonnull String skuId, @Nonnull String ownerId, @Nonnull TestEntitlementCreateAction.OwnerType ownerType) + { + return createTestEntitlement(MiscUtil.parseSnowflake(skuId), MiscUtil.parseSnowflake(ownerId), ownerType); + } + + /** + * Constructs a new {@link Entitlement Entitlement} with the skuId and the type. + *
Use the returned {@link TestEntitlementCreateAction TestEntitlementCreateAction} to provide more details. + * + * @param skuId + * The id of the SKU the entitlement is for + * + * @param ownerId + * The id of the owner of the entitlement + * + * @param ownerType + * The type of the owner of the entitlement + * + * @throws IllegalArgumentException + * If the provided ownerType is null + * + * @return {@link TestEntitlementCreateAction TestEntitlementCreateAction} + *
Allows for setting various details for the resulting Entitlement + */ + @Nonnull + @CheckReturnValue + TestEntitlementCreateAction createTestEntitlement(long skuId, long ownerId, @Nonnull TestEntitlementCreateAction.OwnerType ownerType); + + /** + * Deletes a test entitlement by its id. + * + * @param entitlementId + * The id of the entitlement to delete + * + * @throws IllegalArgumentException + * If the provided id is not a valid snowflake + * + * @return {@link RestAction} - Type: Void + */ + @Nonnull + @CheckReturnValue + default RestAction deleteTestEntitlement(@Nonnull String entitlementId) + { + return deleteTestEntitlement(MiscUtil.parseSnowflake(entitlementId)); + } + + /** + * Deletes a test entitlement by its id. + * + * @param entitlementId + * The id of the entitlement to delete + * + * @return {@link RestAction} - Type: Void + */ + @Nonnull + @CheckReturnValue + RestAction deleteTestEntitlement(long entitlementId); + /** * 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/Entitlement.java b/src/main/java/net/dv8tion/jda/api/entities/Entitlement.java index 94fb94dfa3..1852ef6381 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Entitlement.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Entitlement.java @@ -16,6 +16,9 @@ package net.dv8tion.jda.api.entities; +import net.dv8tion.jda.api.requests.RestAction; + +import javax.annotation.CheckReturnValue; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.time.OffsetDateTime; @@ -104,8 +107,6 @@ default String getGuildId() /** * The type of the Entitlement - *
The only possible type of Entitlement currently is {@link EntitlementType#APPLICATION_SUBSCRIPTION} - *
Discord doesn't currently support other types for entitlements. * * @return the {@link Entitlement Entitlement} type */ @@ -137,11 +138,60 @@ default String getGuildId() @Nullable OffsetDateTime getTimeEnding(); + /** + * Whether the {@link Entitlement Entitlement} was consumed or not. + * + * @return True if the {@link Entitlement Entitlement} was consumed, False otherwise + */ + boolean isConsumed(); + + /** + * Consumes the {@link Entitlement Entitlement} if it has not already been consumed. + *
Only One-Time Purchase consumable {@link Entitlement Entitlements} can be consumed. + *
After the {@link Entitlement Entitlement} has been consumed, it will be marked as consumed. + * + * @return A {@link RestAction} that will consume the {@link Entitlement Entitlement} + */ + @Nonnull + @CheckReturnValue + RestAction consume(); + /** * Represents the type of this Entitlement */ enum EntitlementType { + /** + * Entitlement was purchased by user + */ + PURCHASE(1), + /** + * Entitlement for Discord Nitro subscription + */ + PREMIUM_SUBSCRIPTION(2), + /** + * Entitlement was gifted by developer + */ + DEVELOPER_GIFT(3), + /** + * Entitlement was purchased by a dev in application test mode + */ + TEST_MODE_PURCHASE(4), + /** + * Entitlement was granted when the SKU was free + */ + FREE_PURCHASE(5), + /** + * Entitlement was gifted by another user + */ + USER_GIFT(6), + /** + * Entitlement was claimed by user for free as a Nitro Subscriber + */ + PREMIUM_PURCHASE(7), + /** + * Entitlement was purchased as an app subscription + */ APPLICATION_SUBSCRIPTION(8), /** * Placeholder for unsupported types. 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 6c1adb4904..0e165f9f85 100644 --- a/src/main/java/net/dv8tion/jda/api/requests/Route.java +++ b/src/main/java/net/dv8tion/jda/api/requests/Route.java @@ -46,6 +46,10 @@ public static class Applications public static final Route GET_ROLE_CONNECTION_METADATA = new Route(GET, "applications/{application_id}/role-connections/metadata"); public static final Route UPDATE_ROLE_CONNECTION_METADATA = new Route(PUT, "applications/{application_id}/role-connections/metadata"); public static final Route GET_ENTITLEMENTS = new Route(GET, "applications/{application_id}/entitlements"); + public static final Route GET_ENTITLEMENT = new Route(GET, "applications/{application_id}/entitlements/{entitlement_id}"); + public static final Route CONSUME_ENTITLEMENT = new Route(POST, "applications/{application_id}/entitlements/{entitlement_id}/consume"); + public static final Route CREATE_TEST_ENTITLEMENT = new Route(POST, "applications/{application_id}/entitlements"); + public static final Route DELETE_TEST_ENTITLEMENT = new Route(DELETE, "applications/{application_id}/entitlements/{entitlement_id}"); } public static class Interactions diff --git a/src/main/java/net/dv8tion/jda/api/requests/restaction/TestEntitlementCreateAction.java b/src/main/java/net/dv8tion/jda/api/requests/restaction/TestEntitlementCreateAction.java new file mode 100644 index 0000000000..7a7293c425 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/requests/restaction/TestEntitlementCreateAction.java @@ -0,0 +1,137 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.requests.restaction; + +import net.dv8tion.jda.api.entities.Entitlement; +import net.dv8tion.jda.api.requests.RestAction; +import net.dv8tion.jda.api.utils.MiscUtil; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; + +/** + * Extension of {@link net.dv8tion.jda.api.requests.RestAction RestAction} specifically + * designed to create a {@link Entitlement Entitlement}. + * This extension allows setting properties before executing the action. + * + * @see net.dv8tion.jda.api.JDA + * @see net.dv8tion.jda.api.JDA#createTestEntitlement(long, long, OwnerType) + */ +public interface TestEntitlementCreateAction extends RestAction +{ + + /** + * Set the SKU's id to create the entitlement in + * + * @param skuId + * The id of the SKU + * + * @throws IllegalArgumentException + * If the provided skuId is not a valid snowflake + * + * @return The current {@link TestEntitlementCreateAction} for chaining convenience + */ + @CheckReturnValue + @Nonnull + default TestEntitlementCreateAction setSkuId(@Nonnull String skuId) + { + return setSkuId(MiscUtil.parseSnowflake(skuId)); + } + + /** + * Set the SKU's id to create the entitlement in + * + * @param skuId + * The id of the SKU + * + * @return The current {@link TestEntitlementCreateAction} for chaining convenience + */ + @CheckReturnValue + @Nonnull + TestEntitlementCreateAction setSkuId(long skuId); + + /** + * Set the owner's id to create the entitlement for + * + * @param ownerId + * The id of the owner - either guild id or user id + * + * @throws IllegalArgumentException + * If the provided ownerId is not a valid snowflake + * + * @return The current {@link TestEntitlementCreateAction} for chaining convenience + */ + @CheckReturnValue + @Nonnull + default TestEntitlementCreateAction setOwnerId(@Nonnull String ownerId) + { + return setOwnerId(MiscUtil.parseSnowflake(ownerId)); + } + + /** + * Set the owner's id to create the entitlement for + * + * @param ownerId + * The id of the owner - either guild id or user id + * + * @return The current {@link TestEntitlementCreateAction} for chaining convenience + */ + @CheckReturnValue + @Nonnull + TestEntitlementCreateAction setOwnerId(long ownerId); + + /** + * Set the owner type to create the entitlement for + * + * @param type + * The type of the owner + * + * @throws IllegalArgumentException + * If the provided type is null + * + * @return The current {@link TestEntitlementCreateAction} for chaining convenience + */ + @CheckReturnValue + @Nonnull + TestEntitlementCreateAction setOwnerType(@Nonnull OwnerType type); + + /** + * The type of the owner for the entitlement + */ + enum OwnerType + { + GUILD_SUBSCRIPTION(1), + USER_SUBSCRIPTION(2); + + private final int key; + + OwnerType(int key) + { + this.key = key; + } + + /** + * The Discord defined id key for this OwnerType. + * + * @return the id key. + */ + public int getKey() + { + return key; + } + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java index 01f84c3a50..f269cfc0ce 100644 --- a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java @@ -48,10 +48,7 @@ import net.dv8tion.jda.api.managers.AudioManager; import net.dv8tion.jda.api.managers.Presence; import net.dv8tion.jda.api.requests.*; -import net.dv8tion.jda.api.requests.restaction.CacheRestAction; -import net.dv8tion.jda.api.requests.restaction.CommandCreateAction; -import net.dv8tion.jda.api.requests.restaction.CommandEditAction; -import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction; +import net.dv8tion.jda.api.requests.restaction.*; import net.dv8tion.jda.api.requests.restaction.pagination.EntitlementPaginationAction; import net.dv8tion.jda.api.sharding.ShardManager; import net.dv8tion.jda.api.utils.*; @@ -72,10 +69,7 @@ import net.dv8tion.jda.internal.managers.DirectAudioControllerImpl; import net.dv8tion.jda.internal.managers.PresenceImpl; import net.dv8tion.jda.internal.requests.*; -import net.dv8tion.jda.internal.requests.restaction.CommandCreateActionImpl; -import net.dv8tion.jda.internal.requests.restaction.CommandEditActionImpl; -import net.dv8tion.jda.internal.requests.restaction.CommandListUpdateActionImpl; -import net.dv8tion.jda.internal.requests.restaction.GuildActionImpl; +import net.dv8tion.jda.internal.requests.restaction.*; import net.dv8tion.jda.internal.requests.restaction.pagination.EntitlementPaginationActionImpl; import net.dv8tion.jda.internal.utils.Helpers; import net.dv8tion.jda.internal.utils.*; @@ -1173,6 +1167,30 @@ public EntitlementPaginationAction retrieveEntitlements() return new EntitlementPaginationActionImpl(this); } + @Nonnull + @Override + public RestAction retrieveEntitlementById(long entitlementId) + { + return new RestActionImpl<>(this, Route.Applications.GET_ENTITLEMENT.compile(getSelfUser().getApplicationId(), Long.toUnsignedString(entitlementId))); + } + + @Nonnull + @Override + public TestEntitlementCreateAction createTestEntitlement(long skuId, long ownerId, @Nonnull TestEntitlementCreateActionImpl.OwnerType ownerType) + { + Checks.notNull(ownerType, "ownerType"); + + return new TestEntitlementCreateActionImpl(this, skuId, ownerId, ownerType); + } + + @Nonnull + @Override + public RestAction deleteTestEntitlement(long entitlementId) + { + Route.CompiledRoute route = Route.Applications.DELETE_TEST_ENTITLEMENT.compile(getSelfUser().getApplicationId(), Long.toUnsignedString(entitlementId)); + return new RestActionImpl<>(this, route); + } + @Nonnull @Override public JDA setRequiredScopes(@Nonnull Collection scopes) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/EntitlementImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/EntitlementImpl.java index e77d790477..2b1aa414cd 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntitlementImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntitlementImpl.java @@ -16,7 +16,12 @@ package net.dv8tion.jda.internal.entities; +import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.Entitlement; +import net.dv8tion.jda.api.requests.RestAction; +import net.dv8tion.jda.api.requests.Route; +import net.dv8tion.jda.internal.requests.CompletedRestAction; +import net.dv8tion.jda.internal.requests.RestActionImpl; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -24,6 +29,7 @@ public class EntitlementImpl implements Entitlement { + private final JDA api; private long id; private long skuId; private long applicationId; @@ -33,9 +39,11 @@ public class EntitlementImpl implements Entitlement private boolean deleted; private OffsetDateTime startsAt; private OffsetDateTime endsAt; + private boolean consumed; - public EntitlementImpl(long id, long skuId, long applicationId, long userId, long guildId, EntitlementType type, boolean deleted, @Nullable OffsetDateTime startsAt, @Nullable OffsetDateTime endsAt) + public EntitlementImpl(JDA api, long id, long skuId, long applicationId, long userId, long guildId, EntitlementType type, boolean deleted, @Nullable OffsetDateTime startsAt, @Nullable OffsetDateTime endsAt, boolean consumed) { + this.api = api; this.id = id; this.skuId = skuId; this.applicationId = applicationId; @@ -45,6 +53,7 @@ public EntitlementImpl(long id, long skuId, long applicationId, long userId, lon this.deleted = deleted; this.startsAt = startsAt; this.endsAt = endsAt; + this.consumed = consumed; } @Override @@ -103,4 +112,21 @@ public OffsetDateTime getTimeEnding() { return endsAt; } + + @Override + public boolean isConsumed() + { + return consumed; + } + + @Nonnull + @Override + public RestAction consume() + { + if (consumed) + return new CompletedRestAction<>(api, null); + + Route.CompiledRoute route = Route.Applications.CONSUME_ENTITLEMENT.compile(getApplicationId(), getId()); + return new RestActionImpl<>(api, route); + } } 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 c381bd24a9..cf7e1583e9 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -2606,6 +2606,7 @@ public AuditLogChange createAuditLogChange(DataObject change) public Entitlement createEntitlement(DataObject object) { return new EntitlementImpl( + getJDA(), object.getUnsignedLong("id"), object.getUnsignedLong("sku_id"), object.getUnsignedLong("application_id"), @@ -2614,7 +2615,8 @@ public Entitlement createEntitlement(DataObject object) Entitlement.EntitlementType.fromKey(object.getInt("type")), object.getBoolean("deleted"), object.getOffsetDateTime("starts_at", null), - object.getOffsetDateTime("ends_at", null) + object.getOffsetDateTime("ends_at", null), + object.getBoolean("consumed", false) ); } diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/TestEntitlementCreateActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/TestEntitlementCreateActionImpl.java new file mode 100644 index 0000000000..20d7d174bc --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/TestEntitlementCreateActionImpl.java @@ -0,0 +1,91 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.requests.restaction; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.Entitlement; +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.TestEntitlementCreateAction; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.requests.RestActionImpl; +import net.dv8tion.jda.internal.utils.Checks; +import okhttp3.RequestBody; + +import javax.annotation.Nonnull; + +public class TestEntitlementCreateActionImpl extends RestActionImpl implements TestEntitlementCreateAction +{ + + private long skuId; + private long ownerId; + private OwnerType type; + + public TestEntitlementCreateActionImpl(JDA api, long skuId, long ownerId, OwnerType type) + { + super(api, Route.Applications.CREATE_TEST_ENTITLEMENT.compile(api.getSelfUser().getApplicationId())); + + this.skuId = skuId; + this.ownerId = ownerId; + this.type = type; + } + + @Nonnull + @Override + public TestEntitlementCreateAction setSkuId(long skuId) + { + this.skuId = skuId; + return this; + } + + @Nonnull + @Override + public TestEntitlementCreateAction setOwnerId(long ownerId) + { + this.ownerId = ownerId; + return this; + } + + @Nonnull + @Override + public TestEntitlementCreateAction setOwnerType(@Nonnull OwnerType type) + { + Checks.notNull(type, "type"); + + this.type = type; + return this; + } + + @Override + protected void handleSuccess(Response response, Request request) + { + DataObject object = response.getObject(); + request.onSuccess(api.getEntityBuilder().createEntitlement(object)); + } + + @Override + protected RequestBody finalizeData() + { + DataObject object = DataObject.empty(); + object.put("sku_id", skuId); + object.put("owner_id", ownerId); + object.put("owner_type", type.getKey()); + + return getRequestBody(object); + } +}