From 9e93a35ee7d16aed6b3c1d443d06cbe55a655695 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Tue, 16 May 2023 11:30:43 -0400 Subject: [PATCH] Parsing NIP-19 to improve search results. --- .../com/vitorpamplona/amethyst/model/Hex.kt | 17 +++++++++++ .../amethyst/model/LocalCache.kt | 29 +++++++++++++++++++ .../NostrSearchEventOrUserDataSource.kt | 12 +++----- 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Hex.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Hex.kt index a9ab40817..612f1fdb5 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Hex.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Hex.kt @@ -50,6 +50,23 @@ fun decodePublicKey(key: String): ByteArray { } } +fun decodePublicKeyAsHexOrNull(key: String): HexKey? { + return try { + val parsed = Nip19.uriToRoute(key) + val pubKeyParsed = parsed?.hex + + if (key.startsWith("nsec")) { + Persona(privKey = key.bechToBytes()).pubKey.toHexKey() + } else if (pubKeyParsed != null) { + pubKeyParsed + } else { + Hex.decode(key).toHexKey() + } + } catch (e: Exception) { + null + } +} + data class DirtyKeyInfo(val key: Nip19.Return, val restOfWord: String) fun parseDirtyWordForKey(mightBeAKey: String): DirtyKeyInfo? { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index d53a73a75..088a811c0 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -5,12 +5,15 @@ import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.vitorpamplona.amethyst.Amethyst import com.vitorpamplona.amethyst.service.model.* +import com.vitorpamplona.amethyst.service.nip19.Nip19 import com.vitorpamplona.amethyst.service.relays.Relay import com.vitorpamplona.amethyst.ui.components.BundledInsert import fr.acinq.secp256k1.Hex import kotlinx.coroutines.* import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow +import nostr.postr.Persona +import nostr.postr.bechToBytes import nostr.postr.toNpub import java.io.ByteArrayInputStream import java.io.File @@ -833,6 +836,12 @@ object LocalCache { } fun findUsersStartingWith(username: String): List { + val key = decodePublicKeyAsHexOrNull(username) + + if (key != null && users[key] != null) { + return listOfNotNull(users[key]) + } + return users.values.filter { (it.anyNameStartsWith(username)) || it.pubkeyHex.startsWith(username, true) || @@ -841,6 +850,16 @@ object LocalCache { } fun findNotesStartingWith(text: String): List { + val key = try { + Nip19.uriToRoute(text)?.hex ?: Hex.decode(text).toHexKey() + } catch (e: Exception) { + null + } + + if (key != null && (notes[key] ?: addressables[key]) != null) { + return listOfNotNull(notes[key] ?: addressables[key]) + } + return notes.values.filter { (it.event is TextNoteEvent && it.event?.content()?.contains(text, true) ?: false) || (it.event is PollNoteEvent && it.event?.content()?.contains(text, true) ?: false) || @@ -856,6 +875,16 @@ object LocalCache { } fun findChannelsStartingWith(text: String): List { + val key = try { + Nip19.uriToRoute(text)?.hex ?: Hex.decode(text).toHexKey() + } catch (e: Exception) { + null + } + + if (key != null && channels[key] != null) { + return listOfNotNull(channels[key]) + } + return channels.values.filter { it.anyNameStartsWith(text) || it.idHex.startsWith(text, true) || diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/NostrSearchEventOrUserDataSource.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/NostrSearchEventOrUserDataSource.kt index f2656f949..958335ad3 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/NostrSearchEventOrUserDataSource.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/NostrSearchEventOrUserDataSource.kt @@ -1,11 +1,14 @@ package com.vitorpamplona.amethyst.service import com.vitorpamplona.amethyst.model.decodePublicKey +import com.vitorpamplona.amethyst.model.toHexKey import com.vitorpamplona.amethyst.service.model.* +import com.vitorpamplona.amethyst.service.nip19.Nip19 import com.vitorpamplona.amethyst.service.relays.COMMON_FEED_TYPES import com.vitorpamplona.amethyst.service.relays.FeedType import com.vitorpamplona.amethyst.service.relays.JsonFilter import com.vitorpamplona.amethyst.service.relays.TypedFilter +import fr.acinq.secp256k1.Hex import nostr.postr.bechToBytes import nostr.postr.toHex @@ -19,15 +22,8 @@ object NostrSearchEventOrUserDataSource : NostrDataSource("SingleEventFeed") { } val hexToWatch = try { - if (mySearchString.startsWith("npub") || mySearchString.startsWith("nsec")) { - decodePublicKey(mySearchString).toHex() - } else if (mySearchString.startsWith("note")) { - mySearchString.bechToBytes().toHex() - } else { - mySearchString - } + Nip19.uriToRoute(mySearchString)?.hex ?: Hex.decode(mySearchString).toHexKey() } catch (e: Exception) { - // Usually when people add an incomplete npub or note. null }