Skip to content

Commit

Permalink
Add update-feed Deno script
Browse files Browse the repository at this point in the history
  • Loading branch information
19759 committed Feb 2, 2025
1 parent 38b930c commit 8411136
Show file tree
Hide file tree
Showing 6 changed files with 1,186 additions and 1 deletion.
2 changes: 2 additions & 0 deletions bluesky-feed-gen/.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
BSKY_IDENTIFIER=somebody.bsky.social
BSKY_APP_PASSWORD=<app password here>
8 changes: 8 additions & 0 deletions bluesky-feed-gen/deno.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"tasks": {
"update-feed": "deno run --allow-net --allow-env --env-file ./generate-solomon-threads-feed.ts > ../docs/xrpc/app.bsky.feed.getFeedSkeleton/index.json"
},
"imports": {
"@atproto/api": "npm:@atproto/api@^0.13.32"
}
}
80 changes: 80 additions & 0 deletions bluesky-feed-gen/deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions bluesky-feed-gen/generate-solomon-threads-feed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { AtpAgent } from "npm:@atproto/api";
import {
getIdentifier,
getPassword,
LongformThreadFinder,
} from "./lib/bsky.ts";

// handle: solomonmissouri.bsky.social
const authorDID = "did:plc:w6adnkpcgqb67lqi5nxcy7l5";
const minDepth = 5;

const agent = new AtpAgent({
service: "https://bsky.social",
});
await agent.login({
identifier: getIdentifier(),
password: getPassword(),
});

const threadFinder = new LongformThreadFinder(agent, authorDID, minDepth);
await threadFinder.populateFeedMap();

const rootPosts = threadFinder.getRootPosts();

const feed = rootPosts.map((r) => {
return { post: r.post.uri };
});

console.log(JSON.stringify({ feed }, undefined, 2));
106 changes: 106 additions & 0 deletions bluesky-feed-gen/lib/bsky.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { AtpAgent } from "npm:@atproto/api";
import { FeedViewPost } from "@atproto/api";

export function getIdentifier() {
const password = Deno.env.get("BSKY_IDENTIFIER");
if (!password) {
throw new Error(
"BSKY_IDENTIFIER not defined. Did you add it to an .env file and use Deno --env-file?",
);
}
return password;
}

export function getPassword() {
const password = Deno.env.get("BSKY_APP_PASSWORD");
if (!password) {
throw new Error(
"BSKY_APP_PASSWORD not defined. Did you add it to an .env file and use Deno --env-file?",
);
}
return password;
}

// export type FeedMap = { [key: string]: FeedViewPost };
export type FeedMap = Map<string, FeedViewPost>;

export class LongformThreadFinder {
agent: AtpAgent;
authorDID: string;
minDepth: number;
feedMap: FeedMap | undefined;

constructor(agent: AtpAgent, authorDID: string, minDepth: number) {
this.agent = agent;
this.authorDID = authorDID;
this.minDepth = minDepth;
}

async populateFeedMap() {
this.feedMap = new Map<string, FeedViewPost>();

let cursor: string | undefined;
let i = 0;
do {
const { data } = await this.agent.getAuthorFeed({
actor: this.authorDID,
filter: "posts_and_author_threads",
limit: 100,
cursor,
});
cursor = data.cursor;

for (const feedView of data.feed) {
// filter out reposts
if (feedView.post.author.did === this.authorDID) {
this.feedMap.set(feedView.post.uri, feedView);
}
}

if (i++ > 1000) {
break;
}
} while (cursor);
}

getRootPosts(): FeedViewPost[] {
if (!this.feedMap) {
throw new Error("call populateFeedMap first");
}

const rootFeeds = new Set<FeedViewPost>();

for (const feedView of this.feedMap.values()) {
const result = this.getRootAndDepth(feedView.post.uri, 1);
if (result && result.depth >= this.minDepth) {
rootFeeds.add(result.rootFeed);
}
}

const sortedFeeds = [...rootFeeds.values()].toSorted((a, b) =>
b.post.record.createdAt.localeCompare(a.post.record.createdAt)
);
return sortedFeeds;
}

getRootAndDepth(
postURI: string,
depth: number,
): { rootFeed: FeedViewPost; depth: number } | undefined {
if (!this.feedMap) {
throw new Error("call populateFeedMap first");
}

const feed = this.feedMap.get(postURI);

if (!feed) {
return undefined;
}

if (!feed.reply) {
return { rootFeed: feed, depth };
}

return this.getRootAndDepth(feed.reply.parent.uri, depth + 1);
}
}
Loading

0 comments on commit 8411136

Please sign in to comment.