mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-10-11 00:54:30 +02:00
Improves RelayHint normalization to make sure the NormalizedRelayUrls are indeed relays
This commit is contained in:
@@ -20,8 +20,12 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.quartz.nip01Core.relay.normalizer
|
package com.vitorpamplona.quartz.nip01Core.relay.normalizer
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import android.util.Log.e
|
||||||
import androidx.collection.LruCache
|
import androidx.collection.LruCache
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
import org.czeal.rfc3986.URIReference
|
import org.czeal.rfc3986.URIReference
|
||||||
|
import kotlin.contracts.ExperimentalContracts
|
||||||
|
|
||||||
val normalizedUrls = LruCache<String, NormalizedRelayUrl>(5000)
|
val normalizedUrls = LruCache<String, NormalizedRelayUrl>(5000)
|
||||||
|
|
||||||
@@ -37,6 +41,27 @@ class RelayUrlNormalizer {
|
|||||||
|
|
||||||
fun isOnion(url: String) = url.endsWith(".onion") || url.contains(".onion/")
|
fun isOnion(url: String) = url.endsWith(".onion") || url.contains(".onion/")
|
||||||
|
|
||||||
|
fun isRelaySchemePrefix(url: String) = url.length > 6 && url[0] == 'w' && url[1] == 's'
|
||||||
|
|
||||||
|
fun isRelaySchemePrefixSecure(url: String) = url[2] == 's' && url[3] == ':' && url[4] == '/' && url[5] == '/' && url[6] != '/'
|
||||||
|
|
||||||
|
fun isRelaySchemePrefixInsecure(url: String) = url[2] == ':' && url[3] == '/' && url[4] == '/' && url[5] != '/'
|
||||||
|
|
||||||
|
fun isRelayUrl(url: String): Boolean {
|
||||||
|
val trimmed = url.trim().ifEmpty { return false }
|
||||||
|
|
||||||
|
// fast
|
||||||
|
if (isRelaySchemePrefix(trimmed)) {
|
||||||
|
if (isRelaySchemePrefixSecure(trimmed)) {
|
||||||
|
return true
|
||||||
|
} else if (isRelaySchemePrefixInsecure(trimmed)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
private fun norm(url: String) =
|
private fun norm(url: String) =
|
||||||
NormalizedRelayUrl(
|
NormalizedRelayUrl(
|
||||||
URIReference
|
URIReference
|
||||||
@@ -46,19 +71,20 @@ class RelayUrlNormalizer {
|
|||||||
.intern(),
|
.intern(),
|
||||||
)
|
)
|
||||||
|
|
||||||
fun fix(url: String): String {
|
@OptIn(ExperimentalContracts::class)
|
||||||
val trimmed = url.trim()
|
fun fix(url: String): String? {
|
||||||
|
val trimmed = url.trim().ifEmpty { return null }
|
||||||
|
|
||||||
// fast
|
if (trimmed.isEmpty()) return null
|
||||||
if (trimmed.length > 4 && trimmed[0] == 'w' && trimmed[1] == 's') {
|
|
||||||
if (trimmed[2] == 's' && trimmed[3] == ':' && trimmed[4] == '/' && trimmed[5] == '/') {
|
// fast for good wss:// urls
|
||||||
return trimmed
|
if (isRelaySchemePrefix(trimmed)) {
|
||||||
} else if (trimmed[2] == ':' && trimmed[3] == '/' && trimmed[4] == '/') {
|
if (isRelaySchemePrefixSecure(trimmed) || isRelaySchemePrefixInsecure(trimmed)) {
|
||||||
return trimmed
|
return trimmed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fast
|
// fast for good https:// urls
|
||||||
if (trimmed.length > 8 && trimmed[0] == 'h' && trimmed[1] == 't' && trimmed[2] == 't' && trimmed[3] == 'p') {
|
if (trimmed.length > 8 && trimmed[0] == 'h' && trimmed[1] == 't' && trimmed[2] == 't' && trimmed[3] == 'p') {
|
||||||
if (trimmed[4] == 's' && trimmed[5] == ':' && trimmed[6] == '/' && trimmed[7] == '/') {
|
if (trimmed[4] == 's' && trimmed[5] == ':' && trimmed[6] == '/' && trimmed[7] == '/') {
|
||||||
// https://
|
// https://
|
||||||
@@ -69,6 +95,12 @@ class RelayUrlNormalizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (trimmed.contains("://")) {
|
||||||
|
// some other scheme we cannot connect to.
|
||||||
|
Log.w("RelayUrlNormalizer", "Rejected relay URL: $url")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return if (isOnion(trimmed) || isLocalHost(trimmed)) {
|
return if (isOnion(trimmed) || isLocalHost(trimmed)) {
|
||||||
"ws://$trimmed"
|
"ws://$trimmed"
|
||||||
} else {
|
} else {
|
||||||
@@ -80,7 +112,8 @@ class RelayUrlNormalizer {
|
|||||||
normalizedUrls.get(url)?.let { return it }
|
normalizedUrls.get(url)?.let { return it }
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val normalized = norm(fix(url))
|
val fixed = fix(url) ?: return NormalizedRelayUrl(url)
|
||||||
|
val normalized = norm(fixed)
|
||||||
normalizedUrls.put(url, normalized)
|
normalizedUrls.put(url, normalized)
|
||||||
normalized
|
normalized
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -89,13 +122,22 @@ class RelayUrlNormalizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun normalizeOrNull(url: String): NormalizedRelayUrl? {
|
fun normalizeOrNull(url: String): NormalizedRelayUrl? {
|
||||||
normalizedUrls.get(url)?.let { return it }
|
if (url.isEmpty()) return null
|
||||||
|
normalizedUrls[url]?.let { return it }
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val normalized = norm(fix(url))
|
val fixed = fix(url)
|
||||||
normalizedUrls.put(url, normalized)
|
if (fixed != null) {
|
||||||
normalized
|
val normalized = norm(fixed)
|
||||||
|
normalizedUrls.put(url, normalized)
|
||||||
|
return normalized
|
||||||
|
} else {
|
||||||
|
Log.w("NormalizedRelayUrl", "Rejected Error $url")
|
||||||
|
null
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
if (e is CancellationException) throw e
|
||||||
|
Log.w("NormalizedRelayUrl", "Rejected Error $url")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.quartz.nip01Core.tags.events
|
package com.vitorpamplona.quartz.nip01Core.tags.events
|
||||||
|
|
||||||
|
import android.R.attr.tag
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||||
@@ -72,8 +73,7 @@ data class ETag(
|
|||||||
ensure(tag[0] == TAG_NAME) { return null }
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
ensure(tag[1].length == 64) { return null }
|
ensure(tag[1].length == 64) { return null }
|
||||||
|
|
||||||
val hint = tag.getOrNull(2)?.let { RelayUrlNormalizer.normalizeOrNull(it) }
|
return ETag(tag[1], pickRelayHint(tag), pickAuthor(tag))
|
||||||
return ETag(tag[1], hint, tag.getOrNull(3))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@@ -84,6 +84,32 @@ data class ETag(
|
|||||||
return tag[1]
|
return tag[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// simple case ["e", "id", "relay"]
|
||||||
|
// empty tags ["e", "id", "relay", ""]
|
||||||
|
// current root ["e", "id", "relay", "marker"]
|
||||||
|
// current root ["e", "id", "relay", "marker", "pubkey"]
|
||||||
|
// empty tags ["e", "id", "relay", "", "pubkey"]
|
||||||
|
// pubkey marker ["e", "id", "relay", "pubkey"]
|
||||||
|
// pubkey marker ["e", "id", "relay", "pubkey", "marker"]
|
||||||
|
// pubkey marker ["e", "id", "pubkey"] // incorrect
|
||||||
|
// current root ["e", "id", "marker"] // incorrect
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
private fun pickRelayHint(tag: Array<String>): NormalizedRelayUrl? {
|
||||||
|
if (tag.has(2) && tag[2].length > 7 && RelayUrlNormalizer.isRelayUrl(tag[2])) return RelayUrlNormalizer.normalizeOrNull(tag[2])
|
||||||
|
if (tag.has(3) && tag[3].length > 7 && RelayUrlNormalizer.isRelayUrl(tag[3])) return RelayUrlNormalizer.normalizeOrNull(tag[3])
|
||||||
|
if (tag.has(4) && tag[4].length > 7 && RelayUrlNormalizer.isRelayUrl(tag[4])) return RelayUrlNormalizer.normalizeOrNull(tag[4])
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
private fun pickAuthor(tag: Array<String>): HexKey? {
|
||||||
|
if (tag.has(2) && tag[2].length == 64) return tag[2]
|
||||||
|
if (tag.has(3) && tag[3].length == 64) return tag[3]
|
||||||
|
if (tag.has(4) && tag[4].length == 64) return tag[4]
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun parseAsHint(tag: Array<String>): EventIdHint? {
|
fun parseAsHint(tag: Array<String>): EventIdHint? {
|
||||||
ensure(tag.has(2)) { return null }
|
ensure(tag.has(2)) { return null }
|
||||||
@@ -91,7 +117,8 @@ data class ETag(
|
|||||||
ensure(tag[1].length == 64) { return null }
|
ensure(tag[1].length == 64) { return null }
|
||||||
ensure(tag[2].isNotEmpty()) { return null }
|
ensure(tag[2].isNotEmpty()) { return null }
|
||||||
|
|
||||||
val hint = RelayUrlNormalizer.normalizeOrNull(tag[2])
|
val hint = pickRelayHint(tag)
|
||||||
|
|
||||||
ensure(hint != null) { return null }
|
ensure(hint != null) { return null }
|
||||||
|
|
||||||
return EventIdHint(tag[1], hint)
|
return EventIdHint(tag[1], hint)
|
||||||
|
@@ -66,11 +66,16 @@ data class PTag(
|
|||||||
ensure(tag[0] == TAG_NAME) { return null }
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
ensure(tag[1].length == 64) { return null }
|
ensure(tag[1].length == 64) { return null }
|
||||||
|
|
||||||
val hint = tag.getOrNull(2)?.let { RelayUrlNormalizer.normalizeOrNull(it) }
|
val hint = pickRelayHint(tag)
|
||||||
|
|
||||||
return PTag(tag[1], hint)
|
return PTag(tag[1], hint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun pickRelayHint(tag: Array<String>): NormalizedRelayUrl? {
|
||||||
|
if (tag.has(2) && tag[2].length > 7 && RelayUrlNormalizer.isRelayUrl(tag[2])) return RelayUrlNormalizer.normalizeOrNull(tag[2])
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun parseKey(tag: Array<String>): HexKey? {
|
fun parseKey(tag: Array<String>): HexKey? {
|
||||||
ensure(tag.has(1)) { return null }
|
ensure(tag.has(1)) { return null }
|
||||||
@@ -89,10 +94,11 @@ data class PTag(
|
|||||||
ensure(tag[1].length == 64) { return null }
|
ensure(tag[1].length == 64) { return null }
|
||||||
ensure(tag[2].isNotEmpty()) { return null }
|
ensure(tag[2].isNotEmpty()) { return null }
|
||||||
|
|
||||||
val normalized = RelayUrlNormalizer.normalizeOrNull(tag[2])
|
val hint = pickRelayHint(tag)
|
||||||
ensure(normalized != null) { return null }
|
|
||||||
|
|
||||||
return PubKeyHint(tag[1], normalized)
|
ensure(hint != null) { return null }
|
||||||
|
|
||||||
|
return PubKeyHint(tag[1], hint)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@@ -20,12 +20,14 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.quartz.nip10Notes.tags
|
package com.vitorpamplona.quartz.nip10Notes.tags
|
||||||
|
|
||||||
|
import android.R.attr.tag
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||||
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
|
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
|
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer.Companion.isRelayUrl
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.events.GenericETag
|
import com.vitorpamplona.quartz.nip01Core.tags.events.GenericETag
|
||||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
|
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
|
||||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||||
@@ -87,67 +89,71 @@ data class MarkedETag(
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun parse(tag: Array<String>): MarkedETag? {
|
fun parse(tag: Array<String>): MarkedETag? {
|
||||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
ensure(tag.has(2)) { return null }
|
||||||
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
|
ensure(tag[1].length == 64) { return null }
|
||||||
|
ensure(tag[2].isNotEmpty()) { return null }
|
||||||
|
|
||||||
return MarkedETag(
|
return MarkedETag(
|
||||||
tag[ORDER_EVT_ID],
|
eventId = tag[1],
|
||||||
RelayUrlNormalizer.normalizeOrNull(tag[ORDER_RELAY]),
|
relayHint = pickRelayHint(tag),
|
||||||
MARKER.parse(tag[ORDER_MARKER]),
|
marker = pickMarker(tag),
|
||||||
tag.getOrNull(ORDER_PUBKEY),
|
authorPubKeyHex = pickAuthor(tag),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun parseId(tag: Array<String>): HexKey? {
|
fun parseId(tag: Array<String>): HexKey? {
|
||||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
ensure(tag.has(2)) { return null }
|
||||||
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
|
ensure(tag[1].length == 64) { return null }
|
||||||
|
|
||||||
return tag[ORDER_EVT_ID]
|
return tag[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// simple case ["e", "id", "relay"]
|
||||||
|
// empty tags ["e", "id", "relay", ""]
|
||||||
|
// current root ["e", "id", "relay", "marker"]
|
||||||
|
// current root ["e", "id", "relay", "marker", "pubkey"]
|
||||||
|
// empty tags ["e", "id", "relay", "", "pubkey"]
|
||||||
|
// pubkey marker ["e", "id", "relay", "pubkey"]
|
||||||
|
// pubkey marker ["e", "id", "relay", "pubkey", "marker"]
|
||||||
|
// pubkey marker ["e", "id", "pubkey"] // incorrect
|
||||||
|
// current root ["e", "id", "marker"] // incorrect
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
private fun pickRelayHint(tag: Array<String>): NormalizedRelayUrl? {
|
||||||
|
if (tag.has(2) && tag[2].length > 7 && RelayUrlNormalizer.isRelayUrl(tag[2])) return RelayUrlNormalizer.normalizeOrNull(tag[2])
|
||||||
|
if (tag.has(3) && tag[3].length > 7 && RelayUrlNormalizer.isRelayUrl(tag[3])) return RelayUrlNormalizer.normalizeOrNull(tag[3])
|
||||||
|
if (tag.has(4) && tag[4].length > 7 && RelayUrlNormalizer.isRelayUrl(tag[4])) return RelayUrlNormalizer.normalizeOrNull(tag[4])
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
private fun pickAuthor(tag: Array<String>): HexKey? {
|
||||||
|
if (tag.has(3) && tag[3].length == 64) return tag[3]
|
||||||
|
if (tag.has(4) && tag[4].length == 64) return tag[4]
|
||||||
|
if (tag.has(2) && tag[2].length == 64) return tag[2]
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
private fun pickMarker(tag: Array<String>): MARKER? {
|
||||||
|
if (tag.has(3)) MARKER.parse(tag[3])?.let { return it }
|
||||||
|
if (tag.has(4)) MARKER.parse(tag[4])?.let { return it }
|
||||||
|
if (tag.has(2)) MARKER.parse(tag[2])?.let { return it }
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun parseAllThreadTags(tag: Array<String>): MarkedETag? =
|
fun parseAllThreadTags(tag: Array<String>): MarkedETag? =
|
||||||
if (tag.size >= 2 && tag[0] == TAG_NAME) {
|
if (tag.size >= 2 && tag[0] == TAG_NAME) {
|
||||||
if (tag.size <= 3) {
|
MarkedETag(
|
||||||
// simple case ["e", "id", "relay"]
|
eventId = tag[1],
|
||||||
MarkedETag(tag[1], tag.getOrNull(2)?.let { RelayUrlNormalizer.normalizeOrNull(it) }, null, null)
|
relayHint = pickRelayHint(tag),
|
||||||
} else if (tag.size == 4) {
|
marker = pickMarker(tag),
|
||||||
val relayHint = RelayUrlNormalizer.normalizeOrNull(tag[2])
|
authorPubKeyHex = pickAuthor(tag),
|
||||||
if (tag[3].isEmpty()) {
|
)
|
||||||
// empty tags ["e", "id", "relay", ""]
|
|
||||||
MarkedETag(tag[1], relayHint, null, null)
|
|
||||||
} else if (tag[3].length == 64) {
|
|
||||||
// updated case with pubkey instead of marker ["e", "id", "relay", "pubkey"]
|
|
||||||
MarkedETag(tag[1], relayHint, null, tag[3])
|
|
||||||
} else if (tag[3] == MARKER.ROOT.code) {
|
|
||||||
// corrent root ["e", "id", "relay", "root"]
|
|
||||||
MarkedETag(tag[1], relayHint, MARKER.ROOT)
|
|
||||||
} else if (tag[3] == MARKER.REPLY.code) {
|
|
||||||
// correct reply ["e", "id", "relay", "reply"]
|
|
||||||
MarkedETag(tag[1], relayHint, MARKER.REPLY)
|
|
||||||
} else {
|
|
||||||
// ignore "mention" and "fork" markers
|
|
||||||
null
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val relayHint = RelayUrlNormalizer.normalizeOrNull(tag[2])
|
|
||||||
// tag.size >= 5
|
|
||||||
if (tag[3].isEmpty()) {
|
|
||||||
// empty tags ["e", "id", "relay", "", "pubkey"]
|
|
||||||
MarkedETag(tag[1], relayHint, null, tag[4])
|
|
||||||
} else if (tag[3].length == 64) {
|
|
||||||
// updated case with pubkey instead of marker ["e", "id", "relay", "pubkey"]
|
|
||||||
MarkedETag(tag[1], relayHint, null, tag[3])
|
|
||||||
} else if (tag[3] == MARKER.ROOT.code) {
|
|
||||||
// corrent root ["e", "id", "relay", "root"]
|
|
||||||
MarkedETag(tag[1], relayHint, MARKER.ROOT, tag[4])
|
|
||||||
} else if (tag[3] == MARKER.REPLY.code) {
|
|
||||||
// correct reply ["e", "id", "relay", "reply"]
|
|
||||||
MarkedETag(tag[1], relayHint, MARKER.REPLY, tag[4])
|
|
||||||
} else {
|
|
||||||
// ignore "mention" and "fork" markers
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@@ -187,9 +193,8 @@ data class MarkedETag(
|
|||||||
ensure(tag.has(2)) { return null }
|
ensure(tag.has(2)) { return null }
|
||||||
ensure(tag[0] == TAG_NAME) { return null }
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
ensure(tag[1].length == 64) { return null }
|
ensure(tag[1].length == 64) { return null }
|
||||||
ensure(tag[2].isNotEmpty()) { return null }
|
|
||||||
|
|
||||||
val hint = RelayUrlNormalizer.normalizeOrNull(tag[2])
|
val hint = pickRelayHint(tag)
|
||||||
ensure(hint != null) { return null }
|
ensure(hint != null) { return null }
|
||||||
|
|
||||||
return EventIdHint(tag[1], hint)
|
return EventIdHint(tag[1], hint)
|
||||||
@@ -197,15 +202,18 @@ data class MarkedETag(
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun parseRoot(tag: Array<String>): MarkedETag? {
|
fun parseRoot(tag: Array<String>): MarkedETag? {
|
||||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
ensure(tag.has(3)) { return null }
|
||||||
if (tag[ORDER_MARKER] != MARKER.ROOT.code) return null
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
|
ensure(tag[1].length == 64) { return null }
|
||||||
|
|
||||||
|
val marker = pickMarker(tag)
|
||||||
|
ensure(marker == MARKER.ROOT) { return null }
|
||||||
|
|
||||||
// ["e", id hex, relay hint, marker, pubkey]
|
|
||||||
return MarkedETag(
|
return MarkedETag(
|
||||||
eventId = tag[ORDER_EVT_ID],
|
eventId = tag[1],
|
||||||
relayHint = RelayUrlNormalizer.normalizeOrNull(tag[ORDER_RELAY]),
|
relayHint = pickRelayHint(tag),
|
||||||
marker = MARKER.ROOT,
|
marker = marker,
|
||||||
authorPubKeyHex = tag.getOrNull(ORDER_PUBKEY),
|
authorPubKeyHex = pickAuthor(tag),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,23 +223,25 @@ data class MarkedETag(
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun parseUnmarkedRoot(tag: Array<String>): MarkedETag? =
|
fun parseUnmarkedRoot(tag: Array<String>): MarkedETag? =
|
||||||
if (tag.size in 2..3 && tag[0] == TAG_NAME) {
|
if (tag.size in 2..3 && tag[0] == TAG_NAME) {
|
||||||
MarkedETag(tag[1], tag.getOrNull(2)?.let { RelayUrlNormalizer.normalizeOrNull(it) }, MARKER.ROOT)
|
MarkedETag(tag[1], pickRelayHint(tag), MARKER.ROOT)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun parseReply(tag: Array<String>): MarkedETag? {
|
fun parseReply(tag: Array<String>): MarkedETag? {
|
||||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
ensure(tag.has(3)) { return null }
|
||||||
if (tag[ORDER_MARKER] != MARKER.REPLY.code) return null
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
// ["e", id hex, relay hint, marker, pubkey]
|
ensure(tag[1].length == 64) { return null }
|
||||||
|
|
||||||
|
val marker = pickMarker(tag)
|
||||||
|
ensure(marker == MARKER.REPLY) { return null }
|
||||||
|
|
||||||
return MarkedETag(
|
return MarkedETag(
|
||||||
tag[ORDER_EVT_ID],
|
eventId = tag[1],
|
||||||
RelayUrlNormalizer.normalizeOrNull(tag[ORDER_RELAY]),
|
relayHint = pickRelayHint(tag),
|
||||||
MARKER.REPLY,
|
marker = marker,
|
||||||
tag.getOrNull(
|
authorPubKeyHex = pickAuthor(tag),
|
||||||
ORDER_PUBKEY,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,17 +251,21 @@ data class MarkedETag(
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun parseUnmarkedReply(tag: Array<String>): MarkedETag? =
|
fun parseUnmarkedReply(tag: Array<String>): MarkedETag? =
|
||||||
if (tag.size in 2..3 && tag[0] == TAG_NAME) {
|
if (tag.size in 2..3 && tag[0] == TAG_NAME) {
|
||||||
MarkedETag(tag[1], tag.getOrNull(2)?.let { RelayUrlNormalizer.normalizeOrNull(it) }, MARKER.REPLY)
|
MarkedETag(tag[1], pickRelayHint(tag), MARKER.REPLY)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun parseRootId(tag: Array<String>): HexKey? {
|
fun parseRootId(tag: Array<String>): HexKey? {
|
||||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
ensure(tag.has(3)) { return null }
|
||||||
if (tag[ORDER_MARKER] != MARKER.ROOT.code) return null
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
// ["e", id hex, relay hint, marker, pubkey]
|
ensure(tag[1].length == 64) { return null }
|
||||||
return tag[ORDER_EVT_ID]
|
|
||||||
|
val marker = pickMarker(tag)
|
||||||
|
ensure(marker == MARKER.ROOT) { return null }
|
||||||
|
|
||||||
|
return tag[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@@ -20,9 +20,11 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.quartz.nip18Reposts.quotes
|
package com.vitorpamplona.quartz.nip18Reposts.quotes
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||||
import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
|
import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
|
||||||
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
|
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
|
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
||||||
import com.vitorpamplona.quartz.utils.ensure
|
import com.vitorpamplona.quartz.utils.ensure
|
||||||
@@ -38,16 +40,28 @@ interface QTag {
|
|||||||
ensure(tag.has(1)) { return null }
|
ensure(tag.has(1)) { return null }
|
||||||
ensure(tag[0] == TAG_NAME) { return null }
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
|
|
||||||
val relayHint = tag.getOrNull(2)?.let { RelayUrlNormalizer.normalizeOrNull(it) }
|
val relayHint = pickRelayHint(tag)
|
||||||
|
|
||||||
return if (tag[1].length == 64) {
|
return if (tag[1].length == 64) {
|
||||||
QEventTag(tag[1], relayHint, tag.getOrNull(3))
|
QEventTag(tag[1], relayHint, pickAuthor(tag))
|
||||||
} else {
|
} else {
|
||||||
val address = Address.parse(tag[1]) ?: return null
|
val address = Address.parse(tag[1]) ?: return null
|
||||||
QAddressableTag(address, relayHint)
|
QAddressableTag(address, relayHint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun pickRelayHint(tag: Array<String>): NormalizedRelayUrl? {
|
||||||
|
if (tag.has(2) && tag[2].length > 7 && RelayUrlNormalizer.isRelayUrl(tag[2])) return RelayUrlNormalizer.normalizeOrNull(tag[2])
|
||||||
|
if (tag.has(3) && tag[3].length > 7 && RelayUrlNormalizer.isRelayUrl(tag[3])) return RelayUrlNormalizer.normalizeOrNull(tag[3])
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pickAuthor(tag: Array<String>): HexKey? {
|
||||||
|
if (tag.has(2) && tag[2].length == 64) return tag[2]
|
||||||
|
if (tag.has(3) && tag[3].length == 64) return tag[3]
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun parseKey(tag: Array<String>): String? {
|
fun parseKey(tag: Array<String>): String? {
|
||||||
ensure(tag.has(1)) { return null }
|
ensure(tag.has(1)) { return null }
|
||||||
@@ -62,7 +76,7 @@ interface QTag {
|
|||||||
ensure(tag[1].length == 64) { return null }
|
ensure(tag[1].length == 64) { return null }
|
||||||
ensure(tag[2].isNotEmpty()) { return null }
|
ensure(tag[2].isNotEmpty()) { return null }
|
||||||
|
|
||||||
val relayHint = RelayUrlNormalizer.normalizeOrNull(tag[2])
|
val relayHint = pickRelayHint(tag)
|
||||||
ensure(relayHint != null) { return null }
|
ensure(relayHint != null) { return null }
|
||||||
|
|
||||||
return EventIdHint(tag[1], relayHint)
|
return EventIdHint(tag[1], relayHint)
|
||||||
@@ -76,7 +90,7 @@ interface QTag {
|
|||||||
ensure(tag[2].isNotEmpty()) { return null }
|
ensure(tag[2].isNotEmpty()) { return null }
|
||||||
ensure(!tag[1].contains(':')) { return null }
|
ensure(!tag[1].contains(':')) { return null }
|
||||||
|
|
||||||
val relayHint = RelayUrlNormalizer.normalizeOrNull(tag[2])
|
val relayHint = pickRelayHint(tag)
|
||||||
ensure(relayHint != null) { return null }
|
ensure(relayHint != null) { return null }
|
||||||
|
|
||||||
return AddressHint(tag[1], relayHint)
|
return AddressHint(tag[1], relayHint)
|
||||||
|
Reference in New Issue
Block a user