fix: use relays from nip65 for broadcasting DMs
All checks were successful
Release / build_and_release (push) Successful in 57s
All checks were successful
Release / build_and_release (push) Successful in 57s
This commit is contained in:
parent
f4c26945c1
commit
349e26b628
@ -7,7 +7,7 @@ import {
|
|||||||
verifyEvent,
|
verifyEvent,
|
||||||
Event
|
Event
|
||||||
} from 'nostr-tools'
|
} from 'nostr-tools'
|
||||||
import { ProfileMetadata } from '../types'
|
import { ProfileMetadata, RelaySet } from '../types'
|
||||||
import { NostrController } from '.'
|
import { NostrController } from '.'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
@ -64,6 +64,66 @@ export class MetadataController {
|
|||||||
throw new Error('Mo metadata found.')
|
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) => {
|
public extractProfileMetadataContent = (event: VerifiedEvent) => {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(event.content) as ProfileMetadata
|
return JSON.parse(event.content) as ProfileMetadata
|
||||||
@ -94,9 +154,9 @@ export class MetadataController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.nostrController
|
await this.nostrController
|
||||||
.publishEvent(signedMetadataEvent, this.specialMetadataRelay)
|
.publishEvent(signedMetadataEvent, [this.specialMetadataRelay])
|
||||||
.then((res) => {
|
.then((relays) => {
|
||||||
toast.success(`Metadata: ${res}`)
|
toast.success(`Metadata event published on: ${relays.join('\n')}`)
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
toast.error(err.message)
|
toast.error(err.message)
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { EventEmitter } from 'tseep'
|
|
||||||
import NDK, {
|
import NDK, {
|
||||||
NDKEvent,
|
NDKEvent,
|
||||||
NDKNip46Signer,
|
NDKNip46Signer,
|
||||||
@ -9,12 +8,13 @@ import NDK, {
|
|||||||
import {
|
import {
|
||||||
Event,
|
Event,
|
||||||
EventTemplate,
|
EventTemplate,
|
||||||
Relay,
|
SimplePool,
|
||||||
UnsignedEvent,
|
UnsignedEvent,
|
||||||
finalizeEvent,
|
finalizeEvent,
|
||||||
nip04,
|
nip04,
|
||||||
nip19
|
nip19
|
||||||
} from 'nostr-tools'
|
} from 'nostr-tools'
|
||||||
|
import { EventEmitter } from 'tseep'
|
||||||
import { updateNsecbunkerPubkey } from '../store/actions'
|
import { updateNsecbunkerPubkey } from '../store/actions'
|
||||||
import { AuthState, LoginMethods } from '../store/auth/types'
|
import { AuthState, LoginMethods } from '../store/auth/types'
|
||||||
import store from '../store/store'
|
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) => {
|
publishEvent = async (event: Event, relays: string[]) => {
|
||||||
const relay = await Relay.connect(relayUrl)
|
const simplePool = new SimplePool()
|
||||||
await relay.publish(event)
|
|
||||||
relay.close()
|
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -278,6 +278,7 @@ export const HomePage = () => {
|
|||||||
setLoadingSpinnerDesc('Signing auth event for uploading zip')
|
setLoadingSpinnerDesc('Signing auth event for uploading zip')
|
||||||
const authEvent = await nostrController.signEvent(event)
|
const authEvent = await nostrController.signEvent(event)
|
||||||
|
|
||||||
|
// todo: use env variable
|
||||||
const FILE_STORAGE_URL = 'https://blossom.sigit.io'
|
const FILE_STORAGE_URL = 'https://blossom.sigit.io'
|
||||||
|
|
||||||
const response = await axios.put(`${FILE_STORAGE_URL}/upload`, file, {
|
const response = await axios.put(`${FILE_STORAGE_URL}/upload`, file, {
|
||||||
@ -304,9 +305,16 @@ export const HomePage = () => {
|
|||||||
|
|
||||||
setLoadingSpinnerDesc('encrypting content for DM')
|
setLoadingSpinnerDesc('encrypting content for DM')
|
||||||
|
|
||||||
// todo: add timeout
|
const timeoutPromise = new Promise<never>((_, reject) => {
|
||||||
const encrypted = await nostrController
|
setTimeout(() => {
|
||||||
.nip04Encrypt(pubkey, content)
|
reject(new Error('Timeout occurred'))
|
||||||
|
}, 15000) // timeout duration = 15 sec
|
||||||
|
})
|
||||||
|
|
||||||
|
const encrypted = await Promise.race([
|
||||||
|
nostrController.nip04Encrypt(pubkey, content),
|
||||||
|
timeoutPromise
|
||||||
|
])
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res
|
return res
|
||||||
})
|
})
|
||||||
@ -339,15 +347,30 @@ export const HomePage = () => {
|
|||||||
|
|
||||||
if (!signedEvent) return
|
if (!signedEvent) return
|
||||||
|
|
||||||
// const metadata = metadataMap[pubkey]
|
|
||||||
|
|
||||||
setLoadingSpinnerDesc('Publishing encrypted DM')
|
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
|
await nostrController
|
||||||
.publishEvent(signedEvent, 'wss://relay.damus.io')
|
.publishEvent(signedEvent, relaySet.read)
|
||||||
.then(() => {
|
.then((relays) => {
|
||||||
toast.success('DM sent to first signer')
|
toast.success(`Encrypted DM sent on: ${relays.join('\n')}`)
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log('err :>> ', err)
|
console.log('err :>> ', err)
|
||||||
|
@ -7,3 +7,8 @@ export interface SignedEvent {
|
|||||||
id: string
|
id: string
|
||||||
sig: string
|
sig: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RelaySet {
|
||||||
|
read: string[]
|
||||||
|
write: string[]
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user