browsing-bluesky について
公開 API と任意の認証アカウントを使ってBlueskyを調査するPythonスキルです。投稿、フィード、トレンド、スレッド、ソーシャルグラフの取得に加え、アカウント分析にも対応します。
主な機能
- 01投稿とユーザーの検索
- 02プロフィール、フィード、スレッドの取得
- 03トレンドとfirehoseサンプルの確認
- 04いいね、リポスト、引用の確認
- 05アカウントとソーシャルグラフの分析
活用シーン
- 01トピックに関する議論の調査
- 02ユーザーの最近の投稿の追跡
- 03トレンドと投稿の探索
- 04フォロー中または指定アカウントの分類
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ oaustegard/ claude-skills --skill browsing-blueskyインストール後、Claude Code で/browsing-blueskyを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
__init__.pyCHANGELOG.mdREADME.mdscripts/__init__.pybsky.pyimage_transcribe.pyzeitgeist-sample.jstests/test_auth_identity.py詳細
Bluesky の閲覧
公開 API とリアルタイムの firehose を通じて Bluesky のコンテンツにアクセスします。パーソナライズされたフィード向けに、オプションで認証を利用できます。分類のためのアカウント分析機能も含まれます。
実装
スキルディレクトリをパスに追加してインポートします。
import sys
sys.path.insert(0, '/path/to/skills/browsing-bluesky') # or use .claude/skills symlink path
from browsing_bluesky import (
# Core browsing
search_posts, get_user_posts, get_profile, get_feed_posts, sample_firehose,
get_thread, get_quotes, get_likes, get_reposts,
get_followers, get_following, search_users,
# Trending
get_trending, get_trending_topics,
# Account analysis
get_all_following, get_all_followers, extract_post_text,
extract_keywords, analyze_account, analyze_accounts,
# Authentication utilities
is_authenticated, get_authenticated_user, authenticated_identity,
clear_session
)
認証(任意)
認証すると、誰がリクエストしているかを把握する必要がある、Paper Skygest のようなパーソナライズされたフィードを利用できます。
セットアップ
- Bluesky でアプリパスワードを作成します。Settings → Privacy and Security → App Passwords
- 環境変数を設定します。
export BSKY_HANDLE="yourhandle.bsky.social" export BSKY_APP_PASSWORD="xxxx-xxxx-xxxx-xxxx"
読み取りに使用するアカウント
2 組の認証情報が認識されます。異なるのはプレフィックスのみです。
| 組 | ID |
|---|---|
MUNINN_BSKY_HANDLE / MUNINN_BSKY_APP_PASSWORD | muninn — 優先 |
BSKY_HANDLE / BSKY_APP_PASSWORD | owner — Muninn の組が設定されていない場合に使用 |
BSKY_IDENTITY=muninn|owner を指定すると、使用するアカウントを明示的に選べます。その名前の組が、別の組で代用されることはありません。Muninn の組を設定せずに muninn を指定した場合は、何も通知されないまま owner のアカウントで読み取るのではなく、公開データとして読み取ります。認識されない値を指定するとエラーになります。
これは、起動済みのコンテナに両方の組が設定されているため重要です。2026-08-28 まではプレフィックスなしの組が優先されていたため、認証付きの読み取りはすべて、タイムラインも、それらすべてに影響するミュート、ブロック、ラベラーも、出力にその旨が一切表示されないまま、アカウントの所有者として取得されていました。このスキルは読み取り専用と説明されていましたが、実際にはそのアカウントに対する書き込みスコープを持つアプリパスワードを保持していました。ドキュメントに記載された BSKY_* の組だけが設定されているマシンでは、何も変わりません。
動作
- 透過的:認証情報の有無にかかわらず、すべての関数は同じように動作します。
- 自動:認証情報がある場合、認証ヘッダーが適宜追加されます。
- 柔軟:認証に失敗すると、通知なしで公開アクセスにフォールバックします。誤記された
BSKY_IDENTITYは認証の失敗ではなく設定の問題として扱われ、エラーになります。 - 安全:トークンはメモリ内にのみキャッシュされ、ログへの記録や永続化はされません。
認証状態の確認
if is_authenticated():
print(f"Logged in as: {get_authenticated_user()}")
else:
print("Using public access")
# Which pair answered — a handle alone does not say whether it was chosen
# or simply the only one set.
authenticated_identity() # {'identity': 'muninn', 'handle': ..., 'did': ...}
# Clear session if needed (e.g., switching accounts or identities)
clear_session()
リサーチのワークフロー
トピックを調査する
bsky.app の高度な検索に一致するクエリ構文を使って search_posts() を呼び出します。
- 基本語句:
event sourcing - 完全一致するフレーズ:
"event sourcing" - ユーザーフィルター:
from:acairns.co.uk、またはauthor=パラメーターを使用 - 日付フィルター:
since:2025-01-01、またはsince=パラメーターを使用 - ハッシュタグ、メンション、ドメインリンク:
#python mentions:user domain:github.com
複雑な検索では、クエリ構文と関数パラメーターを組み合わせます。
ユーザーをモニタリングする
get_profile(handle)でプロフィールを取得し、背景情報(自己紹介、フォロワー数、投稿数)を確認します。get_user_posts(handle, limit=N)で最近の投稿を取得します。- 特定のトピックに関するユーザーの投稿には、
search_posts(query, author=handle)を使用します。
トレンドを見つける
推奨ワークフロー — まずトレンド API を使い、詳しく調べる場合は firehose を使用します。
1. トレンドトピックを使ってすばやく確認(約 500 トークン)
topics = get_trending_topics(limit=10)
# Returns: {topics: [{topic, display_name, description, link}, ...],
# suggested: [...]}
2. 投稿数と投稿者を含む詳細なトレンド
trends = get_trending(limit=10)
for t in trends:
print(f"{t['display_name']} — {t['post_count']} posts ({t['status']})")
# Each trend includes: topic, display_name, link, started_at,
# post_count, status, category, actors
3. 選んだトレンドを対象に詳しく調査
posts = search_posts(trend["topic"], limit=25)
4. 任意:投稿速度のモニタリングやロングテールの発見に firehose を使用
前提条件:セッションごとに一度、Node.js の依存関係をインストールします。
cd /home/claude && npm install ws https-proxy-agent 2>/dev/null
data = sample_firehose(duration=30) # Full firehose sample
data = sample_firehose(duration=20, filter="python") # Filtered sample
次のキーを含む辞書を返します。
- window:
{startTime, endTime, durationSeconds}— サンプリング時間範囲 - stats:
{totalReceived, totalPosts, postsPerSecond, filter, languages}— ボリューム指標と言語の内訳 - topWords:
[[word, count], ...]— 上位 50 語(count >= 3) - topPhrases:
[[bigram, count], ...]— 上位 30 個のバイグラム(count >= 2) - topTrigrams:
[[trigram, count], ...]— 上位 20 個のトライグラム(count >= 2) - entities:
[[entity, count], ...]— 上位 25 件のハンドル/ハッシュタグ(count >= 2) - samplePosts:
[{text, altTexts, hasImages}, ...]— 条件に一致する最初の 50 件の投稿
フィードとリストを読む
get_feed_posts() は次のものを受け付けます。
- リスト URL:
https://bsky.app/profile/austegard.com/lists/3lankcdrlip2f - フィード URL:
https://bsky.app/profile/did:plc:xxx/feed/feedname - AT-URI:
at://did:plc:xxx/app.bsky.graph.list/xyz
この関数は URL から AT-URI を自動的に抽出します。
スレッドを調べる
親投稿と返信を含む投稿のスレッド全体のコンテキストを取得します。
thread = get_thread("https://bsky.app/profile/user/post/xyz", depth=10)
# Returns: {post: {...}, parent: {...}, replies: [...]}
引用投稿を探す
特定の投稿を引用している投稿を見つけます。
quotes = get_quotes("https://bsky.app/profile/user/post/xyz")
for q in quotes:
print(f"@{q['author_handle']}: {q['text'][:80]}")
エンゲージメントを分析する
投稿にエンゲージメントしたユーザーを取得します。
likes = get_likes(post_url)
reposts = get_reposts(post_url)
# Accepts both URLs and AT-URIs
likes = get_likes("at://did:plc:.../app.bsky.feed.post/...")
埋め込み画像を読む
解析された各投稿には images フィールドがあり、埋め込み画像ごとの {alt, url, transcription} 辞書のリストが含まれます。従来の image_alts: list[str] フィールドも維持されます(空でない alt のみ)。
alt テキストがなく、画像の内容が重要な場合は、投稿取得関数(get_user_posts、search_posts、get_feed_posts、get_thread、get_quotes)の transcribe パラメーターを使って、モデルによる文字起こしを有効にします。
# Routine/bulk work (zeitgeist, inbox review, news scans) —
# gemini-2.5-flash-lite is the recommended default. Cheapest production
# model anywhere ($0.10/$0.40 per 1M tokens), ~95% accuracy on dense
# screenshots in May 2026 benchmarks:
posts = get_user_posts("ayourtch.bsky.social", limit=40, transcribe="gemini-lite")
# Token-perfect transcription, still cheap:
posts = get_user_posts(..., transcribe="gemini-flash")
# Frontier model with thinking_level=minimal — for cases where the image
# content needs reasoning, not just transcription:
posts = get_user_posts(..., transcribe="gemini-3.5-flash")
# Anthropic single-vendor option (note: empirically weaker prompt-following
# than Gemini on dense transcription — Haiku tends to summarize rather
# than transcribe):
posts = get_user_posts(..., transcribe="haiku")
# Interactive sessions where image is part of the active task and you want
# conversation context to inform interpretation (only available on Anthropic):
thread = get_thread(post_url, transcribe="opus")
# Default (no transcription) — current behavior preserved:
posts = get_user_posts("ayourtch.bsky.social", limit=40)
ポリシーはすべての呼び出し元で一貫しています。alt テキストが空でない画像は文字起こしされません(投稿者がすでに画像を説明しているため、その内容を信頼します)。alt がない、または空の画像のみがモデルに送信されます。ネットワークまたは API のエラーが発生した場合、transcription は None のままになり、呼び出し元にはエラーが通知されずに処理が続行されます。
コスト/品質の実測値(2026 年 5 月、詳細な端末スクリーンショット 3 件、各 1 回実行。サンプル数が少ないため、傾向を示す参考値として扱ってください)。
| エイリアス | レイテンシ | 画像あたりの料金 | コードトークン再現率 |
|---|---|---|---|
gemini-lite | 約 8 秒 | 約 $0.001 | 95% |
gemini-flash | 約 10 秒 | 約 $0.003 | 100% |
gemini-3.5-flash | 約 10 秒 | 約 $0.014 | 100% |
haiku | 約 7 秒 | 約 $0.008 | 18%(要約) |
opus | 約 20 秒 | 約 $0.12 | 91% |
haiku と opus の行は、Haiku 4.5 と Opus 4.7 で測定したものです。現在、エイリアスは claude-haiku-5-5 と claude-opus-5-5 に解決されます。これらの行はまだ再測定されていません。
haiku/opus エイリアスには ANTHROPIC_API_KEY(または /mnt/project/claude.env 内の API_KEY)、gemini-* エイリアスには /mnt/project/proxy.env 内の CF AI Gateway 認証情報が必要です。文字起こしはパラメーターが設定されている場合にのみ実行されるため、該当する認証情報がない呼び出し元は、別のエイリアスを選ぶか、この機能を無効のままにできます。
ソーシャルグラフを調べる
フォロワー/フォロー関係をたどります。
followers = get_followers("handle.bsky.social")
following = get_following("handle.bsky.social")
# Returns list of actor dicts with handle, display_name, did, description, etc.
ユーザーを探す
名前、ハンドル、またはプロフィール文でユーザーを検索します。
users = search_users("machine learning researcher")
for u in users:
print(f"{u['display_name']} (@{u['handle']}): {u['description'][:100]}")
API エンドポイントに関する注意事項
- Public AppView:認証なしの読み取りには
https://api.bsky.app/xrpc/を使用します - PDS:認証済みリクエストには
https://bsky.social/xrpc/を使用します - トレンド:
app.bsky.unspecced.getTrends(詳細)とapp.bsky.unspecced.getTrendingTopics(軽量) - Firehose:
wss://jetstream1.us-east.bsky.network/subscribe - エンドポイントのルーティングは自動です。認証済みリクエストは PDS に、公開リクエストは AppView に送られます
- レート制限はありますが、読み取り操作では十分に余裕があります
戻り値の形式
すべての API 関数は、次の内容を含む構造化された辞書を返します。
uri:AT プロトコル識別子text:投稿内容created_at:ISO タイムスタンプauthor_handle:ユーザーのハンドルauthor_name:表示名likes、reposts、replies:エンゲージメント数links:投稿のファセットから抽出された完全な URL(投稿テキスト内の URL は「...」で省略されます)image_alts:埋め込み画像の alt テキストurl:bsky.app 上の投稿への直接リンク
プロフィール関数は handle、display_name、description、followers、following、posts、did を返します。
アカウント分析
トピック別に分類するためにアカウントを分析します。プロフィールと投稿を取得し、キーワードを抽出して、Claude が分類できるよう構造化データを返します。
ユーザーのネットワークを分析する
# Analyze accounts you follow
results = analyze_accounts(following="yourhandle.bsky.social", limit=50)
# Analyze your followers
results = analyze_accounts(followers="yourhandle.bsky.social", limit=50)
# Analyze specific handles
results = analyze_accounts(handles=["user1.bsky.social", "user2.bsky.social"])
単一アカウントの分析
analysis = analyze_account("user.bsky.social")
# Returns: {handle, display_name, description, keywords, post_count, followers, following}
キーワード抽出オプション
ストップワードのパラメーターで、ドメイン固有のノイズをフィルタリングします。
"en":英語(汎用、デフォルト)"ai":AI/ML 分野(技術関連の定型文をフィルタリング)"ls":ライフサイエンス(研究手法をフィルタリング)
results = analyze_accounts(following="handle", stopwords="ai")
必要条件:キーワード抽出に extracting-keywords スキルと YAKE の仮想環境が必要です。
アカウントのフィルタリング
results = analyze_accounts(
following="handle",
exclude_patterns=["bot", "spam", "promo"] # Skip accounts matching these
)
フォロー中/フォロワーのページネーション
get_following/get_followers の上限である 100 件を超える大規模なアカウントリストの場合:
all_following = get_all_following("handle", limit=500) # Handles pagination
all_followers = get_all_followers("handle", limit=500)
アカウント分析の出力
分析された各アカウントから、次の情報が返されます。
{
"handle": "user.bsky.social",
"display_name": "User Name",
"description": "Bio text here",
"keywords": ["keyword1", "keyword2", "keyword3"],
"post_count": 20,
"followers": 1234,
"following": 567
}
Claude は、ハードコードされたルールを使わずに、プロフィール文とキーワードを使ってアカウントをトピック別に分類します。