- Normalizes the use of ETag

- Creates a BaseReplaceable event to avoid duplicates of empty d tags.
This commit is contained in:
Vitor Pamplona
2025-01-15 11:48:01 -05:00
parent 5a3c821da4
commit c25d64558e
46 changed files with 393 additions and 145 deletions
@@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.blossom
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
@@ -36,9 +36,7 @@ class BlossomServersEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun servers(): List<String> =
tags.mapNotNull {
if (it.size > 1 && it[0] == "server") {
@@ -50,12 +48,11 @@ class BlossomServersEvent(
companion object {
const val KIND = 10063
const val FIXED_D_TAG = ""
const val ALT = "File servers used by the author"
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATag(KIND, pubKey, FIXED_D_TAG)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
fun createTagArray(servers: List<String>): Array<Array<String>> =
servers
@@ -24,7 +24,7 @@ import android.util.Log
import androidx.compose.runtime.Immutable
import com.fasterxml.jackson.module.kotlin.readValue
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
import com.vitorpamplona.quartz.nip01Core.core.TagArray
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
@@ -43,15 +43,13 @@ class PrivateOutboxRelayListEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
@Transient private var privateTagsCache: Array<Array<String>>? = null
override fun countMemory(): Long =
super.countMemory() +
pointerSizeInBytes + (privateTagsCache?.sumOf { pointerSizeInBytes + it.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } } ?: 0)
override fun dTag() = FIXED_D_TAG
fun relays(): List<String>? =
tags
.mapNotNull {
@@ -102,12 +100,11 @@ class PrivateOutboxRelayListEvent(
companion object {
const val KIND = 10013
const val FIXED_D_TAG = ""
val TAGS = arrayOf(AltTagSerializer.toTagArray("Relay list to store private content from this author"))
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATag(KIND, pubKey, FIXED_D_TAG)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
fun encryptTags(
privateTags: Array<Array<String>>? = null,
@@ -29,7 +29,6 @@ import com.vitorpamplona.quartz.nip01Core.tags.hashtags.buildHashtagTags
import com.vitorpamplona.quartz.nip10Notes.content.buildUrlRefs
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
import com.vitorpamplona.quartz.nip10Notes.content.findURLs
import com.vitorpamplona.quartz.nip19Bech32.parse
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrl
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
import com.vitorpamplona.quartz.nip36SensitiveContent.ContentWarningSerializer
@@ -49,7 +49,7 @@ class InteractiveStoryPrologueEvent(
fun createAddressTag(
pubKey: HexKey,
dtag: String,
): String = ATag.assembleATag(KIND, pubKey, dtag)
): String = ATag.assembleATagId(KIND, pubKey, dtag)
fun create(
baseId: String,
@@ -27,7 +27,6 @@ import com.vitorpamplona.quartz.nip01Core.core.firstTag
import com.vitorpamplona.quartz.nip01Core.core.firstTagValue
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip19Bech32.parse
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.removeTrailingNullsAndEmptyOthers
@@ -66,7 +65,7 @@ class InteractiveStoryReadingStateEvent(
fun createAddressTag(
pubKey: HexKey,
dtag: String,
): String = ATag.assembleATag(KIND, pubKey, dtag)
): String = ATag.assembleATagId(KIND, pubKey, dtag)
fun update(
base: InteractiveStoryReadingStateEvent,
@@ -49,7 +49,7 @@ class InteractiveStorySceneEvent(
fun createAddressTag(
pubKey: HexKey,
dtag: String,
): String = ATag.assembleATag(KIND, pubKey, dtag)
): String = ATag.assembleATagId(KIND, pubKey, dtag)
fun create(
baseId: String,
@@ -23,7 +23,7 @@ package com.vitorpamplona.quartz.nip01Core.core
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.dTag
import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag
@Immutable
open class BaseAddressableEvent(
@@ -43,5 +43,5 @@ open class BaseAddressableEvent(
/**
* Creates the tag in a memory efficient way (without creating the ATag class
*/
override fun addressTag() = ATag.assembleATag(kind, pubKey, dTag())
override fun addressTag() = ATag.assembleATagId(kind, pubKey, dTag())
}
@@ -0,0 +1,49 @@
/**
* 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.nip01Core.core
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
@Immutable
open class BaseReplaceableEvent(
id: HexKey,
pubKey: HexKey,
createdAt: Long,
kind: Int,
tags: TagArray,
content: String,
sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
override fun address(relayHint: String?) = ATag(kind, pubKey, FIXED_D_TAG, relayHint)
/**
* Creates the tag in a memory efficient way (without creating the ATag class
*/
override fun addressTag() = ATag.assembleATagId(kind, pubKey, FIXED_D_TAG)
companion object {
const val FIXED_D_TAG = ""
}
}
@@ -20,7 +20,11 @@
*/
package com.vitorpamplona.quartz.nip01Core.tags.addressables
import android.util.Log
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.utils.Hex
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
import com.vitorpamplona.quartz.utils.removeTrailingNullsAndEmptyOthers
@@ -35,7 +39,7 @@ data class ATag(
constructor(
kind: Int,
pubKeyHex: String,
pubKeyHex: HexKey,
dTag: String,
relayHint: String?,
) : this(kind, pubKeyHex, dTag) {
@@ -49,17 +53,56 @@ data class ATag(
dTag.bytesUsedInMemory() +
(relay?.bytesUsedInMemory() ?: 0)
fun toTag() = assembleATag(kind, pubKeyHex, dTag)
fun toTag() = assembleATagId(kind, pubKeyHex, dTag)
fun toATagArray() = removeTrailingNullsAndEmptyOthers("a", toTag(), relay)
fun toATagArray() = removeTrailingNullsAndEmptyOthers(TAG_NAME, toTag(), relay)
fun toQTagArray() = removeTrailingNullsAndEmptyOthers("q", toTag(), relay)
companion object {
fun assembleATag(
const val TAG_NAME = "a"
fun assembleATagId(
kind: Int,
pubKeyHex: HexKey,
dTag: String,
) = "$kind:$pubKeyHex:$dTag"
fun parse(
aTagId: String,
relay: String?,
): ATag? =
try {
val parts = aTagId.split(":", limit = 3)
if (Hex.isHex(parts[1])) {
ATag(parts[0].toInt(), parts[1], parts[2], relay)
} else {
Log.w("ATag", "Error parsing A Tag. Pubkey is not hex: $aTagId")
null
}
} catch (t: Throwable) {
Log.w("ATag", "Error parsing A Tag: $aTagId: ${t.message}")
null
}
@JvmStatic
fun parse(tags: Array<String>): ATag? {
require(tags[0] == TAG_NAME)
return parse(tags[1], tags.getOrNull(2))
}
@JvmStatic
fun assemble(
aTagId: HexKey,
relay: String?,
) = arrayOfNotNull(TAG_NAME, aTagId, relay)
@JvmStatic
fun assemble(
kind: Int,
pubKeyHex: String,
dTag: String,
) = "$kind:$pubKeyHex:$dTag"
relay: String?,
) = arrayOfNotNull(TAG_NAME, assembleATagId(kind, pubKeyHex, dTag), relay)
}
}
@@ -28,35 +28,33 @@ import com.vitorpamplona.quartz.nip01Core.core.mapTagged
import com.vitorpamplona.quartz.nip01Core.core.mapValueTagged
import com.vitorpamplona.quartz.nip19Bech32.parse
fun TagArray.dTag() = this.firstOrNull { it.size > 1 && it[0] == "d" }?.get(1) ?: ""
fun <R> TagArray.mapTaggedAddress(map: (address: String) -> R) = this.mapValueTagged("a", map)
fun <R> TagArray.mapTaggedAddress(map: (address: String) -> R) = this.mapValueTagged(ATag.TAG_NAME, map)
fun TagArray.firstIsTaggedAddressableNote(addressableNotes: Set<String>) =
this
.firstOrNull { it.size > 1 && it[0] == "a" && it[1] in addressableNotes }
.firstOrNull { it.size > 1 && it[0] == ATag.TAG_NAME && it[1] in addressableNotes }
?.getOrNull(1)
fun TagArray.isTaggedAddressableNote(idHex: String) = this.isTagged("a", idHex)
fun TagArray.isTaggedAddressableNote(idHex: String) = this.isTagged(ATag.TAG_NAME, idHex)
fun TagArray.isTaggedAddressableNotes(idHexes: Set<String>) = this.isAnyTagged("a", idHexes)
fun TagArray.isTaggedAddressableNotes(idHexes: Set<String>) = this.isAnyTagged(ATag.TAG_NAME, idHexes)
fun TagArray.isTaggedAddressableKind(kind: Int): Boolean {
val kindStr = kind.toString()
return this.any { it.size > 1 && it[0] == "a" && it[1].startsWith(kindStr) }
return this.any { it.size > 1 && it[0] == ATag.TAG_NAME && it[1].startsWith(kindStr) }
}
fun TagArray.getTagOfAddressableKind(kind: Int): ATag? {
val kindStr = kind.toString()
val aTag =
this
.firstOrNull { it.size > 1 && it[0] == "a" && it[1].startsWith(kindStr) }
.firstOrNull { it.size > 1 && it[0] == ATag.TAG_NAME && it[1].startsWith(kindStr) }
?.getOrNull(1)
?: return null
return ATag.parse(aTag, null)
}
fun TagArray.taggedAddresses() = this.mapTagged("a") { ATag.parse(it[1], it.getOrNull(2)) }
fun TagArray.taggedAddresses() = this.mapTagged(ATag.TAG_NAME) { ATag.parse(it) }
fun TagArray.firstTaggedAddress() = this.firstMapTagged("a") { ATag.parse(it[1], it.getOrNull(2)) }
fun TagArray.firstTaggedAddress() = this.firstMapTagged(ATag.TAG_NAME) { ATag.parse(it) }
@@ -0,0 +1,45 @@
/**
* 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.nip01Core.tags.dTags
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
class DTag(
val dId: String,
) {
fun countMemory(): Long = 1 * pointerSizeInBytes + dId.bytesUsedInMemory()
fun toTagArray() = assemble(dId)
companion object {
const val TAG_NAME = "d"
@JvmStatic
fun parse(tags: Array<String>): DTag {
require(tags[0] == TAG_NAME)
return DTag(tags[1])
}
@JvmStatic
fun assemble(dId: String) = arrayOf(TAG_NAME, dId)
}
}
@@ -0,0 +1,25 @@
/**
* 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.nip01Core.tags.dTags
import com.vitorpamplona.quartz.nip01Core.core.Event
fun Event.dTag() = tags.dTag()
@@ -0,0 +1,25 @@
/**
* 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.nip01Core.tags.dTags
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
fun TagArrayBuilder.dTag(name: String) = add(DTag.assemble(name))
@@ -0,0 +1,26 @@
/**
* 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.nip01Core.tags.dTags
import com.vitorpamplona.quartz.nip01Core.core.TagArray
import com.vitorpamplona.quartz.nip01Core.core.firstTagValue
fun TagArray.dTag() = this.firstTagValue(DTag.TAG_NAME) ?: ""
@@ -18,14 +18,14 @@
* 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.nip10Notes
package com.vitorpamplona.quartz.nip01Core.tags.events
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
import com.vitorpamplona.quartz.utils.removeTrailingNullsAndEmptyOthers
@Immutable
data class ETag(
@@ -40,13 +40,31 @@ data class ETag(
}
fun countMemory(): Long =
2 * pointerSizeInBytes + // 2 fields, 4 bytes each reference (32bit)
3 * pointerSizeInBytes + // 3 fields, 4 bytes each reference (32bit)
eventId.bytesUsedInMemory() +
(relay?.bytesUsedInMemory() ?: 0)
(relay?.bytesUsedInMemory() ?: 0) +
(authorPubKeyHex?.bytesUsedInMemory() ?: 0)
fun toNEvent(): String = NEvent.create(eventId, authorPubKeyHex, null, relay)
fun toETagArray() = removeTrailingNullsAndEmptyOthers("e", eventId, relay, authorPubKeyHex)
fun toETagArray() = arrayOfNotNull(TAG_NAME, eventId, relay, authorPubKeyHex)
fun toQTagArray() = removeTrailingNullsAndEmptyOthers("q", eventId, relay, authorPubKeyHex)
fun toQTagArray() = arrayOfNotNull("q", eventId, relay, authorPubKeyHex)
companion object {
const val TAG_NAME = "e"
@JvmStatic
fun parse(tags: Array<String>): ETag {
require(tags[0] == TAG_NAME)
return ETag(tags[1], tags.getOrNull(2), tags.getOrNull(3))
}
@JvmStatic
fun assemble(
eventId: HexKey,
relay: String?,
author: HexKey?,
) = arrayOfNotNull(TAG_NAME, eventId, relay, author)
}
}
@@ -23,12 +23,14 @@ package com.vitorpamplona.quartz.nip01Core.tags.events
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Event
fun Event.forEachTaggedEvent(onEach: (eventId: HexKey) -> Unit) = tags.forEachTaggedEvent(onEach)
fun Event.forEachTaggedEventId(onEach: (eventId: HexKey) -> Unit) = tags.forEachTaggedEventId(onEach)
fun <R> Event.mapTaggedEvent(map: (eventId: HexKey) -> R) = tags.mapTaggedEvent(map)
fun <R> Event.mapTaggedEventId(map: (eventId: HexKey) -> R) = tags.mapTaggedEventId(map)
fun Event.taggedEvents() = tags.taggedEvents()
fun Event.taggedEventIds() = tags.taggedEventIds()
fun Event.firstTaggedEvent() = tags.firstTaggedEvent()
fun Event.isTaggedEvent(idHex: String) = tags.isTaggedEvent(idHex)
@@ -22,18 +22,22 @@ package com.vitorpamplona.quartz.nip01Core.tags.events
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArray
import com.vitorpamplona.quartz.nip01Core.core.firstTagValue
import com.vitorpamplona.quartz.nip01Core.core.firstMapTagged
import com.vitorpamplona.quartz.nip01Core.core.forEachTagged
import com.vitorpamplona.quartz.nip01Core.core.isTagged
import com.vitorpamplona.quartz.nip01Core.core.mapTagged
import com.vitorpamplona.quartz.nip01Core.core.mapValueTagged
import com.vitorpamplona.quartz.nip01Core.core.mapValues
import com.vitorpamplona.quartz.nip19Bech32.parse
fun TagArray.forEachTaggedEvent(onEach: (eventId: HexKey) -> Unit) = this.forEachTagged("e", onEach)
fun TagArray.forEachTaggedEventId(onEach: (eventId: HexKey) -> Unit) = this.forEachTagged(ETag.TAG_NAME, onEach)
fun <R> TagArray.mapTaggedEvent(map: (eventId: HexKey) -> R) = this.mapValueTagged("e", map)
fun <R> TagArray.mapTaggedEventId(map: (eventId: HexKey) -> R) = this.mapValueTagged(ETag.TAG_NAME, map)
fun TagArray.taggedEvents() = this.mapValues("e")
fun TagArray.taggedEvents() = this.mapTagged(ETag.TAG_NAME) { ETag.parse(it) }
fun TagArray.firstTaggedEvent() = this.firstTagValue("e")
fun TagArray.taggedEventIds() = this.mapValues(ETag.TAG_NAME)
fun TagArray.isTaggedEvent(idHex: String) = this.isTagged("e", idHex)
fun TagArray.firstTaggedEvent() = this.firstMapTagged(ETag.TAG_NAME) { ETag.parse(it) }
fun TagArray.isTaggedEvent(idHex: String) = this.isTagged(ETag.TAG_NAME, idHex)
@@ -26,6 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedAddresses
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEventIds
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -41,6 +42,8 @@ class DeletionEvent(
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
fun deleteEvents() = taggedEvents()
fun deleteEventIds() = taggedEventIds()
fun deleteAddresses() = taggedAddresses()
fun deleteAddressTags() = tags.mapNotNull { if (it.size > 1 && it[0] == "a") it[1] else null }
@@ -33,7 +33,7 @@ class PoWTag(
fun toTagArray() = assemble(nonce, commitment)
companion object {
val TAG_NAME = "nonce"
const val TAG_NAME = "nonce"
@JvmStatic
fun parse(tags: Array<String>): PoWTag {
@@ -20,6 +20,7 @@
*/
package com.vitorpamplona.quartz.nip14Subject
import com.vitorpamplona.quartz.nip01Core.tags.dTags.DTag
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@@ -31,12 +32,12 @@ class SubjectTag(
fun toTagArray() = assemble(subject)
companion object {
val TAG_NAME = "subject"
const val TAG_NAME = "subject"
@JvmStatic
fun parse(tags: Array<String>): SubjectTag {
fun parse(tags: Array<String>): DTag {
require(tags[0] == TAG_NAME)
return SubjectTag(tags[1])
return DTag(tags[1])
}
@JvmStatic
@@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.nip17Dm
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
@@ -37,9 +37,7 @@ class ChatMessageRelayListEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun relays(): List<String> =
tags.mapNotNull {
if (it.size > 1 && it[0] == "relay") {
@@ -51,11 +49,10 @@ class ChatMessageRelayListEvent(
companion object {
const val KIND = 10050
const val FIXED_D_TAG = ""
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATag(KIND, pubKey, FIXED_D_TAG)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
fun createTagArray(relays: List<String>): Array<Array<String>> =
relays
@@ -40,7 +40,7 @@ data class NAddress(
val dTag: String,
val relay: List<String>,
) : Entity {
fun aTag(): String = ATag.assembleATag(kind, author, dTag)
fun aTag(): String = ATag.assembleATagId(kind, author, dTag)
companion object {
fun parse(naddr: String): NAddress? {
@@ -27,10 +27,10 @@ import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashMipMap
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.buildHashtagTags
import com.vitorpamplona.quartz.nip10Notes.BaseTextNoteEvent
import com.vitorpamplona.quartz.nip10Notes.ETag
import com.vitorpamplona.quartz.nip10Notes.PTag
import com.vitorpamplona.quartz.nip10Notes.content.buildUrlRefs
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
@@ -25,7 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.dTag
import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
import com.vitorpamplona.quartz.nip10Notes.BaseTextNoteEvent
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
@@ -45,7 +45,7 @@ class LongTextNoteEvent(
override fun address(relayHint: String?) = ATag(kind, pubKey, dTag(), relayHint)
override fun addressTag() = ATag.assembleATag(kind, pubKey, dTag())
override fun addressTag() = ATag.assembleATagId(kind, pubKey, dTag())
fun topics() = hashtags()
@@ -45,8 +45,6 @@ class ChannelListEvent(
super.countMemory() +
32 + (publicAndPrivateEventCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0L) // rough calculation
override fun dTag() = FIXED_D_TAG
fun publicAndPrivateEvents(
signer: NostrSigner,
onReady: (ImmutableSet<HexKey>) -> Unit,
@@ -67,7 +65,6 @@ class ChannelListEvent(
companion object {
const val KIND = 10005
const val FIXED_D_TAG = ""
const val ALT = "Public Chat List"
fun blockListFor(pubKeyHex: HexKey): String = "$KIND:$pubKeyHex:"
@@ -22,11 +22,10 @@ package com.vitorpamplona.quartz.nip30CustomEmoji
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
import com.vitorpamplona.quartz.utils.TimeUtils
@Immutable
@@ -37,28 +36,14 @@ class EmojiPackSelectionEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
companion object {
const val KIND = 10030
const val FIXED_D_TAG = ""
const val ALT = "Emoji selection"
fun createAddressATag(pubKey: HexKey): ATag =
ATag(
KIND,
pubKey,
AdvertisedRelayListEvent.FIXED_D_TAG,
null,
)
fun createAddressATag(pubKey: HexKey) = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String =
ATag.assembleATag(
KIND,
pubKey,
AdvertisedRelayListEvent.FIXED_D_TAG,
)
fun createAddressTag(pubKey: HexKey) = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
fun create(
listOfEmojiPacks: List<ATag>?,
@@ -125,7 +125,7 @@ class DraftEvent(
fun createAddressTag(
pubKey: HexKey,
dTag: String,
): String = ATag.assembleATag(KIND, pubKey, dTag)
): String = ATag.assembleATagId(KIND, pubKey, dTag)
fun create(
dTag: String,
@@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.nip50Search
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
@@ -37,9 +37,7 @@ class SearchRelayListEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun relays(): List<String> =
tags.mapNotNull {
if (it.size > 1 && it[0] == "relay") {
@@ -51,11 +49,10 @@ class SearchRelayListEvent(
companion object {
const val KIND = 10007
const val FIXED_D_TAG = ""
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATag(KIND, pubKey, FIXED_D_TAG)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
fun createTagArray(relays: List<String>): Array<Array<String>> =
relays
@@ -29,7 +29,6 @@ import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashes
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers
import com.vitorpamplona.quartz.nip19Bech32.parse
import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.toImmutableSet
@@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.nip51Lists
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent.Companion.FIXED_D_TAG
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -76,7 +77,6 @@ class MuteListEvent(
companion object {
const val KIND = 10000
const val FIXED_D_TAG = ""
const val ALT = "Mute List"
fun blockListFor(pubKeyHex: HexKey): String = "10000:$pubKeyHex:"
@@ -44,7 +44,7 @@ class WikiNoteEvent(
override fun address(relayHint: String?) = ATag(kind, pubKey, dTag(), relayHint)
override fun addressTag() = ATag.assembleATag(kind, pubKey, dTag())
override fun addressTag() = ATag.assembleATagId(kind, pubKey, dTag())
fun topics() = hashtags()
@@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.nip65RelayList
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
@@ -37,9 +37,7 @@ class AdvertisedRelayListEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun relays(): List<AdvertisedRelayInfo> =
tags.mapNotNull {
if (it.size > 1 && it[0] == "r") {
@@ -85,12 +83,11 @@ class AdvertisedRelayListEvent(
companion object {
const val KIND = 10002
const val FIXED_D_TAG = ""
const val ALT = "Relay list to discover the user's content"
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATag(KIND, pubKey, FIXED_D_TAG)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
fun updateRelayList(
earlierVersion: AdvertisedRelayListEvent,
@@ -25,9 +25,9 @@ import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashMipMap
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.buildHashtagTags
import com.vitorpamplona.quartz.nip10Notes.ETag
import com.vitorpamplona.quartz.nip10Notes.PTag
import com.vitorpamplona.quartz.nip10Notes.content.buildUrlRefs
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
@@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.nip72ModCommunities
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent.Companion.FIXED_D_TAG
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip19Bech32.parseAtag
@@ -72,7 +73,6 @@ class CommunityListEvent(
companion object {
const val KIND = 10004
const val FIXED_D_TAG = ""
const val ALT = "Community List"
fun blockListFor(pubKeyHex: HexKey): String = "$KIND:$pubKeyHex:"
@@ -25,7 +25,6 @@ import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip19Bech32.parse
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.nip96FileStorage
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
@@ -36,9 +36,7 @@ class FileServersEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun servers(): List<String> =
tags.mapNotNull {
if (it.size > 1 && it[0] == "server") {
@@ -50,12 +48,11 @@ class FileServersEvent(
companion object {
const val KIND = 10096
const val FIXED_D_TAG = ""
const val ALT = "File servers used by the author"
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATag(KIND, pubKey, FIXED_D_TAG)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
fun createTagArray(servers: List<String>): Array<Array<String>> =
servers