Merge branch 'event-type-refactor' into 'develop'

Delete event.ts, use nostr-tools types

See merge request soapbox-pub/ditto!11
This commit is contained in:
Alex Gleason 2023-08-17 02:55:42 +00:00
commit b185d54f8d
15 changed files with 62 additions and 88 deletions

View file

@ -1,10 +1,8 @@
import { Conf } from '@/config.ts'; import { Conf } from '@/config.ts';
import { finishEvent, nip19 } from '@/deps.ts'; import { type Event, type EventTemplate, finishEvent, nip19 } from '@/deps.ts';
import type { EventTemplate, SignedEvent } from '@/event.ts';
// deno-lint-ignore require-await // deno-lint-ignore require-await
async function signAdminEvent<K extends number = number>(event: EventTemplate<K>): Promise<SignedEvent<K>> { async function signAdminEvent<K extends number = number>(event: EventTemplate<K>): Promise<Event<K>> {
if (!Conf.nsec) throw new Error('No secret key. Set one with DITTO_NSEC.'); if (!Conf.nsec) throw new Error('No secret key. Set one with DITTO_NSEC.');
const result = nip19.decode(Conf.nsec); const result = nip19.decode(Conf.nsec);

View file

@ -1,5 +1,13 @@
import { type Context, cors, type Handler, Hono, type HonoEnv, logger, type MiddlewareHandler } from '@/deps.ts'; import {
import { type Event } from '@/event.ts'; type Context,
cors,
type Event,
type Handler,
Hono,
type HonoEnv,
logger,
type MiddlewareHandler,
} from '@/deps.ts';
import '@/firehose.ts'; import '@/firehose.ts';
import { actorController } from './controllers/activitypub/actor.ts'; import { actorController } from './controllers/activitypub/actor.ts';

View file

@ -1,6 +1,5 @@
import { Conf } from '@/config.ts'; import { Conf } from '@/config.ts';
import { Author, type Filter, findReplyTag, matchFilters, RelayPool, TTLCache } from '@/deps.ts'; import { Author, type Event, type Filter, findReplyTag, matchFilters, RelayPool, TTLCache } from '@/deps.ts';
import { type Event, type SignedEvent } from '@/event.ts';
import { eventDateComparator, type PaginationParams, Time } from '@/utils.ts'; import { eventDateComparator, type PaginationParams, Time } from '@/utils.ts';
import type { GetFiltersOpts } from '@/types.ts'; import type { GetFiltersOpts } from '@/types.ts';
@ -30,15 +29,15 @@ function getPool(): Pool {
} }
/** Get events from a NIP-01 filter. */ /** Get events from a NIP-01 filter. */
function getFilters<K extends number>(filters: Filter<K>[], opts: GetFiltersOpts = {}): Promise<SignedEvent<K>[]> { function getFilters<K extends number>(filters: Filter<K>[], opts: GetFiltersOpts = {}): Promise<Event<K>[]> {
return new Promise((resolve) => { return new Promise((resolve) => {
let tid: number; let tid: number;
const results: SignedEvent[] = []; const results: Event[] = [];
const unsub = getPool().subscribe( const unsub = getPool().subscribe(
filters, filters,
Conf.poolRelays, Conf.poolRelays,
(event: SignedEvent | null) => { (event: Event | null) => {
if (event && matchFilters(filters, event)) { if (event && matchFilters(filters, event)) {
results.push({ results.push({
id: event.id, id: event.id,
@ -53,41 +52,41 @@ function getFilters<K extends number>(filters: Filter<K>[], opts: GetFiltersOpts
if (typeof opts.limit === 'number' && results.length >= opts.limit) { if (typeof opts.limit === 'number' && results.length >= opts.limit) {
unsub(); unsub();
clearTimeout(tid); clearTimeout(tid);
resolve(results as SignedEvent<K>[]); resolve(results as Event<K>[]);
} }
}, },
undefined, undefined,
() => { () => {
unsub(); unsub();
clearTimeout(tid); clearTimeout(tid);
resolve(results as SignedEvent<K>[]); resolve(results as Event<K>[]);
}, },
); );
if (typeof opts.timeout === 'number') { if (typeof opts.timeout === 'number') {
tid = setTimeout(() => { tid = setTimeout(() => {
unsub(); unsub();
resolve(results as SignedEvent<K>[]); resolve(results as Event<K>[]);
}, opts.timeout); }, opts.timeout);
} }
}); });
} }
/** Get a Nostr event by its ID. */ /** Get a Nostr event by its ID. */
const getEvent = async <K extends number = number>(id: string, kind?: K): Promise<SignedEvent<K> | undefined> => { 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<SignedEvent>); const event = await (getPool().getEventById(id, Conf.poolRelays, 0) as Promise<Event>);
if (event) { if (event) {
if (event.id !== id) return undefined; if (event.id !== id) return undefined;
if (kind && event.kind !== kind) return undefined; if (kind && event.kind !== kind) return undefined;
return event as SignedEvent<K>; return event as Event<K>;
} }
}; };
/** Get a Nostr `set_medatadata` event for a user's pubkey. */ /** Get a Nostr `set_medatadata` event for a user's pubkey. */
const getAuthor = async (pubkey: string, timeout = 1000): Promise<SignedEvent<0> | undefined> => { const getAuthor = async (pubkey: string, timeout = 1000): Promise<Event<0> | undefined> => {
const author = new Author(getPool(), Conf.poolRelays, pubkey); 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); setTimeout(resolve, timeout, null);
return author.metaData(resolve, 0); return author.metaData(resolve, 0);
}); });
@ -96,7 +95,7 @@ const getAuthor = async (pubkey: string, timeout = 1000): Promise<SignedEvent<0>
}; };
/** Get users the given pubkey follows. */ /** Get users the given pubkey follows. */
const getFollows = async (pubkey: string): Promise<SignedEvent<3> | undefined> => { const getFollows = async (pubkey: string): Promise<Event<3> | undefined> => {
const [event] = await getFilters([{ authors: [pubkey], kinds: [3] }], { timeout: 5000 }); 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?) // 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<SignedEvent<3> | undefined> =
await db.set(['event3', pubkey], event); await db.set(['event3', pubkey], event);
return event; return event;
} else { } else {
return (await db.get<SignedEvent<3>>(['event3', pubkey])).value || undefined; return (await db.get<Event<3>>(['event3', pubkey])).value || undefined;
} }
}; };
/** Get events from people the user follows. */ /** Get events from people the user follows. */
async function getFeed(event3: Event<3>, params: PaginationParams): Promise<SignedEvent<1>[]> { async function getFeed(event3: Event<3>, params: PaginationParams): Promise<Event<1>[]> {
const authors = event3.tags const authors = event3.tags
.filter((tag) => tag[0] === 'p') .filter((tag) => tag[0] === 'p')
.map((tag) => tag[1]); .map((tag) => tag[1]);
@ -123,12 +122,12 @@ async function getFeed(event3: Event<3>, params: PaginationParams): Promise<Sign
...params, ...params,
}; };
const results = await getFilters([filter], { timeout: 5000 }) as SignedEvent<1>[]; const results = await getFilters([filter], { timeout: 5000 }) as Event<1>[];
return results.sort(eventDateComparator); return results.sort(eventDateComparator);
} }
/** Get a feed of all known text notes. */ /** Get a feed of all known text notes. */
async function getPublicFeed(params: PaginationParams): Promise<SignedEvent<1>[]> { async function getPublicFeed(params: PaginationParams): Promise<Event<1>[]> {
const results = await getFilters([{ kinds: [1], ...params }], { timeout: 5000 }); const results = await getFilters([{ kinds: [1], ...params }], { timeout: 5000 });
return results.sort(eventDateComparator); return results.sort(eventDateComparator);
} }
@ -151,12 +150,12 @@ async function getAncestors(event: Event<1>, result = [] as Event<1>[]): Promise
return result.reverse(); return result.reverse();
} }
function getDescendants(eventId: string): Promise<SignedEvent<1>[]> { function getDescendants(eventId: string): Promise<Event<1>[]> {
return getFilters([{ kinds: [1], '#e': [eventId] }], { limit: 200, timeout: 2000 }) as Promise<SignedEvent<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: SignedEvent, relays = Conf.publishRelays): void { function publish(event: Event, relays = Conf.publishRelays): void {
console.log('Publishing event', event, relays); console.log('Publishing event', event, relays);
try { try {
getPool().publish(event, relays); getPool().publish(event, relays);

View file

@ -1,7 +1,6 @@
import { type AppController } from '@/app.ts'; import { type AppController } from '@/app.ts';
import { getAncestors, getDescendants, getEvent, publish } from '@/client.ts'; import { getAncestors, getDescendants, getEvent, publish } from '@/client.ts';
import { ISO6391, Kind, z } from '@/deps.ts'; import { type Event, ISO6391, Kind, z } from '@/deps.ts';
import { type Event } from '@/event.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

@ -10,14 +10,13 @@ import {
} from '@/schemas/nostr.ts'; } from '@/schemas/nostr.ts';
import type { AppController } from '@/app.ts'; import type { AppController } from '@/app.ts';
import type { Filter } from '@/deps.ts'; import type { Event, Filter } from '@/deps.ts';
import type { SignedEvent } from '@/event.ts';
/** Limit of events returned per-filter. */ /** Limit of events returned per-filter. */
const FILTER_LIMIT = 100; const FILTER_LIMIT = 100;
type RelayMsg = type RelayMsg =
| ['EVENT', string, SignedEvent] | ['EVENT', string, Event]
| ['NOTICE', string] | ['NOTICE', string]
| ['EOSE', string] | ['EOSE', string]
| ['OK', string, boolean, string]; | ['OK', string, boolean, string];

View file

@ -1,10 +1,9 @@
import { db, type TagRow } from '@/db.ts'; import { db, type TagRow } from '@/db.ts';
import { type Insertable } from '@/deps.ts'; import { type Event, type Insertable } from '@/deps.ts';
import { type SignedEvent } from '@/event.ts';
import type { DittoFilter, GetFiltersOpts } from '@/types.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. */ /** Conditions for when to index certain tags. */
const tagConditions: Record<string, TagCondition> = { const tagConditions: Record<string, TagCondition> = {
@ -17,7 +16,7 @@ const tagConditions: Record<string, TagCondition> = {
}; };
/** Insert an event (and its tags) into the database. */ /** Insert an event (and its tags) into the database. */
function insertEvent(event: SignedEvent): Promise<void> { function insertEvent(event: Event): Promise<void> {
return db.transaction().execute(async (trx) => { return db.transaction().execute(async (trx) => {
await trx.insertInto('events') await trx.insertInto('events')
.values({ .values({
@ -108,14 +107,14 @@ function getFilterQuery(filter: DittoFilter) {
async function getFilters<K extends number>( async function getFilters<K extends number>(
filters: DittoFilter<K>[], filters: DittoFilter<K>[],
_opts?: GetFiltersOpts, _opts?: GetFiltersOpts,
): Promise<SignedEvent<K>[]> { ): Promise<Event<K>[]> {
const events = await filters const events = await filters
.map(getFilterQuery) .map(getFilterQuery)
.reduce((acc, curr) => acc.union(curr)) .reduce((acc, curr) => acc.union(curr))
.execute(); .execute();
return events.map((event) => ( return events.map((event) => (
{ ...event, tags: JSON.parse(event.tags) } as SignedEvent<K> { ...event, tags: JSON.parse(event.tags) } as Event<K>
)); ));
} }

View file

@ -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 { 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 { Author, RelayPool } from 'https://dev.jspm.io/nostr-relaypool@0.6.28';
export { export {
type Event,
type EventTemplate,
type Filter, type Filter,
finishEvent, finishEvent,
getEventHash, getEventHash,

View file

@ -1,22 +0,0 @@
interface EventTemplate<K extends number = number> {
kind: K;
tags: string[][];
content: string;
created_at: number;
}
interface UnsignedEvent<K extends number = number> extends EventTemplate<K> {
pubkey: string;
}
interface Event<K extends number = number> extends UnsignedEvent<K> {
id?: string;
sig?: string;
}
interface SignedEvent<K extends number = number> extends Event<K> {
id: string;
sig: string;
}
export type { Event, EventTemplate, SignedEvent, UnsignedEvent };

View file

@ -1,12 +1,10 @@
import { insertEvent, isLocallyFollowed } from '@/db/events.ts'; import { insertEvent, isLocallyFollowed } from '@/db/events.ts';
import { addRelays, getActiveRelays } from '@/db/relays.ts'; import { addRelays, getActiveRelays } from '@/db/relays.ts';
import { findUser } from '@/db/users.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 { trends } from '@/trends.ts';
import { isRelay, nostrDate, nostrNow } from '@/utils.ts'; import { isRelay, nostrDate, nostrNow } from '@/utils.ts';
import type { SignedEvent } from '@/event.ts';
const relays = await getActiveRelays(); const relays = await getActiveRelays();
const pool = new RelayPool(relays); const pool = new RelayPool(relays);
@ -22,7 +20,7 @@ pool.subscribe(
); );
/** Handle events through the firehose pipeline. */ /** Handle events through the firehose pipeline. */
async function handleEvent(event: SignedEvent): Promise<void> { async function handleEvent(event: Event): Promise<void> {
console.info(`firehose: Event<${event.kind}> ${event.id}`); console.info(`firehose: Event<${event.kind}> ${event.id}`);
trackHashtags(event); trackHashtags(event);
@ -34,7 +32,7 @@ async function handleEvent(event: SignedEvent): Promise<void> {
} }
/** Track whenever a hashtag is used, for processing trending tags. */ /** 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 date = nostrDate(event.created_at);
const tags = event.tags const tags = event.tags
@ -53,7 +51,7 @@ function trackHashtags(event: SignedEvent): void {
} }
/** Tracks known relays in the database. */ /** Tracks known relays in the database. */
function trackRelays(event: SignedEvent) { function trackRelays(event: Event) {
const relays = new Set<`wss://${string}`>(); const relays = new Set<`wss://${string}`>();
event.tags.forEach((tag) => { event.tags.forEach((tag) => {

View file

@ -1,7 +1,6 @@
import { type AppMiddleware } from '@/app.ts'; import { type AppMiddleware } from '@/app.ts';
import { Conf } from '@/config.ts'; import { Conf } from '@/config.ts';
import { HTTPException } from '@/deps.ts'; import { type Event, HTTPException } from '@/deps.ts';
import { type Event } from '@/event.ts';
import { decode64Schema, jsonSchema } from '@/schema.ts'; import { decode64Schema, jsonSchema } from '@/schema.ts';
import { signedEventSchema } from '@/schemas/nostr.ts'; import { signedEventSchema } from '@/schemas/nostr.ts';
import { eventAge, findTag, sha256, Time } from '@/utils.ts'; import { eventAge, findTag, sha256, Time } from '@/utils.ts';

View file

@ -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 getFiltersClient } from '@/client.ts';
import { getFilters as getFiltersDB } from '@/db/events.ts'; import { getFilters as getFiltersDB } from '@/db/events.ts';
import { eventDateComparator } from '@/utils.ts'; import { eventDateComparator } from '@/utils.ts';
import type { SignedEvent } from '@/event.ts';
import type { DittoFilter, GetFiltersOpts } from '@/types.ts'; import type { DittoFilter, GetFiltersOpts } from '@/types.ts';
/** Get filters from the database and pool, and mix the best results together. */ /** Get filters from the database and pool, and mix the best results together. */
async function getFilters<K extends number>( async function getFilters<K extends number>(
filters: DittoFilter<K>[], filters: DittoFilter<K>[],
opts?: GetFiltersOpts, opts?: GetFiltersOpts,
): Promise<SignedEvent<K>[]> { ): Promise<Event<K>[]> {
const results = await Promise.allSettled([ const results = await Promise.allSettled([
getFiltersClient(filters, opts), getFiltersClient(filters, opts),
getFiltersDB(filters, opts), getFiltersDB(filters, opts),
]); ]);
const events = results const events = results
.filter((result): result is PromiseFulfilledResult<SignedEvent<K>[]> => result.status === 'fulfilled') .filter((result): result is PromiseFulfilledResult<Event<K>[]> => result.status === 'fulfilled')
.flatMap((result) => result.value); .flatMap((result) => result.value);
return unmixEvents(events, filters); return unmixEvents(events, filters);
} }
/** Combine and sort events to match the filters. */ /** Combine and sort events to match the filters. */
function unmixEvents<K extends number>(events: SignedEvent<K>[], filters: DittoFilter<K>[]): SignedEvent<K>[] { function unmixEvents<K extends number>(events: Event<K>[], filters: DittoFilter<K>[]): Event<K>[] {
events = dedupeEvents(events); events = dedupeEvents(events);
events = takeNewestEvents(events); events = takeNewestEvents(events);
events = events.filter((event) => matchFilters(filters, event)); events = events.filter((event) => matchFilters(filters, event));
@ -35,17 +34,17 @@ function unmixEvents<K extends number>(events: SignedEvent<K>[], filters: DittoF
} }
/** Deduplicate events by ID. */ /** Deduplicate events by ID. */
function dedupeEvents<K extends number>(events: SignedEvent<K>[]): SignedEvent<K>[] { function dedupeEvents<K extends number>(events: Event<K>[]): Event<K>[] {
return [...new Map(events.map((event) => [event.id, event])).values()]; return [...new Map(events.map((event) => [event.id, event])).values()];
} }
/** Take the newest events among replaceable ones. */ /** Take the newest events among replaceable ones. */
function takeNewestEvents<K extends number>(events: SignedEvent<K>[]): SignedEvent<K>[] { function takeNewestEvents<K extends number>(events: Event<K>[]): Event<K>[] {
const isReplaceable = (kind: number) => const isReplaceable = (kind: number) =>
kind === 0 || kind === 3 || (10000 <= kind && kind < 20000) || (30000 <= kind && kind < 40000); kind === 0 || kind === 3 || (10000 <= kind && kind < 20000) || (30000 <= kind && kind < 40000);
// Group events by author and kind. // Group events by author and kind.
const groupedEvents = events.reduce<Map<string, SignedEvent<K>[]>>((acc, event) => { const groupedEvents = events.reduce<Map<string, Event<K>[]>>((acc, event) => {
const key = `${event.pubkey}:${event.kind}`; const key = `${event.pubkey}:${event.kind}`;
const group = acc.get(key) || []; const group = acc.get(key) || [];
acc.set(key, [...group, event]); acc.set(key, [...group, event]);

View file

@ -1,10 +1,8 @@
import { type AppContext } from '@/app.ts'; 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 { signedEventSchema } from '@/schemas/nostr.ts';
import { ws } from '@/stream.ts'; import { ws } from '@/stream.ts';
import type { Event, EventTemplate, SignedEvent } from '@/event.ts';
/** Get signing WebSocket from app context. */ /** Get signing WebSocket from app context. */
function getSignStream(c: AppContext): WebSocket | undefined { function getSignStream(c: AppContext): WebSocket | undefined {
const pubkey = c.get('pubkey'); 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 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. * - If a signing WebSocket is provided, it will be used to sign the event.
*/ */
async function signEvent<K extends number = number>(event: EventTemplate<K>, c: AppContext): Promise<SignedEvent<K>> { async function signEvent<K extends number = number>(event: EventTemplate<K>, c: AppContext): Promise<Event<K>> {
const seckey = c.get('seckey'); const seckey = c.get('seckey');
const stream = getSignStream(c); const stream = getSignStream(c);
if (!seckey && stream) { if (!seckey && stream) {
try { try {
return await new Promise<SignedEvent<K>>((resolve, reject) => { return await new Promise<Event<K>>((resolve, reject) => {
const handleMessage = (e: MessageEvent) => { const handleMessage = (e: MessageEvent) => {
try { try {
const { data: event } = nostrStreamingEventSchema.parse(JSON.parse(e.data)); const { data: event } = nostrStreamingEventSchema.parse(JSON.parse(e.data));
stream.removeEventListener('message', handleMessage); stream.removeEventListener('message', handleMessage);
resolve(event as SignedEvent<K>); resolve(event as Event<K>);
} catch (_e) { } catch (_e) {
// //
} }
@ -67,7 +65,7 @@ async function signEvent<K extends number = number>(event: EventTemplate<K>, c:
(event as Event<K>).id = getEventHash(event as Event<K>); (event as Event<K>).id = getEventHash(event as Event<K>);
(event as Event<K>).sig = getSignature(event as Event<K>, seckey); (event as Event<K>).sig = getSignature(event as Event<K>, seckey);
return event as SignedEvent<K>; return event as Event<K>;
} }
export { signEvent }; export { signEvent };

View file

@ -2,7 +2,7 @@ import { Conf } from '@/config.ts';
import { jsonMetaContentSchema } from '@/schemas/nostr.ts'; import { jsonMetaContentSchema } from '@/schemas/nostr.ts';
import { getPublicKeyPem } from '@/utils/rsa.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'; import type { Actor } from '@/schemas/activitypub.ts';
/** Nostr metadata event to ActivityPub actor. */ /** Nostr metadata event to ActivityPub actor. */

View file

@ -2,8 +2,7 @@ import { isCWTag } from 'https://gitlab.com/soapbox-pub/mostr/-/raw/c67064aee5ad
import { getAuthor } from '@/client.ts'; import { getAuthor } from '@/client.ts';
import { Conf } from '@/config.ts'; import { Conf } from '@/config.ts';
import { findReplyTag, lodash, nip19, sanitizeHtml, TTLCache, unfurl, z } from '@/deps.ts'; import { type Event, findReplyTag, lodash, nip19, sanitizeHtml, TTLCache, unfurl, z } from '@/deps.ts';
import { type Event } from '@/event.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 { emojiTagSchema, filteredArray } from '@/schema.ts'; import { emojiTagSchema, filteredArray } from '@/schema.ts';

View file

@ -1,7 +1,6 @@
import { getAuthor } from '@/client.ts'; import { getAuthor } from '@/client.ts';
import { Conf } from '@/config.ts'; import { Conf } from '@/config.ts';
import { type Context, nip19, parseFormData, z } from '@/deps.ts'; import { type Context, type Event, nip19, parseFormData, z } from '@/deps.ts';
import { type Event } from '@/event.ts';
import { lookupNip05Cached } from '@/nip05.ts'; import { lookupNip05Cached } from '@/nip05.ts';
/** Get the current time in Nostr format. */ /** Get the current time in Nostr format. */