BloomFeed Addons v0.22

This commit is contained in:
Esteban
2026-07-04 20:22:16 +02:00
parent c4d1308497
commit d58f2195f5
22 changed files with 832 additions and 7 deletions
+6
View File
@@ -13,6 +13,10 @@ use Throwable;
class FeedFetcherService
{
public function __construct(private readonly NotifierDispatchService $notifier)
{
}
/**
* Fetch, parse and store new articles for a single feed.
*
@@ -66,6 +70,8 @@ class FeedFetcherService
$article->feed()->associate($feed);
$article->save();
$this->notifier->notifyNewArticle($article);
$newCount++;
}
+116
View File
@@ -0,0 +1,116 @@
<?php
namespace App\Services;
use App\Models\Article;
use App\Models\Notifier;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Throwable;
class NotifierDispatchService
{
/**
* Notify every enabled notifier of the article's owner that matches this
* article's feed (scope "all", or scope "feeds" including this feed).
*/
public function notifyNewArticle(Article $article): void
{
$feed = $article->feed;
$notifiers = Notifier::query()
->where('user_id', $feed->user_id)
->enabled()
->forFeed($feed->id)
->get();
foreach ($notifiers as $notifier) {
$this->send($notifier, $article);
}
}
public function sendTest(Notifier $notifier): bool
{
$payload = $notifier->type === Notifier::TYPE_DISCORD
? $this->discordTestPayload()
: $this->webhookTestPayload();
return $this->deliver($notifier, $payload);
}
private function send(Notifier $notifier, Article $article): void
{
$payload = $notifier->type === Notifier::TYPE_DISCORD
? $this->discordPayload($notifier, $article)
: $this->webhookPayload($notifier, $article);
$this->deliver($notifier, $payload);
}
private function deliver(Notifier $notifier, array $payload): bool
{
try {
$response = Http::timeout(6)->post($notifier->url, $payload);
if ($response->failed()) {
$notifier->update([
'last_error' => Str::limit("HTTP {$response->status()}", 250, ''),
]);
return false;
}
$notifier->update(['last_triggered_at' => now(), 'last_error' => null]);
return true;
} catch (Throwable $e) {
Log::warning('Notifier delivery failed', [
'notifier_id' => $notifier->id,
'error' => $e->getMessage(),
]);
$notifier->update(['last_error' => Str::limit($e->getMessage(), 250, '')]);
return false;
}
}
private function discordPayload(Notifier $notifier, Article $article): array
{
return [
'embeds' => [[
'title' => Str::limit($article->title, 250),
'url' => $article->link,
'description' => $article->excerpt ? Str::limit($article->excerpt, 300) : null,
'color' => 15768330, // amber accent
'footer' => ['text' => $article->feed->displayTitle()],
'timestamp' => $article->published_at?->toIso8601String() ?? now()->toIso8601String(),
]],
];
}
private function webhookPayload(Notifier $notifier, Article $article): array
{
return [
'event' => 'article.created',
'feed' => $article->feed->displayTitle(),
'article' => [
'title' => $article->title,
'url' => $article->link,
'excerpt' => $article->excerpt,
'published_at' => $article->published_at?->toIso8601String(),
],
];
}
private function discordTestPayload(): array
{
return ['content' => '🔔 BloomFeed test notification — your Discord webhook is correctly configured.'];
}
private function webhookTestPayload(): array
{
return ['event' => 'test', 'message' => 'BloomFeed test notification — your webhook is correctly configured.'];
}
}