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

# Tutorial de bots - Laravel Bluesky

> Versión en Laravel del tutorial oficial de bots de AT Protocol. Implementa bots de publicación, respuesta y etiquetado usando el paquete laravel-bluesky.

## Descripción general

Esta página es la versión en Laravel del [tutorial oficial de bots de AT Protocol](https://atproto.com/ja/guides/bot-tutorial). 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](https://github.com/invokable/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.

```mermaid theme={null}
graph LR
    A["Tutorial oficial<br>(TypeScript)"] -->|"Versión Laravel"| B["laravel-bluesky"]
    B --> C["Comandos Artisan"]
    B --> D["Trait HasShortHand"]
    B --> E["Programación de tareas<br>/ GitHub Actions"]
```

## 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](/es/packages/laravel-bluesky/index).

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

```bash theme={null}
php artisan make:command BotPostCommand
```

Edita el `app/Console/Commands/BotPostCommand.php` generado.

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

```bash theme={null}
php artisan bot:post
```

### Ejecución automática con programación de tareas

Añade la configuración de ejecución periódica en `routes/console.php`.

```php theme={null}
use Illuminate\Support\Facades\Schedule;

// Publica cada tres horas
Schedule::command('bot:post')->everyThreeHours();
```

Para activar el scheduler, configura lo siguiente en cron.

```bash theme={null}
* * * * * 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.

```yaml theme={null}
# .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 }}
```

<Tip>
  Registra `BLUESKY_IDENTIFIER` y `BLUESKY_APP_PASSWORD` como secrets de GitHub Actions.
</Tip>

***

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

```bash theme={null}
php artisan make:command BotReplyCommand
```

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

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

### Programar el sondeo

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

```bash theme={null}
composer require laravel/ai revolution/laravel-amazon-bedrock
```

Crea el agente de IA.

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

<Tip>
  Consulta la configuración de `laravel-amazon-bedrock` en el [driver de Amazon Bedrock](/es/packages/laravel-amazon-bedrock).
</Tip>

***

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

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

### Crear el comando del bot de etiquetas

```bash theme={null}
php artisan make:command BotLabelCommand
```

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

```bash theme={null}
php artisan bot:label
```

Ejecuta este comando una sola vez durante la configuración inicial del bot. La etiqueta se guarda de forma permanente en el perfil.

<Warning>
  `!bot` es una self label estándar de Bluesky. Se recomienda asignarla siempre en las cuentas de bots.
</Warning>

***

## 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](/es/packages/laravel-bluesky/feed-generator) se explica en detalle cómo implementar un feed generator personalizado con Laravel.

### Autenticación OAuth

En [Socialite](/es/packages/laravel-bluesky/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](https://github.com/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

* [Tutorial oficial de bots de AT Protocol (inglés)](https://atproto.com/guides/bot-tutorial)
* [Tutorial oficial de bots de AT Protocol (japonés)](https://atproto.com/ja/guides/bot-tutorial)
* [laravel-bluesky](https://github.com/invokable/laravel-bluesky)
* [BlueskyManager y HasShortHand](/es/packages/laravel-bluesky/bluesky-manager)
* [Canal de notificaciones](/es/packages/laravel-bluesky/notification)
* [Driver de Amazon Bedrock](/es/packages/laravel-amazon-bedrock)


## Related topics

- [Canal de notificaciones - Laravel Bluesky](/es/packages/laravel-bluesky/notification.md)
- [Comparativa de métodos de autenticación - Laravel Bluesky](/es/packages/laravel-bluesky/authentication.md)
- [Laravel Socialite (autenticación social)](/es/socialite.md)
- [Webhook / Bot - LINE SDK for Laravel](/es/packages/laravel-line-sdk/bot.md)
- [Laravel Bluesky](/es/packages/laravel-bluesky/index.md)
