add verify msg relay

This commit is contained in:
2026-05-21 18:25:04 +07:00
parent 39d899b249
commit a7215c7283
4 changed files with 164 additions and 8 deletions

View File

@@ -348,7 +348,7 @@ class Nostr {
is RelayMessageEnum.EndOfStoredEvents -> {
val subscriptionId = message.subscriptionId
if (subscriptionId == "messages") {
if (subscriptionId == "all-gift-wraps" || subscriptionId == "newest-gift-wraps") {
onSubscriptionClose()
}
}
@@ -471,7 +471,7 @@ class Nostr {
return relayList
}
private suspend fun getMsgRelayList(): List<RelayUrl> {
suspend fun getDefaultMsgRelayList(): List<RelayUrl> {
// Construct a list of messaging relays
val msgRelayList = listOf(
RelayUrl.parse("wss://relay.0xchat.com"),
@@ -500,7 +500,7 @@ class Nostr {
)
// Send messaging relay list event
val msgRelayList = getMsgRelayList()
val msgRelayList = getDefaultMsgRelayList()
val msgRelayListEvent = EventBuilder.nip17RelayList(msgRelayList).signWithKeys(keys)
client?.sendEvent(
@@ -578,6 +578,39 @@ class Nostr {
}
}
suspend fun setMsgRelays(urls: List<RelayUrl>) {
try {
val event = EventBuilder.nip17RelayList(urls).signAsync(signer)
client?.sendEvent(
event = event,
target = SendEventTarget.toNip65(),
ackPolicy = AckPolicy.none(),
)
val kind = Kind.fromStd(KindStandard.INBOX_RELAYS);
val filter = Filter().kind(kind).author(signer.currentUser!!).limit(1u)
val target = ReqTarget.auto(listOf(filter))
val opts = SubscribeAutoCloseOptions().exitPolicy(ReqExitPolicy.ExitOnEose)
client?.subscribe(target = target, closeOn = opts)
} catch (e: Exception) {
throw IllegalStateException("Failed to set msg relays: ${e.message}", e)
}
}
suspend fun getMsgRelays(publicKey: PublicKey): List<RelayUrl> {
try {
val kind = Kind.fromStd(KindStandard.INBOX_RELAYS)
val filter = Filter().kind(kind).author(publicKey).limit(1u)
val events = client?.database()?.query(filter)
return nip17ExtractRelayList(events?.toVec()?.firstOrNull() ?: return emptyList())
} catch (e: Exception) {
throw IllegalStateException("Failed to get msg relays: ${e.message}", e)
}
}
suspend fun getChatRooms(): Set<Room>? {
try {
val userPubkey = signer.currentUser ?: throw IllegalStateException("User not signed in")

View File

@@ -7,6 +7,7 @@ import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -51,6 +52,9 @@ class NostrViewModel(
private val _isPartialProcessedGiftWrap = MutableStateFlow(false)
val isPartialProcessedGiftWrap = _isPartialProcessedGiftWrap.asStateFlow()
private val _isRelayListEmpty = MutableStateFlow(false)
val isRelayListEmpty = _isRelayListEmpty.asStateFlow()
private val _newEvents = MutableSharedFlow<UnsignedEvent>(extraBufferCapacity = 100)
val newEvents = _newEvents.asSharedFlow()
@@ -69,6 +73,7 @@ class NostrViewModel(
startMetadataBatchHandler()
getCacheMetadata()
login()
observeSignerAndCheckRelays()
}
override fun onCleared() {
@@ -202,6 +207,25 @@ class NostrViewModel(
}
}
private fun observeSignerAndCheckRelays() {
viewModelScope.launch {
while (true) {
val pubkey = nostr.signer.currentUser
if (pubkey != null) {
delay(3000)
val relays = nostr.getMsgRelays(pubkey)
if (relays.isEmpty()) {
_isRelayListEmpty.value = true
}
break
}
delay(1000)
}
}
}
private fun requestMetadata(pubkey: PublicKey) {
if (seenPublicKeys.add(pubkey)) {
viewModelScope.launch {
@@ -234,6 +258,10 @@ class NostrViewModel(
}
}
fun dismissRelayWarning() {
_isRelayListEmpty.value = false
}
private suspend fun getOrInitAppKeys(): Keys {
val secret = secretStore.get("app_keys")
@@ -349,6 +377,15 @@ class NostrViewModel(
}
}
suspend fun useDefaultMsgRelayList() {
try {
val defaultRelays = nostr.getDefaultMsgRelayList()
nostr.setMsgRelays(defaultRelays)
} catch (e: Exception) {
showError("Error: ${e.message}")
}
}
fun createChatRoom(to: List<PublicKey>): Long {
if (nostr.signer.currentUser == null) throw IllegalStateException("User not signed in")
if (to.isEmpty()) throw IllegalArgumentException("At least one recipient is required")