> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Feed Generator

> So erstellen und veröffentlichen Sie eigene algorithmische Feeds mit Laravel Bluesky. Registrierung, Kommando-Erstellung, mehrere Feeds, Klassenaufteilung, Authentifizierung und fortgeschrittene Anwendung.

## Ü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.

<Info>
  Offizielles Tutorial: [Custom-Feed erstellen](https://atproto.com/ja/guides/custom-feed-tutorial)
</Info>

<Info>
  Offizielles Starter-Kit: [bluesky-social/feed-generator](https://github.com/bluesky-social/feed-generator)
</Info>

```mermaid theme={null}
sequenceDiagram
    participant Bluesky as Bluesky<br>Server
    participant App as Laravel-<br>Anwendung
    participant DB as Datenbank

    Bluesky->>App: GET /xrpc/app.bsky.feed.getFeedSkeleton
    App->>DB: Feed-Daten abrufen
    DB-->>App: posts
    App-->>Bluesky: { cursor, feed }
```

## Feed-Generator-Algorithmus registrieren

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

```php theme={null}
// 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`.

```php theme={null}
[
    '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`).

<Tip>
  Sie müssen nur den `name` und die Implementierung des Feed Generators selbst festlegen.
</Tip>

## 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.

<Steps>
  <Step title="Kommando erzeugen">
    ```bash theme={null}
    php artisan make:command PublishGeneratorCommand
    ```
  </Step>

  <Step title="Kommando implementieren">
    ```php theme={null}
    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;
        }
    }
    ```
  </Step>

  <Step title="Kommando ausführen">
    ```bash theme={null}
    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.
  </Step>
</Steps>

## Mehrere Feed Generators erstellen

Sie können mehrere Feeds erstellen, indem Sie `register` mit unterschiedlichen `name`-Werten mehrfach aufrufen.

```php theme={null}
// 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.

```php theme={null}
// 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`.

```php theme={null}
// 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');
    }
}
```

```php theme={null}
// 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.

```php theme={null}
// 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');
});
```

<Warning>
  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.
</Warning>

## 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.

```php theme={null}
// 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');
});
```

```php theme={null}
// Beispiel: Posts regelmäßig per Scheduler sammeln (routes/console.php)

use Illuminate\Support\Facades\Schedule;

Schedule::command('bluesky:collect-posts')->everyFiveMinutes();
```

<Info>
  Source: [docs/feed-generator.md](https://github.com/invokable/laravel-bluesky/blob/main/docs/feed-generator.md)
</Info>


## Related topics

- [Tests](/de/packages/laravel-bluesky/testing.md)
- [BlueskyManager und HasShortHand](/de/packages/laravel-bluesky/bluesky-manager.md)
- [Bot-Tutorial - Laravel Bluesky](/de/packages/laravel-bluesky/bot-tutorial.md)
- [Laravel Bluesky](/de/packages/laravel-bluesky/index.md)
- [Crypto – Kryptografie im AT Protocol](/de/packages/laravel-bluesky/crypto.md)
