Blueskyのウワサ(クシュン)1
はじめに
本記事はBluesky Advent Calendar 2025の4日目の記事です。
2024年5月ごろに実装されたFeedFeedbackAPIについて、当時書きかけたままサードパーティーへの開放を待っているうちに忘れていた記事を、出すなら今かということで空き枠に滑り込ませてもらいます。(1年以上間が空いているのでつじつま合わない所あったらすみません)
⏪3日目はすいばり(@suibari.com)さんの「Bluesky AI Bot「全肯定botたん」の2025年の軌跡」です
⏩5日目は桃色豆腐(@momoiro.me)さんの「公式pdsから引っ越して、公式pdsに戻ってみるまで試す」です
拙作の記事投稿についてのPostをまとめたカスタムフィード(by Bluefeed)もあります
これなに
Blueskyの公式アプリのバージョン1.81.02にて、カスタムフィードに対してのフィードバック"Show more like this(このような投稿の表示を増やす)🙂","Show less like this(このような投稿の表示を減らす)☹"が送信できるようになりました。3
これは先日のProduct Roadmapにあった "New feedback mechanisms which the algorithmic feeds can request, such as “show more” and “show less” buttons, and a way to track which posts have been seen to stop duplicates from showing so often."によるもので、フィードバックによって表示される内容を変化させることができるようになるみたいです。
リリース内容にもありますが、この機能は1.81.0時点では公式のDiscoverフィードに対してのみ動作しますていました。ですが今後は他のカスタムフィードにも開放されるようなので、 その後1年の時を経て1.107.04、1.109.05にてサードパーティーのカスタムフィードに対しても開放されるようになりました。拙作のRepostNextPostにも対応しましたが、その際にGitHubのコードに潜って探索した記録を残しておこうと思います。
スキーマ実装:atproto#2383
-
app.bsky.feed.defs#feedViewPost
こちらはapp.bsky.feed.getFeedで取得されるPostに付加されるもののようです。(PDS→クライアント)
-
feedContextが追加 6
"feedContext": { "type": "string", "description": "Context provided by feed generator that may be passed back alongside interactions.", "maxLength": 2000 } -
reqIdが追加7
"reqId": { "type": "string", "description": "Unique identifier per request that may be passed back alongside interactions.", "maxLength": 100 }
-
- app.bsky.feed.defs#generatorView
-
acceptsInteractionsが追加 8
こちらはapp.bsky.feed.getFeedGeneratorでフィードジェネレーターの情報を返す際にインタラクションを受け取ることができるかを返すようです。
"acceptsInteractions": { "type": "boolean" }
-
- app.bsky.feed.defs#skeletonFeedPost
-
feedContextが追加 9
こちらはapp.bsky.feed.getFeedSkeletonで返すPostに付加するもののようです。(フィードジェネレーター→AppView→PDS→クライアントのfeedViewPostへ)
"feedContext": { "type": "string", "description": "Context that will be passed through to client and may be passed to feed generator back alongside interactions.", "maxLength": 2000 }
-
-
app.bsky.feed.defs#interaction が追加10
フィードジェネレーター側へ送られるフィードバック(interaction)と、getFeedSkeletonで渡したfeedContext、reqIdが定義されています。
"interaction": { "type": "object", "properties": { "item": { "type": "string", "format": "at-uri" }, "event": { "type": "string", "knownValues": [ "app.bsky.feed.defs#requestLess", "app.bsky.feed.defs#requestMore", "app.bsky.feed.defs#clickthroughItem", "app.bsky.feed.defs#clickthroughAuthor", "app.bsky.feed.defs#clickthroughReposter", "app.bsky.feed.defs#clickthroughEmbed", "app.bsky.feed.defs#interactionSeen", "app.bsky.feed.defs#interactionLike", "app.bsky.feed.defs#interactionRepost", "app.bsky.feed.defs#interactionReply", "app.bsky.feed.defs#interactionQuote", "app.bsky.feed.defs#interactionShare" ] }, "feedContext": { "type": "string", "description": "Context on a feed item that was orginally supplied by the feed generator on getFeedSkeleton.", "maxLength": 2000 } "reqId": { "type": "string", "description": "Unique identifier per request that may be passed back alongside interactions.", "maxLength": 100 } } },
-
以下、interactionのknownValuesたち
-
app.bsky.feed.defs#requestLess が追加 11
Postのメニューから"Show less like this(このような投稿の表示を減らす)☹"を選択された場合に送信されます。
"requestLess": { "type": "token", "description": "Request that less content like the given feed item be shown in the feed" },
-
app.bsky.feed.defs#requestMore が追加 12
Postのメニューから"Show more like this(このような投稿の表示を増やす)🙂"を選択された場合に送信されます。
"requestMore": { "type": "token", "description": "Request that more content like the given feed item be shown in the feed" },
-
app.bsky.feed.defs#clickthroughItem が追加 13
Postをクリック(タップ)してPostの画面を開いた場合に送信されます。
"clickthroughItem": { "type": "token", "description": "User clicked through to the feed item" },
-
app.bsky.feed.defs#clickthroughAuthor が追加 14
Postのアバターや名前をクリックしてプロフィール画面を開いた場合に送信されます。
"clickthroughAuthor": { "type": "token", "description": "User clicked through to the author of the feed item" },
-
app.bsky.feed.defs#clickthroughReposter が追加 15
Postの「~~がリポスト」をクリックしてリポスト者を開いた場合に送信されます。
"clickthroughReposter": { "type": "token", "description": "User clicked through to the reposter of the feed item" },
-
app.bsky.feed.defs#clickthroughEmbed が追加 16
Postのリンクカードや引用など埋め込みをクリックして開いた場合に送信されます。
"clickthroughEmbed": { "type": "token", "description": "User clicked through to the embedded content of the feed item" },
-
app.bsky.feed.defs#interactionSeen が追加 17
Postを一定時間表示させた場合に送信されます。
"interactionSeen": { "type": "token", "description": "Feed item was seen by user" },
-
app.bsky.feed.defs#interactionLike が追加 18
Postにいいねした場合に送信されます。
"interactionLike": { "type": "token", "description": "User liked the feed item" },
-
app.bsky.feed.defs#interactionRepost が追加 19
PostをRepostした場合に送信されます。
"interactionRepost": { "type": "token", "description": "User reposted the feed item" },
-
app.bsky.feed.defs#interactionReply が追加 20
Postに返信しようとした場合に送信されます。
"interactionReply": { "type": "token", "description": "User replied to the feed item" },
-
app.bsky.feed.defs#interactionQuote が追加 21
Postを引用しようとした場合に送信されます。
"interactionQuote": { "type": "token", "description": "User quoted the feed item" },
-
app.bsky.feed.defs#interactionShare が追加 22
Postを共有しようとした場合に送信されます。
"interactionShare": { "type": "token", "description": "User shared the feed item" }
-
PDS/Appview実装:atproto#2402
アプリから送信されたInteractionやフィードジェネレーターからのfeedContextをPDSを通してAppViewと送受信する実装です。
公式アプリ実装:social-app#3498
スキーマ側で実装されたInteractionがどのように送信されるかを見ていきます。
- packages.json
- lodash/throttleが追加されており、後ほど出てきますが一定間隔でInteractionの送信を制御するのに使用されています。
- src/state/feed-feedback.tsx
- FeedFeedbackの実体です。
fixes(1.82.0で実装):social-app#3968
1.81.0リリース後に行われている修正(改善)です。
- interactionSeenの判定する時間を1.5秒に変更
- リプライ元のPostについてもFeedContextを返すように修正(ちゃんと動き読めてないかも)
Include feedContext in DOM as data-(1.84.0で実装) :social-app#4206
- skeletonFeedPostのfeedContextが各Postのdivタグに
data-feed-contextとして出力されるように変更
Enable show less / more buttons for third party feeds(1.107.0で実装) social-app#8672
- サードパーティーのカスタムフィードにもrequestLessとrequestMoreを送信するよう変更
Send inferrable interactions to third-party feeds(1.109.0で実装) social-app#9094
- interactionLike、interactionQuote、interactionReply、interactionRepost、interactionSeenも送信するよう変更
- 以前Kyoto mini meetupでdan氏にQAでサードパーティーへの開放について聞いてもらった際、PRにも記述があるように、interactionSeenは"privacy-sensitive"という回答をもらっていました。ですが、ページネーションで次のページを取得する=それまでの投稿はスクロールで閲覧されているということになるため、既読の情報を送る用にした方がメリットも大きく対応されることとなりました。
結局対応するにはどうすればいいの
自前のカスタムフィードで対応したPRが多少参考にはなると思いますが主に2点変更が必要です。
app.bsky.feed.generatorレコードの変更
- カスタムフィード登録時のapp.bsky.feed.generatorレコードにacceptsInteractionsを追加します。
{
...
"$type": "app.bsky.feed.generator",
...
"acceptsInteractions": true
}
app.bsky.feed.sendInteractionsのXRPC対応
- example.com/xrpc/app.bsky.feed.sendInteractionsに上記のInteractionが送信されてきますので、それに対して処理を行います。
{
"interactions": [
{
"$type": "app.bsky.feed.defs#interaction",
"item": "at://did:plc:xxxxxxxxxxxxxxxxxxxxxxxx/app.bsky.feed.post/xxxxxxxxxxxxx",
"event": "app.bsky.feed.defs#xxxxxxxxxxx",
"feedContext": "context",
"reqId": ""
},
...
]
feedContextの対応
- sendInteractionsの中身にコンテキストを転送したい場合はapp.bsky.feed.getFeedSkeletonにfeedContextを追加します。(RepostNextPostでは対応していません)
{
"feed": [
"post": "at://did:plc:xxxxxxxxxxxxxxxxxxxxxxxx/app.bsky.feed.post/xxxxxxxxxxxxx",
"feedContext": "context"
],
"cursor": "xxxxxxxxxxx"
}
さいごに
RepostNextPostではrequestLessで投稿を非表示、requestMoreで最後に非表示にした投稿を再表示という処理を入れましたが、 初のDiscoverフィードのように表示内容をいろいろカスタムに活用できるかと思いますので参考になれば幸いです。 それではみなさまよいBlueskyライフを!
(再掲) ⏩5日目は桃色豆腐(@momoiro.me)さんの「公式pdsから引っ越して、公式pdsに戻ってみるまで試す」です
Footnotes
-
ちょうど懐かしい動画を見たところでして…… ↩
-
後述しますが他にもフィードバックが送られています。 ↩
-
https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L53-L57 ↩
-
https://github.com/bluesky-social/atproto/blob/30267df6f17a25f7caa080136652824b54b69017/lexicons/app/bsky/feed/defs.json#L70-L74 ↩
-
https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L150 ↩
-
https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L171-L175 ↩
-
https://github.com/bluesky-social/atproto/blob/30267df6f17a25f7caa080136652824b54b69017/lexicons/app/bsky/feed/defs.json#L233-L265 ↩
-
https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L225-L228 ↩
-
https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L229-L232 ↩
-
https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L233-L236 ↩
-
https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L241-L244 ↩
-
https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L245-L248 ↩
-
https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L249-L252 ↩
-
https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L253-256 ↩
-
https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L257-260 ↩
-
https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L261-L264 ↩
-
https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L265-L268 ↩