Assigning hints to NIPs until NIP-53

Speeding up Bech32 and Hex processing
Updating Boosts and Reposts to the new way of declaring tags
Renaming the SHA256 hash tag from NIP94 to not confuse with # hashtags
This commit is contained in:
Vitor Pamplona
2025-02-24 14:57:15 -05:00
parent 3790325a0f
commit a2d9f31a8a
79 changed files with 916 additions and 254 deletions
@@ -34,7 +34,7 @@ import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
@@ -62,7 +62,7 @@ class FileStorageHeaderEvent(
fun mimeType() = tags.firstNotNullOfOrNull(MimeTypeTag::parse)
fun hash() = tags.firstNotNullOfOrNull(HashTag::parse)
fun hash() = tags.firstNotNullOfOrNull(HashSha256Tag::parse)
fun size() = tags.firstNotNullOfOrNull(SizeTag::parse)
@@ -25,7 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
@@ -37,7 +37,7 @@ import com.vitorpamplona.quartz.nip94FileMetadata.tags.TorrentInfoHash
fun TagArrayBuilder<FileStorageHeaderEvent>.mimeType(mimeType: String) = add(MimeTypeTag.assemble(mimeType))
fun TagArrayBuilder<FileStorageHeaderEvent>.hash(hash: HexKey) = add(HashTag.assemble(hash))
fun TagArrayBuilder<FileStorageHeaderEvent>.hash(hash: HexKey) = add(HashSha256Tag.assemble(hash))
fun TagArrayBuilder<FileStorageHeaderEvent>.fileSize(size: Int) = add(SizeTag.assemble(size))
@@ -31,7 +31,7 @@ import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
@@ -58,7 +58,7 @@ class ProfileGalleryEntryEvent(
fun mimeType() = tags.firstNotNullOfOrNull(MimeTypeTag::parse)
fun hash() = tags.firstNotNullOfOrNull(HashTag::parse)
fun hash() = tags.firstNotNullOfOrNull(HashSha256Tag::parse)
fun size() = tags.firstNotNullOfOrNull(SizeTag::parse)
@@ -26,7 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
@@ -42,7 +42,7 @@ fun TagArrayBuilder<ProfileGalleryEntryEvent>.url(url: String) = add(UrlTag.asse
fun TagArrayBuilder<ProfileGalleryEntryEvent>.mimeType(mimeType: String) = add(MimeTypeTag.assemble(mimeType))
fun TagArrayBuilder<ProfileGalleryEntryEvent>.hash(hash: HexKey) = add(HashTag.assemble(hash))
fun TagArrayBuilder<ProfileGalleryEntryEvent>.hash(hash: HexKey) = add(HashSha256Tag.assemble(hash))
fun TagArrayBuilder<ProfileGalleryEntryEvent>.fileSize(size: Int) = add(SizeTag.assemble(size))
@@ -30,8 +30,8 @@ import com.vitorpamplona.quartz.nip01Core.core.tagArray
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag
import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nip51Lists.Nip51PrivateTags
import com.vitorpamplona.quartz.nip51Lists.PrivateTagArrayEvent
import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent
import com.vitorpamplona.quartz.utils.TimeUtils
@Immutable
@@ -77,7 +77,7 @@ class RelationshipStatusEvent(
privateInitializer()
}
Nip51PrivateTags.encryptNip44(privateTags, signer) { content ->
PrivateTagsInContent.encryptNip44(privateTags, signer) { content ->
signer.sign(createdAt, KIND, publicTags, content, onReady)
}
}
@@ -28,3 +28,7 @@ typealias HexKey = String
fun ByteArray.toHexKey(): HexKey = Hex.encode(this)
fun HexKey.hexToByteArray(): ByteArray = Hex.decode(this)
const val PUBKEY_LENGTH = 64
const val EVENT_ID_LENGTH = 64
@@ -22,6 +22,16 @@ package com.vitorpamplona.quartz.nip01Core.core
typealias Tag = Array<String>
/**
* Returns if the Tag has at least $index elements.
*/
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]
@@ -24,10 +24,14 @@ import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint
interface HintProvider {
fun eventHints(): EventIdHint
fun addressIdHints(): AddressHint
fun pubKeyHints(): PubKeyHint
interface EventHintProvider {
fun eventHints(): List<EventIdHint>
}
interface AddressHintProvider {
fun addressHints(): List<AddressHint>
}
interface PubKeyHintProvider {
fun pubKeyHints(): List<PubKeyHint>
}
@@ -25,6 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey
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
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@@ -128,6 +129,12 @@ data class ATag(
return tag[1]
}
@JvmStatic
fun parseAsHint(tag: Array<String>): AddressHint? {
if (tag.size < 3 || tag[0] != TAG_NAME || !tag[1].contains(':') || tag[2].isEmpty()) return null
return AddressHint(tag[1], tag[2])
}
@JvmStatic
fun assemble(
aTagId: HexKey,
@@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.nip01Core.tags.events
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
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
@@ -58,7 +59,7 @@ data class ETag(
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.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1].length == 64
@JvmStatic
fun isTagged(
@@ -68,16 +69,22 @@ data class ETag(
@JvmStatic
fun parse(tag: Array<String>): ETag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || 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) return null
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || 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
return EventIdHint(tag[1], tag[2])
}
@JvmStatic
fun assemble(
eventId: HexKey,
@@ -20,7 +20,7 @@
*/
package com.vitorpamplona.quartz.nip01Core.tags.hashtags
class HashTag {
class HashtagTag {
companion object {
const val TAG_NAME = "t"
const val TAG_SIZE = 2
@@ -30,7 +30,7 @@ class HashTag {
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].isEmpty()) return null
return tag[1]
}
@@ -23,6 +23,6 @@ package com.vitorpamplona.quartz.nip01Core.tags.hashtags
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
fun <T : Event> TagArrayBuilder<T>.hashtag(tag: String) = addAll(HashTag.assembleDualCase(tag))
fun <T : Event> TagArrayBuilder<T>.hashtag(tag: String) = addAll(HashtagTag.assembleDualCase(tag))
fun <T : Event> TagArrayBuilder<T>.hashtags(tag: List<String>) = addAll(HashTag.assemble(tag))
fun <T : Event> TagArrayBuilder<T>.hashtags(tag: List<String>) = addAll(HashtagTag.assemble(tag))
@@ -30,18 +30,18 @@ import com.vitorpamplona.quartz.nip01Core.core.isAnyLowercaseTagged
import com.vitorpamplona.quartz.nip01Core.core.isTagged
import com.vitorpamplona.quartz.nip01Core.core.mapValues
fun TagArray.forEachHashTag(onEach: (eventId: HexKey) -> Unit) = this.forEachTagged(HashTag.TAG_NAME, onEach)
fun TagArray.forEachHashTag(onEach: (eventId: HexKey) -> Unit) = this.forEachTagged(HashtagTag.TAG_NAME, onEach)
fun TagArray.anyHashTag(onEach: (str: String) -> Boolean) = this.anyTagged(HashTag.TAG_NAME, onEach)
fun TagArray.anyHashTag(onEach: (str: String) -> Boolean) = this.anyTagged(HashtagTag.TAG_NAME, onEach)
fun TagArray.hasHashtags() = this.hasTagWithContent(HashTag.TAG_NAME)
fun TagArray.hasHashtags() = this.hasTagWithContent(HashtagTag.TAG_NAME)
fun TagArray.hashtags() = this.mapValues(HashTag.TAG_NAME)
fun TagArray.hashtags() = this.mapValues(HashtagTag.TAG_NAME)
fun TagArray.countHashtags() = this.count(HashTag::isTagged)
fun TagArray.countHashtags() = this.count(HashtagTag::isTagged)
fun TagArray.isTaggedHash(hashtag: String) = this.isTagged(HashTag.TAG_NAME, hashtag, true)
fun TagArray.isTaggedHash(hashtag: String) = this.isTagged(HashtagTag.TAG_NAME, hashtag, true)
fun TagArray.isTaggedHashes(hashtags: Set<String>) = this.isAnyLowercaseTagged(HashTag.TAG_NAME, hashtags)
fun TagArray.isTaggedHashes(hashtags: Set<String>) = this.isAnyLowercaseTagged(HashtagTag.TAG_NAME, hashtags)
fun TagArray.firstIsTaggedHashes(hashtags: Set<String>) = this.firstAnyLowercaseTaggedValue(HashTag.TAG_NAME, hashtags)
fun TagArray.firstIsTaggedHashes(hashtags: Set<String>) = this.firstAnyLowercaseTaggedValue(HashtagTag.TAG_NAME, hashtags)
@@ -25,7 +25,7 @@ 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.hexToByteArray
import com.vitorpamplona.quartz.nip01Core.core.isNotName
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
@@ -59,14 +59,14 @@ data class PTag(
@JvmStatic
fun parse(tag: Tag): PTag? {
if (tag.isNotName(TAG_NAME, TAG_SIZE)) return null
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
if (tag[1].length != 64) return null
return PTag(tag[1], tag.getOrNull(2))
}
@JvmStatic
fun parseKey(tag: Array<String>): HexKey? {
if (tag.isNotName(TAG_NAME, TAG_SIZE)) return null
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
if (tag[1].length != 64) {
Log.w("PTag", "Invalid `$TAG_NAME` value ${tag.joinToString(", ")}")
return null
@@ -74,6 +74,12 @@ data class PTag(
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
return PubKeyHint(tag[1], tag[2])
}
@JvmStatic
fun assemble(
pubkey: HexKey,
@@ -23,6 +23,8 @@ package com.vitorpamplona.quartz.nip02FollowList
import androidx.compose.runtime.Stable
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
@@ -32,8 +34,9 @@ import com.vitorpamplona.quartz.nip01Core.tags.geohash.isTaggedGeoHash
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.countHashtags
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.isTaggedHash
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser
import com.vitorpamplona.quartz.nip02FollowList.tags.AddressFollowTag
import com.vitorpamplona.quartz.nip02FollowList.tags.ContactTag
import com.vitorpamplona.quartz.nip31Alts.AltTag
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -45,7 +48,13 @@ class ContactListEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
) : Event(id, pubKey, createdAt, KIND, tags, content, sig),
AddressHintProvider,
PubKeyHintProvider {
override fun addressHints() = tags.mapNotNull(ATag::parseAsHint)
override fun pubKeyHints() = tags.mapNotNull(ContactTag::parseAsHint)
/**
* Returns a list of p-tags that are verified as hex keys.
*/
@@ -54,9 +63,9 @@ class ContactListEvent(
/**
* Returns a list of a-tags that are verified as correct.
*/
fun verifiedFollowAddressSet(): Set<HexKey> = tags.mapNotNullTo(HashSet(), ATag::parseValidAddress)
fun verifiedFollowAddressSet(): Set<HexKey> = tags.mapNotNullTo(HashSet(), AddressFollowTag::parseValidAddress)
fun unverifiedFollowKeySet() = tags.mapNotNull(PTag::parseKey)
fun unverifiedFollowKeySet() = tags.mapNotNull(ContactTag::parseKey)
fun unverifiedFollowTagSet() = tags.hashtags()
@@ -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.nip02FollowList.tags
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
typealias AddressFollowTag = ATag
@@ -18,12 +18,13 @@
* 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.nip02FollowList
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.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
@@ -62,13 +63,13 @@ data class ContactTag(
@JvmStatic
fun parse(tag: Array<String>): ContactTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || 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) return null
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null
return try {
ContactTag(decodePublicKey(tag[1]).toHexKey(), tag.getOrNull(2), tag.getOrNull(3))
} catch (e: Exception) {
@@ -77,9 +78,15 @@ 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
return tag[1]
}
@JvmStatic
fun parseValidKey(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null
return try {
decodePublicKey(tag[1]).toHexKey()
} catch (e: Exception) {
@@ -88,6 +95,12 @@ 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
return PubKeyHint(tag[1], tag[2])
}
@JvmStatic
fun assemble(
pubkey: HexKey,
@@ -26,10 +26,9 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.eTag
import com.vitorpamplona.quartz.nip01Core.tags.kinds.kind
import com.vitorpamplona.quartz.nip03Timestamp.tags.TargetEventTag
import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.utils.TimeUtils
import java.util.Base64
@@ -42,10 +41,13 @@ class OtsEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
) : Event(id, pubKey, createdAt, KIND, tags, content, sig),
EventHintProvider {
override fun eventHints() = tags.mapNotNull(TargetEventTag::parseAsHint)
override fun isContentEncoded() = true
fun digestEventId() = tags.firstNotNullOfOrNull(ETag::parseId)
fun digestEventId() = tags.firstNotNullOfOrNull(TargetEventTag::parseId)
fun otsByteArray(): ByteArray = decodeOtsState(content)
@@ -82,7 +84,7 @@ class OtsEvent(
initializer: TagArrayBuilder<OtsEvent>.() -> Unit = {},
) = eventTemplate(KIND, encodeOtsState(otsState), createdAt) {
alt(ALT)
eTag(ETag(eventId))
targetEvent(TargetEventTag(eventId))
initializer()
}
@@ -95,8 +97,8 @@ class OtsEvent(
) = eventTemplate(KIND, encodeOtsState(otsState), createdAt) {
alt(ALT)
eTag(event.toETag())
kind(event.event.kind)
targetEvent(event.toETag())
targetKind(event.event.kind)
initializer()
}
@@ -0,0 +1,36 @@
/**
* 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.nip03Timestamp
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip03Timestamp.tags.TargetEventKindTag
import com.vitorpamplona.quartz.nip03Timestamp.tags.TargetEventTag
fun <T : Event> TagArrayBuilder<T>.targetEvent(tag: TargetEventTag) = add(tag.toTagArray())
fun <T : Event> TagArrayBuilder<T>.targetEvents(tag: List<TargetEventTag>) = addAll(tag.map { it.toTagArray() })
fun <T : Event> TagArrayBuilder<T>.targetKind(kind: Int) = add(TargetEventKindTag.assemble(kind))
fun <T : Event> TagArrayBuilder<T>.targetKinds(kinds: List<Int>) = addAll(TargetEventKindTag.assemble(kinds))
fun <T : Event> TagArrayBuilder<T>.targetKinds(kinds: Set<Int>) = addAll(TargetEventKindTag.assemble(kinds))
@@ -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.nip03Timestamp.tags
import com.vitorpamplona.quartz.nip01Core.tags.kinds.KindTag
typealias TargetEventKindTag = KindTag
@@ -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.nip03Timestamp.tags
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
typealias TargetEventTag = ETag
@@ -25,6 +25,8 @@ 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.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.aTag
@@ -48,7 +50,13 @@ class DeletionEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
) : Event(id, pubKey, createdAt, KIND, tags, content, sig),
EventHintProvider,
AddressHintProvider {
override fun eventHints() = tags.mapNotNull(ETag::parseAsHint)
override fun addressHints() = tags.mapNotNull(ATag::parseAsHint)
fun deleteEvents() = taggedEvents()
fun deleteEventIds() = taggedEventIds()
@@ -23,12 +23,18 @@ package com.vitorpamplona.quartz.nip10Notes
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedATags
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers
import com.vitorpamplona.quartz.nip10Notes.content.findIndexTagsWithEventsOrAddresses
import com.vitorpamplona.quartz.nip10Notes.content.findIndexTagsWithPeople
import com.vitorpamplona.quartz.nip10Notes.content.findNostrUris
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
import com.vitorpamplona.quartz.nip18Reposts.quotes.QTag
import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress
import com.vitorpamplona.quartz.nip19Bech32.entities.NEmbed
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
@@ -48,7 +54,16 @@ open class BaseThreadedEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
) : Event(id, pubKey, createdAt, kind, tags, content, sig),
EventHintProvider,
AddressHintProvider,
PubKeyHintProvider {
override fun eventHints() = tags.mapNotNull(MarkedETag::parseAsHint) + tags.mapNotNull(QTag::parseEventAsHint)
override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) + tags.mapNotNull(QTag::parseAddressAsHint)
override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint)
fun mentions() = taggedUsers()
fun markedRoot() = tags.firstNotNullOfOrNull(MarkedETag::parseRoot)
@@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.nip10Notes.tags
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
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
@@ -180,6 +181,12 @@ data class MarkedETag(
null
}
@JvmStatic
fun parseAsHint(tag: Array<String>): EventIdHint? {
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) return null
return EventIdHint(tag[1], tag[2])
}
@JvmStatic
fun parseRoot(tag: Array<String>): MarkedETag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
@@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.nip17Dm.base
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip59Giftwrap.WrappedEvent
import kotlinx.collections.immutable.toImmutableSet
@@ -37,7 +38,10 @@ open class BaseDMGroupEvent(
sig: HexKey,
) : WrappedEvent(id, pubKey, createdAt, kind, tags, content, sig),
ChatroomKeyable,
NIP17Group {
NIP17Group,
PubKeyHintProvider {
override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint)
/** Recipients intended to receive this conversation */
fun recipients() = tags.mapNotNull(PTag::parse)
@@ -37,7 +37,7 @@ import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.OriginalHashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.SizeTag
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -57,7 +57,7 @@ class ChatMessageEncryptedFileHeaderEvent(
fun mimeType() = tags.firstNotNullOfOrNull(FileTypeTag::parse)
fun hash() = tags.firstNotNullOfOrNull(HashTag::parse)
fun hash() = tags.firstNotNullOfOrNull(HashSha256Tag::parse)
fun size() = tags.firstNotNullOfOrNull(SizeTag::parse)
@@ -35,7 +35,7 @@ import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.OriginalHashTag
@@ -61,7 +61,7 @@ fun TagArrayBuilder<ChatMessageEncryptedFileHeaderEvent>.encryptionNonce(nonce:
fun TagArrayBuilder<ChatMessageEncryptedFileHeaderEvent>.mimeType(mimeType: String) = add(FileTypeTag.assemble(mimeType))
fun TagArrayBuilder<ChatMessageEncryptedFileHeaderEvent>.hash(hash: HexKey) = add(HashTag.assemble(hash))
fun TagArrayBuilder<ChatMessageEncryptedFileHeaderEvent>.hash(hash: HexKey) = add(HashSha256Tag.assemble(hash))
fun TagArrayBuilder<ChatMessageEncryptedFileHeaderEvent>.fileSize(size: Int) = add(SizeTag.assemble(size))
@@ -38,6 +38,6 @@ class EncryptionNonce {
}
@JvmStatic
fun assemble(nonce: ByteArray) = arrayOf(EncryptionKey.TAG_NAME, nonce.toHexKey())
fun assemble(nonce: ByteArray) = arrayOf(TAG_NAME, nonce.toHexKey())
}
}
@@ -25,8 +25,12 @@ 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.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.aTag
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.eTag
@@ -45,10 +49,29 @@ class GenericRepostEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
fun boostedPost() = tags.filter { it.firstOrNull() == "e" }.mapNotNull { it.getOrNull(1) }
) : Event(id, pubKey, createdAt, KIND, tags, content, sig),
EventHintProvider,
PubKeyHintProvider,
AddressHintProvider {
override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint)
fun originalAuthor() = tags.filter { it.firstOrNull() == "p" }.mapNotNull { it.getOrNull(1) }
override fun eventHints() = tags.mapNotNull(ETag::parseAsHint)
override fun addressHints() = tags.mapNotNull(ATag::parseAsHint)
fun boostedEvents() = tags.mapNotNull(ETag::parse)
fun boostedATags() = tags.mapNotNull(ATag::parse)
fun boostedAddresses() = tags.mapNotNull(ATag::parseAddress)
fun originalAuthors() = tags.mapNotNull(PTag::parse)
fun boostedEventIds() = tags.mapNotNull(ETag::parseId)
fun boostedAddressIds() = tags.mapNotNull(ATag::parseAddressId)
fun originalAuthorKeys() = tags.mapNotNull(PTag::parseKey)
fun containedPost() =
try {
@@ -25,15 +25,17 @@ 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.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.aTag
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.eTag
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents
import com.vitorpamplona.quartz.nip01Core.tags.kinds.kind
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers
import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -45,10 +47,29 @@ class RepostEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
fun boostedPost() = taggedEvents()
) : Event(id, pubKey, createdAt, KIND, tags, content, sig),
EventHintProvider,
PubKeyHintProvider,
AddressHintProvider {
override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint)
fun originalAuthor() = taggedUsers()
override fun eventHints() = tags.mapNotNull(ETag::parseAsHint)
override fun addressHints() = tags.mapNotNull(ATag::parseAsHint)
fun boostedEvents() = tags.mapNotNull(ETag::parse)
fun boostedATags() = tags.mapNotNull(ATag::parse)
fun boostedAddresses() = tags.mapNotNull(ATag::parseAddress)
fun originalAuthors() = tags.mapNotNull(PTag::parse)
fun boostedEventIds() = tags.mapNotNull(ETag::parseId)
fun boostedAddressIds() = tags.mapNotNull(ATag::parseAddressId)
fun originalAuthorKeys() = tags.mapNotNull(PTag::parseKey)
fun containedPost() =
try {
@@ -20,6 +20,9 @@
*/
package com.vitorpamplona.quartz.nip18Reposts.quotes
import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
interface QTag {
fun toTagArray(): Array<String>
@@ -36,5 +39,17 @@ interface QTag {
QAddressableTag.parse(tag)
}
}
@JvmStatic
fun parseEventAsHint(tag: Array<String>): EventIdHint? {
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) 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
return AddressHint(tag[1], tag[2])
}
}
}
@@ -51,6 +51,12 @@ object Bech32 {
const val ALPHABET: String = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
const val ALPHABET_UPPERCASE: String = "QPZRY9X8GF2TVDW0S3JN54KHCE6MUA7L"
private const val GEN0 = 0x3b6a57b2
private const val GEN1 = 0x26508e6d
private const val GEN2 = 0x1ea119fa
private const val GEN3 = 0x3d4233dd
private const val GEN4 = 0x2a1462b3
enum class Encoding(
val constant: Int,
) {
@@ -74,17 +80,17 @@ object Bech32 {
fun expand(hrp: String): Array<Int5> {
val half = hrp.length + 1
val size = half + hrp.length
val firstPart = hrp.indices
val secondPart = half until size
return Array<Int5>(size) {
when (it) {
in hrp.indices -> hrp[it].code.shr(5).toByte()
in half until size -> (hrp[it - half].code and 31).toByte()
in firstPart -> hrp[it].code.shr(5).toByte()
in secondPart -> (hrp[it - half].code and 31).toByte()
else -> 0
}
}
}
private val GEN = arrayOf(0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3)
fun polymod(
values: Array<Int5>,
values1: Array<Int5>,
@@ -93,16 +99,20 @@ object Bech32 {
values.forEach { v ->
val b = chk shr 25
chk = ((chk and 0x1ffffff) shl 5) xor v.toInt()
for (i in 0..4) {
if (((b shr i) and 1) != 0) chk = chk xor GEN[i]
}
if (((b shr 0) and 1) != 0) chk = chk xor GEN0
if (((b shr 1) and 1) != 0) chk = chk xor GEN1
if (((b shr 2) and 1) != 0) chk = chk xor GEN2
if (((b shr 3) and 1) != 0) chk = chk xor GEN3
if (((b shr 4) and 1) != 0) chk = chk xor GEN4
}
values1.forEach { v ->
val b = chk shr 25
chk = ((chk and 0x1ffffff) shl 5) xor v.toInt()
for (i in 0..4) {
if (((b shr i) and 1) != 0) chk = chk xor GEN[i]
}
if (((b shr 0) and 1) != 0) chk = chk xor GEN0
if (((b shr 1) and 1) != 0) chk = chk xor GEN1
if (((b shr 2) and 1) != 0) chk = chk xor GEN2
if (((b shr 3) and 1) != 0) chk = chk xor GEN3
if (((b shr 4) and 1) != 0) chk = chk xor GEN4
}
return chk
}
@@ -128,8 +138,7 @@ object Bech32 {
else -> addChecksum(hrp, int5s, encoding)
}
val charArray =
CharArray(dataWithChecksum.size) { ALPHABET[dataWithChecksum[it].toInt()] }.concatToString()
val charArray = CharArray(dataWithChecksum.size) { ALPHABET[dataWithChecksum[it].toInt()] }.concatToString()
return hrp + "1" + charArray
}
@@ -231,8 +240,8 @@ object Bech32 {
@JvmStatic
public fun eight2five(input: ByteArray): ArrayList<Int5> {
var buffer = 0L
val output =
ArrayList<Int5>(input.size * 2) // larger array on purpose. Checksum is added later.
// larger array on purpose. Checksum is added later.
val output = ArrayList<Int5>(input.size * 2)
var count = 0
input.forEach { b ->
buffer = (buffer shl 8) or (b.toLong() and 0xff)
@@ -25,7 +25,10 @@ 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.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
import com.vitorpamplona.quartz.nip21UriScheme.toNostrUri
@@ -53,7 +56,16 @@ class CommentEvent(
content: String,
sig: HexKey,
) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig),
RootScope {
RootScope,
EventHintProvider,
PubKeyHintProvider,
AddressHintProvider {
override fun pubKeyHints() = tags.mapNotNull(RootAuthorTag::parseAsHint) + tags.mapNotNull(ReplyAuthorTag::parseAsHint)
override fun eventHints() = tags.mapNotNull(RootEventTag::parseAsHint) + tags.mapNotNull(ReplyEventTag::parseAsHint)
override fun addressHints() = tags.mapNotNull(RootAddressTag::parseAsHint) + tags.mapNotNull(ReplyAddressTag::parseAsHint)
fun rootAuthor() = tags.firstNotNullOfOrNull(RootAuthorTag::parse)
fun replyAuthor() = tags.firstNotNullOfOrNull(ReplyAuthorTag::parse)
@@ -25,6 +25,7 @@ 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.hints.types.AddressHint
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.utils.arrayOfNotNull
@@ -51,6 +52,12 @@ class ReplyAddressTag(
@JvmStatic
fun parseAddress(tag: Array<String>) = tag.valueIfMatches(TAG_NAME, TAG_SIZE)
@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
return AddressHint(tag[1], tag[2])
}
@JvmStatic
fun assemble(
addressId: HexKey,
@@ -24,6 +24,7 @@ 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.hints.types.PubKeyHint
import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag
import com.vitorpamplona.quartz.utils.arrayOfNotNull
@@ -55,6 +56,12 @@ data class ReplyAuthorTag(
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
return PubKeyHint(tag[1], tag[2])
}
@JvmStatic
fun assemble(
pubkey: HexKey,
@@ -25,6 +25,7 @@ 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.hints.types.EventIdHint
import com.vitorpamplona.quartz.nip01Core.tags.events.EventReference
import com.vitorpamplona.quartz.utils.arrayOfNotNull
@@ -71,6 +72,12 @@ class ReplyEventTag(
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
return EventIdHint(tag[1], tag[2])
}
@JvmStatic
fun assemble(
eventId: HexKey,
@@ -25,6 +25,7 @@ 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.hints.types.AddressHint
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.utils.arrayOfNotNull
@@ -69,6 +70,12 @@ class RootAddressTag(
@JvmStatic
fun parseAddress(tag: Array<String>) = tag.valueIfMatches(TAG_NAME, TAG_SIZE)
@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
return AddressHint(tag[1], tag[2])
}
@JvmStatic
fun assemble(
addressId: HexKey,
@@ -22,10 +22,13 @@ package com.vitorpamplona.quartz.nip22Comments.tags
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.match
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
@Immutable
data class RootAuthorTag(
@@ -36,25 +39,45 @@ data class RootAuthorTag(
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.size > 1 && tag[0] == TAG_NAME
@JvmStatic
fun parse(tag: Tag): ReplyAuthorTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
if (tag[1].length != 64) return null
if (tag.size < 2 || tag[0] != TAG_NAME || tag[1].length != PUBKEY_LENGTH) return null
return ReplyAuthorTag(tag[1], tag.getOrNull(2))
}
@JvmStatic
fun parse3(tag: Tag): ReplyAuthorTag? {
ensure(tag.size >= 2) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == PUBKEY_LENGTH) { 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
if (tag.size < 2 || tag[0] != TAG_NAME || tag[1].length != PUBKEY_LENGTH) return null
return tag[1]
}
@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
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,
@@ -25,6 +25,7 @@ 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.hints.types.EventIdHint
import com.vitorpamplona.quartz.nip01Core.tags.events.EventReference
import com.vitorpamplona.quartz.utils.arrayOfNotNull
@@ -71,6 +72,12 @@ class RootEventTag(
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
return EventIdHint(tag[1], tag[2])
}
@JvmStatic
fun assemble(
eventId: HexKey,
@@ -24,12 +24,17 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
import com.vitorpamplona.quartz.nip18Reposts.quotes.QTag
import com.vitorpamplona.quartz.nip23LongContent.tags.ImageTag
import com.vitorpamplona.quartz.nip23LongContent.tags.PublishedAtTag
import com.vitorpamplona.quartz.nip23LongContent.tags.SummaryTag
@@ -47,7 +52,16 @@ class LongTextNoteEvent(
content: String,
sig: HexKey,
) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig),
AddressableEvent {
AddressableEvent,
EventHintProvider,
PubKeyHintProvider,
AddressHintProvider {
override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint)
override fun eventHints() = tags.mapNotNull(QTag::parseEventAsHint)
override fun addressHints() = tags.mapNotNull(QTag::parseAddressAsHint)
override fun dTag() = tags.dTag()
override fun aTag(relayHint: String?) = ATag(kind, pubKey, dTag(), relayHint)
@@ -24,8 +24,12 @@ import androidx.compose.runtime.Immutable
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.hints.AddressHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.aTag
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.eTag
@@ -44,7 +48,16 @@ class ReactionEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
) : Event(id, pubKey, createdAt, KIND, tags, content, sig),
EventHintProvider,
PubKeyHintProvider,
AddressHintProvider {
override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint)
override fun eventHints() = tags.mapNotNull(ETag::parseAsHint)
override fun addressHints() = tags.mapNotNull(ATag::parseAsHint)
fun originalPost() = tags.mapNotNull(ETag::parseId)
fun originalAuthor() = tags.mapNotNull(PTag::parseKey)
@@ -24,6 +24,8 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip28PublicChat.base.ChannelData
import com.vitorpamplona.quartz.nip31Alts.alt
@@ -37,7 +39,10 @@ class ChannelCreateEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
) : Event(id, pubKey, createdAt, KIND, tags, content, sig),
EventHintProvider {
override fun eventHints() = channelInfo().relays?.map { EventIdHint(id, it) } ?: emptyList()
fun channelInfo() = ChannelData.parse(content) ?: ChannelData()
companion object {
@@ -24,6 +24,7 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.eTags
@@ -41,7 +42,10 @@ class ChannelHideMessageEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BasePublicChatEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
) : BasePublicChatEvent(id, pubKey, createdAt, KIND, tags, content, sig),
EventHintProvider {
override fun eventHints() = tags.mapNotNull(ETag::parseAsHint)
fun eventsToHide() = tags.taggedEventIds()
companion object {
@@ -24,6 +24,8 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip28PublicChat.base.BasePublicChatEvent
@@ -40,7 +42,10 @@ class ChannelMetadataEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BasePublicChatEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
) : BasePublicChatEvent(id, pubKey, createdAt, KIND, tags, content, sig),
EventHintProvider {
override fun eventHints() = channelInfo().relays?.map { EventIdHint(id, it) } ?: emptyList()
fun channelInfo() = ChannelData.parse(content) ?: ChannelData()
companion object {
@@ -24,6 +24,7 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTags
@@ -41,7 +42,10 @@ class ChannelMuteUserEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BasePublicChatEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
) : BasePublicChatEvent(id, pubKey, createdAt, KIND, tags, content, sig),
PubKeyHintProvider {
override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint)
fun usersToMute() = tags.taggedUserIds()
companion object {
@@ -24,9 +24,11 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.signers.eventUpdate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedATags
import com.vitorpamplona.quartz.nip30CustomEmoji.pack.EmojiPackEvent
@@ -41,7 +43,10 @@ class EmojiPackSelectionEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig),
AddressHintProvider {
override fun addressHints() = tags.mapNotNull(ATag::parseAsHint)
fun emojiPacks() = taggedATags()
companion object {
@@ -23,13 +23,17 @@ package com.vitorpamplona.quartz.nip34Git.issue
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
import com.vitorpamplona.quartz.nip14Subject.SubjectTag
import com.vitorpamplona.quartz.nip18Reposts.quotes.QTag
import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -42,7 +46,16 @@ class GitIssueEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig),
PubKeyHintProvider,
EventHintProvider,
AddressHintProvider {
override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint)
override fun eventHints() = tags.mapNotNull(QTag::parseEventAsHint)
override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) + tags.mapNotNull(QTag::parseAddressAsHint)
fun repositoryHex() = tags.firstNotNullOfOrNull(ATag::parseAddressId)
fun repositoryAddress() = tags.firstNotNullOfOrNull(ATag::parseAddress)
@@ -23,9 +23,14 @@ package com.vitorpamplona.quartz.nip34Git.patch
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
import com.vitorpamplona.quartz.nip31Alts.AltTag
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -37,7 +42,16 @@ class GitPatchEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
) : Event(id, pubKey, createdAt, KIND, tags, content, sig),
PubKeyHintProvider,
EventHintProvider,
AddressHintProvider {
override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint)
override fun eventHints() = tags.mapNotNull(MarkedETag::parseAsHint)
override fun addressHints() = tags.mapNotNull(ATag::parseAsHint)
private fun innerRepository() =
tags.firstOrNull { it.size > 3 && it[0] == "a" && it[3] == "root" }
?: tags.firstOrNull { it.size > 1 && it[0] == "a" }
@@ -23,13 +23,18 @@ package com.vitorpamplona.quartz.nip34Git.reply
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
import com.vitorpamplona.quartz.nip10Notes.tags.markedETags
import com.vitorpamplona.quartz.nip10Notes.tags.prepareMarkedETagsAsReplyTo
import com.vitorpamplona.quartz.nip18Reposts.quotes.QTag
import com.vitorpamplona.quartz.nip34Git.issue.GitIssueEvent
import com.vitorpamplona.quartz.nip34Git.patch.GitPatchEvent
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -43,7 +48,16 @@ class GitReplyEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig),
PubKeyHintProvider,
EventHintProvider,
AddressHintProvider {
override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint)
override fun eventHints() = tags.mapNotNull(MarkedETag::parseAsHint) + tags.mapNotNull(QTag::parseEventAsHint)
override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) + tags.mapNotNull(QTag::parseAddressAsHint)
fun repositoryHex() = tags.firstNotNullOfOrNull(ATag::parseAddressId)
fun repository() = tags.firstNotNullOfOrNull(ATag::parse)
@@ -23,15 +23,20 @@ package com.vitorpamplona.quartz.nip35Torrents
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.eTags
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
import com.vitorpamplona.quartz.nip10Notes.tags.positionalMarkedTags
import com.vitorpamplona.quartz.nip18Reposts.quotes.QTag
import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -43,7 +48,16 @@ class TorrentCommentEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig),
EventHintProvider,
PubKeyHintProvider,
AddressHintProvider {
override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint)
override fun eventHints() = tags.mapNotNull(ETag::parseAsHint) + tags.mapNotNull(QTag::parseEventAsHint)
override fun addressHints() = tags.mapNotNull(QTag::parseAddressAsHint)
fun torrent() = tags.firstNotNullOfOrNull(MarkedETag::parseRoot) ?: tags.firstNotNullOfOrNull(ETag::parse)
fun torrentIds() = tags.firstNotNullOfOrNull(MarkedETag::parseRootId) ?: tags.firstNotNullOfOrNull(ETag::parseId)
@@ -23,14 +23,19 @@ package com.vitorpamplona.quartz.nip51Lists
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.isTagged
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashes
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUserIds
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.HashtagTag
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip51Lists.tags.NameTag
import com.vitorpamplona.quartz.nip51Lists.tags.TitleTag
import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.toImmutableSet
@@ -43,20 +48,22 @@ abstract class GeneralListEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : PrivateTagArrayEvent(id, pubKey, createdAt, kind, tags, content, sig) {
fun category() = dTag()
) : PrivateTagArrayEvent(id, pubKey, createdAt, kind, tags, content, sig),
EventHintProvider,
AddressHintProvider,
PubKeyHintProvider {
override fun eventHints() = tags.mapNotNull(ETag::parseAsHint) + (cachedPrivateTags()?.mapNotNull(ETag::parseAsHint) ?: emptyList())
fun bookmarkedPosts() = taggedEvents()
override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) + (cachedPrivateTags()?.mapNotNull(ATag::parseAsHint) ?: emptyList())
fun bookmarkedPeople() = taggedUsers()
override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint) + (cachedPrivateTags()?.mapNotNull(PTag::parseAsHint) ?: emptyList())
fun bookmarkedPeopleIds() = taggedUserIds()
fun name() = tags.firstNotNullOfOrNull(NameTag::parse)
fun name() = tags.firstOrNull { it.size > 1 && it[0] == "name" }?.get(1)
@Deprecated("NIP-51 has deprecated Title. Use name instead", ReplaceWith("name()"))
fun title() = tags.firstNotNullOfOrNull(TitleTag::parse)
fun title() = tags.firstOrNull { it.size > 1 && it[0] == "title" }?.get(1)
fun nameOrTitle() = name()?.ifBlank { null } ?: title()?.ifBlank { null }
fun nameOrTitle() = name() ?: title()
fun filterTagList(
key: String,
@@ -124,13 +131,13 @@ abstract class GeneralListEvent(
onReady: (List<Address>) -> Unit,
) = privateTags(signer) { onReady(filterAddresses(it)) }
fun filterUsers(tags: Array<Array<String>>): List<String> = tags.filter { it.size > 1 && it[0] == "p" }.map { it[1] }
fun filterUsers(tags: Array<Array<String>>): List<String> = tags.mapNotNull(PTag::parseKey)
fun filterHashtags(tags: Array<Array<String>>): List<String> = tags.filter { it.size > 1 && it[0] == "t" }.map { it[1] }
fun filterHashtags(tags: Array<Array<String>>): List<String> = tags.mapNotNull(HashtagTag::parse)
fun filterGeohashes(tags: Array<Array<String>>): List<String> = tags.geohashes()
fun filterEvents(tags: Array<Array<String>>): List<String> = tags.filter { it.size > 1 && it[0] == "e" }.map { it[1] }
fun filterEvents(tags: Array<Array<String>>): List<String> = tags.mapNotNull(ETag::parseId)
fun filterATags(tags: Array<Array<String>>): List<ATag> = tags.mapNotNull(ATag::parse)
@@ -26,9 +26,6 @@ import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip31Alts.AltTag
import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
import kotlinx.collections.immutable.ImmutableSet
@Immutable
class MuteListEvent(
@@ -39,39 +36,16 @@ class MuteListEvent(
content: String,
sig: HexKey,
) : GeneralListEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
@Transient var publicAndPrivateUserCache: ImmutableSet<HexKey>? = null
@Transient var publicAndPrivateWordCache: ImmutableSet<String>? = null
override fun countMemory(): Long =
super.countMemory() +
pointerSizeInBytes + (publicAndPrivateUserCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0) +
pointerSizeInBytes + (publicAndPrivateWordCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0)
override fun dTag() = FIXED_D_TAG
fun publicAndPrivateUsersAndWords(
signer: NostrSigner,
onReady: (PeopleListEvent.UsersAndWords) -> Unit,
) {
publicAndPrivateUserCache?.let { userList ->
publicAndPrivateWordCache?.let { wordList ->
onReady(PeopleListEvent.UsersAndWords(userList, wordList))
return
}
}
privateTagsOrEmpty(signer) {
publicAndPrivateUserCache = filterTagList("p", it)
publicAndPrivateWordCache = filterTagList("word", it)
publicAndPrivateUserCache?.let { userList ->
publicAndPrivateWordCache?.let { wordList ->
onReady(
PeopleListEvent.UsersAndWords(userList, wordList),
)
}
}
onReady(
PeopleListEvent.UsersAndWords(filterTagList("p", it), filterTagList("word", it)),
)
}
}
@@ -26,9 +26,6 @@ import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip31Alts.AltTag
import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
import kotlinx.collections.immutable.ImmutableSet
@Immutable
class PeopleListEvent(
@@ -39,45 +36,6 @@ class PeopleListEvent(
content: String,
sig: HexKey,
) : GeneralListEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
@Transient var publicAndPrivateUserCache: ImmutableSet<HexKey>? = null
@Transient var publicAndPrivateWordCache: ImmutableSet<String>? = null
override fun countMemory(): Long =
super.countMemory() +
pointerSizeInBytes + (publicAndPrivateUserCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0) +
pointerSizeInBytes + (publicAndPrivateWordCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0)
fun publicAndPrivateWords(
signer: NostrSigner,
onReady: (ImmutableSet<String>) -> Unit,
) {
publicAndPrivateWordCache?.let {
onReady(it)
return
}
privateTagsOrEmpty(signer) {
publicAndPrivateWordCache = filterTagList("word", it)
publicAndPrivateWordCache?.let { onReady(it) }
}
}
fun publicAndPrivateUsers(
signer: NostrSigner,
onReady: (ImmutableSet<String>) -> Unit,
) {
publicAndPrivateUserCache?.let {
onReady(it)
return
}
privateTagsOrEmpty(signer) {
publicAndPrivateUserCache = filterTagList("p", it)
publicAndPrivateUserCache?.let { onReady(it) }
}
}
@Immutable
class UsersAndWords(
val users: Set<String> = setOf(),
@@ -88,24 +46,13 @@ class PeopleListEvent(
signer: NostrSigner,
onReady: (UsersAndWords) -> Unit,
) {
publicAndPrivateUserCache?.let { userList ->
publicAndPrivateWordCache?.let { wordList ->
onReady(UsersAndWords(userList, wordList))
return
}
}
privateTagsOrEmpty(signer) {
publicAndPrivateUserCache = filterTagList("p", it)
publicAndPrivateWordCache = filterTagList("word", it)
publicAndPrivateUserCache?.let { userList ->
publicAndPrivateWordCache?.let { wordList ->
onReady(
UsersAndWords(userList, wordList),
)
}
}
onReady(
UsersAndWords(
filterTagList("p", it),
filterTagList("word", it),
),
)
}
}
@@ -25,6 +25,7 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@@ -63,7 +64,7 @@ abstract class PrivateTagArrayEvent(
}
try {
Nip51PrivateTags.decrypt(content, signer) {
PrivateTagsInContent.decrypt(content, signer) {
privateTagsCache = it
privateTagsCache?.let { onReady(it) }
}
@@ -78,7 +79,7 @@ abstract class PrivateTagArrayEvent(
onReady: (content: String) -> Unit,
) {
privateTags(signer) { privateTags ->
Nip51PrivateTags.encryptNip04(
PrivateTagsInContent.encryptNip04(
privateTags = change(privateTags),
signer = signer,
) { encryptedTags ->
@@ -97,7 +98,7 @@ abstract class PrivateTagArrayEvent(
) {
if (toPrivate) {
current.privateTags(signer) { privateTags ->
Nip51PrivateTags.encryptNip04(
PrivateTagsInContent.encryptNip04(
privateTags = privateTags.plus(newTag),
signer = signer,
) { encryptedTags ->
@@ -118,7 +119,7 @@ abstract class PrivateTagArrayEvent(
) {
if (toPrivate) {
current.privateTags(signer) { privateTags ->
Nip51PrivateTags.encryptNip04(
PrivateTagsInContent.encryptNip04(
privateTags = privateTags.plus(newTag),
signer = signer,
) { encryptedTags ->
@@ -168,7 +169,7 @@ abstract class PrivateTagArrayEvent(
onReady: (content: String, tags: Array<Array<String>>) -> Unit,
) {
current.privateTags(signer) { privateTags ->
Nip51PrivateTags.encryptNip04(
PrivateTagsInContent.encryptNip04(
privateTags = privateTags.replaceAll(oldTagStartsWith, newTag),
signer = signer,
) { encryptedTags ->
@@ -185,7 +186,7 @@ abstract class PrivateTagArrayEvent(
onReady: (content: String, tags: Array<Array<String>>) -> Unit,
) {
current.privateTags(signer) { privateTags ->
Nip51PrivateTags.encryptNip04(
PrivateTagsInContent.encryptNip04(
privateTags = privateTags.remove(oldTagStartsWith),
signer = signer,
) { encryptedTags ->
@@ -201,7 +202,7 @@ abstract class PrivateTagArrayEvent(
onReady: (content: String, tags: Array<Array<String>>) -> Unit,
) {
current.privateTags(signer) { privateTags ->
Nip51PrivateTags.encryptNip04(
PrivateTagsInContent.encryptNip04(
privateTags = privateTags.remove(oldTagStartsWith),
signer = signer,
) { encryptedTags ->
@@ -224,7 +225,7 @@ abstract class PrivateTagArrayEvent(
onReady: (content: String, tags: Array<Array<String>>) -> Unit,
) {
current.privateTags(signer) { privateTags ->
Nip51PrivateTags.encryptNip04(
PrivateTagsInContent.encryptNip04(
privateTags = privateTags.remove(oldTagStartsWith),
signer = signer,
) { encryptedTags ->
@@ -239,7 +240,7 @@ abstract class PrivateTagArrayEvent(
signer: NostrSigner,
onReady: (content: String, tags: Array<Array<String>>) -> Unit,
) {
Nip51PrivateTags.encryptNip04(
PrivateTagsInContent.encryptNip04(
privateTags = arrayOf(newTag),
signer = signer,
) { encryptedTags ->
@@ -18,13 +18,13 @@
* 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.nip51Lists
package com.vitorpamplona.quartz.nip51Lists.encryption
import com.fasterxml.jackson.module.kotlin.readValue
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
class Nip51PrivateTags {
class PrivateTagsInContent {
companion object {
fun decode(content: String) = EventMapper.mapper.readValue<Array<Array<String>>>(content)
@@ -0,0 +1,36 @@
/**
* 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.nip51Lists.tags
class NameTag {
companion object {
const val TAG_NAME = "name"
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < 2 || tag[0] != TAG_NAME || tag[1].isEmpty()) return null
return tag[1]
}
@JvmStatic
fun assemble(name: String) = arrayOf(TAG_NAME, name)
}
}
@@ -0,0 +1,37 @@
/**
* 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.nip51Lists.tags
@Deprecated("Use NameTag Instead")
class TitleTag {
companion object {
const val TAG_NAME = "title"
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < 2 || tag[0] != TAG_NAME || tag[1].isEmpty()) return null
return tag[1]
}
@JvmStatic
fun assemble(name: String) = arrayOf(TAG_NAME, name)
}
}
@@ -23,10 +23,16 @@ package com.vitorpamplona.quartz.nip53LiveActivities.chat
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
import com.vitorpamplona.quartz.nip18Reposts.quotes.QTag
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -38,7 +44,16 @@ class LiveActivitiesChatMessageEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig),
EventHintProvider,
PubKeyHintProvider,
AddressHintProvider {
override fun pubKeyHints() = tags.mapNotNull(PTag::parseAsHint)
override fun eventHints() = tags.mapNotNull(ETag::parseAsHint) + tags.mapNotNull(QTag::parseEventAsHint)
override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) + tags.mapNotNull(QTag::parseAddressAsHint)
private fun activityHex() = tags.firstNotNullOfOrNull(ATag::parseAddressId)
fun activity() = tags.firstNotNullOfOrNull(ATag::parse)
@@ -28,7 +28,7 @@ import com.vitorpamplona.quartz.nip92IMeta.IMetaTagBuilder
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
@@ -48,7 +48,7 @@ fun IMetaTagBuilder.mimeType(mime: String) = add(MimeTypeTag.TAG_NAME, mime)
fun IMetaTagBuilder.alt(alt: String) = add(AltTag.TAG_NAME, alt)
fun IMetaTagBuilder.hash(hash: HexKey) = add(HashTag.TAG_NAME, hash)
fun IMetaTagBuilder.hash(hash: HexKey) = add(HashSha256Tag.TAG_NAME, hash)
fun IMetaTagBuilder.size(size: Int) = add(SizeTag.TAG_NAME, size.toString())
@@ -27,7 +27,7 @@ import com.vitorpamplona.quartz.nip92IMeta.IMetaTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
@@ -47,7 +47,7 @@ fun IMetaTag.mimeType() = properties.get(MimeTypeTag.TAG_NAME)
fun IMetaTag.alt() = properties.get(AltTag.TAG_NAME)
fun IMetaTag.hash() = properties.get(HashTag.TAG_NAME)
fun IMetaTag.hash() = properties.get(HashSha256Tag.TAG_NAME)
fun IMetaTag.size() = properties.get(SizeTag.TAG_NAME)
@@ -33,7 +33,7 @@ import com.vitorpamplona.quartz.nip92IMeta.imetas
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
@@ -68,7 +68,7 @@ class PictureEvent(
private fun mimeType() = tags.firstNotNullOfOrNull(MimeTypeTag::parse)
private fun hash() = tags.firstNotNullOfOrNull(HashTag::parse)
private fun hash() = tags.firstNotNullOfOrNull(HashSha256Tag::parse)
private fun size() = tags.firstNotNullOfOrNull(SizeTag::parse)
@@ -24,7 +24,7 @@ import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip23LongContent.tags.PublishedAtTag
import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
fun TagArrayBuilder<PictureEvent>.title(title: String) = addUnique(TitleTag.assemble(title))
@@ -43,7 +43,7 @@ fun TagArrayBuilder<PictureEvent>.pictureIMeta(
fun TagArrayBuilder<PictureEvent>.pictureIMeta(imeta: PictureMeta): TagArrayBuilder<PictureEvent> {
add(imeta.toIMetaArray())
imeta.hash?.let { add(HashTag.assemble(it)) }
imeta.hash?.let { add(HashSha256Tag.assemble(it)) }
imeta.mimeType?.let { add(MimeTypeTag.assemble(it)) }
return this
}
@@ -27,7 +27,7 @@ import com.vitorpamplona.quartz.nip92IMeta.IMetaTagBuilder
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
@@ -47,7 +47,7 @@ fun IMetaTagBuilder.mimeType(mime: String) = add(MimeTypeTag.TAG_NAME, mime)
fun IMetaTagBuilder.alt(alt: String) = add(AltTag.TAG_NAME, alt)
fun IMetaTagBuilder.hash(hash: HexKey) = add(HashTag.TAG_NAME, hash)
fun IMetaTagBuilder.hash(hash: HexKey) = add(HashSha256Tag.TAG_NAME, hash)
fun IMetaTagBuilder.size(size: Int) = add(SizeTag.TAG_NAME, size.toString())
@@ -26,7 +26,7 @@ import com.vitorpamplona.quartz.nip92IMeta.IMetaTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
@@ -46,7 +46,7 @@ fun IMetaTag.mimeType() = properties.get(MimeTypeTag.TAG_NAME)
fun IMetaTag.alt() = properties.get(AltTag.TAG_NAME)
fun IMetaTag.hash() = properties.get(HashTag.TAG_NAME)
fun IMetaTag.hash() = properties.get(HashSha256Tag.TAG_NAME)
fun IMetaTag.size() = properties.get(SizeTag.TAG_NAME)
@@ -37,7 +37,7 @@ import com.vitorpamplona.quartz.nip92IMeta.imetas
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
@@ -65,7 +65,7 @@ abstract class VideoEvent(
private fun mimeType() = tags.firstNotNullOfOrNull(MimeTypeTag::parse)
private fun hash() = tags.firstNotNullOfOrNull(HashTag::parse)
private fun hash() = tags.firstNotNullOfOrNull(HashSha256Tag::parse)
private fun size() = tags.firstNotNullOfOrNull(SizeTag::parse)
@@ -30,7 +30,7 @@ import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
@@ -57,7 +57,7 @@ class FileHeaderEvent(
fun mimeType() = tags.firstNotNullOfOrNull(MimeTypeTag::parse)
fun hash() = tags.firstNotNullOfOrNull(HashTag::parse)
fun hash() = tags.firstNotNullOfOrNull(HashSha256Tag::parse)
fun size() = tags.firstNotNullOfOrNull(SizeTag::parse)
@@ -27,7 +27,7 @@ import com.vitorpamplona.quartz.nip92IMeta.IMetaTagBuilder
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
@@ -47,7 +47,7 @@ fun IMetaTagBuilder.mimeType(mime: String) = add(MimeTypeTag.TAG_NAME, mime)
fun IMetaTagBuilder.alt(alt: String) = add(AltTag.TAG_NAME, alt)
fun IMetaTagBuilder.hash(hash: HexKey) = add(HashTag.TAG_NAME, hash)
fun IMetaTagBuilder.hash(hash: HexKey) = add(HashSha256Tag.TAG_NAME, hash)
fun IMetaTagBuilder.size(size: Int) = add(SizeTag.TAG_NAME, size.toString())
@@ -25,7 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
@@ -41,7 +41,7 @@ fun TagArrayBuilder<FileHeaderEvent>.url(url: String) = add(UrlTag.assemble(url)
fun TagArrayBuilder<FileHeaderEvent>.mimeType(mimeType: String) = add(MimeTypeTag.assemble(mimeType))
fun TagArrayBuilder<FileHeaderEvent>.hash(hash: HexKey) = add(HashTag.assemble(hash))
fun TagArrayBuilder<FileHeaderEvent>.hash(hash: HexKey) = add(HashSha256Tag.assemble(hash))
fun TagArrayBuilder<FileHeaderEvent>.fileSize(size: Int) = add(SizeTag.assemble(size))
@@ -20,14 +20,14 @@
*/
package com.vitorpamplona.quartz.nip94FileMetadata.tags
class HashTag {
class HashSha256Tag {
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
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].isEmpty()) return null
return tag[1]
}
@@ -0,0 +1,35 @@
/**
* 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.utils
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@OptIn(ExperimentalContracts::class)
inline fun ensure(
condition: Boolean,
exit: () -> Nothing,
) {
contract {
returns() implies condition
}
if (!condition) exit()
}
@@ -21,24 +21,25 @@
package com.vitorpamplona.quartz.utils
object Hex {
private val lowerCaseHex = arrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f')
private val upperCaseHex = arrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')
private const val LOWER_CASE_HEX = "0123456789abcdef"
private const val UPPER_CASE_HEX = "0123456789ABCDEF"
private val hexToByte: IntArray =
IntArray(256) { -1 }.apply {
lowerCaseHex.forEachIndexed { index, char -> this[char.code] = index }
upperCaseHex.forEachIndexed { index, char -> this[char.code] = index }
LOWER_CASE_HEX.forEachIndexed { index, char -> this[char.code] = index }
UPPER_CASE_HEX.forEachIndexed { index, char -> this[char.code] = index }
}
// Encodes both chars in a single Int variable
private val byteToHex =
IntArray(256) {
(lowerCaseHex[(it shr 4)].code shl 8) or lowerCaseHex[(it and 0xF)].code
(LOWER_CASE_HEX[(it shr 4)].code shl 8) or LOWER_CASE_HEX[(it and 0xF)].code
}
@JvmStatic
fun isHex(hex: String?): Boolean {
if (hex.isNullOrEmpty() || hex.length and 1 != 0) return false
if (hex.isNullOrEmpty()) return false
if (hex.length and 1 != 0) return false
try {
for (c in hex.indices) {