Add queries module for getting events for certain application needs

This commit is contained in:
Alex Gleason 2023-08-17 13:12:24 -05:00
parent b1c5dd0e34
commit 870a6f5261
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7
8 changed files with 83 additions and 93 deletions

View File

@ -1,11 +1,9 @@
import { Conf } from '@/config.ts'; import { Conf } from '@/config.ts';
import { Author, type Event, type Filter, findReplyTag, matchFilters, RelayPool, TTLCache } from '@/deps.ts'; import { type Event, type Filter, matchFilters, RelayPool, TTLCache } from '@/deps.ts';
import { eventDateComparator, type PaginationParams, Time } from '@/utils.ts'; import { Time } from '@/utils.ts';
import type { GetFiltersOpts } from '@/types.ts'; import type { GetFiltersOpts } from '@/types.ts';
const db = await Deno.openKv();
type Pool = InstanceType<typeof RelayPool>; type Pool = InstanceType<typeof RelayPool>;
/** HACK: Websockets in Deno are finnicky... get a new pool every 30 minutes. */ /** HACK: Websockets in Deno are finnicky... get a new pool every 30 minutes. */
@ -72,88 +70,6 @@ function getFilters<K extends number>(filters: Filter<K>[], opts: GetFiltersOpts
}); });
} }
/** Get a Nostr event by its ID. */
const getEvent = async <K extends number = number>(id: string, kind?: K): Promise<Event<K> | undefined> => {
const event = await (getPool().getEventById(id, Conf.poolRelays, 0) as Promise<Event>);
if (event) {
if (event.id !== id) return undefined;
if (kind && event.kind !== kind) return undefined;
return event as Event<K>;
}
};
/** Get a Nostr `set_medatadata` event for a user's pubkey. */
const getAuthor = async (pubkey: string, timeout = 1000): Promise<Event<0> | undefined> => {
const author = new Author(getPool(), Conf.poolRelays, pubkey);
const event: Event<0> | null = await new Promise((resolve) => {
setTimeout(resolve, timeout, null);
return author.metaData(resolve, 0);
});
return event?.pubkey === pubkey ? event : undefined;
};
/** Get users the given pubkey follows. */
const getFollows = async (pubkey: string): Promise<Event<3> | undefined> => {
const [event] = await getFilters([{ authors: [pubkey], kinds: [3] }], { timeout: 5000 });
// TODO: figure out a better, more generic & flexible way to handle event cache (and timeouts?)
// Prewarm cache in GET `/api/v1/accounts/verify_credentials`
if (event) {
await db.set(['event3', pubkey], event);
return event;
} else {
return (await db.get<Event<3>>(['event3', pubkey])).value || undefined;
}
};
/** Get events from people the user follows. */
async function getFeed(event3: Event<3>, params: PaginationParams): Promise<Event<1>[]> {
const authors = event3.tags
.filter((tag) => tag[0] === 'p')
.map((tag) => tag[1]);
authors.push(event3.pubkey); // see own events in feed
const filter: Filter = {
authors,
kinds: [1],
...params,
};
const results = await getFilters([filter], { timeout: 5000 }) as Event<1>[];
return results.sort(eventDateComparator);
}
/** Get a feed of all known text notes. */
async function getPublicFeed(params: PaginationParams): Promise<Event<1>[]> {
const results = await getFilters([{ kinds: [1], ...params }], { timeout: 5000 });
return results.sort(eventDateComparator);
}
async function getAncestors(event: Event<1>, result = [] as Event<1>[]): Promise<Event<1>[]> {
if (result.length < 100) {
const replyTag = findReplyTag(event);
const inReplyTo = replyTag ? replyTag[1] : undefined;
if (inReplyTo) {
const parentEvent = await getEvent(inReplyTo, 1);
if (parentEvent) {
result.push(parentEvent);
return getAncestors(parentEvent, result);
}
}
}
return result.reverse();
}
function getDescendants(eventId: string): Promise<Event<1>[]> {
return getFilters([{ kinds: [1], '#e': [eventId] }], { limit: 200, timeout: 2000 }) as Promise<Event<1>[]>;
}
/** Publish an event to the Nostr relay. */ /** Publish an event to the Nostr relay. */
function publish(event: Event, relays = Conf.publishRelays): void { function publish(event: Event, relays = Conf.publishRelays): void {
console.log('Publishing event', event, relays); console.log('Publishing event', event, relays);
@ -164,4 +80,4 @@ function publish(event: Event, relays = Conf.publishRelays): void {
} }
} }
export { getAncestors, getAuthor, getDescendants, getEvent, getFeed, getFilters, getFollows, getPublicFeed, publish }; export { getFilters, publish };

View File

@ -1,5 +1,5 @@
import { getAuthor } from '@/client.ts';
import { findUser } from '@/db/users.ts'; import { findUser } from '@/db/users.ts';
import { getAuthor } from '@/queries.ts';
import { toActor } from '@/transformers/nostr-to-activitypub.ts'; import { toActor } from '@/transformers/nostr-to-activitypub.ts';
import { activityJson } from '@/utils.ts'; import { activityJson } from '@/utils.ts';

View File

@ -1,7 +1,8 @@
import { type AppController } from '@/app.ts'; import { type AppController } from '@/app.ts';
import { type Filter, findReplyTag, z } from '@/deps.ts'; import { type Filter, findReplyTag, z } from '@/deps.ts';
import { getAuthor, getFollows, publish } from '@/client.ts'; import { publish } from '@/client.ts';
import { getFilters } from '@/mixer.ts'; import { getFilters } from '@/mixer.ts';
import { getAuthor, getFollows } from '@/queries.ts';
import { jsonMetaContentSchema } from '@/schemas/nostr.ts'; import { jsonMetaContentSchema } from '@/schemas/nostr.ts';
import { signEvent } from '@/sign.ts'; import { signEvent } from '@/sign.ts';
import { toAccount, toStatus } from '@/transformers/nostr-to-mastoapi.ts'; import { toAccount, toStatus } from '@/transformers/nostr-to-mastoapi.ts';

View File

@ -1,6 +1,7 @@
import { type AppController } from '@/app.ts'; import { type AppController } from '@/app.ts';
import { getAncestors, getDescendants, getEvent, publish } from '@/client.ts'; import { publish } from '@/client.ts';
import { type Event, ISO6391, Kind, z } from '@/deps.ts'; import { type Event, ISO6391, Kind, z } from '@/deps.ts';
import { getAncestors, getDescendants, getEvent } from '@/queries.ts';
import { signEvent } from '@/sign.ts'; import { signEvent } from '@/sign.ts';
import { toStatus } from '@/transformers/nostr-to-mastoapi.ts'; import { toStatus } from '@/transformers/nostr-to-mastoapi.ts';
import { nostrNow, parseBody } from '@/utils.ts'; import { nostrNow, parseBody } from '@/utils.ts';

View File

@ -1,4 +1,4 @@
import { getFeed, getFollows, getPublicFeed } from '@/client.ts'; import { getFeed, getFollows, getPublicFeed } from '@/queries.ts';
import { toStatus } from '@/transformers/nostr-to-mastoapi.ts'; import { toStatus } from '@/transformers/nostr-to-mastoapi.ts';
import { buildLinkHeader, paginationSchema } from '@/utils.ts'; import { buildLinkHeader, paginationSchema } from '@/utils.ts';

72
src/queries.ts Normal file
View File

@ -0,0 +1,72 @@
import { type Event, type Filter, findReplyTag } from '@/deps.ts';
import { eventDateComparator, type PaginationParams } from '@/utils.ts';
import { getFilters as getFiltersMixer } from './mixer.ts';
/** Get a Nostr event by its ID. */
const getEvent = async <K extends number = number>(id: string, kind?: K): Promise<Event<K> | undefined> => {
const filter: Filter<K> = { ids: [id], limit: 1 };
if (kind) filter.kinds = [kind];
const [event] = await getFiltersMixer([filter], { limit: 1, timeout: 1000 });
return event;
};
/** Get a Nostr `set_medatadata` event for a user's pubkey. */
const getAuthor = async (pubkey: string, timeout = 1000): Promise<Event<0> | undefined> => {
const [event] = await getFiltersMixer([{ authors: [pubkey], kinds: [0] }], { timeout });
return event;
};
/** Get users the given pubkey follows. */
const getFollows = async (pubkey: string): Promise<Event<3> | undefined> => {
const [event] = await getFiltersMixer([{ authors: [pubkey], kinds: [3] }], { timeout: 5000 });
return event;
};
/** Get events from people the user follows. */
async function getFeed(event3: Event<3>, params: PaginationParams): Promise<Event<1>[]> {
const authors = event3.tags
.filter((tag) => tag[0] === 'p')
.map((tag) => tag[1]);
authors.push(event3.pubkey); // see own events in feed
const filter: Filter = {
authors,
kinds: [1],
...params,
};
const results = await getFiltersMixer([filter], { timeout: 5000 }) as Event<1>[];
return results.sort(eventDateComparator);
}
/** Get a feed of all known text notes. */
async function getPublicFeed(params: PaginationParams): Promise<Event<1>[]> {
const results = await getFiltersMixer([{ kinds: [1], ...params }], { timeout: 5000 });
return results.sort(eventDateComparator);
}
async function getAncestors(event: Event<1>, result = [] as Event<1>[]): Promise<Event<1>[]> {
if (result.length < 100) {
const replyTag = findReplyTag(event);
const inReplyTo = replyTag ? replyTag[1] : undefined;
if (inReplyTo) {
const parentEvent = await getEvent(inReplyTo, 1);
if (parentEvent) {
result.push(parentEvent);
return getAncestors(parentEvent, result);
}
}
}
return result.reverse();
}
function getDescendants(eventId: string): Promise<Event<1>[]> {
return getFiltersMixer([{ kinds: [1], '#e': [eventId] }], { limit: 200, timeout: 2000 }) as Promise<Event<1>[]>;
}
export { getAncestors, getAuthor, getDescendants, getEvent, getFeed, getFollows, getPublicFeed };

View File

@ -1,10 +1,10 @@
import { isCWTag } from 'https://gitlab.com/soapbox-pub/mostr/-/raw/c67064aee5ade5e01597c6d23e22e53c628ef0e2/src/nostr/tags.ts'; import { isCWTag } from 'https://gitlab.com/soapbox-pub/mostr/-/raw/c67064aee5ade5e01597c6d23e22e53c628ef0e2/src/nostr/tags.ts';
import { getAuthor } from '@/client.ts';
import { Conf } from '@/config.ts'; import { Conf } from '@/config.ts';
import { type Event, findReplyTag, lodash, nip19, sanitizeHtml, TTLCache, unfurl, z } from '@/deps.ts'; import { type Event, findReplyTag, lodash, nip19, sanitizeHtml, TTLCache, unfurl, z } from '@/deps.ts';
import { verifyNip05Cached } from '@/nip05.ts'; import { verifyNip05Cached } from '@/nip05.ts';
import { getMediaLinks, type MediaLink, parseNoteContent } from '@/note.ts'; import { getMediaLinks, type MediaLink, parseNoteContent } from '@/note.ts';
import { getAuthor } from '@/queries.ts';
import { emojiTagSchema, filteredArray } from '@/schema.ts'; import { emojiTagSchema, filteredArray } from '@/schema.ts';
import { jsonMetaContentSchema } from '@/schemas/nostr.ts'; import { jsonMetaContentSchema } from '@/schemas/nostr.ts';
import { type Nip05, nostrDate, parseNip05, Time } from '@/utils.ts'; import { type Nip05, nostrDate, parseNip05, Time } from '@/utils.ts';

View File

@ -1,7 +1,7 @@
import { getAuthor } from '@/client.ts';
import { Conf } from '@/config.ts'; import { Conf } from '@/config.ts';
import { type Context, type Event, nip19, parseFormData, z } from '@/deps.ts'; import { type Context, type Event, nip19, parseFormData, z } from '@/deps.ts';
import { lookupNip05Cached } from '@/nip05.ts'; import { lookupNip05Cached } from '@/nip05.ts';
import { getAuthor } from '@/queries.ts';
/** Get the current time in Nostr format. */ /** Get the current time in Nostr format. */
const nostrNow = () => Math.floor(new Date().getTime() / 1000); const nostrNow = () => Math.floor(new Date().getTime() / 1000);