Blueskyの公式ドキュメントにはTypeScriptやPythonの例は豊富ですが、PHPの解説は極めて少ないのが現状です。本記事では、公式のHTTP API (CURL) 仕様をベースに、PHP標準のcurl関数だけでアイキャッチ画像付きリンクカードを投稿する方法を詳しく解説します。
2. Bluesky API (AT Protocol) の仕組み
Blueskyの投稿(Record)は、単なるテキスト送信ではなく、以下の構造を組み合わせてHTTP POSTリクエストを送ることで成立します。
-
認証:
com.atproto.server.createSessionでJWTトークンを取得。 -
画像アップロード:
com.atproto.repo.uploadBlobで画像をバイナリ送信し、blobオブジェクト(CID)を取得。 -
投稿作成:
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情報の取得と組み合わせることで、ブログ更新通知などの自動化に強力な威力を発揮します。
参考記事



コメント