Adds support for NIP-22 exclusive geo posts
This commit is contained in:
@@ -24,14 +24,25 @@ import android.util.Log
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||
import com.vitorpamplona.quartz.utils.removeTrailingNullsAndEmptyOthers
|
||||
|
||||
@Immutable
|
||||
data class ATag(
|
||||
val kind: Int,
|
||||
val pubKeyHex: String,
|
||||
val dTag: String,
|
||||
val relay: String?,
|
||||
) {
|
||||
var relay: String? = null
|
||||
|
||||
constructor(
|
||||
kind: Int,
|
||||
pubKeyHex: String,
|
||||
dTag: String,
|
||||
relayHint: String?,
|
||||
) : this(kind, pubKeyHex, dTag) {
|
||||
this.relay = relayHint
|
||||
}
|
||||
|
||||
fun countMemory(): Long =
|
||||
5 * pointerSizeInBytes + // 7 fields, 4 bytes each reference (32bit)
|
||||
8L + // kind
|
||||
@@ -41,6 +52,10 @@ data class ATag(
|
||||
|
||||
fun toTag() = assembleATag(kind, pubKeyHex, dTag)
|
||||
|
||||
fun toATagArray() = removeTrailingNullsAndEmptyOthers("a", toTag(), relay)
|
||||
|
||||
fun toQTagArray() = removeTrailingNullsAndEmptyOthers("q", toTag(), relay)
|
||||
|
||||
fun toNAddr(): String =
|
||||
TlvBuilder()
|
||||
.apply {
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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.encoders
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||
import com.vitorpamplona.quartz.utils.removeTrailingNullsAndEmptyOthers
|
||||
|
||||
@Immutable
|
||||
data class ETag(
|
||||
val eventId: HexKey,
|
||||
) {
|
||||
var relay: String? = null
|
||||
var authorPubKeyHex: HexKey? = null
|
||||
|
||||
constructor(eventId: HexKey, relayHint: String? = null, authorPubKeyHex: HexKey? = null) : this(eventId) {
|
||||
this.relay = relayHint
|
||||
this.authorPubKeyHex = authorPubKeyHex
|
||||
}
|
||||
|
||||
fun countMemory(): Long =
|
||||
2 * pointerSizeInBytes + // 2 fields, 4 bytes each reference (32bit)
|
||||
eventId.bytesUsedInMemory() +
|
||||
(relay?.bytesUsedInMemory() ?: 0)
|
||||
|
||||
fun toNEvent(): String = Nip19Bech32.createNEvent(eventId, authorPubKeyHex, null, relay)
|
||||
|
||||
fun toNote(): String = Nip19Bech32.createNote(eventId)
|
||||
|
||||
fun toETagArray() = removeTrailingNullsAndEmptyOthers("e", eventId, relay, authorPubKeyHex)
|
||||
|
||||
fun toQTagArray() = removeTrailingNullsAndEmptyOthers("q", eventId, relay, authorPubKeyHex)
|
||||
|
||||
companion object {
|
||||
fun parseNIP19(nevent: String): ETag? {
|
||||
try {
|
||||
val parsed = Nip19Bech32.uriToRoute(nevent)?.entity
|
||||
|
||||
return when (parsed) {
|
||||
is Nip19Bech32.Note -> ETag(parsed.hex)
|
||||
is Nip19Bech32.NEvent -> ETag(parsed.hex, parsed.author, parsed.relay.firstOrNull())
|
||||
else -> null
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.w("PTag", "Issue trying to Decode NIP19 $this: ${e.message}")
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.encoders
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.events.Event
|
||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||
|
||||
@Immutable
|
||||
data class EventHint<T : Event>(
|
||||
val event: T,
|
||||
) {
|
||||
var relay: String? = null
|
||||
|
||||
constructor(event: T, relayHint: String? = null) : this(event) {
|
||||
this.relay = relayHint
|
||||
}
|
||||
|
||||
fun countMemory(): Long =
|
||||
2 * pointerSizeInBytes + // 2 fields, 4 bytes each reference (32bit)
|
||||
event.countMemory() +
|
||||
(relay?.bytesUsedInMemory() ?: 0)
|
||||
|
||||
fun toNEvent(): String = Nip19Bech32.createNEvent(event.id, event.pubKey, event.kind, relay)
|
||||
|
||||
fun toNPub(): String = Nip19Bech32.createNPub(event.id)
|
||||
|
||||
fun toTagArray(tag: String) = listOfNotNull(tag, event.id, relay, event.pubKey).toTypedArray()
|
||||
|
||||
fun toETagArray() = toTagArray("e")
|
||||
|
||||
fun toQTagArray() = toTagArray("q")
|
||||
}
|
||||
@@ -270,6 +270,10 @@ object Nip19Bech32 {
|
||||
}.build()
|
||||
.toNEvent()
|
||||
|
||||
fun createNote(eventId: HexKey): String = eventId.hexToByteArray().toNote()
|
||||
|
||||
fun createNPub(authorPubKeyHex: HexKey): String = authorPubKeyHex.hexToByteArray().toNpub()
|
||||
|
||||
fun createNProfile(
|
||||
authorPubKeyHex: String,
|
||||
relay: List<String>,
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* 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.encoders
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||
import com.vitorpamplona.quartz.utils.removeTrailingNullsAndEmptyOthers
|
||||
|
||||
@Immutable
|
||||
data class PTag(
|
||||
val pubKeyHex: HexKey,
|
||||
) {
|
||||
var relay: String? = null
|
||||
|
||||
constructor(pubKeyHex: HexKey, relayHint: String?) : this(pubKeyHex) {
|
||||
this.relay = relayHint?.ifBlank { null }
|
||||
}
|
||||
|
||||
fun countMemory(): Long =
|
||||
2 * pointerSizeInBytes + // 2 fields, 4 bytes each reference (32bit)
|
||||
pubKeyHex.bytesUsedInMemory() +
|
||||
(relay?.bytesUsedInMemory() ?: 0)
|
||||
|
||||
fun toNProfile(): String = Nip19Bech32.createNProfile(pubKeyHex, relay?.let { listOf(it) } ?: emptyList())
|
||||
|
||||
fun toNPub(): String = Nip19Bech32.createNPub(pubKeyHex)
|
||||
|
||||
fun toPTagArray() = removeTrailingNullsAndEmptyOthers("p", pubKeyHex, relay)
|
||||
|
||||
companion object {
|
||||
fun parseNAddr(nprofile: String): PTag? {
|
||||
try {
|
||||
val parsed = Nip19Bech32.uriToRoute(nprofile)?.entity
|
||||
|
||||
return when (parsed) {
|
||||
is Nip19Bech32.NPub -> PTag(parsed.hex)
|
||||
is Nip19Bech32.NProfile -> PTag(parsed.hex, parsed.relay.firstOrNull())
|
||||
else -> null
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.w("PTag", "Issue trying to Decode NIP19 $this: ${e.message}")
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,7 @@ open class BaseTextNoteEvent(
|
||||
}
|
||||
}
|
||||
|
||||
fun replyingTo(): HexKey? {
|
||||
open fun replyingTo(): HexKey? {
|
||||
val oldStylePositional = tags.lastOrNull { it.size > 1 && it.size <= 3 && it[0] == "e" }?.get(1)
|
||||
val newStyleReply = tags.lastOrNull { it.size > 3 && it[0] == "e" && it[3] == "reply" }?.get(1)
|
||||
val newStyleRoot = tags.lastOrNull { it.size > 3 && it[0] == "e" && it[3] == "root" }?.get(1)
|
||||
@@ -79,7 +79,7 @@ open class BaseTextNoteEvent(
|
||||
return newStyleReply ?: newStyleRoot ?: oldStylePositional
|
||||
}
|
||||
|
||||
fun replyingToAddress(): ATag? {
|
||||
open fun replyingToAddress(): ATag? {
|
||||
val oldStylePositional = tags.lastOrNull { it.size > 1 && it.size <= 3 && it[0] == "a" }?.let { ATag.parseAtag(it[1], it[2]) }
|
||||
val newStyleReply = tags.lastOrNull { it.size > 3 && it[0] == "a" && it[3] == "reply" }?.let { ATag.parseAtag(it[1], it[2]) }
|
||||
val newStyleRoot = tags.lastOrNull { it.size > 3 && it[0] == "a" && it[3] == "root" }?.let { ATag.parseAtag(it[1], it[2]) }
|
||||
@@ -87,7 +87,7 @@ open class BaseTextNoteEvent(
|
||||
return newStyleReply ?: newStyleRoot ?: oldStylePositional
|
||||
}
|
||||
|
||||
fun replyingToAddressOrEvent(): String? {
|
||||
open fun replyingToAddressOrEvent(): String? {
|
||||
val oldStylePositional = tags.lastOrNull { it.size > 1 && it.size <= 3 && (it[0] == "e" || it[0] == "a") }?.get(1)
|
||||
val newStyleReply = tags.lastOrNull { it.size > 3 && (it[0] == "e" || it[0] == "a") && it[3] == "reply" }?.get(1)
|
||||
val newStyleRoot = tags.lastOrNull { it.size > 3 && (it[0] == "e" || it[0] == "a") && it[3] == "root" }?.get(1)
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
/**
|
||||
* 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.events
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.ETag
|
||||
import com.vitorpamplona.quartz.encoders.EventHint
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.Nip92MediaAttachments
|
||||
import com.vitorpamplona.quartz.encoders.PTag
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import com.vitorpamplona.quartz.utils.removeTrailingNullsAndEmptyOthers
|
||||
|
||||
@Immutable
|
||||
class CommentEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : BaseTextNoteEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
fun root() = tags.firstOrNull { it.size > 3 && it[3] == "root" }?.get(1)
|
||||
|
||||
fun getRootScopes() = tags.filter { it.size > 1 && it[0] == "I" || it[0] == "A" || it[0] == "E" }
|
||||
|
||||
fun getRootKinds() = tags.filter { it.size > 1 && it[0] == "K" }
|
||||
|
||||
fun getDirectReplies() = tags.filter { it.size > 1 && it[0] == "i" || it[0] == "a" || it[0] == "e" }
|
||||
|
||||
fun getDirectKinds() = tags.filter { it.size > 1 && it[0] == "k" }
|
||||
|
||||
fun isGeohashTag(tag: Array<String>) = tag.size > 1 && (tag[0] == "i" || tag[0] == "I") && tag[1].startsWith("geo:")
|
||||
|
||||
private fun getGeoHashList() = tags.filter { isGeohashTag(it) }
|
||||
|
||||
override fun hasGeohashes() = tags.any { isGeohashTag(it) }
|
||||
|
||||
override fun geohashes() = getGeoHashList().map { it[1].drop(4).lowercase() }
|
||||
|
||||
override fun getGeoHash(): String? = geohashes().maxByOrNull { it.length }
|
||||
|
||||
override fun isTaggedGeoHash(hashtag: String) = tags.any { isGeohashTag(it) && it[1].endsWith(hashtag, true) }
|
||||
|
||||
override fun isTaggedGeoHashes(hashtags: Set<String>) = geohashes().any { it in hashtags }
|
||||
|
||||
override fun replyTos(): List<HexKey> = tags.filter { it.size > 1 && it[0] == "e" }.map { it[1] } + tags.filter { it.size > 1 && it[0] == "E" }.map { it[1] }
|
||||
|
||||
override fun replyingTo(): HexKey? =
|
||||
tags.lastOrNull { it.size > 1 && it[0] == "e" }?.get(1)
|
||||
?: tags.lastOrNull { it.size > 1 && it[0] == "E" }?.get(1)
|
||||
|
||||
override fun replyingToAddress(): ATag? =
|
||||
tags.lastOrNull { it.size > 1 && it[0] == "a" }?.let { ATag.parseAtag(it[1], it.getOrNull(2)) }
|
||||
?: tags.lastOrNull { it.size > 1 && it[0] == "A" }?.let { ATag.parseAtag(it[1], it.getOrNull(2)) }
|
||||
|
||||
override fun replyingToAddressOrEvent(): HexKey? = replyingToAddress()?.toTag() ?: replyingTo()
|
||||
|
||||
companion object {
|
||||
const val KIND = 1111
|
||||
|
||||
fun rootGeohashMipMap(geohash: String): Array<Array<String>> =
|
||||
geohash.indices
|
||||
.asSequence()
|
||||
.map { arrayOf("I", "geo:" + geohash.substring(0, it + 1)) }
|
||||
.toList()
|
||||
.reversed()
|
||||
.toTypedArray()
|
||||
|
||||
fun replyComment(
|
||||
msg: String,
|
||||
replyingTo: EventHint<CommentEvent>,
|
||||
usersMentioned: Set<PTag> = emptySet(),
|
||||
addressesMentioned: Set<ATag> = emptySet(),
|
||||
eventsMentioned: Set<ETag> = emptySet(),
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
geohash: String? = null,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
markAsSensitive: Boolean = false,
|
||||
zapRaiserAmount: Long? = null,
|
||||
isDraft: Boolean,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (CommentEvent) -> Unit,
|
||||
) {
|
||||
val tags = mutableListOf<Array<String>>()
|
||||
|
||||
tags.addAll(replyingTo.event.getRootScopes())
|
||||
tags.addAll(replyingTo.event.getRootKinds())
|
||||
|
||||
tags.add(removeTrailingNullsAndEmptyOthers("e", replyingTo.event.id, replyingTo.relay, replyingTo.event.pubKey))
|
||||
tags.add(arrayOf("k", "${replyingTo.event.kind}"))
|
||||
|
||||
create(msg, tags, usersMentioned, addressesMentioned, eventsMentioned, nip94attachments, geohash, zapReceiver, markAsSensitive, zapRaiserAmount, isDraft, signer, createdAt, onReady)
|
||||
}
|
||||
|
||||
fun createGeoComment(
|
||||
msg: String,
|
||||
geohash: String? = null,
|
||||
usersMentioned: Set<PTag> = emptySet(),
|
||||
addressesMentioned: Set<ATag> = emptySet(),
|
||||
eventsMentioned: Set<ETag> = emptySet(),
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
markAsSensitive: Boolean = false,
|
||||
zapRaiserAmount: Long? = null,
|
||||
isDraft: Boolean,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (CommentEvent) -> Unit,
|
||||
) {
|
||||
val tags = mutableListOf<Array<String>>()
|
||||
geohash?.let { tags.addAll(rootGeohashMipMap(it)) }
|
||||
tags.add(arrayOf("K", "geo"))
|
||||
|
||||
create(msg, tags, usersMentioned, addressesMentioned, eventsMentioned, nip94attachments, null, zapReceiver, markAsSensitive, zapRaiserAmount, isDraft, signer, createdAt, onReady)
|
||||
}
|
||||
|
||||
private fun create(
|
||||
msg: String,
|
||||
tags: MutableList<Array<String>>,
|
||||
usersMentioned: Set<PTag> = emptySet(),
|
||||
addressesMentioned: Set<ATag> = emptySet(),
|
||||
eventsMentioned: Set<ETag> = emptySet(),
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
geohash: String? = null,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
markAsSensitive: Boolean = false,
|
||||
zapRaiserAmount: Long? = null,
|
||||
isDraft: Boolean,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (CommentEvent) -> Unit,
|
||||
) {
|
||||
usersMentioned.forEach { tags.add(it.toPTagArray()) }
|
||||
addressesMentioned.forEach { tags.add(it.toQTagArray()) }
|
||||
eventsMentioned.forEach { tags.add(it.toQTagArray()) }
|
||||
|
||||
findHashtags(msg).forEach {
|
||||
val lowercaseTag = it.lowercase()
|
||||
tags.add(arrayOf("t", it))
|
||||
if (it != lowercaseTag) {
|
||||
tags.add(arrayOf("t", it.lowercase()))
|
||||
}
|
||||
}
|
||||
|
||||
findURLs(msg).forEach { tags.add(arrayOf("r", it)) }
|
||||
|
||||
zapReceiver?.forEach {
|
||||
tags.add(arrayOf("zap", it.lnAddressOrPubKeyHex, it.relay ?: "", it.weight.toString()))
|
||||
}
|
||||
if (markAsSensitive) {
|
||||
tags.add(arrayOf("content-warning", ""))
|
||||
}
|
||||
zapRaiserAmount?.let { tags.add(arrayOf("zapraiser", "$it")) }
|
||||
geohash?.let { tags.addAll(geohashMipMap(it)) }
|
||||
nip94attachments?.let {
|
||||
it.forEach {
|
||||
Nip92MediaAttachments().convertFromFileHeader(it)?.let {
|
||||
tags.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isDraft) {
|
||||
signer.assembleRumor(createdAt, KIND, tags.toTypedArray(), msg, onReady)
|
||||
} else {
|
||||
signer.sign(createdAt, KIND, tags.toTypedArray(), msg, onReady)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,21 +141,11 @@ class ContactListEvent(
|
||||
val tags =
|
||||
listOf(arrayOf("alt", ALT)) +
|
||||
followUsers.map {
|
||||
if (it.relayUri != null) {
|
||||
arrayOf("p", it.pubKeyHex, it.relayUri)
|
||||
} else {
|
||||
arrayOf("p", it.pubKeyHex)
|
||||
}
|
||||
listOfNotNull("a", it.pubKeyHex, it.relayUri).toTypedArray()
|
||||
} +
|
||||
followTags.map { arrayOf("t", it) } +
|
||||
followEvents.map { arrayOf("e", it) } +
|
||||
followCommunities.map {
|
||||
if (it.relay != null) {
|
||||
arrayOf("a", it.toTag(), it.relay)
|
||||
} else {
|
||||
arrayOf("a", it.toTag())
|
||||
}
|
||||
} +
|
||||
followCommunities.map { it.toATagArray() } +
|
||||
followGeohashes.map { arrayOf("g", it) }
|
||||
|
||||
return signer.sign(createdAt, KIND, tags.toTypedArray(), content)
|
||||
@@ -189,13 +179,7 @@ class ContactListEvent(
|
||||
} +
|
||||
followTags.map { arrayOf("t", it) } +
|
||||
followEvents.map { arrayOf("e", it) } +
|
||||
followCommunities.map {
|
||||
if (it.relay != null) {
|
||||
arrayOf("a", it.toTag(), it.relay)
|
||||
} else {
|
||||
arrayOf("a", it.toTag())
|
||||
}
|
||||
} +
|
||||
followCommunities.map { it.toATagArray() } +
|
||||
followGeohashes.map { arrayOf("g", it) }
|
||||
|
||||
return create(
|
||||
|
||||
@@ -188,6 +188,18 @@ class DraftEvent(
|
||||
create(dTag, originalNote, tagsWithMarkers, signer, createdAt, onReady)
|
||||
}
|
||||
|
||||
fun create(
|
||||
dTag: String,
|
||||
originalNote: CommentEvent,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (DraftEvent) -> Unit,
|
||||
) {
|
||||
val tagsWithMarkers = originalNote.getRootScopes() + originalNote.getDirectReplies()
|
||||
|
||||
create(dTag, originalNote, tagsWithMarkers, signer, createdAt, onReady)
|
||||
}
|
||||
|
||||
fun create(
|
||||
dTag: String,
|
||||
originalNote: TextNoteEvent,
|
||||
|
||||
@@ -75,6 +75,7 @@ class EventFactory {
|
||||
}
|
||||
ChatMessageRelayListEvent.KIND -> ChatMessageRelayListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
ClassifiedsEvent.KIND -> ClassifiedsEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
CommentEvent.KIND -> CommentEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
CommunityDefinitionEvent.KIND -> CommunityDefinitionEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
CommunityListEvent.KIND -> CommunityListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
CommunityPostApprovalEvent.KIND -> CommunityPostApprovalEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
public fun removeTrailingNullsAndEmptyOthers(vararg elements: String?): Array<String> {
|
||||
val lastNonNullIndex = elements.indexOfLast { it != null }
|
||||
|
||||
if (lastNonNullIndex < 0) return Array(0) { "" }
|
||||
|
||||
return Array(lastNonNullIndex + 1) { index ->
|
||||
elements[index] ?: ""
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user