import { Filter, SimplePool, VerifiedEvent, kinds, validateEvent, verifyEvent, Event } from 'nostr-tools' import { ProfileMetadata, RelaySet } from '../types' import { NostrController } from '.' import { toast } from 'react-toastify' export class MetadataController { private nostrController: NostrController private specialMetadataRelay = 'wss://purplepag.es' constructor() { this.nostrController = NostrController.getInstance() } public findMetadata = async (hexKey: string) => { const eventFilter: Filter = { kinds: [kinds.Metadata], authors: [hexKey] } const pool = new SimplePool() const metadataEvent = await pool .get([this.specialMetadataRelay], eventFilter) .catch((err) => { console.error(err) return null }) if ( metadataEvent && validateEvent(metadataEvent) && verifyEvent(metadataEvent) ) { return metadataEvent } const mostPopularRelays = import.meta.env.VITE_MOST_POPULAR_RELAYS const hardcodedPopularRelays = (mostPopularRelays || '').split(' ') const relays = [...hardcodedPopularRelays] const events = await pool.querySync(relays, eventFilter).catch((err) => { console.error(err) return null }) if (events && events.length) { events.sort((a, b) => b.created_at - a.created_at) for (const event of events) { if (validateEvent(event) && verifyEvent(event)) { return event } } } throw new Error('Mo metadata found.') } public findRelayListMetadata = async (hexKey: string) => { const eventFilter: Filter = { kinds: [kinds.RelayList], authors: [hexKey] } const pool = new SimplePool() let relayEvent = await pool .get([this.specialMetadataRelay], eventFilter) .catch((err) => { console.error(err) return null }) if (!relayEvent) { const mostPopularRelays = import.meta.env.VITE_MOST_POPULAR_RELAYS const hardcodedPopularRelays = (mostPopularRelays || '').split(' ') const relays = [...hardcodedPopularRelays] relayEvent = await pool.get(relays, eventFilter).catch((err) => { console.error(err) return null }) } if (relayEvent) { const relaySet: RelaySet = { read: [], write: [] } // a list of r tags with relay URIs and a read or write marker. const relayTags = relayEvent.tags.filter((tag) => tag[0] === 'r') // Relays marked as read / write are called READ / WRITE relays, respectively relayTags.forEach((tag) => { if (tag.length >= 3) { const marker = tag[2] if (marker === 'read') { relaySet.read.push(tag[1]) } else if (marker === 'write') { relaySet.write.push(tag[1]) } } // If the marker is omitted, the relay is used for both purposes if (tag.length === 2) { relaySet.read.push(tag[1]) relaySet.write.push(tag[1]) } }) return relaySet } throw new Error('No relay list metadata found.') } public extractProfileMetadataContent = (event: VerifiedEvent) => { try { return JSON.parse(event.content) as ProfileMetadata } catch (error) { console.log('error in parsing metadata event content :>> ', error) return null } } /** * Function will not sign provided event if the SIG exists */ public publishMetadataEvent = async (event: Event) => { let signedMetadataEvent = event if (event.sig.length < 1) { const timestamp = Math.floor(Date.now() / 1000) // Metadata event to publish to the wss://purplepag.es relay const newMetadataEvent: Event = { ...event, created_at: timestamp } signedMetadataEvent = await this.nostrController.signEvent( newMetadataEvent ) } await this.nostrController .publishEvent(signedMetadataEvent, [this.specialMetadataRelay]) .then((relays) => { toast.success(`Metadata event published on: ${relays.join('\n')}`) }) .catch((err) => { toast.error(err.message) }) } public validate = (event: Event) => validateEvent(event) && verifyEvent(event) }