From b5673820feceba976377b466c9a595e2be746b66 Mon Sep 17 00:00:00 2001 From: SQKo <87897282+SQKo@users.noreply.github.com> Date: Fri, 16 Sep 2022 19:03:17 +0700 Subject: [PATCH 01/13] fix and add missing phpdoc for exceptions --- src/Discord/Parts/Channel/Channel.php | 2 ++ src/Discord/Parts/Channel/Invite.php | 8 ++++---- src/Discord/Parts/Channel/Message.php | 4 ++++ src/Discord/Parts/Channel/Overwrite.php | 2 -- src/Discord/Parts/Embed/Embed.php | 2 ++ src/Discord/Parts/Guild/Guild.php | 4 ++-- src/Discord/Parts/Guild/Integration.php | 4 ++-- src/Discord/Parts/Guild/Role.php | 2 -- src/Discord/Parts/Guild/ScheduledEvent.php | 8 ++++---- src/Discord/Parts/Thread/Member.php | 4 ++-- src/Discord/Parts/Thread/Thread.php | 6 ++++++ src/Discord/Parts/User/Activity.php | 2 ++ src/Discord/Parts/User/Member.php | 8 ++++++-- src/Discord/Parts/User/User.php | 2 +- src/Discord/Parts/WebSockets/TypingStart.php | 4 ++-- src/Discord/Parts/WebSockets/VoiceStateUpdate.php | 2 ++ 16 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/Discord/Parts/Channel/Channel.php b/src/Discord/Parts/Channel/Channel.php index 0d12e3c24..41dbbbcb0 100644 --- a/src/Discord/Parts/Channel/Channel.php +++ b/src/Discord/Parts/Channel/Channel.php @@ -261,6 +261,8 @@ protected function getGuildAttribute(): ?Guild * Gets the last pinned message timestamp. * * @return Carbon|null + * + * @throws \Exception */ protected function getLastPinTimestampAttribute(): ?Carbon { diff --git a/src/Discord/Parts/Channel/Invite.php b/src/Discord/Parts/Channel/Invite.php index a8c945c5c..82610d99a 100644 --- a/src/Discord/Parts/Channel/Invite.php +++ b/src/Discord/Parts/Channel/Invite.php @@ -178,8 +178,6 @@ protected function getChannelIdAttribute(): ?string /** * Returns the inviter attribute. * - * @throws \Exception - * * @return User|null The User that invited you. */ protected function getInviterAttribute(): ?User @@ -230,9 +228,9 @@ protected function getTargetApplicationAttribute(): ?Application /** * Returns the expires at attribute. * - * @throws \Exception - * * @return Carbon|null The time that the invite was created. + * + * @throws \Exception */ protected function getExpiresAtAttribute(): ?Carbon { @@ -267,6 +265,8 @@ protected function getGuildScheduledEventAttribute(): ?ScheduledEvent * Returns the created at attribute. * * @return Carbon|null The time that the invite was created. + * + * @throws \Exception */ protected function getCreatedAtAttribute(): ?Carbon { diff --git a/src/Discord/Parts/Channel/Message.php b/src/Discord/Parts/Channel/Message.php index ad5870517..e7719a021 100644 --- a/src/Discord/Parts/Channel/Message.php +++ b/src/Discord/Parts/Channel/Message.php @@ -603,6 +603,8 @@ protected function getReferencedMessageAttribute(): ?Message * Returns the timestamp attribute. * * @return Carbon|null The time that the message was sent. + * + * @throws \Exception */ protected function getTimestampAttribute(): ?Carbon { @@ -617,6 +619,8 @@ protected function getTimestampAttribute(): ?Carbon * Returns the edited_timestamp attribute. * * @return Carbon|null The time that the message was edited. + * + * @throws \Exception */ protected function getEditedTimestampAttribute(): ?Carbon { diff --git a/src/Discord/Parts/Channel/Overwrite.php b/src/Discord/Parts/Channel/Overwrite.php index abd4bbd4a..d428ca5c3 100644 --- a/src/Discord/Parts/Channel/Overwrite.php +++ b/src/Discord/Parts/Channel/Overwrite.php @@ -50,7 +50,6 @@ class Overwrite extends Part * Sets the allow attribute of the role. * * @param ChannelPermission|int $allow - * @throws \Exception */ protected function setAllowAttribute($allow): void { @@ -65,7 +64,6 @@ protected function setAllowAttribute($allow): void * Sets the deny attribute of the role. * * @param ChannelPermission|int $deny - * @throws \Exception */ protected function setDenyAttribute($deny): void { diff --git a/src/Discord/Parts/Embed/Embed.php b/src/Discord/Parts/Embed/Embed.php index e5c9fbcaa..8c80d1a28 100644 --- a/src/Discord/Parts/Embed/Embed.php +++ b/src/Discord/Parts/Embed/Embed.php @@ -70,6 +70,8 @@ class Embed extends Part * Gets the timestamp attribute. * * @return Carbon|null The timestamp attribute. + * + * @throws \Exception */ protected function getTimestampAttribute(): ?Carbon { diff --git a/src/Discord/Parts/Guild/Guild.php b/src/Discord/Parts/Guild/Guild.php index 3776992b7..2aa2a4f0a 100644 --- a/src/Discord/Parts/Guild/Guild.php +++ b/src/Discord/Parts/Guild/Guild.php @@ -371,9 +371,9 @@ protected function getOwnerAttribute(): ?User /** * Returns the joined_at attribute. * - * @throws \Exception - * * @return Carbon|null The joined_at attribute. + * + * @throws \Exception */ protected function getJoinedAtAttribute(): ?Carbon { diff --git a/src/Discord/Parts/Guild/Integration.php b/src/Discord/Parts/Guild/Integration.php index d31a0cec0..607d61ff4 100644 --- a/src/Discord/Parts/Guild/Integration.php +++ b/src/Discord/Parts/Guild/Integration.php @@ -92,9 +92,9 @@ protected function getUserAttribute(): ?User /** * Returns the synced_at attribute. * - * @throws \Exception - * * @return Carbon|null The synced_at attribute. + * + * @throws \Exception */ protected function getSyncedAtAttribute(): ?Carbon { diff --git a/src/Discord/Parts/Guild/Role.php b/src/Discord/Parts/Guild/Role.php index bdbaa307c..b384f33b5 100644 --- a/src/Discord/Parts/Guild/Role.php +++ b/src/Discord/Parts/Guild/Role.php @@ -63,8 +63,6 @@ class Role extends Part * Sets the permissions attribute. * * @param RolePermission|int $permission The permissions to set. - * - * @throws \Exception */ protected function setPermissionsAttribute($permission): void { diff --git a/src/Discord/Parts/Guild/ScheduledEvent.php b/src/Discord/Parts/Guild/ScheduledEvent.php index 8ce0ade36..acefedcdf 100644 --- a/src/Discord/Parts/Guild/ScheduledEvent.php +++ b/src/Discord/Parts/Guild/ScheduledEvent.php @@ -202,9 +202,9 @@ protected function getImageHashAttribute(): ?string /** * Returns the created at attribute. * - * @throws \Exception - * * @return Carbon The time the scheduled event will start. + * + * @throws \Exception */ protected function getScheduledStartTimeAttribute(): Carbon { @@ -214,9 +214,9 @@ protected function getScheduledStartTimeAttribute(): Carbon /** * Returns the created at attribute. * - * @throws \Exception - * * @return Carbon|null The time the scheduled event will end, required if entity_type is EXTERNAL. + * + * @throws \Exception */ protected function getScheduledEndTimeAttribute(): ?Carbon { diff --git a/src/Discord/Parts/Thread/Member.php b/src/Discord/Parts/Thread/Member.php index ea0c79818..411fb5a7c 100644 --- a/src/Discord/Parts/Thread/Member.php +++ b/src/Discord/Parts/Thread/Member.php @@ -55,9 +55,9 @@ protected function getUserAttribute(): ?User /** * Returns the time that the member joined the thread. * - * @throws \Exception - * * @return Carbon + * + * @throws \Exception */ protected function getJoinTimestampAttribute(): Carbon { diff --git a/src/Discord/Parts/Thread/Thread.php b/src/Discord/Parts/Thread/Thread.php index 582337c2a..7563fe7d6 100644 --- a/src/Discord/Parts/Thread/Thread.php +++ b/src/Discord/Parts/Thread/Thread.php @@ -175,6 +175,8 @@ protected function getParentAttribute(): ?Channel * Returns the timestamp when the last message was pinned in the thread. * * @return Carbon|null + * + * @throws \Exception */ protected function getLastPinTimestampAttribute(): ?Carbon { @@ -263,6 +265,8 @@ protected function setAutoArchiveDurationAttribute(int $value) * also mean the time when the thread was created, archived, unarchived etc. * * @return Carbon + * + * @throws \Exception */ protected function getArchiveTimestampAttribute(): Carbon { @@ -273,6 +277,8 @@ protected function getArchiveTimestampAttribute(): Carbon * Returns the timestamp when the thread was created; only populated for threads created after 2022-01-09. * * @return Carbon|null + * + * @throws \Exception */ protected function getCreateTimestampAttribute(): ?Carbon { diff --git a/src/Discord/Parts/User/Activity.php b/src/Discord/Parts/User/Activity.php index a893961aa..b5ba3cf43 100644 --- a/src/Discord/Parts/User/Activity.php +++ b/src/Discord/Parts/User/Activity.php @@ -91,6 +91,8 @@ class Activity extends Part * Gets the created at timestamp. * * @return Carbon|null + * + * @throws \Exception */ protected function getCreatedAtAttribute(): ?Carbon { diff --git a/src/Discord/Parts/User/Member.php b/src/Discord/Parts/User/Member.php index 39dbb89fb..d8f51e13d 100644 --- a/src/Discord/Parts/User/Member.php +++ b/src/Discord/Parts/User/Member.php @@ -588,9 +588,9 @@ protected function getRolesAttribute(): Collection /** * Returns the joined at attribute. * - * @throws \Exception - * * @return Carbon|null The timestamp from when the member joined. + * + * @throws \Exception */ protected function getJoinedAtAttribute(): ?Carbon { @@ -644,6 +644,8 @@ protected function getAvatarHashAttribute(): ?string * Returns the premium since attribute. * * @return Carbon|null + * + * @throws \Exception */ protected function getPremiumSinceAttribute(): ?Carbon { @@ -675,6 +677,8 @@ protected function getPermissionsAttribute(): ?RolePermission * Returns the communication disabled until attribute. * * @return Carbon|null + * + * @throws \Exception */ protected function getCommunicationDisabledUntilAttribute(): ?Carbon { diff --git a/src/Discord/Parts/User/User.php b/src/Discord/Parts/User/User.php index 608a4e716..1a0fdc85d 100644 --- a/src/Discord/Parts/User/User.php +++ b/src/Discord/Parts/User/User.php @@ -140,7 +140,7 @@ public function sendMessage($message, bool $tts = false, $embed = null, $allowed * * @link https://discord.com/developers/docs/resources/channel#trigger-typing-indicator * - * @throws \Exception + * @throws \RuntimeException * * @return ExtendedPromiseInterface */ diff --git a/src/Discord/Parts/WebSockets/TypingStart.php b/src/Discord/Parts/WebSockets/TypingStart.php index 4b2bc8688..3423bd23d 100644 --- a/src/Discord/Parts/WebSockets/TypingStart.php +++ b/src/Discord/Parts/WebSockets/TypingStart.php @@ -106,9 +106,9 @@ protected function getUserAttribute(): ?User /** * Gets the timestamp attribute. * - * @throws \Exception - * * @return Carbon The time that the user started typing. + * + * @throws \Exception */ protected function getTimestampAttribute(): Carbon { diff --git a/src/Discord/Parts/WebSockets/VoiceStateUpdate.php b/src/Discord/Parts/WebSockets/VoiceStateUpdate.php index 08c53cff4..d31159fd9 100644 --- a/src/Discord/Parts/WebSockets/VoiceStateUpdate.php +++ b/src/Discord/Parts/WebSockets/VoiceStateUpdate.php @@ -133,6 +133,8 @@ protected function getMemberAttribute(): ?Member * Gets the request_to_speak_timestamp attribute. * * @return Carbon|null + * + * @throws \Exception */ protected function getRequestToSpeakTimestampAttribute(): ?Carbon { From 33a2fe619e9be4d43744ec67e2d4934fe128c184 Mon Sep 17 00:00:00 2001 From: SQKo <87897282+SQKo@users.noreply.github.com> Date: Fri, 16 Sep 2022 19:04:10 +0700 Subject: [PATCH 02/13] allow components only message --- src/Discord/Builders/MessageBuilder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Discord/Builders/MessageBuilder.php b/src/Discord/Builders/MessageBuilder.php index 4a7e951f4..5bf52c78f 100644 --- a/src/Discord/Builders/MessageBuilder.php +++ b/src/Discord/Builders/MessageBuilder.php @@ -662,6 +662,7 @@ public function jsonSerialize(): array if (isset($this->components)) { $body['components'] = $this->components; + $empty = false; } if ($this->sticker_ids) { From 664a5db3588b2c1d7048af223cf3cbc5f1f3f828 Mon Sep 17 00:00:00 2001 From: SQKo <87897282+SQKo@users.noreply.github.com> Date: Fri, 16 Sep 2022 19:34:28 +0700 Subject: [PATCH 03/13] Add pre-check for non deletable messages --- src/Discord/Parts/Channel/Message.php | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/Discord/Parts/Channel/Message.php b/src/Discord/Parts/Channel/Message.php index e7719a021..8c4cee217 100644 --- a/src/Discord/Parts/Channel/Message.php +++ b/src/Discord/Parts/Channel/Message.php @@ -804,7 +804,7 @@ public function delayedDelete(int $delay, &$timer = null): ExtendedPromiseInterf $deferred = new Deferred(); $timer = $this->discord->getLoop()->addTimer($delay / 1000, function () use ($deferred) { - $this->delete([$deferred, 'resolve'], [$deferred, 'reject']); + $this->delete()->done([$deferred, 'resolve'], [$deferred, 'reject']); }); return $deferred->promise(); @@ -905,9 +905,15 @@ private function _edit(MessageBuilder $message): ExtendedPromiseInterface * @link https://discord.com/developers/docs/resources/channel#delete-message * * @return ExtendedPromiseInterface + * + * @throws \RuntimeException This type of message cannot be deleted. */ public function delete(): ExtendedPromiseInterface { + if (! $this->isDeletable()) { + return reject(new \RuntimeException("Cannot delete this type of message: {$this->type}", 50021)); + } + return $this->http->delete(Endpoint::bind(Endpoint::CHANNEL_MESSAGE, $this->channel_id, $this->id)); } @@ -977,6 +983,22 @@ public function addEmbed(Embed $embed): ExtendedPromiseInterface ->addEmbed($embed)); } + public function isDeletable(): bool + { + return ! in_array($this->type, [ + self::TYPE_RECIPIENT_ADD, + self::TYPE_RECIPIENT_REMOVE, + self::TYPE_CALL, + self::TYPE_CHANNEL_NAME_CHANGE, + self::TYPE_CHANNEL_ICON_CHANGE, + self::TYPE_GUILD_DISCOVERY_DISQUALIFIED, + self::TYPE_GUILD_DISCOVERY_REQUALIFIED, + self::TYPE_GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING, + self::TYPE_GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING, + self::TYPE_THREAD_STARTER_MESSAGE, + ]); + } + /** * @inheritDoc * From 618d4544a48e1675d2fa4f11add9cae4787b525c Mon Sep 17 00:00:00 2001 From: SQKo <87897282+SQKo@users.noreply.github.com> Date: Fri, 16 Sep 2022 19:50:32 +0700 Subject: [PATCH 04/13] BC Guild_Create event allow unavailable listener --- docs/src/pages/api/03_events/04_guilds.md | 13 ++++++++++-- src/Discord/Discord.php | 20 +++++++++---------- src/Discord/WebSockets/Events/GuildCreate.php | 5 ++--- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/docs/src/pages/api/03_events/04_guilds.md b/docs/src/pages/api/03_events/04_guilds.md index 54dd015e0..4c5d180d7 100644 --- a/docs/src/pages/api/03_events/04_guilds.md +++ b/docs/src/pages/api/03_events/04_guilds.md @@ -13,8 +13,17 @@ Called with a `Guild` object in one of the following situations: 3. When the Bot joins a new guild. ```php -$discord->on(Event::GUILD_CREATE, function (Guild $guild, Discord $discord) { - // ... +$discord->on(Event::GUILD_CREATE, function ($guild, Discord $discord) { + if (! ($guild instanceof Guild)) { + // the guild is unavailable due to an outage, $guild is a stdClass + // { + // "id": "", + // "unavailable": true, + // } + return; + } + + // the Bot has joined the guild }); ``` diff --git a/src/Discord/Discord.php b/src/Discord/Discord.php index 3014c3152..48e80f212 100644 --- a/src/Discord/Discord.php +++ b/src/Discord/Discord.php @@ -473,11 +473,9 @@ protected function handleReady(object $data) /** @var ExtendedPromiseInterface */ $promise = coroutine([$event, 'handle'], $guild); - $promise->done(null, function ($d) use (&$unavailable) { - list($status, $data) = $d; - - if ($status == 'unavailable') { - $unavailable[$data] = $data; + $promise->done(function ($d) use (&$unavailable) { + if (! empty($d->unavailable)) { + $unavailable[$d->id] = $d->unavailable; } }); } @@ -496,8 +494,10 @@ protected function handleReady(object $data) $guildLoad = new Deferred(); $onGuildCreate = function ($guild) use (&$unavailable, $guildLoad) { - $this->logger->debug('guild available', ['guild' => $guild->id, 'unavailable' => count($unavailable)]); - unset($unavailable[$guild->id]); + if (empty($guild->unavailable)) { + $this->logger->debug('guild available', ['guild' => $guild->id, 'unavailable' => count($unavailable)]); + unset($unavailable[$guild->id]); + } if (count($unavailable) < 1) { $guildLoad->resolve(); } @@ -508,9 +508,9 @@ protected function handleReady(object $data) if ($guild->unavailable) { $this->logger->debug('guild unavailable', ['guild' => $guild->id, 'unavailable' => count($unavailable)]); unset($unavailable[$guild->id]); - if (count($unavailable) < 1) { - $guildLoad->resolve(); - } + } + if (count($unavailable) < 1) { + $guildLoad->resolve(); } }; $this->on(Event::GUILD_DELETE, $onGuildDelete); diff --git a/src/Discord/WebSockets/Events/GuildCreate.php b/src/Discord/WebSockets/Events/GuildCreate.php index 010d95deb..58444a90b 100644 --- a/src/Discord/WebSockets/Events/GuildCreate.php +++ b/src/Discord/WebSockets/Events/GuildCreate.php @@ -25,7 +25,6 @@ use Discord\Parts\Thread\Thread; use function React\Promise\all; -use function React\Promise\reject; /** * @link https://discord.com/developers/docs/topics/gateway#guild-create @@ -39,8 +38,8 @@ class GuildCreate extends Event */ public function handle($data) { - if (isset($data->unavailable) && $data->unavailable) { - return reject(['unavailable', $data->id]); + if (! empty($data->unavailable)) { + return $data; } /** @var Guild */ From 2b7750d8ebb5dc22d8681e687767737691a22aa5 Mon Sep 17 00:00:00 2001 From: SQKo <87897282+SQKo@users.noreply.github.com> Date: Fri, 16 Sep 2022 19:53:29 +0700 Subject: [PATCH 05/13] add afk_timeout allowed time seconds phpdoc --- src/Discord/Parts/Guild/Guild.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord/Parts/Guild/Guild.php b/src/Discord/Parts/Guild/Guild.php index 2aa2a4f0a..65c563fb4 100644 --- a/src/Discord/Parts/Guild/Guild.php +++ b/src/Discord/Parts/Guild/Guild.php @@ -65,7 +65,7 @@ * @property-read User|null $owner The owner of the guild. * @property ?string|null $region The region the guild's voice channels are hosted in. * @property string $afk_channel_id The unique identifier of the AFK channel ID. - * @property int $afk_timeout How long you will remain in the voice channel until you are moved into the AFK channel. + * @property int $afk_timeout How long in seconds you will remain in the voice channel until you are moved into the AFK channel. Can be set to: 60, 300, 900, 1800, 3600. * @property bool|null $widget_enabled Is server widget enabled. * @property ?string|null $widget_channel_id Channel that the widget will create an invite to. * @property int $verification_level The verification level used for the guild. From 368abe604f136accad00cd4ec99c92764b620a90 Mon Sep 17 00:00:00 2001 From: SQKo <87897282+SQKo@users.noreply.github.com> Date: Fri, 16 Sep 2022 20:38:37 +0700 Subject: [PATCH 06/13] Add auto moderation audit log entry consts and optional options --- src/Discord/Parts/Guild/AuditLog/Entry.php | 2 ++ src/Discord/Parts/Guild/AuditLog/Options.php | 22 ++++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Discord/Parts/Guild/AuditLog/Entry.php b/src/Discord/Parts/Guild/AuditLog/Entry.php index 9c07b0618..e376ba2d1 100644 --- a/src/Discord/Parts/Guild/AuditLog/Entry.php +++ b/src/Discord/Parts/Guild/AuditLog/Entry.php @@ -86,6 +86,8 @@ class Entry extends Part public const AUTO_MODERATION_RULE_UPDATE = 141; public const AUTO_MODERATION_RULE_DELETE = 142; public const AUTO_MODERATION_BLOCK_MESSAGE = 143; + public const AUTO_MODERATION_FLAG_TO_CHANNEL = 144; + public const AUTO_MODERATION_USER_COMMUNICATION_DISABLED = 145; // AUDIT LOG ENTRY TYPES diff --git a/src/Discord/Parts/Guild/AuditLog/Options.php b/src/Discord/Parts/Guild/AuditLog/Options.php index cf621b97e..63696d8ba 100644 --- a/src/Discord/Parts/Guild/AuditLog/Options.php +++ b/src/Discord/Parts/Guild/AuditLog/Options.php @@ -21,15 +21,17 @@ * * @since 5.1.0 * - * @property string $application_id ID of the app whose permissions were targeted. - * @property string $channel_id Channel in which the entities were targeted. - * @property string $count Number of entities that were targeted. - * @property string $delete_member_days Number of days after which inactive members were kicked. - * @property string $id Id of the overwritten entity. - * @property string $members_removed Number of members removed by the prune. - * @property string $message_id Id of the message that was targeted. - * @property string $role_name Name of the role if type is "0" (not present if type is "1"). - * @property string $type Type of overwritten entity - "0" for "role" or "1" for "member". + * @property string $application_id ID of the app whose permissions were targeted. + * @property string $auto_moderation_rule_name Name of the Auto Moderation rule that was triggered. + * @property string $auto_moderation_rule_trigger_type Trigger type of the Auto Moderation rule that was triggered. + * @property string $channel_id Channel in which the entities were targeted. + * @property string $count Number of entities that were targeted. + * @property string $delete_member_days Number of days after which inactive members were kicked. + * @property string $id Id of the overwritten entity. + * @property string $members_removed Number of members removed by the prune. + * @property string $message_id Id of the message that was targeted. + * @property string $role_name Name of the role if type is "0" (not present if type is "1"). + * @property string $type Type of overwritten entity - "0" for "role" or "1" for "member". */ class Options extends Part { @@ -38,6 +40,8 @@ class Options extends Part */ protected $fillable = [ 'application_id', + 'auto_moderation_rule_name', + 'auto_moderation_rule_trigger_type', 'channel_id', 'count', 'delete_member_days', From a8d80935c815d48df2ea310fb29298503206942c Mon Sep 17 00:00:00 2001 From: SQKo <87897282+SQKo@users.noreply.github.com> Date: Fri, 16 Sep 2022 21:40:45 +0700 Subject: [PATCH 07/13] Add Guild::setFeatures --- src/Discord/Parts/Guild/Guild.php | 45 +++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/Discord/Parts/Guild/Guild.php b/src/Discord/Parts/Guild/Guild.php index 65c563fb4..556462575 100644 --- a/src/Discord/Parts/Guild/Guild.php +++ b/src/Discord/Parts/Guild/Guild.php @@ -1267,6 +1267,51 @@ public function updateMFALevel(int $level, ?string $reason = null): ExtendedProm }); } + /** + * Modify the guild feature. + * + * @link https://discord.com/developers/docs/resources/guild#modify-guild + * + * @param bool[] $features Array of features to set/unset, e.g. `['COMMUNITY' => true, 'INVITES_DISABLED' => false]`. + * + * @return ExtendedPromiseInterface This guild. + * + * @throws \OutOfRangeException Feature is not mutable. + * @throws \RuntimeException Guild feature is already set. + */ + public function setFeatures(array $features, ?string $reason = null): ExtendedPromiseInterface + { + $setFeatures = $this->features; + foreach ($features as $feature => $set) { + if (! in_array($feature, ['COMMUNITY', 'INVITES_DISABLED', 'DISCOVERABLE'])) { + return reject(new \OutOfRangeException("Guild feature {$feature} is not mutable")); + } + $featureIdx = array_search($feature, $setFeatures); + if ($set) { + if ($featureIdx !== false) { + return reject(new \RuntimeException("Guild feature {$feature} is already set")); + } + $setFeatures[] = $feature; + } else { + if ($featureIdx === false) { + return reject(new \RuntimeException("Guild feature {$feature} is already not set")); + } + unset($setFeatures[$featureIdx]); + } + } + + $headers = []; + if (isset($reason)) { + $headers['X-Audit-Log-Reason'] = $reason; + } + + return $this->http->patch(Endpoint::bind(Endpoint::GUILD, $this->id), ['features' => array_values($setFeatures)], $headers)->then(function ($response) { + $this->features = $response->features; + + return $this; + }); + } + /** * @inheritDoc * From 5da11f8a2a53678640db6aad11f7524e1cd7b2ec Mon Sep 17 00:00:00 2001 From: SQKo <87897282+SQKo@users.noreply.github.com> Date: Fri, 16 Sep 2022 22:11:37 +0700 Subject: [PATCH 08/13] mark event params as object that can be different instance --- docs/src/pages/api/03_events/04_guilds.md | 8 ++++---- docs/src/pages/api/03_events/05_invites.md | 2 +- docs/src/pages/api/03_events/07_messages.md | 2 +- docs/src/pages/api/03_events/11_webhooks.md | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/src/pages/api/03_events/04_guilds.md b/docs/src/pages/api/03_events/04_guilds.md index 4c5d180d7..372376905 100644 --- a/docs/src/pages/api/03_events/04_guilds.md +++ b/docs/src/pages/api/03_events/04_guilds.md @@ -8,12 +8,12 @@ Requires the `Intents::GUILDS` intent. Called with a `Guild` object in one of the following situations: -1. When the Bot is first starting and the guilds are becoming available. +1. When the Bot is first starting and the guilds are becoming available. (unless the listener is put inside after 'ready' event) 2. When a guild was unavailable and is now available due to an outage. 3. When the Bot joins a new guild. ```php -$discord->on(Event::GUILD_CREATE, function ($guild, Discord $discord) { +$discord->on(Event::GUILD_CREATE, function (object $guild, Discord $discord) { if (! ($guild instanceof Guild)) { // the guild is unavailable due to an outage, $guild is a stdClass // { @@ -137,7 +137,7 @@ $discord->on(Event::GUILD_MEMBER_REMOVE, function (Member $member, Discord $disc Called with two `Member` objects when a member is updated in a guild. Note that the old member _may_ be `null` if `loadAllMembers` is disabled. ```php -$discord->on(Event::GUILD_MEMBER_UPDATE, function (Member $member, Discord $discord, Member $oldMember) { +$discord->on(Event::GUILD_MEMBER_UPDATE, function (Member $member, Discord $discord, ?Member $oldMember) { // ... }); ``` @@ -171,7 +171,7 @@ $discord->on(Event::GUILD_ROLE_UPDATE, function (Role $role, Discord $discord, ? Called with a `Role` object when a role is deleted in a guild. `$role` may return `Role` object if it was cached. ```php -$discord->on(Event::GUILD_ROLE_DELETE, function ($role, Discord $discord) { +$discord->on(Event::GUILD_ROLE_DELETE, function (object $role, Discord $discord) { if ($role instanceof Role) { // Role is present in cache } diff --git a/docs/src/pages/api/03_events/05_invites.md b/docs/src/pages/api/03_events/05_invites.md index ef264f4ea..92b0ace93 100644 --- a/docs/src/pages/api/03_events/05_invites.md +++ b/docs/src/pages/api/03_events/05_invites.md @@ -19,7 +19,7 @@ $discord->on(Event::INVITE_CREATE, function (Invite $invite, Discord $discord) { Called with an object when an invite is created. ```php -$discord->on(Event::INVITE_DELETE, function ($invite, Discord $discord) { +$discord->on(Event::INVITE_DELETE, function (object $invite, Discord $discord) { if ($invite instanceof Invite) { // Invite is present in cache } diff --git a/docs/src/pages/api/03_events/07_messages.md b/docs/src/pages/api/03_events/07_messages.md index de28619bb..e63c5c0ad 100644 --- a/docs/src/pages/api/03_events/07_messages.md +++ b/docs/src/pages/api/03_events/07_messages.md @@ -35,7 +35,7 @@ The `Message` object may be the raw payload if `storeMessages` is not enabled _o Discord does not provide a way to get deleted messages. ```php -$discord->on(Event::MESSAGE_DELETE, function ($message, Discord $discord) { +$discord->on(Event::MESSAGE_DELETE, function (object $message, Discord $discord) { if ($message instanceof Message) { // Message is present in cache } diff --git a/docs/src/pages/api/03_events/11_webhooks.md b/docs/src/pages/api/03_events/11_webhooks.md index b6f698505..849c2a239 100644 --- a/docs/src/pages/api/03_events/11_webhooks.md +++ b/docs/src/pages/api/03_events/11_webhooks.md @@ -7,7 +7,7 @@ title: "Webhooks" Called with a `Guild` and `Channel` object when a guild channel's webhooks are is created, updated, or deleted. ```php -$discord->on(Event::WEBHOOKS_UPDATE, function ($guild, Discord $discord, $channel) { +$discord->on(Event::WEBHOOKS_UPDATE, function (object $guild, Discord $discord, object $channel) { if ($guild instanceof Guild && $channel instanceof Channel) { // Guild and Channel is present in cache } From 864afa7ea26c576648e0a9091d8fb4888e38f572 Mon Sep 17 00:00:00 2001 From: SQKo <87897282+SQKo@users.noreply.github.com> Date: Fri, 16 Sep 2022 22:21:01 +0700 Subject: [PATCH 09/13] update docs event params --- docs/src/pages/api/03_events/01_application_commands.md | 6 ++---- docs/src/pages/api/03_events/02_auto_moderations.md | 2 +- docs/src/pages/api/03_events/03_channels.md | 2 +- docs/src/pages/api/03_events/04_guilds.md | 9 ++++++--- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/src/pages/api/03_events/01_application_commands.md b/docs/src/pages/api/03_events/01_application_commands.md index 404056084..94cab1975 100644 --- a/docs/src/pages/api/03_events/01_application_commands.md +++ b/docs/src/pages/api/03_events/01_application_commands.md @@ -4,12 +4,10 @@ title: "Application Commands" ### Application Command Permissions Update -Called with an `Overwrite` object when an application command's permissions are updated. +Called with an `CommandPermissions` object when an application command's permissions are updated. ```php -// use Discord\Parts\Interactions\Command\Overwrite; - -$discord->on(Event::APPLICATION_COMMAND_PERMISSIONS_UPDATE, function (Overwrite $overwrite, Discord $discord, Overwrite $oldOverwrite) { +$discord->on(Event::APPLICATION_COMMAND_PERMISSIONS_UPDATE, function (CommandPermissions $commandPermission, Discord $discord, ?CommandPermissions $oldCommandPermission) { // ... }); ``` diff --git a/docs/src/pages/api/03_events/02_auto_moderations.md b/docs/src/pages/api/03_events/02_auto_moderations.md index 1f7dd688f..b569b7a66 100644 --- a/docs/src/pages/api/03_events/02_auto_moderations.md +++ b/docs/src/pages/api/03_events/02_auto_moderations.md @@ -21,7 +21,7 @@ Requires the `Intents::AUTO_MODERATION_CONFIGURATION` intent. Called with a `Rule` object when an auto moderation rule is updated. ```php -$discord->on(Event::AUTO_MODERATION_RULE_UPDATE, function (Rule $rule, Discord $discord, Rule $oldRule) { +$discord->on(Event::AUTO_MODERATION_RULE_UPDATE, function (Rule $rule, Discord $discord, ?Rule $oldRule) { // ... }); ``` diff --git a/docs/src/pages/api/03_events/03_channels.md b/docs/src/pages/api/03_events/03_channels.md index 21484a3c1..d1d2e1b8b 100644 --- a/docs/src/pages/api/03_events/03_channels.md +++ b/docs/src/pages/api/03_events/03_channels.md @@ -79,7 +79,7 @@ $discord->on(Event::THREAD_UPDATE, function (Thread $thread, Discord $discord, ? Called with an old `Thread` object when a thread relevant to the Bot is deleted. ```php -$discord->on(Event::THREAD_DELETE, function ($thread, Discord $discord) { +$discord->on(Event::THREAD_DELETE, function (object $thread, Discord $discord) { // { // "type": 0, // "id": "", diff --git a/docs/src/pages/api/03_events/04_guilds.md b/docs/src/pages/api/03_events/04_guilds.md index 372376905..6fb307bae 100644 --- a/docs/src/pages/api/03_events/04_guilds.md +++ b/docs/src/pages/api/03_events/04_guilds.md @@ -45,7 +45,7 @@ Called with a `Guild` object in one of the following situations: 2. The guild is unavailable due to an outage. ```php -$discord->on(Event::GUILD_DELETE, function ($guild, Discord $discord, bool $unavailable) { +$discord->on(Event::GUILD_DELETE, function (object $guild, Discord $discord, bool $unavailable) { // ... if ($unavailable) { // the guild is unavailabe due to an outage, $guild is a stdClass @@ -248,8 +248,11 @@ Requires the `Intents::GUILD_INTEGRATIONS` intent. Called with a cached `Guild` object when a guild integration is updated. ```php -$discord->on(Event::GUILD_INTEGRATIONS_UPDATE, function (?Guild $guild, Discord $discord) { +$discord->on(Event::GUILD_INTEGRATIONS_UPDATE, function (object $guild, Discord $discord) { // ... + if ($guild instanceof Guild) { + // Guild is present on cache + } }); ``` @@ -278,7 +281,7 @@ $discord->on(Event::INTEGRATION_UPDATE, function (Integration $integration, Disc Called with an old `Integration` object when a integration is deleted from a guild. ```php -$discord->on(Event::INTEGRATION_DELETE, function ($integration, Discord $discord) { +$discord->on(Event::INTEGRATION_DELETE, function (object $integration, Discord $discord) { // if $integration is not cached, // { // "id": "", From f9b7fbd31ab8a9d7329b8a92d34a9538d40a9a2b Mon Sep 17 00:00:00 2001 From: SQKo <87897282+SQKo@users.noreply.github.com> Date: Sat, 17 Sep 2022 00:19:41 +0700 Subject: [PATCH 10/13] event docs --- docs/src/pages/api/03_events/03_channels.md | 6 +++++ docs/src/pages/api/03_events/04_guilds.md | 30 ++++++++++++++------- docs/src/pages/api/03_events/05_invites.md | 4 +-- docs/src/pages/api/03_events/07_messages.md | 8 +++--- docs/src/pages/api/03_events/11_webhooks.md | 4 +-- 5 files changed, 34 insertions(+), 18 deletions(-) diff --git a/docs/src/pages/api/03_events/03_channels.md b/docs/src/pages/api/03_events/03_channels.md index d1d2e1b8b..27c325b1c 100644 --- a/docs/src/pages/api/03_events/03_channels.md +++ b/docs/src/pages/api/03_events/03_channels.md @@ -80,12 +80,18 @@ Called with an old `Thread` object when a thread relevant to the Bot is deleted. ```php $discord->on(Event::THREAD_DELETE, function (object $thread, Discord $discord) { + if ($thread instanceof Thread) { + // $thread was cached + } + // $thread was not in cache: + else { // { // "type": 0, // "id": "", // "guild_id": "", // "parent_id": "" // } + } }); ``` diff --git a/docs/src/pages/api/03_events/04_guilds.md b/docs/src/pages/api/03_events/04_guilds.md index 6fb307bae..9cfb10121 100644 --- a/docs/src/pages/api/03_events/04_guilds.md +++ b/docs/src/pages/api/03_events/04_guilds.md @@ -173,9 +173,9 @@ Called with a `Role` object when a role is deleted in a guild. `$role` may retur ```php $discord->on(Event::GUILD_ROLE_DELETE, function (object $role, Discord $discord) { if ($role instanceof Role) { - // Role is present in cache + // $role was cached } - // If the role is not present in the cache: + // $role was not in cache: else { // { // "guild_id": "" // role guild ID @@ -249,9 +249,14 @@ Called with a cached `Guild` object when a guild integration is updated. ```php $discord->on(Event::GUILD_INTEGRATIONS_UPDATE, function (object $guild, Discord $discord) { - // ... if ($guild instanceof Guild) { - // Guild is present on cache + // $guild was cached + } + // $guild was not in cache: + else { + // { + // "guild_id": "", + // } } }); ``` @@ -282,11 +287,16 @@ Called with an old `Integration` object when a integration is deleted from a gui ```php $discord->on(Event::INTEGRATION_DELETE, function (object $integration, Discord $discord) { - // if $integration is not cached, - // { - // "id": "", - // "guild_id": "", - // "application_id": "" - // } + if ($integration instanceof Integration) { + // $integration was cached + } + // $integration was not in cache: + else { + // { + // "id": "", + // "guild_id": "", + // "application_id": "" + // } + } }); ``` diff --git a/docs/src/pages/api/03_events/05_invites.md b/docs/src/pages/api/03_events/05_invites.md index 92b0ace93..26315e306 100644 --- a/docs/src/pages/api/03_events/05_invites.md +++ b/docs/src/pages/api/03_events/05_invites.md @@ -21,9 +21,9 @@ Called with an object when an invite is created. ```php $discord->on(Event::INVITE_DELETE, function (object $invite, Discord $discord) { if ($invite instanceof Invite) { - // Invite is present in cache + // $invite was cached } - // If the invite is not present in the cache: + // If $invite was not in cache: else { // { // "channel_id": "", diff --git a/docs/src/pages/api/03_events/07_messages.md b/docs/src/pages/api/03_events/07_messages.md index e63c5c0ad..5d8452e2a 100644 --- a/docs/src/pages/api/03_events/07_messages.md +++ b/docs/src/pages/api/03_events/07_messages.md @@ -37,9 +37,9 @@ Discord does not provide a way to get deleted messages. ```php $discord->on(Event::MESSAGE_DELETE, function (object $message, Discord $discord) { if ($message instanceof Message) { - // Message is present in cache + // $message was cached } - // If the message is not present in the cache: + // $message was not in cache: else { // { // "id": "", // deleted message ID, @@ -60,9 +60,9 @@ Discord does not provide a way to get deleted messages. $discord->on(Event::MESSAGE_DELETE_BULK, function (Collection $messages, Discord $discord) { foreach ($messages as $message) { if ($message instanceof Message) { - // Message is present in cache + // $message was cached } - // If the message is not present in the cache: + // $message was not in cache: else { // { // "id": "", // deleted message ID, diff --git a/docs/src/pages/api/03_events/11_webhooks.md b/docs/src/pages/api/03_events/11_webhooks.md index 849c2a239..c46e803da 100644 --- a/docs/src/pages/api/03_events/11_webhooks.md +++ b/docs/src/pages/api/03_events/11_webhooks.md @@ -9,9 +9,9 @@ Called with a `Guild` and `Channel` object when a guild channel's webhooks are i ```php $discord->on(Event::WEBHOOKS_UPDATE, function (object $guild, Discord $discord, object $channel) { if ($guild instanceof Guild && $channel instanceof Channel) { - // Guild and Channel is present in cache + // $guild and $channel was cached } - // If not present in the cache: + // $guild and/or $channel was not in cache: else { // { // "guild_id": "" // webhook guild ID From 043c88dc868ab2a3eb9c325a6a81bd4901f62aca Mon Sep 17 00:00:00 2001 From: SQKo <87897282+SQKo@users.noreply.github.com> Date: Sat, 17 Sep 2022 14:54:43 +0700 Subject: [PATCH 11/13] add missing phpdoc param for audit log --- src/Discord/Parts/Guild/Guild.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Discord/Parts/Guild/Guild.php b/src/Discord/Parts/Guild/Guild.php index 556462575..63da58f67 100644 --- a/src/Discord/Parts/Guild/Guild.php +++ b/src/Discord/Parts/Guild/Guild.php @@ -1248,8 +1248,8 @@ public function createInvite(...$args): ExtendedPromiseInterface * * @link https://discord.com/developers/docs/resources/guild#modify-guild-mfa-level * - * @param int $level The new MFA level `Guild::MFA_NONE` or `Guild::MFA_ELEVATED`. - * @param string $reason Reason for Audit Log. + * @param int $level The new MFA level `Guild::MFA_NONE` or `Guild::MFA_ELEVATED`. + * @param string|null $reason Reason for Audit Log. * * @return ExtendedPromiseInterface This guild. */ @@ -1272,7 +1272,8 @@ public function updateMFALevel(int $level, ?string $reason = null): ExtendedProm * * @link https://discord.com/developers/docs/resources/guild#modify-guild * - * @param bool[] $features Array of features to set/unset, e.g. `['COMMUNITY' => true, 'INVITES_DISABLED' => false]`. + * @param bool[] $features Array of features to set/unset, e.g. `['COMMUNITY' => true, 'INVITES_DISABLED' => false]`. + * @param string|null $reason Reason for Audit Log. * * @return ExtendedPromiseInterface This guild. * From d789bf36397f2fad0060dc78fb429d312af44fca Mon Sep 17 00:00:00 2001 From: SQKo <87897282+SQKo@users.noreply.github.com> Date: Sun, 18 Sep 2022 20:14:41 +0700 Subject: [PATCH 12/13] Temporary fix for cached interaction->member->permissions --- src/Discord/Parts/Interactions/Interaction.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Discord/Parts/Interactions/Interaction.php b/src/Discord/Parts/Interactions/Interaction.php index 6151ce41d..b601d6d9f 100644 --- a/src/Discord/Parts/Interactions/Interaction.php +++ b/src/Discord/Parts/Interactions/Interaction.php @@ -133,8 +133,10 @@ protected function getGuildAttribute(): ?Guild */ protected function getChannelAttribute(): ?Channel { - if ($this->guild && $channel = $this->guild->channels->get('id', $this->channel_id)) { - return $channel; + if ($guild = $this->guild) { + if ($channel = $guild->channels->get('id', $this->channel_id)) { + return $channel; + } } return $this->discord->getChannel($this->channel_id); @@ -148,8 +150,14 @@ protected function getChannelAttribute(): ?Channel protected function getMemberAttribute(): ?Member { if (isset($this->attributes['member'])) { - if ($this->guild && $member = $this->guild->members->get('id', $this->attributes['member']->user->id)) { - return $member; + if ($guild = $this->guild) { + if ($member = $guild->members->get('id', $this->attributes['member']->user->id)) { + // @todo Temporary workaround until member is cached from INTERACTION_CREATE event + if (! $member->permissions) { + $member->permissions = $this->attributes['member']->permissions; + } + return $member; + } } return $this->factory->part(Member::class, (array) $this->attributes['member'] + ['guild_id' => $this->guild_id], true); From 5060daabe768c32afa4a79686f93085dfa004927 Mon Sep 17 00:00:00 2001 From: SQKo <87897282+SQKo@users.noreply.github.com> Date: Sun, 18 Sep 2022 21:12:34 +0700 Subject: [PATCH 13/13] Always overwrite permissions from attribute --- src/Discord/Parts/Interactions/Interaction.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Discord/Parts/Interactions/Interaction.php b/src/Discord/Parts/Interactions/Interaction.php index b601d6d9f..ed9f4b5ed 100644 --- a/src/Discord/Parts/Interactions/Interaction.php +++ b/src/Discord/Parts/Interactions/Interaction.php @@ -153,9 +153,7 @@ protected function getMemberAttribute(): ?Member if ($guild = $this->guild) { if ($member = $guild->members->get('id', $this->attributes['member']->user->id)) { // @todo Temporary workaround until member is cached from INTERACTION_CREATE event - if (! $member->permissions) { - $member->permissions = $this->attributes['member']->permissions; - } + $member->permissions = $this->attributes['member']->permissions; return $member; } }