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

# Bottutorial - Laravel Bluesky

> De Laravel-versie van de officiële AT Protocol-bottutorial. We implementeren een postbot, replybot en labelbot met het laravel-bluesky-pakket.

## Overzicht

Deze pagina is de Laravel-versie van de [officiële AT Protocol-bottutorial](https://atproto.com/ja/guides/bot-tutorial). We laten zien hoe je de op TypeScript gerichte inhoud implementeert in PHP/Laravel met het `laravel-bluesky`-pakket.

In de officiële tutorial download je met het `lex`-commando telkens Lexicon-bestanden, maar `laravel-bluesky` heeft alle Lexicons al vooraf opgenomen via [atproto-lexicon-contracts](https://github.com/invokable/atproto-lexicon-contracts), dus die stap is niet nodig. Met Artisan-commando's en de methodes van de `HasShortHand`-trait kun je vrijwel alle operaties uitvoeren.

```mermaid theme={null}
graph LR
    A["Officiële tutorial<br>(TypeScript)"] -->|"Laravel-versie"| B["laravel-bluesky"]
    B --> C["Artisan-commando's"]
    B --> D["HasShortHand-trait"]
    B --> E["Taakplanning<br>/ GitHub Actions"]
```

## Vereisten

* Het `laravel-bluesky`-pakket is geïnstalleerd
* Je hebt een Bluesky-account en een app password klaarstaan

Zie [Laravel Bluesky](/nl/packages/laravel-bluesky/index) voor de installatie-instructies.

```dotenv theme={null}
BLUESKY_IDENTIFIER=yourbot.bsky.social
BLUESKY_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx
```

***

## Deel 1: basisbot (posten)

### Een Artisan-commando maken

Maak met `php artisan make:command` een commando voor de bot.

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

Bewerk het gegenereerde bestand `app/Console/Commands/BotPostCommand.php`.

```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.');
    }
}
```

### Handmatig uitvoeren

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

### Automatisch uitvoeren via taakplanning

Voeg de configuratie voor periodieke uitvoering toe aan `routes/console.php`.

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

// Elke 3 uur posten
Schedule::command('bot:post')->everyThreeHours();
```

Om de scheduler te activeren stel je het volgende in via cron.

```bash theme={null}
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
```

### Automatisch uitvoeren met GitHub Actions

Heb je geen server, dan kun je ook automatisch uitvoeren via GitHub Actions.

```yaml theme={null}
# .github/workflows/bot.yml
name: Bot Post

on:
  schedule:
    - cron: '0 */3 * * *'  # Elke 3 uur
  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>
  Registreer `BLUESKY_IDENTIFIER` en `BLUESKY_APP_PASSWORD` als secrets in GitHub Actions.
</Tip>

***

## Deel 2: replybot (mentions monitoren)

We maken een bot die automatisch reageert op mentions. We laten ook een voorbeeld zien waarbij AI de reply genereert.

### Notificaties ophalen en op mentions reageren

```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) {
            // Alleen mention-notificaties verwerken
            if (data_get($notification, 'reason') !== 'mention') {
                continue;
            }

            // Gelezen notificaties overslaan (waar isRead true is, is al verwerkt)
            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('Hallo! Bedankt voor de mention. 🙂')
                ->reply($reply);

            Bluesky::post($post);

            $this->info("Replied to: {$uri}");
        }

        // Notificaties als gelezen markeren
        Bluesky::updateSeenNotifications(now()->toISOString());
    }
}
```

<Info>
  Als de root van de thread afwijkt, haal dan met `app.bsky.feed.getPostThread` de threadinformatie op en stel die in als `root`. In deze eenvoudige implementatie behandelen we de bovenliggende post als root.
</Info>

### Polling inplannen

```php theme={null}
// routes/console.php
use Illuminate\Support\Facades\Schedule;

Schedule::command('bot:reply')->everyFiveMinutes();
```

### Replies genereren met AI

Door het `laravel/ai`-pakket te combineren met de `laravel-amazon-bedrock`-driver kun je AI-replies genereren die zijn afgestemd op de inhoud van de mention.

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

Maak een AI-agent.

```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 'Je bent een vriendelijke bot die actief is op Bluesky. '
            . 'Genereer op berichten van gebruikers een korte, vriendelijke reply in het Nederlands. '
            . 'Houd de reply binnen 200 tekens.';
    }
}
```

Gebruik de AI-reply in het commando.

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

            // Reply genereren met 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());
    }
}
```

<Tip>
  Zie [Amazon Bedrock-driver](/nl/packages/laravel-amazon-bedrock) voor de configuratie van `laravel-amazon-bedrock`.
</Tip>

***

## Deel 3: labelbot

Dit komt overeen met `labelAsBot` uit de officiële tutorial. We kennen een zelflabel toe aan het profiel van het botaccount om duidelijk te maken dat het een geautomatiseerd account is.

<Info>
  Het labelen voer je één keer uit bij de eerste setup. We implementeren dit als een apart commando, onafhankelijk van de commando's uit deel 1 en 2.
</Info>

### Het labelbotcommando maken

```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) {
            // Het !bot-label instellen
            $profile->labels(SelfLabels::make(['!bot']));
        });

        $this->info('Bot label applied successfully.');
    }
}
```

### Uitvoeren

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

Voer dit commando slechts één keer uit bij de eerste setup van de bot. Het label wordt permanent opgeslagen in het profiel.

<Warning>
  `!bot` is een standaard zelflabel van Bluesky. Het wordt sterk aanbevolen om dit altijd in te stellen voor botaccounts.
</Warning>

***

## Over de andere officiële tutorials

De officiële AT Protocol-tutorials behandelen nog een aantal andere onderwerpen. Hier vind je een overzicht van de ondersteuning in `laravel-bluesky`.

### Custom feeds

In [Feed Generator](/nl/packages/laravel-bluesky/feed-generator) wordt uitgebreid uitgelegd hoe je een custom feedgenerator implementeert met Laravel.

### OAuth-authenticatie

In [Socialite](/nl/packages/laravel-bluesky/socialite) wordt uitgelegd hoe je OAuth-authenticatie implementeert met Laravel Socialite. Dat is eenvoudiger dan in de officiële tutorial.

### Sociale app (statusphere)

Er is een Laravel-versie van statusphere gepubliceerd als [invokable/statusphere](https://github.com/invokable/statusphere). Omdat deze gebaseerd is op een oudere statusphere die de commando's `lex` en `tap` niet gebruikt, wijkt hij op enkele punten af van de nieuwste officiële tutorial.

## Referenties

* [Officiële AT Protocol-bottutorial (Engels)](https://atproto.com/guides/bot-tutorial)
* [Officiële AT Protocol-bottutorial (Japans)](https://atproto.com/ja/guides/bot-tutorial)
* [laravel-bluesky](https://github.com/invokable/laravel-bluesky)
* [BlueskyManager en HasShortHand](/nl/packages/laravel-bluesky/bluesky-manager)
* [Notificatiekanaal](/nl/packages/laravel-bluesky/notification)
* [Amazon Bedrock-driver](/nl/packages/laravel-amazon-bedrock)


## Related topics

- [Laravel Bluesky](/nl/packages/laravel-bluesky/index.md)
- [Socialite - Laravel Bluesky](/nl/packages/laravel-bluesky/socialite.md)
- [Notificatiekanaal - Laravel Bluesky](/nl/packages/laravel-bluesky/notification.md)
- [Basic client - Laravel Bluesky](/nl/packages/laravel-bluesky/basic-client.md)
- [Vergelijking van authenticatiemethodes - Laravel Bluesky](/nl/packages/laravel-bluesky/authentication.md)
