diff --git a/.gitignore b/.gitignore --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ # release hooks create-release-*.sh .tmp.release_info +venv/ diff --git a/docs/publish-feed.md b/docs/publish-feed.md new file mode 100644 --- /dev/null +++ b/docs/publish-feed.md @@ -0,0 +1,28 @@ +# How to publish a feed + +Supercell is a feed generator, that is to say it is the service that fulfills feed requests and returns the posts that are displayed. + +To invoke Supercell, you first need to create a record on your PDS saying that you are publishing a feed for others to use. This is called feed publishing. + +This is done by creating a record on your PDS with the following structure: + +```json +{ + "$type": "app.bsky.feed.generator", + "did": "did:web:the_hostname", + "display_name": "Feed A", + "description": "A useful feed.", + "created_at": "2024-10-30T16:15:31Z" +} +``` + +The `did` is a did:web identifier where the hostname is used to service a DID document that contains a "BskyFeedGenerator" service structure with the hostname to make API calls. + +## Publish script + +The `publish.py` script can be used to create new feed records or update existing ones. + +1. Create a new virtual environment to run the script in: `python -m venv ./venv/` +2. Install the atproto library in the virtual environment: `./venv/bin/pip install atproto` +3. Invoke the script using the virtual environment: `./venv/bin/python ./etc/publish.py --help` + diff --git a/etc/publish.py b/etc/publish.py new file mode 100644 --- /dev/null +++ b/etc/publish.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +from typing import Optional +from atproto import Client, models +import argparse + +def main(user: str, password: str, name: str, description: str, server: str, rkey: Optional[str] = None, image: Optional[str] = None): + client = Client() + client.login(user, password) + avatar_blob = None + if image: + with open(image, 'rb') as f: + avatar_data = f.read() + avatar_blob = client.upload_blob(avatar_data).blob + response = client.com.atproto.repo.put_record(models.ComAtprotoRepoPutRecord.Data( + repo=client.me.did, + collection=models.ids.AppBskyFeedGenerator, + rkey=rkey, + record=models.AppBskyFeedGenerator.Record( + did=f'did:web:{server}', + display_name=name, + description=description, + avatar=avatar_blob, + created_at=client.get_current_time_iso(), + ) + )) + print('Feed URI :', response.uri) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("-u", "--user", help="The handle to publish the feed under. Ex: smokesignal.events") + parser.add_argument("-p", "--password", help="The password for the handle publishing the feed") + parser.add_argument("-n", "--name", help="The name of the feed. Ex: What's Hot") + parser.add_argument("-d", "--description", help="The description of the feed. Ex: Top trending content from the whole network") + parser.add_argument("-i", "--image", default=None, help="The path to the avatar image for the feed. Ex: ./path/to/avatar.jpeg") + parser.add_argument("-s", "--server", help="The server hostname servicing the feed. Ex: feeds.smokesignal.events") + parser.add_argument("-r", "--rkey", default=None, help="The rkey of a feed being updated.") + args = parser.parse_args() + main(args.user, args.password, args.name, args.description, args.server, args.rkey, args.image) +