Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// All of this is heavily based on RobPC's feed generator sample
// As well as the official bsky sample feed
type Feed = {
uri: string;
};
type DescribeFeedGeneratorOutput = {
did: string;
feeds: Feed[];
links?: {
privacyPolicy?: string;
termsOfService?: string;
};
};
type GetFeedSkeleton = {
feed: string;
limit?: number;
cursor?: string;
};
type SkeletonFeedPostReason = SkeletonReasonRepost;
// format: at-uri
type SkeletonReasonRepost = {
repost: string;
};
type SkeletonFeedPost = {
post: string;
};
type GetFeedSkeletonOutput = {
feed: SkeletonFeedPost[];
cursor?: string;
}
export async function feedGenerator({ feed, serviceDID, publisherDID, hostname }: {
serviceDID?: string;
publisherDID: string;
hostname: string;
feed: {
id: string;
displayName: string;
description?: string;
algorithm: (ctx, GetFeedSkeleton) => Promise<GetFeedSkeletonOutput>;
};
}) {
const { default: atproto } = await import("https://cdn.jsdelivr.net/npm/@atproto/api/+esm");
const { default: resolver } = await import("npm:@atproto/did-resolver");
const feedUri = atproto.AtUri.make(
publisherDID,
"app.bsky.feed.generator",
feed.id,
);
const query = (req: express.Request, name: string) => {
const q = req.query[name];
if (!q)
return [];
if (Array.isArray(q)) {
return q.map<string>((i) => i.toString());
}
return [q.toString()];
};
return async function (req: express.Request, res: express.Response) {
const serviceDid = serviceDID ?? `did:web:${hostname}`;
const didCache = new resolver.MemoryCache();
const didResolver = new resolver.DidResolver(
{ plcUrl: 'https://plc.directory',didCache, },
);
const ctx = {serviceDid, hostname, didResolver, didCache};
if (req.path === "/.well-known/did.json") {
res.json({
"@context": ["https://www.w3.org/ns/did/v1"],
id: serviceDid,
service: [
{
id: "#bsky_fg",
type: "BskyFeedGenerator",
serviceEndpoint: `https://${hostname}`,
},
],
});
}
else if (req.path === "/xrpc/app.bsky.feed.describeFeedGenerator") {
if (!serviceDid.endsWith(hostname)) {
return res.status(500).json({
error: "This feed generator has an invalid Service DID",
});
}
const feeds: Feed[] = [{ uri: feedUri }];
res.json({ did: serviceDid, feeds } satisfies DescribeFeedGeneratorOutput);
}
else if (req.path === "/xrpc/app.bsky.feed.getFeedSkeleton") {
const atUri = query(req, "feed")[0];
const limit = query(req, "limit")[0];
const cursor = query(req, "cursor")[0];
if (!atUri) {
res.status(400).json({ error: "Missing param 'feed'" });
return
}
const [did, collection, key] = atUri.replace("at://", "").split("/");
if (
did !== publisherDID ||
collection !== "app.bsky.feed.generator"
October 23, 2023