Bluesky に画像をポストするボットのコード

一般公開が始まった Bluesky、ユーザー数が一気に増えたので自動でポストを行うボットのニーズも増えてきたかと思います。いずれ誰かがもっと体系的な情報を整備すると思いますが、とりあえずボットを動かすことができたので、2024年2月時点でのポストを行うコードを公開しておこうと思います。ほんとに簡単です。

Japan EQ LocatorWorld EQ Locator では地震が発生するたびに、テキスト、サイトの URL、地震スクリーンショットの画像をポストしています。以下では、これらのデータはすでに準備ができているものとして、Bluesky へのポストの部分のみに注目します。なお、使用言語は Python です。

まず AT Protocol SDK atproto と画像ライブラリ Pillow をインストールしておきます。

pip install atproto Pillow

そして、コードの先頭でこれらのパッケージのクラスやモジュールをインポートします。なお、BytesIO は変換した画像のバッファとして使用します。

from atproto import Client, client_utils
from PIL import Image
from io import BytesIO

次に、AT Protocol クライアントを作成します。Client のコンストラクタの引数には base_url として 'https://bsky.social' を指定します。そして、自分の Bluesky ハンドル名とパスワードを使ってログインします。

client = Client(base_url='https://bsky.social')
client.login('<handle>.bsky.social', '<password>')

で、こちらがポストのテキストを用意する部分。text には投稿内容、url にはリンクが格納されている前提です。ポイントは、client_utils.TextBuilder を使ってテキストを組み立てている点です。X と異なり、Bluesky ではテキスト中の URL は自動ではリンクにならないため、client_utils.TextBuilder を使ってリンクや装飾などが付いたリッチテキストを組み立てる必要があります。

text = f'{year}年{month}月{day}日{hour}時{minute}分頃、' \
    f'{location}を震源とする地震がありました。' \
    f'震源の深さは{depth}、地震の規模は{magnitude}。'
url = 'https://nagix.github.io/japan-eq-locator/?' \
    + urllib.parse.urlencode(quake)

text_builder = client_utils.TextBuilder() \
    .text(text + '\n') \
    .link('nagix.github.io/japan-eq-loc...', url)

続いて画像を用意します。img_file に画像のパス名が入っている前提です。画像を未加工でポストするなら、ファイルをバイナリとして open して read() で返ってくる bytes オブジェクトを取得するだけて良いのですが、このケースでは読み込んだ PNG ファイルのサイズが Bluesky でポストできる画像のサイズの上限 1,000,000 バイトを超える恐れがあったため、Pillow を使って読み込み、RGB モードに変換(アルファ成分を除去)した上で JPEG としてバッファに書き出し、bytes オブジェクトを取得しています。

img_file = 'screenshot.png'

img = Image.open(img_file).convert('RGB')
with BytesIO() as f:
    img.save(f, format='JPEG')
    jpg = f.getvalue()

最後に、引数に組み立てたテキストデータ、画像データ、画像の代替文字列(ALT テキスト、ここではリンクを除いた本文を使用)、言語を指定して send_image() で送ります。

client.send_image(text_builder, jpg, text, langs=['ja'])

以上です!思ったより簡単ですね?もし詳しい SDK の解説を見たい方は、こちらのリファレンスが参考になると思います。

atproto.blue