跳转到主要内容

概览

本页是 AT Protocol 官方 bot 教程 的 Laravel 版。介绍如何用 laravel-bluesky 包,通过 PHP/Laravel 实现原本面向 TypeScript 的内容。 官方教程会通过 lex 命令每次下载 Lexicon 文件,但 laravel-bluesky 已经从 atproto-lexicon-contracts 事先导入了全部 Lexicon,所以不需要这一步。通过 Artisan 命令和 HasShortHand trait 的方法,几乎可以完成所有操作。

前置条件

  • 已经安装 laravel-bluesky
  • 已经准备好 Bluesky 账号和 App Password
安装方法请参考 Laravel Bluesky
BLUESKY_IDENTIFIER=yourbot.bsky.social
BLUESKY_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx

第 1 部分:基础 bot(发帖)

创建 Artisan 命令

使用 php artisan make:command 创建 bot 用的命令。
php artisan make:command BotPostCommand
编辑生成的 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.');
    }
}

手动执行

php artisan bot:post

使用定时任务自动执行

routes/console.php 中添加定时执行的设置。
use Illuminate\Support\Facades\Schedule;

// 每 3 小时发一次
Schedule::command('bot:post')->everyThreeHours();
要启用调度器,请在 cron 中配置以下条目。
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1

用 GitHub Actions 自动执行

如果没有服务器,也可以用 GitHub Actions 自动执行。
# .github/workflows/bot.yml
name: Bot Post

on:
  schedule:
    - cron: '0 */3 * * *'  # 每 3 小时
  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 }}
请把 BLUESKY_IDENTIFIERBLUESKY_APP_PASSWORD 注册到 GitHub Actions 的 secrets 中。

第 2 部分:回复 bot(监听 mention)

创建一个对 mention 自动回复的 bot。同时也介绍使用 AI 生成回复的示例。

获取通知并对 mention 回复

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) {
            // 只处理 mention 类型的通知
            if (data_get($notification, 'reason') !== 'mention') {
                continue;
            }

            // 跳过已读通知(isRead 为 true 的表示已处理)
            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('你好!感谢你的 mention。🙂')
                ->reply($reply);

            Bluesky::post($post);

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

        // 将通知标记为已读
        Bluesky::updateSeenNotifications(now()->toISOString());
    }
}
如果 thread 的 root 与父帖不同,请通过 app.bsky.feed.getPostThread 获取 thread 信息并设置到 root。这里的简易实现直接把父帖当作 root。

定时轮询

// routes/console.php
use Illuminate\Support\Facades\Schedule;

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

用 AI 生成回复

结合 laravel/ai 包和 laravel-amazon-bedrock 驱动,可以根据 mention 内容生成 AI 回复。
composer require laravel/ai revolution/laravel-amazon-bedrock
创建 AI agent。
<?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 '你是一个在 Bluesky 上活动的友好 bot。'
            . '请对用户的消息用中文生成简短且亲切的回复。'
            . '回复请控制在 200 字以内。';
    }
}
在命令中使用 AI 回复。
<?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;
            }

            // 用 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());
    }
}
laravel-amazon-bedrock 的配置方法请参考 Amazon Bedrock 驱动

第 3 部分:label bot

对应官方教程中的 labelAsBot。给 bot 账号的个人资料添加 self label,明确表明这是一个自动化账号。
label 只需要在初次设置时执行一次。它作为独立于第 1、2 部分的命令来实现。

创建 label bot 命令

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

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

执行

php artisan bot:label
请在 bot 初次设置时执行一次即可。label 会持久保存在个人资料中。
!bot 是 Bluesky 的标准 self label。官方推荐所有 bot 账号都应设置。

其他官方教程

AT Protocol 的官方教程还有其他几个主题。以下是 laravel-bluesky 的对应情况。

自定义 feed

Feed Generator 中详细介绍了用 Laravel 实现自定义 feed generator 的方法。

OAuth 认证

Socialite 中介绍了如何用 Laravel Socialite 实现 OAuth 认证。比官方教程更易于实现。

社交应用(statusphere)

Laravel 版的 statusphere 以 invokable/statusphere 公开发布。它基于尚未使用 lextap 命令的旧版 statusphere,因此与最新官方教程略有差异。

参考链接

最后修改于 2026年7月13日