Bluesky Skill
Interact with Bluesky via the AT Protocol API. Supports public reads, search, authenticated posting, and profile monitoring.
Configuration
Credentials can come from openclaw config or environment variables:
| Source | Handle | App Password |
|---|
| Config | INLINECODE0 | INLINECODE1 |
| Env var |
BSKY_HANDLE |
BSKY_APP_PASSWORD |
App passwords are created at: https://bsky.app/settings/app-passwords
Never use a main account password. Always use an app password.
1. Read — Fetch Recent Posts from a Profile
No auth required. Uses the public API.
API Endpoint
CODEBLOCK0
- -
actor — Bluesky handle (e.g. alice.bsky.social) or DID - INLINECODE6 — number of posts to return (1–100, default 50)
curl Example
CODEBLOCK1
Helper Script
CODEBLOCK2
Response Structure
The response JSON has a feed array. Each entry contains:
- -
.post.record.text — the post text - INLINECODE9 — ISO 8601 timestamp
- INLINECODE10 — AT URI of the post
- INLINECODE11 — author handle
- INLINECODE12 — author display name
- INLINECODE13 ,
.post.repostCount, .post.replyCount — engagement counts
2. Search — Find Posts by Keyword
No auth required. Uses the public API.
API Endpoint
CODEBLOCK3
- -
q — search query (keywords, hashtags, phrases) - INLINECODE17 — number of results (1–100, default 25)
curl Example
CODEBLOCK4
Helper Script
CODEBLOCK5
Response Structure
The response JSON has a posts array. Each entry contains:
- -
.record.text — the post text - INLINECODE20 — ISO 8601 timestamp
- INLINECODE21 — author handle
- INLINECODE22 — author display name
- INLINECODE23 — AT URI of the post
3. Post — Create a New Post
Requires auth. Uses app password authentication.
Step 1: Authenticate
CODEBLOCK6
This returns a session with accessJwt and did.
curl Example
CODEBLOCK7
Step 2: Create the Post
CODEBLOCK8
curl Example
CODEBLOCK9
Helper Script
CODEBLOCK10
Post Length Limit
Bluesky posts have a 300-character limit (grapheme count). Check length before posting.
4. Monitor — Check for New Posts Since Last Check
Use the read endpoint with a timestamp comparison to detect new posts. This is useful for heartbeat monitoring.
Approach
- 1. Fetch recent posts from the target profile.
- Compare
.post.record.createdAt against the last-known timestamp. - Any post with a
createdAt newer than the stored timestamp is new.
curl Example
CODEBLOCK11
Monitoring Loop Pattern
For heartbeat use, store the latest seen timestamp and compare on each check:
CODEBLOCK12
Error Handling
| HTTP Status | Meaning | Action |
|---|
| 200 | Success | Parse response |
| 400 |
Bad request | Check parameters |
| 401 | Unauthorized | Re-authenticate (token may be expired) |
| 404 | Not found | Check handle/DID exists |
| 429 | Rate limited | Back off and retry after delay |
Auth Token Expiry
Access tokens expire. If you get a 401, re-authenticate by calling createSession again. Do not cache tokens for more than a few minutes.
Quick Reference
| Operation | Auth? | Script |
|---|
| Read profile feed | No | INLINECODE29 |
| Search posts |
No |
./scripts/bsky-search.sh <query> [limit] |
| Create post | Yes |
./scripts/bsky-post.sh <handle> <app_password> <text> |
| Monitor new posts | No | Use read + timestamp filter (see section 4) |
Bluesky 技能
通过 AT 协议 API 与 Bluesky 交互。支持公开读取、搜索、认证发帖和个人资料监控。
配置
凭证可来自 openclaw 配置或环境变量:
| 来源 | 用户名 | 应用密码 |
|---|
| 配置 | channels.bluesky.handle | channels.bluesky.appPassword |
| 环境变量 |
BSKY
HANDLE | BSKYAPP_PASSWORD |
应用密码创建地址:https://bsky.app/settings/app-passwords
切勿使用主账户密码。始终使用应用密码。
1. 读取 — 获取个人资料的最新帖子
无需认证。 使用公开 API。
API 端点
GET https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed?actor=<用户名>&limit=<数量>
- - actor — Bluesky 用户名(例如 alice.bsky.social)或 DID
- limit — 返回的帖子数量(1–100,默认 50)
curl 示例
bash
curl -s https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed?actor=alice.bsky.social&limit=5 | jq .feed[] | {text: .post.record.text, createdAt: .post.record.createdAt, uri: .post.uri}
辅助脚本
bash
./scripts/bsky-read.sh alice.bsky.social 5
响应结构
响应 JSON 包含一个 feed 数组。每个条目包含:
- - .post.record.text — 帖子文本
- .post.record.createdAt — ISO 8601 时间戳
- .post.uri — 帖子的 AT URI
- .post.author.handle — 作者用户名
- .post.author.displayName — 作者显示名称
- .post.likeCount、.post.repostCount、.post.replyCount — 互动计数
2. 搜索 — 按关键词查找帖子
无需认证。 使用公开 API。
API 端点
GET https://public.api.bsky.app/xrpc/app.bsky.feed.searchPosts?q=<查询>&limit=<数量>
- - q — 搜索查询(关键词、标签、短语)
- limit — 结果数量(1–100,默认 25)
curl 示例
bash
curl -s https://public.api.bsky.app/xrpc/app.bsky.feed.searchPosts?q=openclaw&limit=10 | jq .posts[] | {text: .record.text, author: .author.handle, createdAt: .record.createdAt}
辅助脚本
bash
./scripts/bsky-search.sh openclaw 10
响应结构
响应 JSON 包含一个 posts 数组。每个条目包含:
- - .record.text — 帖子文本
- .record.createdAt — ISO 8601 时间戳
- .author.handle — 作者用户名
- .author.displayName — 作者显示名称
- .uri — 帖子的 AT URI
3. 发帖 — 创建新帖子
需要认证。 使用应用密码认证。
步骤 1:认证
POST https://bsky.social/xrpc/com.atproto.server.createSession
Content-Type: application/json
{identifier: <用户名>, password: <应用密码>}
这将返回一个包含 accessJwt 和 did 的会话。
curl 示例
bash
始终使用环境变量 — 切勿将凭证直接插入 shell 字符串
SESSION=$(curl -s -X POST https://bsky.social/xrpc/com.atproto.server.createSession \
-H Content-Type: application/json \
-d $(jq -n --arg h $BSKY
HANDLE --arg p $BSKYAPP_PASSWORD {identifier: $h, password: $p}))
ACCESS_TOKEN=$(echo $SESSION | jq -r .accessJwt)
DID=$(echo $SESSION | jq -r .did)
步骤 2:创建帖子
POST https://bsky.social/xrpc/com.atproto.repo.createRecord
Authorization: Bearer
Content-Type: application/json
{
repo: ,
collection: app.bsky.feed.post,
record: {
$type: app.bsky.feed.post,
text: <帖子文本>,
createdAt:
}
}
curl 示例
bash
curl -s -X POST https://bsky.social/xrpc/com.atproto.repo.createRecord \
-H Authorization: Bearer ${ACCESS_TOKEN} \
-H Content-Type: application/json \
-d {
\repo\: \${DID}\,
\collection\: \app.bsky.feed.post\,
\record\: {
\\$type\: \app.bsky.feed.post\,
\text\: \来自 OpenClaw 的问候!\,
\createdAt\: \$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\
}
}
辅助脚本
bash
通过环境变量传递应用密码 — 切勿作为 CLI 参数(在 ps/shell 历史中可见)
BSKY
APPPASSWORD=xxxx-xxxx-xxxx-xxxx ./scripts/bsky-post.sh alice.bsky.social 来自 OpenClaw 的问候!
帖子长度限制
Bluesky 帖子有 300 个字符限制(字素计数)。发帖前请检查长度。
4. 监控 — 检查自上次检查以来的新帖子
使用带时间戳比较的读取端点来检测新帖子。这对于心跳监控非常有用。
方法
- 1. 获取目标个人资料的最近帖子。
- 比较 .post.record.createdAt 与上次已知的时间戳。
- 任何 createdAt 晚于存储时间戳的帖子都是新帖子。
curl 示例
bash
获取最近 10 条帖子
FEED=$(curl -s https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed?actor=alice.bsky.social&limit=10)
筛选出晚于给定时间戳的帖子
SINCE=2026-03-24T00:00:00.000Z
echo $FEED | jq --arg since $SINCE .feed[] | select(.post.record.createdAt > $since) | {text: .post.record.text, createdAt: .post.record.createdAt}
监控循环模式
对于心跳使用,存储上次看到的时间戳,并在每次检查时进行比较:
bash
使用用户拥有的状态目录,而非全局可读的 /tmp
LAST
SEENFILE=${XDG
STATEHOME:-$HOME/.local/state}/bsky-monitor-${HANDLE}.txt
mkdir -p $(dirname $LAST
SEENFILE)
HANDLE=alice.bsky.social
读取上次看到的时间戳(或默认使用 24 小时前)
if [ -f $LAST
SEENFILE ]; then
SINCE=$(cat $LAST
SEENFILE)
else
SINCE=$(date -u -v-24H +%Y-%m-%dT%H:%M:%S.000Z 2>/dev/null || date -u -d 24 hours ago +%Y-%m-%dT%H:%M:%S.000Z)
fi
FEED=$(curl -s https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed?actor=${HANDLE}&limit=20)
NEW_POSTS=$(echo $FEED | jq --arg since $SINCE [.feed[] | select(.post.record.createdAt > $since)])
COUNT=$(echo $NEW_POSTS | jq length)
if [ $COUNT -gt 0 ]; then
echo 自 $SINCE 以来,从 $HANDLE 发现 $COUNT 条新帖子
echo $NEW_POSTS | jq .[] | {text: .post.record.text, createdAt: .post.record.createdAt}
# 将上次看到的时间戳更新为最新帖子的时间戳
echo $NEWPOSTS | jq -r .[0].post.record.createdAt > $LASTSEEN_FILE
else
echo 自 $SINCE 以来,$HANDLE 没有新帖子
fi
错误处理
| HTTP