Site icon image usounds

usoundsの日常や技術的なメモを残すブログです

Blueskyの自己表現ラベラーの立ち上げ手順

Blueskyのラベラーを既存のライブラリを使って作ってみました。

実運用1時間なので、もしかしたら一部上手く動かないかもしれません&試行錯誤したので、通しで実施すると不整合があるかもしれません。

🚨
不整合あったので2月9日ごろに諸々修正しました

対象

  • Blueskyでラベラーを作りたい方
  • TypeScriptでのコーディングに抵抗のない方
  • 下記の条件を満たすサーバーをお持ちの方
    • Linuxかmac(Windowsは諸々の理由で動きません)
    • 外部からHTTPS通信可能であること
    • WebSocket通信(In/Out)が可能であること
    • 常時起動していること(サーバーレスファンクションやApp Run系のオンデマンドはNG)

現実、VPSに置くか自宅サーバーに置くかなどになると思います。さくらのクラウドかVPS、ConoHa VPS、WebARENA VPSクラウドなど。

私は自宅サーバーにCloudflare Tunnelを使用することでこの条件を満たしています。他のトンネルソリューションはHTTPS通信可能なもの、VPSに平置きする場合はCaddyなどでHTTPS通信できる様にしてください。

なお、今回は私も使ったことがないSkywareのラベラーパッケージを活用したものを使用してみました。

公式のOzoneを使う場合は公式の英語のドキュメントが一番手っ取り早いのでそちらをご参照下さい。

https://github.com/bluesky-social/ozone/blob/main/HOSTING.md

出来るようになること

  • Blueskyにラベラーを作ることが出来る
  • 自己表現のためのラベルを、特定の投稿に「いいね」すると、そのアカウントに「ラベル」を張ることが出来る
  • 特定の投稿を「いいね」すると、すでに張られたラベルを削除出来る

手順

Step 1:新規Blueskyアカウント登録

ラベラーはその実態はBlueskyのアカウントです。よって、新しいラベラー用のアカウントの登録が必要です。とりあえず公式でも独自PDSでもどこでもよいのでアカウントを新しく作ります。今回はテストであることがわかりやすいように「labeltest20250208.bsky.social」を作りました。

作った後は、Eメールの認証をしておきます。後ろの手順でラベラーアカウントからポストする必要があり、今のBlueskyはポストするにはEメールの認証が必要です。

Step 2:ラベラーの準備

稼働するサーバーで下記を行います

コマンドを見ると、bunxを使うようですので、使っていない場合はインストールします。OSによって異なりますので公式サイトを参照しましょう。

この後CLIでの対話型のインターフェースが開始されます。事前に下記の情報をコピペできるように準備しておきます。

ラベラーに変化されるアカウントのハンドル:例:@labeltest20250208.bsky.social
そのアカウントの生パスワード:Step 1で作ったアカウントのパスワードです。
外部から呼び出し可能なホスト名:例:https://labelertest.usounds.work

この時点でこのサーバーが稼働していなくてもOKですが、HTTPS通信必須なのでIPアドレスはNGです

ディレクトリ上で下記を実行します

bun i
bunx @skyware/labeler setup

対話式で色々聞かれます

DID or handle of the account to use:

Step1で作成した新アカウントを指定します。Handleですが頭に「@」が必要のようです。「@labeltest20250208.bsky.social」のような形式で指定します

Account password (cannot be an app password):

Step1で作成した新アカウントの生パスワードを入力します(App Passwordは不可です)

URL of the PDS where the account is located

Step1で作成した新アカウントを公式PDSの場合はそのままEnterします。セルフホストPDSで作成した場合は、「https://pds.usounds.work」の様に指定します。

You will receive a confirmation code via email. Code:

メールボックスを見ると、「PLC Update Operation Requested」という件名のメールが来ているはずです。このメールに記載のあるコードを入力します。

💡
メールは即時に届かない場合がありますので、このCLIを中断せずにそのまま我慢して待ちます。
URL where the labeler will be hosted:

外部に公開するラベラーのホスト名を入力します。この時点で稼働していなくてもオッケーです。私は「https://labelertest.usounds.work」としました

Enter a signing key to use, or leave blank to generate a new one:

初期構築時はそのままEnterします。すると下記のようなメッセージが表示されます

This is your labeler's signing key. It will be needed to sign any labels you create. You will not be able to retrieve this key again, so make sure to save it somewhere safe. If you lose this key, you can run this again to generate a new one.
Signing key: zzzzzzzzzz

Signing key後続の作業で必要となるので保存しておきましょう。英語で書かれている通り、このSigning Keyはパスワードと同じ様に厳重に、かつ、他人から見えない様に保存しておきましょう。

Have you saved the signing key and are you ready to begin defining labels?

「Y」と入力すると、下記のようなメッセージが表示されます

🚨
初出時「N」としておりましたが、正しくは「Y」です
Next, you will need to define a name, description, and settings for each of the labels you want this labeler to apply.
Enter the details for the next label you would like this labeler to apply.
Press Esc or Ctrl+C to exit at any time with the labels defined so far.

ラベルの定義は後続の作業で行うので、EscキーかCtrl+Cで中断します

No labels were defined. You can use the label add command later to define new labels.
Labeler setup complete!

これで対話型CLIは完了です。これでBlueskyの世界にあなたのラベラーアカウントが誕生しました。

この時点では、Blueskyアプリ上ではラベラーとしては見えません。代わりに、PDSlsというツールで見てみましょう。

https://pdsls.dev/at/labeltest20250208.bsky.social

💡
URLの最後は、あなたのラベラーのハンドルに変更してください。

すると、Serviceのところに#atproto_labeler、Verification Methodに#atproto_labelが追加されているはずです。現時点ではこの様になっていれば問題ありません

Image in a image block
🚨
初出時、この時点でBlueskyアプリ上でラベラー扱いになると記載しておりましたが、正しくはPDSls上でのみ確認できるが正しいです

Step 3:.envの修正

ラベラーのサーバー側の設定を行います。.envを作る必要があります。これは「.env.example」をコピペして作りますが、事前に下記の情報を収集しましょう。

ラベラーアカウント自体のDID:下記のURLで取得できます。最後の引数はラベラーアカウントのハンドルに変更するようにしてください
https://api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=labeltest20250208.bsky.social
ラベラーアカウントのApp Password:ここから先はApp Passwordで作業できますので、このタイミングでApp Passwordを作ります。

設定すると下記の様になります。METRICS_PORT、FIREHOSE_URL、CURSOR_UPDATE_INTERVAL以外は全て更新します。ぶつかり難い4100がデフォルトに指定されているので大丈夫かと思いますが、他のプロセスとダブっている場合は修正してください。METRICS_PORTはGrafana用のポートで、このラベラーの稼働状況がわかる様になるそうです(使っていない)

DID=<ラベラーアカウント自体のDID>
SIGNING_KEY=<Step 2でコピペしたSigned Key>
BSKY_IDENTIFIER=<Step 1で作成したラベラーアカウントのHandleを@無しで>
BSKY_PASSWORD=<直前に作ったApp Password>
HOST=<稼働ホストのIPアドレス>
PORT=4100
METRICS_PORT=4101
FIREHOSE_URL=wss://jetstream2.us-west.bsky.network/subscribe
CURSOR_UPDATE_INTERVAL=10000

私は下記の様になりました

DID=did:plc:mcbzwxukyz2t4urlfhg5n6b2
SIGNING_KEY=zzzzzzzzzzz
BSKY_IDENTIFIER=labeltest20250208.bsky.social
BSKY_PASSWORD=your-app-pass-word
HOST=127.0.0.1
PORT=4100
METRICS_PORT=4101
FIREHOSE_URL=wss://jetstream2.us-west.bsky.network/subscribe
CURSOR_UPDATE_INTERVAL=10000
.env

Step 4:ラベル定義の作成と発行

src/constants.tsにラベルの定義を行う箇所があります。Label[]を下記の様に変更します。

export const LABELS: Label[] = [
  {
    rkey: 'test1',
    identifier: 'first',
    locales: [
      { lang: 'en', name: 'テストラベル', description: 'てすとの説明だよ'},
    ]
  },
  {
    rkey: 'test2',
    identifier: 'second',
    locales: [
      { lang: 'en', name: '2個目', description: '2個目だよ'},
    ]
  },
]
src/constants.ts

rkeyはこの時点ではダミー値でいいようです。修正したら下記を実行します

bun set-posts

実行すると

WARNING: This will delete all posts in your profile. Are you sure you want to continue? (y/n)

と聞かれますので、yと回答します。実行すると自動でBlueskyにポストされます。

Image in a image block

CLIではそのrkeyがわかりますので、メモします。

Label rkeys:
name: 'テストラベル',
rkey: '3lhnwzyqcsx2a',
name: '2個目',
rkey: '3lhnwzyusrp2x',
Delete post rkey:
export const DELETE = '3lhnwzyzess2e';

rkeyがわかったので、src/constants.tsに反映します。前回修正していないDELETEの定数も変更します

export const DELETE = '3lhnwzyzess2e';
export const LABEL_LIMIT = 1;
export const LABELS: Label[] = [
  {
    rkey: '3lhnwzyqcsx2a',
    identifier: 'first',
    locales: [
      { lang: 'en', name: 'テストラベル', description: 'てすとの説明だよ'},
    ]
  },
  {
    rkey: '3lhnwzyusrp2x',
    identifier: 'second',
    locales: [
      { lang: 'en', name: '2個目', description: '2個目だよ'},
    ]
  },
]
src/constants.ts

修正したら、これをラベラーアカウントに反映します

bun set-labels

実行して、

[2025-02-08 19:55:29.041 +0900] INFO: Label definitions set successfully.

と表示されればOKです。改めてBlueskyのラベラーをBlueskyのアプリから見てみましょう。この2つのラベルが反映されているはずです。

Image in a image block

Step 5:サーバーを立ち上げる

下記のコマンドを実行します

bun run start

下記の様にMetrics、Labelerが起動して、Connected to Jetstreamまで見れれば成功です。

usounds@ubuntsu:/ext1/labeler-starter-kit-bsky$ bun run start
$ bun x tsx src/main.ts
[2025-02-08 19:58:48.593 +0900] INFO: Trying to read cursor from cursor.txt...
[2025-02-08 19:58:48.594 +0900] INFO: Cursor found: 1739007019775421 (2025-02-08T09:30:19.775Z)
[2025-02-08 19:58:48.615 +0900] INFO: Metrics server is listening on 127.0.0.1:4101
[2025-02-08 19:58:48.627 +0900] INFO: Labeler server listening on http://192.168.x.xxx:4100
[2025-02-08 19:58:49.077 +0900] INFO: Connected to Jetstream at wss://jetstream2.us-west.bsky.network/subscribe with cursor 1739007019775421 (2025-02-08T09:30:19.775Z)

今気づきましたが、私のLinuxサーバー名、Typoしてますね・・・

.envのHOSTの値でアクセスできますので、サーバーのIPアドレスで下記のエンドポイントを叩いて結果が返ってくるかをみましょう。

http://127.0.0.1/xrpc/com.atproto.label.queryLabels

正常にサーバーが起動していると

{"cursor":"0","labels":[]}

が返却されます。

この状態で、HTTPS通信を試します。私はCloudflare Tunnel使っていますので、Zero Trust上で下記の様にします。

Image in a image block

HTTPS経由でのアクセスを試します。

https://labelertest.usounds.work/xrpc/com.atproto.label.queryLabels

生IPと同じ様にエラーなく返却されたらOKです。

Step 6:動作を試す

ここまでの操作を行なっているとBlueskyのアプリは「ラベラーのアカウント」になっていると思います。これを「普段使っているアカウント」に切り替えます。合わせて必ずこのタイミングで「普段使っているアカウント」から「このラベラーアカウント」を「ラベラーを登録する」してください。

切り替えとラベラーを登録するが完了した状態で、「テストラベル」という投稿にいいねしてみましょう。

Image in a image block

いいねすると、ラベラーサーバー側に下記の様なログが表示されます

[2025-02-08 20:10:03.241 +0900] INFO: Received rkey: 3lhnwzyqcsx2a for did:plc:rgdcflm4ylsl6udghmtblydc
[2025-02-08 20:10:03.242 +0900] INFO: New label: first
[2025-02-08 20:10:03.286 +0900] INFO: Successfully labeled did:plc:rgdcflm4ylsl6udghmtblydc with first

なんかラベル貼れてるっぽいですね!

🚨
ログが出ていない場合は、絶対に先に進んではいけません

この状態で、もう一度APIを叩いておきましょう

https://labelertest.usounds.work/xrpc/com.atproto.label.queryLabels

今度は下記の様に結果が変わりました。

{"cursor":"1","labels":[{"id":1,"src":"did:plc:mcbzwxukyz2t4urlfhg5n6b2","uri":"did:plc:rgdcflm4ylsl6udghmtblydc","val":"first","neg":false,"cts":"2025-02-08T11:10:03.242Z","sig":{"$bytes":"IFUtJJQ+nLjk9qohfTf+eH0dhuY09nK1S+Zz/QlE3OQKxyuSUiewcz2gEFT6Zw7x5ec2isMmYYg8DhsHU0N1/w"},"ver":1}]}

私のメインアカウントであるusounds.workのDIDであるdid:plc:rgdcflm4ylsl6udghmtblydcに対して、firstというラベルが貼られたようです。

Blueskyの世界を見てみると、テストラベルが追加されてますね

Image in a image block
💡
ラベルが表示されない場合は、「メインアカウントからいいねをしているか」「メインアカウントからラベラーアカウントのラベラーを購読しているか」を確認しましょう

これで概ねの説明は終わりました。常時起動するのはそれぞれの状況に応じてpm2やsystemctlなどの方法で実施いただければ良いと思います。

終わりに

サーバー立ててTypeScriptがなんとなく理解できればどうにかなったかなと思います。いいねをする投稿を日本語にしたい場合は、src/set-posts.tsの

const post = await bot.post({
  text: 'Like the replies to this post to receive labels.',
  threadgate: { allowLists: [] },
});

〜中略〜

const deletePost = await bot.post({ text: 'Like this post to delete all labels.' });
src/set-posts.ts

を修正すればいいはずです。(修正後はset-postsから実施し直しになると思いますが試してみてください。おそらくはプロセスを落とした状態で実施した方がいい様に思えます)

ぜひあなたのクラスターにも自己表現できるラベラーを作ってみてください!