やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

BlueSky API で投稿してみる Python編

 Pythonライブラリを使って試す。

成果物

前回まで

必要なもの

  • BlueSkyアカウント
    • ハンドル: ytyaru.bsky.social
    • アプリ用パスワード: (以下手順で作成する)
    • DID: ytyaru.bsky.social
  • Python
    • 仮想環境
      • atprotoライブラリ

アプリ用パスワードを生成する

  1. BlueSkyにアクセスする
  2. 設定プライバシーとセキュリティアプリパスワードアプリパスワードを追加
  3. 固有名を入力せず次へ
  4. アプリパスワードをコピーしてどこかにメモる
このアプリパスワードに固有の名前を入力するか、ランダムに生成された名前を使用してください。
☑ダイレクトメッセージへのアクセスを許可

 ダイレクトメッセージへのアクセスを許可は必要ないと思うけど一応。

アプリパスワードをお知らせします!
ハンドルと一緒にこのパスワードを使用して、他のアプリにサインインします。
セキュリティ上の理由から、これを再度表示することはできません。このアプリパスワードを紛失した場合は、新しいものを生成する必要があります。

 パスワードはメモること!

情報源

 CURL, Python, TypeScript, Dart, Goで実行可能らしい。JavaScriptは?

Pythonライブラリ

インストール

pip(エラー)

 Docs get-startedの最初にある以下コマンドを入力したがエラー。

$ pip install atproto
error: externally-managed-environment

 ラズパイ4B bookworm 2025-05-13版では pipで何かをインストールしようとすると上記エラーが出る。

 なので次のような対処をする必要がある。(めんどくさい)

  • インストール対象がコマンドアプリのときはpipxでインストールする
  • インストール対象がPythonライブラリのときは仮想環境を作ってインストールする

 今回はPythonライブラリをインストールするので後者を選択する。

仮想環境

作成

python -m venv blueskyposttest

アクティベート

cd blueskyposttest/bin
chmod +x activate
./activate

 仮想環境ディレクトリのbin/配下にあるactivateコマンドを実行することで、仮想環境を有効化する。

 なぜか実行権限がついていなかったので、chmodコマンドで付与してから実行した。

 仮想環境がアクティベートされると、端末の行頭にアクティベートされた仮想環境の名前が出てくる。

(blueskyposttest) pi@raspberrypi:/tmp/work/blueskyposttest/bin $ 

ディアクティベート

 無効化するのは以下コマンド。

deactivate

pip

 仮想環境をアクティベートした状態でpipを実行する。

(blueskyposttest) pi@raspberrypi:/tmp/work/blueskyposttest/bin $ pip install atproto

 インストールできた!

依存関係

 パッケージの依存関係をファイルに出力しておく。すると後から一括でインストールできて便利。

書き出し

pip freeze > requirements.txt

 現在インストールされているパッケージの一覧をファイル出力する。

インストール

pip install -r requirements.txt

 所定のファイルに記録されたパッケージ一覧を全てインストールする。

テストコードを書く

最小

from atproto import Client

client = Client()
client.login('ハンドル', 'パスワード')

リッチ・テキスト

 atproto_client.utils.TextBuilderを見るとメンションなどの機能を使える。

関数 用途 引数
text 通常テキスト 投稿テキスト
tag ハッシュタグ 表記テキスト, ハッシュタグ
mention メンション(特定ユーザ) 表記テキスト, DID(分散ID)
link URL(任意テキスト表記) 表記テキスト, URL

 特にURLを任意テキストにできるのは嬉しい。
 でもこれ、文字数制限ってどうなるの?

  • 表記テキストの長さでカウント
  • URLの長さでカウント
  • 両方の長さでカウント

 わからんけど、多分両方だろう。

main.py

#!/usr/bin/env python3

import sys
from atproto import Client, client_utils

def main(handle, password) -> None:
    client = Client(base_url='https://bsky.social')
    client.login(handle, password)

    text_builder = client_utils.TextBuilder()
    text_builder.text('APIで投稿してみる。\n改行確認。\n\nPythonライブラリatprotoで実行した。\n\n')
    text_builder.text('タグの挿入。実際の値はblueskyだが以下別の表記にしてみた。\n')
    text_builder.tag('#ブルスカ', 'bluesky')
    text_builder.text(' ')
    text_builder.text('\nハッシュ記号のないタグも挿入できるか試す。\n')
    text_builder.text(' ')
    text_builder.tag('BlueSky', 'bluesky')
    text_builder.text(' \n\n')
    text_builder.mention('私自身へのメンション', 'did:plc:ffttrlkbljhdeoypveptm4sj')
    text_builder.text(' ')
    text_builder.link('私のはてなブログ', 'https://ytyaru.hatenablog.com/')
    text_builder.text('へのリンク。通常はURL文字列だが任意テキストで表示されたはず。')

    client.send_post(text_builder)


if __name__ == '__main__':
    if len(sys.argv) < 3: 
        print("Error: BlueSkyユーザのハンドルとパスワードを引数に渡してください。", file=sys.stderr)
        sys.exit(1)
    main(sys.argv[1], sys.argv[2])

実行

BS_HANDLE=ytyaru.bsky.social
BS_PASSWORD=アプリ用パスワード
$ ./main.py "$BS_HANDLE" "$BS_PASSWORD"

結果

APIで投稿してみる。 改行確認。 Pythonライブラリatprotoで実行した。 タグの挿入。実際の値はblueskyだが以下別の表記にしてみた。 #ブルスカ ハッシュ記号のないタグも挿入できるか試す。 BlueSky 私自身へのメンション 私のはてなブログへのリンク。通常はURL文字列だが任意テキストで表示されたはず。

— ytyaru (@ytyaru.bsky.social) 2025年8月4日 16:36

おまけ

 自分のDIDが必要ならclient.get_profile(client.me.handle)関数で取得できる。

...
    # DID取得するため
    profile = client.get_profile(client.me.handle)
    print(f'DID:{profile.did}')
...
    text_builder.mention('私自身へのメンション', profile.did) # 'did:plc:ffttrlkbljhdeoypveptm4sj'

所感

 あとは連携を考えたい所。

  • クライアントの自作
  • 予約投稿(指定日時になったら所定の内容を投稿する)
  • webhook連携(GitHubにプッシュが来たらその情報をBlueSkyで投稿する等)

 以下が参考になりそう。

 AIのAPIで投稿内容を作成すれば完全なBOTが作れそう。

 残念なことに「はてなブログ」はBlueSkyと連携できない。BlueSkyと連携できるようにして欲しいなー。はてなブログで投稿したらBlueSkyにも自動投稿されるようにして欲しいなぁ。

 はてなwebhookはあるけど、有料なんだよなぁ。無料ユーザにも開放して欲しいなぁ。|д゚)チラッ