fix: use relays from nip65 for broadcasting DMs
All checks were successful
Release / build_and_release (push) Successful in 57s

This commit is contained in:
SwiftHawk 2024-04-16 11:12:29 +05:00
parent f4c26945c1
commit 349e26b628
4 changed files with 124 additions and 21 deletions

View File

@ -7,7 +7,7 @@ import {
verifyEvent,
Event
} from 'nostr-tools'
import { ProfileMetadata } from '../types'
import { ProfileMetadata, RelaySet } from '../types'
import { NostrController } from '.'
import { toast } from 'react-toastify'
@ -64,6 +64,66 @@ export class MetadataController {
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
@ -94,9 +154,9 @@ export class MetadataController {
}
await this.nostrController
.publishEvent(signedMetadataEvent, this.specialMetadataRelay)
.then((res) => {
toast.success(`Metadata: ${res}`)
.publishEvent(signedMetadataEvent, [this.specialMetadataRelay])
.then((relays) => {
toast.success(`Metadata event published on: ${relays.join('\n')}`)
})
.catch((err) => {
toast.error(err.message)

View File

@ -1,4 +1,3 @@
import { EventEmitter } from 'tseep'
import NDK, {
NDKEvent,
NDKNip46Signer,
@ -9,12 +8,13 @@ import NDK, {
import {
Event,
EventTemplate,
Relay,
SimplePool,
UnsignedEvent,
finalizeEvent,
nip04,
nip19
} from 'nostr-tools'
import { EventEmitter } from 'tseep'
import { updateNsecbunkerPubkey } from '../store/actions'
import { AuthState, LoginMethods } from '../store/auth/types'
import store from '../store/store'
@ -195,14 +195,29 @@ export class NostrController extends EventEmitter {
}
/**
* Function will publish provided event to the provided relay
* Function will publish provided event to the provided relays
*/
publishEvent = async (event: Event, relayUrl: string) => {
const relay = await Relay.connect(relayUrl)
await relay.publish(event)
relay.close()
publishEvent = async (event: Event, relays: string[]) => {
const simplePool = new SimplePool()
return `event published to relay: ${relayUrl}`
const promises = simplePool.publish(relays, event)
const results = await Promise.allSettled(promises)
const publishedRelays: string[] = []
console.log('results of publish event :>> ', results)
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
publishedRelays.push(relays[index])
}
})
if (publishedRelays.length === 0)
throw new Error(`Couldn't publish to any relay`)
return publishedRelays
}
/**

View File

@ -278,6 +278,7 @@ export const HomePage = () => {
setLoadingSpinnerDesc('Signing auth event for uploading zip')
const authEvent = await nostrController.signEvent(event)
// todo: use env variable
const FILE_STORAGE_URL = 'https://blossom.sigit.io'
const response = await axios.put(`${FILE_STORAGE_URL}/upload`, file, {
@ -304,9 +305,16 @@ export const HomePage = () => {
setLoadingSpinnerDesc('encrypting content for DM')
// todo: add timeout
const encrypted = await nostrController
.nip04Encrypt(pubkey, content)
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => {
reject(new Error('Timeout occurred'))
}, 15000) // timeout duration = 15 sec
})
const encrypted = await Promise.race([
nostrController.nip04Encrypt(pubkey, content),
timeoutPromise
])
.then((res) => {
return res
})
@ -339,15 +347,30 @@ export const HomePage = () => {
if (!signedEvent) return
// const metadata = metadataMap[pubkey]
setLoadingSpinnerDesc('Publishing encrypted DM')
// todo: do not use hardcoded relay
const metadataController = new MetadataController()
const relaySet = await metadataController
.findRelayListMetadata(pubkey)
.catch((err) => {
toast.error(
err.message || 'An error occurred while finding relay list metadata'
)
return null
})
if (!relaySet) return
// NOTE: according to Nip65
// DMs SHOULD only be broadcasted to the author's WRITE relays and to the receiver's READ relays to keep maximum privacy.
if (relaySet.read.length === 0) {
toast.error('No relay found for publishing encrypted DM')
}
await nostrController
.publishEvent(signedEvent, 'wss://relay.damus.io')
.then(() => {
toast.success('DM sent to first signer')
.publishEvent(signedEvent, relaySet.read)
.then((relays) => {
toast.success(`Encrypted DM sent on: ${relays.join('\n')}`)
})
.catch((err) => {
console.log('err :>> ', err)

View File

@ -7,3 +7,8 @@ export interface SignedEvent {
id: string
sig: string
}
export interface RelaySet {
read: string[]
write: string[]
}