- Adds support for https://github.com/nostr-protocol/nips/pull/1770
- Improves signer utils for testing - Improves test cases for User metadata
This commit is contained in:
@@ -24,7 +24,7 @@ import androidx.compose.runtime.Immutable
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.EventManualSerializer
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||
@@ -52,16 +52,19 @@ open class Event(
|
||||
|
||||
fun toJson(): String = EventManualSerializer.toJson(id, pubKey, createdAt, kind, tags, content, sig)
|
||||
|
||||
/**
|
||||
* For debug purposes only
|
||||
*/
|
||||
fun toPrettyJson(): String = EventManualSerializer.toPrettyJson(id, pubKey, createdAt, kind, tags, content, sig)
|
||||
|
||||
companion object {
|
||||
fun fromJson(json: String): Event = EventMapper.fromJson(json)
|
||||
|
||||
fun create(
|
||||
signer: NostrSigner,
|
||||
fun build(
|
||||
kind: Int,
|
||||
tags: Array<Array<String>> = emptyArray(),
|
||||
content: String = "",
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (Event) -> Unit,
|
||||
) = signer.sign(createdAt, kind, tags, content, onReady)
|
||||
initializer: TagArrayBuilder<Event>.() -> Unit = {},
|
||||
) = eventTemplate(kind, content, createdAt, initializer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.crypto
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
|
||||
|
||||
class DeterministicSigner(
|
||||
val key: KeyPair,
|
||||
val pubKey: HexKey = key.pubKey.toHexKey(),
|
||||
) {
|
||||
fun <T : Event> sign(
|
||||
createdAt: Long,
|
||||
kind: Int,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
): T = EventAssembler.hashAndSign(pubKey, createdAt, kind, tags, content, key.privKey!!, nonce = null)
|
||||
|
||||
fun <T : Event> sign(ev: EventTemplate<T>): T = sign(ev.createdAt, ev.kind, ev.tags, ev.content)
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.crypto
|
||||
|
||||
import com.vitorpamplona.quartz.EventFactory
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||
import com.vitorpamplona.quartz.utils.RandomInstance
|
||||
|
||||
class EventAssembler {
|
||||
companion object {
|
||||
fun <T : Event> hashAndSign(
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
kind: Int,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
privKey: ByteArray,
|
||||
nonce: ByteArray? = RandomInstance.bytes(32),
|
||||
): T {
|
||||
val id = EventHasher.hashIdBytes(pubKey, createdAt, kind, tags, content)
|
||||
val sig = Nip01.sign(id, privKey, nonce).toHexKey()
|
||||
|
||||
return EventFactory.create(
|
||||
id.toHexKey(),
|
||||
pubKey,
|
||||
createdAt,
|
||||
kind,
|
||||
tags,
|
||||
content,
|
||||
sig,
|
||||
) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
+49
-22
@@ -21,10 +21,42 @@
|
||||
package com.vitorpamplona.quartz.nip01Core.jackson
|
||||
|
||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
|
||||
class EventManualSerializer {
|
||||
companion object {
|
||||
private fun assemble(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
kind: Int,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
sig: String,
|
||||
): ObjectNode {
|
||||
val factory = JsonNodeFactory.instance
|
||||
|
||||
return factory.objectNode().apply {
|
||||
put("id", id)
|
||||
put("pubkey", pubKey)
|
||||
put("created_at", createdAt)
|
||||
put("kind", kind)
|
||||
replace(
|
||||
"tags",
|
||||
factory.arrayNode(tags.size).apply {
|
||||
tags.forEach { tag ->
|
||||
add(
|
||||
factory.arrayNode(tag.size).apply { tag.forEach { add(it) } },
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
put("content", content)
|
||||
put("sig", sig)
|
||||
}
|
||||
}
|
||||
|
||||
fun toJson(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
@@ -34,29 +66,24 @@ class EventManualSerializer {
|
||||
content: String,
|
||||
sig: String,
|
||||
): String {
|
||||
val factory = JsonNodeFactory.instance
|
||||
|
||||
val obj =
|
||||
factory.objectNode().apply {
|
||||
put("id", id)
|
||||
put("pubkey", pubKey)
|
||||
put("created_at", createdAt)
|
||||
put("kind", kind)
|
||||
replace(
|
||||
"tags",
|
||||
factory.arrayNode(tags.size).apply {
|
||||
tags.forEach { tag ->
|
||||
add(
|
||||
factory.arrayNode(tag.size).apply { tag.forEach { add(it) } },
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
put("content", content)
|
||||
put("sig", sig)
|
||||
}
|
||||
|
||||
val obj = assemble(id, pubKey, createdAt, kind, tags, content, sig)
|
||||
return EventMapper.mapper.writeValueAsString(obj)
|
||||
}
|
||||
|
||||
/**
|
||||
* For debug purposes only
|
||||
*/
|
||||
fun toPrettyJson(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
kind: Int,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
sig: String,
|
||||
): String {
|
||||
val obj = assemble(id, pubKey, createdAt, kind, tags, content, sig)
|
||||
return EventMapper.mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,10 +41,13 @@ import com.vitorpamplona.quartz.nip59Giftwrap.rumors.RumorSerializer
|
||||
|
||||
class EventMapper {
|
||||
companion object {
|
||||
val defaultPrettyPrinter = InliningTagArrayPrettyPrinter()
|
||||
|
||||
val mapper =
|
||||
jacksonObjectMapper()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
.enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature())
|
||||
.setDefaultPrettyPrinter(defaultPrettyPrinter)
|
||||
.registerModule(
|
||||
SimpleModule()
|
||||
.addSerializer(Event::class.java, EventSerializer())
|
||||
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.jackson
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator
|
||||
import com.fasterxml.jackson.core.util.DefaultIndenter
|
||||
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter
|
||||
import com.fasterxml.jackson.core.util.Separators
|
||||
|
||||
class InliningTagArrayPrettyPrinter : DefaultPrettyPrinter {
|
||||
companion object {
|
||||
val MY_SEPARATORS =
|
||||
DEFAULT_SEPARATORS
|
||||
.withObjectFieldValueSpacing(Separators.Spacing.AFTER)
|
||||
}
|
||||
|
||||
init {
|
||||
indentArraysWith(DefaultIndenter(" ", "\n"))
|
||||
}
|
||||
|
||||
constructor(separators: Separators? = MY_SEPARATORS) : super(separators)
|
||||
|
||||
constructor(base: InliningTagArrayPrettyPrinter) : super(base)
|
||||
|
||||
override fun createInstance(): DefaultPrettyPrinter = InliningTagArrayPrettyPrinter(this)
|
||||
|
||||
override fun writeStartArray(g: JsonGenerator) {
|
||||
if (!_arrayIndenter.isInline) {
|
||||
++_nesting
|
||||
}
|
||||
g.writeRaw('[')
|
||||
}
|
||||
|
||||
override fun beforeArrayValues(g: JsonGenerator) {
|
||||
if (_nesting < 3) {
|
||||
_arrayIndenter.writeIndentation(g, _nesting)
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeArrayValueSeparator(g: JsonGenerator) {
|
||||
g.writeRaw(_arrayValueSeparator)
|
||||
if (_nesting < 3) {
|
||||
_arrayIndenter.writeIndentation(g, _nesting)
|
||||
} else {
|
||||
g.writeRaw(' ')
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeEndArray(
|
||||
g: JsonGenerator,
|
||||
nrOfValues: Int,
|
||||
) {
|
||||
if (!_arrayIndenter.isInline) {
|
||||
--_nesting
|
||||
}
|
||||
if (nrOfValues > 0) {
|
||||
if (_nesting < 2) {
|
||||
_arrayIndenter.writeIndentation(g, _nesting)
|
||||
}
|
||||
}
|
||||
g.writeRaw(']')
|
||||
}
|
||||
}
|
||||
+142
-71
@@ -21,19 +21,35 @@
|
||||
package com.vitorpamplona.quartz.nip01Core.metadata
|
||||
|
||||
import android.util.Log
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
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.core.builder
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.AboutTag
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.BannerTag
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.DisplayNameTag
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.Lud06Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.Lud16Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.NameTag
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.Nip05Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.PictureTag
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.PronounsTag
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.WebsiteTag
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip21UriScheme.toNostrUri
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||
import com.vitorpamplona.quartz.nip39ExtIdentities.updateClaims
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.nip39ExtIdentities.IdentityClaimTag
|
||||
import com.vitorpamplona.quartz.nip39ExtIdentities.claims
|
||||
import com.vitorpamplona.quartz.nip39ExtIdentities.githubClaim
|
||||
import com.vitorpamplona.quartz.nip39ExtIdentities.mastodonClaim
|
||||
import com.vitorpamplona.quartz.nip39ExtIdentities.replaceClaims
|
||||
import com.vitorpamplona.quartz.nip39ExtIdentities.twitterClaim
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.StringWriter
|
||||
|
||||
class MetadataEvent(
|
||||
id: HexKey,
|
||||
@@ -43,11 +59,12 @@ class MetadataEvent(
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
fun contactMetadataJson() = jacksonObjectMapper().readTree(content) as? ObjectNode
|
||||
|
||||
fun contactMetaData() =
|
||||
try {
|
||||
EventMapper.mapper.readValue(content, UserMetadata::class.java)
|
||||
} catch (e: Exception) {
|
||||
// e.printStackTrace()
|
||||
Log.w("MetadataEvent", "Content Parse Error: ${toNostrUri()} ${e.localizedMessage}")
|
||||
null
|
||||
}
|
||||
@@ -57,76 +74,130 @@ class MetadataEvent(
|
||||
|
||||
fun newUser(
|
||||
name: String?,
|
||||
signer: NostrSignerSync,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
): MetadataEvent? {
|
||||
// Tries to not delete any existing attribute that we do not work with.
|
||||
val currentJson = ObjectMapper().createObjectNode()
|
||||
|
||||
name?.let { addIfNotBlank(currentJson, "name", it.trim()) }
|
||||
val writer = StringWriter()
|
||||
ObjectMapper().writeValue(writer, currentJson)
|
||||
|
||||
val tags = mutableListOf<Array<String>>()
|
||||
|
||||
tags.add(
|
||||
AltTag.assemble("User profile for ${name ?: currentJson.get("name").asText() ?: ""}"),
|
||||
)
|
||||
|
||||
return signer.sign(createdAt, KIND, tags.toTypedArray(), writer.buffer.toString())
|
||||
initializer: TagArrayBuilder<MetadataEvent>.() -> Unit = {},
|
||||
): EventTemplate<MetadataEvent> {
|
||||
val metadata = JsonNodeFactory.instance.objectNode()
|
||||
name?.let { addIfNotBlank(metadata, "name", it.trim()) }
|
||||
return eventTemplate(KIND, metadata.toString(), createdAt) {
|
||||
alt("User profile for $name")
|
||||
initializer()
|
||||
}
|
||||
}
|
||||
|
||||
fun updateFromPast(
|
||||
latest: MetadataEvent?,
|
||||
name: String?,
|
||||
picture: String?,
|
||||
banner: String?,
|
||||
website: String?,
|
||||
about: String?,
|
||||
nip05: String?,
|
||||
lnAddress: String?,
|
||||
lnURL: String?,
|
||||
pronouns: String?,
|
||||
twitter: String?,
|
||||
mastodon: String?,
|
||||
github: String?,
|
||||
signer: NostrSigner,
|
||||
fun createNew(
|
||||
name: String? = null,
|
||||
displayName: String? = null,
|
||||
picture: String? = null,
|
||||
banner: String? = null,
|
||||
website: String? = null,
|
||||
about: String? = null,
|
||||
nip05: String? = null,
|
||||
lnAddress: String? = null,
|
||||
lnURL: String? = null,
|
||||
pronouns: String? = null,
|
||||
twitter: String? = null,
|
||||
mastodon: String? = null,
|
||||
github: String? = null,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (MetadataEvent) -> Unit,
|
||||
) {
|
||||
initializer: TagArrayBuilder<MetadataEvent>.() -> Unit = {},
|
||||
): EventTemplate<MetadataEvent> {
|
||||
// Tries to not delete any existing attribute that we do not work with.
|
||||
val currentJson =
|
||||
if (latest != null) {
|
||||
ObjectMapper()
|
||||
.readTree(
|
||||
ByteArrayInputStream(latest.content.toByteArray(Charsets.UTF_8)),
|
||||
) as ObjectNode
|
||||
} else {
|
||||
ObjectMapper().createObjectNode()
|
||||
val currentMetadata = JsonNodeFactory.instance.objectNode()
|
||||
|
||||
name?.let { addIfNotBlank(currentMetadata, NameTag.TAG_NAME, it.trim()) }
|
||||
displayName?.let { addIfNotBlank(currentMetadata, DisplayNameTag.TAG_NAME, it.trim()) }
|
||||
picture?.let { addIfNotBlank(currentMetadata, PictureTag.TAG_NAME, it.trim()) }
|
||||
banner?.let { addIfNotBlank(currentMetadata, BannerTag.TAG_NAME, it.trim()) }
|
||||
website?.let { addIfNotBlank(currentMetadata, WebsiteTag.TAG_NAME, it.trim()) }
|
||||
pronouns?.let { addIfNotBlank(currentMetadata, PronounsTag.TAG_NAME, it.trim()) }
|
||||
about?.let { addIfNotBlank(currentMetadata, AboutTag.TAG_NAME, it.trim()) }
|
||||
nip05?.let { addIfNotBlank(currentMetadata, Nip05Tag.TAG_NAME, it.trim()) }
|
||||
lnAddress?.let { addIfNotBlank(currentMetadata, Lud16Tag.TAG_NAME, it.trim()) }
|
||||
lnURL?.let { addIfNotBlank(currentMetadata, Lud06Tag.TAG_NAME, it.trim()) }
|
||||
|
||||
return eventTemplate(KIND, currentMetadata.toString(), createdAt) {
|
||||
alt("User profile for ${currentMetadata.get("name").asText() ?: "Anonymous"}")
|
||||
|
||||
// For https://github.com/nostr-protocol/nips/pull/1770
|
||||
currentMetadata.get(NameTag.TAG_NAME)?.asText()?.let { name(it) }
|
||||
currentMetadata.get(DisplayNameTag.TAG_NAME)?.asText()?.let { displayName(it) }
|
||||
currentMetadata.get(PictureTag.TAG_NAME)?.asText()?.let { picture(it) }
|
||||
currentMetadata.get(BannerTag.TAG_NAME)?.asText()?.let { banner(it) }
|
||||
currentMetadata.get(WebsiteTag.TAG_NAME)?.asText()?.let { website(it) }
|
||||
currentMetadata.get(PronounsTag.TAG_NAME)?.asText()?.let { pronouns(it) }
|
||||
currentMetadata.get(AboutTag.TAG_NAME)?.asText()?.let { about(it) }
|
||||
currentMetadata.get(Nip05Tag.TAG_NAME)?.asText()?.let { nip05(it) }
|
||||
currentMetadata.get(Lud16Tag.TAG_NAME)?.asText()?.let { lud16(it) }
|
||||
currentMetadata.get(Lud06Tag.TAG_NAME)?.asText()?.let { lud06(it) }
|
||||
|
||||
twitter?.let { twitterClaim(it) }
|
||||
?: mastodon?.let { mastodonClaim(it) }
|
||||
github?.let { githubClaim(it) }
|
||||
|
||||
initializer()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates fields from the latest Metadata Event. Null params remain unchanged. Empty params get deleted.
|
||||
*/
|
||||
fun updateFromPast(
|
||||
latest: MetadataEvent,
|
||||
name: String? = null,
|
||||
displayName: String? = null,
|
||||
picture: String? = null,
|
||||
banner: String? = null,
|
||||
website: String? = null,
|
||||
about: String? = null,
|
||||
nip05: String? = null,
|
||||
lnAddress: String? = null,
|
||||
lnURL: String? = null,
|
||||
pronouns: String? = null,
|
||||
twitter: String? = null,
|
||||
mastodon: String? = null,
|
||||
github: String? = null,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
initializer: TagArrayBuilder<MetadataEvent>.() -> Unit = {},
|
||||
): EventTemplate<MetadataEvent> {
|
||||
// Tries to not delete any existing attribute that we do not work with.
|
||||
val currentMetadata = latest.contactMetadataJson() ?: JsonNodeFactory.instance.objectNode()
|
||||
|
||||
name?.let { addIfNotBlank(currentMetadata, NameTag.TAG_NAME, it.trim()) }
|
||||
displayName?.let { addIfNotBlank(currentMetadata, DisplayNameTag.TAG_NAME, it.trim()) }
|
||||
picture?.let { addIfNotBlank(currentMetadata, PictureTag.TAG_NAME, it.trim()) }
|
||||
banner?.let { addIfNotBlank(currentMetadata, BannerTag.TAG_NAME, it.trim()) }
|
||||
website?.let { addIfNotBlank(currentMetadata, WebsiteTag.TAG_NAME, it.trim()) }
|
||||
pronouns?.let { addIfNotBlank(currentMetadata, PronounsTag.TAG_NAME, it.trim()) }
|
||||
about?.let { addIfNotBlank(currentMetadata, AboutTag.TAG_NAME, it.trim()) }
|
||||
nip05?.let { addIfNotBlank(currentMetadata, Nip05Tag.TAG_NAME, it.trim()) }
|
||||
lnAddress?.let { addIfNotBlank(currentMetadata, Lud16Tag.TAG_NAME, it.trim()) }
|
||||
lnURL?.let { addIfNotBlank(currentMetadata, Lud06Tag.TAG_NAME, it.trim()) }
|
||||
|
||||
val tags =
|
||||
latest.tags.builder {
|
||||
alt("User profile for ${currentMetadata.get("name").asText() ?: "Anonymous"}")
|
||||
|
||||
// For https://github.com/nostr-protocol/nips/pull/1770
|
||||
currentMetadata.get(NameTag.TAG_NAME)?.asText()?.let { name(it) } ?: run { remove(NameTag.TAG_NAME) }
|
||||
currentMetadata.get(DisplayNameTag.TAG_NAME)?.asText()?.let { displayName(it) } ?: run { remove(DisplayNameTag.TAG_NAME) }
|
||||
currentMetadata.get(PictureTag.TAG_NAME)?.asText()?.let { picture(it) } ?: run { remove(PictureTag.TAG_NAME) }
|
||||
currentMetadata.get(BannerTag.TAG_NAME)?.asText()?.let { banner(it) } ?: run { remove(BannerTag.TAG_NAME) }
|
||||
currentMetadata.get(WebsiteTag.TAG_NAME)?.asText()?.let { website(it) } ?: run { remove(WebsiteTag.TAG_NAME) }
|
||||
currentMetadata.get(PronounsTag.TAG_NAME)?.asText()?.let { pronouns(it) } ?: run { remove(PronounsTag.TAG_NAME) }
|
||||
currentMetadata.get(AboutTag.TAG_NAME)?.asText()?.let { about(it) } ?: run { remove(AboutTag.TAG_NAME) }
|
||||
currentMetadata.get(Nip05Tag.TAG_NAME)?.asText()?.let { nip05(it) } ?: run { remove(Nip05Tag.TAG_NAME) }
|
||||
currentMetadata.get(Lud16Tag.TAG_NAME)?.asText()?.let { lud16(it) } ?: run { remove(Lud16Tag.TAG_NAME) }
|
||||
currentMetadata.get(Lud06Tag.TAG_NAME)?.asText()?.let { lud06(it) } ?: run { remove(Lud06Tag.TAG_NAME) }
|
||||
|
||||
val newClaims = latest.replaceClaims(twitter, mastodon, github)
|
||||
remove(IdentityClaimTag.TAG_NAME)
|
||||
claims(newClaims)
|
||||
|
||||
initializer()
|
||||
}
|
||||
|
||||
name?.let { addIfNotBlank(currentJson, "name", it.trim()) }
|
||||
name?.let { addIfNotBlank(currentJson, "display_name", it.trim()) }
|
||||
picture?.let { addIfNotBlank(currentJson, "picture", it.trim()) }
|
||||
banner?.let { addIfNotBlank(currentJson, "banner", it.trim()) }
|
||||
website?.let { addIfNotBlank(currentJson, "website", it.trim()) }
|
||||
pronouns?.let { addIfNotBlank(currentJson, "pronouns", it.trim()) }
|
||||
about?.let { addIfNotBlank(currentJson, "about", it.trim()) }
|
||||
nip05?.let { addIfNotBlank(currentJson, "nip05", it.trim()) }
|
||||
lnAddress?.let { addIfNotBlank(currentJson, "lud16", it.trim()) }
|
||||
lnURL?.let { addIfNotBlank(currentJson, "lud06", it.trim()) }
|
||||
|
||||
val writer = StringWriter()
|
||||
ObjectMapper().writeValue(writer, currentJson)
|
||||
|
||||
val tags = mutableListOf<Array<String>>()
|
||||
tags.add(AltTag.assemble("User profile for ${name ?: currentJson.get("name").asText() ?: ""}"))
|
||||
|
||||
latest?.updateClaims(twitter, github, mastodon)?.forEach {
|
||||
tags.add(it)
|
||||
}
|
||||
|
||||
signer.sign(createdAt, KIND, tags.toTypedArray(), writer.buffer.toString(), onReady)
|
||||
return EventTemplate(createdAt, KIND, tags, currentMetadata.toString())
|
||||
}
|
||||
|
||||
private fun addIfNotBlank(
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.metadata
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.AboutTag
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.BannerTag
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.DisplayNameTag
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.Lud06Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.Lud16Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.NameTag
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.Nip05Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.PictureTag
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.PronounsTag
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.tags.WebsiteTag
|
||||
|
||||
fun TagArrayBuilder<MetadataEvent>.name(name: String) = addUnique(NameTag.assemble(name))
|
||||
|
||||
fun TagArrayBuilder<MetadataEvent>.displayName(name: String) = addUnique(DisplayNameTag.assemble(name))
|
||||
|
||||
fun TagArrayBuilder<MetadataEvent>.picture(picture: String) = addUnique(PictureTag.assemble(picture))
|
||||
|
||||
fun TagArrayBuilder<MetadataEvent>.about(about: String) = addUnique(AboutTag.assemble(about))
|
||||
|
||||
fun TagArrayBuilder<MetadataEvent>.website(url: String) = addUnique(WebsiteTag.assemble(url))
|
||||
|
||||
fun TagArrayBuilder<MetadataEvent>.nip05(nip05: String) = addUnique(Nip05Tag.assemble(nip05))
|
||||
|
||||
fun TagArrayBuilder<MetadataEvent>.lud16(lud16: String) = addUnique(Lud16Tag.assemble(lud16))
|
||||
|
||||
fun TagArrayBuilder<MetadataEvent>.lud06(lud06: String) = addUnique(Lud06Tag.assemble(lud06))
|
||||
|
||||
fun TagArrayBuilder<MetadataEvent>.banner(banner: String) = addUnique(BannerTag.assemble(banner))
|
||||
|
||||
fun TagArrayBuilder<MetadataEvent>.pronouns(pronouns: String) = addUnique(PronounsTag.assemble(pronouns))
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.metadata.tags
|
||||
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class AboutTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "about"
|
||||
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.size > 1) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
fun assemble(name: String) = arrayOf(TAG_NAME, name)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.metadata.tags
|
||||
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class BannerTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "banner"
|
||||
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.size > 1) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
fun assemble(name: String) = arrayOf(TAG_NAME, name)
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.metadata.tags
|
||||
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class DisplayNameTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "display_name"
|
||||
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.size > 1) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
fun assemble(name: String) = arrayOf(TAG_NAME, name)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.metadata.tags
|
||||
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class Lud06Tag {
|
||||
companion object {
|
||||
const val TAG_NAME = "lud06"
|
||||
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.size > 1) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
fun assemble(name: String) = arrayOf(TAG_NAME, name)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.metadata.tags
|
||||
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class Lud16Tag {
|
||||
companion object {
|
||||
const val TAG_NAME = "lud16"
|
||||
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.size > 1) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
fun assemble(name: String) = arrayOf(TAG_NAME, name)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.metadata.tags
|
||||
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class NameTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "name"
|
||||
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.size > 1) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
fun assemble(name: String) = arrayOf(TAG_NAME, name)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.metadata.tags
|
||||
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class Nip05Tag {
|
||||
companion object {
|
||||
const val TAG_NAME = "nip05"
|
||||
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.size > 1) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
fun assemble(name: String) = arrayOf(TAG_NAME, name)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.metadata.tags
|
||||
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class PictureTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "picture"
|
||||
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.size > 1) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
fun assemble(name: String) = arrayOf(TAG_NAME, name)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.metadata.tags
|
||||
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class PronounsTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "pronouns"
|
||||
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.size > 1) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
fun assemble(name: String) = arrayOf(TAG_NAME, name)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.metadata.tags
|
||||
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class WebsiteTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "website"
|
||||
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.size > 1) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
fun assemble(name: String) = arrayOf(TAG_NAME, name)
|
||||
}
|
||||
}
|
||||
@@ -21,14 +21,12 @@
|
||||
package com.vitorpamplona.quartz.nip01Core.signers
|
||||
|
||||
import android.util.Log
|
||||
import com.vitorpamplona.quartz.EventFactory
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.EventHasher
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.EventAssembler
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.nip04Dm.crypto.Nip04
|
||||
import com.vitorpamplona.quartz.nip44Encryption.Nip44
|
||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapPrivateEvent
|
||||
@@ -36,7 +34,7 @@ import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
|
||||
import com.vitorpamplona.quartz.nip57Zaps.PrivateZapRequestBuilder
|
||||
|
||||
class NostrSignerSync(
|
||||
val keyPair: KeyPair,
|
||||
val keyPair: KeyPair = KeyPair(),
|
||||
val pubKey: HexKey = keyPair.pubKey.toHexKey(),
|
||||
) {
|
||||
fun <T : Event> sign(ev: EventTemplate<T>) = signNormal<T>(ev.createdAt, ev.kind, ev.tags, ev.content)
|
||||
@@ -72,18 +70,7 @@ class NostrSignerSync(
|
||||
): T? {
|
||||
if (keyPair.privKey == null) return null
|
||||
|
||||
val id = EventHasher.hashIdBytes(pubKey, createdAt, kind, tags, content)
|
||||
val sig = Nip01.sign(id, keyPair.privKey).toHexKey()
|
||||
|
||||
return EventFactory.create(
|
||||
id.toHexKey(),
|
||||
pubKey,
|
||||
createdAt,
|
||||
kind,
|
||||
tags,
|
||||
content,
|
||||
sig,
|
||||
) as T
|
||||
return EventAssembler.hashAndSign<T>(pubKey, createdAt, kind, tags, content, keyPair.privKey)
|
||||
}
|
||||
|
||||
fun nip04Encrypt(
|
||||
|
||||
@@ -20,26 +20,15 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip39ExtIdentities
|
||||
|
||||
import android.util.Log
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||
import com.vitorpamplona.quartz.nip01Core.core.mapTagged
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||
|
||||
fun MetadataEvent.identityClaims() =
|
||||
tags.mapTagged("i") {
|
||||
try {
|
||||
IdentityClaim.create(it[1], it[2])
|
||||
} catch (e: Exception) {
|
||||
Log.e("MetadataEvent", "Can't parse identity [${it.joinToString { "," }}]", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
fun MetadataEvent.identityClaims() = tags.mapNotNull(IdentityClaimTag::parse)
|
||||
|
||||
fun MetadataEvent.updateClaims(
|
||||
fun MetadataEvent.replaceClaims(
|
||||
twitter: String?,
|
||||
mastodon: String?,
|
||||
github: String?,
|
||||
): TagArray {
|
||||
): List<IdentityClaimTag> {
|
||||
var claims = identityClaims()
|
||||
|
||||
// null leave as is. blank deletes it.
|
||||
@@ -68,5 +57,5 @@ fun MetadataEvent.updateClaims(
|
||||
}
|
||||
}
|
||||
|
||||
return claims.map { arrayOf("i", it.platformIdentity(), it.proof) }.toTypedArray()
|
||||
return claims
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ package com.vitorpamplona.quartz.nip39ExtIdentities
|
||||
class GitHubIdentity(
|
||||
identity: String,
|
||||
proof: String,
|
||||
) : IdentityClaim(identity, proof) {
|
||||
) : IdentityClaimTag(identity, proof) {
|
||||
override fun toProofUrl() = "https://gist.github.com/$identity/$proof"
|
||||
|
||||
override fun platform() = platform
|
||||
|
||||
+27
-3
@@ -20,10 +20,12 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip39ExtIdentities
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
@Stable
|
||||
abstract class IdentityClaim(
|
||||
abstract class IdentityClaimTag(
|
||||
val identity: String,
|
||||
val proof: String,
|
||||
) {
|
||||
@@ -31,13 +33,17 @@ abstract class IdentityClaim(
|
||||
|
||||
abstract fun platform(): String
|
||||
|
||||
fun toTagArray() = assemble(platformIdentity(), proof)
|
||||
|
||||
fun platformIdentity() = "${platform()}:$identity"
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "i"
|
||||
|
||||
fun create(
|
||||
platformIdentity: String,
|
||||
proof: String,
|
||||
): IdentityClaim {
|
||||
): IdentityClaimTag {
|
||||
val (platform, identity) = platformIdentity.split(':')
|
||||
|
||||
return when (platform.lowercase()) {
|
||||
@@ -45,8 +51,26 @@ abstract class IdentityClaim(
|
||||
TwitterIdentity.platform -> TwitterIdentity(identity, proof)
|
||||
TelegramIdentity.platform -> TelegramIdentity(identity, proof)
|
||||
MastodonIdentity.platform -> MastodonIdentity(identity, proof)
|
||||
else -> throw IllegalArgumentException("Platform $platform not supported")
|
||||
else -> UnsupportedIdentity(platform, identity, proof)
|
||||
}
|
||||
}
|
||||
|
||||
fun parse(tag: Array<String>): IdentityClaimTag? {
|
||||
ensure(tag.size > 2) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
ensure(tag[2].isNotEmpty()) { return null }
|
||||
return try {
|
||||
create(tag[1], tag[2])
|
||||
} catch (e: Exception) {
|
||||
Log.e("IdentityClaim", "Can't parse identity [${tag.joinToString { "," }}]", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun assemble(
|
||||
platformIdentity: String,
|
||||
proof: String,
|
||||
): Array<String> = arrayOf(TAG_NAME, platformIdentity, proof)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -23,7 +23,7 @@ package com.vitorpamplona.quartz.nip39ExtIdentities
|
||||
class MastodonIdentity(
|
||||
identity: String,
|
||||
proof: String,
|
||||
) : IdentityClaim(identity, proof) {
|
||||
) : IdentityClaimTag(identity, proof) {
|
||||
override fun toProofUrl() = "https://$identity/$proof"
|
||||
|
||||
override fun platform() = platform
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.nip39ExtIdentities
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||
|
||||
fun TagArrayBuilder<MetadataEvent>.claims(identities: List<IdentityClaimTag>) = addAll(identities.map { it.toTagArray() })
|
||||
|
||||
fun TagArrayBuilder<MetadataEvent>.twitterClaim(twitter: TwitterIdentity) = add(twitter.toTagArray())
|
||||
|
||||
fun TagArrayBuilder<MetadataEvent>.mastodonClaim(mastodon: MastodonIdentity) = add(mastodon.toTagArray())
|
||||
|
||||
fun TagArrayBuilder<MetadataEvent>.githubClaim(github: GitHubIdentity) = add(github.toTagArray())
|
||||
|
||||
fun TagArrayBuilder<MetadataEvent>.twitterClaim(twitterUrl: String) = TwitterIdentity.parseProofUrl(twitterUrl)?.let { twitterClaim(it) }
|
||||
|
||||
fun TagArrayBuilder<MetadataEvent>.mastodonClaim(mastodonUrl: String) = MastodonIdentity.parseProofUrl(mastodonUrl)?.let { mastodonClaim(it) }
|
||||
|
||||
fun TagArrayBuilder<MetadataEvent>.githubClaim(githubUrl: String) = GitHubIdentity.parseProofUrl(githubUrl)?.let { githubClaim(it) }
|
||||
+1
-1
@@ -23,7 +23,7 @@ package com.vitorpamplona.quartz.nip39ExtIdentities
|
||||
class TelegramIdentity(
|
||||
identity: String,
|
||||
proof: String,
|
||||
) : IdentityClaim(identity, proof) {
|
||||
) : IdentityClaimTag(identity, proof) {
|
||||
override fun toProofUrl() = "https://t.me/$proof"
|
||||
|
||||
override fun platform() = platform
|
||||
|
||||
@@ -23,7 +23,7 @@ package com.vitorpamplona.quartz.nip39ExtIdentities
|
||||
class TwitterIdentity(
|
||||
identity: String,
|
||||
proof: String,
|
||||
) : IdentityClaim(identity, proof) {
|
||||
) : IdentityClaimTag(identity, proof) {
|
||||
override fun toProofUrl() = "https://x.com/$identity/status/$proof"
|
||||
|
||||
override fun platform() = platform
|
||||
|
||||
+31
@@ -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.nip39ExtIdentities
|
||||
|
||||
class UnsupportedIdentity(
|
||||
val platform: String,
|
||||
identity: String,
|
||||
proof: String,
|
||||
) : IdentityClaimTag(identity, proof) {
|
||||
override fun toProofUrl() = "Unsupported Identity"
|
||||
|
||||
override fun platform() = platform
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.jackson
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class InliningTagArrayPrettyPrinterTest {
|
||||
val mapper =
|
||||
jacksonObjectMapper().apply {
|
||||
setDefaultPrettyPrinter(InliningTagArrayPrettyPrinter())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test() {
|
||||
val writer = mapper.writerWithDefaultPrettyPrinter()
|
||||
|
||||
val data =
|
||||
mapOf(
|
||||
"tags" to arrayOf(intArrayOf(1, 2, 3), intArrayOf(4, 5, 6), intArrayOf(7, 8, 9)),
|
||||
)
|
||||
val expected =
|
||||
"""
|
||||
{
|
||||
"tags": [
|
||||
[1, 2, 3],
|
||||
[4, 5, 6],
|
||||
[7, 8, 9]
|
||||
]
|
||||
}
|
||||
""".trimIndent()
|
||||
val json = writer.writeValueAsString(data)
|
||||
assertEquals(expected, json)
|
||||
|
||||
val data2 =
|
||||
mapOf(
|
||||
"tags" to arrayOf(arrayOf(intArrayOf(1, 2), intArrayOf(3, 4)), arrayOf(intArrayOf(5, 6), intArrayOf(7, 8))),
|
||||
)
|
||||
val expected2 =
|
||||
"""
|
||||
{
|
||||
"tags": [
|
||||
[[1, 2], [3, 4]],
|
||||
[[5, 6], [7, 8]]
|
||||
]
|
||||
}
|
||||
""".trimIndent()
|
||||
val json2 = writer.writeValueAsString(data2)
|
||||
assertEquals(expected2, json2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEvent() {
|
||||
val nostrObject =
|
||||
"""
|
||||
{
|
||||
"id": "490d7439e530423f2540d4f2bdb73a0a2935f3df9e1f2a6f699a140c7db311fe",
|
||||
"pubkey": "70a9b3c312a6b83e476739bd29d60ca700da1d5b982cbca87b5f3d27d4038d67",
|
||||
"created_at": 1740669816,
|
||||
"kind": 0,
|
||||
"tags": [
|
||||
["alt", "User profile for Vitor"],
|
||||
["name", "Vitor"]
|
||||
],
|
||||
"content": "{\"name\":\"Vitor\"}",
|
||||
"sig": "977a6152199f17d103d8d56736ed1b7767054464cf9423d017c01c8cdd2344698f0a5e13da8dff98d01bb1f798837e3b6271e1fd1cac861bb90686f622ae6ef4"
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val tree = mapper.readTree(nostrObject)
|
||||
|
||||
val prettified = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(tree)
|
||||
|
||||
assertEquals(nostrObject, prettified)
|
||||
}
|
||||
}
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.metadata
|
||||
|
||||
import com.vitorpamplona.quartz.utils.nsecToSigner
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class UpdateMetadataTest {
|
||||
val signer = "nsec10g0wheggqn9dawlc0yuv6adnat6n09anr7eyykevw2dm8xa5fffs0wsdsr".nsecToSigner()
|
||||
|
||||
@Test
|
||||
fun createNewMetadata() {
|
||||
val test = signer.sign(MetadataEvent.createNew("Vitor", createdAt = 1740669816))
|
||||
|
||||
val expected =
|
||||
"""
|
||||
{
|
||||
"id": "490d7439e530423f2540d4f2bdb73a0a2935f3df9e1f2a6f699a140c7db311fe",
|
||||
"pubkey": "70a9b3c312a6b83e476739bd29d60ca700da1d5b982cbca87b5f3d27d4038d67",
|
||||
"created_at": 1740669816,
|
||||
"kind": 0,
|
||||
"tags": [
|
||||
["alt", "User profile for Vitor"],
|
||||
["name", "Vitor"]
|
||||
],
|
||||
"content": "{\"name\":\"Vitor\"}",
|
||||
"sig": "977a6152199f17d103d8d56736ed1b7767054464cf9423d017c01c8cdd2344698f0a5e13da8dff98d01bb1f798837e3b6271e1fd1cac861bb90686f622ae6ef4"
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
assertEquals(expected, test.toPrettyJson())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateMetadata() {
|
||||
val test =
|
||||
signer.sign(
|
||||
MetadataEvent.createNew(
|
||||
name = "Vitor",
|
||||
displayName = "Vitor Pamplona",
|
||||
about = "Nostr's Chief Android Officer - #Amethyst",
|
||||
picture = "https://vitorpamplona.com/images/me_300.jpg",
|
||||
banner = "https://pbs.twimg.com/profile_banners/15064756/1414451651/1080x360",
|
||||
pronouns = "he/him",
|
||||
website = "https://vitorpamplona.com",
|
||||
nip05 = "_@vitorpamplona.com",
|
||||
lnAddress = "vitor@vitorpamplona.com",
|
||||
lnURL = "TEST",
|
||||
github = "https://gist.github.com/vitorpamplona/cf19e2d1d7f8dac6348ad37b35ec8421",
|
||||
createdAt = 1740669816,
|
||||
),
|
||||
)
|
||||
|
||||
val expected =
|
||||
"""
|
||||
{
|
||||
"id": "2b2761d66db4a83d5fb7a98cafb8414e9b0c238ceb67d729bedacc1a092d516f",
|
||||
"pubkey": "70a9b3c312a6b83e476739bd29d60ca700da1d5b982cbca87b5f3d27d4038d67",
|
||||
"created_at": 1740669816,
|
||||
"kind": 0,
|
||||
"tags": [
|
||||
["alt", "User profile for Vitor"],
|
||||
["name", "Vitor"],
|
||||
["display_name", "Vitor Pamplona"],
|
||||
["picture", "https://vitorpamplona.com/images/me_300.jpg"],
|
||||
["banner", "https://pbs.twimg.com/profile_banners/15064756/1414451651/1080x360"],
|
||||
["website", "https://vitorpamplona.com"],
|
||||
["pronouns", "he/him"],
|
||||
["about", "Nostr's Chief Android Officer - #Amethyst"],
|
||||
["nip05", "_@vitorpamplona.com"],
|
||||
["lud16", "vitor@vitorpamplona.com"],
|
||||
["lud06", "TEST"],
|
||||
["i", "github:vitorpamplona", "cf19e2d1d7f8dac6348ad37b35ec8421"]
|
||||
],
|
||||
"content": "{\"name\":\"Vitor\",\"display_name\":\"Vitor Pamplona\",\"picture\":\"https://vitorpamplona.com/images/me_300.jpg\",\"banner\":\"https://pbs.twimg.com/profile_banners/15064756/1414451651/1080x360\",\"website\":\"https://vitorpamplona.com\",\"pronouns\":\"he/him\",\"about\":\"Nostr's Chief Android Officer - #Amethyst\",\"nip05\":\"_@vitorpamplona.com\",\"lud16\":\"vitor@vitorpamplona.com\",\"lud06\":\"TEST\"}",
|
||||
"sig": "0a8c78eb0c5e0ba46e4781cc445fb7b6d275b434cead9231bd19b4f95671e3ab872264e50d4456d6036a84cc81e517bfa24229571519aabefcbce431e0c7163e"
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
assertEquals(expected, test.toPrettyJson())
|
||||
|
||||
val expected2 =
|
||||
"""
|
||||
{
|
||||
"id": "2e1e57fae4e4baddac025ea0b49afc093f2aa27610a05e584184ed26b29d7590",
|
||||
"pubkey": "70a9b3c312a6b83e476739bd29d60ca700da1d5b982cbca87b5f3d27d4038d67",
|
||||
"created_at": 1740669817,
|
||||
"kind": 0,
|
||||
"tags": [
|
||||
["alt", "User profile for 2 Vitor"],
|
||||
["name", "2 Vitor"],
|
||||
["display_name", "2 Vitor Pamplona"],
|
||||
["picture", "2 https://vitorpamplona.com/images/me_300.jpg"],
|
||||
["banner", "2 https://pbs.twimg.com/profile_banners/15064756/1414451651/1080x360"],
|
||||
["website", "2 https://vitorpamplona.com"],
|
||||
["pronouns", "2 he/him"],
|
||||
["about", "2 Nostr's Chief Android Officer - #Amethyst"],
|
||||
["nip05", "2 _@vitorpamplona.com"],
|
||||
["lud16", "2 vitor@vitorpamplona.com"],
|
||||
["lud06", "2 TEST"],
|
||||
["i", "github:vitorpamplona", "2cf19e2d1d7f8dac6348ad37b35ec8421"]
|
||||
],
|
||||
"content": "{\"name\":\"2 Vitor\",\"display_name\":\"2 Vitor Pamplona\",\"picture\":\"2 https://vitorpamplona.com/images/me_300.jpg\",\"banner\":\"2 https://pbs.twimg.com/profile_banners/15064756/1414451651/1080x360\",\"website\":\"2 https://vitorpamplona.com\",\"pronouns\":\"2 he/him\",\"about\":\"2 Nostr's Chief Android Officer - #Amethyst\",\"nip05\":\"2 _@vitorpamplona.com\",\"lud16\":\"2 vitor@vitorpamplona.com\",\"lud06\":\"2 TEST\"}",
|
||||
"sig": "a25483bc0fcc79ccd337e3ff846351097109fc13f1cd1c9cc15f7f2ad46417aeb7c05c1524aeadbab93036fc0743c8c310a5b2b7b482e1808649b12a3ee29d49"
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val test2 =
|
||||
signer.sign(
|
||||
MetadataEvent.updateFromPast(
|
||||
latest = test,
|
||||
name = "2 Vitor",
|
||||
displayName = "2 Vitor Pamplona",
|
||||
about = "2 Nostr's Chief Android Officer - #Amethyst",
|
||||
picture = "2 https://vitorpamplona.com/images/me_300.jpg",
|
||||
banner = "2 https://pbs.twimg.com/profile_banners/15064756/1414451651/1080x360",
|
||||
pronouns = "2 he/him",
|
||||
website = "2 https://vitorpamplona.com",
|
||||
nip05 = "2 _@vitorpamplona.com",
|
||||
lnAddress = "2 vitor@vitorpamplona.com",
|
||||
lnURL = "2 TEST",
|
||||
github = "https://gist.github.com/vitorpamplona/2cf19e2d1d7f8dac6348ad37b35ec8421",
|
||||
createdAt = 1740669817,
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(expected2, test2.toPrettyJson())
|
||||
|
||||
val expected3 =
|
||||
"""
|
||||
{
|
||||
"id": "94879b9a27fecf1337ede32013006ec4dfd5a3286a1e819abddfa1c3c132f008",
|
||||
"pubkey": "70a9b3c312a6b83e476739bd29d60ca700da1d5b982cbca87b5f3d27d4038d67",
|
||||
"created_at": 1740669817,
|
||||
"kind": 0,
|
||||
"tags": [
|
||||
["alt", "User profile for 2 Vitor"],
|
||||
["name", "2 Vitor"],
|
||||
["picture", "2 https://vitorpamplona.com/images/me_300.jpg"],
|
||||
["banner", "2 https://pbs.twimg.com/profile_banners/15064756/1414451651/1080x360"]
|
||||
],
|
||||
"content": "{\"name\":\"2 Vitor\",\"picture\":\"2 https://vitorpamplona.com/images/me_300.jpg\",\"banner\":\"2 https://pbs.twimg.com/profile_banners/15064756/1414451651/1080x360\"}",
|
||||
"sig": "729ee02364b4d429b6a66400ec850e80424602e3981ff2ad8a14d61bdee04f76ec68ddc4a0f38b0527f0218f7fa67668012a5c0f3c8ef943517b504040caa07e"
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val test3 =
|
||||
signer.sign(
|
||||
MetadataEvent.updateFromPast(
|
||||
latest = test2,
|
||||
name = null,
|
||||
displayName = "",
|
||||
about = "",
|
||||
picture = null,
|
||||
banner = null,
|
||||
pronouns = "",
|
||||
website = "",
|
||||
nip05 = "",
|
||||
lnAddress = "",
|
||||
lnURL = "",
|
||||
github = "",
|
||||
createdAt = 1740669817,
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(expected3, test3.toPrettyJson())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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 com.vitorpamplona.quartz.nip01Core.crypto.DeterministicSigner
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip19Bech32.bech32.bechToBytes
|
||||
|
||||
fun String.nsecToKeyPair() = KeyPair(this.bechToBytes())
|
||||
|
||||
fun String.nsecToSigner() = this.nsecToKeyPair().let { DeterministicSigner(it) }
|
||||
Reference in New Issue
Block a user