From 42d47abba17e0742368ff35f2ab079435532d318 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Wed, 2 Apr 2025 10:46:50 -0300 Subject: [PATCH] update readme and add more examples. --- README.md | 207 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 119 insertions(+), 88 deletions(-) diff --git a/README.md b/README.md index 5baf784..ad254cc 100644 --- a/README.md +++ b/README.md @@ -57,43 +57,43 @@ let event = finalizeEvent({ let isGood = verifyEvent(event) ``` -### Interacting with a relay +### Interacting with one or multiple relays + +Doesn't matter what you do, you always should be using a `SimplePool`: ```js import { finalizeEvent, generateSecretKey, getPublicKey } from 'nostr-tools/pure' -import { Relay } from 'nostr-tools/relay' +import { SimplePool } from 'nostr-tools/pool' -const relay = await Relay.connect('wss://relay.example.com') -console.log(`connected to ${relay.url}`) +const pool = new SimplePool() // let's query for an event that exists -const sub = relay.subscribe([ +const event = relay.get( + ['wss://relay.example.com'], { ids: ['d7dd5eb3ab747e16f8d0212d53032ea2a7cadef53837e5a6c66d42849fcb9027'], }, -], { - onevent(event) { - console.log('we got the event we wanted:', event) - }, - oneose() { - sub.close() - } -}) +) +if (event) { + console.log('it exists indeed on this relay:', event) +} // let's publish a new event while simultaneously monitoring the relay for it let sk = generateSecretKey() let pk = getPublicKey(sk) -relay.subscribe([ +pool.subscribe( + ['wss://a.com', 'wss://b.com', 'wss://c.com'], { kinds: [1], authors: [pk], }, -], { - onevent(event) { - console.log('got event:', event) + { + onevent(event) { + console.log('got event:', event) + } } -}) +) let eventTemplate = { kind: 1, @@ -104,7 +104,7 @@ let eventTemplate = { // this assigns the pubkey, calculates the event id and signs the event in a single step const signedEvent = finalizeEvent(eventTemplate, sk) -await relay.publish(signedEvent) +await pool.publish(['wss://a.com', 'wss://b.com'], signedEvent) relay.close() ``` @@ -119,59 +119,116 @@ import WebSocket from 'ws' useWebSocketImplementation(WebSocket) ``` -### Interacting with multiple relays +### Parsing references (mentions) from a content based on NIP-27 ```js -import { SimplePool } from 'nostr-tools/pool' +import * as nip27 from '@nostr/tools/nip27' -const pool = new SimplePool() - -let relays = ['wss://relay.example.com', 'wss://relay.example2.com'] - -let h = pool.subscribeMany( - [...relays, 'wss://relay.example3.com'], - [ - { - authors: ['32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245'], - }, - ], - { - onevent(event) { - // this will only be called once the first time the event is received - // ... - }, - oneose() { - h.close() +for (let block of nip27.parse(evt.content)) { + switch (block.type) { + case 'text': + console.log(block.text) + break + case 'reference': { + if ('id' in block.pointer) { + console.log("it's a nevent1 uri", block.pointer) + } else if ('identifier' in block.pointer) { + console.log("it's a naddr1 uri", block.pointer) + } else { + console.log("it's an npub1 or nprofile1 uri", block.pointer) + } + break } + case 'url': { + console.log("it's a normal url:", block.url) + break + } + case 'image': + case 'video': + case 'audio': + console.log("it's a media url:", block.url) + case 'relay': + console.log("it's a websocket url, probably a relay address:", block.url) + default: + break } -) - -await Promise.any(pool.publish(relays, newEvent)) -console.log('published to at least one relay!') - -let events = await pool.querySync(relays, { kinds: [0, 1] }) -let event = await pool.get(relays, { - ids: ['44e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245'], -}) +} ``` -### Parsing references (mentions) from a content using NIP-10 and NIP-27 +### Connecting to a bunker using NIP-46 ```js -import { parseReferences } from 'nostr-tools/references' +import { generateSecretKey, getPublicKey } from '@nostr/tools/pure' +import { BunkerSigner, parseBunkerInput } from '@nostr/tools/nip46' +import { SimplePool } from '@nostr/tools/pool' -let references = parseReferences(event) -let simpleAugmentedContent = event.content -for (let i = 0; i < references.length; i++) { - let { text, profile, event, address } = references[i] - let augmentedReference = profile - ? `@${profilesCache[profile.pubkey].name}` - : event - ? `${eventsCache[event.id].content.slice(0, 5)}` - : address - ? `[link]` - : text - simpleAugmentedContent.replaceAll(text, augmentedReference) +// the client needs a local secret key (which is generally persisted) for communicating with the bunker +const localSecretKey = generateSecretKey() + +// parse a bunker URI +const bunkerPointer = await parseBunkerInput('bunker://abcd...?relay=wss://relay.example.com') +if (!bunkerPointer) { + throw new Error('Invalid bunker input') +} + +// create the bunker instance +const pool = new SimplePool() +const bunker = new BunkerSigner(localSecretKey, bunkerPointer, { pool }) +await bunker.connect() + +// and use it +const pubkey = await bunker.getPublicKey() +const event = await bunker.signEvent({ + kind: 1, + created_at: Math.floor(Date.now() / 1000), + tags: [], + content: 'Hello from bunker!' +}) + +// cleanup +await signer.close() +pool.close([]) +``` + +### Parsing thread from any note based on NIP-10 + +```js +import * as nip10 from '@nostr/tools/nip10' + +// event is a nostr event with tags +const refs = nip10.parse(event) + +// get the root event of the thread +if (refs.root) { + console.log('root event:', refs.root.id) + console.log('root event relay hints:', refs.root.relays) + console.log('root event author:', refs.root.author) +} + +// get the immediate parent being replied to +if (refs.reply) { + console.log('reply to:', refs.reply.id) + console.log('reply relay hints:', refs.reply.relays) + console.log('reply author:', refs.reply.author) +} + +// get any mentioned events +for (let mention of refs.mentions) { + console.log('mentioned event:', mention.id) + console.log('mention relay hints:', mention.relays) + console.log('mention author:', mention.author) +} + +// get any quoted events +for (let quote of refs.quotes) { + console.log('quoted event:', quote.id) + console.log('quote relay hints:', quote.relays) +} + +// get any referenced profiles +for (let profile of refs.profiles) { + console.log('referenced profile:', profile.pubkey) + console.log('profile relay hints:', profile.relays) } ``` @@ -205,32 +262,6 @@ declare global { } ``` - -### Generating NIP-06 keys -```js -import { - privateKeyFromSeedWords, - accountFromSeedWords, - extendedKeysFromSeedWords, - accountFromExtendedKey -} from 'nostr-tools/nip06' - -const mnemonic = 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong' -const passphrase = '123' // optional -const accountIndex = 0 -const sk0 = privateKeyFromSeedWords(mnemonic, passphrase, accountIndex) - -const { privateKey: sk1, publicKey: pk1 } = accountFromSeedWords(mnemonic, passphrase, accountIndex) - -const extendedAccountIndex = 0 - -const { privateExtendedKey, publicExtendedKey } = extendedKeysFromSeedWords(mnemonic, passphrase, extendedAccountIndex) - -const { privateKey: sk2, publicKey: pk2 } = accountFromExtendedKey(privateExtendedKey) - -const { publicKey: pk3 } = accountFromExtendedKey(publicExtendedKey) -``` - ### Encoding and decoding NIP-19 codes ```js