{
"path": "/posts/feed-01",
"site": "at://did:plc:rgdcflm4ylsl6udghmtblydc/site.standard.publication/blog",
"tags": [
"Bluesky"
],
"$type": "site.standard.document",
"title": "Blueskyのカスタムフィードの仕組みを一から自分で考える #01",
"updatedAt": "2026-02-22T10:07:04.973Z",
"publishedAt": "2023-12-20T00:00:00.000Z",
"textContent": "## 前段\n\nSkyFeedも便利だけど、7日間の制限やタイムラグなどの制限を突破してみたい・・・と考えた私が、ようやく理解するに至った経緯をつらつらと書きます。\n\nこちらのサンプルソース、概ね 4つの機能で成り立っています\n\n1. Blueskyのサーバーから「投稿」のイベントを受け取って、メモリかSQLiteにその投稿をひたすら書き込む\n1. カスタムフィードのサーバーとして最低限の認証情報を返す\n1. カスタムフィードが呼び出された時に、メモリかSQLiteにある投稿を返す\n1. カスタムフィードそのものをBlueskyのサーバに登録し、解決できるようにする\nこの機能の中で、単なるカスタムフィードを成立させるのであれば、2と3だけあればOKなのです。\n\n## Blueskyのサーバーから「投稿」のイベントを受け取って、メモリかSQLiteにその投稿をひたすら書き込む\n\n一番最初が一番話が重いので、別記事で解説します。\n\n## カスタムフィードのサーバーとして最低限の認証情報を返す\n\n2は下記のようにエンドポイントが書いてあるJSONを返します。.well-known自体はBluesky固有ではなく、インターネットの世界でよく使われます。単純にサーバー名返せばOKなので、物理的なファイルを配置しても良いです。\n\nhttps://feedgenerator.usounds.work/.well-known/did.json\n\n## カスタムフィードが呼び出された時に、メモリかSQLiteにある投稿を返す\n\n3は下記のようなJSONを返却します\n\nhttps://feedgenerator.usounds.work/xrpc/app.bsky.feed.getFeedSkeleton?feed=at://did:plc:rgdcflm4ylsl6udghmtblydc/app.bsky.feed.generator/aaajpyz7pnyko\n\nJSONを見ると下記のようなcursorとpostの羅列です。postの配列はat://did:plc:oznd7j2sgw5yaokcuj23kbs7/app.bsky.feed.post/3kgxttdfgvb2gといういわゆる投稿をユニークに特定するキー(DID)になっています。すなわち、「表示したい投稿のDIDをpostの配列とそれっぽいcursorをつけて返せば良い」ことになります。cursorを返すかは任意なので、極論、(常に同じ結果を返し、スクロールに対応しないフィードジェネレーターでよければ)固定のJSONを返す実装をすれば良いのです。\n\n```plain text\n{\n \"cursor\":\"1702737520220::bafyreignf2tpupmnv4hv3juiyd3jxzk3f7sh4yruab7cgxqjjssvnfdfeu\",\n \"feed\":[\n {\"post\":\"at://did:plc:oznd7j2sgw5yaokcuj23kbs7/app.bsky.feed.post/3kgxttdfgvb2g\"},\n {\"post\":\"at://did:plc:oznd7j2sgw5yaokcuj23kbs7/app.bsky.feed.post/3kgxqhxnrs326\"}\n}\n```\n\n## カスタムフィードそのものをBlueskyのサーバに登録し、解決できるようにする\n\n4は相当に特殊です。2,3はカスタムフィードを表示するときに毎回呼ばれる処理、であることは理解できるかと思います。4はBlueskyに登録するだけの機能です。「https://feedgenerator.usounds.work/xrpc/app.bsky.feed.getFeedSkeleton?feed=at://did:plc:rgdcflm4ylsl6udghmtblydc/app.bsky.feed.generator/aaajpyz7pnyko」をBlueskyの世界に登録する、ということを行います。\n\n### カスタムフィード登録時のリクエスト\n\n登録を行う際のリクエストは下記のようになります。\n\n36行目のdid:plc:rgdcflm4ylsl6udghmtblydcは私のBlueskyのアカウントのDIDです。\n\n37行目のrkeyは私のDIDに「aaajpyz7pnyko」(スクショだと別の文字になっています)という名前のFeedGeneratorを登録します。\n\n41行目はその登録するFeed Generatorの実際のサーバーは「did:web:feedgenerator.usounds.work」(スクショだと別のサーバーになっています)です。それ以降はカスタムフィードの名前やら画像やらが登録されています。\n\nすなわち、この登録を行うことで\n\nhttps://bsky.app/profile/did:plc:rgdcflm4ylsl6udghmtblydc/feed/aaajpyz7pnyko\n\nのカスタムフィードにアクセスがあると、Bluesakyのサーバーは、/alfの実態サーバーはfeedgenerator.usounds.workであることがわかっている。よって、実際のデータは\n\nhttps://feedgenerator.usounds.work/xrpc/app.bsky.feed.getFeedSkeleton?feed=at://did:plc:rgdcflm4ylsl6udghmtblydc/app.bsky.feed.generator/aaajpyz7pnyko\n\nからとってくる\n\nという処理になります。DNSの名前解決みたいですね。一度登録すれば、aaajpyz7pnykoはfeedgenerator.usounds.workサーバーにある、という紐付きを変えない限り登録しっぱなしになります。カスタムフィードの条件をかえても、都度Publishする必要はありません。画像や説明文を変えたいときだけ使います。(SkyFeedは毎回Publishするじゃないかはその通りで、SkyFeedの検索条件は実はBlueskyの世界に保存されています。なので、検索条件を変更する都度Publishする必要があるんですね。)\n\nまた、Publishするサーバーは、どこでもよいです。私は作業用PCから登録してます。\n\nなお、今の所公開を取りやめる仕組みは提供されていません。筆者が作ったWebアプリでテスト版で自己責任ですが非表示にする事が出来ます。\n\nSkyFeedで実運用しているフィードを非表示にすると、破滅を引き起こすのでご注意下さい。あくまでも、サンプルソースを単純に動かして消せなくなった方向け、SkyFeedで不整合が起こってフィードを非表示に出来なくなった方向けです。\n\n"
}