Descripción general
Esta página es la versión en Laravel del tutorial oficial de bots de AT Protocol. Explica cómo implementar en PHP/Laravel, mediante el paquete laravel-bluesky, un contenido pensado originalmente para TypeScript.
En el tutorial oficial se descargan los archivos Lexicon en cada ocasión con el comando lex, pero laravel-bluesky ya incorpora previamente todos los Lexicon desde atproto-lexicon-contracts, así que ese paso no es necesario. Usando los comandos de Artisan y los métodos del trait HasShortHand, puedes realizar prácticamente todas las operaciones.
Requisitos previos
- Tener instalado el paquete
laravel-bluesky
- Disponer de una cuenta de Bluesky y una app password
Consulta el método de instalación en Laravel Bluesky.
BLUESKY_IDENTIFIER=yourbot.bsky.social
BLUESKY_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx
Parte 1: bot básico (publicación)
Crear un comando Artisan
Crea el comando para el bot con php artisan make:command.
php artisan make:command BotPostCommand
Edita el app/Console/Commands/BotPostCommand.php generado.
<?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.');
}
}
Ejecución manual
Ejecución automática con programación de tareas
Añade la configuración de ejecución periódica en routes/console.php.
use Illuminate\Support\Facades\Schedule;
// Publica cada tres horas
Schedule::command('bot:post')->everyThreeHours();
Para activar el scheduler, configura lo siguiente en cron.
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
Ejecución automática con GitHub Actions
Si no dispones de servidor, también puedes ejecutarlo automáticamente con GitHub Actions.
# .github/workflows/bot.yml
name: Bot Post
on:
schedule:
- cron: '0 */3 * * *' # cada tres horas
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 y BLUESKY_APP_PASSWORD como secrets de GitHub Actions.
Parte 2: bot de respuestas (monitorización de menciones)
Crearemos un bot que responde automáticamente a las menciones. También se incluye un ejemplo que usa IA para generar las respuestas.
Obtener notificaciones y responder a las menciones
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) {
// Procesa solo notificaciones de menciones
if (data_get($notification, 'reason') !== 'mention') {
continue;
}
// Omite las notificaciones ya leídas (las que tienen isRead a true ya se han procesado)
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('¡Hola! Gracias por la mención. 🙂')
->reply($reply);
Bluesky::post($post);
$this->info("Replied to: {$uri}");
}
// Marca las notificaciones como leídas
Bluesky::updateSeenNotifications(now()->toISOString());
}
}
Si la raíz del hilo es diferente, obtén la información del hilo con app.bsky.feed.getPostThread y configúralo en root. En la implementación simplificada se toma la publicación padre como raíz.
Programar el sondeo
// routes/console.php
use Illuminate\Support\Facades\Schedule;
Schedule::command('bot:reply')->everyFiveMinutes();
Generar respuestas usando IA
Combinando el paquete laravel/ai con el driver laravel-amazon-bedrock, puedes generar respuestas con IA según el contenido de la mención.
composer require laravel/ai revolution/laravel-amazon-bedrock
Crea el agente de IA.
<?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 'Eres un bot amigable que opera en Bluesky. '
. 'Genera una respuesta breve y cercana al mensaje del usuario. '
. 'La respuesta debe tener 200 caracteres o menos.';
}
}
Usa la respuesta de IA desde el 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 respuesta con IA
$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 de etiquetas
Es el equivalente al labelAsBot del tutorial oficial. Se aplica una self label al perfil de la cuenta del bot para indicar explícitamente que se trata de una cuenta automatizada.
El etiquetado se ejecuta una sola vez durante la configuración inicial. Se implementa como un comando aparte, independiente de los comandos de las partes 1 y 2.
Crear el comando del bot de etiquetas
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) {
// Aplica la etiqueta !bot
$profile->labels(SelfLabels::make(['!bot']));
});
$this->info('Bot label applied successfully.');
}
}
Ejecutar
Ejecuta este comando una sola vez durante la configuración inicial del bot. La etiqueta se guarda de forma permanente en el perfil.
!bot es una self label estándar de Bluesky. Se recomienda asignarla siempre en las cuentas de bots.
Sobre otros tutoriales oficiales
El tutorial oficial de AT Protocol incluye varios temas más. A continuación resumimos el estado de compatibilidad con laravel-bluesky.
Feed personalizado
En Feed Generator se explica en detalle cómo implementar un feed generator personalizado con Laravel.
Autenticación OAuth
En Socialite se explica cómo implementar la autenticación OAuth con Laravel Socialite. Es más sencilla que en el tutorial oficial.
Aplicación social (statusphere)
Está publicada una versión Laravel de statusphere en invokable/statusphere. Como se basa en un statusphere anterior que no usa los comandos lex ni tap, hay algunas diferencias respecto al tutorial oficial más reciente.
Enlaces de referencia