FeedFeedbackAPIってなんなんだー?

@l-tan.dolciss.net

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がどのように送信されるかを見ていきます。

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

  1. ちょうど懐かしい動画を見たところでして……

  2. https://bsky.app/profile/bsky.app/post/3ks36igfly32h

  3. 後述しますが他にもフィードバックが送られています。

  4. https://bsky.app/profile/bsky.app/post/3lxxo3i5pck2c

  5. https://bsky.app/profile/bsky.app/post/3m47grlweoc2a

  6. https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L53-L57

  7. https://github.com/bluesky-social/atproto/blob/30267df6f17a25f7caa080136652824b54b69017/lexicons/app/bsky/feed/defs.json#L70-L74

  8. https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L150

  9. https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L171-L175

  10. https://github.com/bluesky-social/atproto/blob/30267df6f17a25f7caa080136652824b54b69017/lexicons/app/bsky/feed/defs.json#L233-L265

  11. https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L225-L228

  12. https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L229-L232

  13. https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L233-L236

  14. https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L237-L240

  15. https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L241-L244

  16. https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L245-L248

  17. https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L249-L252

  18. https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L253-256

  19. https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L257-260

  20. https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L261-L264

  21. https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L265-L268

  22. https://github.com/bluesky-social/atproto/blob/4184a652225eaf2de5f4d4702562e84c2fd3fde7/lexicons/app/bsky/feed/defs.json#L269-L272

l-tan.dolciss.net
えるたん

@l-tan.dolciss.net

地方のしがないエンジニアもどき
中身はただのアイマスP

From:Nara🦌, Japan🇯🇵

Since:2023/03/16

Post reaction in Bluesky

*To be shown as a reaction, include article link in the post or add link card

Reactions from everyone (0)