【PHP】ライブラリ無しでBluesky API自動投稿システムの構築(cURL)

PHP

Blueskyの公式ドキュメントにはTypeScriptやPythonの例は豊富ですが、PHPの解説は極めて少ないのが現状です。本記事では、公式のHTTP API (CURL) 仕様をベースに、PHP標準のcurl関数だけでアイキャッチ画像付きリンクカードを投稿する方法を詳しく解説します。


2. Bluesky API (AT Protocol) の仕組み

Blueskyの投稿(Record)は、単なるテキスト送信ではなく、以下の構造を組み合わせてHTTP POSTリクエストを送ることで成立します。

  1. 認証: com.atproto.server.createSession でJWTトークンを取得。

  2. 画像アップロード: com.atproto.repo.uploadBlob で画像をバイナリ送信し、blob オブジェクト(CID)を取得。

  3. 投稿作成: com.atproto.repo.createRecord で、テキストと上記で得た画像データを紐づけて送信。


3. 実装サンプルコード:公式仕様のPHP再現

3.1 認証セッションの作成

公式の com.atproto.server.createSession 仕様に基づき、PHPでログイン処理を実装します。

/**
* step 1: 認証セッションを作成し、トークンを取得
*/
function getBlueskySession($handle, $appPassword) {
$url = 'https://bsky.social/xrpc/com.atproto.server.createSession';
$postData = ['identifier' => $handle, 'password' => $appPassword];

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postData));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);

$response = json_decode(curl_exec($ch), true);
curl_close($ch);

return $response; // 'accessJwt' と 'did' を取得
}

3.2 アイキャッチ画像のアップロード

リンクカードに表示する画像は、事前に専用エンドポイントへバイナリ形式でPOSTし、識別子(blob)を受け取る必要があります。

/**
 * step 2: 画像バイナリをアップロードして識別子(blob)を取得
 */
function uploadBlob($token, $imageBinary, $mimeType = 'image/jpeg') {
    $url = 'https://bsky.social/xrpc/com.atproto.repo.uploadBlob';

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $imageBinary);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        "Authorization: Bearer $token",
        "Content-Type: $mimeType"
    ]);

    $response = json_decode(curl_exec($ch), true);
    curl_close($ch);

    return $res['blob'] ?? null;
}

3.3 リンクカード付き投稿の作成

最後に、リンク先のURL・タイトル・説明文・画像(blob)を embed パラメータに含めて送信します。

/**
 * step 3: リンクカード付き投稿の実行
 */
function createLinkCardPost($token, $did, $text, $cardData) {
    $url = 'https://bsky.social/xrpc/com.atproto.repo.createRecord';

    $payload = [
        'repo' => $did,
        'collection' => 'app.bsky.feed.post',
        'record' => [
            '$type' => 'app.bsky.feed.post',
            'text' => $text,
            'createdAt' => date('c'),
            'embed' => [
                '$type' => 'app.bsky.embed.external',
                'external' => [
                    'uri' => $cardData['uri'],
                    'title' => $cardData['title'],
                    'description' => $cardData['description'],
                    'thumb' => $cardData['thumb'] // uploadBlobで取得したblob
                ]
            ]
        ]
    ];

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        "Authorization: Bearer $token",
        "Content-Type: application/json"
    ]);

    $response = curl_exec($ch);
    curl_close($ch);

    return json_decode($response, true);
}

4. 開発における重要なTIPS

4.1 URLを青文字リンクにする「Facets」

Blueskyでは、本文にURLを記載しただけではリンクになりません。公式仕様では facets という仕組みを使い、**「バイト単位」**での位置指定を求めています。日本語混在時は mb_strlen ではなく strlen を用いて、URLが始まるバイト位置を正確に指定してください。

4.2 サーバー側のタイムゾーン設定

APIは createdAt フィールドにISO 8601形式の時刻を求めます。PHP環境のデフォルトがUTCでない場合、投稿順序に影響が出る可能性があるため、必ず date_default_timezone_set('Asia/Tokyo') 等で時刻を制御しましょう。


5. まとめ

公式SDKがないPHPでも、CURLリファレンス通りにHTTPリクエストを構成すれば、自由度の高いボットが作成可能です。特にリンクカードの実装は、OGP情報の取得と組み合わせることで、ブログ更新通知などの自動化に強力な威力を発揮します。

参考記事

Get Started | Bluesky
Make your first post to the Bluesky app via the API in under 5 minutes.

コメント

タイトルとURLをコピーしました