Adds support for NipTexts on Amethyst
This commit is contained in:
+4
@@ -0,0 +1,4 @@
|
||||
package com.vitorpamplona.quartz.experimental.forks
|
||||
|
||||
class IForkableEvent {
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.nipsOnNostr
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.experimental.forks.parseFork
|
||||
import com.vitorpamplona.quartz.experimental.nipsOnNostr.tags.ForkTag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
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.types.AddressHint
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.aTag.ATag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.kinds.kinds
|
||||
import com.vitorpamplona.quartz.nip10Notes.BaseNoteEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
|
||||
import com.vitorpamplona.quartz.nip18Reposts.quotes.QTag
|
||||
import com.vitorpamplona.quartz.nip19Bech32.addressHints
|
||||
import com.vitorpamplona.quartz.nip19Bech32.addressIds
|
||||
import com.vitorpamplona.quartz.nip19Bech32.eventHints
|
||||
import com.vitorpamplona.quartz.nip19Bech32.eventIds
|
||||
import com.vitorpamplona.quartz.nip22Comments.RootScope
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.nip50Search.SearchableEvent
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
@Immutable
|
||||
class NipTextEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : BaseNoteEvent(id, pubKey, createdAt, KIND, tags, content, sig),
|
||||
AddressableEvent,
|
||||
RootScope,
|
||||
EventHintProvider,
|
||||
AddressHintProvider,
|
||||
SearchableEvent {
|
||||
override fun indexableContent() = "title: " + title() + "\n" + content
|
||||
|
||||
override fun dTag() = tags.dTag()
|
||||
|
||||
override fun aTag(relayHint: NormalizedRelayUrl?) = ATag(kind, pubKey, dTag(), relayHint)
|
||||
|
||||
override fun address() = Address(kind, pubKey, dTag())
|
||||
|
||||
override fun addressTag() = Address.assemble(kind, pubKey, dTag())
|
||||
|
||||
override fun eventHints(): List<EventIdHint> {
|
||||
val qHints = tags.mapNotNull(QTag::parseEventAsHint)
|
||||
val nip19Hints = citedNIP19().eventHints()
|
||||
|
||||
return qHints + nip19Hints
|
||||
}
|
||||
|
||||
override fun linkedEventIds(): List<HexKey> {
|
||||
val qHints = tags.mapNotNull(QTag::parseEventId)
|
||||
val nip19Hints = citedNIP19().eventIds()
|
||||
|
||||
return qHints + nip19Hints
|
||||
}
|
||||
|
||||
override fun addressHints(): List<AddressHint> {
|
||||
val aHints = tags.mapNotNull(ATag::parseAsHint)
|
||||
val qHints = tags.mapNotNull(QTag::parseAddressAsHint)
|
||||
val nip19Hints = citedNIP19().addressHints()
|
||||
|
||||
return aHints + qHints + nip19Hints
|
||||
}
|
||||
|
||||
override fun linkedAddressIds(): List<String> {
|
||||
val aHints = tags.mapNotNull(ATag::parseAddressId)
|
||||
val qHints = tags.mapNotNull(QTag::parseAddressId)
|
||||
val nip19Hints = citedNIP19().addressIds()
|
||||
|
||||
return aHints + qHints + nip19Hints
|
||||
}
|
||||
|
||||
fun isAFork() = tags.any { it.size > 3 && (it[0] == "a" || it[0] == "e") && it[3] == "fork" }
|
||||
|
||||
fun forkFromAddress() = tags.firstNotNullOfOrNull(ForkTag::parseAddress)
|
||||
|
||||
fun forkFromVersion() = tags.firstNotNullOfOrNull(MarkedETag::parseFork)
|
||||
|
||||
fun title() = tags.firstNotNullOfOrNull(TitleTag::parse)
|
||||
|
||||
fun summary() =
|
||||
content.lineSequence().drop(1).firstNotNullOfOrNull {
|
||||
if (it.isBlank() ||
|
||||
it.startsWith("---") ||
|
||||
it.startsWith("`draft") ||
|
||||
it.startsWith("#")
|
||||
) {
|
||||
null
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KIND = 30817
|
||||
const val KIND_STR = "30817"
|
||||
|
||||
@OptIn(ExperimentalUuidApi::class)
|
||||
fun build(
|
||||
description: String,
|
||||
title: String,
|
||||
kinds: List<Int>,
|
||||
dTag: String = Uuid.random().toString(),
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
initializer: TagArrayBuilder<NipTextEvent>.() -> Unit = {},
|
||||
) = eventTemplate(KIND, description, createdAt) {
|
||||
dTag(dTag)
|
||||
alt("NIP: $title")
|
||||
|
||||
title(title)
|
||||
kinds(kinds)
|
||||
|
||||
initializer()
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.nipsOnNostr
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.kinds.KindTag
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag
|
||||
|
||||
fun TagArrayBuilder<NipTextEvent>.title(title: String) = addUnique(TitleTag.assemble(title))
|
||||
|
||||
fun TagArrayBuilder<NipTextEvent>.kinds(kinds: List<Int>) = addAll(KindTag.assemble(kinds))
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.nipsOnNostr.tags
|
||||
|
||||
import com.vitorpamplona.quartz.experimental.nipsOnNostr.NipTextEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class ForkTag(
|
||||
val address: Address,
|
||||
val relayHint: NormalizedRelayUrl? = null,
|
||||
) {
|
||||
fun toTag() = Address.assemble(address.kind, address.pubKeyHex, address.dTag)
|
||||
|
||||
fun toTagArray() = assemble(address, relayHint)
|
||||
|
||||
fun toTagIdOnly() = assemble(address, null)
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "a"
|
||||
|
||||
fun isTagged(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && Address.isOfKind(tag[1], NipTextEvent.KIND_STR)
|
||||
|
||||
fun isTagged(
|
||||
tag: Array<String>,
|
||||
addressId: String,
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == addressId
|
||||
|
||||
fun isTagged(
|
||||
tag: Array<String>,
|
||||
address: ForkTag,
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == address.toTag()
|
||||
|
||||
fun isIn(
|
||||
tag: Array<String>,
|
||||
addressIds: Set<String>,
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in addressIds
|
||||
|
||||
fun parse(tag: Array<String>): ForkTag? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(
|
||||
Address.Companion.isOfKind(
|
||||
tag[1],
|
||||
CommunityDefinitionEvent.Companion.KIND_STR,
|
||||
),
|
||||
) { return null }
|
||||
|
||||
val address = Address.Companion.parse(tag[1]) ?: return null
|
||||
val relayHint = tag.getOrNull(2)?.let { RelayUrlNormalizer.Companion.normalizeOrNull(it) }
|
||||
return ForkTag(address, relayHint)
|
||||
}
|
||||
|
||||
fun parseValidAddress(tag: Array<String>): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(
|
||||
Address.Companion.isOfKind(
|
||||
tag[1],
|
||||
CommunityDefinitionEvent.Companion.KIND_STR,
|
||||
),
|
||||
) { return null }
|
||||
return Address.Companion.parse(tag[1])?.toValue()
|
||||
}
|
||||
|
||||
fun parseAddress(tag: Array<String>): Address? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
val address = Address.parse(tag[1]) ?: return null
|
||||
ensure(address.kind == NipTextEvent.KIND) { return null }
|
||||
return address
|
||||
}
|
||||
|
||||
fun parseAddressId(tag: Array<String>): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(
|
||||
Address.isOfKind(
|
||||
tag[1],
|
||||
NipTextEvent.KIND_STR,
|
||||
),
|
||||
) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
fun parseAsHint(tag: Array<String>): AddressHint? {
|
||||
ensure(tag.has(2)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(
|
||||
Address.isOfKind(
|
||||
tag[1],
|
||||
NipTextEvent.KIND_STR,
|
||||
),
|
||||
) { return null }
|
||||
ensure(tag[2].isNotEmpty()) { return null }
|
||||
|
||||
val relayHint = RelayUrlNormalizer.normalizeOrNull(tag[2])
|
||||
ensure(relayHint != null) { return null }
|
||||
|
||||
return AddressHint(tag[1], relayHint)
|
||||
}
|
||||
|
||||
fun assemble(
|
||||
aTagId: String,
|
||||
relay: NormalizedRelayUrl?,
|
||||
) = arrayOfNotNull(TAG_NAME, aTagId, relay?.url, "fork")
|
||||
|
||||
fun assemble(
|
||||
address: Address,
|
||||
relay: NormalizedRelayUrl?,
|
||||
) = assemble(address.toValue(), relay)
|
||||
|
||||
fun assemble(
|
||||
kind: Int,
|
||||
pubKey: String,
|
||||
dTag: String,
|
||||
relay: NormalizedRelayUrl?,
|
||||
) = assemble(Address.assemble(kind, pubKey, dTag), relay)
|
||||
}
|
||||
}
|
||||
+15
-14
@@ -18,21 +18,22 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.forks
|
||||
package com.vitorpamplona.quartz.experimental.nipsOnNostr.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
fun BaseThreadedEvent.isAFork() = tags.any { it.size > 3 && (it[0] == "a" || it[0] == "e") && it[3] == "fork" }
|
||||
class TitleTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "title"
|
||||
|
||||
fun BaseThreadedEvent.forkFromAddress() =
|
||||
tags.firstOrNull { it.size > 3 && it[0] == "a" && it[3] == "fork" }?.let {
|
||||
val aTagValue = it[1]
|
||||
Address.parse(aTagValue)
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
fun assemble(title: String) = arrayOf(TAG_NAME, title)
|
||||
}
|
||||
|
||||
fun BaseThreadedEvent.forkFromVersion() = tags.firstNotNullOfOrNull(MarkedETag::parseFork)
|
||||
|
||||
fun BaseThreadedEvent.isForkFromAddressWithPubkey(authorHex: HexKey) = tags.any { it.size > 3 && it[0] == "a" && it[3] == "fork" && it[1].contains(authorHex) }
|
||||
}
|
||||
+2
-1
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.quartz.nip10Notes
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.experimental.nipsOnNostr.NipTextEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.aTag.taggedATags
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers
|
||||
@@ -79,7 +80,7 @@ open class BaseThreadedEvent(
|
||||
val tagAddresses =
|
||||
taggedATags()
|
||||
.filter { aTag ->
|
||||
aTag.kind != CommunityDefinitionEvent.KIND && (kind != WikiNoteEvent.KIND || aTag.kind != WikiNoteEvent.KIND)
|
||||
aTag.kind != CommunityDefinitionEvent.KIND && (kind != WikiNoteEvent.KIND || aTag.kind != WikiNoteEvent.KIND) && (kind != NipTextEvent.KIND || aTag.kind != NipTextEvent.KIND)
|
||||
// removes forks from itself.
|
||||
}.map {
|
||||
it.toTag()
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.vitorpamplona.quartz.nip54Wiki
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.PublishedAtTag
|
||||
|
||||
fun TagArrayBuilder<LongTextNoteEvent>.title(title: String) = addUnique(TitleTag.assemble(title))
|
||||
|
||||
fun TagArrayBuilder<LongTextNoteEvent>.summary(summary: String) = addUnique(SummaryTag.assemble(summary))
|
||||
|
||||
fun TagArrayBuilder<LongTextNoteEvent>.image(imageUrl: String) = addUnique(ImageTag.assemble(imageUrl))
|
||||
|
||||
fun TagArrayBuilder<LongTextNoteEvent>.publishedAt(publishedAt: Long) = addUnique(PublishedAtTag.assemble(publishedAt))
|
||||
@@ -131,9 +131,7 @@ class WikiNoteEvent(
|
||||
fun image() = tags.firstOrNull { it.size > 1 && it[0] == "image" }?.get(1)
|
||||
|
||||
override fun publishedAt(): Long? {
|
||||
val publishedAt = tags.firstNotNullOfOrNull(PublishedAtTag::parse)
|
||||
|
||||
if (publishedAt == null) return null
|
||||
val publishedAt = tags.firstNotNullOfOrNull(PublishedAtTag::parse) ?: return null
|
||||
|
||||
// removes posts in the future.
|
||||
return if (publishedAt <= createdAt) {
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.vitorpamplona.quartz.experimental.medical.FhirResourceEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.experimental.nipsOnNostr.NipTextEvent
|
||||
import com.vitorpamplona.quartz.experimental.nns.NNSEvent
|
||||
import com.vitorpamplona.quartz.experimental.profileGallery.ProfileGalleryEntryEvent
|
||||
import com.vitorpamplona.quartz.experimental.publicMessages.PublicMessageEvent
|
||||
@@ -266,6 +267,7 @@ class EventFactory {
|
||||
MetadataEvent.KIND -> MetadataEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
MuteListEvent.KIND -> MuteListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
NNSEvent.KIND -> NNSEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
NipTextEvent.KIND -> NipTextEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
NostrConnectEvent.KIND -> NostrConnectEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
NIP90StatusEvent.KIND -> NIP90StatusEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
NIP90ContentDiscoveryRequestEvent.KIND -> NIP90ContentDiscoveryRequestEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
|
||||
Reference in New Issue
Block a user