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.
Registrazione dell’algoritmo FeedGenerator
L’uso più semplice consiste nel registrare l’algoritmo come closure in AppServiceProvider::boot().
// 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.
[
'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).
Le uniche cose che decidi tu sono il name del FeedGenerator e il contenuto dell’implementazione.
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.
Genera il comando
php artisan make:command PublishGeneratorCommand
Implementa il comando
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;
}
}
Esegui il comando
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.
Creazione di più FeedGenerator
Puoi creare più feed semplicemente chiamando register più volte cambiando name.
// 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.
// 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.
// 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');
}
}
// 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.
// 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');
});
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.
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.
// 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');
});
// Esempio di schedulazione per raccogliere periodicamente i post (routes/console.php)
use Illuminate\Support\Facades\Schedule;
Schedule::command('bluesky:collect-posts')->everyFiveMinutes();