Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

✨ Entitlements, SKUs, and Subscriptions #1257

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/Discord/Discord.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@
use Discord\Parts\User\Member;
use Discord\Parts\User\User;
use Discord\Repository\AbstractRepository;
use Discord\Repository\EntitlementRepository;
use Discord\Repository\EmojiRepository;
use Discord\Repository\GuildRepository;
use Discord\Repository\PrivateChannelRepository;
use Discord\Repository\SKUsRepository;
use Discord\Repository\UserRepository;
use Discord\Voice\VoiceClient;
use Discord\WebSockets\Event;
Expand Down Expand Up @@ -74,10 +76,14 @@
* @property bool $bot Whether the client is a bot.
* @property User $user The user instance of the client.
* @property Application $application The OAuth2 application of the bot.

* @property EntitlementRepository $entitlements
* @property EmojiRepository $emojis
* @property GuildRepository $guilds
* @property PrivateChannelRepository $private_channels
* @property SKUsRepository $skus
* @property SoundRepository $sounds
* @property UserRepository $users
* @property EmojiRepository $emojis
*/
class Discord
{
Expand Down
156 changes: 156 additions & 0 deletions src/Discord/Parts/Entitlement.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<?php

/*
* This file is a part of the DiscordPHP project.
*
* Copyright (c) 2015-present David Cole <[email protected]>
*
* This file is subject to the MIT license that is bundled
* with this source code in the LICENSE.md file.
*/

namespace Discord\Parts;

use Carbon\Carbon;
use Discord\Http\Endpoint;
use Discord\Parts\Part;
use Discord\Parts\User\User;
use Discord\Parts\Guild\Guild;
use React\Promise\ExtendedPromiseInterface;

/**
* An entitlement object represents a premium offering in the application that a user or guild has access to.
*
* @link https://discord.com/developers/docs/resources/entitlement
*
* @since 10.0.0
*
* @property string $id ID of the entitlement.
* @property string $sku_id ID of the SKU.
* @property string $application_id ID of the parent application.
* @property string|null $user_id ID of the user that is granted access to the entitlement's SKU.
* @property int $type Type of entitlement.
* @property bool $deleted Entitlement was deleted.
* @property Carbon|null $starts_at Start date at which the entitlement is valid.
* @property Carbon|null $ends_at Date at which the entitlement is no longer valid.
* @property string|null $guild_id ID of the guild that is granted access to the entitlement's SKU.
* @property bool|null $consumed For consumable items, whether or not the entitlement has been consumed.
* @property-read Guild|null $guild The guild that is granted access to the entitlement's SKU.
* @property-read User|null $user The user that is granted access to the entitlement's SKU.
*/
class Entitlement extends Part
{
public const OWNER_TYPE_GUILD = 1;
public const OWNER_TYPE_USER = 2;

public const TYPE_PURCHASE = 1;
public const TYPE_PREMIUM_SUBSCRIPTION = 2;
public const TYPE_DEVELOPER_GIFT = 3;
public const TYPE_TEST_MODE_PURCHASE = 4;
public const TYPE_FREE_PURCHASE = 5;
public const TYPE_USER_GIFT = 6;
public const TYPE_PREMIUM_PURCHASE = 7;
public const TYPE_APPLICATION_SUBSCRIPTION = 8;

/**
* {@inheritDoc}
*/
protected $fillable = [
'id',
'sku_id',
'application_id',
'user_id',
'type',
'deleted',
'starts_at',
'ends_at',
'guild_id',
'consumed',

// @internal
'guild',
'user',
];

/**
* Returns the guild attribute that is granted access to the entitlement's sku.
*
* @return Guild|null
*/
protected function getGuildAttribute(): ?Guild
{
return $this->discord->guilds->get('id', $this->guild_id);
}

/**
* Gets the user that is granted access to the entitlement's sku.
*
* @return User|null
*/
protected function getUserAttribute(): ?User
{
return $this->discord->users->get('id', $this->user_id);
}

/**
* {@inheritDoc}
*/
public function getRepositoryAttributes(): array
{
return [
'entitlement_id' => $this->id,
'application_id' => $this->application_id,
];
}

/**
* Returns the starts at attribute.
*
* @return Carbon|null The time that the invite was created.
*
* @throws \Exception
*/
protected function getStartsAtAttribute(): ?Carbon
{
if (! isset($this->attributes['starts_at'])) {
return null;
}

return new Carbon($this->attributes['starts_at']);
}

/**
* Returns the ends at attribute.
*
* @return Carbon|null The time that the invite was created.
*
* @throws \Exception
*/
protected function getEndsAtAttribute(): ?Carbon
{
if (! isset($this->attributes['ends_at'])) {
return null;
}

return new Carbon($this->attributes['ends_at']);
}

/**
* Consumes an entitlement.
*
* For One-Time Purchase consumable SKUs, marks a given entitlement for the user as consumed.
* The entitlement will have consumed: true when using List Entitlements.
*
* @param Entitlement $part
*
* @return ExtendedPromiseInterface A promise that resolves to an Entitlement object.
*/
public function consume(Entitlement $part): ExtendedPromiseInterface
{
return $this->http->post(Endpoint::bind(Endpoint::APPLICATION_ENTITLEMENT_CONSUME, $this->application_id, $part->id))
->then(function ($response) use ($part) { // Returns a 204 No Content on success.
$part->consumed = true;
return $part;
});
}
}
3 changes: 3 additions & 0 deletions src/Discord/Parts/Guild/Guild.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use Discord\Parts\Part;
use Discord\Parts\User\Member;
use Discord\Parts\User\User;
use Discord\Repository\EntitlementRepository;
use Discord\Repository\Guild\BanRepository;
use Discord\Repository\Guild\ChannelRepository;
use Discord\Repository\Guild\EmojiRepository;
Expand Down Expand Up @@ -141,6 +142,7 @@
*
* @property AutoModerationRuleRepository $auto_moderation_rules
* @property BanRepository $bans
* @property EntitlementRepository $entitlements
* @property GuildCommandRepository $commands
* @property CommandPermissionsRepository $command_permissions
* @property IntegrationRepository $integrations
Expand Down Expand Up @@ -286,6 +288,7 @@ class Guild extends Part
*/
protected $repositories = [
'roles' => RoleRepository::class,
'entitlements' => EntitlementRepository::class,
'emojis' => EmojiRepository::class,
'stickers' => StickerRepository::class,
'members' => MemberRepository::class,
Expand Down
102 changes: 102 additions & 0 deletions src/Discord/Parts/SKUs/SKU.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

/*
* This file is a part of the DiscordPHP project.
*
* Copyright (c) 2015-present David Cole <[email protected]>
*
* This file is subject to the MIT license that is bundled
* with this source code in the LICENSE.md file.
*/

namespace Discord\Parts\SKUs;

use Discord\Parts\Part;
use Discord\Repository\SKUs\SubscriptionRepository;

/**
* An SKU object represents a premium offering in the application that a user or guild can purchase.
*
* @link https://discord.com/developers/docs/resources/sku
*
* @since 10.0.0
*
* @property string $id ID of the SKU.
* @property int $type Type of SKU.
* @property string $application_id ID of the parent application.
* @property string $name Customer-facing name of the premium offering.
* @property string $slug System-generated URL slug based on the SKU's name.
* @property int $flags SKU flags combined as a bitfield.
* @property-read SubscriptionRepository $subscriptions Repository for the subscriptions that belong to this SKU.
*/
class SKU extends Part
{
public const TYPE_DURABLE = 2;
public const TYPE_CONSUMABLE = 3;
public const TYPE_SUBSCRIPTION = 5;
public const TYPE_SUBSCRIPTION_GROUP = 6;

public const FLAG_AVAILABLE = 1 << 2;
public const FLAG_GUILD_SUBSCRIPTION = 1 << 7;
public const FLAG_USER_SUBSCRIPTION = 1 << 8;

/**
* {@inheritDoc}
*/
protected $fillable = [
'id',
'type',
'application_id',
'name',
'slug',
'flags',
];

/**
* {@inheritDoc}
*/
protected $repositories = [
'subscriptions' => SubscriptionRepository::class,
];

/**
* Checks if the SKU is available for purchase.
*
* @return bool
*/
public function isAvailable(): bool
{
return ($this->flags & self::FLAG_AVAILABLE) === self::FLAG_AVAILABLE;
}

/**
* Checks if the SKU is a guild subscription.
*
* @return bool
*/
public function isGuildSubscription(): bool
{
return ($this->flags & self::FLAG_GUILD_SUBSCRIPTION) === self::FLAG_GUILD_SUBSCRIPTION;
}

/**
* Checks if the SKU is a user subscription.
*
* @return bool
*/
public function isUserSubscription(): bool
{
return ($this->flags & self::FLAG_USER_SUBSCRIPTION) === self::FLAG_USER_SUBSCRIPTION;
}

/**
* {@inheritDoc}
*/
public function getRepositoryAttributes(): array
{
return [
'sku_id' => $this->id,
'application_id' => $this->application_id,
];
}
}
Loading