Runs every 1 hrs
Fork
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
import { encounteredIDs } from "https://esm.town/v/buttondown/encounteredIDs";
import { email } from "https://esm.town/v/std/email?v=9";
import { set } from "https://esm.town/v/std/set?v=11";
import { fetchJSON } from "https://esm.town/v/stevekrouse/fetchJSON?v=41";
export const runner = async () => {
const ALERT_STRINGS = [
"buttondown",
"buttondown.email",
];
// Let's not email ourselves when we post..
const USERNAME_DENYLIST = [
"buttondown.bsky.social",
];
const promises = ALERT_STRINGS.map(async function(keyword) {
let posts = await fetchJSON(
`https://search.bsky.social/search/posts?q=${keyword}`,
);
let newPosts = posts.filter((post) =>
!encounteredIDs.includes(post.tid)
&& !USERNAME_DENYLIST.includes(post.user.handle)
);
newPosts.map((post) => {
encounteredIDs.push(post.tid);
});
await Promise.all(newPosts.map(async (post) => {
const id = post.tid.split("/")[1];
// We intentionally dedupe subjects so that multiple responses all
// have their own threads; this makes it easier to figure out what
// I've seen and/or responded to over time.
await email({
html: `https://bsky.app/profile/${post.user.did}/post/${id}`,
subject: `New post in Bluesky for ${keyword} (${id})`,
});
}));
});
await Promise.all(promises);
await set("encounteredIDs", encounteredIDs);
};
👆 This is a val. Vals are TypeScript snippets of code, written in the browser and run on our servers. Create scheduled functions, email yourself, and persist small pieces of data — all from the browser.