关于 browsing-bluesky
这是一个通过公共 API 和可选认证账户研究 Bluesky 的 Python skill,适合需要检索社交内容的研究者。它涵盖帖子、动态、趋势、讨论串和社交关系,并支持账户分析。
主要功能
- 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"
使用哪个账户读取
系统可识别两组凭据,它们的唯一区别是前缀:
| 凭据对 | 身份 |
|---|---|
MUNINN_BSKY_HANDLE / MUNINN_BSKY_APP_PASSWORD | muninn — 优先使用 |
BSKY_HANDLE / BSKY_APP_PASSWORD | owner — 未设置 Muninn 凭据对时使用 |
BSKY_IDENTITY=muninn|owner 可直接指定其中一个身份,且不会用其他凭据对替代指定的凭据:如果请求 muninn,但未设置 Muninn 凭据对,则会读取公共内容,而不会悄悄改用所有者账户。无法识别的值会引发错误。
这一点很重要,因为已启动的容器同时持有两组凭据。直到 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()
研究工作流
调查主题
使用 search_posts(),并采用与 bsky.app 高级搜索匹配的查询语法:
- 基本词语:
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 条匹配的帖子
阅读 Feed 和列表
get_feed_posts() 接受:
- 列表 URL:
https://bsky.app/profile/austegard.com/lists/3lankcdrlip2f - Feed 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 月,n=3 张内容密集的终端截图,每张各运行一次——样本量较小,仅供参考):
| 别名 | 延迟 | $/张图片 | 和弦 Token 召回率 |
|---|---|---|---|
gemini-lite | ~8s | ~$0.001 | 95% |
gemini-flash | ~10s | ~$0.003 | 100% |
gemini-3.5-flash | ~10s | ~$0.014 | 100% |
haiku | ~7s | ~$0.008 | 18%(生成摘要) |
opus | ~20s | ~$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 端点说明
- 公共 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:从帖子 facets 中提取的完整 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 venv。
筛选账号
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 会使用简介和关键词,按主题对账号进行分类,而无需硬编码规则