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

> Come creare e pubblicare feed algoritmici personalizzati con Laravel Bluesky. Illustra registrazione, creazione di comandi, feed multipli, separazione delle classi, autenticazione e utilizzi avanzati.

## Panoramica

Il Feed Generator è il meccanismo per i "feed algoritmici" che opera su Bluesky. Puoi pubblicare feed personalizzati basati su parole chiave o condizioni utente specifiche. Con `laravel-bluesky` puoi implementare facilmente un Feed Generator in un'applicazione Laravel.

<Info>
  Tutorial ufficiale: [Creazione di feed personalizzati](https://atproto.com/ja/guides/custom-feed-tutorial)
</Info>

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

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

    Bluesky->>App: GET /xrpc/app.bsky.feed.getFeedSkeleton
    App->>DB: Recupero dei dati del feed
    DB-->>App: posts
    App-->>Bluesky: { cursor, feed }
```

## Registrazione dell'algoritmo FeedGenerator

L'uso più semplice consiste nel registrare l'algoritmo come closure in `AppServiceProvider::boot()`.

```php theme={null}
// Registra in AppServiceProvider::boot()

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 {
    // L'implementazione è a tua discrezione.

    // È necessaria l'autenticazione a causa di una restrizione temporanea dell'API
    $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();

    // Puoi anche usare l'oggetto Request per restituire risultati basati sullo stato dell'utente.
    info('user: '.$user); // DID dell'utente che ha effettuato la richiesta. 'did:plc:***'
    info('header', $request->header());

    return compact('cursor', 'feed');
});
```

Per `name` utilizza una stringa URL-safe.

Il valore di ritorno dell'algoritmo è un array che contiene `cursor` e `feed`.

```php theme={null}
[
    'cursor' => '',
    'feed' => [
       ['post' => 'at://'],
       ['post' => 'at://'],
    ],
]
```

Tutte le rotte necessarie al pacchetto vengono registrate automaticamente.

* `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`
* Il Service DID viene generato automaticamente dall'URL attuale (es.: `did:web:example.com`).

<Tip>
  Le uniche cose che decidi tu sono il `name` del FeedGenerator e il contenuto dell'implementazione.
</Tip>

## Pubblicazione del feed (creazione del comando)

Implementare il FeedGenerator nell'app Laravel non basta a pubblicarlo su Bluesky. Crea ed esegui un comando che invochi `publishFeedGenerator`.

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

  <Step title="Implementa il comando">
    ```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 = 'Pubblica un FeedGenerator su Bluesky';

        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="Esegui il comando">
    ```bash theme={null}
    php artisan bluesky:publish-generator
    ```

    In caso di successo, sull'elenco dei feed del profilo Bluesky verrà aggiunto un link. `publishFeedGenerator` si limita ad aggiornare le informazioni, quindi puoi eseguirlo più volte senza problemi.
  </Step>
</Steps>

## Creazione di più FeedGenerator

Puoi creare più feed semplicemente chiamando `register` più volte cambiando `name`.

```php theme={null}
// AppServiceProvider::boot()

use Revolution\Bluesky\FeedGenerator\FeedGenerator;

FeedGenerator::register(name: 'feed1', algo: function() {
    // Implementazione di feed1
});

FeedGenerator::register(name: 'feed2', algo: function() {
    // Implementazione di feed2
});
```

Nello stesso modo, nel comando di pubblicazione chiami `publishFeedGenerator` più volte.

```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);
```

## Separazione dell'algoritmo in una classe

Usare una classe indipendente al posto di una closure aiuta a organizzare meglio il codice. Crea una classe callable che implementi il contract `FeedGeneratorAlgorithm` e registrala in `AppServiceProvider`.

```php theme={null}
// Crea dove preferisci

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
    {
        // È necessaria l'autenticazione a causa di una restrizione temporanea dell'API
        $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);
```

## Autenticazione

La funzione di autenticazione dello starter kit ufficiale è abilitata per impostazione predefinita. Per disabilitarla, passa a `validateAuthUsing` una closure che restituisce semplicemente il DID dell'utente.

```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>
  I feed sono influenzati dalle "impostazioni di lingua" dell'account. Se il FeedGenerator recupera i post ma il feed non viene visualizzato su Bluesky, controlla le impostazioni di lingua dell'account.
</Warning>

## Utilizzo avanzato

Se usi un comando Artisan e il task scheduler per salvare i post in un database, e nell'algoritmo li recuperi soltanto dal DB, puoi realizzare un feed veloce senza chiamate API.

```php theme={null}
// Esempio di algoritmo che restituisce il feed dal DB

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}
// Esempio di schedulazione per raccogliere periodicamente i post (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

- [Testing](/it/packages/laravel-bluesky/testing.md)
- [BlueskyManager e HasShortHand](/it/packages/laravel-bluesky/bluesky-manager.md)
- [Tutorial per bot - Laravel Bluesky](/it/packages/laravel-bluesky/bot-tutorial.md)
- [Laravel Bluesky](/it/packages/laravel-bluesky/index.md)
- [Crypto — crittografia AT Protocol](/it/packages/laravel-bluesky/crypto.md)
