micro_blog_repo_fetchers/src/nostr.ts
2024-07-07 13:55:08 +02:00

97 lines
2.7 KiB
TypeScript

import pino from "pino";
import WebSocket from "ws";
import { lambdaRequestTracker, pinoLambdaDestination } from "pino-lambda";
import { MicroBlogBackend } from "./pocketbase";
// custom destination formatter
const destination = pinoLambdaDestination();
const logger = pino({}, destination);
const withRequest = lambdaRequestTracker();
const pb = new MicroBlogBackend(logger);
const fetcherNpub = process.env.NOSTR_FETCHER_NPUB!;
const myNpub = process.env.NOSTR_ID!;
type NostrTag = ["t" | "r" | "imeta", string];
type NostrEvent = [
"EVENT" | "EOSE",
string,
{
id: string;
pubkey: string;
created_at: number;
kind: number; // 1
tags: NostrTag[];
content: string;
sig: string;
},
];
exports.run = async (event: any, context: any) => {
withRequest(event, context);
const events: NostrEvent[] = [];
// figure out when the last post of wasved
const lastSavedPost = await pb.getLatestPostBySource("nostr");
let since: number | undefined;
if (lastSavedPost) {
since = new Date(lastSavedPost.posted).getTime() / 1000;
}
// listen for new events for 30 seconds
logger.info("trying to connecting to nostr relay");
const ws = new WebSocket(process.env.NOSTR_RELAY!);
// Other Relay URLs
// "wss://nos.lol",
// "wss://nostr.wine",
// "wss://nostr.einundzwanzig.space",
ws.on("error", logger.error);
ws.on("message", function message(data: Buffer) {
const decodedData = JSON.parse(
Buffer.from(data).toString("utf8")
) as NostrEvent;
logger.info({ decodedData }, "recived a message from nostr relay");
if (decodedData[0] === "EVENT") {
events.push(decodedData);
}
});
ws.on("open", function open() {
logger.info("connection established");
ws.send(
JSON.stringify([
"REQ",
fetcherNpub,
{ kinds: [1], authors: [myNpub], ...(since ? { since } : {}) },
])
);
});
await new Promise((resolve) => setTimeout(resolve, 30000));
logger.info("closing connection to nostr relay");
ws.close();
logger.info("saving nostr posts");
for (const event of events) {
const post = await pb.savePost({
remoteId: event[2].id,
fullPost: event[2],
posted: new Date(event[2].created_at * 1000).toISOString(),
source: "nostr",
authorId: event[1],
});
for (const tag of event[2].tags) {
if (tag[0] === "t") {
await pb.setTag(tag[1], post.id);
} else if (tag[0] === "imeta") {
const value = tag[1];
// remove "url " from the start of the string
const url = value.slice(4);
await pb.saveAndSetImage(
{
remoteURL: url,
},
post.id
);
}
}
}
};