webapp/src/lib/nostr.ts

123 lines
No EOL
3.5 KiB
TypeScript

import type { NostrEvent, NostrRelayConfig } from '../types/nostr'
declare global {
interface Window {
NostrTools: {
getPublicKey: (privkey: string) => string
generatePrivateKey: () => string
nip04: {
encrypt: (privkey: string, pubkey: string, content: string) => Promise<string>
decrypt: (privkey: string, pubkey: string, content: string) => Promise<string>
}
getEventHash: (event: NostrEvent) => string
signEvent: (event: NostrEvent, privkey: string) => Promise<string>
getSignature: (event: NostrEvent, privkey: string) => string
verifySignature: (event: NostrEvent) => boolean
nip19: {
decode: (str: string) => { type: string; data: string }
npubEncode: (hex: string) => string
}
relayInit: (url: string) => {
connect: () => Promise<void>
sub: (filters: any[]) => {
on: (event: string, callback: (event: NostrEvent) => void) => void
}
publish: (event: NostrEvent) => {
on: (type: 'ok' | 'failed', cb: (msg?: string) => void) => void
}
close: () => void
}
}
}
}
export async function connectToRelay(url: string) {
const relay = window.NostrTools.relayInit(url)
try {
await relay.connect()
return relay
} catch (err) {
console.error(`Failed to connect to ${url}:`, err)
return null
}
}
export async function publishEvent(event: NostrEvent, relays: NostrRelayConfig[]) {
const connectedRelays = await Promise.all(
relays.map(relay => connectToRelay(relay.url))
)
const activeRelays = connectedRelays.filter(relay => relay !== null)
return Promise.all(
activeRelays.map(relay =>
new Promise((resolve) => {
const pub = relay.publish(event)
pub.on('ok', () => {
resolve(true)
})
pub.on('failed', () => {
resolve(false)
})
})
)
)
}
export async function encryptMessage(privkey: string, pubkey: string, content: string): Promise<string> {
return await window.NostrTools.nip04.encrypt(privkey, pubkey, content)
}
export async function decryptMessage(privkey: string, pubkey: string, content: string): Promise<string> {
return await window.NostrTools.nip04.decrypt(privkey, pubkey, content)
}
export function generatePrivateKey(): string {
return window.NostrTools.generatePrivateKey()
}
export function getPublicKey(privateKey: string): string {
return window.NostrTools.getPublicKey(privateKey)
}
export function getEventHash(event: NostrEvent): string {
return window.NostrTools.getEventHash(event)
}
export async function signEvent(event: NostrEvent, privateKey: string): Promise<string> {
return window.NostrTools.getSignature(event, privateKey)
}
export function verifySignature(event: NostrEvent): boolean {
return window.NostrTools.verifySignature(event)
}
export function npubToHex(npub: string): string {
try {
const { type, data } = window.NostrTools.nip19.decode(npub)
if (type !== 'npub') throw new Error('Invalid npub')
return data
} catch (err) {
console.error('Failed to decode npub:', err)
throw err
}
}
export function hexToNpub(hex: string): string {
return window.NostrTools.nip19.npubEncode(hex)
}
export function isValidPrivateKey(key: string): boolean {
try {
if (!/^[0-9a-fA-F]{64}$/.test(key)) {
return false
}
return true
} catch {
return false
}
}
export function formatPrivateKey(key: string): string {
return key.trim().toLowerCase()
}