diff --git a/composer.json b/composer.json index c496015..1b362aa 100644 --- a/composer.json +++ b/composer.json @@ -31,6 +31,7 @@ "laravel/sanctum": "^4.0", "react/async": "^4.2", "react/http": "^1.9", + "react/promise": "^3.0", "symfony/psr-http-message-bridge": "^7.0", "team-reflex/discord-php": "^10.1" }, diff --git a/src/Discord/Message.php b/src/Discord/Message.php index e81dd78..e3040fe 100644 --- a/src/Discord/Message.php +++ b/src/Discord/Message.php @@ -21,12 +21,12 @@ use Discord\Parts\User\User; use Discord\Repository\Channel\WebhookRepository; use Exception; +use Illuminate\Contracts\Support\Arrayable; use Illuminate\Support\Carbon; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; use Laracord\Laracord; -use React\Promise\ExtendedPromiseInterface; use React\Promise\PromiseInterface; use Throwable; @@ -169,6 +169,11 @@ class Message */ protected string|bool $webhook = false; + /** + * The additional message embeds. + */ + protected array $embeds = []; + /** * The default embed colors. */ @@ -227,6 +232,12 @@ public function build(): MessageBuilder $message->addEmbed($this->getEmbed()); } + if ($this->hasEmbeds()) { + foreach ($this->embeds as $embed) { + $message->addEmbed($embed); + } + } + if ($this->hasSelects()) { foreach ($this->selects as $select) { $message->addComponent($select); @@ -255,7 +266,7 @@ public function build(): MessageBuilder /** * Send the message. */ - public function send(mixed $destination = null): PromiseInterface|ExtendedPromiseInterface|null + public function send(mixed $destination = null): ?PromiseInterface { if ($destination) { $this->channel($destination); @@ -271,7 +282,7 @@ public function send(mixed $destination = null): PromiseInterface|ExtendedPromis /** * Send the message to a user. */ - public function sendTo(mixed $user): ?ExtendedPromiseInterface + public function sendTo(mixed $user): ?PromiseInterface { if (is_numeric($user)) { $member = $this->bot->discord()->users->get('id', $user); @@ -301,7 +312,7 @@ public function sendTo(mixed $user): ?ExtendedPromiseInterface /** * Send the message as a webhook. */ - protected function handleWebhook(): ?ExtendedPromiseInterface + protected function handleWebhook(): ?PromiseInterface { try { /** @var WebhookRepository $webhooks */ @@ -357,7 +368,7 @@ public function webhook(string|bool $value = true): self /** * Reply to a message or interaction. */ - public function reply(Interaction|ChannelMessage $message, bool $ephemeral = false): ExtendedPromiseInterface + public function reply(Interaction|ChannelMessage $message, bool $ephemeral = false): PromiseInterface { if ($message instanceof Interaction) { return $message->respondWithMessage($this->build(), ephemeral: $ephemeral); @@ -369,7 +380,7 @@ public function reply(Interaction|ChannelMessage $message, bool $ephemeral = fal /** * Edit an existing message or interaction message. */ - public function edit(Interaction|ChannelMessage $message): ExtendedPromiseInterface + public function edit(Interaction|ChannelMessage $message): PromiseInterface { if ($message instanceof Interaction) { return $message->updateMessage($this->build()); @@ -381,7 +392,7 @@ public function edit(Interaction|ChannelMessage $message): ExtendedPromiseInterf /** * Edit an existing message if it is owned by the bot, otherwise replying instead. */ - public function editOrReply(Interaction|ChannelMessage $message, bool $ephemeral = false): ExtendedPromiseInterface + public function editOrReply(Interaction|ChannelMessage $message, bool $ephemeral = false): PromiseInterface { if ($message instanceof Interaction) { return $message->message?->user_id === $this->bot->discord()->id @@ -858,8 +869,14 @@ public function clearAuthor(): self /** * Set the message fields. */ - public function fields(array $fields, bool $inline = true): self + public function fields(array|Arrayable|Collection $fields, bool $inline = true): self { + $fields = match (true) { + $fields instanceof Collection => $fields->all(), + $fields instanceof Arrayable => $fields->toArray(), + default => $fields + }; + foreach ($fields as $key => $value) { $this->field($key, $value, $inline); } @@ -1173,6 +1190,60 @@ public function hasPoll(): bool return ! is_null($this->poll); } + /** + * Add an additional embed to the message. + */ + public function withEmbed(MessageBuilder|self $builder): self + { + if (count($this->embeds) === 10) { + throw new Exception('Messages cannot exceed 10 embeds.'); + } + + if ($builder instanceof self) { + $builder = $builder->build(); + } + + $embeds = $builder->getEmbeds(); + + if (! $embeds) { + throw new Exception('Builder must contain at least one embed.'); + } + + $this->embeds[] = $embeds[0]; + + return $this; + } + + /** + * Add additional embeds to the message. + */ + public function withEmbeds(array $builders): self + { + foreach ($builders as $builder) { + $this->withEmbed($builder); + } + + return $this; + } + + /** + * Determine if the message has additional embeds. + */ + public function hasEmbeds(): bool + { + return ! empty($this->embeds); + } + + /** + * Clear the additional embeds from the message. + */ + public function clearEmbeds(): self + { + $this->embeds = []; + + return $this; + } + /** * Set the interaction route prefix. */ diff --git a/src/Discord/Modal.php b/src/Discord/Modal.php index 550a75c..e0032cf 100644 --- a/src/Discord/Modal.php +++ b/src/Discord/Modal.php @@ -7,7 +7,7 @@ use Discord\Parts\Interactions\Interaction; use Exception; use Illuminate\Support\Str; -use React\Promise\ExtendedPromiseInterface; +use React\Promise\PromiseInterface; class Modal { @@ -144,7 +144,7 @@ public function paragraph( /** * Show the modal. */ - public function show(?Interaction $interaction = null): ExtendedPromiseInterface + public function show(?Interaction $interaction = null): PromiseInterface { $interaction = $interaction ?? $this->interaction; diff --git a/src/Laracord.php b/src/Laracord.php index 75a8a02..f82779e 100644 --- a/src/Laracord.php +++ b/src/Laracord.php @@ -35,6 +35,7 @@ use function React\Async\await; use function React\Promise\all; +use function React\Promise\set_rejection_handler; class Laracord { @@ -212,6 +213,8 @@ public static function make(ConsoleCommand $console): self */ public function boot(): void { + set_rejection_handler(fn (Throwable $e) => $this->console()->error($e->getTraceAsString())); + $this->beforeBoot(); $this->bootDiscord();