fix(private-replies): fall back to recipient inbox relays, not our own
When the recipient hasn't published a kind 10050, fall back to their NIP-65 inbox (read) relays — fetching the kind 10002 list fresh from indexers if we haven't seen it yet — instead of silently dropping the wrap on our own write relays where the recipient never queries. Drop the prior write-relays / own-write-relays fallback chain entirely: neither is an inbox the recipient is listening on, so a wrap landing there was guaranteed lost. If we can't resolve any inbox we return sentCount=0 and surface the existing "no relays connected" error. PeerRelayListLookup is a small shared helper for the kind 10002 fetch.
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
package com.wisp.app.repo
|
||||
|
||||
import com.wisp.app.nostr.ClientMessage
|
||||
import com.wisp.app.nostr.Filter
|
||||
import com.wisp.app.relay.RelayConfig
|
||||
import com.wisp.app.relay.RelayEvent
|
||||
import com.wisp.app.relay.RelayPool
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
|
||||
/**
|
||||
* Fetch a peer's kind 10002 relay list (NIP-65) from indexer relays + the connected pool,
|
||||
* then populate [RelayListRepository] with the freshest result. Caller can then ask the
|
||||
* repository for the peer's read/inbox or write/outbox relays.
|
||||
*/
|
||||
object PeerRelayListLookup {
|
||||
suspend fun fetch(
|
||||
pubkey: String,
|
||||
relayPool: RelayPool,
|
||||
relayListRepo: RelayListRepository
|
||||
) {
|
||||
val subId = "rl_${pubkey.take(8)}"
|
||||
val filter = Filter(
|
||||
kinds = listOf(10002),
|
||||
authors = listOf(pubkey),
|
||||
limit = 1
|
||||
)
|
||||
val reqMsg = ClientMessage.req(subId, filter)
|
||||
for (url in RelayConfig.DEFAULT_INDEXER_RELAYS) {
|
||||
relayPool.sendToRelayOrEphemeral(url, reqMsg, skipBadCheck = true)
|
||||
}
|
||||
relayPool.sendToAll(reqMsg)
|
||||
|
||||
val results = mutableListOf<RelayEvent>()
|
||||
withTimeoutOrNull(4000L) {
|
||||
relayPool.relayEvents
|
||||
.filter { it.subscriptionId == subId }
|
||||
.collect { results.add(it) }
|
||||
}
|
||||
|
||||
val closeMsg = ClientMessage.close(subId)
|
||||
for (url in RelayConfig.DEFAULT_INDEXER_RELAYS) {
|
||||
relayPool.sendToRelay(url, closeMsg)
|
||||
}
|
||||
relayPool.sendToAll(closeMsg)
|
||||
|
||||
val best = results.maxByOrNull { it.event.created_at }
|
||||
if (best != null) {
|
||||
relayListRepo.updateFromEvent(best.event)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,22 +72,27 @@ object PrivateReplyPublisher {
|
||||
createdAt = rumorCreatedAt
|
||||
)
|
||||
|
||||
// Recipient resolution: their kind 10050 DM relays, or — if none — their NIP-65
|
||||
// inbox (read) relays. The recipient never queries our write relays, so we don't
|
||||
// fall back to those: a wrap there would be silently lost.
|
||||
val recipientRelays: List<String> = run {
|
||||
val dmRelays = DmRelayLookup.fetch(replyTo.pubkey, relayPool, dmRepo)
|
||||
if (dmRelays.isNotEmpty()) return@run dmRelays
|
||||
relayListRepo?.getReadRelays(replyTo.pubkey)?.takeIf { it.isNotEmpty() }?.let { return@run it }
|
||||
relayListRepo?.getWriteRelays(replyTo.pubkey)?.takeIf { it.isNotEmpty() }?.let { return@run it }
|
||||
if (relayListRepo != null) {
|
||||
relayListRepo.getReadRelays(replyTo.pubkey)?.takeIf { it.isNotEmpty() }?.let { return@run it }
|
||||
// Cache miss — fetch kind 10002 fresh from indexers, then re-check.
|
||||
PeerRelayListLookup.fetch(replyTo.pubkey, relayPool, relayListRepo)
|
||||
relayListRepo.getReadRelays(replyTo.pubkey)?.takeIf { it.isNotEmpty() }?.let { return@run it }
|
||||
}
|
||||
emptyList()
|
||||
}
|
||||
|
||||
if (recipientRelays.isEmpty()) return Result(0, null)
|
||||
|
||||
val recipientMsg = ClientMessage.event(recipientWrap)
|
||||
var sentCount = 0
|
||||
if (recipientRelays.isNotEmpty()) {
|
||||
for (url in recipientRelays) {
|
||||
if (relayPool.sendToRelayOrEphemeral(url, recipientMsg, skipBadCheck = true)) sentCount++
|
||||
}
|
||||
} else {
|
||||
sentCount += relayPool.sendToWriteRelays(recipientMsg)
|
||||
for (url in recipientRelays) {
|
||||
if (relayPool.sendToRelayOrEphemeral(url, recipientMsg, skipBadCheck = true)) sentCount++
|
||||
}
|
||||
|
||||
if (sentCount == 0) return Result(0, null)
|
||||
|
||||
Reference in New Issue
Block a user