- 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:
@@ -1349,9 +1349,14 @@ class Account(
|
|||||||
) {
|
) {
|
||||||
if (!isWriteable()) return
|
if (!isWriteable()) return
|
||||||
|
|
||||||
|
val latest = userProfile().latestMetadata
|
||||||
|
|
||||||
|
val template =
|
||||||
|
if (latest != null) {
|
||||||
MetadataEvent.updateFromPast(
|
MetadataEvent.updateFromPast(
|
||||||
latest = userProfile().latestMetadata,
|
latest = latest,
|
||||||
name = name,
|
name = name,
|
||||||
|
displayName = name,
|
||||||
picture = picture,
|
picture = picture,
|
||||||
banner = banner,
|
banner = banner,
|
||||||
website = website,
|
website = website,
|
||||||
@@ -1363,13 +1368,29 @@ class Account(
|
|||||||
twitter = twitter,
|
twitter = twitter,
|
||||||
mastodon = mastodon,
|
mastodon = mastodon,
|
||||||
github = github,
|
github = github,
|
||||||
signer = signer,
|
)
|
||||||
) {
|
} else {
|
||||||
|
MetadataEvent.createNew(
|
||||||
|
name = name,
|
||||||
|
displayName = name,
|
||||||
|
picture = picture,
|
||||||
|
banner = banner,
|
||||||
|
website = website,
|
||||||
|
pronouns = pronouns,
|
||||||
|
about = about,
|
||||||
|
nip05 = nip05,
|
||||||
|
lnAddress = lnAddress,
|
||||||
|
lnURL = lnURL,
|
||||||
|
twitter = twitter,
|
||||||
|
mastodon = mastodon,
|
||||||
|
github = github,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
signer.sign(template) {
|
||||||
Amethyst.instance.client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reactionTo(
|
fun reactionTo(
|
||||||
|
|||||||
+1
-1
@@ -142,7 +142,7 @@ fun NewUserMetadataScreen(
|
|||||||
modifier = Modifier.padding(10.dp).verticalScroll(rememberScrollState()),
|
modifier = Modifier.padding(10.dp).verticalScroll(rememberScrollState()),
|
||||||
) {
|
) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
label = { Text(text = stringRes(R.string.display_name)) },
|
label = { Text(text = stringRes(R.string.profile_name)) },
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
value = postViewModel.displayName.value,
|
value = postViewModel.displayName.value,
|
||||||
onValueChange = { postViewModel.displayName.value = it },
|
onValueChange = { postViewModel.displayName.value = it },
|
||||||
|
|||||||
+1
-1
@@ -285,7 +285,7 @@ class AccountStateViewModel : ViewModel() {
|
|||||||
AccountSettings(
|
AccountSettings(
|
||||||
keyPair = keyPair,
|
keyPair = keyPair,
|
||||||
transientAccount = false,
|
transientAccount = false,
|
||||||
backupUserMetadata = MetadataEvent.newUser(name, tempSigner),
|
backupUserMetadata = tempSigner.sign(MetadataEvent.newUser(name)),
|
||||||
backupContactList =
|
backupContactList =
|
||||||
ContactListEvent.createFromScratch(
|
ContactListEvent.createFromScratch(
|
||||||
followUsers = listOf(ContactTag(keyPair.pubKey.toHexKey(), null, null)),
|
followUsers = listOf(ContactTag(keyPair.pubKey.toHexKey(), null, null)),
|
||||||
|
|||||||
+4
-4
@@ -68,7 +68,7 @@ import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
|||||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||||
import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList
|
import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList
|
||||||
import com.vitorpamplona.quartz.nip39ExtIdentities.GitHubIdentity
|
import com.vitorpamplona.quartz.nip39ExtIdentities.GitHubIdentity
|
||||||
import com.vitorpamplona.quartz.nip39ExtIdentities.IdentityClaim
|
import com.vitorpamplona.quartz.nip39ExtIdentities.IdentityClaimTag
|
||||||
import com.vitorpamplona.quartz.nip39ExtIdentities.MastodonIdentity
|
import com.vitorpamplona.quartz.nip39ExtIdentities.MastodonIdentity
|
||||||
import com.vitorpamplona.quartz.nip39ExtIdentities.TelegramIdentity
|
import com.vitorpamplona.quartz.nip39ExtIdentities.TelegramIdentity
|
||||||
import com.vitorpamplona.quartz.nip39ExtIdentities.TwitterIdentity
|
import com.vitorpamplona.quartz.nip39ExtIdentities.TwitterIdentity
|
||||||
@@ -196,7 +196,7 @@ fun DrawAdditionalInfo(
|
|||||||
|
|
||||||
val identities = user.latestMetadata?.identityClaims()
|
val identities = user.latestMetadata?.identityClaims()
|
||||||
if (!identities.isNullOrEmpty()) {
|
if (!identities.isNullOrEmpty()) {
|
||||||
identities.forEach { identity: IdentityClaim ->
|
identities.forEach { identity: IdentityClaimTag ->
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Icon(
|
Icon(
|
||||||
tint = Color.Unspecified,
|
tint = Color.Unspecified,
|
||||||
@@ -241,7 +241,7 @@ fun DrawAdditionalInfo(
|
|||||||
DisplayAppRecommendations(appRecommendations, accountViewModel, nav)
|
DisplayAppRecommendations(appRecommendations, accountViewModel, nav)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getIdentityClaimIcon(identity: IdentityClaim): Int =
|
fun getIdentityClaimIcon(identity: IdentityClaimTag): Int =
|
||||||
when (identity) {
|
when (identity) {
|
||||||
is TwitterIdentity -> R.drawable.x
|
is TwitterIdentity -> R.drawable.x
|
||||||
is TelegramIdentity -> R.drawable.telegram
|
is TelegramIdentity -> R.drawable.telegram
|
||||||
@@ -250,7 +250,7 @@ fun getIdentityClaimIcon(identity: IdentityClaim): Int =
|
|||||||
else -> R.drawable.github
|
else -> R.drawable.github
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getIdentityClaimDescription(identity: IdentityClaim): Int =
|
fun getIdentityClaimDescription(identity: IdentityClaimTag): Int =
|
||||||
when (identity) {
|
when (identity) {
|
||||||
is TwitterIdentity -> R.string.twitter
|
is TwitterIdentity -> R.string.twitter
|
||||||
is TelegramIdentity -> R.string.telegram
|
is TelegramIdentity -> R.string.telegram
|
||||||
|
|||||||
@@ -111,6 +111,7 @@
|
|||||||
<string name="global_feed">Global Feed</string>
|
<string name="global_feed">Global Feed</string>
|
||||||
<string name="search_feed">Search Feed</string>
|
<string name="search_feed">Search Feed</string>
|
||||||
<string name="add_a_relay">Add a Relay</string>
|
<string name="add_a_relay">Add a Relay</string>
|
||||||
|
<string name="profile_name">Name</string>
|
||||||
<string name="display_name">Display Name</string>
|
<string name="display_name">Display Name</string>
|
||||||
<string name="my_display_name">My display name</string>
|
<string name="my_display_name">My display name</string>
|
||||||
<string name="my_awesome_name">Ostrich McAwesome</string>
|
<string name="my_awesome_name">Ostrich McAwesome</string>
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-cor
|
|||||||
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
|
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
|
||||||
rfc3986-normalizer = { group = "org.czeal", name = "rfc3986", version.ref = "rfc3986" }
|
rfc3986-normalizer = { group = "org.czeal", name = "rfc3986", version.ref = "rfc3986" }
|
||||||
secp256k1-kmp-jni-android = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp-jni-android", version.ref = "secp256k1KmpJniAndroid" }
|
secp256k1-kmp-jni-android = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp-jni-android", version.ref = "secp256k1KmpJniAndroid" }
|
||||||
|
secp256k1-kmp-jni-jvm = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp-jni-jvm", version.ref = "secp256k1KmpJniAndroid" }
|
||||||
tor-android = { module = "info.guardianproject:tor-android", version.ref = "torAndroid" }
|
tor-android = { module = "info.guardianproject:tor-android", version.ref = "torAndroid" }
|
||||||
unifiedpush = { group = "com.github.UnifiedPush", name = "android-connector", version.ref = "unifiedpush" }
|
unifiedpush = { group = "com.github.UnifiedPush", name = "android-connector", version.ref = "unifiedpush" }
|
||||||
url-detector = { group = "io.github.url-detector", name = "url-detector", version.ref = "urlDetector" }
|
url-detector = { group = "io.github.url-detector", name = "url-detector", version.ref = "urlDetector" }
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ dependencies {
|
|||||||
api libs.rfc3986.normalizer
|
api libs.rfc3986.normalizer
|
||||||
|
|
||||||
testImplementation libs.junit
|
testImplementation libs.junit
|
||||||
|
testImplementation libs.secp256k1.kmp.jni.jvm
|
||||||
androidTestImplementation platform(libs.androidx.compose.bom)
|
androidTestImplementation platform(libs.androidx.compose.bom)
|
||||||
androidTestImplementation libs.androidx.junit
|
androidTestImplementation libs.androidx.junit
|
||||||
androidTestImplementation libs.androidx.espresso.core
|
androidTestImplementation libs.androidx.espresso.core
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import androidx.compose.runtime.Immutable
|
|||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.vitorpamplona.quartz.nip01Core.jackson.EventManualSerializer
|
import com.vitorpamplona.quartz.nip01Core.jackson.EventManualSerializer
|
||||||
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
|
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.TimeUtils
|
||||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
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)
|
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 {
|
companion object {
|
||||||
fun fromJson(json: String): Event = EventMapper.fromJson(json)
|
fun fromJson(json: String): Event = EventMapper.fromJson(json)
|
||||||
|
|
||||||
fun create(
|
fun build(
|
||||||
signer: NostrSigner,
|
|
||||||
kind: Int,
|
kind: Int,
|
||||||
tags: Array<Array<String>> = emptyArray(),
|
|
||||||
content: String = "",
|
content: String = "",
|
||||||
createdAt: Long = TimeUtils.now(),
|
createdAt: Long = TimeUtils.now(),
|
||||||
onReady: (Event) -> Unit,
|
initializer: TagArrayBuilder<Event>.() -> Unit = {},
|
||||||
) = signer.sign(createdAt, kind, tags, content, onReady)
|
) = 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+31
-4
@@ -21,11 +21,12 @@
|
|||||||
package com.vitorpamplona.quartz.nip01Core.jackson
|
package com.vitorpamplona.quartz.nip01Core.jackson
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory
|
import com.fasterxml.jackson.databind.node.JsonNodeFactory
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
|
||||||
class EventManualSerializer {
|
class EventManualSerializer {
|
||||||
companion object {
|
companion object {
|
||||||
fun toJson(
|
private fun assemble(
|
||||||
id: HexKey,
|
id: HexKey,
|
||||||
pubKey: HexKey,
|
pubKey: HexKey,
|
||||||
createdAt: Long,
|
createdAt: Long,
|
||||||
@@ -33,11 +34,10 @@ class EventManualSerializer {
|
|||||||
tags: Array<Array<String>>,
|
tags: Array<Array<String>>,
|
||||||
content: String,
|
content: String,
|
||||||
sig: String,
|
sig: String,
|
||||||
): String {
|
): ObjectNode {
|
||||||
val factory = JsonNodeFactory.instance
|
val factory = JsonNodeFactory.instance
|
||||||
|
|
||||||
val obj =
|
return factory.objectNode().apply {
|
||||||
factory.objectNode().apply {
|
|
||||||
put("id", id)
|
put("id", id)
|
||||||
put("pubkey", pubKey)
|
put("pubkey", pubKey)
|
||||||
put("created_at", createdAt)
|
put("created_at", createdAt)
|
||||||
@@ -55,8 +55,35 @@ class EventManualSerializer {
|
|||||||
put("content", content)
|
put("content", content)
|
||||||
put("sig", sig)
|
put("sig", sig)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toJson(
|
||||||
|
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.writeValueAsString(obj)
|
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 {
|
class EventMapper {
|
||||||
companion object {
|
companion object {
|
||||||
|
val defaultPrettyPrinter = InliningTagArrayPrettyPrinter()
|
||||||
|
|
||||||
val mapper =
|
val mapper =
|
||||||
jacksonObjectMapper()
|
jacksonObjectMapper()
|
||||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||||
.enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature())
|
.enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature())
|
||||||
|
.setDefaultPrettyPrinter(defaultPrettyPrinter)
|
||||||
.registerModule(
|
.registerModule(
|
||||||
SimpleModule()
|
SimpleModule()
|
||||||
.addSerializer(Event::class.java, EventSerializer())
|
.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(']')
|
||||||
|
}
|
||||||
|
}
|
||||||
+141
-70
@@ -21,19 +21,35 @@
|
|||||||
package com.vitorpamplona.quartz.nip01Core.metadata
|
package com.vitorpamplona.quartz.nip01Core.metadata
|
||||||
|
|
||||||
import android.util.Log
|
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.databind.node.ObjectNode
|
||||||
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
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.jackson.EventMapper
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
import com.vitorpamplona.quartz.nip01Core.metadata.tags.AboutTag
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
|
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.nip21UriScheme.toNostrUri
|
||||||
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||||
import com.vitorpamplona.quartz.nip39ExtIdentities.updateClaims
|
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 com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
import java.io.StringWriter
|
|
||||||
|
|
||||||
class MetadataEvent(
|
class MetadataEvent(
|
||||||
id: HexKey,
|
id: HexKey,
|
||||||
@@ -43,11 +59,12 @@ class MetadataEvent(
|
|||||||
content: String,
|
content: String,
|
||||||
sig: HexKey,
|
sig: HexKey,
|
||||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||||
|
fun contactMetadataJson() = jacksonObjectMapper().readTree(content) as? ObjectNode
|
||||||
|
|
||||||
fun contactMetaData() =
|
fun contactMetaData() =
|
||||||
try {
|
try {
|
||||||
EventMapper.mapper.readValue(content, UserMetadata::class.java)
|
EventMapper.mapper.readValue(content, UserMetadata::class.java)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// e.printStackTrace()
|
|
||||||
Log.w("MetadataEvent", "Content Parse Error: ${toNostrUri()} ${e.localizedMessage}")
|
Log.w("MetadataEvent", "Content Parse Error: ${toNostrUri()} ${e.localizedMessage}")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@@ -57,76 +74,130 @@ class MetadataEvent(
|
|||||||
|
|
||||||
fun newUser(
|
fun newUser(
|
||||||
name: String?,
|
name: String?,
|
||||||
signer: NostrSignerSync,
|
|
||||||
createdAt: Long = TimeUtils.now(),
|
createdAt: Long = TimeUtils.now(),
|
||||||
): MetadataEvent? {
|
initializer: TagArrayBuilder<MetadataEvent>.() -> Unit = {},
|
||||||
// Tries to not delete any existing attribute that we do not work with.
|
): EventTemplate<MetadataEvent> {
|
||||||
val currentJson = ObjectMapper().createObjectNode()
|
val metadata = JsonNodeFactory.instance.objectNode()
|
||||||
|
name?.let { addIfNotBlank(metadata, "name", it.trim()) }
|
||||||
name?.let { addIfNotBlank(currentJson, "name", it.trim()) }
|
return eventTemplate(KIND, metadata.toString(), createdAt) {
|
||||||
val writer = StringWriter()
|
alt("User profile for $name")
|
||||||
ObjectMapper().writeValue(writer, currentJson)
|
initializer()
|
||||||
|
}
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(),
|
||||||
|
initializer: TagArrayBuilder<MetadataEvent>.() -> Unit = {},
|
||||||
|
): EventTemplate<MetadataEvent> {
|
||||||
|
// Tries to not delete any existing attribute that we do not work with.
|
||||||
|
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(
|
fun updateFromPast(
|
||||||
latest: MetadataEvent?,
|
latest: MetadataEvent,
|
||||||
name: String?,
|
name: String? = null,
|
||||||
picture: String?,
|
displayName: String? = null,
|
||||||
banner: String?,
|
picture: String? = null,
|
||||||
website: String?,
|
banner: String? = null,
|
||||||
about: String?,
|
website: String? = null,
|
||||||
nip05: String?,
|
about: String? = null,
|
||||||
lnAddress: String?,
|
nip05: String? = null,
|
||||||
lnURL: String?,
|
lnAddress: String? = null,
|
||||||
pronouns: String?,
|
lnURL: String? = null,
|
||||||
twitter: String?,
|
pronouns: String? = null,
|
||||||
mastodon: String?,
|
twitter: String? = null,
|
||||||
github: String?,
|
mastodon: String? = null,
|
||||||
signer: NostrSigner,
|
github: String? = null,
|
||||||
createdAt: Long = TimeUtils.now(),
|
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.
|
// Tries to not delete any existing attribute that we do not work with.
|
||||||
val currentJson =
|
val currentMetadata = latest.contactMetadataJson() ?: JsonNodeFactory.instance.objectNode()
|
||||||
if (latest != null) {
|
|
||||||
ObjectMapper()
|
name?.let { addIfNotBlank(currentMetadata, NameTag.TAG_NAME, it.trim()) }
|
||||||
.readTree(
|
displayName?.let { addIfNotBlank(currentMetadata, DisplayNameTag.TAG_NAME, it.trim()) }
|
||||||
ByteArrayInputStream(latest.content.toByteArray(Charsets.UTF_8)),
|
picture?.let { addIfNotBlank(currentMetadata, PictureTag.TAG_NAME, it.trim()) }
|
||||||
) as ObjectNode
|
banner?.let { addIfNotBlank(currentMetadata, BannerTag.TAG_NAME, it.trim()) }
|
||||||
} else {
|
website?.let { addIfNotBlank(currentMetadata, WebsiteTag.TAG_NAME, it.trim()) }
|
||||||
ObjectMapper().createObjectNode()
|
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()) }
|
return EventTemplate(createdAt, KIND, tags, currentMetadata.toString())
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addIfNotBlank(
|
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
|
package com.vitorpamplona.quartz.nip01Core.signers
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.vitorpamplona.quartz.EventFactory
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
|
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
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.KeyPair
|
||||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
|
||||||
import com.vitorpamplona.quartz.nip04Dm.crypto.Nip04
|
import com.vitorpamplona.quartz.nip04Dm.crypto.Nip04
|
||||||
import com.vitorpamplona.quartz.nip44Encryption.Nip44
|
import com.vitorpamplona.quartz.nip44Encryption.Nip44
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapPrivateEvent
|
import com.vitorpamplona.quartz.nip57Zaps.LnZapPrivateEvent
|
||||||
@@ -36,7 +34,7 @@ import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
|
|||||||
import com.vitorpamplona.quartz.nip57Zaps.PrivateZapRequestBuilder
|
import com.vitorpamplona.quartz.nip57Zaps.PrivateZapRequestBuilder
|
||||||
|
|
||||||
class NostrSignerSync(
|
class NostrSignerSync(
|
||||||
val keyPair: KeyPair,
|
val keyPair: KeyPair = KeyPair(),
|
||||||
val pubKey: HexKey = keyPair.pubKey.toHexKey(),
|
val pubKey: HexKey = keyPair.pubKey.toHexKey(),
|
||||||
) {
|
) {
|
||||||
fun <T : Event> sign(ev: EventTemplate<T>) = signNormal<T>(ev.createdAt, ev.kind, ev.tags, ev.content)
|
fun <T : Event> sign(ev: EventTemplate<T>) = signNormal<T>(ev.createdAt, ev.kind, ev.tags, ev.content)
|
||||||
@@ -72,18 +70,7 @@ class NostrSignerSync(
|
|||||||
): T? {
|
): T? {
|
||||||
if (keyPair.privKey == null) return null
|
if (keyPair.privKey == null) return null
|
||||||
|
|
||||||
val id = EventHasher.hashIdBytes(pubKey, createdAt, kind, tags, content)
|
return EventAssembler.hashAndSign<T>(pubKey, createdAt, kind, tags, content, keyPair.privKey)
|
||||||
val sig = Nip01.sign(id, keyPair.privKey).toHexKey()
|
|
||||||
|
|
||||||
return EventFactory.create(
|
|
||||||
id.toHexKey(),
|
|
||||||
pubKey,
|
|
||||||
createdAt,
|
|
||||||
kind,
|
|
||||||
tags,
|
|
||||||
content,
|
|
||||||
sig,
|
|
||||||
) as T
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun nip04Encrypt(
|
fun nip04Encrypt(
|
||||||
|
|||||||
@@ -20,26 +20,15 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.quartz.nip39ExtIdentities
|
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
|
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||||
|
|
||||||
fun MetadataEvent.identityClaims() =
|
fun MetadataEvent.identityClaims() = tags.mapNotNull(IdentityClaimTag::parse)
|
||||||
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.updateClaims(
|
fun MetadataEvent.replaceClaims(
|
||||||
twitter: String?,
|
twitter: String?,
|
||||||
mastodon: String?,
|
mastodon: String?,
|
||||||
github: String?,
|
github: String?,
|
||||||
): TagArray {
|
): List<IdentityClaimTag> {
|
||||||
var claims = identityClaims()
|
var claims = identityClaims()
|
||||||
|
|
||||||
// null leave as is. blank deletes it.
|
// 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(
|
class GitHubIdentity(
|
||||||
identity: String,
|
identity: String,
|
||||||
proof: String,
|
proof: String,
|
||||||
) : IdentityClaim(identity, proof) {
|
) : IdentityClaimTag(identity, proof) {
|
||||||
override fun toProofUrl() = "https://gist.github.com/$identity/$proof"
|
override fun toProofUrl() = "https://gist.github.com/$identity/$proof"
|
||||||
|
|
||||||
override fun platform() = platform
|
override fun platform() = platform
|
||||||
|
|||||||
+27
-3
@@ -20,10 +20,12 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.quartz.nip39ExtIdentities
|
package com.vitorpamplona.quartz.nip39ExtIdentities
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
|
import com.vitorpamplona.quartz.utils.ensure
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
abstract class IdentityClaim(
|
abstract class IdentityClaimTag(
|
||||||
val identity: String,
|
val identity: String,
|
||||||
val proof: String,
|
val proof: String,
|
||||||
) {
|
) {
|
||||||
@@ -31,13 +33,17 @@ abstract class IdentityClaim(
|
|||||||
|
|
||||||
abstract fun platform(): String
|
abstract fun platform(): String
|
||||||
|
|
||||||
|
fun toTagArray() = assemble(platformIdentity(), proof)
|
||||||
|
|
||||||
fun platformIdentity() = "${platform()}:$identity"
|
fun platformIdentity() = "${platform()}:$identity"
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
const val TAG_NAME = "i"
|
||||||
|
|
||||||
fun create(
|
fun create(
|
||||||
platformIdentity: String,
|
platformIdentity: String,
|
||||||
proof: String,
|
proof: String,
|
||||||
): IdentityClaim {
|
): IdentityClaimTag {
|
||||||
val (platform, identity) = platformIdentity.split(':')
|
val (platform, identity) = platformIdentity.split(':')
|
||||||
|
|
||||||
return when (platform.lowercase()) {
|
return when (platform.lowercase()) {
|
||||||
@@ -45,8 +51,26 @@ abstract class IdentityClaim(
|
|||||||
TwitterIdentity.platform -> TwitterIdentity(identity, proof)
|
TwitterIdentity.platform -> TwitterIdentity(identity, proof)
|
||||||
TelegramIdentity.platform -> TelegramIdentity(identity, proof)
|
TelegramIdentity.platform -> TelegramIdentity(identity, proof)
|
||||||
MastodonIdentity.platform -> MastodonIdentity(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(
|
class MastodonIdentity(
|
||||||
identity: String,
|
identity: String,
|
||||||
proof: String,
|
proof: String,
|
||||||
) : IdentityClaim(identity, proof) {
|
) : IdentityClaimTag(identity, proof) {
|
||||||
override fun toProofUrl() = "https://$identity/$proof"
|
override fun toProofUrl() = "https://$identity/$proof"
|
||||||
|
|
||||||
override fun platform() = platform
|
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(
|
class TelegramIdentity(
|
||||||
identity: String,
|
identity: String,
|
||||||
proof: String,
|
proof: String,
|
||||||
) : IdentityClaim(identity, proof) {
|
) : IdentityClaimTag(identity, proof) {
|
||||||
override fun toProofUrl() = "https://t.me/$proof"
|
override fun toProofUrl() = "https://t.me/$proof"
|
||||||
|
|
||||||
override fun platform() = platform
|
override fun platform() = platform
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ package com.vitorpamplona.quartz.nip39ExtIdentities
|
|||||||
class TwitterIdentity(
|
class TwitterIdentity(
|
||||||
identity: String,
|
identity: String,
|
||||||
proof: String,
|
proof: String,
|
||||||
) : IdentityClaim(identity, proof) {
|
) : IdentityClaimTag(identity, proof) {
|
||||||
override fun toProofUrl() = "https://x.com/$identity/status/$proof"
|
override fun toProofUrl() = "https://x.com/$identity/status/$proof"
|
||||||
|
|
||||||
override fun platform() = platform
|
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