Manually creating a Bluesky custom feed
First, decide on a domain name where you'll host your feed(s). We'll use example.com for demo purposes.
On this host create a DID document at /.well-known/did.json
with the domain name in the id
field and the serviceEndpoint
field:
{
"@context": [
"https://www.w3.org/ns/did/v1"
],
"id": "did:web:example.com",
"service": [
{
"id": "#bsky_fg",
"type": "BskyFeedGenerator",
"serviceEndpoint": "https://example.com"
}
]
}
Next, add a record to your repo's app.bsky.feed.generator
collection with a did
pointing to the domain you selected:
>>> record_details = {
... 'did': 'did:web:example.com', # DID document defines BskyFeedGenerator service endpoint
... 'displayName': 'first feedgen',
... 'description': 'description here',
... 'createdAt': '2024-01-21T06:19:24.520Z'
... }
>>> record_data = atproto.models.com.atproto.repo.put_record.Data(
... repo = 'did:plc:4nsduwlpivpuur4mqkbfvm6a', # your user repo DID
... collection = 'app.bsky.feed.generator',
... rkey = 'test1', # describe your feed using a-z and dashes only
... record = record_details
... )
>>> client.com.atproto.repo.put_record(record_data)
Response(cid='bafyreicve366fluvlmbyygwm4yfn5bab6ahoznn4ii7ytjfzu4dgfm44zi', uri='at://did:plc:4nsduwlpivpuur4mqkbfvm6a/app.bsky.feed.generator/test1')
Your Bluesky PDS first fetches the DID document of the did
property (i.e., did:web:example.com
) which defines the serviceEndpoint
location of your feed generator.
Once it has this, your PDS fires off another request to a app.bsky.feed.getFeedSkeleton
NSID hosted at the service endpoint with the feed at-uri provided as a feed
query parameter.
Pulling it all together, the request URL will look like this:
https://example.com/xrpc/app.bsky.feed.getFeedSkeleton?feed=at://did:plc:4nsduwlpivpuur4mqkbfvm6a/app.bsky.feed.generator/test1
This NSID should return a JSON document in the following shape with a Content-Type: application/json
header:
{
"cursor": "",
"feed": [
{
"post": "at://did:plc:4nsduwlpivpuur4mqkbfvm6a/app.bsky.feed.post/3kjhr4hwujn2q"
}
]
}
If there is a cursor
value, the PDS uses that to paginate results. Leave it empty if there's only one page.
Refs: https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/feed/getFeedSkeleton.json
Created: 2024-01-21
Last Updated: 2024-01-23