SwiftでBlueskyに投稿を行う

イーロン・マスクの買収以降、Twitterは何かと混乱していますが、日本ではそこまでの影響はないように思えます。

それでも一部の人たちはTwitterから新天地を求めて、いろいろなSNSを試している段階ではないでしょうか。

現在マイクロブログのトレンドは分散型で、サービスそのもので囲い込まずに、プロトコルを定義していろいろなサービス・サーバがつながっていく、という流れになっています。

主なプロトコルには、[Mastodon](https://joinmastodon.org/ja)・[Misskey](https://misskey.io/)などが採用するW3C標準の
[ActivityPub](https://www.w3.org/TR/activitypub/)、Twitterの創設者であるジャック・ドーシーが投資している[Nostr](https://nostr.com/)、そして今回の主題である[Bluesky](https://blueskyweb.xyz/)が採用する[AT Protocol](https://atproto.com/)などがあります。

一応これらのサービスを一通り試して見たのですが、現状1番面白く、居心地良いのはBlueskyです。

Blueskyはなぜか日本の開発者が活発に活動しており、Blueskyの世界初のミートアップは日本で開催されました。

[Bluesky Meetup in Tokyo - connpass](https://428lab.connpass.com/event/279572/)

またタイムラインにはクライアントやライブラリに関する投稿やボットによる投稿がどんどん流れてきます。

自分も開発者の端くれではあるので、そんな投稿に触発されてAT Protocolで軽く何か試してみようという気になりました。

そこで今回、単純にSwift PlaygroundからBlueskyへの投稿をするスクリプトを作ってみることにします。

##  参考にしたもの

[Bluesky](https://scrapbox.io/Bluesky/)

先ほど申し上げた通り、Blueskyは日本で活発に開発されており、有志による日本語ドキュメントが整備されています。

ただ、こちらのページを見ればAT Protocolがわかるという感じではありませんが、わからないことを検索するとこちらのページがヒットする、ということがままあります。

[AT Protocol (BlueSky Social)仕様解説 ～ W3C DID仕様を添えて ～ - Qiita](https://qiita.com/gpsnmeajp/items/eb665d639f088b85454e)

全体像を知る、という意味ではこちらのページがまとまっていてわかりやすかったです。

[Docs | AT Protocol](https://atproto.com/docs)

またAT Protocolの仕様自体は、やはりというか本家のドキュメントを参照するのが1番早いように思われます。

## Lexicon

[Lexicon - Bluesky](https://scrapbox.io/Bluesky/Lexicon)

AT protocolでできることは全てLexiconというJSONスキーマで定義されています。

上記ドキュメントではLexiconの詳細な定義が記載されていますが、Lexiconには有り体に言ってしまえば、サーバへのリクエスト方法とレスポンスのJSONのフォーマットが記載されています。

```json
{
  "lexicon": 1,
  "id": "com.atproto.server.createSession",
  "defs": {
    "main": {
      "type": "procedure",
      "description": "Create an authentication session.",
      "input": {
        "encoding": "application/json",
        "schema": {
          "type": "object",
          "required": [
            "identifier",
            "password"
          ],
          "properties": {
            "identifier": {
              "type": "string",
              "description": "Handle or other identifier supported by the server for the authenticating user."
            },
            "password": {
              "type": "string"
            }
          }
        }
      },
      "output": {
        "encoding": "application/json",
        "schema": {
          "type": "object",
          "required": [
            "accessJwt",
            "refreshJwt",
            "handle",
            "did"
          ],
          "properties": {
            "accessJwt": {
              "type": "string"
            },
            "refreshJwt": {
              "type": "string"
            },
            "handle": {
              "type": "string",
              "format": "handle"
            },
            "did": {
              "type": "string",
              "format": "did"
            },
            "email": {
              "type": "string"
            }
          }
        }
      },
      "errors": [
        {
          "name": "AccountTakedown"
        }
      ]
    }
  }
}
```

[com.atproto.server | AT Protocol](https://atproto.com/lexicons/com-atproto-server#comatprotoservercreatesession)

こちらはサーバとのセッションを確立するための`com.atproto.server.createSession`のLexiconになります。

`input`以下のオブジェクトがリクエストの際に必要なパラメータを記載しており、`output`以下がレスポンスで帰ってくるJSONのフォーマットが記載されています。

`"type": "procedure"`という部分は主にHTTPのメソッドに関わるもので、`procedure`の場合POSTメソッド、`query`の場合GETメソッドというような分類になります。[^1]

`type`が`procedure`の場合、POSTのHTTPBodyにJSONを渡す形でリクエストを行います。

```json
{"identifier":"(ハンドルもしくはDID)","password":"(パスワード)"}
```

するとこういったレスポンスが返ってきます。

```json
{"accessJwt":"(アクセストークン)","refreshJwt":"(更新用トークン?)","handle":"(ハンドル)","did":"(DID)","email":"(メルアド)"}
```

## XRPC

[XRPC - Bluesky](https://scrapbox.io/Bluesky/XRPC)

サーバへの問い合わせはXRPCというプロトコルで行われます。

上記記事を雑にまとめると、必要な場合はヘッダに認証情報を付加して、`/xrpc/{methodId}`にアクセスするという感じになります。

先ほどの`com.atproto.server.createSession`を例にとると、

```
https://bsky.social/xrpc/com.atproto.server.createSession
```

にPOSTすることになります。

分散型SNSですので、インスタンスによってドメインの部分は変わる場合がありますが、問い合わせ方法は仕様として決まっています。

# 投稿までの流れ

今回、スクリプトを作成するためにいろいろ調べている間に、「Blueskyへの投稿を行うシェルスクリプト」という身も蓋もないものを見つけてしまいました。

https://gist.github.com/pojntfx/72403066a96593c1ba8fd5df2b531f2d

こちらのスクリプトは

1. ハンドルで`com.atproto.identity.resolveHandle`に問い合わせ、ユーザーのDIDを取得
2. DIDとパスワードで`com.atproto.server.createSession`に問い合わせ、セッション確立し、アクセストークンを取得
3. アクセストークンで`com.atproto.repo.createRecord`に問い合わせ、投稿

という流れになっています。

ただ、現状2はDIDでなくハンドルでアクセストークンを取得できるので、1はスキップできるようです。

# ソースコード

そして今回作成したスクリプトがこちらになります。

https://gist.github.com/HonmaMasaru/08cb36f1a0a2e550a4588b4c089f4b7b

-----------

Mac専用にはなりますが、ショートカットアプリに組み込んだりできるので、オートメーションなどとも組み合わせて色々やれそうです。

![スクリーンショット 2023-05-09 5.19.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2763088/59e5be22-dc36-0bec-0916-796139353990.png)

それで何をやるかは特に思いつきませんが、投稿ができただけで割と満足です。

[^1]: [XRPC - Bluesky](https://scrapbox.io/Bluesky/XRPC)のRequestsの項目を参照