171 lines
4.7 KiB
TypeScript
171 lines
4.7 KiB
TypeScript
import { simpleFetchHandler, XRPC } from "@atcute/client";
|
|
import "@atcute/bluesky/lexicons";
|
|
import type {
|
|
AppBskyActorDefs,
|
|
AppBskyActorProfile,
|
|
AppBskyFeedPost,
|
|
At,
|
|
ComAtprotoRepoListRecords,
|
|
} from "@atcute/client/lexicons";
|
|
import type App from "../App.svelte";
|
|
// import { ComAtprotoRepoListRecords.Record } from "@atcute/client/lexicons";
|
|
// import { AppBskyFeedPost } from "@atcute/client/lexicons";
|
|
// import { AppBskyActorDefs } from "@atcute/client/lexicons";
|
|
|
|
interface AccountMetadata {
|
|
did: string;
|
|
displayName: string;
|
|
avatarCid: string | null;
|
|
}
|
|
class Post {
|
|
authorDid: string;
|
|
authorAvatarCid: string | null;
|
|
displayName: string;
|
|
text: string;
|
|
timestamp: number;
|
|
timenotstamp: string;
|
|
quotingDid: string | null;
|
|
replyingDid: string | null;
|
|
imagesCid: string[] | null;
|
|
videosLinkCid: string | null;
|
|
|
|
constructor(
|
|
record: ComAtprotoRepoListRecords.Record,
|
|
account: AccountMetadata,
|
|
) {
|
|
this.authorDid = account.did;
|
|
this.authorAvatarCid = account.avatarCid;
|
|
this.displayName = account.displayName;
|
|
const post = record.value as AppBskyFeedPost.Record;
|
|
this.timenotstamp = post.createdAt;
|
|
this.text = post.text;
|
|
this.timestamp = Date.parse(post.createdAt);
|
|
if (post.reply) {
|
|
this.replyingDid = didFromATuri(post.reply.parent.uri).repo;
|
|
} else {
|
|
this.replyingDid = null;
|
|
}
|
|
this.quotingDid = null;
|
|
this.imagesCid = null;
|
|
this.videosLinkCid = null;
|
|
switch (post.embed?.$type) {
|
|
case "app.bsky.embed.images":
|
|
this.imagesCid = post.embed.images.map((imageRecord: any) =>
|
|
imageRecord.image.ref.$link
|
|
);
|
|
break;
|
|
case "app.bsky.embed.video":
|
|
this.videosLinkCid = post.embed.video.ref.$link;
|
|
break;
|
|
case "app.bsky.embed.record":
|
|
this.quotingDid = didFromATuri(post.embed.record.uri).repo;
|
|
break;
|
|
case "app.bsky.embed.recordWithMedia":
|
|
this.quotingDid = didFromATuri(post.embed.record.record.uri).repo;
|
|
switch (post.embed.media.$type) {
|
|
case "app.bsky.embed.images":
|
|
this.imagesCid = post.embed.media.images.map((imageRecord) =>
|
|
imageRecord.image.ref.$link
|
|
);
|
|
|
|
break;
|
|
case "app.bsky.embed.video":
|
|
this.videosLinkCid = post.embed.media.video.ref.$link;
|
|
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const didFromATuri = (aturi: string) => {
|
|
const parts = aturi.split("/");
|
|
return {
|
|
repo: parts[2],
|
|
collection: parts[3],
|
|
rkey: parts[4],
|
|
};
|
|
};
|
|
|
|
const rpc = new XRPC({
|
|
handler: simpleFetchHandler({
|
|
service: "https://pds.witchcraft.systems",
|
|
}),
|
|
});
|
|
|
|
const getDidsFromPDS = async () => {
|
|
const { data } = await rpc.get("com.atproto.sync.listRepos", {
|
|
params: {},
|
|
});
|
|
return data.repos.map((repo: any) => (repo.did));
|
|
};
|
|
const getAccountMetadata = async (did: `did:${string}:${string}`) => {
|
|
// gonna assume self exists in the app.bsky.actor.profile
|
|
const { data } = await rpc.get("com.atproto.repo.getRecord", {
|
|
params: {
|
|
repo: did,
|
|
collection: "app.bsky.actor.profile",
|
|
rkey: "self",
|
|
},
|
|
});
|
|
const value = data.value as AppBskyActorProfile.Record;
|
|
const account: AccountMetadata = {
|
|
did: did,
|
|
displayName: value.displayName || "",
|
|
avatarCid: null,
|
|
};
|
|
if (value.avatar) {
|
|
account.avatarCid = value.avatar.ref["$link"];
|
|
}
|
|
return account;
|
|
};
|
|
|
|
const getAllMetadataFromPds = async () => {
|
|
const dids = await getDidsFromPDS();
|
|
const metadata = await Promise.all(
|
|
dids.map(async (repo: `did:${string}:${string}`) => {
|
|
return await getAccountMetadata(repo);
|
|
}),
|
|
);
|
|
return metadata;
|
|
};
|
|
|
|
const fetchPosts = async (did: string) => {
|
|
const { data } = await rpc.get("com.atproto.repo.listRecords", {
|
|
params: {
|
|
repo: did as At.Identifier,
|
|
collection: "app.bsky.feed.post",
|
|
limit: 5,
|
|
},
|
|
});
|
|
return {
|
|
records: data.records as ComAtprotoRepoListRecords.Record[],
|
|
did: did,
|
|
};
|
|
};
|
|
|
|
const fetchAllPosts = async () => {
|
|
const users: AccountMetadata[] = await getAllMetadataFromPds();
|
|
const postRecords = await Promise.all(
|
|
users.map(async (metadata: AccountMetadata) =>
|
|
await fetchPosts(metadata.did)
|
|
),
|
|
);
|
|
const posts: Post[] = postRecords.flatMap((userFetch) =>
|
|
userFetch.records.map((record) => {
|
|
const user = users.find((user: AccountMetadata) =>
|
|
user.did == userFetch.did
|
|
);
|
|
if (!user) {
|
|
throw new Error(`User with DID ${userFetch.did} not found`);
|
|
}
|
|
return new Post(record, user);
|
|
})
|
|
);
|
|
posts.sort((a, b) => b.timestamp - a.timestamp);
|
|
return posts;
|
|
};
|
|
|
|
export { fetchAllPosts, getAllMetadataFromPds, Post };
|
|
export type { AccountMetadata };
|