Zum Hauptinhalt springen

Überblick

Feed Generator ist der Mechanismus für „algorithmische Feeds” auf Bluesky. Sie können eigene Feeds veröffentlichen, die auf bestimmte Keywords oder Nutzerkriterien zugeschnitten sind. Mit laravel-bluesky lassen sich Feed Generators einfach in einer Laravel-Anwendung implementieren.
Offizielles Tutorial: Custom-Feed erstellen
Offizielles Starter-Kit: bluesky-social/feed-generator

Feed-Generator-Algorithmus registrieren

Die einfachste Verwendung besteht darin, den Algorithmus als Closure in AppServiceProvider::boot() zu registrieren.
// In AppServiceProvider::boot() registrieren

use Illuminate\Http\Request;
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\FeedGenerator\FeedGenerator;

FeedGenerator::register(name: 'artisan', algo: function(int $limit, ?string $cursor, ?string $user, Request $request): array {
    // Die Implementierung können Sie frei gestalten.

    // Aufgrund einer temporären API-Einschränkung ist Authentifizierung nötig
    $response = Bluesky::login(identifier: config('bluesky.identifier'), password: config('bluesky.password'))
                       ->searchPosts(q: '#laravel', until: $cursor, limit: $limit);

    $cursor = data_get($response->collect('posts')->last(), 'indexedAt');

    $feed = $response->collect('posts')->map(function(array $post) {
        return ['post' => data_get($post, 'uri')];
    })->toArray();

    // Sie können auch das Request-Objekt nutzen, um nutzerabhängige Ergebnisse zurückzugeben.
    info('user: '.$user); // DID des anfragenden Nutzers. 'did:plc:***'
    info('header', $request->header());

    return compact('cursor', 'feed');
});
Verwenden Sie für name einen URL-sicheren String. Der Rückgabewert des Algorithmus ist ein Array mit cursor und feed.
[
    'cursor' => '',
    'feed' => [
       ['post' => 'at://'],
       ['post' => 'at://'],
    ],
]
Alle vom Paket benötigten Routen werden automatisch registriert.
  • http://localhost/xrpc/app.bsky.feed.getFeedSkeleton?feed=at://did:web:example.com/app.bsky.feed.generator/artisan
  • http://localhost/xrpc/app.bsky.feed.describeFeedGenerator
  • http://localhost/.well-known/did.json
  • Die Service-DID wird automatisch aus der aktuellen URL abgeleitet (z. B. did:web:example.com).
Sie müssen nur den name und die Implementierung des Feed Generators selbst festlegen.

Feed veröffentlichen (Kommando erstellen)

Nur die Implementierung eines Feed Generators in der Laravel-Anwendung veröffentlicht ihn noch nicht auf Bluesky. Erstellen und führen Sie ein Kommando aus, das publishFeedGenerator aufruft.
1

Kommando erzeugen

php artisan make:command PublishGeneratorCommand
2

Kommando implementieren

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Record\Generator;

class PublishGeneratorCommand extends Command
{
    protected $signature = 'bluesky:publish-generator';

    protected $description = 'Feed Generator auf Bluesky veröffentlichen';

    public function handle()
    {
        $generator = Generator::create(did: 'did:web:example.com', displayName: 'Feed name')
                              ->description('Feed description');

        $res = Bluesky::login(identifier: config('bluesky.identifier'), password: config('bluesky.password'))
                      ->publishFeedGenerator(name: 'artisan', generator: $generator);

        dump($res->json());

        return 0;
    }
}
3

Kommando ausführen

php artisan bluesky:publish-generator
Bei Erfolg wird in der Feed-Liste Ihres Bluesky-Profils ein Link ergänzt. publishFeedGenerator aktualisiert lediglich Informationen, sodass Sie es beliebig oft ausführen können.

Mehrere Feed Generators erstellen

Sie können mehrere Feeds erstellen, indem Sie register mit unterschiedlichen name-Werten mehrfach aufrufen.
// AppServiceProvider::boot()

use Revolution\Bluesky\FeedGenerator\FeedGenerator;

FeedGenerator::register(name: 'feed1', algo: function() {
    // Implementierung von feed1
});

FeedGenerator::register(name: 'feed2', algo: function() {
    // Implementierung von feed2
});
Rufen Sie im Publish-Kommando entsprechend mehrfach publishFeedGenerator auf.
// PublishGeneratorCommand

Bluesky::login(identifier: config('bluesky.identifier'), password: config('bluesky.password'));

$generator1 = Generator::create(did: 'did:web:example.com', displayName: 'Feed 1')
                       ->description('Feed 1');
Bluesky::publishFeedGenerator(name: 'feed1', generator: $generator1);

$generator2 = Generator::create(did: 'did:web:example.com', displayName: 'Feed 2')
                       ->description('Feed 2');
Bluesky::publishFeedGenerator(name: 'feed2', generator: $generator2);

Algorithmus-Klassen auslagern

Statt einer Closure sorgt eine eigenständige Klasse für aufgeräumten Code. Erstellen Sie eine callable Klasse, die den Contract FeedGeneratorAlgorithm implementiert, und registrieren Sie sie im AppServiceProvider.
// An beliebigem Ort ablegen

namespace App\FeedGenerator;

use Illuminate\Http\Request;
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Contracts\FeedGeneratorAlgorithm;

class ArtisanFeed implements FeedGeneratorAlgorithm
{
    public function __invoke(int $limit, ?string $cursor, ?string $user, Request $request): array
    {
        // Aufgrund einer temporären API-Einschränkung ist Authentifizierung nötig
        $response = Bluesky::login(identifier: config('bluesky.identifier'), password: config('bluesky.password'))
            ->searchPosts(q: '#laravel', until: $cursor, limit: $limit);

        $cursor = data_get($response->collect('posts')->last(), 'indexedAt');

        $feed = $response->collect('posts')->map(function (array $post) {
            return ['post' => data_get($post, 'uri')];
        })->toArray();

        info('user: '.$user);
        info('header', $request->header());

        return compact('cursor', 'feed');
    }
}
// AppServiceProvider::boot()

use Revolution\Bluesky\FeedGenerator\FeedGenerator;
use App\FeedGenerator\ArtisanFeed;

FeedGenerator::register(name: 'artisan', algo: ArtisanFeed::class);

Authentifizierung

Die Authentifizierungsfunktion des offiziellen Starter-Kits ist standardmäßig aktiv. Zum Deaktivieren geben Sie an validateAuthUsing eine Closure, die einfach die Nutzer-DID zurückgibt.
// AppServiceProvider::boot()

use Illuminate\Http\Request;
use Revolution\Bluesky\Crypto\JsonWebToken;
use Revolution\Bluesky\FeedGenerator\FeedGenerator;

FeedGenerator::validateAuthUsing(function (?string $jwt, Request $request): ?string {
    [, $payload] = JsonWebToken::explode($jwt);
    return data_get($payload, 'iss');
});
Der Feed unterliegt den Spracheinstellungen des Accounts. Wenn der Feed Generator zwar Posts abruft, der Feed aber auf Bluesky nicht angezeigt wird, prüfen Sie die Spracheinstellungen Ihres Accounts.

Fortgeschrittene Verwendung

Speichern Sie Posts über ein Artisan-Kommando und Task-Scheduling in einer Datenbank und lassen Sie den Algorithmus lediglich aus der DB lesen – so erreichen Sie ohne API-Aufrufe schnelle Feeds.
// Beispiel: Der Algorithmus gibt einen Feed aus der DB zurück

FeedGenerator::register(name: 'cached-feed', algo: function(int $limit, ?string $cursor): array {
    $query = \App\Models\Post::query()
        ->orderByDesc('indexed_at')
        ->limit($limit);

    if ($cursor) {
        $query->where('indexed_at', '<', $cursor);
    }

    $posts = $query->get();

    $cursor = $posts->last()?->indexed_at;

    $feed = $posts->map(fn ($post) => ['post' => $post->uri])->toArray();

    return compact('cursor', 'feed');
});
// Beispiel: Posts regelmäßig per Scheduler sammeln (routes/console.php)

use Illuminate\Support\Facades\Schedule;

Schedule::command('bluesky:collect-posts')->everyFiveMinutes();
Zuletzt geändert am 13. Juli 2026