diff --git a/src/Component/Button/PremiumButton.php b/src/Component/Button/PremiumButton.php new file mode 100644 index 00000000..44f9b311 --- /dev/null +++ b/src/Component/Button/PremiumButton.php @@ -0,0 +1,32 @@ + 2, + 'style' => $this->style, + 'sku_id' => $this->skuId, + 'disabled' => $this->disabled, + ]; + + return $data; + } +} diff --git a/src/Enums/ActionType.php b/src/Enums/ActionType.php index 6521bdff..d3f22d88 100644 --- a/src/Enums/ActionType.php +++ b/src/Enums/ActionType.php @@ -9,4 +9,5 @@ enum ActionType: int case BLOCK_MESSAGE = 1; case SENT_ALERT_MESSAGE = 2; case TIMEOUT = 3; + case BLOCK_MEMBER_INTERACTION = 4; } diff --git a/src/Enums/ApplicationIntegrationType.php b/src/Enums/ApplicationIntegrationType.php new file mode 100644 index 00000000..fe86ac27 --- /dev/null +++ b/src/Enums/ApplicationIntegrationType.php @@ -0,0 +1,11 @@ +contexts instead + */ public ?bool $dm_permission; public ?bool $default_permission; public ?bool $nsfw; public string $version; + /** + * @var ApplicationIntegrationType[] + */ + #[ArrayMapping(ApplicationIntegrationType::class)] + public ?array $integration_types; + /** + * @var InteractionContextType[] + */ + #[ArrayMapping(InteractionContextType::class)] + public ?array $contexts; } diff --git a/src/Parts/ApplicationIntegrationType.php b/src/Parts/ApplicationIntegrationType.php new file mode 100644 index 00000000..4935c382 --- /dev/null +++ b/src/Parts/ApplicationIntegrationType.php @@ -0,0 +1,10 @@ +interaction_metadata instead + */ public ?MessageInteraction $interaction; public ?Channel $thread; /** diff --git a/src/Parts/MessageInteraction.php b/src/Parts/MessageInteraction.php index 250c6dc5..290958db 100644 --- a/src/Parts/MessageInteraction.php +++ b/src/Parts/MessageInteraction.php @@ -4,14 +4,22 @@ namespace Ragnarok\Fenrir\Parts; -use Ragnarok\Fenrir\Attributes\Partial; use Ragnarok\Fenrir\Enums\InteractionType; +/** + * @see https://discord.com/developers/docs/resources/message#message-interaction-metadata-object-message-interaction-metadata-structure + */ class MessageInteraction { public string $id; public InteractionType $type; - public string $name; public User $user; - public ?GuildMember $member; + /** + * @var string[] + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-authorizing-integration-owners-object + */ + public array $authorizing_integration_owners; + public ?string $original_response_message_id; + public ?string $interacted_message_id; + public ?self $triggering_interaction_metadata; } diff --git a/src/Parts/MessageInteractionMetadata.php b/src/Parts/MessageInteractionMetadata.php new file mode 100644 index 00000000..bc8ce17c --- /dev/null +++ b/src/Parts/MessageInteractionMetadata.php @@ -0,0 +1,16 @@ +getAuditLogReasonHeader($reason) )->otherwise($this->logThrowable(...)); } + + /** + * @see https://discord.com/developers/docs/resources/emoji#list-application-emojis + * + * @return ExtendedPromiseInterface<\Ragnarok\Fenrir\Parts\Emoji[]> + */ + public function listApplicationEmojis(string $applicationId): ExtendedPromiseInterface + { + return $this->mapArrayPromise( + $this->http->get( + Endpoint::bind( + 'applications/:application/emojis', + $applicationId + ), + ), + PartsEmoji::class + )->otherwise($this->logThrowable(...)); + } + + /** + * @see https://discord.com/developers/docs/resources/emoji#get-application-emoji + * + * @return ExtendedPromiseInterface<\Ragnarok\Fenrir\Parts\Emoji> + */ + public function getApplicationEmoji(string $guildId, string $emojiId): ExtendedPromiseInterface + { + return $this->mapPromise( + $this->http->get( + Endpoint::bind( + 'applications/:application/emojis/:emoji', + $guildId, + $emojiId + ) + ), + PartsEmoji::class + )->otherwise($this->logThrowable(...)); + } + + /** + * @see https://discord.com/developers/docs/resources/emoji#create-application-emoji + * + * @return ExtendedPromiseInterface<\Ragnarok\Fenrir\Parts\Emoji> + */ + public function createApplicationEmoji( + string $applicationId, + CreateEmojiBuilder $emojiBuilder, + ): ExtendedPromiseInterface { + return $this->mapPromise( + $this->http->post( + Endpoint::bind( + 'applications/:application/emojis', + $applicationId + ), + $emojiBuilder->get(), + ), + PartsEmoji::class + )->otherwise($this->logThrowable(...)); + } + + /** + * @see https://discord.com/developers/docs/resources/emoji#modify-application-emoji + * + * @return ExtendedPromiseInterface<\Ragnarok\Fenrir\Parts\Emoji> + */ + public function modifyApplicationEmoji( + string $applicationId, + string $emojiId, + CreateEmojiBuilder $emojiBuilder, + ): ExtendedPromiseInterface { + return $this->mapPromise( + $this->http->patch( + Endpoint::bind( + 'applications/:application/emojis/:emoji', + $applicationId, + $emojiId + ), + $emojiBuilder->get(), + ), + PartsEmoji::class + )->otherwise($this->logThrowable(...)); + } + + /** + * @see https://discord.com/developers/docs/resources/emoji#delete-application-emoji + * + * @return ExtendedPromiseInterface + */ + public function deleteApplicationEmoji( + string $applicationId, + string $emojiId, + ): ExtendedPromiseInterface { + return $this->http->delete( + Endpoint::bind( + 'applications/:application/emojis/:emoji', + $applicationId, + $emojiId + ), + )->otherwise($this->logThrowable(...)); + } } diff --git a/src/Rest/Guild.php b/src/Rest/Guild.php index 09198734..0d3d8eb0 100644 --- a/src/Rest/Guild.php +++ b/src/Rest/Guild.php @@ -17,6 +17,7 @@ use Ragnarok\Fenrir\Parts\PruneCount; use Ragnarok\Fenrir\Parts\Role; use Ragnarok\Fenrir\Parts\VoiceRegion; +use Ragnarok\Fenrir\Parts\VoiceState; use Ragnarok\Fenrir\Parts\WelcomeScreen; use Ragnarok\Fenrir\Parts\Widget; use Ragnarok\Fenrir\Parts\WidgetSettings; @@ -28,6 +29,8 @@ * * @SuppressWarnings(PHPMD.TooManyMethods) * @SuppressWarnings(PHPMD.TooManyPublicMethods) + * @SuppressWarnings(PHPMD.ExcessivePublicCount) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class Guild extends HttpResource { @@ -486,6 +489,25 @@ public function getRoles(string $guildId): ExtendedPromiseInterface )->otherwise($this->logThrowable(...)); } + /** + * @see https://discord.com/developers/docs/resources/guild#get-guild-role + * + * @return ExtendedPromiseInterface<\Ragnarok\Fenrir\Parts\Role> + */ + public function getRole(string $guildId, string $roleId): ExtendedPromiseInterface + { + return $this->mapPromise( + $this->http->get( + Endpoint::bind( + Endpoint::GUILD_ROLE, + $guildId, + $roleId + ), + ), + Role::class, + )->otherwise($this->logThrowable(...)); + } + /** * @see https://discord.com/developers/docs/resources/guild#create-guild-role * @@ -653,6 +675,43 @@ public function getVoiceRegions(string $guildId): ExtendedPromiseInterface )->otherwise($this->logThrowable(...)); } + /** + * @see https://discord.com/developers/docs/resources/voice#get-current-user-voice-state + * + * @return ExtendedPromiseInterface<\Ragnarok\Fenrir\Parts\VoiceState> + */ + public function getCurrentUserVoiceState(string $guildId): ExtendedPromiseInterface + { + return $this->mapPromise( + $this->http->get( + Endpoint::bind( + '/guilds/:guild/voice-states/@me', + $guildId, + ), + ), + VoiceState::class + )->otherwise($this->logThrowable(...)); + } + + /** + * @see https://discord.com/developers/docs/resources/voice#get-user-voice-state + * + * @return ExtendedPromiseInterface<\Ragnarok\Fenrir\Parts\VoiceState> + */ + public function getUserVoiceState(string $guildId, string $userId): ExtendedPromiseInterface + { + return $this->mapPromise( + $this->http->get( + Endpoint::bind( + '/guilds/:guild/voice-states/:user', + $guildId, + $userId, + ), + ), + VoiceState::class + )->otherwise($this->logThrowable(...)); + } + /** * @see https://discord.com/developers/docs/resources/guild#get-guild-invites * diff --git a/src/Rest/Helpers/Command/CommandBuilder.php b/src/Rest/Helpers/Command/CommandBuilder.php index 894345bf..8af20591 100644 --- a/src/Rest/Helpers/Command/CommandBuilder.php +++ b/src/Rest/Helpers/Command/CommandBuilder.php @@ -7,6 +7,8 @@ use Ragnarok\Fenrir\Bitwise\Bitwise; use Ragnarok\Fenrir\Constants\Validation\Command; use Ragnarok\Fenrir\Enums\ApplicationCommandTypes; +use Ragnarok\Fenrir\Enums\ApplicationIntegrationType; +use Ragnarok\Fenrir\Enums\InteractionContextType; use Ragnarok\Fenrir\Exceptions\Rest\Helpers\Command\InvalidCommandNameException; use Ragnarok\Fenrir\Rest\Helpers\GetNew; use Spatie\Regex\Regex; @@ -17,6 +19,16 @@ class CommandBuilder private array $data = []; + /** + * @var ApplicationIntegrationType[] + */ + private array $integrationTypes; + + /** + * @var InteractionContextType[] + */ + private array $contextTypes; + /** @var ?CommandOptionBuilder[] */ private array $commandOptionBuilders; @@ -176,6 +188,30 @@ public function getNsfw(): ?bool return $this->data['nsfw'] ?? null; } + public function setIntegrationTypes(ApplicationIntegrationType ...$integrationTypes): self + { + $this->integrationTypes = $integrationTypes; + + return $this; + } + + public function getIntegrationTypes(): ?array + { + return $this->integrationTypes ?? null; + } + + public function setContexts(InteractionContextType ...$contextTypes): self + { + $this->contextTypes = $contextTypes; + + return $this; + } + + public function getContexts(): ?array + { + return $this->contextTypes ?? null; + } + private function isAllowedName($name): bool { return Regex::match(Command::NAME_REGEX, $name)->hasMatch(); @@ -192,6 +228,20 @@ public function get(): array ); } + if (isset($this->integrationTypes)) { + $data['integration_types'] = array_map( + fn (ApplicationIntegrationType $integrationType) => $integrationType->value, + $this->integrationTypes + ); + } + + if (isset($this->contextTypes)) { + $data['contexts'] = array_map( + fn (InteractionContextType $contextType) => $contextType->value, + $this->contextTypes + ); + } + return $data; } } diff --git a/tests/Component/ButtonTest.php b/tests/Component/ButtonTest.php index 76977251..702c7c08 100644 --- a/tests/Component/ButtonTest.php +++ b/tests/Component/ButtonTest.php @@ -7,6 +7,7 @@ use PHPUnit\Framework\TestCase; use Ragnarok\Fenrir\Component\Button\DangerButton; use Ragnarok\Fenrir\Component\Button\LinkButton; +use Ragnarok\Fenrir\Component\Button\PremiumButton; use Ragnarok\Fenrir\Component\Button\PrimaryButton; use Ragnarok\Fenrir\Component\Button\SecondaryButton; use Ragnarok\Fenrir\Component\Button\SuccessButton; @@ -186,4 +187,43 @@ public static function convertionExpectationProviderLinkButton(): array ], ]; } + + /** + * @dataProvider convertionExpectationProviderPremiumButton + */ + public function testCorrectlyConvertedPremiumButton(array $args, array $expected): void + { + $button = new PremiumButton(...$args); + + $this->assertEquals($expected, $button->get()); + } + + public static function convertionExpectationProviderPremiumButton(): array + { + return [ + 'Completely filled out' => [ + 'args' => [ + '::sku::', + true, + ], + 'expected' => [ + 'type' => 2, + 'style' => ButtonStyle::Premium, + 'sku_id' => '::sku::', + 'disabled' => true + ], + ], + 'Missing disabled' => [ + 'args' => [ + '::sku::', + ], + 'expected' => [ + 'type' => 2, + 'style' => ButtonStyle::Premium, + 'sku_id' => '::sku::', + 'disabled' => false + ], + ], + ]; + } } diff --git a/tests/Rest/EmojiTest.php b/tests/Rest/EmojiTest.php index edb395ba..b803238c 100644 --- a/tests/Rest/EmojiTest.php +++ b/tests/Rest/EmojiTest.php @@ -70,6 +70,61 @@ public static function httpBindingsProvider(): array ], 'validationOptions' => [] ], + + 'List application emojis' => [ + 'method' => 'listApplicationEmojis', + 'args' => ['::application id::'], + 'mockOptions' => [ + 'method' => 'get', + 'return' => [(object) [], (object) [], (object) []], + ], + 'validationOptions' => [ + 'returnType' => PartsEmoji::class, + 'array' => true, + ] + ], + 'Get application emoji' => [ + 'method' => 'getApplicationEmoji', + 'args' => ['::application id::', '::emoji id::'], + 'mockOptions' => [ + 'method' => 'get', + 'return' => (object) [], + ], + 'validationOptions' => [ + 'returnType' => PartsEmoji::class, + ] + ], + 'Create application emoji' => [ + 'method' => 'createApplicationEmoji', + 'args' => ['::application id::', new CreateEmojiBuilder()], + 'mockOptions' => [ + 'method' => 'post', + 'return' => (object) [], + ], + 'validationOptions' => [ + 'returnType' => PartsEmoji::class, + ] + ], + 'Modify application emoji' => [ + 'method' => 'modifyApplicationEmoji', + 'args' => ['::application id::', '::emoji id::', new CreateEmojiBuilder()], + 'mockOptions' => [ + 'method' => 'patch', + 'return' => (object) [], + ], + 'validationOptions' => [ + 'returnType' => PartsEmoji::class, + ] + ], + 'Delete application emoji' => [ + 'method' => 'deleteApplicationEmoji', + 'args' => ['::application id::', '::emoji id::'], + 'mockOptions' => [ + 'method' => 'delete', + 'return' => null, + ], + 'validationOptions' => [] + ], ]; } } diff --git a/tests/Rest/Helpers/Command/CommandBuilderTest.php b/tests/Rest/Helpers/Command/CommandBuilderTest.php index 608beae8..9554d9d5 100644 --- a/tests/Rest/Helpers/Command/CommandBuilderTest.php +++ b/tests/Rest/Helpers/Command/CommandBuilderTest.php @@ -8,6 +8,8 @@ use Ragnarok\Fenrir\Bitwise\Bitwise; use Ragnarok\Fenrir\Enums\ApplicationCommandOptionType; use Ragnarok\Fenrir\Enums\ApplicationCommandTypes; +use Ragnarok\Fenrir\Enums\ApplicationIntegrationType; +use Ragnarok\Fenrir\Enums\InteractionContextType; use Ragnarok\Fenrir\Exceptions\Rest\Helpers\Command\InvalidCommandNameException; use Ragnarok\Fenrir\Rest\Helpers\Command\CommandBuilder; use Ragnarok\Fenrir\Rest\Helpers\Command\CommandOptionBuilder; @@ -118,4 +120,22 @@ public function testSetNsfw(): void $this->assertTrue($commandBuilder->getNsfw()); $this->assertTrue($commandBuilder->get()['nsfw']); } + + public function testSetIntegrationTypes() + { + $types = [ApplicationIntegrationType::GUILD_INSTALL, ApplicationIntegrationType::USER_INSTALL]; + $commandBuilder = new CommandBuilder(); + $commandBuilder->setIntegrationTypes(...$types); + $this->assertEquals($types, $commandBuilder->getIntegrationTypes()); + $this->assertEquals([0, 1], $commandBuilder->get()['integration_types']); + } + + public function testSetContexts() + { + $types = [InteractionContextType::GUILD, InteractionContextType::PRIVATE_CHANNEL]; + $commandBuilder = new CommandBuilder(); + $commandBuilder->setContexts(...$types); + $this->assertEquals($types, $commandBuilder->getContexts()); + $this->assertEquals([0, 2], $commandBuilder->get()['contexts']); + } }