Merge branch 'vitorpamplona:main' into profiles-list-management
This commit is contained in:
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.audio.header.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class DownloadUrlTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "download_url"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.audio.header.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class StreamUrlTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "stream_url"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -21,7 +21,9 @@
|
||||
package com.vitorpamplona.quartz.experimental.audio.header.tags
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class WaveformTag(
|
||||
val wave: List<Int>,
|
||||
@@ -30,12 +32,12 @@ class WaveformTag(
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "waveform"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): WaveformTag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
if (tag[1].isEmpty()) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
val wave = runCatching { EventMapper.mapper.readValue<List<Int>>(tag[1]) }.getOrNull()
|
||||
if (wave.isNullOrEmpty()) return null
|
||||
return WaveformTag(wave)
|
||||
|
||||
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.audio.track.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class CoverTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "cover"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.audio.track.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class MediaTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "media"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
+9
-7
@@ -23,9 +23,10 @@ package com.vitorpamplona.quartz.experimental.audio.track.tags
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.isNotName
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
@Immutable
|
||||
data class ParticipantTag(
|
||||
@@ -36,26 +37,27 @@ data class ParticipantTag(
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "p"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Tag): ParticipantTag? {
|
||||
if (tag.isNotName(TAG_NAME, TAG_SIZE)) return null
|
||||
if (tag[1].length != 64) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return ParticipantTag(tag[1], tag.getOrNull(2))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseKey(tag: Tag): String? {
|
||||
if (tag.isNotName(TAG_NAME, TAG_SIZE)) return null
|
||||
if (tag[1].length != 64) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
pubkey: HexKey,
|
||||
relayHint: String?,
|
||||
relayHint: String? = null,
|
||||
) = arrayOfNotNull(TAG_NAME, pubkey, relayHint)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.audio.track.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class PriceTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "price"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.audio.track.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class TypeTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "c"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
@@ -20,16 +20,19 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.bounties
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
import java.math.BigDecimal
|
||||
|
||||
class RewardTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "reward"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): BigDecimal? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
|
||||
return runCatching { BigDecimal(tag[1]) }.getOrNull()
|
||||
}
|
||||
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.decoupling
|
||||
|
||||
import com.vitorpamplona.quartz.experimental.decoupling.setup.EncryptionKeyListEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip44Encryption.Nip44
|
||||
|
||||
class DecoupledCipher {
|
||||
fun innerEncrypt(
|
||||
content: String,
|
||||
privKey: ByteArray,
|
||||
toPublicKey: HexKey,
|
||||
) = Nip44
|
||||
.encrypt(content, privKey, toPublicKey.hexToByteArray())
|
||||
.encodePayload()
|
||||
|
||||
fun innerDecrypt(
|
||||
ciphertext: String,
|
||||
privKey: ByteArray,
|
||||
fromPublicKey: HexKey,
|
||||
) = Nip44.decrypt(
|
||||
payload = ciphertext,
|
||||
privateKey = privKey,
|
||||
pubKey = fromPublicKey.hexToByteArray(),
|
||||
)
|
||||
|
||||
fun encrypt(
|
||||
decryptedContent: String,
|
||||
toPublicKey: HexKey,
|
||||
fromKeyList: EncryptionKeyListEvent,
|
||||
toKeyList: EncryptionKeyListEvent,
|
||||
signer: NostrSigner,
|
||||
onReady: (String) -> Unit,
|
||||
) {
|
||||
val toKeys = toKeyList.keys()
|
||||
val sendToKey = if (toKeys.isEmpty()) toKeyList.pubKey else toKeys.random().pubkey
|
||||
|
||||
val fromKeys = fromKeyList.keys()
|
||||
|
||||
// uses the main key
|
||||
if (fromKeys.isEmpty()) {
|
||||
signer.nip44Encrypt(decryptedContent, sendToKey, onReady)
|
||||
} else {
|
||||
val keyToUse = fromKeys.random()
|
||||
|
||||
EncryptionKeyCache.getOrLoad(
|
||||
deriveFromPubKey = signer.pubKey,
|
||||
nonce = keyToUse.nonce,
|
||||
load = { onLoaded ->
|
||||
signer.deriveKey(keyToUse.nonce) { newPrivKey ->
|
||||
onLoaded(newPrivKey.hexToByteArray())
|
||||
}
|
||||
},
|
||||
) { derivedPrivKey ->
|
||||
onReady(innerEncrypt(decryptedContent, derivedPrivKey, sendToKey))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun decrypt(
|
||||
encryptedContent: String,
|
||||
fromPublicKey: HexKey,
|
||||
toPublicKey: HexKey,
|
||||
fromKeyList: EncryptionKeyListEvent,
|
||||
toEncryptedKeyList: EncryptionKeyListEvent,
|
||||
signer: NostrSigner,
|
||||
onReady: (String) -> Unit,
|
||||
) {
|
||||
val fromKeys = fromKeyList.keys()
|
||||
val sentFromKey = if (fromKeys.isEmpty()) fromKeyList.pubKey else fromKeys.random().pubkey
|
||||
|
||||
val keyToUse = toEncryptedKeyList.keys().firstOrNull { it.pubkey == toPublicKey }
|
||||
|
||||
// uses the main key
|
||||
if (signer.pubKey == toPublicKey) {
|
||||
signer.nip44Decrypt(encryptedContent, sentFromKey, onReady)
|
||||
} else if (keyToUse != null) {
|
||||
EncryptionKeyCache.getOrLoad(
|
||||
deriveFromPubKey = signer.pubKey,
|
||||
nonce = keyToUse.nonce,
|
||||
load = { onLoaded ->
|
||||
signer.deriveKey(keyToUse.nonce) { newPrivKey ->
|
||||
onLoaded(newPrivKey.hexToByteArray())
|
||||
}
|
||||
},
|
||||
) { derivedPrivKey ->
|
||||
innerDecrypt(encryptedContent, derivedPrivKey, sentFromKey)?.let { onReady(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.decoupling
|
||||
|
||||
import android.util.LruCache
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
|
||||
object EncryptionKeyCache {
|
||||
private val sharedNonceKeyCache = LruCache<EncryptionKeyCacheKey, ByteArray>(50)
|
||||
|
||||
private fun idx(
|
||||
key: HexKey,
|
||||
nonce: HexKey,
|
||||
) = EncryptionKeyCacheKey(key, nonce)
|
||||
|
||||
fun put(
|
||||
deriveFromPubKey: HexKey,
|
||||
nonce: HexKey,
|
||||
privKey: ByteArray,
|
||||
) = sharedNonceKeyCache.put(idx(deriveFromPubKey, nonce), privKey)
|
||||
|
||||
fun get(
|
||||
deriveFromPubKey: HexKey,
|
||||
nonce: HexKey,
|
||||
) = sharedNonceKeyCache.get(idx(deriveFromPubKey, nonce))
|
||||
|
||||
inline fun getOrLoad(
|
||||
deriveFromPubKey: HexKey,
|
||||
nonce: HexKey,
|
||||
load: (onLoaded: (privKey: ByteArray) -> Unit) -> Unit,
|
||||
crossinline whenReady: (privKey: ByteArray) -> Unit,
|
||||
) {
|
||||
val cachedPrivKey = get(deriveFromPubKey, nonce)
|
||||
if (cachedPrivKey != null) {
|
||||
whenReady(cachedPrivKey)
|
||||
return
|
||||
}
|
||||
|
||||
load { newPrivKey ->
|
||||
put(deriveFromPubKey, nonce, newPrivKey)
|
||||
whenReady(newPrivKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// keeps a cache per signed-in user
|
||||
private data class EncryptionKeyCacheKey(
|
||||
val pubkey: HexKey,
|
||||
val nonce: HexKey,
|
||||
)
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.decoupling
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip44Encryption.crypto.Hkdf
|
||||
import com.vitorpamplona.quartz.utils.sha256.sha256
|
||||
|
||||
class EncryptionKeyDerivation {
|
||||
companion object {
|
||||
private val saltPrefix = "nip44kd".toByteArray(Charsets.UTF_8)
|
||||
|
||||
fun derivePrivateKey(
|
||||
privKey: ByteArray,
|
||||
nonce: ByteArray?,
|
||||
): ByteArray {
|
||||
if (nonce == null) {
|
||||
return privKey
|
||||
}
|
||||
return sha256(Hkdf().extract(privKey, saltPrefix + nonce))
|
||||
}
|
||||
|
||||
fun deriveKeyPair(
|
||||
privKey: ByteArray,
|
||||
nonce: ByteArray?,
|
||||
): KeyPair = KeyPair(privKey = derivePrivateKey(privKey, nonce))
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.decoupling.setup
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.experimental.decoupling.setup.tags.KeyTag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventUpdate
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable
|
||||
class EncryptionKeyListEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
fun keys() = tags.mapNotNull(KeyTag::parse)
|
||||
|
||||
companion object {
|
||||
const val KIND = 10044
|
||||
const val ALT = "Encryption keys"
|
||||
|
||||
fun add(
|
||||
key: KeyTag,
|
||||
currentEvent: EncryptionKeyListEvent,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
initializer: TagArrayBuilder<EncryptionKeyListEvent>.() -> Unit = {},
|
||||
) = eventUpdate(currentEvent, createdAt) {
|
||||
alt(ALT)
|
||||
key(key)
|
||||
initializer()
|
||||
}
|
||||
|
||||
fun remove(
|
||||
key: KeyTag,
|
||||
currentEvent: EncryptionKeyListEvent,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
initializer: TagArrayBuilder<EncryptionKeyListEvent>.() -> Unit = {},
|
||||
) = eventUpdate(currentEvent, createdAt) {
|
||||
alt(ALT)
|
||||
removeIf(KeyTag::isSameKey, key.toTagArray())
|
||||
initializer()
|
||||
}
|
||||
|
||||
fun build(
|
||||
keys: List<KeyTag>,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
initializer: TagArrayBuilder<EncryptionKeyListEvent>.() -> Unit = {},
|
||||
) = eventTemplate(KIND, "", createdAt) {
|
||||
alt(ALT)
|
||||
keys(keys)
|
||||
initializer()
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.decoupling.setup
|
||||
|
||||
import com.vitorpamplona.quartz.experimental.decoupling.setup.tags.KeyTag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
|
||||
fun TagArrayBuilder<EncryptionKeyListEvent>.key(
|
||||
key: HexKey,
|
||||
nonce: HexKey,
|
||||
) = add(KeyTag.assemble(key, nonce))
|
||||
|
||||
fun TagArrayBuilder<EncryptionKeyListEvent>.key(key: KeyTag) = add(key.toTagArray())
|
||||
|
||||
fun TagArrayBuilder<EncryptionKeyListEvent>.keys(keys: List<KeyTag>) = addAll(keys.map { it.toTagArray() })
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.decoupling.setup.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class KeyTag(
|
||||
val pubkey: HexKey,
|
||||
val nonce: HexKey,
|
||||
) {
|
||||
fun toTagArray() = assemble(pubkey, nonce)
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "n"
|
||||
|
||||
fun isSameKey(
|
||||
tag1: Array<String>,
|
||||
tag2: Array<String>,
|
||||
): Boolean {
|
||||
ensure(tag1.has(1)) { return false }
|
||||
ensure(tag2.has(1)) { return false }
|
||||
ensure(tag1[0] == tag2[0]) { return false }
|
||||
ensure(tag1[1] == tag2[1]) { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): KeyTag? {
|
||||
ensure(tag.has(2)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
ensure(tag[2].isNotEmpty()) { return null }
|
||||
return KeyTag(tag[1], tag[2])
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
key: HexKey,
|
||||
nonce: HexKey,
|
||||
) = arrayOf(TAG_NAME, key, nonce)
|
||||
}
|
||||
}
|
||||
+2
@@ -47,6 +47,8 @@ class PrivateOutboxRelayListEvent(
|
||||
) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
@Transient private var privateTagsCache: Array<Array<String>>? = null
|
||||
|
||||
override fun isContentEncoded() = true
|
||||
|
||||
override fun countMemory(): Long =
|
||||
super.countMemory() +
|
||||
pointerSizeInBytes + (privateTagsCache?.sumOf { pointerSizeInBytes + it.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } } ?: 0)
|
||||
|
||||
@@ -24,7 +24,7 @@ import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
|
||||
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag.MARKER
|
||||
|
||||
fun MarkedETag.Companion.parseFork(tag: Array<String>): MarkedETag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
if (tag.size < 4 || tag[0] != "e") return null
|
||||
if (tag[ORDER_MARKER] != MARKER.FORK.code) return null
|
||||
// ["e", id hex, relay hint, marker, pubkey]
|
||||
return MarkedETag(
|
||||
|
||||
+6
-2
@@ -20,6 +20,9 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.interactiveStories.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class ReadStatusTag {
|
||||
enum class STATUS(
|
||||
val value: String,
|
||||
@@ -34,11 +37,12 @@ class ReadStatusTag {
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "status"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -60,7 +60,6 @@ data class RootSceneTag(
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "A"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
fun assembleATagId(
|
||||
kind: Int,
|
||||
|
||||
-1
@@ -35,7 +35,6 @@ class StoryOptionTag(
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "option"
|
||||
const val TAG_SIZE = 3
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): StoryOptionTag? {
|
||||
|
||||
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.nns.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class IPv4Tag {
|
||||
companion object {
|
||||
const val TAG_NAME = "ip4"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.nns.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class IPv6Tag {
|
||||
companion object {
|
||||
const val TAG_NAME = "ip6"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.nns.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class VersionTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "version"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.relationshipStatus.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class PetnameTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "petname"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.relationshipStatus.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class RankTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "rank"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): Int? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1].toIntOrNull()
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.relationshipStatus.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class SummaryTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "summary"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.zapPolls.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class ClosedAtTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "closed_at"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): Long? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1].toLongOrNull()
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -20,16 +20,19 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.zapPolls.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
import kotlin.math.round
|
||||
|
||||
class ConsensusThresholdTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "consensus_threshold"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): Double? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1].toLongOrNull()?.toDouble()?.div(100)
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.zapPolls.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class MaximumTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "value_maximum"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): Long? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1].toLongOrNull()
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.zapPolls.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class MinimumTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "value_minimum"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): Long? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1].toLongOrNull()
|
||||
}
|
||||
|
||||
|
||||
+7
-2
@@ -20,6 +20,9 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.zapPolls.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class PollOptionTag(
|
||||
val index: Int,
|
||||
val descriptor: String,
|
||||
@@ -28,11 +31,13 @@ class PollOptionTag(
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "poll_option"
|
||||
const val TAG_SIZE = 3
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): PollOptionTag? {
|
||||
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].isNotEmpty()) { return null }
|
||||
ensure(tag[2].isNotEmpty()) { return null }
|
||||
|
||||
val index = tag[1].toIntOrNull() ?: return null
|
||||
|
||||
|
||||
@@ -27,64 +27,12 @@ typealias Tag = Array<String>
|
||||
*/
|
||||
fun Tag.has(index: Int) = size > index
|
||||
|
||||
/**
|
||||
* Returns if the Tag has less than $index elements.
|
||||
*/
|
||||
fun Tag.lacks(index: Int) = size <= index
|
||||
|
||||
fun Tag.name() = this[0]
|
||||
|
||||
fun Tag.value() = this[1]
|
||||
|
||||
fun Tag.hasValue() = this[1].isNotEmpty()
|
||||
fun Tag.hasValue() = if (has(1)) this[1].isNotEmpty() else false
|
||||
|
||||
fun Tag.nameOrNull() = if (size > 0) name() else null
|
||||
fun Tag.nameOrNull() = if (has(0)) this[0] else null
|
||||
|
||||
fun Tag.valueOrNull() = if (size > 1) value() else null
|
||||
|
||||
fun Tag.isNameUnsafe(name: String): Boolean = name() == name
|
||||
|
||||
fun Tag.isValueUnsafe(value: String): Boolean = value() == value
|
||||
|
||||
fun Tag.isValueInUnsafe(values: Set<String>): Boolean = value() in values
|
||||
|
||||
fun Tag.match(name: String): Boolean = if (size > 0) isNameUnsafe(name) else false
|
||||
|
||||
fun Tag.isValue(value: String): Boolean = if (size > 1) isValueUnsafe(value) else false
|
||||
|
||||
fun Tag.match(
|
||||
name: String,
|
||||
value: String,
|
||||
minSize: Int,
|
||||
): Boolean = if (size >= minSize) isNameUnsafe(name) && isValueUnsafe(value) else false
|
||||
|
||||
fun Tag.match(
|
||||
name: String,
|
||||
values: Set<String>,
|
||||
minSize: Int,
|
||||
): Boolean = if (size >= minSize) isNameUnsafe(name) && isValueInUnsafe(values) else false
|
||||
|
||||
fun Tag.match(
|
||||
name: String,
|
||||
minSize: Int,
|
||||
): Boolean = if (size >= minSize) isNameUnsafe(name) else false
|
||||
|
||||
fun Tag.isNotName(
|
||||
name: String,
|
||||
minSize: Int,
|
||||
): Boolean = !match(name, minSize)
|
||||
|
||||
fun Tag.matchAndHasValue(
|
||||
name: String,
|
||||
minSize: Int,
|
||||
): Boolean = if (size >= minSize) isNameUnsafe(name) && hasValue() else false
|
||||
|
||||
fun Tag.valueIfMatches(
|
||||
name: String,
|
||||
minSize: Int,
|
||||
): String? = if (match(name, minSize)) value() else null
|
||||
|
||||
fun Tag.valueToIntIfMatches(
|
||||
name: String,
|
||||
minSize: Int,
|
||||
): Int? = if (match(name, minSize)) value().toIntOrNull() else null
|
||||
fun Tag.valueOrNull() = if (has(1)) this[1] else null
|
||||
|
||||
@@ -35,7 +35,7 @@ class TagArrayBuilder<T : IEvent> {
|
||||
tagName: String,
|
||||
tagValue: String,
|
||||
): TagArrayBuilder<T> {
|
||||
tagList[tagName]?.removeIf { it.value() == tagValue }
|
||||
tagList[tagName]?.removeIf { it.valueOrNull() == tagValue }
|
||||
if (tagList[tagName]?.isEmpty() == true) {
|
||||
tagList.remove(tagName)
|
||||
}
|
||||
@@ -46,18 +46,8 @@ class TagArrayBuilder<T : IEvent> {
|
||||
predicate: (Tag, Tag) -> Boolean,
|
||||
toCompare: Tag,
|
||||
): TagArrayBuilder<T> {
|
||||
tagList[toCompare.name()]?.removeIf { predicate(it, toCompare) }
|
||||
if (tagList[toCompare.name()]?.isEmpty() == true) {
|
||||
tagList.remove(toCompare.name())
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun removeIf(
|
||||
tagName: String,
|
||||
tagValue: String,
|
||||
): TagArrayBuilder<T> {
|
||||
tagList[tagName]?.removeIf { it.value() == tagValue }
|
||||
val tagName = toCompare.nameOrNull() ?: return this
|
||||
tagList[tagName]?.removeIf { predicate(it, toCompare) }
|
||||
if (tagList[tagName]?.isEmpty() == true) {
|
||||
tagList.remove(tagName)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||
class KeyPair(
|
||||
privKey: ByteArray? = null,
|
||||
pubKey: ByteArray? = null,
|
||||
forceReplacePubkey: Boolean = true,
|
||||
) {
|
||||
val privKey: ByteArray?
|
||||
val pubKey: ByteArray
|
||||
@@ -44,7 +45,11 @@ class KeyPair(
|
||||
} else {
|
||||
// as private key is provided, ignore the public key and set keys according to private key
|
||||
this.privKey = privKey
|
||||
this.pubKey = Nip01.pubKeyCreate(privKey)
|
||||
if (pubKey == null || forceReplacePubkey) {
|
||||
this.pubKey = Nip01.pubKeyCreate(privKey)
|
||||
} else {
|
||||
this.pubKey = pubKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,11 @@ abstract class NostrSigner(
|
||||
onReady: (LnZapPrivateEvent) -> Unit,
|
||||
)
|
||||
|
||||
abstract fun deriveKey(
|
||||
nonce: HexKey,
|
||||
onReady: (HexKey) -> Unit,
|
||||
)
|
||||
|
||||
fun decrypt(
|
||||
encryptedContent: String,
|
||||
fromPublicKey: HexKey,
|
||||
|
||||
@@ -80,4 +80,11 @@ class NostrSignerInternal(
|
||||
) {
|
||||
signerSync.decryptZapEvent(event)?.let { onReady(it) }
|
||||
}
|
||||
|
||||
override fun deriveKey(
|
||||
nonce: HexKey,
|
||||
onReady: (HexKey) -> Unit,
|
||||
) {
|
||||
signerSync.deriveKey(nonce)?.let { onReady(it) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.quartz.nip01Core.signers
|
||||
|
||||
import android.util.Log
|
||||
import com.vitorpamplona.quartz.experimental.decoupling.EncryptionKeyDerivation
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
|
||||
@@ -128,4 +129,10 @@ class NostrSignerSync(
|
||||
}
|
||||
|
||||
fun decryptZapEvent(event: LnZapRequestEvent): LnZapPrivateEvent? = PrivateZapRequestBuilder().decryptZapEvent(event, this)
|
||||
|
||||
fun deriveKey(nonce: HexKey): HexKey? {
|
||||
if (keyPair.privKey == null) return null
|
||||
|
||||
return EncryptionKeyDerivation.derivePrivateKey(keyPair.privKey, nonce.hexToByteArray()).toHexKey()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,6 @@ package com.vitorpamplona.quartz.nip01Core.tags.addressables
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.core.match
|
||||
import com.vitorpamplona.quartz.nip01Core.core.name
|
||||
import com.vitorpamplona.quartz.nip01Core.core.value
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||
@@ -57,40 +54,44 @@ data class ATag(
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "a"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(tag: Array<String>) = tag.size >= 2 && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
fun isTagged(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
|
||||
@JvmStatic
|
||||
fun isSameAddress(
|
||||
tag1: Array<String>,
|
||||
tag2: Array<String>,
|
||||
) = tag1.match(tag2.name(), tag2.value(), TAG_SIZE)
|
||||
): Boolean {
|
||||
ensure(tag1.has(1)) { return false }
|
||||
ensure(tag2.has(1)) { return false }
|
||||
ensure(tag1[0] == tag2[0]) { return false }
|
||||
ensure(tag1[1] == tag2[1]) { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(
|
||||
tag: Array<String>,
|
||||
addressId: String,
|
||||
) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1] == addressId
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == addressId
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(
|
||||
tag: Array<String>,
|
||||
address: ATag,
|
||||
) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1] == address.toTag()
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == address.toTag()
|
||||
|
||||
@JvmStatic
|
||||
fun isIn(
|
||||
tag: Array<String>,
|
||||
addressIds: Set<String>,
|
||||
) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1] in addressIds
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in addressIds
|
||||
|
||||
@JvmStatic
|
||||
fun isTaggedWithKind(
|
||||
tag: Array<String>,
|
||||
kind: String,
|
||||
) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && Address.isOfKind(tag[1], kind)
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && Address.isOfKind(tag[1], kind)
|
||||
|
||||
@JvmStatic
|
||||
fun parseIfOfKind(
|
||||
|
||||
@@ -22,10 +22,12 @@ package com.vitorpamplona.quartz.nip01Core.tags.events
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||
|
||||
@Immutable
|
||||
@@ -56,32 +58,38 @@ data class ETag(
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "e"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(tag: Array<String>) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1].length == 64
|
||||
fun isTagged(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].length == 64
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(
|
||||
tag: Array<String>,
|
||||
eventId: HexKey,
|
||||
) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1] == eventId
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == eventId
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): ETag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return ETag(tag[1], tag.getOrNull(2), tag.getOrNull(3))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseId(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseAsHint(tag: Array<String>): EventIdHint? {
|
||||
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) 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 EventIdHint(tag[1], tag[2])
|
||||
}
|
||||
|
||||
|
||||
@@ -20,17 +20,21 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.tags.hashtags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class HashtagTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "t"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(tag: Array<String>) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
fun isTagged(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].isEmpty()) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
@@ -20,26 +20,30 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.tags.kinds
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class KindTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "k"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
fun match(tag: Array<String>) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME
|
||||
fun match(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME
|
||||
|
||||
fun isTagged(
|
||||
tag: Array<String>,
|
||||
kind: String,
|
||||
) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1] == kind
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == kind
|
||||
|
||||
fun isIn(
|
||||
tag: Array<String>,
|
||||
kinds: Set<String>,
|
||||
) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1] in kinds
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in kinds
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): Int? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1].toInt()
|
||||
}
|
||||
|
||||
|
||||
@@ -24,12 +24,14 @@ import android.util.Log
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NProfile
|
||||
import com.vitorpamplona.quartz.nip19Bech32.toNpub
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||
|
||||
@Immutable
|
||||
@@ -50,24 +52,25 @@ data class PTag(
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "p"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
fun isTagged(
|
||||
tag: Array<String>,
|
||||
key: HexKey,
|
||||
): Boolean = tag.size >= 2 && tag[0] == TAG_NAME && tag[1] == key
|
||||
): Boolean = tag.has(1) && tag[0] == TAG_NAME && tag[1] == key
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Tag): PTag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
if (tag[1].length != 64) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return PTag(tag[1], tag.getOrNull(2))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseKey(tag: Array<String>): HexKey? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
if (tag[1].length != 64) {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) {
|
||||
Log.w("PTag", "Invalid `$TAG_NAME` value ${tag.joinToString(", ")}")
|
||||
return null
|
||||
}
|
||||
@@ -76,7 +79,10 @@ data class PTag(
|
||||
|
||||
@JvmStatic
|
||||
fun parseAsHint(tag: Array<String>): PubKeyHint? {
|
||||
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) 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 PubKeyHint(tag[1], tag[2])
|
||||
}
|
||||
|
||||
|
||||
+9
-5
@@ -20,34 +20,38 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.tags.references
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip96FileStorage.HttpUrlFormatter
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class ReferenceTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "r"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(
|
||||
tag: Array<String>,
|
||||
reference: String,
|
||||
): Boolean = tag.size >= 2 && tag[0] == TAG_NAME && tag[1] == reference
|
||||
): Boolean = tag.has(1) && tag[0] == TAG_NAME && tag[1] == reference
|
||||
|
||||
@JvmStatic
|
||||
fun isIn(
|
||||
tag: Array<String>,
|
||||
references: Set<String>,
|
||||
): Boolean = tag.size >= 2 && tag[0] == TAG_NAME && tag[1] in references
|
||||
): Boolean = tag.has(1) && tag[0] == TAG_NAME && tag[1] in references
|
||||
|
||||
@JvmStatic
|
||||
fun hasReference(tag: Array<String>): Boolean {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return false
|
||||
ensure(tag.has(1)) { return false }
|
||||
ensure(tag[0] == TAG_NAME) { return false }
|
||||
return tag[1].isNotEmpty()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
@@ -23,11 +23,13 @@ package com.vitorpamplona.quartz.nip02FollowList.tags
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint
|
||||
import com.vitorpamplona.quartz.nip19Bech32.decodePublicKey
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||
|
||||
@Immutable
|
||||
@@ -56,20 +58,23 @@ data class ContactTag(
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "p"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(tag: Array<String>) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
fun isTagged(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): ContactTag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return ContactTag(tag[1], tag.getOrNull(2), tag.getOrNull(3))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseValid(tag: Array<String>): ContactTag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return try {
|
||||
ContactTag(decodePublicKey(tag[1]).toHexKey(), tag.getOrNull(2), tag.getOrNull(3))
|
||||
} catch (e: Exception) {
|
||||
@@ -80,13 +85,17 @@ data class ContactTag(
|
||||
|
||||
@JvmStatic
|
||||
fun parseKey(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseValidKey(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return try {
|
||||
decodePublicKey(tag[1]).toHexKey()
|
||||
} catch (e: Exception) {
|
||||
@@ -97,7 +106,10 @@ data class ContactTag(
|
||||
|
||||
@JvmStatic
|
||||
fun parseAsHint(tag: Array<String>): PubKeyHint? {
|
||||
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) 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 PubKeyHint(tag[1], tag[2])
|
||||
}
|
||||
|
||||
|
||||
@@ -22,11 +22,13 @@ package com.vitorpamplona.quartz.nip10Notes.tags
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.events.GenericETag
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||
|
||||
@Immutable
|
||||
@@ -71,7 +73,7 @@ data class MarkedETag(
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "e"
|
||||
const val TAG_SIZE = 4
|
||||
private const val TAG_SIZE = 4
|
||||
|
||||
const val ORDER_NAME = 0
|
||||
const val ORDER_EVT_ID = 1
|
||||
@@ -183,7 +185,10 @@ data class MarkedETag(
|
||||
|
||||
@JvmStatic
|
||||
fun parseAsHint(tag: Array<String>): EventIdHint? {
|
||||
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) 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 EventIdHint(tag[1], tag[2])
|
||||
}
|
||||
|
||||
|
||||
@@ -48,9 +48,9 @@ fun List<String>.positionalMarkedTags(
|
||||
}
|
||||
}.map {
|
||||
when (it) {
|
||||
root -> arrayOf(tagName, it, "", "root")
|
||||
replyingTo -> arrayOf(tagName, it, "", "reply")
|
||||
forkedFrom -> arrayOf(tagName, it, "", "fork")
|
||||
root -> arrayOf(tagName, it, "", MarkedETag.MARKER.ROOT)
|
||||
replyingTo -> arrayOf(tagName, it, "", MarkedETag.MARKER.REPLY)
|
||||
forkedFrom -> arrayOf(tagName, it, "", MarkedETag.MARKER.FORK)
|
||||
else -> arrayOf(tagName, it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip13Pow.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||
|
||||
class PoWTag(
|
||||
@@ -34,20 +36,23 @@ class PoWTag(
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "nonce"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun hasTagWithContent(tag: Array<String>) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
fun hasTagWithContent(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): PoWTag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return PoWTag(tag[1], tag.getOrNull(2)?.toIntOrNull())
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseCommitment(tag: Array<String>): Int? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag.getOrNull(2)?.toIntOrNull()
|
||||
}
|
||||
|
||||
|
||||
@@ -20,17 +20,21 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip14Subject
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class SubjectTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "subject"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun hasTagWithContent(tag: Array<String>) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
fun hasTagWithContent(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
@@ -20,17 +20,21 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip17Dm.files.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class EncryptionAlgo {
|
||||
companion object {
|
||||
const val TAG_NAME = "encryption-algorithm"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun isTag(tag: Array<String>) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME
|
||||
fun isTag(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
@@ -20,20 +20,23 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip17Dm.files.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class EncryptionKey {
|
||||
companion object {
|
||||
const val TAG_NAME = "decryption-key"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun isTag(tag: Array<String>) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME
|
||||
fun isTag(tag: Array<String>) = tag.has(0) && tag[0] == TAG_NAME
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): ByteArray? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return runCatching { tag[1].hexToByteArray() }.getOrNull()
|
||||
}
|
||||
|
||||
|
||||
@@ -20,20 +20,23 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip17Dm.files.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class EncryptionNonce {
|
||||
companion object {
|
||||
const val TAG_NAME = "decryption-nonce"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun isTag(tag: Array<String>) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME
|
||||
fun isTag(tag: Array<String>) = tag.has(0) && tag[0] == TAG_NAME
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): ByteArray? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return runCatching { tag[1].hexToByteArray() }.getOrNull()
|
||||
}
|
||||
|
||||
|
||||
@@ -20,19 +20,23 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip17Dm.files.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class FileTypeTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "file-type"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
fun isIn(
|
||||
tag: Array<String>,
|
||||
mimeTypes: Set<String>,
|
||||
) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1] in mimeTypes
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in mimeTypes
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -59,13 +59,13 @@ data class QAddressableTag(
|
||||
override fun toTagArray() = assemble(address, relay)
|
||||
|
||||
companion object {
|
||||
const val TAG_SIZE = 2
|
||||
const val TAG_NAME = "q"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): QAddressableTag? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == QTag.TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length != 64) { return null }
|
||||
val address = Address.parse(tag[1]) ?: return null
|
||||
return QAddressableTag(address, tag.getOrNull(2))
|
||||
}
|
||||
@@ -76,12 +76,12 @@ data class QAddressableTag(
|
||||
pubKeyHex: HexKey,
|
||||
dTag: String,
|
||||
relay: String?,
|
||||
) = arrayOfNotNull(QTag.TAG_NAME, Address.assemble(kind, pubKeyHex, dTag), relay)
|
||||
) = arrayOfNotNull(TAG_NAME, Address.assemble(kind, pubKeyHex, dTag), relay)
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
address: Address,
|
||||
relay: String?,
|
||||
) = arrayOfNotNull(QTag.TAG_NAME, address.toValue(), relay)
|
||||
) = arrayOfNotNull(TAG_NAME, address.toValue(), relay)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,10 @@ package com.vitorpamplona.quartz.nip18Reposts.quotes
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||
|
||||
@Immutable
|
||||
@@ -47,11 +49,13 @@ data class QEventTag(
|
||||
override fun toTagArray() = assemble(eventId, relay, author)
|
||||
|
||||
companion object {
|
||||
const val TAG_SIZE = 2
|
||||
const val TAG_NAME = "q"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): QEventTag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != QTag.TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return QEventTag(tag[1], tag.getOrNull(2), tag.getOrNull(3))
|
||||
}
|
||||
|
||||
@@ -60,6 +64,6 @@ data class QEventTag(
|
||||
eventId: HexKey,
|
||||
relay: String?,
|
||||
author: HexKey?,
|
||||
) = arrayOfNotNull(QTag.TAG_NAME, eventId, relay, author)
|
||||
) = arrayOfNotNull(TAG_NAME, eventId, relay, author)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,35 +20,46 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip18Reposts.quotes
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
interface QTag {
|
||||
fun toTagArray(): Array<String>
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "q"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): QTag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
return if (tag[1].length == 64) {
|
||||
QEventTag.parse(tag)
|
||||
QEventTag(tag[1], tag.getOrNull(2), tag.getOrNull(3))
|
||||
} else {
|
||||
QAddressableTag.parse(tag)
|
||||
val address = Address.parse(tag[1]) ?: return null
|
||||
QAddressableTag(address, tag.getOrNull(2))
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseEventAsHint(tag: Array<String>): EventIdHint? {
|
||||
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
ensure(tag[2].isNotEmpty()) { return null }
|
||||
return EventIdHint(tag[1], tag[2])
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseAddressAsHint(tag: Array<String>): AddressHint? {
|
||||
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length == 64 || !tag[1].contains(':') || tag[2].isEmpty()) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length != 64) { return null }
|
||||
ensure(tag[2].isNotEmpty()) { return null }
|
||||
ensure(!tag[1].contains(':')) { return null }
|
||||
return AddressHint(tag[1], tag[2])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,8 +107,8 @@ class CommentEvent(
|
||||
?: tags.lastNotNullOfOrNull(RootEventTag::parseKey)
|
||||
|
||||
fun replyingToAddressId(): String? =
|
||||
tags.lastNotNullOfOrNull(RootAddressTag::parseAddress)
|
||||
?: tags.lastNotNullOfOrNull(ReplyAddressTag::parseAddress)
|
||||
tags.lastNotNullOfOrNull(RootAddressTag::parseAddressId)
|
||||
?: tags.lastNotNullOfOrNull(ReplyAddressTag::parseAddressId)
|
||||
|
||||
override fun replyingToAddressOrEvent(): HexKey? = replyingToAddressId() ?: replyingTo()
|
||||
|
||||
|
||||
+16
-7
@@ -23,11 +23,11 @@ package com.vitorpamplona.quartz.nip22Comments.tags
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.match
|
||||
import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
@Immutable
|
||||
class ReplyAddressTag(
|
||||
@@ -38,23 +38,32 @@ class ReplyAddressTag(
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "a"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
|
||||
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): ReplyAddressTag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return ReplyAddressTag(tag[1], tag.getOrNull(2))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseAddress(tag: Array<String>) = tag.valueIfMatches(TAG_NAME, TAG_SIZE)
|
||||
fun parseAddressId(tag: Array<String>): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseAsHint(tag: Array<String>): AddressHint? {
|
||||
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length == 64 || !tag[1].contains(':') || tag[2].isEmpty()) 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 AddressHint(tag[1], tag[2])
|
||||
}
|
||||
|
||||
|
||||
@@ -23,10 +23,11 @@ package com.vitorpamplona.quartz.nip22Comments.tags
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.match
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
@Immutable
|
||||
data class ReplyAuthorTag(
|
||||
@@ -37,28 +38,32 @@ data class ReplyAuthorTag(
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "p"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
|
||||
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Tag): ReplyAuthorTag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
if (tag[1].length != 64) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return ReplyAuthorTag(tag[1], tag.getOrNull(2))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseKey(tag: Tag): HexKey? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
if (tag[1].length != 64) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseAsHint(tag: Array<String>): PubKeyHint? {
|
||||
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) 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 PubKeyHint(tag[1], tag[2])
|
||||
}
|
||||
|
||||
|
||||
@@ -23,11 +23,12 @@ package com.vitorpamplona.quartz.nip22Comments.tags
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.match
|
||||
import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.events.EventReference
|
||||
import com.vitorpamplona.quartz.utils.Hex
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
@Immutable
|
||||
class ReplyEventTag(
|
||||
@@ -39,42 +40,53 @@ class ReplyEventTag(
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "e"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
|
||||
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(
|
||||
tag: Array<String>,
|
||||
eventId: String,
|
||||
) = tag.match(TAG_NAME, eventId, TAG_SIZE)
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == eventId
|
||||
|
||||
@JvmStatic
|
||||
fun isIn(
|
||||
tag: Array<String>,
|
||||
eventIds: Set<String>,
|
||||
) = tag.match(TAG_NAME, eventIds, TAG_SIZE)
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in eventIds
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): ReplyEventTag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return ReplyEventTag(tag[1], tag.getOrNull(2), tag.getOrNull(3))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseKey(tag: Array<String>) = tag.valueIfMatches(TAG_NAME, TAG_SIZE)
|
||||
fun parseKey(tag: Array<String>): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseValidKey(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
if (tag[1].length != 64) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
ensure(Hex.isHex(tag[1])) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseAsHint(tag: Array<String>): EventIdHint? {
|
||||
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) 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 EventIdHint(tag[1], tag[2])
|
||||
}
|
||||
|
||||
|
||||
+9
-5
@@ -22,24 +22,28 @@ package com.vitorpamplona.quartz.nip22Comments.tags
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.match
|
||||
import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHash
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.GeohashId
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
@Immutable
|
||||
class ReplyIdentifierTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "i"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
|
||||
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Tag) = tag.valueIfMatches(TAG_NAME, TAG_SIZE)
|
||||
fun parse(tag: Tag): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
|
||||
@@ -21,36 +21,36 @@
|
||||
package com.vitorpamplona.quartz.nip22Comments.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.match
|
||||
import com.vitorpamplona.quartz.nip01Core.core.matchAndHasValue
|
||||
import com.vitorpamplona.quartz.nip01Core.core.valueToIntIfMatches
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class ReplyKindTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "k"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(tag: Tag) = tag.matchAndHasValue(TAG_NAME, TAG_SIZE)
|
||||
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(
|
||||
tag: Tag,
|
||||
kind: String,
|
||||
) = tag.match(TAG_NAME, kind, TAG_SIZE)
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == kind
|
||||
|
||||
@JvmStatic
|
||||
fun isIn(
|
||||
tag: Tag,
|
||||
kinds: Set<String>,
|
||||
) = tag.match(TAG_NAME, kinds, TAG_SIZE)
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in kinds
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Tag) = tag.valueToIntIfMatches(TAG_NAME, TAG_SIZE)
|
||||
fun parse(tag: Tag): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(kind: String) = arrayOf(TAG_NAME, kind)
|
||||
|
||||
@@ -24,8 +24,6 @@ import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.core.match
|
||||
import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
@@ -40,26 +38,27 @@ class RootAddressTag(
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "A"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
|
||||
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(
|
||||
tag: Array<String>,
|
||||
addressId: String,
|
||||
) = tag.match(TAG_NAME, addressId, TAG_SIZE)
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == addressId
|
||||
|
||||
@JvmStatic
|
||||
fun isIn(
|
||||
tag: Array<String>,
|
||||
addressIds: Set<String>,
|
||||
) = tag.match(TAG_NAME, addressIds, TAG_SIZE)
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in addressIds
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): RootAddressTag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return RootAddressTag(tag[1], tag.getOrNull(2))
|
||||
}
|
||||
|
||||
@@ -72,11 +71,19 @@ class RootAddressTag(
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseAddress(tag: Array<String>) = tag.valueIfMatches(TAG_NAME, TAG_SIZE)
|
||||
fun parseAddressId(tag: Array<String>): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseAsHint(tag: Array<String>): AddressHint? {
|
||||
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length == 64 || !tag[1].contains(':') || tag[2].isEmpty()) 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 AddressHint(tag[1], tag[2])
|
||||
}
|
||||
|
||||
|
||||
@@ -24,11 +24,11 @@ import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.PUBKEY_LENGTH
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
@Immutable
|
||||
data class RootAuthorTag(
|
||||
@@ -41,7 +41,7 @@ data class RootAuthorTag(
|
||||
const val TAG_NAME = "P"
|
||||
|
||||
@JvmStatic
|
||||
fun match(tag: Tag) = tag.size > 1 && tag[0] == TAG_NAME
|
||||
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Tag): ReplyAuthorTag? {
|
||||
@@ -65,19 +65,13 @@ data class RootAuthorTag(
|
||||
|
||||
@JvmStatic
|
||||
fun parseAsHint(tag: Array<String>): PubKeyHint? {
|
||||
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != PUBKEY_LENGTH || tag[2].isEmpty()) 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 PubKeyHint(tag[1], tag[2])
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
inline fun ensure(
|
||||
condition: Boolean,
|
||||
exit: () -> Nothing,
|
||||
) {
|
||||
contract { returns() implies condition }
|
||||
if (!condition) exit()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
pubkey: HexKey,
|
||||
|
||||
@@ -23,11 +23,12 @@ package com.vitorpamplona.quartz.nip22Comments.tags
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.match
|
||||
import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.events.EventReference
|
||||
import com.vitorpamplona.quartz.utils.Hex
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
@Immutable
|
||||
class RootEventTag(
|
||||
@@ -39,42 +40,53 @@ class RootEventTag(
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "E"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
|
||||
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(
|
||||
tag: Array<String>,
|
||||
eventId: String,
|
||||
) = tag.match(TAG_NAME, eventId, TAG_SIZE)
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == eventId
|
||||
|
||||
@JvmStatic
|
||||
fun isIn(
|
||||
tag: Array<String>,
|
||||
eventIds: Set<String>,
|
||||
) = tag.match(TAG_NAME, eventIds, TAG_SIZE)
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in eventIds
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): RootEventTag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return RootEventTag(tag[1], tag.getOrNull(2), tag.getOrNull(3))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseKey(tag: Array<String>) = tag.valueIfMatches(TAG_NAME, TAG_SIZE)
|
||||
fun parseKey(tag: Array<String>): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseValidKey(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
if (tag[1].length != 64) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
ensure(Hex.isHex(tag[1])) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseAsHint(tag: Array<String>): EventIdHint? {
|
||||
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) 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 EventIdHint(tag[1], tag[2])
|
||||
}
|
||||
|
||||
|
||||
+9
-5
@@ -22,24 +22,28 @@ package com.vitorpamplona.quartz.nip22Comments.tags
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.match
|
||||
import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHash
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.GeohashId
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
@Immutable
|
||||
class RootIdentifierTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "I"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
|
||||
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Tag) = tag.valueIfMatches(TAG_NAME, TAG_SIZE)
|
||||
fun parse(tag: Tag): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
|
||||
@@ -21,20 +21,24 @@
|
||||
package com.vitorpamplona.quartz.nip22Comments.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.match
|
||||
import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class RootKindTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "K"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
|
||||
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Tag) = tag.valueIfMatches(TAG_NAME, TAG_SIZE)
|
||||
fun parse(tag: Tag): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(kind: String): Tag = arrayOf(TAG_NAME, kind)
|
||||
|
||||
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip23LongContent.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class ImageTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "image"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip23LongContent.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class PublishedAtTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "published_at"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): Long? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1].toLongOrNull()
|
||||
}
|
||||
|
||||
|
||||
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip23LongContent.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class SummaryTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "summary"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip23LongContent.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class TitleTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "title"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
@@ -20,18 +20,22 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip31Alts
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class AltTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "alt"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(altDescriptor: String = "") = arrayOf(TAG_NAME, altDescriptor)
|
||||
fun assemble(altDescriptor: String) = arrayOf(TAG_NAME, altDescriptor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip34Git.repository.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class CloneTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "clone"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip34Git.repository.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class DescriptionTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "description"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip34Git.repository.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class NameTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "name"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip34Git.repository.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class WebTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "web"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip35Torrents.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class BtihTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "btih"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
package com.vitorpamplona.quartz.nip35Torrents.tags
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip46RemoteSigner.getOrNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
@Immutable
|
||||
class FileTag(
|
||||
@@ -32,18 +34,21 @@ class FileTag(
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "file"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): FileTag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return FileTag(tag[1], tag.getOrNull(2)?.toLongOrNull())
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseBytes(tag: Array<String>): Long? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return tag.getOrNull(2)?.toLongOrNull()
|
||||
ensure(tag.has(2)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[2].isNotEmpty()) { return null }
|
||||
return tag[2].toLongOrNull()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
||||
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip35Torrents.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class InfoHashTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "x"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip35Torrents.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class TrackerTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "tracker"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class CurrentParticipantsTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "current_participants"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): Int? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1].toIntOrNull()
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class EndsTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "ends"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): Long? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1].toLongOrNull()
|
||||
}
|
||||
|
||||
|
||||
+19
-15
@@ -23,9 +23,10 @@ package com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.isNotName
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
enum class ROLE(
|
||||
val code: String,
|
||||
@@ -43,40 +44,43 @@ data class ParticipantTag(
|
||||
) : PubKeyReferenceTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "p"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
fun isIn(
|
||||
tag: Array<String>,
|
||||
keys: Set<HexKey>,
|
||||
) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1] in keys
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in keys
|
||||
|
||||
@JvmStatic
|
||||
fun isHost(tag: Tag): Boolean {
|
||||
if (tag.isNotName(TAG_NAME, TAG_SIZE)) return false
|
||||
if (tag[1].length != 64) return false
|
||||
if (tag.getOrNull(3).equals(ROLE.HOST.code)) return true
|
||||
return false
|
||||
ensure(tag.has(3)) { return false }
|
||||
ensure(tag[0] == TAG_NAME) { return false }
|
||||
ensure(tag[1].length == 64) { return false }
|
||||
ensure(tag[3] == ROLE.HOST.code) { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Tag): ParticipantTag? {
|
||||
if (tag.isNotName(TAG_NAME, TAG_SIZE)) return null
|
||||
if (tag[1].length != 64) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return ParticipantTag(tag[1], tag.getOrNull(2), tag.getOrNull(3), tag.getOrNull(4))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseHost(tag: Tag): ParticipantTag? {
|
||||
if (tag.isNotName(TAG_NAME, TAG_SIZE)) return null
|
||||
if (tag[1].length != 64) return null
|
||||
if (!tag.getOrNull(3).equals(ROLE.HOST.code)) return null
|
||||
return ParticipantTag(tag[1], tag.getOrNull(2), tag.getOrNull(3), tag.getOrNull(4))
|
||||
ensure(tag.has(3)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
ensure(tag[3] == ROLE.HOST.code) { return null }
|
||||
return ParticipantTag(tag[1], tag[2], tag[3], tag.getOrNull(4))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseKey(tag: Tag): String? {
|
||||
if (tag.isNotName(TAG_NAME, TAG_SIZE)) return null
|
||||
if (tag[1].length != 64) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -20,16 +20,20 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class RelayListTag(
|
||||
val relayUrls: List<String>,
|
||||
) {
|
||||
companion object {
|
||||
const val TAG_NAME = "relays"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): RelayListTag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
val relays =
|
||||
tag.mapIndexedNotNull { index, s ->
|
||||
if (index == 0) null else s
|
||||
|
||||
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class StartsTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "starts"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): Long? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1].toLongOrNull()
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -20,6 +20,9 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class StatusTag {
|
||||
enum class STATUS(
|
||||
val code: String,
|
||||
@@ -34,11 +37,12 @@ class StatusTag {
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "status"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class StreamingTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "streaming"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -20,14 +20,18 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class TotalParticipantsTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "total_participants"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): Int? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1].toIntOrNull()
|
||||
}
|
||||
|
||||
|
||||
+34
@@ -46,6 +46,7 @@ enum class SignerType {
|
||||
NIP44_DECRYPT,
|
||||
GET_PUBLIC_KEY,
|
||||
DECRYPT_ZAP_EVENT,
|
||||
DERIVE_KEY,
|
||||
}
|
||||
|
||||
class Permission(
|
||||
@@ -219,6 +220,7 @@ class ExternalSignerLauncher(
|
||||
SignerType.NIP44_DECRYPT -> "nip44_decrypt"
|
||||
SignerType.GET_PUBLIC_KEY -> "get_public_key"
|
||||
SignerType.DECRYPT_ZAP_EVENT -> "decrypt_zap_event"
|
||||
SignerType.DERIVE_KEY -> "derive_key"
|
||||
}
|
||||
intent.putExtra("type", signerType)
|
||||
intent.putExtra("pubKey", pubKey)
|
||||
@@ -318,6 +320,15 @@ class ExternalSignerLauncher(
|
||||
return kotlin.Result.success(null)
|
||||
}
|
||||
|
||||
fun hashCodeFields(
|
||||
str1: String,
|
||||
onReady: (String) -> Unit,
|
||||
): Int {
|
||||
var result = str1.hashCode()
|
||||
result = 31 * result + onReady.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
fun hashCodeFields(
|
||||
str1: String,
|
||||
str2: String,
|
||||
@@ -398,4 +409,27 @@ class ExternalSignerLauncher(
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fun deriveKey(
|
||||
nonce: HexKey,
|
||||
signerType: SignerType = SignerType.DERIVE_KEY,
|
||||
onReady: (String) -> Unit,
|
||||
) {
|
||||
getDataFromResolver(signerType, arrayOf(nonce)).fold(
|
||||
onFailure = { },
|
||||
onSuccess = {
|
||||
if (it == null) {
|
||||
openSignerApp(
|
||||
nonce,
|
||||
signerType,
|
||||
"",
|
||||
hashCodeFields(nonce, onReady).toString(),
|
||||
onReady,
|
||||
)
|
||||
} else {
|
||||
onReady(it)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+11
@@ -133,6 +133,17 @@ class NostrSignerExternal(
|
||||
)
|
||||
}
|
||||
|
||||
override fun deriveKey(
|
||||
nonce: HexKey,
|
||||
onReady: (HexKey) -> Unit,
|
||||
) {
|
||||
launcher.deriveKey(
|
||||
nonce,
|
||||
SignerType.DERIVE_KEY,
|
||||
onReady,
|
||||
)
|
||||
}
|
||||
|
||||
override fun decryptZapEvent(
|
||||
event: LnZapRequestEvent,
|
||||
onReady: (LnZapPrivateEvent) -> Unit,
|
||||
|
||||
@@ -25,14 +25,15 @@ import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.nip56Reports.tags.DefaultReportTag
|
||||
import com.vitorpamplona.quartz.nip56Reports.tags.ReportedAddressTag
|
||||
import com.vitorpamplona.quartz.nip56Reports.tags.ReportedAuthorTag
|
||||
import com.vitorpamplona.quartz.nip56Reports.tags.ReportedEventTag
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable data class ReportedKey(
|
||||
val key: String,
|
||||
val reportType: ReportEvent.ReportType,
|
||||
)
|
||||
|
||||
// NIP 56 event.
|
||||
@Immutable
|
||||
class ReportEvent(
|
||||
@@ -43,48 +44,36 @@ class ReportEvent(
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
@Transient
|
||||
private var defaultType: ReportType? = null
|
||||
|
||||
private fun defaultReportTypes() = tags.mapNotNull(DefaultReportTag::parse)
|
||||
|
||||
private fun defaultReportType(): ReportType {
|
||||
// Works with old and new structures for report.
|
||||
var reportType =
|
||||
tags
|
||||
.filter { it.firstOrNull() == "report" }
|
||||
.mapNotNull { it.getOrNull(1) }
|
||||
.map { ReportType.valueOf(it.uppercase()) }
|
||||
.firstOrNull()
|
||||
if (reportType == null) {
|
||||
reportType =
|
||||
tags.mapNotNull { it.getOrNull(2) }.map { ReportType.valueOf(it.uppercase()) }.firstOrNull()
|
||||
}
|
||||
if (reportType == null) {
|
||||
reportType = ReportType.SPAM
|
||||
}
|
||||
defaultType?.let { return it }
|
||||
|
||||
// search for any type in any tag.
|
||||
val reportType =
|
||||
defaultReportTypes().firstOrNull()
|
||||
?: tags.firstNotNullOfOrNull {
|
||||
ReportedAuthorTag.parse(it)?.type
|
||||
?: ReportedEventTag.parse(it)?.type
|
||||
?: ReportedAddressTag.parse(it)?.type
|
||||
} ?: ReportType.SPAM
|
||||
|
||||
defaultType = reportType
|
||||
return reportType
|
||||
}
|
||||
|
||||
fun reportedPost() =
|
||||
tags
|
||||
.filter { it.size > 1 && it[0] == "e" }
|
||||
.map {
|
||||
ReportedKey(
|
||||
it[1],
|
||||
it.getOrNull(2)?.uppercase()?.let { it1 -> ReportType.valueOf(it1) }
|
||||
?: defaultReportType(),
|
||||
)
|
||||
}
|
||||
fun reportedPost() = tags.mapNotNull { ReportedEventTag.parse(it, defaultReportType()) }
|
||||
|
||||
fun reportedAuthor() =
|
||||
tags
|
||||
.filter { it.size > 1 && it[0] == "p" }
|
||||
.map {
|
||||
ReportedKey(
|
||||
it[1],
|
||||
it.getOrNull(2)?.uppercase()?.let { it1 -> ReportType.valueOf(it1) }
|
||||
?: defaultReportType(),
|
||||
)
|
||||
}
|
||||
fun reportedAddresses() = tags.mapNotNull { ReportedAddressTag.parse(it, defaultReportType()) }
|
||||
|
||||
fun reportedAuthor() = tags.mapNotNull { ReportedAuthorTag.parse(it, defaultReportType()) }
|
||||
|
||||
companion object {
|
||||
const val KIND = 1984
|
||||
const val ALT_PREFIX = "Report for "
|
||||
|
||||
fun create(
|
||||
reportedPost: Event,
|
||||
@@ -108,32 +97,27 @@ class ReportEvent(
|
||||
signer.sign(createdAt, KIND, tags, content, onReady)
|
||||
}
|
||||
|
||||
fun create(
|
||||
reportedUser: String,
|
||||
fun build(
|
||||
reportedPost: Event,
|
||||
type: ReportType,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (ReportEvent) -> Unit,
|
||||
) {
|
||||
val content = ""
|
||||
) = eventTemplate(KIND, "", createdAt) {
|
||||
alt(ALT_PREFIX + type.code)
|
||||
event(reportedPost.id, type)
|
||||
user(reportedPost.pubKey, type)
|
||||
|
||||
val reportAuthorTag = arrayOf("p", reportedUser, type.name.lowercase())
|
||||
val alt = AltTag.assemble("Report for ${type.name}")
|
||||
if (reportedPost is AddressableEvent) {
|
||||
address(reportedPost.address(), type)
|
||||
}
|
||||
}
|
||||
|
||||
val tags: Array<Array<String>> = arrayOf(reportAuthorTag, alt)
|
||||
signer.sign(createdAt, KIND, tags, content, onReady)
|
||||
fun build(
|
||||
reportedUser: HexKey,
|
||||
type: ReportType,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
) = eventTemplate(KIND, "", createdAt) {
|
||||
alt(ALT_PREFIX + type.code)
|
||||
user(reportedUser, type)
|
||||
}
|
||||
}
|
||||
|
||||
enum class ReportType {
|
||||
EXPLICIT, // Not used anymore.
|
||||
ILLEGAL,
|
||||
SPAM,
|
||||
IMPERSONATION,
|
||||
NUDITY,
|
||||
PROFANITY,
|
||||
MALWARE,
|
||||
MOD,
|
||||
OTHER,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip56Reports
|
||||
|
||||
import android.util.Log
|
||||
|
||||
enum class ReportType(
|
||||
val code: String,
|
||||
) {
|
||||
EXPLICIT("explicit"), // Not used anymore.
|
||||
ILLEGAL("illegal"),
|
||||
SPAM("spam"),
|
||||
IMPERSONATION("impersonation"),
|
||||
NUDITY("nudity"),
|
||||
PROFANITY("profanity"),
|
||||
MALWARE("malware"),
|
||||
MOD("mod"),
|
||||
HARASSMENT("Harrassment"),
|
||||
OTHER("other"),
|
||||
;
|
||||
|
||||
companion object {
|
||||
fun parseOrNull(
|
||||
code: String,
|
||||
tag: Array<String>,
|
||||
): ReportType? =
|
||||
when (code) {
|
||||
EXPLICIT.code -> EXPLICIT
|
||||
ILLEGAL.code -> ILLEGAL
|
||||
SPAM.code -> SPAM
|
||||
IMPERSONATION.code -> IMPERSONATION
|
||||
NUDITY.code -> NUDITY
|
||||
PROFANITY.code -> PROFANITY
|
||||
MALWARE.code -> MALWARE
|
||||
MOD.code -> MOD
|
||||
"MOD" -> MOD
|
||||
HARASSMENT.code -> HARASSMENT
|
||||
OTHER.code -> OTHER
|
||||
else -> {
|
||||
Log.w("ReportedEventTag", "Report type not supported: $code ${tag.joinToString(", ")}")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip56Reports
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
||||
import com.vitorpamplona.quartz.nip56Reports.tags.HashSha256Tag
|
||||
import com.vitorpamplona.quartz.nip56Reports.tags.ReportedAddressTag
|
||||
import com.vitorpamplona.quartz.nip56Reports.tags.ReportedAuthorTag
|
||||
import com.vitorpamplona.quartz.nip56Reports.tags.ReportedEventTag
|
||||
import com.vitorpamplona.quartz.nip56Reports.tags.ServerTag
|
||||
|
||||
fun TagArrayBuilder<ReportEvent>.event(
|
||||
eventId: HexKey,
|
||||
reportType: ReportType,
|
||||
) = addUnique(ReportedEventTag.assemble(eventId, reportType))
|
||||
|
||||
fun TagArrayBuilder<ReportEvent>.address(
|
||||
address: Address,
|
||||
reportType: ReportType,
|
||||
) = addUnique(ReportedAddressTag.assemble(address, reportType))
|
||||
|
||||
fun TagArrayBuilder<ReportEvent>.user(
|
||||
pubkey: HexKey,
|
||||
reportType: ReportType,
|
||||
) = addUnique(ReportedAuthorTag.assemble(pubkey, reportType))
|
||||
|
||||
fun TagArrayBuilder<ReportEvent>.hash(
|
||||
x: String,
|
||||
reportType: ReportType,
|
||||
) = addUnique(HashSha256Tag.assemble(x, reportType))
|
||||
|
||||
fun TagArrayBuilder<ReportEvent>.server(url: String) = addUnique(ServerTag.assemble(url))
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip56Reports.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip56Reports.ReportType
|
||||
|
||||
interface BaseReportTag {
|
||||
val type: ReportType?
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip56Reports.tags
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip56Reports.ReportType
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
@Immutable
|
||||
@Deprecated("Old version had [`report`, `<reason>`] tags")
|
||||
class DefaultReportTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "report"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): ReportType? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
|
||||
return ReportType.parseOrNull(tag[1], tag)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(type: ReportType? = null) = arrayOfNotNull(TAG_NAME, type?.code)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip56Reports.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip56Reports.ReportType
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class HashSha256Tag(
|
||||
val hash: HexKey,
|
||||
override val type: ReportType? = null,
|
||||
) : BaseReportTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "x"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(
|
||||
tag: Array<String>,
|
||||
defaultReportType: ReportType? = null,
|
||||
): HashSha256Tag? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
|
||||
val type = tag.getOrNull(2)?.let { ReportType.parseOrNull(it, tag) } ?: defaultReportType
|
||||
return HashSha256Tag(tag[1], type)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
hash: String,
|
||||
type: ReportType? = null,
|
||||
) = arrayOfNotNull(TAG_NAME, hash, type?.code)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip56Reports.tags
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
||||
import com.vitorpamplona.quartz.nip56Reports.ReportType
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
@Immutable
|
||||
class ReportedAddressTag(
|
||||
val address: Address,
|
||||
override val type: ReportType? = null,
|
||||
) : BaseReportTag {
|
||||
fun toTagArray() = assemble(address, type)
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "a"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(
|
||||
tag: Array<String>,
|
||||
defaultReportType: ReportType? = null,
|
||||
): ReportedAddressTag? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
|
||||
val address = Address.parse(tag[1])
|
||||
|
||||
ensure(address != null) { return null }
|
||||
|
||||
val type =
|
||||
if (tag.size == 2) {
|
||||
defaultReportType
|
||||
} else if (tag.size == 3) {
|
||||
ReportType.parseOrNull(tag[2], tag) ?: defaultReportType
|
||||
} else {
|
||||
ReportType.parseOrNull(tag[3], tag) ?: defaultReportType
|
||||
}
|
||||
|
||||
return ReportedAddressTag(address, type)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
address: Address,
|
||||
type: ReportType? = null,
|
||||
) = arrayOfNotNull(TAG_NAME, address.toValue(), type?.code)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip56Reports.tags
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip56Reports.ReportType
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
@Immutable
|
||||
class ReportedAuthorTag(
|
||||
val pubkey: HexKey,
|
||||
override val type: ReportType? = null,
|
||||
) : BaseReportTag {
|
||||
fun toTagArray() = assemble(pubkey, type)
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "p"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(
|
||||
tag: Array<String>,
|
||||
defaultReportType: ReportType? = null,
|
||||
): ReportedAuthorTag? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
|
||||
val type =
|
||||
if (tag.size == 2) {
|
||||
defaultReportType
|
||||
} else if (tag.size == 3) {
|
||||
ReportType.parseOrNull(tag[2], tag) ?: defaultReportType
|
||||
} else {
|
||||
ReportType.parseOrNull(tag[3], tag) ?: defaultReportType
|
||||
}
|
||||
|
||||
return ReportedAuthorTag(tag[1], type)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
pubkey: HexKey,
|
||||
type: ReportType? = null,
|
||||
) = arrayOfNotNull(TAG_NAME, pubkey, type?.code)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip56Reports.tags
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip56Reports.ReportType
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
@Immutable
|
||||
class ReportedEventTag(
|
||||
val eventId: HexKey,
|
||||
override val type: ReportType? = null,
|
||||
) : BaseReportTag {
|
||||
fun toTagArray() = assemble(eventId, type)
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "e"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(
|
||||
tag: Array<String>,
|
||||
defaultReportType: ReportType? = null,
|
||||
): ReportedEventTag? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
|
||||
val type =
|
||||
if (tag.size == 2) {
|
||||
defaultReportType
|
||||
} else if (tag.size == 3) {
|
||||
ReportType.parseOrNull(tag[2], tag) ?: defaultReportType
|
||||
} else {
|
||||
ReportType.parseOrNull(tag[3], tag) ?: defaultReportType
|
||||
}
|
||||
|
||||
return ReportedEventTag(tag[1], type)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
eventId: HexKey,
|
||||
type: ReportType? = null,
|
||||
) = arrayOfNotNull(TAG_NAME, eventId, type?.code)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip56Reports.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class ServerTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "server"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(url: String) = arrayOf(TAG_NAME, url)
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(urls: List<String>) = urls.map { assemble(it) }
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ class ZapRaiserTag(
|
||||
fun toTagArray() = assemble(amountInSats)
|
||||
|
||||
companion object {
|
||||
val TAG_NAME = "zapraiser"
|
||||
const val TAG_NAME = "zapraiser"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tags: Array<String>): ZapRaiserTag? {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user