Blueskyに投稿する(GAS使用+IFTTTとの連携)

画像を添付する

画像を添付する場合は当然画像のアップロード処理が必要になるのでPostするときのJSONを以下のように変更する。画像は最大4枚まで送れるのでimagesは配列。

{
  "repo": "DIDを指定",
  "collection": "app.bsky.feed.post",
  "record": {
    "text": "投稿文",
    "createdAt": "ISO 8601フォーマットの投稿日時",
    "facets": [],
    "embed": {
      "$type": "app.bsky.embed.images",
      "images": [],
    }
  }
}

ところが、com.atproto.repo.createRecordが受け付けているContent-Typeはapplication/jsonなのでこれで直接バイナリデータは送れない。

実際にrecord.embed.imagesに設定するべきデータは以下のようになっていて(altaspectRatioは任意)、

参照情報って何?と思うけど要するに「先に画像をアップロードして、その戻り値として参照情報(文字列型)を受け取れるので、それを設定する」という流れで処理をしなければならない。

なのでまずはcom.atproto.repo.createRecordで送る前にcom.atproto.repo.uploadBlobを使用して画像をアップロードする。

※ 以前は以下の仕様でしたが、2024/2/22頃から前述の新仕様でないとエラーになるように変更されたようです。

画像をアップロードする

今回作成するWebサービスは外部連携を想定していて、他サービスからの情報を受け取る際は画像に関してはURLで渡されてくるはずなので、「画像のURLを受け取ってそれをBLOBとして取得し、uploadBlobに送信する」ような処理にした。

let imagesURLs = ["画像URL1", "画像URL2", "画像URL3", "画像URL4"]
let url = "https://bsky.social/xrpc/com.atproto.repo.uploadBlob";

let imagesArr: Object[] = [];
if (imagesURLs != null && 0 < imagesURLs.length) {
  for (let i = 0; i < imagesURLs.length && i < 4; ++i) {
    let imgURL = imagesURLs[i];

    let blob = UrlFetchApp.fetch(imageURL).getBlob();

    const options: GoogleAppsScript.URL_Fetch.URLFetchRequestOptions & { method: GoogleAppsScript.URL_Fetch.HttpMethod } = {
      "method": "post",
      "headers": {
        "Authorization": Bearer ${accessJwt}
      },
      "payload": blob,
    };

    let response = UrlFetchApp.fetch(url, options);
    let responseJSON = JSON.parse(response.getContentText());

    imagesArr.push({
      "image": {
        "$type": "blob",
        "ref": {
          "$link": responseJSON.blob.ref.$link
        },
        "mimeType": responseJSON.blob.mimeType,
        "size": responseJSON.blob.size
      },
      "alt": ""
    });
  }
}

投稿する

取得した画像参照情報をJSONに加えてPostする。

{
  "repo": did,
  "collection": "app.bsky.feed.post",
  "record": {
    "text": "New blog posts: https://nigauri.me/everyone-in-2023/",
    "createdAt": (new Date()).toISOString(),
    "facets": [
      {
        "index": {
            "byteStart": 16,
            "byteEnd": 52
        },
        "features": [
            {
                "$type": "app.bsky.richtext.facet#link",
                "uri": "https://nigauri.me/everyone-in-2023/"
            }
        ]
      }
    ],
    "embed": {
      "$type": "app.bsky.embed.images",
      "images": [
        {
          "image": {
            "$type": "blob",
            "ref": {
              "$link": "アップロードした画像の参照情報"
            },
            "mimeType": "画像のMIMEタイプ",
            "size": 画像のファイルサイズ
          },
          "alt": ""
        }
      ]
    },
  }
}

リンクカードを設定する

リンクカードを設定する場合は以下のように…

{
  "repo": "DIDを指定",
  "collection": "app.bsky.feed.post",
  "record": {
    "text": "投稿文",
    "createdAt": "ISO 8601フォーマットの投稿日時",
    "facets": [],
    "embed": {
      "$type": "app.bsky.embed.external",
      "external": {
        "uri": "URL",
        "title": "タイトル",
        "description": "説明文",
        "thumb": {
          "image": {
            "$type": "blob",
            "ref": {
              "$link": "アップロードした画像の参照情報"
            },
            "mimeType": "画像のMIMEタイプ",
            "size": 画像のファイルサイズ
          },
          "alt": "文字列"
        }
      }
    }
  }
}

あれ?record.embedは配列じゃないんだけど…画像とリンクカードを同時に設定したい場合はどうすれば?

答えは画像とリンクカードを同時には指定できない。なのでどっちかにしてください。

サムネイル画像のアップロード方法とかは普通の画像添付のやつと一緒。