Back to packages list

Vals using feed

Description from the NPM package:
Feed is a RSS, Atom and JSON feed generator for Node.js, making content syndication simple and intuitive!
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
import { DOMParser } from "https://deno.land/x/deno_dom/deno-dom-wasm.ts";
import { Feed } from "npm:feed";
import ky from "npm:ky";
const BASE_URL = "https://midnight.pub";
// @see: https://git.sr.ht/~m15o/midnight-pub/tree/master/item/model/user.go#L28
const reUserFeed = /^\/(?<username>~[a-z0-9-_]+)\.(?<format>atom|rss)$/;
export default async function(req: Request): Promise<Response> {
const { pathname } = new URL(req.url);
const match = pathname.match(reUserFeed);
if (!match) {
return new Response(null, { status: 400 });
}
const { format, username } = match.groups;
const profileURL = new URL(`/${username}`, BASE_URL);
const posts = await grabPosts(profileURL);
const feed = new Feed({
id: profileURL.href,
link: profileURL.href,
title: username,
description: `${username}'s posts`,
author: {
name: username,
link: profileURL.href,
},
feedLinks: {
atom: req.url,
rss: req.url,
},
copyright: undefined, // I have no idea what to put there ¯\_(ツ)_/¯
});
posts.forEach(post =>
feed.addItem({
id: post.href,
link: post.href,
date: post.createdAt,
title: post.title,
description: post.title,
})
);
if (format === "rss") {
const headers = { "content-type": "application/xml" };
return new Response(feed.rss2(), { headers });
}
const headers = { "content-type": "application/atom+xml" };
return new Response(feed.atom1(), { headers });
}
async function grabPosts(profileURL: string | URL) {
const html = await ky.get(profileURL).text();
const doc = new DOMParser().parseFromString(html, "text/html");
const posts: HTMLAnchorElement[] = doc.querySelectorAll("a[href^=\"/posts/\"]");
return Array.from(posts).map(post => {
const href = new URL(post.getAttribute("href"), BASE_URL).href;
const offset = post.textContent.indexOf(" ");
const createdAt = new Date(post.textContent.slice(0, offset));
const title = post.textContent.slice(offset + 1);
return { href, createdAt, title };
});
}

Creates an RSS feed from a given job board hosted on Greenhouse (https://www.greenhouse.com/).

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
import { fetch } from "https://esm.town/v/std/fetch";
export async function greenhouseToRSS(req: Request) {
const { Feed } = await import("npm:feed");
const searchParams = new URL(req.url).searchParams;
const account = searchParams.get("account");
const mode = searchParams.get("mode");
const SOURCE_URL = `https://boards-api.greenhouse.io/v1/boards/${account}/jobs`;
const { jobs: sourceJobs } = await fetch(SOURCE_URL).then((res) => res.json());
const getFeedLink = (mode) => `https://donmccurdy-greenhousetorss.express.val.run/?account=${account}&mode=${mode}`;
const feed = new Feed({
title: `Jobs: ${account} (via Greenhouse)`,
description: "Job postings automatically forwarded from Greenhouse API to RSS.",
id: getFeedLink(mode),
link: "https://www.val.town/v/donmccurdy.greenhouseToRSS",
generator: "leverToRSS",
feedLinks: {
rss: getFeedLink("rss"),
json: getFeedLink("json"),
atom: getFeedLink("atom"),
},
} as any);
const categorySet = new Set();
for (const job of sourceJobs) {
const item = {
title: job.title,
id: job.id,
link: job.absolute_url,
description: `Listing for ${job.title}, posted ${new Date(job.updated_at).toLocaleDateString()}`,
content: `
<ul>
<li>Title: ${job.title}</li>
<li>Location: ${job.location?.name || ""}</li>
<li>Updated: ${new Date(job.updated_at).toLocaleDateString()}</li>
<li>Link: <a href="${job.absolute_url}" target="_blank">${job.absolute_url}</a></li>
</ul>
`.trim(),
date: new Date(job.updated_at),
};
feed.addItem(item);
}
switch (mode) {
case "rss":
return new Response(feed.rss2(), { headers: { "Content-Type": "application/xml" } });
case "atom":
return new Response(feed.atom1(), { headers: { "Content-Type": "application/xml" } });
case "json":
return Response.json(JSON.parse(feed.json1()));
default:
throw new Error(`Unknown mode, "${mode}".`);
}
}

Creates an RSS feed from a given job board hosted on Lever (https://www.lever.co/).

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
import { fetch } from "https://esm.town/v/std/fetch";
export async function leverToRSS(req: Request) {
const { Feed } = await import("npm:feed");
const { XMLParser } = await import("npm:fast-xml-parser");
const searchParams = new URL(req.url).searchParams;
const account = searchParams.get("account");
const mode = searchParams.get("mode");
const SOURCE_URL = `https://api.lever.co/v0/postings/${account}?mode=xml`;
const parser = new XMLParser();
const sourceXML = await fetch(SOURCE_URL).then((res) => res.text());
const { job: sourceJobs } = parser.parse(sourceXML).jobs || { job: [] };
const getFeedLink = (mode) => `https://donmccurdy-levertorss.express.val.run/?account=${account}&mode=${mode}`;
const feed = new Feed({
title: `Jobs: ${account} (via Lever)`,
description: "Job postings automatically forwarded from Lever XML feed to RSS.",
id: getFeedLink(mode),
link: "https://www.val.town/v/donmccurdy.leverToRSS",
generator: "leverToRSS",
feedLinks: {
rss: getFeedLink("rss"),
json: getFeedLink("json"),
atom: getFeedLink("atom"),
},
} as any);
const categorySet = new Set();
for (const job of sourceJobs) {
const item = {
title: job.position,
id: job.id,
link: job.apply_url,
description: `Listing for ${job.position}, posted ${new Date(job.post_date).toLocaleDateString()}`,
content: job.description,
date: new Date(job.post_date),
categories: [
job.employer,
job.category,
job.location,
],
};
for (const category of item.categories) {
categorySet.add(category);
}
feed.addItem(item);
}
const categoryList = Array.from(categorySet)
.filter((category) => !!category)
.sort() as string[];
for (const category of categoryList) {
feed.addCategory(category);
}
switch (mode) {
case "rss":
return new Response(feed.rss2(), { headers: { "Content-Type": "application/xml" } });
case "atom":
return new Response(feed.atom1(), { headers: { "Content-Type": "application/xml" } });
case "json":
return Response.json(JSON.parse(feed.json1()));
default:
throw new Error(`Unknown mode, "${mode}".`);
}
}
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
import { fetch } from "https://esm.town/v/std/fetch";
export async function basicsWithBabishRss(req: express.Request, res) {
const { Feed } = await import("npm:feed");
// function toTitleCase(str) {
// return str.replace(/\w\S*/g, function (txt) {
// return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
// });
// }
async function generateRssFeed() {
const { parseDocument } = await import("npm:htmlparser2");
const response = await fetch("https://basicswithbabish.co/episodes");
const body = await response.text();
const dom = parseDocument(body);
const feed = new Feed({
title: "Basics with Babish",
description: "Episodes of basics with babish",
link: "https://basicswithbabish.co/episodes",
generator: "DIIT",
});
const elements = dom.querySelectorAll(".summary-item");
elements.forEach((element) => {
const rawTitle = element.querySelector(".summary-title-link")
?.textContent;
const rawLink = element.querySelector(".summary-thumbnail-container")
?.getAttribute("href");
const img = element.querySelector(".summary-thumbnail-image")
?.getAttribute("src");
const title = rawTitle;
const link = `https://basicswithbabish.co${rawLink}`;
feed.addItem({
id: link,
title,
link,
image: img,
});
});
return feed.rss2();
}
res.type("application/rss+xml");
res.end(generateRssFeed());
}
1
Next