Panoramica
Questa pagina è la versione Laravel del tutorial ufficiale per bot di AT Protocol. Spiega come implementare in PHP/Laravel, tramite il pacchetto laravel-bluesky, i contenuti pensati per TypeScript.
Nel tutorial ufficiale il file Lexicon viene scaricato di volta in volta con il comando lex, ma in laravel-bluesky tutti i Lexicon sono già inclusi in anticipo tramite atproto-lexicon-contracts, quindi questo passaggio non è necessario. Con i comandi Artisan e i metodi del trait HasShortHand puoi realizzare praticamente tutte le operazioni.
Prerequisiti
- Il pacchetto
laravel-bluesky è già installato
- Devi avere un account Bluesky e una app password
Per le istruzioni di installazione consulta Laravel Bluesky.
BLUESKY_IDENTIFIER=yourbot.bsky.social
BLUESKY_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx
Parte 1: bot base (post)
Creare un comando Artisan
Crea un comando per il bot con php artisan make:command.
php artisan make:command BotPostCommand
Modifica il file generato app/Console/Commands/BotPostCommand.php.
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Revolution\Bluesky\Facades\Bluesky;
class BotPostCommand extends Command
{
protected $signature = 'bot:post';
protected $description = 'Post to Bluesky';
public function handle(): void
{
Bluesky::login(
identifier: config('bluesky.identifier'),
password: config('bluesky.password'),
)->post('🙂');
$this->info('Posted successfully.');
}
}
Esecuzione manuale
Esecuzione automatica con il task scheduler
Aggiungi le impostazioni per l’esecuzione periodica in routes/console.php.
use Illuminate\Support\Facades\Schedule;
// Post ogni 3 ore
Schedule::command('bot:post')->everyThreeHours();
Per abilitare lo scheduler, imposta il seguente cron.
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
Esecuzione automatica con GitHub Actions
Se non hai un server, puoi comunque eseguirlo automaticamente con GitHub Actions.
# .github/workflows/bot.yml
name: Bot Post
on:
schedule:
- cron: '0 */3 * * *' # ogni 3 ore
workflow_dispatch:
jobs:
post:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
- run: composer install --no-dev --optimize-autoloader
- run: php artisan bot:post
env:
BLUESKY_IDENTIFIER: ${{ secrets.BLUESKY_IDENTIFIER }}
BLUESKY_APP_PASSWORD: ${{ secrets.BLUESKY_APP_PASSWORD }}
Registra BLUESKY_IDENTIFIER e BLUESKY_APP_PASSWORD nei secret di GitHub Actions.
Parte 2: bot di risposta (monitoraggio delle menzioni)
Creiamo un bot che risponde automaticamente alle menzioni. Vediamo anche un esempio in cui la risposta viene generata da un’AI.
Recuperare le notifiche e rispondere alle menzioni
php artisan make:command BotReplyCommand
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Record\Post;
use Revolution\Bluesky\Types\ReplyRef;
use Revolution\Bluesky\Types\StrongRef;
class BotReplyCommand extends Command
{
protected $signature = 'bot:reply';
protected $description = 'Reply to mentions on Bluesky';
public function handle(): void
{
Bluesky::login(
identifier: config('bluesky.identifier'),
password: config('bluesky.password'),
);
$notifications = Bluesky::listNotifications(limit: 20)->json('notifications', []);
foreach ($notifications as $notification) {
// Elabora solo le notifiche di menzione
if (data_get($notification, 'reason') !== 'mention') {
continue;
}
// Salta le notifiche già lette (isRead a true sono già elaborate)
if (data_get($notification, 'isRead')) {
continue;
}
$uri = data_get($notification, 'uri');
$cid = data_get($notification, 'cid');
if (! $uri || ! $cid) {
continue;
}
$ref = StrongRef::to(uri: $uri, cid: $cid);
$reply = ReplyRef::to(root: $ref, parent: $ref);
$post = Post::create('Ciao! Grazie per la menzione. 🙂')
->reply($reply);
Bluesky::post($post);
$this->info("Replied to: {$uri}");
}
// Segna le notifiche come lette
Bluesky::updateSeenNotifications(now()->toISOString());
}
}
Se il root del thread è diverso, recupera le informazioni del thread con app.bsky.feed.getPostThread e impostalo come root. In questa implementazione semplificata, il post padre viene usato come root.
Schedulare il polling
// routes/console.php
use Illuminate\Support\Facades\Schedule;
Schedule::command('bot:reply')->everyFiveMinutes();
Generare la risposta con l’AI
Combinando il pacchetto laravel/ai con il driver laravel-amazon-bedrock, puoi generare risposte AI adattate al contenuto della menzione.
composer require laravel/ai revolution/laravel-amazon-bedrock
Crea un agente AI.
<?php
namespace App\Ai\Agents;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Promptable;
class BotReplyAgent implements Agent
{
use Promptable;
public function instructions(): string
{
return 'Sei un bot amichevole attivo su Bluesky.'
. ' Genera risposte brevi e cordiali in italiano ai messaggi degli utenti.'
. ' Le risposte devono essere entro 200 caratteri.';
}
}
Usa le risposte AI nel comando.
<?php
namespace App\Console\Commands;
use App\Ai\Agents\BotReplyAgent;
use Illuminate\Console\Command;
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Record\Post;
use Revolution\Bluesky\Types\ReplyRef;
use Revolution\Bluesky\Types\StrongRef;
class BotReplyCommand extends Command
{
protected $signature = 'bot:reply';
protected $description = 'Reply to mentions on Bluesky using AI';
public function handle(): void
{
Bluesky::login(
identifier: config('bluesky.identifier'),
password: config('bluesky.password'),
);
$notifications = Bluesky::listNotifications(limit: 20)->json('notifications', []);
foreach ($notifications as $notification) {
if (data_get($notification, 'reason') !== 'mention') {
continue;
}
if (data_get($notification, 'isRead')) {
continue;
}
$uri = data_get($notification, 'uri');
$cid = data_get($notification, 'cid');
$mentionText = data_get($notification, 'record.text', '');
if (! $uri || ! $cid) {
continue;
}
// Genera la risposta con l'AI
$replyText = (new BotReplyAgent)->prompt($mentionText)->text;
$ref = StrongRef::to(uri: $uri, cid: $cid);
$reply = ReplyRef::to(root: $ref, parent: $ref);
$post = Post::create($replyText)->reply($reply);
Bluesky::post($post);
$this->info("AI replied to: {$uri}");
}
Bluesky::updateSeenNotifications(now()->toISOString());
}
}
Parte 3: bot per label
È la funzionalità equivalente a labelAsBot del tutorial ufficiale. Applica una self-label al profilo dell’account bot per dichiarare esplicitamente che si tratta di un account automatizzato.
L’applicazione della label va eseguita una sola volta, in fase di configurazione iniziale. Va implementata come comando separato dai comandi della Parte 1 e 2.
Creare il comando del bot per label
php artisan make:command BotLabelCommand
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Record\Profile;
use Revolution\Bluesky\Types\SelfLabels;
class BotLabelCommand extends Command
{
protected $signature = 'bot:label';
protected $description = 'Add bot self-label to the Bluesky profile';
public function handle(): void
{
Bluesky::login(
identifier: config('bluesky.identifier'),
password: config('bluesky.password'),
)->upsertProfile(function (Profile $profile) {
// Imposta la label !bot
$profile->labels(SelfLabels::make(['!bot']));
});
$this->info('Bot label applied successfully.');
}
}
Esecuzione
Esegui questo comando una sola volta, durante la configurazione iniziale del bot. La label viene salvata in modo persistente nel profilo.
!bot è una self-label standard di Bluesky. Se ne consiglia sempre l’impostazione per gli account bot.
Sugli altri tutorial ufficiali
Il tutorial ufficiale di AT Protocol tratta anche altri argomenti. Ecco lo stato del supporto in laravel-bluesky.
Feed personalizzati
In Feed Generator trovi la spiegazione dettagliata su come implementare un feed generator personalizzato con Laravel.
Autenticazione OAuth
In Socialite trovi la spiegazione su come implementare l’autenticazione OAuth con Laravel Socialite. È più semplice da implementare rispetto al tutorial ufficiale.
App sociale (statusphere)
La versione Laravel di statusphere è pubblicata come invokable/statusphere. È basata su una versione più vecchia di statusphere che non usava i comandi lex o tap, quindi presenta lievi differenze rispetto al tutorial ufficiale più recente.
Link di riferimento