跳转到主要内容

概览

Bluesky Facade 是 BlueskyManager 的一层薄封装。BlueskyManager 负责管理多个 Agent,而 HasShortHand trait 集中提供了一批常用方法的快捷方式。

架构

BlueskyServiceProvider 会将 Factory::class 以作用域单例(scoped singleton,每次请求一个实例)的形式注册为 BlueskyManager
// BlueskyServiceProvider::register()
$this->app->scoped(Factory::class, BlueskyManager::class);
Facade 的 getFacadeAccessor() 会解析这个绑定。
// Facades/Bluesky.php
protected static function getFacadeAccessor(): string
{
    return Factory::class;
}

BlueskyManager 的核心方法

BlueskyManager 自身定义的核心方法如下。将它们与 HasShortHand trait 提供的方法区分开来,有助于理解内部结构。

认证

方法说明
login(string $identifier, string $password, ?string $service = null)使用 App Password 认证并设置 LegacyAgent
withToken(?AbstractSession $token)传入 OAuthSessionLegacySession 来设置 Agent
check(): bool判断是否已认证(同时校验 token 是否有效)
refreshSession()刷新 token
logout()清除 Agent

Agent 操作

方法说明
agent(): ?Agent获取当前的 Agent
withAgent(?Agent $agent)直接设置 Agent
assertDid(): string返回已认证的 DID。未认证时抛出异常

HTTP 客户端

方法说明
client(bool $auth = true): AtpClient返回已认证(或匿名)的 XRPC 客户端
public(): BskyClient返回无需认证的公开端点客户端
send(BackedEnum|string $api, string $method, bool $auth, ?array $params, ?callable $callback)直接调用任意 AT Protocol API

工具方法

方法说明
identity(): Identity获取 DID 解析等身份服务
pds(): PDS获取 PDS 信息服务
entryway(?string $path): string返回服务 URL(例如 bsky.social
publicEndpoint(): string返回公开端点 URL

HasShortHand trait

HasShortHand trait 把 AT Protocol 的底层 API 包装成了对 PHP 更友好的方法。只要在 BlueskyManager 中写上 use HasShortHand;,这些方法就可以直接通过 Bluesky:: 调用。
// BlueskyManager.php
class BlueskyManager implements Factory
{
    use Conditionable;
    use HasShortHand;
    use Macroable;
    // ...
}
HasShortHand 拆成独立 trait,是为了便于测试和自定义。这样既能保持 BlueskyManager 代码简洁,又能提供丰富的 API 快捷方式。

发帖 / feed

方法说明
post(Post|string|array $text)创建新帖子
getPost(string $uri)通过 AT-URI 获取帖子
getPosts(array $uris)批量获取多个帖子
deletePost(string $uri)删除帖子
getTimeline(?string $algorithm, ?int $limit, ?string $cursor)获取首页时间线
getAuthorFeed(?string $actor, ...)获取指定账号的 feed
searchPosts(string $q, ...)搜索帖子

互动

方法说明
like(Like|StrongRef $subject)点赞
deleteLike(string $uri)取消点赞
repost(Repost|StrongRef $subject)转发
deleteRepost(string $uri)取消转发
getActorLikes(?string $actor, ...)获取账号的点赞列表

关注

方法说明
follow(Follow|string $did)关注
deleteFollow(string $uri)取消关注
getFollowers(?string $actor, ...)获取粉丝列表
getFollows(?string $actor, ...)获取关注中列表

个人资料 / 账号

方法说明
getProfile(?string $actor)获取个人资料
upsertProfile(callable $callback)更新个人资料
resolveHandle(string $handle)将 handle 解析为 DID

媒体

方法说明
uploadBlob(StreamInterface|string $data, string $type)上传图片等 blob
uploadVideo(StreamInterface|string $data, string $type)上传视频
getJobStatus(string $jobId)查看视频上传的任务状态
getUploadLimits()获取上传限制

通知

方法说明
listNotifications(...)获取通知列表
countUnreadNotifications(...)获取未读通知数
updateSeenNotifications(string $seenAt)更新已读状态

AT Protocol 记录操作

方法说明
createRecord(string $repo, string $collection, ...)创建 record
getRecord(string $repo, string $collection, string $rkey, ...)获取 record
listRecords(string $repo, string $collection, ...)获取 record 列表
putRecord(string $repo, string $collection, string $rkey, ...)更新或创建 record
deleteRecord(string $repo, string $collection, string $rkey, ...)删除 record

Feed generator 与 labeler

方法说明
publishFeedGenerator(BackedEnum|string $name, Generator $generator)发布 feed generator
createThreadGate(string $post, ?array $allow)创建 thread gate
upsertLabelDefinitions(callable $callback)更新 label 定义
deleteLabelDefinitions()删除 label 定义
createLabels(RepoRef|StrongRef|array $subject, array $labels)添加 label
deleteLabels(RepoRef|StrongRef|array $subject, array $labels)删除 label

常用快捷方式示例

发帖

use Revolution\Bluesky\Facades\Bluesky;

$response = Bluesky::login(
    identifier: config('bluesky.identifier'),
    password: config('bluesky.password'),
)->post('Hello Bluesky');

回复

回复时使用 Post::build() 设置父帖的 StrongRefHasShortHand 中没有专门的 reply() 方法,而是把 Post 对象传给 post()
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Record\Post;
use Revolution\Bluesky\Types\StrongRef;

$parent = StrongRef::to(uri: 'at://did:plc:.../app.bsky.feed.post/...', cid: 'bafyrei...');

$reply = Post::create('回复内容')
    ->reply(root: $parent, parent: $parent);

Bluesky::login(config('bluesky.identifier'), config('bluesky.password'))
    ->post($reply);

点赞与转发

use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Types\StrongRef;

$ref = StrongRef::to(uri: 'at://did:plc:.../app.bsky.feed.post/...', cid: 'bafyrei...');

// 点赞
Bluesky::withToken($session)->like($ref);

// 转发
Bluesky::withToken($session)->repost($ref);

更新个人资料

upsertProfile() 会获取当前的个人资料,然后保存在闭包中修改后的内容。你可以调用 Profile 对象的方法来设置 displayName、description 等。
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Record\Profile;

Bluesky::login(config('bluesky.identifier'), config('bluesky.password'))
    ->upsertProfile(function (Profile $profile) {
        $profile->displayName('新的显示名')
                ->description('个人简介');
    });

设置 self label

通过 SelfLabels,可以对整个账号设置 self label。
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Record\Profile;
use Revolution\Bluesky\Types\SelfLabels;

Bluesky::login(config('bluesky.identifier'), config('bluesky.password'))
    ->upsertProfile(function (Profile $profile) {
        $profile->labels(SelfLabels::make(['!no-unauthenticated']));
    });

直接调用任意 API

HasShortHand 中没有的方法,可以通过 send()client() 调用。
use Revolution\Bluesky\Facades\Bluesky;

// 用 send() 调用任意 XRPC 方法
$response = Bluesky::withToken($session)
    ->send(
        api: 'app.bsky.actor.getProfiles',
        method: 'get',
        params: ['actors' => ['did:plc:...', 'did:plc:...']],
    );

// 通过 client() 进行更细粒度的操作
$response = Bluesky::withToken($session)
    ->client()
    ->bsky()
    ->getProfiles(actors: ['did:plc:...']);

通过 Facade 和直接使用的区别

Bluesky::post()app(Factory::class)->post() 操作的是同一个 BlueskyManager 实例。
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Contracts\Factory;

// 通过 Facade(常规用法)
Bluesky::login(config('bluesky.identifier'), config('bluesky.password'))
    ->post('Hello');

// 从容器直接获取
$manager = app(Factory::class);
$manager->login(config('bluesky.identifier'), config('bluesky.password'))
        ->post('Hello');

// 通过类型提示进行 DI
class MyService
{
    public function __construct(private Factory $bluesky) {}

    public function doPost(): void
    {
        $this->bluesky->login(
            config('bluesky.identifier'),
            config('bluesky.password'),
        )->post('Hello from DI');
    }
}
由于使用了 scoped 注册,同一次请求内 login() 后的 session 状态会被保留。

Conditionable 与 Macroable

BlueskyManager 也使用了 ConditionableMacroable trait。

Conditionable: when() / unless()

use Revolution\Bluesky\Facades\Bluesky;

Bluesky::login(config('bluesky.identifier'), config('bluesky.password'))
    ->when(config('app.env') === 'production', function ($bluesky) {
        $bluesky->post('来自生产环境的帖子');
    });

Macroable:添加自定义方法

通过 macro(),你可以从应用侧向 BlueskyManager 添加方法。通常写在 AppServiceProviderboot() 中。
use Revolution\Bluesky\Facades\Bluesky;

Bluesky::macro('postWithHashtag', function (string $text, string $tag) {
    /** @var \Revolution\Bluesky\BlueskyManager $this */
    return $this->post("{$text} #{$tag}");
});

// 使用示例
Bluesky::login(config('bluesky.identifier'), config('bluesky.password'))
    ->postWithHashtag('Hello', 'laravel');

注入自定义 Agent

通过 withAgent(),可以直接设置任意的 Agent 实现。
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Contracts\Agent;

// 自定义 Agent 时需要实现 Agent 接口
class MyCustomAgent implements Agent
{
    // ...
}

Bluesky::withAgent(new MyCustomAgent());
通过常规认证(login() / withToken())会自动设置 agent,所以直接使用 withAgent() 的场合主要出现在测试中。

参考链接

最后修改于 2026年7月13日