diff options
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/nip17.ts | 52 | ||||
| -rw-r--r-- | src/lib/seed.ts | 41 |
2 files changed, 93 insertions, 0 deletions
diff --git a/src/lib/nip17.ts b/src/lib/nip17.ts index 29a14549..1400b5a5 100644 --- a/src/lib/nip17.ts +++ b/src/lib/nip17.ts @@ -302,6 +302,58 @@ const createAndPublishMessageEvent = async ( return publishToPreferred(event, eventFor, expiryDays != null); }; +// Subscribes to new incoming gift wraps for the user. +// Decrypts new events and passes the rumour to onNewRumour(). +// Returns a close function to stop the subscription. +// Uses Relay directly instead of SimplePool.subscribeMany to avoid +// a filter double-nesting bug in nostr-tools v2.23.0. +export const subscribeToMessages = async ( + user: any, + onNewRumour: (rumour: any) => void, +): Promise<() => void> => { + const preferredRelays = await getPreferredRelays(user.pubkey); + const relayUrls = Array.from(new Set([...preferredRelays, ...DM_RELAYS_LIST])); + const since = Math.floor(Date.now() / 1000) - TWO_DAYS; + const seen = new Set<string>(); + let eoseCount = 0; + + const closers: (() => void)[] = []; + + for (const url of relayUrls) { + try { + const relay = await Relay.connect(url); + const sub = relay.subscribe( + [{ kinds: [1059], "#p": [user.pubkey], since }], + { + onevent: async (event) => { + if (seen.has(event.id)) return; + seen.add(event.id); + if (eoseCount < relayUrls.length) return; + if (expired(event)) return; + try { + const rumour: any = await decrypt(event, user); + const expires = expiration(event); + if (expires) rumour.expiration = expires; + onNewRumour(rumour); + } catch (e) { + console.error("Failed to decrypt subscription event:", event.id, e); + } + }, + oneose: () => { + eoseCount++; + }, + }, + ); + closers.push(() => sub.close()); + } catch (e) { + console.error("Failed to connect to relay for subscription:", url, e); + eoseCount++; + } + } + + return () => closers.forEach((close) => close()); +}; + // Sends a NIP-17 message from `user` to `recipient`. // If expiryDays is set, messages will expire after that many days. export const send = async (message: string, user: any, recipient: any, expiryDays?: number) => { diff --git a/src/lib/seed.ts b/src/lib/seed.ts new file mode 100644 index 00000000..3d58bcb5 --- /dev/null +++ b/src/lib/seed.ts @@ -0,0 +1,41 @@ +import { getWalletEntropy } from "$lib/walletEntropy"; +import { signer } from "$lib/store"; + +export async function deriveNostrKey(entropy: ArrayBuffer) { + const [{ entropyToMnemonic }, { wordlist }, { privateKeyFromSeedWords }, { getPublicKey }] = + await Promise.all([ + import("@scure/bip39"), + import("@scure/bip39/wordlists/english.js"), + import("nostr-tools/nip06"), + import("nostr-tools"), + ]); + + const ent = new Uint8Array(entropy).slice(0, 16); + const mnemonic = entropyToMnemonic(ent, wordlist); + const sk = privateKeyFromSeedWords(mnemonic); + const pubkey = getPublicKey(sk); + return { sk, pubkey }; +} + +export async function autoUnlockNostr(userPubkey: string) { + if (localStorage.getItem("nsec")) return; + + const entropy = await getWalletEntropy(); + if (!entropy) return; + + try { + const { sk, pubkey } = await deriveNostrKey(entropy); + if (pubkey !== userPubkey) return; + + const { nip19 } = await import("nostr-tools"); + const { bytesToHex } = await import("@noble/hashes/utils.js"); + localStorage.setItem("nsec", nip19.nsecEncode(sk)); + signer.set({ + method: "nsec", + params: { sk: bytesToHex(sk), pk: pubkey }, + ready: true, + }); + } catch (e) { + console.error("Nostr auto-unlock failed:", e); + } +} |
