diff --git a/scripts/db-import.ts b/scripts/db-import.ts index 994728b0..e1484c99 100644 --- a/scripts/db-import.ts +++ b/scripts/db-import.ts @@ -5,117 +5,118 @@ import { nip19 } from 'npm:nostr-tools@^2.7.0'; import { DittoDB } from '@/db/DittoDB.ts'; import { EventsDB } from '@/storages/EventsDB.ts'; -import { NSchema, NRelay1, NostrEvent } from '@nostrify/nostrify'; - +import { NostrEvent, NRelay1, NSchema } from '@nostrify/nostrify'; const kysely = await DittoDB.getInstance(); const eventsDB = new EventsDB(kysely); interface ImportEventsOpts { - profilesOnly: boolean; + profilesOnly: boolean; } type DoEvent = (evt: NostrEvent) => void | Promise; -const importUsers = async (authors: string[], relays: string[], opts?: Partial, doEvent: DoEvent = async (evt: NostrEvent) => await eventsDB.event(evt)) => { - // Kind 0s + follow lists. - const profiles: Record> = {}; - // Kind 1s. - const notes = new Set(); +const importUsers = async ( + authors: string[], + relays: string[], + opts?: Partial, + doEvent: DoEvent = async (evt: NostrEvent) => await eventsDB.event(evt), +) => { + // Kind 0s + follow lists. + const profiles: Record> = {}; + // Kind 1s. + const notes = new Set(); - const { profilesOnly = false } = opts || {}; + const { profilesOnly = false } = opts || {}; - await Promise.all(relays.map(async relay => { - if (!relay.startsWith('wss://')) console.error(`Invalid relay url ${relay}`); - const conn = new NRelay1(relay); - const kinds = [0, 3]; - if (!profilesOnly) kinds.push(1); - const matched = await conn.query([{ kinds, authors, limit: 1000 }]); - await conn.close(); - await Promise.all( - matched.map(async event => { - const { kind, pubkey } = event; - if (kind === 1 && !notes.has(event.id)) { - // add the event to eventsDB only if it has not been found already. - notes.add(event.id); - await doEvent(event); - return; - } - - profiles[pubkey] ??= {}; - const existing = profiles[pubkey][kind]; - if (existing?.created_at > event.created_at) return; - else profiles[pubkey][kind] = event; - }) - ) - })) - - - for (const user in profiles) { - const profile = profiles[user]; - for (const kind in profile) { - await doEvent(profile[kind]); + await Promise.all(relays.map(async (relay) => { + if (!relay.startsWith('wss://')) console.error(`Invalid relay url ${relay}`); + const conn = new NRelay1(relay); + const kinds = [0, 3]; + if (!profilesOnly) kinds.push(1); + const matched = await conn.query([{ kinds, authors, limit: 1000 }]); + await conn.close(); + await Promise.all( + matched.map(async (event) => { + const { kind, pubkey } = event; + if (kind === 1 && !notes.has(event.id)) { + // add the event to eventsDB only if it has not been found already. + notes.add(event.id); + await doEvent(event); + return; } + + profiles[pubkey] ??= {}; + const existing = profiles[pubkey][kind]; + if (existing?.created_at > event.created_at) return; + else profiles[pubkey][kind] = event; + }), + ); + })); + + for (const user in profiles) { + const profile = profiles[user]; + for (const kind in profile) { + await doEvent(profile[kind]); } -} + } +}; if (import.meta.main) { - if (!Deno.args.length) { - showHelp(); + if (!Deno.args.length) { + showHelp(); + Deno.exit(1); + } + const pubkeys: string[] = []; + const relays: string[] = []; + + const opts: Partial = {}; + + let optionsEnd = false; + let relaySectionBegun = false; + for (const arg of Deno.args) { + if (arg.startsWith('-')) { + if (optionsEnd) { + console.error('Option encountered after end of options section.'); + showUsage(); + } + switch (arg) { + case '-p': + case '--profile-only': + console.info('Only importing profiles.'); + opts.profilesOnly = true; + break; + } + } else if (arg.startsWith('npub1')) { + optionsEnd = true; + + if (relaySectionBegun) { + console.error('npub specified in relay section'); Deno.exit(1); + } + const decoded = nip19.decode(arg as `npub1${string}`).data; + if (!NSchema.id().safeParse(decoded).success) { + console.error(`invalid pubkey ${arg}, skipping...`); + continue; + } + pubkeys.push(decoded); + } else { + relaySectionBegun = true; + if (!arg.startsWith('wss://')) { + console.error(`invalid relay url ${arg}, skipping...`); + } + relays.push(arg); } - const pubkeys: string[] = []; - const relays: string[] = []; + } - const opts: Partial = {}; - - let optionsEnd = false; - let relaySectionBegun = false; - for (const arg of Deno.args) { - if (arg.startsWith('-')) { - if (optionsEnd) { - console.error("Option encountered after end of options section."); - showUsage(); - } - switch (arg) { - case '-p': - case '--profile-only': - console.info('Only importing profiles.'); - opts.profilesOnly = true; - break; - } - } - else if (arg.startsWith('npub1')) { - optionsEnd = true; - - if (relaySectionBegun) { - console.error('npub specified in relay section'); - Deno.exit(1); - } - const decoded = nip19.decode(arg as `npub1${string}`).data; - if (!NSchema.id().safeParse(decoded).success) { - console.error(`invalid pubkey ${arg}, skipping...`); - continue; - } - pubkeys.push(decoded); - } - else { - relaySectionBegun = true; - if (!arg.startsWith('wss://')) { - console.error(`invalid relay url ${arg}, skipping...`); - } - relays.push(arg); - } - } - - await importUsers(pubkeys, relays, opts); - Deno.exit(0); + await importUsers(pubkeys, relays, opts); + Deno.exit(0); } function showHelp() { - console.info('ditto - db:import'); - console.info('Import users\' posts and kind 0s from a given set of relays.\n'); - showUsage(); - console.info(` + console.info('ditto - db:import'); + console.info("Import users' posts and kind 0s from a given set of relays.\n"); + showUsage(); + console.info(` OPTIONS: -p, --profile-only @@ -124,6 +125,8 @@ OPTIONS: } function showUsage() { - console.info('Usage: deno task db:import [options] npub1xxxxxx[ npub1yyyyyyy]...' + - ' wss://first.relay[ second.relay]...'); + console.info( + 'Usage: deno task db:import [options] npub1xxxxxx[ npub1yyyyyyy]...' + + ' wss://first.relay[ second.relay]...', + ); }