From c15194020f67304a7253aa1806c6faedfbdfa23a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 16 Aug 2023 21:53:51 -0500 Subject: [PATCH] Delete event.ts, use nostr-tools types --- src/admin.ts | 6 ++-- src/app.ts | 12 +++++-- src/client.ts | 41 ++++++++++++------------ src/controllers/api/statuses.ts | 3 +- src/controllers/nostr/relay.ts | 5 ++- src/db/events.ts | 11 +++---- src/deps.ts | 2 ++ src/event.ts | 22 ------------- src/firehose.ts | 10 +++--- src/middleware/auth98.ts | 3 +- src/mixer.ts | 15 ++++----- src/sign.ts | 12 +++---- src/transformers/nostr-to-activitypub.ts | 2 +- src/transformers/nostr-to-mastoapi.ts | 3 +- src/utils.ts | 3 +- 15 files changed, 62 insertions(+), 88 deletions(-) delete mode 100644 src/event.ts diff --git a/src/admin.ts b/src/admin.ts index ee26a7ed..85e2fd88 100644 --- a/src/admin.ts +++ b/src/admin.ts @@ -1,10 +1,8 @@ import { Conf } from '@/config.ts'; -import { finishEvent, nip19 } from '@/deps.ts'; - -import type { EventTemplate, SignedEvent } from '@/event.ts'; +import { type Event, type EventTemplate, finishEvent, nip19 } from '@/deps.ts'; // deno-lint-ignore require-await -async function signAdminEvent(event: EventTemplate): Promise> { +async function signAdminEvent(event: EventTemplate): Promise> { if (!Conf.nsec) throw new Error('No secret key. Set one with DITTO_NSEC.'); const result = nip19.decode(Conf.nsec); diff --git a/src/app.ts b/src/app.ts index 6968b0d8..39df1a75 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,5 +1,13 @@ -import { type Context, cors, type Handler, Hono, type HonoEnv, logger, type MiddlewareHandler } from '@/deps.ts'; -import { type Event } from '@/event.ts'; +import { + type Context, + cors, + type Event, + type Handler, + Hono, + type HonoEnv, + logger, + type MiddlewareHandler, +} from '@/deps.ts'; import '@/firehose.ts'; import { actorController } from './controllers/activitypub/actor.ts'; diff --git a/src/client.ts b/src/client.ts index 6959b9f6..38a6b226 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,6 +1,5 @@ import { Conf } from '@/config.ts'; -import { Author, type Filter, findReplyTag, matchFilters, RelayPool, TTLCache } from '@/deps.ts'; -import { type Event, type SignedEvent } from '@/event.ts'; +import { Author, type Event, type Filter, findReplyTag, matchFilters, RelayPool, TTLCache } from '@/deps.ts'; import { eventDateComparator, type PaginationParams, Time } from '@/utils.ts'; import type { GetFiltersOpts } from '@/types.ts'; @@ -30,15 +29,15 @@ function getPool(): Pool { } /** Get events from a NIP-01 filter. */ -function getFilters(filters: Filter[], opts: GetFiltersOpts = {}): Promise[]> { +function getFilters(filters: Filter[], opts: GetFiltersOpts = {}): Promise[]> { return new Promise((resolve) => { let tid: number; - const results: SignedEvent[] = []; + const results: Event[] = []; const unsub = getPool().subscribe( filters, Conf.poolRelays, - (event: SignedEvent | null) => { + (event: Event | null) => { if (event && matchFilters(filters, event)) { results.push({ id: event.id, @@ -53,41 +52,41 @@ function getFilters(filters: Filter[], opts: GetFiltersOpts if (typeof opts.limit === 'number' && results.length >= opts.limit) { unsub(); clearTimeout(tid); - resolve(results as SignedEvent[]); + resolve(results as Event[]); } }, undefined, () => { unsub(); clearTimeout(tid); - resolve(results as SignedEvent[]); + resolve(results as Event[]); }, ); if (typeof opts.timeout === 'number') { tid = setTimeout(() => { unsub(); - resolve(results as SignedEvent[]); + resolve(results as Event[]); }, opts.timeout); } }); } /** Get a Nostr event by its ID. */ -const getEvent = async (id: string, kind?: K): Promise | undefined> => { - const event = await (getPool().getEventById(id, Conf.poolRelays, 0) as Promise); +const getEvent = async (id: string, kind?: K): Promise | undefined> => { + const event = await (getPool().getEventById(id, Conf.poolRelays, 0) as Promise); if (event) { if (event.id !== id) return undefined; if (kind && event.kind !== kind) return undefined; - return event as SignedEvent; + return event as Event; } }; /** Get a Nostr `set_medatadata` event for a user's pubkey. */ -const getAuthor = async (pubkey: string, timeout = 1000): Promise | undefined> => { +const getAuthor = async (pubkey: string, timeout = 1000): Promise | undefined> => { const author = new Author(getPool(), Conf.poolRelays, pubkey); - const event: SignedEvent<0> | null = await new Promise((resolve) => { + const event: Event<0> | null = await new Promise((resolve) => { setTimeout(resolve, timeout, null); return author.metaData(resolve, 0); }); @@ -96,7 +95,7 @@ const getAuthor = async (pubkey: string, timeout = 1000): Promise }; /** Get users the given pubkey follows. */ -const getFollows = async (pubkey: string): Promise | undefined> => { +const getFollows = async (pubkey: string): Promise | 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?) @@ -105,12 +104,12 @@ const getFollows = async (pubkey: string): Promise | undefined> = await db.set(['event3', pubkey], event); return event; } else { - return (await db.get>(['event3', pubkey])).value || undefined; + return (await db.get>(['event3', pubkey])).value || undefined; } }; /** Get events from people the user follows. */ -async function getFeed(event3: Event<3>, params: PaginationParams): Promise[]> { +async function getFeed(event3: Event<3>, params: PaginationParams): Promise[]> { const authors = event3.tags .filter((tag) => tag[0] === 'p') .map((tag) => tag[1]); @@ -123,12 +122,12 @@ async function getFeed(event3: Event<3>, params: PaginationParams): Promise[]; + 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[]> { +async function getPublicFeed(params: PaginationParams): Promise[]> { const results = await getFilters([{ kinds: [1], ...params }], { timeout: 5000 }); return results.sort(eventDateComparator); } @@ -151,12 +150,12 @@ async function getAncestors(event: Event<1>, result = [] as Event<1>[]): Promise return result.reverse(); } -function getDescendants(eventId: string): Promise[]> { - return getFilters([{ kinds: [1], '#e': [eventId] }], { limit: 200, timeout: 2000 }) as Promise[]>; +function getDescendants(eventId: string): Promise[]> { + return getFilters([{ kinds: [1], '#e': [eventId] }], { limit: 200, timeout: 2000 }) as Promise[]>; } /** Publish an event to the Nostr relay. */ -function publish(event: SignedEvent, relays = Conf.publishRelays): void { +function publish(event: Event, relays = Conf.publishRelays): void { console.log('Publishing event', event, relays); try { getPool().publish(event, relays); diff --git a/src/controllers/api/statuses.ts b/src/controllers/api/statuses.ts index 50ea0320..90d3be24 100644 --- a/src/controllers/api/statuses.ts +++ b/src/controllers/api/statuses.ts @@ -1,7 +1,6 @@ import { type AppController } from '@/app.ts'; import { getAncestors, getDescendants, getEvent, publish } from '@/client.ts'; -import { ISO6391, Kind, z } from '@/deps.ts'; -import { type Event } from '@/event.ts'; +import { type Event, ISO6391, Kind, z } from '@/deps.ts'; import { signEvent } from '@/sign.ts'; import { toStatus } from '@/transformers/nostr-to-mastoapi.ts'; import { nostrNow, parseBody } from '@/utils.ts'; diff --git a/src/controllers/nostr/relay.ts b/src/controllers/nostr/relay.ts index 8dcfd7da..b4d91d16 100644 --- a/src/controllers/nostr/relay.ts +++ b/src/controllers/nostr/relay.ts @@ -10,14 +10,13 @@ import { } from '@/schemas/nostr.ts'; import type { AppController } from '@/app.ts'; -import type { Filter } from '@/deps.ts'; -import type { SignedEvent } from '@/event.ts'; +import type { Event, Filter } from '@/deps.ts'; /** Limit of events returned per-filter. */ const FILTER_LIMIT = 100; type RelayMsg = - | ['EVENT', string, SignedEvent] + | ['EVENT', string, Event] | ['NOTICE', string] | ['EOSE', string] | ['OK', string, boolean, string]; diff --git a/src/db/events.ts b/src/db/events.ts index 8178710f..31ff6f92 100644 --- a/src/db/events.ts +++ b/src/db/events.ts @@ -1,10 +1,9 @@ import { db, type TagRow } from '@/db.ts'; -import { type Insertable } from '@/deps.ts'; -import { type SignedEvent } from '@/event.ts'; +import { type Event, type Insertable } from '@/deps.ts'; import type { DittoFilter, GetFiltersOpts } from '@/types.ts'; -type TagCondition = ({ event, count }: { event: SignedEvent; count: number }) => boolean; +type TagCondition = ({ event, count }: { event: Event; count: number }) => boolean; /** Conditions for when to index certain tags. */ const tagConditions: Record = { @@ -17,7 +16,7 @@ const tagConditions: Record = { }; /** Insert an event (and its tags) into the database. */ -function insertEvent(event: SignedEvent): Promise { +function insertEvent(event: Event): Promise { return db.transaction().execute(async (trx) => { await trx.insertInto('events') .values({ @@ -108,14 +107,14 @@ function getFilterQuery(filter: DittoFilter) { async function getFilters( filters: DittoFilter[], _opts?: GetFiltersOpts, -): Promise[]> { +): Promise[]> { const events = await filters .map(getFilterQuery) .reduce((acc, curr) => acc.union(curr)) .execute(); return events.map((event) => ( - { ...event, tags: JSON.parse(event.tags) } as SignedEvent + { ...event, tags: JSON.parse(event.tags) } as Event )); } diff --git a/src/deps.ts b/src/deps.ts index a65503c4..d8c9c8f7 100644 --- a/src/deps.ts +++ b/src/deps.ts @@ -11,6 +11,8 @@ export { cors, logger } from 'https://deno.land/x/hono@v3.3.4/middleware.ts'; export { z } from 'https://deno.land/x/zod@v3.21.4/mod.ts'; export { Author, RelayPool } from 'https://dev.jspm.io/nostr-relaypool@0.6.28'; export { + type Event, + type EventTemplate, type Filter, finishEvent, getEventHash, diff --git a/src/event.ts b/src/event.ts deleted file mode 100644 index f80bd15c..00000000 --- a/src/event.ts +++ /dev/null @@ -1,22 +0,0 @@ -interface EventTemplate { - kind: K; - tags: string[][]; - content: string; - created_at: number; -} - -interface UnsignedEvent extends EventTemplate { - pubkey: string; -} - -interface Event extends UnsignedEvent { - id?: string; - sig?: string; -} - -interface SignedEvent extends Event { - id: string; - sig: string; -} - -export type { Event, EventTemplate, SignedEvent, UnsignedEvent }; diff --git a/src/firehose.ts b/src/firehose.ts index 689ebc3f..c717c075 100644 --- a/src/firehose.ts +++ b/src/firehose.ts @@ -1,12 +1,10 @@ import { insertEvent, isLocallyFollowed } from '@/db/events.ts'; import { addRelays, getActiveRelays } from '@/db/relays.ts'; import { findUser } from '@/db/users.ts'; -import { RelayPool } from '@/deps.ts'; +import { type Event, RelayPool } from '@/deps.ts'; import { trends } from '@/trends.ts'; import { isRelay, nostrDate, nostrNow } from '@/utils.ts'; -import type { SignedEvent } from '@/event.ts'; - const relays = await getActiveRelays(); const pool = new RelayPool(relays); @@ -22,7 +20,7 @@ pool.subscribe( ); /** Handle events through the firehose pipeline. */ -async function handleEvent(event: SignedEvent): Promise { +async function handleEvent(event: Event): Promise { console.info(`firehose: Event<${event.kind}> ${event.id}`); trackHashtags(event); @@ -34,7 +32,7 @@ async function handleEvent(event: SignedEvent): Promise { } /** Track whenever a hashtag is used, for processing trending tags. */ -function trackHashtags(event: SignedEvent): void { +function trackHashtags(event: Event): void { const date = nostrDate(event.created_at); const tags = event.tags @@ -53,7 +51,7 @@ function trackHashtags(event: SignedEvent): void { } /** Tracks known relays in the database. */ -function trackRelays(event: SignedEvent) { +function trackRelays(event: Event) { const relays = new Set<`wss://${string}`>(); event.tags.forEach((tag) => { diff --git a/src/middleware/auth98.ts b/src/middleware/auth98.ts index 280af908..003ad488 100644 --- a/src/middleware/auth98.ts +++ b/src/middleware/auth98.ts @@ -1,7 +1,6 @@ import { type AppMiddleware } from '@/app.ts'; import { Conf } from '@/config.ts'; -import { HTTPException } from '@/deps.ts'; -import { type Event } from '@/event.ts'; +import { type Event, HTTPException } from '@/deps.ts'; import { decode64Schema, jsonSchema } from '@/schema.ts'; import { signedEventSchema } from '@/schemas/nostr.ts'; import { eventAge, findTag, sha256, Time } from '@/utils.ts'; diff --git a/src/mixer.ts b/src/mixer.ts index 426c6125..82b8ba99 100644 --- a/src/mixer.ts +++ b/src/mixer.ts @@ -1,31 +1,30 @@ -import { matchFilters } from '@/deps.ts'; +import { type Event, matchFilters } from '@/deps.ts'; import { getFilters as getFiltersClient } from '@/client.ts'; import { getFilters as getFiltersDB } from '@/db/events.ts'; import { eventDateComparator } from '@/utils.ts'; -import type { SignedEvent } from '@/event.ts'; import type { DittoFilter, GetFiltersOpts } from '@/types.ts'; /** Get filters from the database and pool, and mix the best results together. */ async function getFilters( filters: DittoFilter[], opts?: GetFiltersOpts, -): Promise[]> { +): Promise[]> { const results = await Promise.allSettled([ getFiltersClient(filters, opts), getFiltersDB(filters, opts), ]); const events = results - .filter((result): result is PromiseFulfilledResult[]> => result.status === 'fulfilled') + .filter((result): result is PromiseFulfilledResult[]> => result.status === 'fulfilled') .flatMap((result) => result.value); return unmixEvents(events, filters); } /** Combine and sort events to match the filters. */ -function unmixEvents(events: SignedEvent[], filters: DittoFilter[]): SignedEvent[] { +function unmixEvents(events: Event[], filters: DittoFilter[]): Event[] { events = dedupeEvents(events); events = takeNewestEvents(events); events = events.filter((event) => matchFilters(filters, event)); @@ -35,17 +34,17 @@ function unmixEvents(events: SignedEvent[], filters: DittoF } /** Deduplicate events by ID. */ -function dedupeEvents(events: SignedEvent[]): SignedEvent[] { +function dedupeEvents(events: Event[]): Event[] { return [...new Map(events.map((event) => [event.id, event])).values()]; } /** Take the newest events among replaceable ones. */ -function takeNewestEvents(events: SignedEvent[]): SignedEvent[] { +function takeNewestEvents(events: Event[]): Event[] { const isReplaceable = (kind: number) => kind === 0 || kind === 3 || (10000 <= kind && kind < 20000) || (30000 <= kind && kind < 40000); // Group events by author and kind. - const groupedEvents = events.reduce[]>>((acc, event) => { + const groupedEvents = events.reduce[]>>((acc, event) => { const key = `${event.pubkey}:${event.kind}`; const group = acc.get(key) || []; acc.set(key, [...group, event]); diff --git a/src/sign.ts b/src/sign.ts index c0bebca2..d78aa4ca 100644 --- a/src/sign.ts +++ b/src/sign.ts @@ -1,10 +1,8 @@ import { type AppContext } from '@/app.ts'; -import { getEventHash, getPublicKey, getSignature, HTTPException, z } from '@/deps.ts'; +import { type Event, type EventTemplate, getEventHash, getPublicKey, getSignature, HTTPException, z } from '@/deps.ts'; import { signedEventSchema } from '@/schemas/nostr.ts'; import { ws } from '@/stream.ts'; -import type { Event, EventTemplate, SignedEvent } from '@/event.ts'; - /** Get signing WebSocket from app context. */ function getSignStream(c: AppContext): WebSocket | undefined { const pubkey = c.get('pubkey'); @@ -27,18 +25,18 @@ const nostrStreamingEventSchema = z.object({ * - If a secret key is provided, it will be used to sign the event. * - If a signing WebSocket is provided, it will be used to sign the event. */ -async function signEvent(event: EventTemplate, c: AppContext): Promise> { +async function signEvent(event: EventTemplate, c: AppContext): Promise> { const seckey = c.get('seckey'); const stream = getSignStream(c); if (!seckey && stream) { try { - return await new Promise>((resolve, reject) => { + return await new Promise>((resolve, reject) => { const handleMessage = (e: MessageEvent) => { try { const { data: event } = nostrStreamingEventSchema.parse(JSON.parse(e.data)); stream.removeEventListener('message', handleMessage); - resolve(event as SignedEvent); + resolve(event as Event); } catch (_e) { // } @@ -67,7 +65,7 @@ async function signEvent(event: EventTemplate, c: (event as Event).id = getEventHash(event as Event); (event as Event).sig = getSignature(event as Event, seckey); - return event as SignedEvent; + return event as Event; } export { signEvent }; diff --git a/src/transformers/nostr-to-activitypub.ts b/src/transformers/nostr-to-activitypub.ts index f868087a..ca90625f 100644 --- a/src/transformers/nostr-to-activitypub.ts +++ b/src/transformers/nostr-to-activitypub.ts @@ -2,7 +2,7 @@ import { Conf } from '@/config.ts'; import { jsonMetaContentSchema } from '@/schemas/nostr.ts'; import { getPublicKeyPem } from '@/utils/rsa.ts'; -import type { Event } from '@/event.ts'; +import type { Event } from '@/deps.ts'; import type { Actor } from '@/schemas/activitypub.ts'; /** Nostr metadata event to ActivityPub actor. */ diff --git a/src/transformers/nostr-to-mastoapi.ts b/src/transformers/nostr-to-mastoapi.ts index 53ff85ed..54f1b2e6 100644 --- a/src/transformers/nostr-to-mastoapi.ts +++ b/src/transformers/nostr-to-mastoapi.ts @@ -2,8 +2,7 @@ import { isCWTag } from 'https://gitlab.com/soapbox-pub/mostr/-/raw/c67064aee5ad import { getAuthor } from '@/client.ts'; import { Conf } from '@/config.ts'; -import { findReplyTag, lodash, nip19, sanitizeHtml, TTLCache, unfurl, z } from '@/deps.ts'; -import { type Event } from '@/event.ts'; +import { type Event, findReplyTag, lodash, nip19, sanitizeHtml, TTLCache, unfurl, z } from '@/deps.ts'; import { verifyNip05Cached } from '@/nip05.ts'; import { getMediaLinks, type MediaLink, parseNoteContent } from '@/note.ts'; import { emojiTagSchema, filteredArray } from '@/schema.ts'; diff --git a/src/utils.ts b/src/utils.ts index 8716d634..face71dc 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,7 +1,6 @@ import { getAuthor } from '@/client.ts'; import { Conf } from '@/config.ts'; -import { type Context, nip19, parseFormData, z } from '@/deps.ts'; -import { type Event } from '@/event.ts'; +import { type Context, type Event, nip19, parseFormData, z } from '@/deps.ts'; import { lookupNip05Cached } from '@/nip05.ts'; /** Get the current time in Nostr format. */