diff --git a/src/Discord/Discord.php b/src/Discord/Discord.php index 678bc7ece..b1f34a7e0 100644 --- a/src/Discord/Discord.php +++ b/src/Discord/Discord.php @@ -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; @@ -74,12 +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 - */ class Discord { diff --git a/src/Discord/Parts/Entitlement.php b/src/Discord/Parts/Entitlement.php new file mode 100644 index 000000000..40f637b64 --- /dev/null +++ b/src/Discord/Parts/Entitlement.php @@ -0,0 +1,156 @@ + + * + * 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; + }); + } +} diff --git a/src/Discord/Parts/Guild/Guild.php b/src/Discord/Parts/Guild/Guild.php index 323542598..2dc244804 100644 --- a/src/Discord/Parts/Guild/Guild.php +++ b/src/Discord/Parts/Guild/Guild.php @@ -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; @@ -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 @@ -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, diff --git a/src/Discord/Parts/SKUs/SKU.php b/src/Discord/Parts/SKUs/SKU.php new file mode 100644 index 000000000..bfd79f741 --- /dev/null +++ b/src/Discord/Parts/SKUs/SKU.php @@ -0,0 +1,102 @@ + + * + * 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, + ]; + } +} diff --git a/src/Discord/Parts/SKUs/Subscription.php b/src/Discord/Parts/SKUs/Subscription.php new file mode 100644 index 000000000..b706f0e2c --- /dev/null +++ b/src/Discord/Parts/SKUs/Subscription.php @@ -0,0 +1,168 @@ + + * + * 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 Carbon\Carbon; +use Discord\Helpers\Collection; +use Discord\Parts\Part; +use React\Promise\ExtendedPromiseInterface; + +/** + * A Subscription object represents a user making recurring payments for at least one SKU over an ongoing period. + * + * @link https://discord.com/developers/docs/resources/subscription + * + * @since 10.0.0 + * + * @property string $id ID of the subscription. + * @property string $user_id ID of the user who is subscribed. + * @property array $sku_ids List of SKUs subscribed to. + * @property array $entitlement_ids List of entitlements granted for this subscription. + * @property Carbon $current_period_start Start of the current subscription period. + * @property Carbon $current_period_end End of the current subscription period. + * @property int $status Current status of the subscription. + * @property Carbon|null $canceled_at When the subscription was canceled. + * @property string|null $country ISO3166-1 alpha-2 country code of the payment source used to purchase the subscription. + * @property-read User $user User who is subscribed. + * @property-read Collection $entitlements Entitlements granted for this subscription. + * @property-read Collection $skus SKUs subscribed to. + */ +class Subscription extends Part +{ + public const STATUS_ACTIVE = 0; + public const STATUS_ENDING = 1; + public const STATUS_INACTIVE = 2; + + /** + * {@inheritDoc} + */ + protected $fillable = [ + 'id', + 'user_id', + 'sku_ids', + 'entitlement_ids', + 'current_period_start', + 'current_period_end', + 'status', + 'canceled_at', + 'country', + + // @internal + 'subscriptions', + ]; + + /** + * Checks if the subscription is active. + * + * @return bool + */ + public function isActive(): bool + { + return $this->status === self::STATUS_ACTIVE; + } + + /** + * Checks if the subscription is ending. + * + * @return bool + */ + public function isEnding(): bool + { + return $this->status === self::STATUS_ENDING; + } + + /** + * Checks if the subscription is inactive. + * + * @return bool + */ + public function isInactive(): bool + { + return $this->status === self::STATUS_INACTIVE; + } + + /** + * Gets the entitlements for this subscription. + * + * @return ExtendedPromiseInterface + */ + protected function getEntitlementsAttribute(): Collection + { + return $this->discord->entitlements->filter(function ($entitlement) { + return in_array($entitlement->id, $this->entitlement_ids); + }); + } + + /** + * Gets the SKUs for this subscription. + * + * @return ExtendedPromiseInterface + */ + protected function getSkusAttribute(): Collection + { + return $this->discord->skus->filter(function ($sku) { + return in_array($sku->id, $this->sku_ids); + }); + + return $repository; + } + + /** + * Gets the start of the current subscription period. + * + * @return Carbon + */ + protected function getCurrentPeriodStartAttribute(): Carbon + { + return new Carbon($this->attributes['current_period_start']); + } + + /** + * Gets the end of the current subscription period. + * + * @return Carbon + */ + protected function getCurrentPeriodEndAttribute(): Carbon + { + return new Carbon($this->attributes['current_period_end']); + } + + /** + * Gets the cancellation time of the subscription. + * + * @return Carbon|null + */ + protected function getCanceledAtAttribute(): ?Carbon + { + return isset($this->attributes['canceled_at']) ? new Carbon($this->attributes['canceled_at']) : null; + } + + /** + * Gets the user who is subscribed. + * + * @return ExtendedPromiseInterface + */ + protected function getUserAttribute(): ExtendedPromiseInterface + { + return $this->discord->users->cacheGet($this->user_id); + } + + /** + * {@inheritDoc} + */ + public function getRepositoryAttributes(): array + { + return [ + 'subscription_id' => $this->id, + ]; + } +} diff --git a/src/Discord/Parts/User/Client.php b/src/Discord/Parts/User/Client.php index 5b4532914..92d1f36ff 100644 --- a/src/Discord/Parts/User/Client.php +++ b/src/Discord/Parts/User/Client.php @@ -15,9 +15,11 @@ use Discord\Http\Endpoint; use Discord\Parts\OAuth\Application; use Discord\Parts\Part; +use Discord\Repository\EntitlementRepository; use Discord\Repository\EmojiRepository; use Discord\Repository\GuildRepository; use Discord\Repository\PrivateChannelRepository; +use Discord\Repository\SKUsRepository; use Discord\Repository\SoundRepository; use Discord\Repository\UserRepository; use React\Promise\ExtendedPromiseInterface; @@ -42,9 +44,11 @@ * @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 */ @@ -75,9 +79,11 @@ class Client extends Part * {@inheritDoc} */ protected $repositories = [ + 'entitlements' => EntitlementRepository::class, 'emojis' => EmojiRepository::class, 'guilds' => GuildRepository::class, 'private_channels' => PrivateChannelRepository::class, + 'skus' => SKUsRepository::class, 'sounds' => SoundRepository::class, 'users' => UserRepository::class, ]; diff --git a/src/Discord/Repository/Entitlements/EntitlementRepository.php b/src/Discord/Repository/Entitlements/EntitlementRepository.php new file mode 100644 index 000000000..42120cb5d --- /dev/null +++ b/src/Discord/Repository/Entitlements/EntitlementRepository.php @@ -0,0 +1,126 @@ + + * + * This file is subject to the MIT license that is bundled + * with this source code in the LICENSE.md file. + */ + +namespace Discord\Repository; + +use Discord\Discord; +use Discord\Http\Endpoint; +use Discord\Parts\Entitlement; +use React\Promise\ExtendedPromiseInterface; + + +use function React\Promise\resolve; + +/** + * Contains entitlements of an application. + * + * @see Entitlement + * @see \Discord\Parts\User\Client + * + * @since 10.0.0 + * + * @method Entitlement|null get(string $discrim, $key) + * @method Entitlement|null pull(string|int $key, $default = null) + * @method Entitlement|null first() + * @method Entitlement|null last() + * @method Entitlement|null find(callable $callback) + */ +class EntitlementRepository extends AbstractRepository +{ + /** + * {@inheritDoc} + */ + protected $endpoints = [ + 'all' => Endpoint::APPLICATION_ENTITLEMENTS, + 'get' => Endpoint::APPLICATION_ENTITLEMENT, + 'create' => Endpoint::APPLICATION_ENTITLEMENTS, + 'delete' => Endpoint::APPLICATION_ENTITLEMENT, + ]; + + /** + * {@inheritDoc} + */ + protected $class = Entitlement::class; + + /** + * {@inheritDoc} + */ + public function __construct(Discord $discord, array $vars = []) + { + $vars['application_id'] = $discord->application->id; + + parent::__construct($discord, $vars); + } + + /** + * @param object $response + * + * @return ExtendedPromiseInterface + */ + protected function cacheFreshen($response): ExtendedPromiseInterface + { + foreach ($response as $value) foreach ($value as $value) { + $value = array_merge($this->vars, (array) $value); + $part = $this->factory->create($this->class, $value, true); + $items[$part->{$this->discrim}] = $part; + } + + if (empty($items)) { + return resolve($this); + } + + return $this->cache->setMultiple($items)->then(fn ($success) => $this); + } + + /** + * Creates a test entitlement to a given SKU for a given guild or user. + * + * @param string $sku_id ID of the SKU to grant the entitlement to. + * @param string $owner_id ID of the guild or user to grant the entitlement to. + * @param int $owner_type 1 for a guild subscription, 2 for a user subscription. + * + * @throws \InvalidArgumentException + * + * @return ExtendedPromiseInterface + */ + public function createTestEntitlement(string $sku_id, string $owner_id, int $owner_type): ExtendedPromiseInterface + { + $allowed_owner_types = [Entitlement::OWNER_TYPE_GUILD, Entitlement::OWNER_TYPE_USER]; + + if (! in_array($owner_type, $allowed_owner_types)) { + throw new \InvalidArgumentException("The given owner type `{$owner_type}` is not valid."); + } + + $payload = [ + 'sku_id' => $sku_id, + 'owner_id' => $owner_id, + 'owner_type' => $owner_type, + ]; + + return $this->http->post(new Endpoint(Endpoint::APPLICATION_ENTITLEMENT), $payload) + ->then(fn ($response) => $this->factory->create($this->class, (array) $response, true)); + } + + /* + * Deletes a currently-active test entitlement. + * Discord will act as though that user or guild no longer has entitlement to your premium offering. + * + * @param Entitlement $part + * + * @return ExtendedPromiseInterface + */ + public function deleteTestEntitlement(Entitlement $part): ExtendedPromiseInterface + { + return $this->http->delete(Endpoint::bind(Endpoint::APPLICATION_ENTITLEMENT, ['application_id' => $part->application_id, 'entitlement_id' => $part->id])) + ->then(fn ($response) => $this->cache->delete($part->{$this->discrim}) + ->then(fn ($success) => $part)); + } +} diff --git a/src/Discord/Repository/SKUs/SubscriptionRepository.php b/src/Discord/Repository/SKUs/SubscriptionRepository.php new file mode 100644 index 000000000..204f4e235 --- /dev/null +++ b/src/Discord/Repository/SKUs/SubscriptionRepository.php @@ -0,0 +1,50 @@ + + * + * This file is subject to the MIT license that is bundled + * with this source code in the LICENSE.md file. + */ + +namespace Discord\Repository\SKUs; + +use Discord\Discord; +use Discord\Http\Endpoint; +use Discord\Parts\SKUs\Subscription; +use Discord\Repository\AbstractRepository; +use React\Promise\ExtendedPromiseInterface; + +use function React\Promise\resolve; + +/** + * Contains subscriptions of an application. + * + * @see Subscription + * @see \Discord\Parts\User\Client + * + * @since 10.0.0 + * + * @method Subscription|null get(string $discrim, $key) + * @method Subscription|null pull(string|int $key, $default = null) + * @method Subscription|null first() + * @method Subscription|null last() + * @method Subscription|null find(callable $callback) + */ +class SubscriptionRepository extends AbstractRepository +{ + /** + * {@inheritDoc} + */ + protected $endpoints = [ + 'all' => Endpoint::SKU_SUBSCRIPTIONS, + 'get' => Endpoint::SKU_SUBSCRIPTION, + ]; + + /** + * {@inheritDoc} + */ + protected $class = Subscription::class; +} diff --git a/src/Discord/Repository/SKUsRepository.php b/src/Discord/Repository/SKUsRepository.php new file mode 100644 index 000000000..6f6b24128 --- /dev/null +++ b/src/Discord/Repository/SKUsRepository.php @@ -0,0 +1,78 @@ + + * + * This file is subject to the MIT license that is bundled + * with this source code in the LICENSE.md file. + */ + +namespace Discord\Repository; + +use Discord\Discord; +use Discord\Http\Endpoint; +use Discord\Parts\SKUs\SKU; +use React\Promise\ExtendedPromiseInterface; + +use function React\Promise\resolve; + +/** + * Contains SKUs of an application. + * + * @see SKU + * @see \Discord\Parts\User\Client + * + * @since 10.0.0 + * + * @method SKU|null get(string $discrim, $key) + * @method SKU|null pull(string|int $key, $default = null) + * @method SKU|null first() + * @method SKU|null last() + * @method SKU|null find(callable $callback) + */ +class SKUsRepository extends AbstractRepository +{ + /** + * {@inheritDoc} + */ + protected $endpoints = [ + 'all' => Endpoint::APPLICATION_SKUS, + ]; + + /** + * {@inheritDoc} + */ + protected $class = SKU::class; + + /** + * {@inheritDoc} + */ + public function __construct(Discord $discord, array $vars = []) + { + $vars['application_id'] = $discord->application->id; + + parent::__construct($discord, $vars); + } + + /** + * @param object $response + * + * @return ExtendedPromiseInterface + */ + protected function cacheFreshen($response): ExtendedPromiseInterface + { + foreach ($response as $value) foreach ($value as $value) { + $value = array_merge($this->vars, (array) $value); + $part = $this->factory->create($this->class, $value, true); + $items[$part->{$this->discrim}] = $part; + } + + if (empty($items)) { + return resolve($this); + } + + return $this->cache->setMultiple($items)->then(fn ($success) => $this); + } +} diff --git a/src/Discord/WebSockets/Event.php b/src/Discord/WebSockets/Event.php index 9ead53bac..bdd537043 100644 --- a/src/Discord/WebSockets/Event.php +++ b/src/Discord/WebSockets/Event.php @@ -120,6 +120,15 @@ abstract class Event public const MESSAGE_POLL_VOTE_ADD = 'MESSAGE_POLL_VOTE_ADD'; public const MESSAGE_POLL_VOTE_REMOVE = 'MESSAGE_POLL_VOTE_REMOVE'; + // Entitlements and SKUs + public const ENTITLEMENT_CREATE = 'ENTITLEMENT_CREATE'; + public const ENTITLEMENT_UPDATE = 'ENTITLEMENT_UPDATE'; + public const ENTITLEMENT_DELETE = 'ENTITLEMENT_DELETE'; + + public const SUBSCRIPTION_CREATE = 'SUBSCRIPTION_CREATE'; + public const SUBSCRIPTION_UPDATE = 'SUBSCRIPTION_UPDATE'; + public const SUBSCRIPTION_DELETE = 'SUBSCRIPTION_DELETE'; + /** * The Discord client instance. * diff --git a/src/Discord/WebSockets/Events/EntitlementCreate.php b/src/Discord/WebSockets/Events/EntitlementCreate.php new file mode 100644 index 000000000..bf0bdf448 --- /dev/null +++ b/src/Discord/WebSockets/Events/EntitlementCreate.php @@ -0,0 +1,46 @@ + + * + * This file is subject to the MIT license that is bundled + * with this source code in the LICENSE.md file. + */ + +namespace Discord\WebSockets\Events; + +use Discord\Parts\Guild\Guild; +use Discord\Parts\Entitlement; +use Discord\WebSockets\Event; + +/** + * @link https://discord.com/developers/docs/topics/gateway-events#guild-soundboard-sound-create + * + * @since 10.0.0 + */ +class EntitlementCreate extends Event +{ + /** + * {@inheritDoc} + */ + public function handle($data) + { + /** @var Entitlement */ + $entitlementPart = $this->factory->part(Entitlement::class, (array) $data, true); + + if (isset($data->guild_id)) { + /** @var Guild|null */ + $guild = yield $this->discord->guilds->cacheGet($data->guild_id); + if ($guild instanceof Guild) { + $guild->entitlements->set($data->id, $entitlementPart); + return $entitlementPart; + } + } + + $this->discord->entitlements->set($data->id, $entitlementPart); + + return $entitlementPart; + } +} diff --git a/src/Discord/WebSockets/Events/EntitlementDelete.php b/src/Discord/WebSockets/Events/EntitlementDelete.php new file mode 100644 index 000000000..96e0e59dd --- /dev/null +++ b/src/Discord/WebSockets/Events/EntitlementDelete.php @@ -0,0 +1,34 @@ + + * + * This file is subject to the MIT license that is bundled + * with this source code in the LICENSE.md file. + */ + +namespace Discord\WebSockets\Events; + +use Discord\Parts\Entitlement; +use Discord\WebSockets\Event; + +/** + * @link https://discord.com/developers/docs/topics/gateway-events#guild-soundboard-sound-delete + * + * @since 10.0.0 + */ +class EntitlementDelete extends Event +{ + /** + * {@inheritDoc} + */ + public function handle($data) + { + /** @var ?Entitlement */ + $entitlementPart = yield $this->discord->entitlements->cachePull($data->id); + + return $entitlementPart ?? $this->factory->part(Entitlement::class, (array) $data); + } +} diff --git a/src/Discord/WebSockets/Events/EntitlementUpdate.php b/src/Discord/WebSockets/Events/EntitlementUpdate.php new file mode 100644 index 000000000..5d71dc624 --- /dev/null +++ b/src/Discord/WebSockets/Events/EntitlementUpdate.php @@ -0,0 +1,68 @@ + + * + * This file is subject to the MIT license that is bundled + * with this source code in the LICENSE.md file. + */ + +namespace Discord\WebSockets\Events; + +use Discord\Parts\Guild\Guild; +use Discord\Parts\Entitlement; +use Discord\WebSockets\Event; + +/** + * @link https://discord.com/developers/docs/topics/gateway-events#guild-entitlementboard-entitlement-update + * + * @since 10.0.0 + */ +class EntitlementUpdate extends Event +{ + /** + * {@inheritDoc} + */ + public function handle($data) + { + $newEntitlementPart = $oldEntitlementPart = null; + + if (isset($data->guild_id)) { + /** @var Guild|null */ + $guild = yield $this->discord->guilds->cacheGet($data->guild_id); + if ($guild instanceof Guild) { + /** @var ?Entitlement */ + $oldEntitlementPart = yield $guild->entitlements->cacheGet($data->id); + if ($oldEntitlementPart instanceof Entitlement) { + $newEntitlementPart = clone $oldEntitlementPart; + $newEntitlementPart->fill((array) $data); + } + $guild->entitlements->set($data->id, $newEntitlementPart ?? $this->factory->part(Entitlement::class, (array) $data, true)); + } + } else { + /** @var ?Entitlement */ + $oldEntitlementPart = yield $this->discord->entitlements->cacheGet($data->id); + if ($oldEntitlementPart instanceof Entitlement) { + $newEntitlementPart = clone $oldEntitlementPart; + $newEntitlementPart->fill((array) $data); + } + } + + /** @var Entitlement */ + $newEntitlementPart = $newEntitlementPart ?? $this->factory->part(Entitlement::class, (array) $data, true); + + $this->discord->entitlements->set($data->id, $newEntitlementPart); + + return [$newEntitlementPart, $oldEntitlementPart]; + } +} + + + + + + + + diff --git a/src/Discord/WebSockets/Events/SubscriptionCreate.php b/src/Discord/WebSockets/Events/SubscriptionCreate.php new file mode 100644 index 000000000..eac8c475e --- /dev/null +++ b/src/Discord/WebSockets/Events/SubscriptionCreate.php @@ -0,0 +1,36 @@ + + * + * This file is subject to the MIT license that is bundled + * with this source code in the LICENSE.md file. + */ + +namespace Discord\WebSockets\Events; + +use Discord\Parts\SKUs\Subscription; +use Discord\WebSockets\Event; + +/** + * @link https://discord.com/developers/docs/topics/gateway-events#guild-soundboard-sound-create + * + * @since 10.0.0 + */ +class SubscriptionCreate extends Event +{ + /** + * {@inheritDoc} + */ + public function handle($data) + { + /** @var Subscription */ + $subscriptionPart = $this->factory->part(Subscription::class, (array) $data, true); + + $this->discord->subscriptions->set($data->id, $subscriptionPart); + + return $subscriptionPart; + } +} diff --git a/src/Discord/WebSockets/Events/SubscriptionDelete.php b/src/Discord/WebSockets/Events/SubscriptionDelete.php new file mode 100644 index 000000000..75642496a --- /dev/null +++ b/src/Discord/WebSockets/Events/SubscriptionDelete.php @@ -0,0 +1,34 @@ + + * + * This file is subject to the MIT license that is bundled + * with this source code in the LICENSE.md file. + */ + +namespace Discord\WebSockets\Events; + +use Discord\Parts\SKUs\Subscription; +use Discord\WebSockets\Event; + +/** + * @link https://discord.com/developers/docs/topics/gateway-events#guild-soundboard-sound-delete + * + * @since 10.0.0 + */ +class SubscriptionDelete extends Event +{ + /** + * {@inheritDoc} + */ + public function handle($data) + { + /** @var ?Subscription */ + $subscriptionPart = yield $this->discord->subscriptions->cachePull($data->id); + + return $subscriptionPart ?? $this->factory->part(Subscription::class, (array) $data); + } +} diff --git a/src/Discord/WebSockets/Events/SubscriptionUpdate.php b/src/Discord/WebSockets/Events/SubscriptionUpdate.php new file mode 100644 index 000000000..8ebb263a1 --- /dev/null +++ b/src/Discord/WebSockets/Events/SubscriptionUpdate.php @@ -0,0 +1,45 @@ + + * + * This file is subject to the MIT license that is bundled + * with this source code in the LICENSE.md file. + */ + +namespace Discord\WebSockets\Events; + +use Discord\Parts\SKUs\Subscription; +use Discord\WebSockets\Event; + +/** + * @link https://discord.com/developers/docs/topics/gateway-events#guild-Subscriptionboard-Subscription-update + * + * @since 10.0.0 + */ +class SubscriptionUpdate extends Event +{ + /** + * {@inheritDoc} + */ + public function handle($data) + { + $newSubscriptionPart = $oldSubscriptionPart = null; + + /** @var ?Subscription */ + $oldSubscriptionPart = yield $this->discord->Subscriptions->cacheGet($data->id); + if ($oldSubscriptionPart instanceof Subscription) { + $newSubscriptionPart = clone $oldSubscriptionPart; + $newSubscriptionPart->fill((array) $data); + } + + /** @var Subscription */ + $newSubscriptionPart = $newSubscriptionPart ?? $this->factory->part(Subscription::class, (array) $data, true); + + $this->discord->Subscriptions->set($data->id, $newSubscriptionPart); + + return [$newSubscriptionPart, $oldSubscriptionPart]; + } +} diff --git a/src/Discord/WebSockets/Handlers.php b/src/Discord/WebSockets/Handlers.php index 8b90f0cbf..10b7e871e 100644 --- a/src/Discord/WebSockets/Handlers.php +++ b/src/Discord/WebSockets/Handlers.php @@ -123,6 +123,15 @@ public function __construct() $this->addHandler(Event::GUILD_SOUNDBOARD_SOUND_UPDATE, \Discord\WebSockets\Events\GuildSoundboardSoundUpdate::class); $this->addHandler(Event::GUILD_SOUNDBOARD_SOUND_DELETE, \Discord\WebSockets\Events\GuildSoundboardSoundDelete::class); $this->addHandler(Event::SOUNDBOARD_SOUNDS, \Discord\WebSockets\Events\SoundboardSounds::class); + + // Entitlement Event Handlers + $this->addHandler(Event::ENTITLEMENT_CREATE, \Discord\WebSockets\Events\EntitlementCreate::class); + $this->addHandler(Event::ENTITLEMENT_UPDATE, \Discord\WebSockets\Events\EntitlementUpdate::class); + $this->addHandler(Event::ENTITLEMENT_DELETE, \Discord\WebSockets\Events\EntitlementDelete::class); + + $this->addHandler(Event::SUBSCRIPTION_CREATE, \Discord\WebSockets\Events\SubscriptionCreate::class); + $this->addHandler(Event::SUBSCRIPTION_UPDATE, \Discord\WebSockets\Events\SubscriptionUpdate::class); + $this->addHandler(Event::SUBSCRIPTION_DELETE, \Discord\WebSockets\Events\SubscriptionDelete::class); } /**