find($this->messageId); if (! $message || in_array($message->status, ['sent', 'delivered', 'read', 'failed', 'unknown'], true)) { return; } $message->increment('attempts'); $conversation = $message->conversation; $upload = data_get($message->media_metadata, 'outbound_upload', []); $disk = $upload['disk'] ?? 'local'; $path = $upload['path'] ?? null; $mime = $upload['mime_type'] ?? null; $filename = $upload['filename'] ?? 'attachment'; try { if (! $conversation?->last_inbound_at?->isAfter(now()->subHours(24))) { throw new RuntimeException('The 24-hour WhatsApp reply window closed before this file could be sent.'); } if (! $path || ! Storage::disk($disk)->exists($path) || ! $policy->isAllowed($mime)) { throw new RuntimeException('The queued file is missing or no longer allowed.'); } $mediaId = $whatsapp->uploadMedia( Storage::disk($disk)->path($path), $mime, $filename ); if (! $mediaId) { throw new RuntimeException('WhatsApp media sending is not configured.'); } $context = [ 'message_uuid' => $message->uuid, 'admin_id' => $message->admin_id, 'user_id' => $conversation->user_id, 'correlation_id' => $message->correlation_id, ]; $result = $policy->isImage($mime) ? $whatsapp->sendImage($conversation->wa_id, $mediaId, $message->body, $context) : $whatsapp->sendDocument($conversation->wa_id, $mediaId, $filename, $message->body, $context); if (! data_get($result, 'messages.0.id')) { throw new RuntimeException('WhatsApp did not accept the queued file.'); } $message->refresh(); $payload = $message->payload ?? []; $payload['request'][$message->type]['mime_type'] = $mime; $payload['request'][$message->type]['file_size'] = $upload['size'] ?? null; if ($message->type === 'document') { $payload['request']['document']['filename'] = $filename; } $message->forceFill(['payload' => $payload])->save(); $this->cleanup($message, $disk, $path); } catch (WhatsAppApiException $exception) { $message->forceFill([ 'status' => $exception->unknownDelivery ? 'unknown' : 'failed', 'failed_at' => now(), 'payload' => ['error' => $exception->payload], ])->save(); $this->cleanup($message, $disk, $path); } catch (Throwable $exception) { $message->forceFill([ 'status' => 'failed', 'failed_at' => now(), 'payload' => ['error' => ['message' => $exception->getMessage()]], ])->save(); $this->cleanup($message, $disk, $path); report($exception); } } public function failed(?Throwable $exception): void { $message = WhatsAppMessage::find($this->messageId); if (! $message) { return; } $upload = data_get($message->media_metadata, 'outbound_upload', []); $message->forceFill([ 'status' => 'failed', 'failed_at' => now(), 'payload' => ['error' => ['message' => $exception?->getMessage() ?: 'The media job failed.']], ])->save(); $this->cleanup($message, $upload['disk'] ?? 'local', $upload['path'] ?? null); } protected function cleanup(WhatsAppMessage $message, string $disk, ?string $path): void { if ($path) { Storage::disk($disk)->delete($path); Storage::disk($disk)->deleteDirectory(dirname($path)); } $metadata = $message->media_metadata ?? []; if (isset($metadata['outbound_upload'])) { $metadata['outbound_upload']['path'] = null; $message->forceFill(['media_metadata' => $metadata])->save(); } } }