From 657c709ec711d5b5dd571ff2a08a69620897fd68 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Sun, 29 Sep 2024 05:06:45 -0400 Subject: [PATCH 1/8] Entitlements, SKUs, and Subscriptions parts, repositories, events, and handlers --- src/Discord/Discord.php | 6 + src/Discord/Parts/Entitlement.php | 156 ++++++++++++++++++ src/Discord/Parts/Guild/Guild.php | 3 + src/Discord/Parts/SKUs/SKU.php | 82 +++++++++ src/Discord/Parts/SKUs/Subscription.php | 146 ++++++++++++++++ src/Discord/Parts/User/Client.php | 6 + .../Entitlements/EntitlementRepository.php | 126 ++++++++++++++ .../Repository/SKUs/SKUsRepository.php | 78 +++++++++ src/Discord/WebSockets/Event.php | 9 + .../WebSockets/Events/EntitlementCreate.php | 46 ++++++ .../WebSockets/Events/EntitlementDelete.php | 34 ++++ .../WebSockets/Events/EntitlementUpdate.php | 68 ++++++++ .../WebSockets/Events/SubscriptionCreate.php | 36 ++++ .../WebSockets/Events/SubscriptionDelete.php | 34 ++++ .../WebSockets/Events/SubscriptionUpdate.php | 45 +++++ src/Discord/WebSockets/Handlers.php | 9 + 16 files changed, 884 insertions(+) create mode 100644 src/Discord/Parts/Entitlement.php create mode 100644 src/Discord/Parts/SKUs/SKU.php create mode 100644 src/Discord/Parts/SKUs/Subscription.php create mode 100644 src/Discord/Repository/Entitlements/EntitlementRepository.php create mode 100644 src/Discord/Repository/SKUs/SKUsRepository.php create mode 100644 src/Discord/WebSockets/Events/EntitlementCreate.php create mode 100644 src/Discord/WebSockets/Events/EntitlementDelete.php create mode 100644 src/Discord/WebSockets/Events/EntitlementUpdate.php create mode 100644 src/Discord/WebSockets/Events/SubscriptionCreate.php create mode 100644 src/Discord/WebSockets/Events/SubscriptionDelete.php create mode 100644 src/Discord/WebSockets/Events/SubscriptionUpdate.php diff --git a/src/Discord/Discord.php b/src/Discord/Discord.php index 9c6b276ea..35a09c5ec 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,8 +76,12 @@ * @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 UserRepository $users * @property EmojiRepository $emojis */ diff --git a/src/Discord/Parts/Entitlement.php b/src/Discord/Parts/Entitlement.php new file mode 100644 index 000000000..c446bf307 --- /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 [ + 'application_id' => $this->application_id, + 'entitlement_id' => $this->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..93e18855a --- /dev/null +++ b/src/Discord/Parts/SKUs/SKU.php @@ -0,0 +1,82 @@ + + * + * 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; + +/** + * 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. + */ +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', + ]; + + /** + * 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; + } +} diff --git a/src/Discord/Parts/SKUs/Subscription.php b/src/Discord/Parts/SKUs/Subscription.php new file mode 100644 index 000000000..3ecb67b59 --- /dev/null +++ b/src/Discord/Parts/SKUs/Subscription.php @@ -0,0 +1,146 @@ + + * + * 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 Discord\Repository\EntitlementRepository; +use Discord\Repository\SKUsRepository; +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 EntitlementRepository $entitlements Entitlements granted for this subscription. + * @property-read SKUsRepository $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', + ]; + + /** + * 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; + } +} 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/SKUsRepository.php b/src/Discord/Repository/SKUs/SKUsRepository.php new file mode 100644 index 000000000..6f6b24128 --- /dev/null +++ b/src/Discord/Repository/SKUs/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 05338b16c..23e57e91a 100644 --- a/src/Discord/WebSockets/Event.php +++ b/src/Discord/WebSockets/Event.php @@ -115,6 +115,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 26b994ddb..d551f1372 100644 --- a/src/Discord/WebSockets/Handlers.php +++ b/src/Discord/WebSockets/Handlers.php @@ -117,6 +117,15 @@ public function __construct() $this->addHandler(Event::AUTO_MODERATION_RULE_UPDATE, \Discord\WebSockets\Events\AutoModerationRuleUpdate::class); $this->addHandler(Event::AUTO_MODERATION_RULE_DELETE, \Discord\WebSockets\Events\AutoModerationRuleDelete::class); $this->addHandler(Event::AUTO_MODERATION_ACTION_EXECUTION, \Discord\WebSockets\Events\AutoModerationActionExecution::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); } /** From dffc9a25eaaac8475432d059944c640b944ba83c Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Sun, 29 Sep 2024 08:00:37 -0400 Subject: [PATCH 2/8] Update Subscription.php --- src/Discord/Parts/SKUs/Subscription.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Discord/Parts/SKUs/Subscription.php b/src/Discord/Parts/SKUs/Subscription.php index 3ecb67b59..c9c642b6f 100644 --- a/src/Discord/Parts/SKUs/Subscription.php +++ b/src/Discord/Parts/SKUs/Subscription.php @@ -34,8 +34,9 @@ * @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 EntitlementRepository $entitlements Entitlements granted for this subscription. - * @property-read SKUsRepository $skus SKUs subscribed to. + * @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 { @@ -143,4 +144,14 @@ 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); + } } From 559c5078ed58c4547907ca0279da6a92671f2f1d Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Sun, 29 Sep 2024 08:11:42 -0400 Subject: [PATCH 3/8] Update Discord.php --- src/Discord/Discord.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Discord/Discord.php b/src/Discord/Discord.php index 35a09c5ec..beb638f8b 100644 --- a/src/Discord/Discord.php +++ b/src/Discord/Discord.php @@ -83,7 +83,6 @@ * @property PrivateChannelRepository $private_channels * @property SKUsRepository $skus * @property UserRepository $users - * @property EmojiRepository $emojis */ class Discord { From cfbc8e0f52f2cf1680dd3182ff7c6529826d3a3d Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Sun, 29 Sep 2024 08:13:12 -0400 Subject: [PATCH 4/8] Add missing Sounds repository --- src/Discord/Discord.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Discord/Discord.php b/src/Discord/Discord.php index beb638f8b..b1f34a7e0 100644 --- a/src/Discord/Discord.php +++ b/src/Discord/Discord.php @@ -82,6 +82,7 @@ * @property GuildRepository $guilds * @property PrivateChannelRepository $private_channels * @property SKUsRepository $skus + * @property SoundRepository $sounds * @property UserRepository $users */ class Discord From 35ce72ffe23e87f115f0aa58ddf43f434f46df40 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Sun, 29 Sep 2024 08:55:58 -0400 Subject: [PATCH 5/8] Placeholder I'm tired and making mistakes --- src/Discord/Parts/SKUs/SKU.php | 31 +++++++++--- src/Discord/Parts/SKUs/Subscription.php | 15 +++++- .../SKUs/SubscriptionRepository.php | 50 +++++++++++++++++++ .../Repository/{SKUs => }/SKUsRepository.php | 0 4 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 src/Discord/Repository/SKUs/SubscriptionRepository.php rename src/Discord/Repository/{SKUs => }/SKUsRepository.php (100%) diff --git a/src/Discord/Parts/SKUs/SKU.php b/src/Discord/Parts/SKUs/SKU.php index 93e18855a..d1dd46ed7 100644 --- a/src/Discord/Parts/SKUs/SKU.php +++ b/src/Discord/Parts/SKUs/SKU.php @@ -12,6 +12,7 @@ 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. @@ -20,12 +21,13 @@ * * @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 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 { @@ -50,6 +52,13 @@ class SKU extends Part 'flags', ]; + /** + * {@inheritDoc} + */ + protected $repositories = [ + 'subscriptions' => SubscriptionRepository::class, + ]; + /** * Checks if the SKU is available for purchase. * @@ -79,4 +88,14 @@ 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, + ]; + } } diff --git a/src/Discord/Parts/SKUs/Subscription.php b/src/Discord/Parts/SKUs/Subscription.php index c9c642b6f..b706f0e2c 100644 --- a/src/Discord/Parts/SKUs/Subscription.php +++ b/src/Discord/Parts/SKUs/Subscription.php @@ -14,8 +14,6 @@ use Carbon\Carbon; use Discord\Helpers\Collection; use Discord\Parts\Part; -use Discord\Repository\EntitlementRepository; -use Discord\Repository\SKUsRepository; use React\Promise\ExtendedPromiseInterface; /** @@ -57,6 +55,9 @@ class Subscription extends Part 'status', 'canceled_at', 'country', + + // @internal + 'subscriptions', ]; /** @@ -154,4 +155,14 @@ 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/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/SKUs/SKUsRepository.php b/src/Discord/Repository/SKUsRepository.php similarity index 100% rename from src/Discord/Repository/SKUs/SKUsRepository.php rename to src/Discord/Repository/SKUsRepository.php From 0345d92335076b38dd3c49e4d5b9a5579248de4f Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Sun, 29 Sep 2024 10:03:48 -0400 Subject: [PATCH 6/8] Update SKU.php --- src/Discord/Parts/SKUs/SKU.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Discord/Parts/SKUs/SKU.php b/src/Discord/Parts/SKUs/SKU.php index d1dd46ed7..bfd79f741 100644 --- a/src/Discord/Parts/SKUs/SKU.php +++ b/src/Discord/Parts/SKUs/SKU.php @@ -96,6 +96,7 @@ public function getRepositoryAttributes(): array { return [ 'sku_id' => $this->id, + 'application_id' => $this->application_id, ]; } } From 5775919bc37f9251dd1f9eb5aac93fc5fc5bfc0e Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Sun, 29 Sep 2024 10:33:25 -0400 Subject: [PATCH 7/8] Update Entitlement.php --- src/Discord/Parts/Entitlement.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord/Parts/Entitlement.php b/src/Discord/Parts/Entitlement.php index c446bf307..40f637b64 100644 --- a/src/Discord/Parts/Entitlement.php +++ b/src/Discord/Parts/Entitlement.php @@ -98,8 +98,8 @@ protected function getUserAttribute(): ?User public function getRepositoryAttributes(): array { return [ - 'application_id' => $this->application_id, 'entitlement_id' => $this->id, + 'application_id' => $this->application_id, ]; } From ab11832cf9305a37601a243d86d246fc84445414 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Mon, 30 Sep 2024 13:01:29 -0400 Subject: [PATCH 8/8] =?UTF-8?q?=E2=9C=A8=20Soundboard=20Events=20and=20Han?= =?UTF-8?q?dlers=20(#1258)=20(#1259)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Discord/WebSockets/Event.php | 5 ++ .../Events/GuildSoundboardSoundCreate.php | 48 ++++++++++++++++ .../Events/GuildSoundboardSoundDelete.php | 44 ++++++++++++++ .../Events/GuildSoundboardSoundUpdate.php | 57 +++++++++++++++++++ .../Events/GuildSoundboardSoundsUpdate.php | 49 ++++++++++++++++ .../WebSockets/Events/SoundboardSounds.php | 49 ++++++++++++++++ src/Discord/WebSockets/Handlers.php | 6 ++ 7 files changed, 258 insertions(+) create mode 100644 src/Discord/WebSockets/Events/GuildSoundboardSoundCreate.php create mode 100644 src/Discord/WebSockets/Events/GuildSoundboardSoundDelete.php create mode 100644 src/Discord/WebSockets/Events/GuildSoundboardSoundUpdate.php create mode 100644 src/Discord/WebSockets/Events/GuildSoundboardSoundsUpdate.php create mode 100644 src/Discord/WebSockets/Events/SoundboardSounds.php diff --git a/src/Discord/WebSockets/Event.php b/src/Discord/WebSockets/Event.php index 23e57e91a..bdd537043 100644 --- a/src/Discord/WebSockets/Event.php +++ b/src/Discord/WebSockets/Event.php @@ -80,6 +80,11 @@ abstract class Event public const GUILD_AUDIT_LOG_ENTRY_CREATE = 'GUILD_AUDIT_LOG_ENTRY_CREATE'; + public const GUILD_SOUNDBOARD_SOUND_CREATE = 'GUILD_SOUNDBOARD_SOUND_CREATE'; + public const GUILD_SOUNDBOARD_SOUND_UPDATE = 'GUILD_SOUNDBOARD_SOUND_UPDATE'; + public const GUILD_SOUNDBOARD_SOUND_DELETE = 'GUILD_SOUNDBOARD_SOUND_DELETE'; + public const SOUNDBOARD_SOUNDS = 'SOUNDBOARD_SOUNDS'; + // Channel public const CHANNEL_CREATE = 'CHANNEL_CREATE'; public const CHANNEL_DELETE = 'CHANNEL_DELETE'; diff --git a/src/Discord/WebSockets/Events/GuildSoundboardSoundCreate.php b/src/Discord/WebSockets/Events/GuildSoundboardSoundCreate.php new file mode 100644 index 000000000..346747c0e --- /dev/null +++ b/src/Discord/WebSockets/Events/GuildSoundboardSoundCreate.php @@ -0,0 +1,48 @@ + + * + * 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\Guild\Sound; +use Discord\Repository\Guild\SoundRepository; +use Discord\WebSockets\Event; + +/** + * @link https://discord.com/developers/docs/topics/gateway-events#guild-soundboard-sound-create + * + * @since 10.0.0 + */ +class GuildSoundboardSoundCreate extends Event +{ + /** + * {@inheritDoc} + */ + public function handle($data) + { + /** @var Sound */ + $part = $this->factory->part(Sound::class, (array) $data, true); + + /** @var Guild|null */ + $guild = yield $this->discord->guilds->cacheGet($data->guild_id); + + if (! $guild instanceof Guild) { + return $part; + } + + /** @var SoundRepository */ + if ($repository = $guild->sounds) { + $repository->set($part->sound_id, $part); + } + + return $part; + } +} diff --git a/src/Discord/WebSockets/Events/GuildSoundboardSoundDelete.php b/src/Discord/WebSockets/Events/GuildSoundboardSoundDelete.php new file mode 100644 index 000000000..3926065da --- /dev/null +++ b/src/Discord/WebSockets/Events/GuildSoundboardSoundDelete.php @@ -0,0 +1,44 @@ + + * + * 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\Guild\Sound; +use Discord\WebSockets\Event; + +/** + * @link https://discord.com/developers/docs/topics/gateway-events#guild-soundboard-sound-delete + * + * @since 10.0.0 + */ +class GuildSoundboardSoundDelete extends Event +{ + /** + * {@inheritDoc} + */ + public function handle($data) + { + $part = null; + + /** @var ?Guild */ + if ($guild = yield $this->discord->guilds->cacheGet($data->guild_id)) { + /** @var ?Sound */ + $part = yield $guild->soundboard_sounds->cachePull($data->sound_id); + if ($part instanceof Sound) { + $part->fill((array) $data); + $part->created = false; + } + } + + return $part ?? $this->factory->part(Sound::class, (array) $data); + } +} diff --git a/src/Discord/WebSockets/Events/GuildSoundboardSoundUpdate.php b/src/Discord/WebSockets/Events/GuildSoundboardSoundUpdate.php new file mode 100644 index 000000000..9d0460266 --- /dev/null +++ b/src/Discord/WebSockets/Events/GuildSoundboardSoundUpdate.php @@ -0,0 +1,57 @@ + + * + * 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\Guild\Sound; +use Discord\WebSockets\Event; + +/** + * @link https://discord.com/developers/docs/topics/gateway-events#guild-soundboard-sound-update + * + * @since 10.0.0 + */ +class GuildSoundboardSoundUpdate extends Event +{ + /** + * {@inheritDoc} + */ + public function handle($data) + { + $newPart = $oldPart = null; + + /** @var Guild|null */ + $guild = yield $this->discord->guilds->cacheGet($data->guild_id); + if (! $guild instanceof Guild) { + /** @var Sound */ + $newPart = $this->factory->part(Sound::class, (array) $data, true); + return [$newPart, $oldPart]; + } + + /** @var ?Sound */ + $oldPart = yield $guild->sounds->cacheGet($data->sound_id); + if ($oldPart instanceof Sound) { + $newPart = clone $oldPart; + $newPart->fill((array) $data); + } else { + /** @var Sound */ + $newPart = $this->factory->part(Sound::class, (array) $data, true); + } + + /** @var SoundRepository */ + if ($repository = $guild->sounds) { + $repository->set($newPart->sound_id, $newPart); + } + + return [$newPart, $oldPart]; + } +} diff --git a/src/Discord/WebSockets/Events/GuildSoundboardSoundsUpdate.php b/src/Discord/WebSockets/Events/GuildSoundboardSoundsUpdate.php new file mode 100644 index 000000000..77448fa96 --- /dev/null +++ b/src/Discord/WebSockets/Events/GuildSoundboardSoundsUpdate.php @@ -0,0 +1,49 @@ + + * + * 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\Guild\Sound; +use Discord\Repository\Guild\SoundRepository; +use Discord\WebSockets\Event; + +/** + * @link https://discord.com/developers/docs/topics/gateway-events#guild-soundboard-sounds-update + * + * @since 10.0.0 + */ +class GuildSoundboardSoundsUpdate extends Event +{ + /** + * {@inheritDoc} + */ + public function handle($data) + { + /** @var Guild|null */ + $guild = yield $this->discord->guilds->cacheGet($data->guild_id); + + if (! $guild instanceof Guild) { + return null; + } + + /** @var SoundRepository */ + $repository = $guild->sounds; + + foreach ($data as $soundData) { + /** @var SoundboardSound */ + $part = $this->factory->part(Sound::class, (array) $soundData, true); + $repository->set($part->sound_id, $part); + } + + return $repository; + } +} diff --git a/src/Discord/WebSockets/Events/SoundboardSounds.php b/src/Discord/WebSockets/Events/SoundboardSounds.php new file mode 100644 index 000000000..997dda9c7 --- /dev/null +++ b/src/Discord/WebSockets/Events/SoundboardSounds.php @@ -0,0 +1,49 @@ + + * + * 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\Guild\Sound; +use Discord\Repository\Guild\SoundRepository; +use Discord\WebSockets\Event; + +/** + * @link https://discord.com/developers/docs/topics/gateway-events#soundboard-sounds + * + * @since 10.0.0 + */ +class SoundboardSounds extends Event +{ + /** + * {@inheritDoc} + */ + public function handle($data) + { + /** @var Guild|null */ + $guild = yield $this->discord->guilds->cacheGet($data->guild_id); + + if (! $guild instanceof Guild) { + return null; + } + + /** @var SoundRepository */ + $repository = $guild->sounds; + + foreach ($data->soundboard_sounds as $soundData) { + /** @var Sound */ + $part = $this->factory->part(Sound::class, (array) $soundData, true); + $repository->set($part->sound_id, $part); + } + + return $repository; + } +} diff --git a/src/Discord/WebSockets/Handlers.php b/src/Discord/WebSockets/Handlers.php index d551f1372..10b7e871e 100644 --- a/src/Discord/WebSockets/Handlers.php +++ b/src/Discord/WebSockets/Handlers.php @@ -118,6 +118,12 @@ public function __construct() $this->addHandler(Event::AUTO_MODERATION_RULE_DELETE, \Discord\WebSockets\Events\AutoModerationRuleDelete::class); $this->addHandler(Event::AUTO_MODERATION_ACTION_EXECUTION, \Discord\WebSockets\Events\AutoModerationActionExecution::class); + // Soundboard Event Handlers + $this->addHandler(Event::GUILD_SOUNDBOARD_SOUND_CREATE, \Discord\WebSockets\Events\GuildSoundboardSoundCreate::class); + $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);