> $embeds */ public function send(string $content = '', array $embeds = []): void { if (blank($this->webhookUrl)) { Log::warning('Discord webhook URL not configured, skipping message.'); return; } $payload = array_filter([ 'content' => $content !== '' ? $content : null, 'embeds' => $embeds !== [] ? $this->withinLimits($embeds) : null, ]); $response = Http::asJson()->post($this->webhookUrl, $payload); if ($response->failed()) { Log::warning('Discord webhook request failed.', [ 'status' => $response->status(), 'body' => $response->body(), ]); } } /** * Trim embed text that would break Discord's limits. Losing the tail of a * legend is a much better outcome than losing the entire report, and the * warning says which text needs shortening. * * @param array> $embeds * @return array> */ private function withinLimits(array $embeds): array { foreach ($embeds as $index => $embed) { if (isset($embed['description'])) { $embeds[$index]['description'] = $this->trim($embed['description'], self::DESCRIPTION_LIMIT, 'description'); } foreach ($embed['fields'] ?? [] as $field => $data) { $embeds[$index]['fields'][$field]['value'] = $this->trim($data['value'], self::FIELD_VALUE_LIMIT, $data['name']); } } return $embeds; } private function trim(string $text, int $limit, string $label): string { if (mb_strlen($text) <= $limit) { return $text; } Log::warning('Discord embed text trimmed to fit the limit.', [ 'label' => $label, 'length' => mb_strlen($text), 'limit' => $limit, ]); return mb_substr($text, 0, $limit - 1).'…'; } }