feat: implement all missing NIP-51 list event kinds
Add 14 missing NIP-51 event kinds with full CRUD support: Replaceable lists: - 10009 SimpleGroupListEvent (NIP-29 group membership) - 10017 GitAuthorListEvent (NIP-34 code follows) - 10018 GitRepositoryListEvent (NIP-34 repo follows) - 10020 MediaFollowListEvent (multimedia follows) - 10101 GoodWikiAuthorListEvent (NIP-54 wiki authors) - 10102 GoodWikiRelayListEvent (NIP-54 wiki relays) Addressable sets: - 30004 ArticleCurationSetEvent (curated articles) - 30005 VideoCurationSetEvent (curated videos) - 30006 PictureCurationSetEvent (curated pictures) - 30007 KindMuteSetEvent (kind-specific mutes) - 30015 InterestSetEvent (hashtag interest groups) - 30063 ReleaseArtifactSetEvent (software release artifacts) - 30267 AppCurationSetEvent (software app curation) - 39092 MediaStarterPackEvent (media starter packs) All events follow existing patterns with NIP-44 private tag encryption, ALT tags, hint providers, and TagArrayBuilder extensions. Registered all new kinds in EventFactory. https://claude.ai/code/session_01QrYyt6KQepNGtRMDcCr5zM
This commit is contained in:
+156
@@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.appCurationSet
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.fastAny
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.AddressBookmark
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.remove
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.DescriptionTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.ImageTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.TitleTag
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
class AppCurationSetEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig),
|
||||||
|
AddressHintProvider {
|
||||||
|
override fun addressHints() = tags.mapNotNull(AddressBookmark::parseAsHint)
|
||||||
|
|
||||||
|
override fun linkedAddressIds() = tags.mapNotNull(AddressBookmark::parseAddressId)
|
||||||
|
|
||||||
|
fun title() = tags.firstNotNullOfOrNull(TitleTag::parse)
|
||||||
|
|
||||||
|
fun description() = tags.firstNotNullOfOrNull(DescriptionTag::parse)
|
||||||
|
|
||||||
|
fun image() = tags.firstNotNullOfOrNull(ImageTag::parse)
|
||||||
|
|
||||||
|
fun apps() = tags.mapNotNull(AddressBookmark::parse)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KIND = 30267
|
||||||
|
const val ALT = "App Curation Set"
|
||||||
|
|
||||||
|
fun createAddress(
|
||||||
|
pubKey: HexKey,
|
||||||
|
dTag: String,
|
||||||
|
) = Address(KIND, pubKey, dTag)
|
||||||
|
|
||||||
|
suspend fun add(
|
||||||
|
earlierVersion: AppCurationSetEvent,
|
||||||
|
app: AddressBookmark,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): AppCurationSetEvent =
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.plus(app.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun remove(
|
||||||
|
earlierVersion: AppCurationSetEvent,
|
||||||
|
app: AddressBookmark,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): AppCurationSetEvent =
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.remove(app.toTagIdOnly()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
content: String,
|
||||||
|
tags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): AppCurationSetEvent {
|
||||||
|
val newTags =
|
||||||
|
if (tags.fastAny(AltTag::match)) {
|
||||||
|
tags
|
||||||
|
} else {
|
||||||
|
tags + AltTag.assemble(ALT)
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer.sign(createdAt, KIND, newTags, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
|
suspend fun create(
|
||||||
|
title: String = "",
|
||||||
|
description: String? = null,
|
||||||
|
image: String? = null,
|
||||||
|
apps: List<AddressBookmark> = emptyList(),
|
||||||
|
dTag: String = Uuid.random().toString(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): AppCurationSetEvent {
|
||||||
|
val template =
|
||||||
|
build(title, apps, dTag, createdAt) {
|
||||||
|
if (description != null) description(description)
|
||||||
|
if (image != null) image(image)
|
||||||
|
}
|
||||||
|
return signer.sign(template)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
|
fun build(
|
||||||
|
title: String = "",
|
||||||
|
apps: List<AddressBookmark> = emptyList(),
|
||||||
|
dTag: String = Uuid.random().toString(),
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
initializer: TagArrayBuilder<AppCurationSetEvent>.() -> Unit = {},
|
||||||
|
) = eventTemplate(
|
||||||
|
kind = KIND,
|
||||||
|
description = "",
|
||||||
|
createdAt = createdAt,
|
||||||
|
) {
|
||||||
|
dTag(dTag)
|
||||||
|
alt(ALT)
|
||||||
|
title(title)
|
||||||
|
apps(apps)
|
||||||
|
|
||||||
|
initializer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.appCurationSet
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.AddressBookmark
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.DescriptionTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.ImageTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.TitleTag
|
||||||
|
|
||||||
|
fun TagArrayBuilder<AppCurationSetEvent>.title(title: String) = addUnique(TitleTag.assemble(title))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<AppCurationSetEvent>.description(desc: String) = addUnique(DescriptionTag.assemble(desc))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<AppCurationSetEvent>.image(imageUrl: String) = addUnique(ImageTag.assemble(imageUrl))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<AppCurationSetEvent>.apps(apps: List<AddressBookmark>) = addAll(apps.map { it.toTagArray() })
|
||||||
+205
@@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.articleCurationSet
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.fastAny
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.PrivateTagArrayEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.AddressBookmark
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.BookmarkIdTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.EventBookmark
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.remove
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.DescriptionTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.ImageTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.TitleTag
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
class ArticleCurationSetEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : PrivateTagArrayEvent(id, pubKey, createdAt, KIND, tags, content, sig),
|
||||||
|
EventHintProvider,
|
||||||
|
AddressHintProvider {
|
||||||
|
override fun eventHints() = tags.mapNotNull(EventBookmark::parseAsHint)
|
||||||
|
|
||||||
|
override fun linkedEventIds() = tags.mapNotNull(EventBookmark::parseId)
|
||||||
|
|
||||||
|
override fun addressHints() = tags.mapNotNull(AddressBookmark::parseAsHint)
|
||||||
|
|
||||||
|
override fun linkedAddressIds() = tags.mapNotNull(AddressBookmark::parseAddressId)
|
||||||
|
|
||||||
|
fun title() = tags.firstNotNullOfOrNull(TitleTag::parse)
|
||||||
|
|
||||||
|
fun description() = tags.firstNotNullOfOrNull(DescriptionTag::parse)
|
||||||
|
|
||||||
|
fun image() = tags.firstNotNullOfOrNull(ImageTag::parse)
|
||||||
|
|
||||||
|
fun publicItems(): List<BookmarkIdTag> = tags.mapNotNull(BookmarkIdTag::parse)
|
||||||
|
|
||||||
|
suspend fun privateItems(signer: NostrSigner): List<BookmarkIdTag>? = privateTags(signer)?.mapNotNull(BookmarkIdTag::parse)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KIND = 30004
|
||||||
|
const val ALT = "Article Curation Set"
|
||||||
|
|
||||||
|
fun createAddress(
|
||||||
|
pubKey: HexKey,
|
||||||
|
dTag: String,
|
||||||
|
) = Address(KIND, pubKey, dTag)
|
||||||
|
|
||||||
|
suspend fun add(
|
||||||
|
earlierVersion: ArticleCurationSetEvent,
|
||||||
|
item: BookmarkIdTag,
|
||||||
|
isPrivate: Boolean,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): ArticleCurationSetEvent =
|
||||||
|
if (isPrivate) {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
resign(
|
||||||
|
tags = earlierVersion.tags,
|
||||||
|
privateTags = privateTags.plus(item.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.plus(item.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun remove(
|
||||||
|
earlierVersion: ArticleCurationSetEvent,
|
||||||
|
item: BookmarkIdTag,
|
||||||
|
isPrivate: Boolean,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): ArticleCurationSetEvent =
|
||||||
|
if (isPrivate) {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
resign(
|
||||||
|
privateTags = privateTags.remove(item.toTagIdOnly()),
|
||||||
|
tags = earlierVersion.tags,
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.remove(item.toTagIdOnly()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
tags: TagArray,
|
||||||
|
privateTags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
) = resign(
|
||||||
|
content = PrivateTagsInContent.encryptNip44(privateTags, signer),
|
||||||
|
tags = tags,
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
content: String,
|
||||||
|
tags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): ArticleCurationSetEvent {
|
||||||
|
val newTags =
|
||||||
|
if (tags.fastAny(AltTag::match)) {
|
||||||
|
tags
|
||||||
|
} else {
|
||||||
|
tags + AltTag.assemble(ALT)
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer.sign(createdAt, KIND, newTags, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
|
suspend fun create(
|
||||||
|
title: String = "",
|
||||||
|
description: String? = null,
|
||||||
|
image: String? = null,
|
||||||
|
publicItems: List<BookmarkIdTag> = emptyList(),
|
||||||
|
privateItems: List<BookmarkIdTag> = emptyList(),
|
||||||
|
dTag: String = Uuid.random().toString(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): ArticleCurationSetEvent {
|
||||||
|
val template =
|
||||||
|
build(title, publicItems, privateItems, signer, dTag, createdAt) {
|
||||||
|
if (description != null) description(description)
|
||||||
|
if (image != null) image(image)
|
||||||
|
}
|
||||||
|
return signer.sign(template)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
|
suspend fun build(
|
||||||
|
title: String = "",
|
||||||
|
publicItems: List<BookmarkIdTag> = emptyList(),
|
||||||
|
privateItems: List<BookmarkIdTag> = emptyList(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
dTag: String = Uuid.random().toString(),
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
initializer: TagArrayBuilder<ArticleCurationSetEvent>.() -> Unit = {},
|
||||||
|
) = eventTemplate(
|
||||||
|
kind = KIND,
|
||||||
|
description = PrivateTagsInContent.encryptNip44(privateItems.map { it.toTagArray() }.toTypedArray(), signer),
|
||||||
|
createdAt = createdAt,
|
||||||
|
) {
|
||||||
|
dTag(dTag)
|
||||||
|
alt(ALT)
|
||||||
|
title(title)
|
||||||
|
bookmarks(publicItems)
|
||||||
|
|
||||||
|
initializer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.articleCurationSet
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.BookmarkIdTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.DescriptionTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.ImageTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.TitleTag
|
||||||
|
|
||||||
|
fun TagArrayBuilder<ArticleCurationSetEvent>.title(title: String) = addUnique(TitleTag.assemble(title))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<ArticleCurationSetEvent>.description(desc: String) = addUnique(DescriptionTag.assemble(desc))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<ArticleCurationSetEvent>.image(imageUrl: String) = addUnique(ImageTag.assemble(imageUrl))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<ArticleCurationSetEvent>.bookmarks(items: List<BookmarkIdTag>) = addAll(items.map { it.toTagArray() })
|
||||||
+142
@@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.gitAuthorList
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.fastAny
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.PrivateTagArrayEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.muteList.tags.UserTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.remove
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
class GitAuthorListEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : PrivateTagArrayEvent(id, pubKey, createdAt, KIND, tags, content, sig),
|
||||||
|
PubKeyHintProvider {
|
||||||
|
override fun pubKeyHints() = tags.mapNotNull(UserTag::parseAsHint)
|
||||||
|
|
||||||
|
override fun linkedPubKeys() = tags.mapNotNull(UserTag::parseKey)
|
||||||
|
|
||||||
|
fun publicAuthors() = tags.mapNotNull(UserTag::parse)
|
||||||
|
|
||||||
|
suspend fun privateAuthors(signer: NostrSigner) = privateTags(signer)?.mapNotNull(UserTag::parse)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KIND = 10017
|
||||||
|
const val ALT = "Git Authors List"
|
||||||
|
|
||||||
|
fun createAddress(pubKey: HexKey) = Address(KIND, pubKey, "")
|
||||||
|
|
||||||
|
suspend fun create(
|
||||||
|
publicAuthors: List<UserTag> = emptyList(),
|
||||||
|
privateAuthors: List<UserTag> = emptyList(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): GitAuthorListEvent =
|
||||||
|
resign(
|
||||||
|
content = PrivateTagsInContent.encryptNip44(privateAuthors.map { it.toTagArray() }.toTypedArray(), signer),
|
||||||
|
tags = publicAuthors.map { it.toTagArray() }.toTypedArray(),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun add(
|
||||||
|
earlierVersion: GitAuthorListEvent,
|
||||||
|
author: UserTag,
|
||||||
|
isPrivate: Boolean,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): GitAuthorListEvent =
|
||||||
|
if (isPrivate) {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
resign(
|
||||||
|
tags = earlierVersion.tags,
|
||||||
|
privateTags = privateTags.plus(author.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.plus(author.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun remove(
|
||||||
|
earlierVersion: GitAuthorListEvent,
|
||||||
|
author: UserTag,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): GitAuthorListEvent {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
return resign(
|
||||||
|
privateTags = privateTags.remove(author.toTagIdOnly()),
|
||||||
|
tags = earlierVersion.tags.remove(author.toTagIdOnly()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
tags: TagArray,
|
||||||
|
privateTags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
) = resign(
|
||||||
|
content = PrivateTagsInContent.encryptNip44(privateTags, signer),
|
||||||
|
tags = tags,
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
content: String,
|
||||||
|
tags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): GitAuthorListEvent {
|
||||||
|
val newTags =
|
||||||
|
if (tags.fastAny(AltTag::match)) {
|
||||||
|
tags
|
||||||
|
} else {
|
||||||
|
tags + AltTag.assemble(ALT)
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer.sign(createdAt, KIND, newTags, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+142
@@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.gitRepositoryList
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.fastAny
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.PrivateTagArrayEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.AddressBookmark
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.remove
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
class GitRepositoryListEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : PrivateTagArrayEvent(id, pubKey, createdAt, KIND, tags, content, sig),
|
||||||
|
AddressHintProvider {
|
||||||
|
override fun addressHints() = tags.mapNotNull(AddressBookmark::parseAsHint)
|
||||||
|
|
||||||
|
override fun linkedAddressIds() = tags.mapNotNull(AddressBookmark::parseAddressId)
|
||||||
|
|
||||||
|
fun publicRepositories() = tags.mapNotNull(AddressBookmark::parse)
|
||||||
|
|
||||||
|
suspend fun privateRepositories(signer: NostrSigner) = privateTags(signer)?.mapNotNull(AddressBookmark::parse)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KIND = 10018
|
||||||
|
const val ALT = "Git Repositories List"
|
||||||
|
|
||||||
|
fun createAddress(pubKey: HexKey) = Address(KIND, pubKey, "")
|
||||||
|
|
||||||
|
suspend fun create(
|
||||||
|
publicRepositories: List<AddressBookmark> = emptyList(),
|
||||||
|
privateRepositories: List<AddressBookmark> = emptyList(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): GitRepositoryListEvent =
|
||||||
|
resign(
|
||||||
|
content = PrivateTagsInContent.encryptNip44(privateRepositories.map { it.toTagArray() }.toTypedArray(), signer),
|
||||||
|
tags = publicRepositories.map { it.toTagArray() }.toTypedArray(),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun add(
|
||||||
|
earlierVersion: GitRepositoryListEvent,
|
||||||
|
repository: AddressBookmark,
|
||||||
|
isPrivate: Boolean,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): GitRepositoryListEvent =
|
||||||
|
if (isPrivate) {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
resign(
|
||||||
|
tags = earlierVersion.tags,
|
||||||
|
privateTags = privateTags.plus(repository.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.plus(repository.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun remove(
|
||||||
|
earlierVersion: GitRepositoryListEvent,
|
||||||
|
repository: AddressBookmark,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): GitRepositoryListEvent {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
return resign(
|
||||||
|
privateTags = privateTags.remove(repository.toTagIdOnly()),
|
||||||
|
tags = earlierVersion.tags.remove(repository.toTagIdOnly()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
tags: TagArray,
|
||||||
|
privateTags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
) = resign(
|
||||||
|
content = PrivateTagsInContent.encryptNip44(privateTags, signer),
|
||||||
|
tags = tags,
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
content: String,
|
||||||
|
tags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): GitRepositoryListEvent {
|
||||||
|
val newTags =
|
||||||
|
if (tags.fastAny(AltTag::match)) {
|
||||||
|
tags
|
||||||
|
} else {
|
||||||
|
tags + AltTag.assemble(ALT)
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer.sign(createdAt, KIND, newTags, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+142
@@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.goodWikiAuthorList
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.fastAny
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.PrivateTagArrayEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.muteList.tags.UserTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.remove
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
class GoodWikiAuthorListEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : PrivateTagArrayEvent(id, pubKey, createdAt, KIND, tags, content, sig),
|
||||||
|
PubKeyHintProvider {
|
||||||
|
override fun pubKeyHints() = tags.mapNotNull(UserTag::parseAsHint)
|
||||||
|
|
||||||
|
override fun linkedPubKeys() = tags.mapNotNull(UserTag::parseKey)
|
||||||
|
|
||||||
|
fun publicAuthors() = tags.mapNotNull(UserTag::parse)
|
||||||
|
|
||||||
|
suspend fun privateAuthors(signer: NostrSigner) = privateTags(signer)?.mapNotNull(UserTag::parse)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KIND = 10101
|
||||||
|
const val ALT = "Good Wiki Authors List"
|
||||||
|
|
||||||
|
fun createAddress(pubKey: HexKey) = Address(KIND, pubKey, "")
|
||||||
|
|
||||||
|
suspend fun create(
|
||||||
|
publicAuthors: List<UserTag> = emptyList(),
|
||||||
|
privateAuthors: List<UserTag> = emptyList(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): GoodWikiAuthorListEvent =
|
||||||
|
resign(
|
||||||
|
content = PrivateTagsInContent.encryptNip44(privateAuthors.map { it.toTagArray() }.toTypedArray(), signer),
|
||||||
|
tags = publicAuthors.map { it.toTagArray() }.toTypedArray(),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun add(
|
||||||
|
earlierVersion: GoodWikiAuthorListEvent,
|
||||||
|
author: UserTag,
|
||||||
|
isPrivate: Boolean,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): GoodWikiAuthorListEvent =
|
||||||
|
if (isPrivate) {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
resign(
|
||||||
|
tags = earlierVersion.tags,
|
||||||
|
privateTags = privateTags.plus(author.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.plus(author.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun remove(
|
||||||
|
earlierVersion: GoodWikiAuthorListEvent,
|
||||||
|
author: UserTag,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): GoodWikiAuthorListEvent {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
return resign(
|
||||||
|
privateTags = privateTags.remove(author.toTagIdOnly()),
|
||||||
|
tags = earlierVersion.tags.remove(author.toTagIdOnly()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
tags: TagArray,
|
||||||
|
privateTags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
) = resign(
|
||||||
|
content = PrivateTagsInContent.encryptNip44(privateTags, signer),
|
||||||
|
tags = tags,
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
content: String,
|
||||||
|
tags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): GoodWikiAuthorListEvent {
|
||||||
|
val newTags =
|
||||||
|
if (tags.fastAny(AltTag::match)) {
|
||||||
|
tags
|
||||||
|
} else {
|
||||||
|
tags + AltTag.assemble(ALT)
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer.sign(createdAt, KIND, newTags, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+141
@@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.goodWikiRelayList
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.fastAny
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.PrivateTagArrayEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.relayLists.tags.RelayTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.relayLists.tags.relays
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.remove
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
class GoodWikiRelayListEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : PrivateTagArrayEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||||
|
fun publicRelays() = tags.relays()
|
||||||
|
|
||||||
|
suspend fun privateRelays(signer: NostrSigner) = privateTags(signer)?.relays()
|
||||||
|
|
||||||
|
suspend fun relays(signer: NostrSigner): List<NormalizedRelayUrl> = publicRelays() + (privateRelays(signer) ?: emptyList())
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KIND = 10102
|
||||||
|
const val ALT = "Good Wiki Relays List"
|
||||||
|
|
||||||
|
fun createAddress(pubKey: HexKey) = Address(KIND, pubKey, "")
|
||||||
|
|
||||||
|
suspend fun create(
|
||||||
|
publicRelays: List<NormalizedRelayUrl> = emptyList(),
|
||||||
|
privateRelays: List<NormalizedRelayUrl> = emptyList(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): GoodWikiRelayListEvent =
|
||||||
|
resign(
|
||||||
|
content = PrivateTagsInContent.encryptNip44(privateRelays.map { RelayTag.assemble(it) }.toTypedArray(), signer),
|
||||||
|
tags = publicRelays.map { RelayTag.assemble(it) }.toTypedArray(),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun add(
|
||||||
|
earlierVersion: GoodWikiRelayListEvent,
|
||||||
|
relay: NormalizedRelayUrl,
|
||||||
|
isPrivate: Boolean,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): GoodWikiRelayListEvent =
|
||||||
|
if (isPrivate) {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
resign(
|
||||||
|
tags = earlierVersion.tags,
|
||||||
|
privateTags = privateTags.plus(RelayTag.assemble(relay)),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.plus(RelayTag.assemble(relay)),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun remove(
|
||||||
|
earlierVersion: GoodWikiRelayListEvent,
|
||||||
|
relay: NormalizedRelayUrl,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): GoodWikiRelayListEvent {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
val relayTag = RelayTag.assemble(relay)
|
||||||
|
return resign(
|
||||||
|
privateTags = privateTags.remove(relayTag),
|
||||||
|
tags = earlierVersion.tags.remove(relayTag),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
tags: TagArray,
|
||||||
|
privateTags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
) = resign(
|
||||||
|
content = PrivateTagsInContent.encryptNip44(privateTags, signer),
|
||||||
|
tags = tags,
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
content: String,
|
||||||
|
tags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): GoodWikiRelayListEvent {
|
||||||
|
val newTags =
|
||||||
|
if (tags.fastAny(AltTag::match)) {
|
||||||
|
tags
|
||||||
|
} else {
|
||||||
|
tags + AltTag.assemble(ALT)
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer.sign(createdAt, KIND, newTags, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+170
@@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.interestSet
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.fastAny
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.HashtagTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.PrivateTagArrayEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.removeAny
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.removeIgnoreCase
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
class InterestSetEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : PrivateTagArrayEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||||
|
fun publicHashtags() = tags.mapNotNull(HashtagTag::parse)
|
||||||
|
|
||||||
|
suspend fun privateHashtags(signer: NostrSigner) = privateTags(signer)?.mapNotNull(HashtagTag::parse)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KIND = 30015
|
||||||
|
const val ALT = "Interest Set"
|
||||||
|
|
||||||
|
fun createAddress(
|
||||||
|
pubKey: HexKey,
|
||||||
|
dTag: String,
|
||||||
|
) = Address(KIND, pubKey, dTag)
|
||||||
|
|
||||||
|
suspend fun add(
|
||||||
|
earlierVersion: InterestSetEvent,
|
||||||
|
hashtag: String,
|
||||||
|
isPrivate: Boolean,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): InterestSetEvent {
|
||||||
|
val hashtagTag = HashtagTag.assemble(hashtag)
|
||||||
|
return if (isPrivate) {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
resign(
|
||||||
|
tags = earlierVersion.tags,
|
||||||
|
privateTags = privateTags.removeAny(listOf(hashtagTag)).plus(hashtagTag),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.removeAny(listOf(hashtagTag)).plus(hashtagTag),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun remove(
|
||||||
|
earlierVersion: InterestSetEvent,
|
||||||
|
hashtag: String,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): InterestSetEvent {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
return resign(
|
||||||
|
privateTags = privateTags.removeIgnoreCase(HashtagTag.assemble(hashtag)),
|
||||||
|
tags = earlierVersion.tags.removeIgnoreCase(HashtagTag.assemble(hashtag)),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
tags: TagArray,
|
||||||
|
privateTags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
) = resign(
|
||||||
|
content = PrivateTagsInContent.encryptNip44(privateTags, signer),
|
||||||
|
tags = tags,
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
content: String,
|
||||||
|
tags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): InterestSetEvent {
|
||||||
|
val newTags =
|
||||||
|
if (tags.fastAny(AltTag::match)) {
|
||||||
|
tags
|
||||||
|
} else {
|
||||||
|
tags + AltTag.assemble(ALT)
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer.sign(createdAt, KIND, newTags, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
|
suspend fun create(
|
||||||
|
title: String = "",
|
||||||
|
publicHashtags: List<String> = emptyList(),
|
||||||
|
privateHashtags: List<String> = emptyList(),
|
||||||
|
dTag: String = Uuid.random().toString(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): InterestSetEvent {
|
||||||
|
val template = build(title, publicHashtags, privateHashtags, signer, dTag, createdAt)
|
||||||
|
return signer.sign(template)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
|
suspend fun build(
|
||||||
|
title: String = "",
|
||||||
|
publicHashtags: List<String> = emptyList(),
|
||||||
|
privateHashtags: List<String> = emptyList(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
dTag: String = Uuid.random().toString(),
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
initializer: TagArrayBuilder<InterestSetEvent>.() -> Unit = {},
|
||||||
|
) = eventTemplate(
|
||||||
|
kind = KIND,
|
||||||
|
description = PrivateTagsInContent.encryptNip44(privateHashtags.map { HashtagTag.assemble(it) }.toTypedArray(), signer),
|
||||||
|
createdAt = createdAt,
|
||||||
|
) {
|
||||||
|
dTag(dTag)
|
||||||
|
alt(ALT)
|
||||||
|
title(title)
|
||||||
|
hashtags(publicHashtags)
|
||||||
|
|
||||||
|
initializer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.interestSet
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.HashtagTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.TitleTag
|
||||||
|
|
||||||
|
fun TagArrayBuilder<InterestSetEvent>.title(title: String) = addUnique(TitleTag.assemble(title))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<InterestSetEvent>.hashtags(hashtags: List<String>) = addAll(hashtags.map { HashtagTag.assemble(it) })
|
||||||
+166
@@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.kindMuteSet
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.fastAny
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.PrivateTagArrayEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.muteList.tags.UserTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.remove
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
class KindMuteSetEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : PrivateTagArrayEvent(id, pubKey, createdAt, KIND, tags, content, sig),
|
||||||
|
PubKeyHintProvider {
|
||||||
|
override fun pubKeyHints() = tags.mapNotNull(UserTag::parseAsHint)
|
||||||
|
|
||||||
|
override fun linkedPubKeys() = tags.mapNotNull(UserTag::parseKey)
|
||||||
|
|
||||||
|
fun publicMutedUsers() = tags.mapNotNull(UserTag::parse)
|
||||||
|
|
||||||
|
suspend fun privateMutedUsers(signer: NostrSigner) = privateTags(signer)?.mapNotNull(UserTag::parse)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KIND = 30007
|
||||||
|
const val ALT = "Kind Mute Set"
|
||||||
|
|
||||||
|
fun createAddress(
|
||||||
|
pubKey: HexKey,
|
||||||
|
dTag: String,
|
||||||
|
) = Address(KIND, pubKey, dTag)
|
||||||
|
|
||||||
|
suspend fun add(
|
||||||
|
earlierVersion: KindMuteSetEvent,
|
||||||
|
user: UserTag,
|
||||||
|
isPrivate: Boolean,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): KindMuteSetEvent =
|
||||||
|
if (isPrivate) {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
resign(
|
||||||
|
tags = earlierVersion.tags,
|
||||||
|
privateTags = privateTags.plus(user.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.plus(user.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun remove(
|
||||||
|
earlierVersion: KindMuteSetEvent,
|
||||||
|
user: UserTag,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): KindMuteSetEvent {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
return resign(
|
||||||
|
privateTags = privateTags.remove(user.toTagIdOnly()),
|
||||||
|
tags = earlierVersion.tags.remove(user.toTagIdOnly()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
tags: TagArray,
|
||||||
|
privateTags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
) = resign(
|
||||||
|
content = PrivateTagsInContent.encryptNip44(privateTags, signer),
|
||||||
|
tags = tags,
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
content: String,
|
||||||
|
tags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): KindMuteSetEvent {
|
||||||
|
val newTags =
|
||||||
|
if (tags.fastAny(AltTag::match)) {
|
||||||
|
tags
|
||||||
|
} else {
|
||||||
|
tags + AltTag.assemble(ALT)
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer.sign(createdAt, KIND, newTags, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun create(
|
||||||
|
kindNumber: Int,
|
||||||
|
publicMutedUsers: List<UserTag> = emptyList(),
|
||||||
|
privateMutedUsers: List<UserTag> = emptyList(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): KindMuteSetEvent {
|
||||||
|
val template = build(kindNumber, publicMutedUsers, privateMutedUsers, signer, createdAt)
|
||||||
|
return signer.sign(template)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun build(
|
||||||
|
kindNumber: Int,
|
||||||
|
publicMutedUsers: List<UserTag> = emptyList(),
|
||||||
|
privateMutedUsers: List<UserTag> = emptyList(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
initializer: TagArrayBuilder<KindMuteSetEvent>.() -> Unit = {},
|
||||||
|
) = eventTemplate(
|
||||||
|
kind = KIND,
|
||||||
|
description = PrivateTagsInContent.encryptNip44(privateMutedUsers.map { it.toTagArray() }.toTypedArray(), signer),
|
||||||
|
createdAt = createdAt,
|
||||||
|
) {
|
||||||
|
dTag(kindNumber.toString())
|
||||||
|
alt(ALT)
|
||||||
|
peoples(publicMutedUsers)
|
||||||
|
|
||||||
|
initializer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.kindMuteSet
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.muteList.tags.UserTag
|
||||||
|
|
||||||
|
fun TagArrayBuilder<KindMuteSetEvent>.peoples(people: List<UserTag>) = addAll(people.map { it.toTagArray() })
|
||||||
|
|
||||||
|
fun TagArrayBuilder<KindMuteSetEvent>.person(person: UserTag) = add(person.toTagArray())
|
||||||
+142
@@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.mediaFollowList
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.fastAny
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.PrivateTagArrayEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.muteList.tags.UserTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.remove
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
class MediaFollowListEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : PrivateTagArrayEvent(id, pubKey, createdAt, KIND, tags, content, sig),
|
||||||
|
PubKeyHintProvider {
|
||||||
|
override fun pubKeyHints() = tags.mapNotNull(UserTag::parseAsHint)
|
||||||
|
|
||||||
|
override fun linkedPubKeys() = tags.mapNotNull(UserTag::parseKey)
|
||||||
|
|
||||||
|
fun publicFollows() = tags.mapNotNull(UserTag::parse)
|
||||||
|
|
||||||
|
suspend fun privateFollows(signer: NostrSigner) = privateTags(signer)?.mapNotNull(UserTag::parse)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KIND = 10020
|
||||||
|
const val ALT = "Media Follow List"
|
||||||
|
|
||||||
|
fun createAddress(pubKey: HexKey) = Address(KIND, pubKey, "")
|
||||||
|
|
||||||
|
suspend fun create(
|
||||||
|
publicFollows: List<UserTag> = emptyList(),
|
||||||
|
privateFollows: List<UserTag> = emptyList(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): MediaFollowListEvent =
|
||||||
|
resign(
|
||||||
|
content = PrivateTagsInContent.encryptNip44(privateFollows.map { it.toTagArray() }.toTypedArray(), signer),
|
||||||
|
tags = publicFollows.map { it.toTagArray() }.toTypedArray(),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun add(
|
||||||
|
earlierVersion: MediaFollowListEvent,
|
||||||
|
follow: UserTag,
|
||||||
|
isPrivate: Boolean,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): MediaFollowListEvent =
|
||||||
|
if (isPrivate) {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
resign(
|
||||||
|
tags = earlierVersion.tags,
|
||||||
|
privateTags = privateTags.plus(follow.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.plus(follow.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun remove(
|
||||||
|
earlierVersion: MediaFollowListEvent,
|
||||||
|
follow: UserTag,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): MediaFollowListEvent {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
return resign(
|
||||||
|
privateTags = privateTags.remove(follow.toTagIdOnly()),
|
||||||
|
tags = earlierVersion.tags.remove(follow.toTagIdOnly()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
tags: TagArray,
|
||||||
|
privateTags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
) = resign(
|
||||||
|
content = PrivateTagsInContent.encryptNip44(privateTags, signer),
|
||||||
|
tags = tags,
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
content: String,
|
||||||
|
tags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): MediaFollowListEvent {
|
||||||
|
val newTags =
|
||||||
|
if (tags.fastAny(AltTag::match)) {
|
||||||
|
tags
|
||||||
|
} else {
|
||||||
|
tags + AltTag.assemble(ALT)
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer.sign(createdAt, KIND, newTags, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+163
@@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.mediaStarterPack
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.fastAny
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.followList.followIdSet
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.followList.followIds
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.followList.follows
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.muteList.tags.UserTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.remove
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.DescriptionTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.ImageTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.TitleTag
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
class MediaStarterPackEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig),
|
||||||
|
PubKeyHintProvider {
|
||||||
|
override fun pubKeyHints() = tags.mapNotNull(UserTag::parseAsHint)
|
||||||
|
|
||||||
|
override fun linkedPubKeys() = tags.mapNotNull(UserTag::parseKey)
|
||||||
|
|
||||||
|
fun title() = tags.firstNotNullOfOrNull(TitleTag::parse)
|
||||||
|
|
||||||
|
fun description() = tags.firstNotNullOfOrNull(DescriptionTag::parse)
|
||||||
|
|
||||||
|
fun image() = tags.firstNotNullOfOrNull(ImageTag::parse)
|
||||||
|
|
||||||
|
fun follows() = tags.follows()
|
||||||
|
|
||||||
|
fun followIds() = tags.followIds()
|
||||||
|
|
||||||
|
fun followIdSet() = tags.followIdSet()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KIND = 39092
|
||||||
|
const val ALT = "Media Starter Pack"
|
||||||
|
|
||||||
|
fun createAddress(
|
||||||
|
pubKey: HexKey,
|
||||||
|
dTag: String,
|
||||||
|
) = Address(KIND, pubKey, dTag)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
|
suspend fun create(
|
||||||
|
name: String,
|
||||||
|
people: List<UserTag> = emptyList(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
dTag: String = Uuid.random().toString(),
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): MediaStarterPackEvent {
|
||||||
|
val template = build(name, people, dTag, createdAt)
|
||||||
|
return signer.sign(template)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun addUsers(
|
||||||
|
earlierVersion: MediaStarterPackEvent,
|
||||||
|
people: List<UserTag>,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): MediaStarterPackEvent =
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.plus(people.map { it.toTagArray() }),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun add(
|
||||||
|
earlierVersion: MediaStarterPackEvent,
|
||||||
|
person: UserTag,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
) = addUsers(earlierVersion, listOf(person), signer, createdAt)
|
||||||
|
|
||||||
|
suspend fun remove(
|
||||||
|
earlierVersion: MediaStarterPackEvent,
|
||||||
|
person: UserTag,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): MediaStarterPackEvent =
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.remove(person.toTagIdOnly()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
content: String,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): MediaStarterPackEvent {
|
||||||
|
val newTags =
|
||||||
|
if (tags.fastAny(AltTag::match)) {
|
||||||
|
tags
|
||||||
|
} else {
|
||||||
|
tags + AltTag.assemble(ALT)
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer.sign(createdAt, KIND, newTags, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
|
fun build(
|
||||||
|
name: String,
|
||||||
|
people: List<UserTag> = emptyList(),
|
||||||
|
dTag: String = Uuid.random().toString(),
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
initializer: TagArrayBuilder<MediaStarterPackEvent>.() -> Unit = {},
|
||||||
|
) = eventTemplate(
|
||||||
|
kind = KIND,
|
||||||
|
description = "",
|
||||||
|
createdAt = createdAt,
|
||||||
|
) {
|
||||||
|
dTag(dTag)
|
||||||
|
alt(ALT)
|
||||||
|
title(name)
|
||||||
|
people(people)
|
||||||
|
|
||||||
|
initializer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.mediaStarterPack
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.muteList.tags.UserTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.DescriptionTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.ImageTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.TitleTag
|
||||||
|
|
||||||
|
fun TagArrayBuilder<MediaStarterPackEvent>.title(title: String) = addUnique(TitleTag.assemble(title))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<MediaStarterPackEvent>.description(desc: String) = addUnique(DescriptionTag.assemble(desc))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<MediaStarterPackEvent>.image(imageUrl: String) = addUnique(ImageTag.assemble(imageUrl))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<MediaStarterPackEvent>.people(peoples: List<UserTag>) = addAll(peoples.map { it.toTagArray() })
|
||||||
|
|
||||||
|
fun TagArrayBuilder<MediaStarterPackEvent>.person(person: UserTag) = add(person.toTagArray())
|
||||||
+198
@@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.pictureCurationSet
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.fastAny
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.PrivateTagArrayEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.BookmarkIdTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.EventBookmark
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.remove
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.DescriptionTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.ImageTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.TitleTag
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
class PictureCurationSetEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : PrivateTagArrayEvent(id, pubKey, createdAt, KIND, tags, content, sig),
|
||||||
|
EventHintProvider {
|
||||||
|
override fun eventHints() = tags.mapNotNull(EventBookmark::parseAsHint)
|
||||||
|
|
||||||
|
override fun linkedEventIds() = tags.mapNotNull(EventBookmark::parseId)
|
||||||
|
|
||||||
|
fun title() = tags.firstNotNullOfOrNull(TitleTag::parse)
|
||||||
|
|
||||||
|
fun description() = tags.firstNotNullOfOrNull(DescriptionTag::parse)
|
||||||
|
|
||||||
|
fun image() = tags.firstNotNullOfOrNull(ImageTag::parse)
|
||||||
|
|
||||||
|
fun publicItems(): List<BookmarkIdTag> = tags.mapNotNull(BookmarkIdTag::parse)
|
||||||
|
|
||||||
|
suspend fun privateItems(signer: NostrSigner): List<BookmarkIdTag>? = privateTags(signer)?.mapNotNull(BookmarkIdTag::parse)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KIND = 30006
|
||||||
|
const val ALT = "Picture Curation Set"
|
||||||
|
|
||||||
|
fun createAddress(
|
||||||
|
pubKey: HexKey,
|
||||||
|
dTag: String,
|
||||||
|
) = Address(KIND, pubKey, dTag)
|
||||||
|
|
||||||
|
suspend fun add(
|
||||||
|
earlierVersion: PictureCurationSetEvent,
|
||||||
|
item: BookmarkIdTag,
|
||||||
|
isPrivate: Boolean,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): PictureCurationSetEvent =
|
||||||
|
if (isPrivate) {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
resign(
|
||||||
|
tags = earlierVersion.tags,
|
||||||
|
privateTags = privateTags.plus(item.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.plus(item.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun remove(
|
||||||
|
earlierVersion: PictureCurationSetEvent,
|
||||||
|
item: BookmarkIdTag,
|
||||||
|
isPrivate: Boolean,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): PictureCurationSetEvent =
|
||||||
|
if (isPrivate) {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
resign(
|
||||||
|
privateTags = privateTags.remove(item.toTagIdOnly()),
|
||||||
|
tags = earlierVersion.tags,
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.remove(item.toTagIdOnly()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
tags: TagArray,
|
||||||
|
privateTags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
) = resign(
|
||||||
|
content = PrivateTagsInContent.encryptNip44(privateTags, signer),
|
||||||
|
tags = tags,
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
content: String,
|
||||||
|
tags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): PictureCurationSetEvent {
|
||||||
|
val newTags =
|
||||||
|
if (tags.fastAny(AltTag::match)) {
|
||||||
|
tags
|
||||||
|
} else {
|
||||||
|
tags + AltTag.assemble(ALT)
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer.sign(createdAt, KIND, newTags, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
|
suspend fun create(
|
||||||
|
title: String = "",
|
||||||
|
description: String? = null,
|
||||||
|
image: String? = null,
|
||||||
|
publicItems: List<BookmarkIdTag> = emptyList(),
|
||||||
|
privateItems: List<BookmarkIdTag> = emptyList(),
|
||||||
|
dTag: String = Uuid.random().toString(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): PictureCurationSetEvent {
|
||||||
|
val template =
|
||||||
|
build(title, publicItems, privateItems, signer, dTag, createdAt) {
|
||||||
|
if (description != null) description(description)
|
||||||
|
if (image != null) image(image)
|
||||||
|
}
|
||||||
|
return signer.sign(template)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
|
suspend fun build(
|
||||||
|
title: String = "",
|
||||||
|
publicItems: List<BookmarkIdTag> = emptyList(),
|
||||||
|
privateItems: List<BookmarkIdTag> = emptyList(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
dTag: String = Uuid.random().toString(),
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
initializer: TagArrayBuilder<PictureCurationSetEvent>.() -> Unit = {},
|
||||||
|
) = eventTemplate(
|
||||||
|
kind = KIND,
|
||||||
|
description = PrivateTagsInContent.encryptNip44(privateItems.map { it.toTagArray() }.toTypedArray(), signer),
|
||||||
|
createdAt = createdAt,
|
||||||
|
) {
|
||||||
|
dTag(dTag)
|
||||||
|
alt(ALT)
|
||||||
|
title(title)
|
||||||
|
bookmarks(publicItems)
|
||||||
|
|
||||||
|
initializer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.pictureCurationSet
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.BookmarkIdTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.DescriptionTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.ImageTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.TitleTag
|
||||||
|
|
||||||
|
fun TagArrayBuilder<PictureCurationSetEvent>.title(title: String) = addUnique(TitleTag.assemble(title))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<PictureCurationSetEvent>.description(desc: String) = addUnique(DescriptionTag.assemble(desc))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<PictureCurationSetEvent>.image(imageUrl: String) = addUnique(ImageTag.assemble(imageUrl))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<PictureCurationSetEvent>.bookmarks(items: List<BookmarkIdTag>) = addAll(items.map { it.toTagArray() })
|
||||||
+158
@@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.releaseArtifactSet
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.fastAny
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.AddressBookmark
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.BookmarkIdTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.EventBookmark
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.remove
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.DescriptionTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.TitleTag
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
class ReleaseArtifactSetEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig),
|
||||||
|
EventHintProvider,
|
||||||
|
AddressHintProvider {
|
||||||
|
override fun eventHints() = tags.mapNotNull(EventBookmark::parseAsHint)
|
||||||
|
|
||||||
|
override fun linkedEventIds() = tags.mapNotNull(EventBookmark::parseId)
|
||||||
|
|
||||||
|
override fun addressHints() = tags.mapNotNull(AddressBookmark::parseAsHint)
|
||||||
|
|
||||||
|
override fun linkedAddressIds() = tags.mapNotNull(AddressBookmark::parseAddressId)
|
||||||
|
|
||||||
|
fun title() = tags.firstNotNullOfOrNull(TitleTag::parse)
|
||||||
|
|
||||||
|
fun description() = tags.firstNotNullOfOrNull(DescriptionTag::parse)
|
||||||
|
|
||||||
|
fun items(): List<BookmarkIdTag> = tags.mapNotNull(BookmarkIdTag::parse)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KIND = 30063
|
||||||
|
const val ALT = "Release Artifact Set"
|
||||||
|
|
||||||
|
fun createAddress(
|
||||||
|
pubKey: HexKey,
|
||||||
|
dTag: String,
|
||||||
|
) = Address(KIND, pubKey, dTag)
|
||||||
|
|
||||||
|
suspend fun add(
|
||||||
|
earlierVersion: ReleaseArtifactSetEvent,
|
||||||
|
item: BookmarkIdTag,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): ReleaseArtifactSetEvent =
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.plus(item.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun remove(
|
||||||
|
earlierVersion: ReleaseArtifactSetEvent,
|
||||||
|
item: BookmarkIdTag,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): ReleaseArtifactSetEvent =
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.remove(item.toTagIdOnly()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
content: String,
|
||||||
|
tags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): ReleaseArtifactSetEvent {
|
||||||
|
val newTags =
|
||||||
|
if (tags.fastAny(AltTag::match)) {
|
||||||
|
tags
|
||||||
|
} else {
|
||||||
|
tags + AltTag.assemble(ALT)
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer.sign(createdAt, KIND, newTags, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
|
suspend fun create(
|
||||||
|
title: String = "",
|
||||||
|
description: String? = null,
|
||||||
|
items: List<BookmarkIdTag> = emptyList(),
|
||||||
|
dTag: String = Uuid.random().toString(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): ReleaseArtifactSetEvent {
|
||||||
|
val template =
|
||||||
|
build(title, items, dTag, createdAt) {
|
||||||
|
if (description != null) description(description)
|
||||||
|
}
|
||||||
|
return signer.sign(template)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
|
fun build(
|
||||||
|
title: String = "",
|
||||||
|
items: List<BookmarkIdTag> = emptyList(),
|
||||||
|
dTag: String = Uuid.random().toString(),
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
initializer: TagArrayBuilder<ReleaseArtifactSetEvent>.() -> Unit = {},
|
||||||
|
) = eventTemplate(
|
||||||
|
kind = KIND,
|
||||||
|
description = "",
|
||||||
|
createdAt = createdAt,
|
||||||
|
) {
|
||||||
|
dTag(dTag)
|
||||||
|
alt(ALT)
|
||||||
|
title(title)
|
||||||
|
artifacts(items)
|
||||||
|
|
||||||
|
initializer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.releaseArtifactSet
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.BookmarkIdTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.DescriptionTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.TitleTag
|
||||||
|
|
||||||
|
fun TagArrayBuilder<ReleaseArtifactSetEvent>.title(title: String) = addUnique(TitleTag.assemble(title))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<ReleaseArtifactSetEvent>.description(desc: String) = addUnique(DescriptionTag.assemble(desc))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<ReleaseArtifactSetEvent>.artifacts(items: List<BookmarkIdTag>) = addAll(items.map { it.toTagArray() })
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.simpleGroupList
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||||
|
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||||
|
import com.vitorpamplona.quartz.utils.ensure
|
||||||
|
|
||||||
|
class GroupTag(
|
||||||
|
val groupId: String,
|
||||||
|
val relayUrl: String,
|
||||||
|
val name: String? = null,
|
||||||
|
) {
|
||||||
|
fun toTagArray() = assemble(groupId, relayUrl, name)
|
||||||
|
|
||||||
|
fun toTagIdOnly() = arrayOf(TAG_NAME, groupId, relayUrl)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG_NAME = "group"
|
||||||
|
|
||||||
|
fun parse(tag: Array<String>): GroupTag? {
|
||||||
|
ensure(tag.has(2)) { return null }
|
||||||
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
|
ensure(tag[1].isNotEmpty()) { return null }
|
||||||
|
ensure(tag[2].isNotEmpty()) { return null }
|
||||||
|
return GroupTag(tag[1], tag[2], tag.getOrNull(3))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assemble(
|
||||||
|
groupId: String,
|
||||||
|
relayUrl: String,
|
||||||
|
name: String? = null,
|
||||||
|
) = arrayOfNotNull(TAG_NAME, groupId, relayUrl, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
+135
@@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.simpleGroupList
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.fastAny
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.PrivateTagArrayEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.remove
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
class SimpleGroupListEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : PrivateTagArrayEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||||
|
fun publicGroups() = tags.mapNotNull(GroupTag::parse)
|
||||||
|
|
||||||
|
suspend fun privateGroups(signer: NostrSigner) = privateTags(signer)?.mapNotNull(GroupTag::parse)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KIND = 10009
|
||||||
|
const val ALT = "Simple Groups List"
|
||||||
|
|
||||||
|
fun createAddress(pubKey: HexKey) = Address(KIND, pubKey, "")
|
||||||
|
|
||||||
|
suspend fun create(
|
||||||
|
publicGroups: List<GroupTag> = emptyList(),
|
||||||
|
privateGroups: List<GroupTag> = emptyList(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): SimpleGroupListEvent =
|
||||||
|
resign(
|
||||||
|
content = PrivateTagsInContent.encryptNip44(privateGroups.map { it.toTagArray() }.toTypedArray(), signer),
|
||||||
|
tags = publicGroups.map { it.toTagArray() }.toTypedArray(),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun add(
|
||||||
|
earlierVersion: SimpleGroupListEvent,
|
||||||
|
group: GroupTag,
|
||||||
|
isPrivate: Boolean,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): SimpleGroupListEvent =
|
||||||
|
if (isPrivate) {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
resign(
|
||||||
|
tags = earlierVersion.tags,
|
||||||
|
privateTags = privateTags.plus(group.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.plus(group.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun remove(
|
||||||
|
earlierVersion: SimpleGroupListEvent,
|
||||||
|
group: GroupTag,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): SimpleGroupListEvent {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
return resign(
|
||||||
|
privateTags = privateTags.remove(group.toTagIdOnly()),
|
||||||
|
tags = earlierVersion.tags.remove(group.toTagIdOnly()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
tags: TagArray,
|
||||||
|
privateTags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
) = resign(
|
||||||
|
content = PrivateTagsInContent.encryptNip44(privateTags, signer),
|
||||||
|
tags = tags,
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
content: String,
|
||||||
|
tags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): SimpleGroupListEvent {
|
||||||
|
val newTags =
|
||||||
|
if (tags.fastAny(AltTag::match)) {
|
||||||
|
tags
|
||||||
|
} else {
|
||||||
|
tags + AltTag.assemble(ALT)
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer.sign(createdAt, KIND, newTags, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.videoCurationSet
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.BookmarkIdTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.DescriptionTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.ImageTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.TitleTag
|
||||||
|
|
||||||
|
fun TagArrayBuilder<VideoCurationSetEvent>.title(title: String) = addUnique(TitleTag.assemble(title))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<VideoCurationSetEvent>.description(desc: String) = addUnique(DescriptionTag.assemble(desc))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<VideoCurationSetEvent>.image(imageUrl: String) = addUnique(ImageTag.assemble(imageUrl))
|
||||||
|
|
||||||
|
fun TagArrayBuilder<VideoCurationSetEvent>.bookmarks(items: List<BookmarkIdTag>) = addAll(items.map { it.toTagArray() })
|
||||||
+205
@@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip51Lists.videoCurationSet
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.fastAny
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||||
|
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.PrivateTagArrayEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.AddressBookmark
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.BookmarkIdTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.EventBookmark
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.remove
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.DescriptionTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.ImageTag
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.tags.TitleTag
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
class VideoCurationSetEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : PrivateTagArrayEvent(id, pubKey, createdAt, KIND, tags, content, sig),
|
||||||
|
EventHintProvider,
|
||||||
|
AddressHintProvider {
|
||||||
|
override fun eventHints() = tags.mapNotNull(EventBookmark::parseAsHint)
|
||||||
|
|
||||||
|
override fun linkedEventIds() = tags.mapNotNull(EventBookmark::parseId)
|
||||||
|
|
||||||
|
override fun addressHints() = tags.mapNotNull(AddressBookmark::parseAsHint)
|
||||||
|
|
||||||
|
override fun linkedAddressIds() = tags.mapNotNull(AddressBookmark::parseAddressId)
|
||||||
|
|
||||||
|
fun title() = tags.firstNotNullOfOrNull(TitleTag::parse)
|
||||||
|
|
||||||
|
fun description() = tags.firstNotNullOfOrNull(DescriptionTag::parse)
|
||||||
|
|
||||||
|
fun image() = tags.firstNotNullOfOrNull(ImageTag::parse)
|
||||||
|
|
||||||
|
fun publicItems(): List<BookmarkIdTag> = tags.mapNotNull(BookmarkIdTag::parse)
|
||||||
|
|
||||||
|
suspend fun privateItems(signer: NostrSigner): List<BookmarkIdTag>? = privateTags(signer)?.mapNotNull(BookmarkIdTag::parse)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KIND = 30005
|
||||||
|
const val ALT = "Video Curation Set"
|
||||||
|
|
||||||
|
fun createAddress(
|
||||||
|
pubKey: HexKey,
|
||||||
|
dTag: String,
|
||||||
|
) = Address(KIND, pubKey, dTag)
|
||||||
|
|
||||||
|
suspend fun add(
|
||||||
|
earlierVersion: VideoCurationSetEvent,
|
||||||
|
item: BookmarkIdTag,
|
||||||
|
isPrivate: Boolean,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): VideoCurationSetEvent =
|
||||||
|
if (isPrivate) {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
resign(
|
||||||
|
tags = earlierVersion.tags,
|
||||||
|
privateTags = privateTags.plus(item.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.plus(item.toTagArray()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun remove(
|
||||||
|
earlierVersion: VideoCurationSetEvent,
|
||||||
|
item: BookmarkIdTag,
|
||||||
|
isPrivate: Boolean,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): VideoCurationSetEvent =
|
||||||
|
if (isPrivate) {
|
||||||
|
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
|
||||||
|
resign(
|
||||||
|
privateTags = privateTags.remove(item.toTagIdOnly()),
|
||||||
|
tags = earlierVersion.tags,
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
resign(
|
||||||
|
content = earlierVersion.content,
|
||||||
|
tags = earlierVersion.tags.remove(item.toTagIdOnly()),
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
tags: TagArray,
|
||||||
|
privateTags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
) = resign(
|
||||||
|
content = PrivateTagsInContent.encryptNip44(privateTags, signer),
|
||||||
|
tags = tags,
|
||||||
|
signer = signer,
|
||||||
|
createdAt = createdAt,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun resign(
|
||||||
|
content: String,
|
||||||
|
tags: TagArray,
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): VideoCurationSetEvent {
|
||||||
|
val newTags =
|
||||||
|
if (tags.fastAny(AltTag::match)) {
|
||||||
|
tags
|
||||||
|
} else {
|
||||||
|
tags + AltTag.assemble(ALT)
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer.sign(createdAt, KIND, newTags, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
|
suspend fun create(
|
||||||
|
title: String = "",
|
||||||
|
description: String? = null,
|
||||||
|
image: String? = null,
|
||||||
|
publicItems: List<BookmarkIdTag> = emptyList(),
|
||||||
|
privateItems: List<BookmarkIdTag> = emptyList(),
|
||||||
|
dTag: String = Uuid.random().toString(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
): VideoCurationSetEvent {
|
||||||
|
val template =
|
||||||
|
build(title, publicItems, privateItems, signer, dTag, createdAt) {
|
||||||
|
if (description != null) description(description)
|
||||||
|
if (image != null) image(image)
|
||||||
|
}
|
||||||
|
return signer.sign(template)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
|
suspend fun build(
|
||||||
|
title: String = "",
|
||||||
|
publicItems: List<BookmarkIdTag> = emptyList(),
|
||||||
|
privateItems: List<BookmarkIdTag> = emptyList(),
|
||||||
|
signer: NostrSigner,
|
||||||
|
dTag: String = Uuid.random().toString(),
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
initializer: TagArrayBuilder<VideoCurationSetEvent>.() -> Unit = {},
|
||||||
|
) = eventTemplate(
|
||||||
|
kind = KIND,
|
||||||
|
description = PrivateTagsInContent.encryptNip44(privateItems.map { it.toTagArray() }.toTypedArray(), signer),
|
||||||
|
createdAt = createdAt,
|
||||||
|
) {
|
||||||
|
dTag(dTag)
|
||||||
|
alt(ALT)
|
||||||
|
title(title)
|
||||||
|
bookmarks(publicItems)
|
||||||
|
|
||||||
|
initializer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -111,14 +111,25 @@ import com.vitorpamplona.quartz.nip47WalletConnect.events.NwcInfoEvent
|
|||||||
import com.vitorpamplona.quartz.nip47WalletConnect.events.NwcNotificationEvent
|
import com.vitorpamplona.quartz.nip47WalletConnect.events.NwcNotificationEvent
|
||||||
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
|
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.PinListEvent
|
import com.vitorpamplona.quartz.nip51Lists.PinListEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.appCurationSet.AppCurationSetEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.articleCurationSet.ArticleCurationSetEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.OldBookmarkListEvent
|
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.OldBookmarkListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent
|
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent
|
import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.gitAuthorList.GitAuthorListEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.gitRepositoryList.GitRepositoryListEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.goodWikiAuthorList.GoodWikiAuthorListEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.goodWikiRelayList.GoodWikiRelayListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListEvent
|
import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.interestSet.InterestSetEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.kindMuteSet.KindMuteSetEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.LabeledBookmarkListEvent
|
import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.LabeledBookmarkListEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.mediaFollowList.MediaFollowListEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.mediaStarterPack.MediaStarterPackEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent
|
import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent
|
import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.pictureCurationSet.PictureCurationSetEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent
|
import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.BroadcastRelayListEvent
|
import com.vitorpamplona.quartz.nip51Lists.relayLists.BroadcastRelayListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent
|
import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent
|
||||||
@@ -126,6 +137,9 @@ import com.vitorpamplona.quartz.nip51Lists.relayLists.ProxyRelayListEvent
|
|||||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent
|
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.TrustedRelayListEvent
|
import com.vitorpamplona.quartz.nip51Lists.relayLists.TrustedRelayListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.relaySets.RelaySetEvent
|
import com.vitorpamplona.quartz.nip51Lists.relaySets.RelaySetEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.releaseArtifactSet.ReleaseArtifactSetEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.simpleGroupList.SimpleGroupListEvent
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.videoCurationSet.VideoCurationSetEvent
|
||||||
import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent
|
import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent
|
||||||
import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent
|
import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent
|
||||||
import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarEvent
|
import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarEvent
|
||||||
@@ -271,6 +285,7 @@ class EventFactory {
|
|||||||
when (kind) {
|
when (kind) {
|
||||||
AcceptedBadgeSetEvent.KIND -> AcceptedBadgeSetEvent(id, pubKey, createdAt, tags, content, sig)
|
AcceptedBadgeSetEvent.KIND -> AcceptedBadgeSetEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
AdvertisedRelayListEvent.KIND -> AdvertisedRelayListEvent(id, pubKey, createdAt, tags, content, sig)
|
AdvertisedRelayListEvent.KIND -> AdvertisedRelayListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
AppCurationSetEvent.KIND -> AppCurationSetEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
AppDefinitionEvent.KIND -> AppDefinitionEvent(id, pubKey, createdAt, tags, content, sig)
|
AppDefinitionEvent.KIND -> AppDefinitionEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
AppRecommendationEvent.KIND -> AppRecommendationEvent(id, pubKey, createdAt, tags, content, sig)
|
AppRecommendationEvent.KIND -> AppRecommendationEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
AppSpecificDataEvent.KIND -> AppSpecificDataEvent(id, pubKey, createdAt, tags, content, sig)
|
AppSpecificDataEvent.KIND -> AppSpecificDataEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
@@ -278,6 +293,7 @@ class EventFactory {
|
|||||||
AttestationRequestEvent.KIND -> AttestationRequestEvent(id, pubKey, createdAt, tags, content, sig)
|
AttestationRequestEvent.KIND -> AttestationRequestEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
AttestorRecommendationEvent.KIND -> AttestorRecommendationEvent(id, pubKey, createdAt, tags, content, sig)
|
AttestorRecommendationEvent.KIND -> AttestorRecommendationEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
AttestorProficiencyEvent.KIND -> AttestorProficiencyEvent(id, pubKey, createdAt, tags, content, sig)
|
AttestorProficiencyEvent.KIND -> AttestorProficiencyEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
ArticleCurationSetEvent.KIND -> ArticleCurationSetEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
AudioHeaderEvent.KIND -> AudioHeaderEvent(id, pubKey, createdAt, tags, content, sig)
|
AudioHeaderEvent.KIND -> AudioHeaderEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
AuctionEvent.KIND -> AuctionEvent(id, pubKey, createdAt, tags, content, sig)
|
AuctionEvent.KIND -> AuctionEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
AudioTrackEvent.KIND -> AudioTrackEvent(id, pubKey, createdAt, tags, content, sig)
|
AudioTrackEvent.KIND -> AudioTrackEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
@@ -356,19 +372,25 @@ class EventFactory {
|
|||||||
GenericRepostEvent.KIND -> GenericRepostEvent(id, pubKey, createdAt, tags, content, sig)
|
GenericRepostEvent.KIND -> GenericRepostEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
GeohashListEvent.KIND -> GeohashListEvent(id, pubKey, createdAt, tags, content, sig)
|
GeohashListEvent.KIND -> GeohashListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
GiftWrapEvent.KIND -> GiftWrapEvent(id, pubKey, createdAt, tags, content, sig)
|
GiftWrapEvent.KIND -> GiftWrapEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
GitAuthorListEvent.KIND -> GitAuthorListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
GitRepositoryListEvent.KIND -> GitRepositoryListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
GitIssueEvent.KIND -> GitIssueEvent(id, pubKey, createdAt, tags, content, sig)
|
GitIssueEvent.KIND -> GitIssueEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
GitReplyEvent.KIND -> GitReplyEvent(id, pubKey, createdAt, tags, content, sig)
|
GitReplyEvent.KIND -> GitReplyEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
GitPatchEvent.KIND -> GitPatchEvent(id, pubKey, createdAt, tags, content, sig)
|
GitPatchEvent.KIND -> GitPatchEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
GitRepositoryEvent.KIND -> GitRepositoryEvent(id, pubKey, createdAt, tags, content, sig)
|
GitRepositoryEvent.KIND -> GitRepositoryEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
GoodWikiAuthorListEvent.KIND -> GoodWikiAuthorListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
GoodWikiRelayListEvent.KIND -> GoodWikiRelayListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
GoalEvent.KIND -> GoalEvent(id, pubKey, createdAt, tags, content, sig)
|
GoalEvent.KIND -> GoalEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
HashtagListEvent.KIND -> HashtagListEvent(id, pubKey, createdAt, tags, content, sig)
|
HashtagListEvent.KIND -> HashtagListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
HighlightEvent.KIND -> HighlightEvent(id, pubKey, createdAt, tags, content, sig)
|
HighlightEvent.KIND -> HighlightEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
HTTPAuthorizationEvent.KIND -> HTTPAuthorizationEvent(id, pubKey, createdAt, tags, content, sig)
|
HTTPAuthorizationEvent.KIND -> HTTPAuthorizationEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
IndexerRelayListEvent.KIND -> IndexerRelayListEvent(id, pubKey, createdAt, tags, content, sig)
|
IndexerRelayListEvent.KIND -> IndexerRelayListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
InterestSetEvent.KIND -> InterestSetEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
InteractiveStoryPrologueEvent.KIND -> InteractiveStoryPrologueEvent(id, pubKey, createdAt, tags, content, sig)
|
InteractiveStoryPrologueEvent.KIND -> InteractiveStoryPrologueEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
InteractiveStorySceneEvent.KIND -> InteractiveStorySceneEvent(id, pubKey, createdAt, tags, content, sig)
|
InteractiveStorySceneEvent.KIND -> InteractiveStorySceneEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
InteractiveStoryReadingStateEvent.KIND -> InteractiveStoryReadingStateEvent(id, pubKey, createdAt, tags, content, sig)
|
InteractiveStoryReadingStateEvent.KIND -> InteractiveStoryReadingStateEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
LabelEvent.KIND -> LabelEvent(id, pubKey, createdAt, tags, content, sig)
|
LabelEvent.KIND -> LabelEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
KindMuteSetEvent.KIND -> KindMuteSetEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
LabeledBookmarkListEvent.KIND -> LabeledBookmarkListEvent(id, pubKey, createdAt, tags, content, sig)
|
LabeledBookmarkListEvent.KIND -> LabeledBookmarkListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
LiveActivitiesChatMessageEvent.KIND -> LiveActivitiesChatMessageEvent(id, pubKey, createdAt, tags, content, sig)
|
LiveActivitiesChatMessageEvent.KIND -> LiveActivitiesChatMessageEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
LiveActivitiesEvent.KIND -> LiveActivitiesEvent(id, pubKey, createdAt, tags, content, sig)
|
LiveActivitiesEvent.KIND -> LiveActivitiesEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
@@ -386,6 +408,8 @@ class EventFactory {
|
|||||||
MeetingRoomPresenceEvent.KIND -> MeetingRoomPresenceEvent(id, pubKey, createdAt, tags, content, sig)
|
MeetingRoomPresenceEvent.KIND -> MeetingRoomPresenceEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
MeetingSpaceEvent.KIND -> MeetingSpaceEvent(id, pubKey, createdAt, tags, content, sig)
|
MeetingSpaceEvent.KIND -> MeetingSpaceEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
MintRecommendationEvent.KIND -> MintRecommendationEvent(id, pubKey, createdAt, tags, content, sig)
|
MintRecommendationEvent.KIND -> MintRecommendationEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
MediaFollowListEvent.KIND -> MediaFollowListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
MediaStarterPackEvent.KIND -> MediaStarterPackEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
MetadataEvent.KIND -> MetadataEvent(id, pubKey, createdAt, tags, content, sig)
|
MetadataEvent.KIND -> MetadataEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
MuteListEvent.KIND -> MuteListEvent(id, pubKey, createdAt, tags, content, sig)
|
MuteListEvent.KIND -> MuteListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
NamedSiteEvent.KIND -> NamedSiteEvent(id, pubKey, createdAt, tags, content, sig)
|
NamedSiteEvent.KIND -> NamedSiteEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
@@ -437,6 +461,7 @@ class EventFactory {
|
|||||||
OtsEvent.KIND -> OtsEvent(id, pubKey, createdAt, tags, content, sig)
|
OtsEvent.KIND -> OtsEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
PaymentTargetsEvent.KIND -> PaymentTargetsEvent(id, pubKey, createdAt, tags, content, sig)
|
PaymentTargetsEvent.KIND -> PaymentTargetsEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
PeopleListEvent.KIND -> PeopleListEvent(id, pubKey, createdAt, tags, content, sig)
|
PeopleListEvent.KIND -> PeopleListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
PictureCurationSetEvent.KIND -> PictureCurationSetEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
P2POrderEvent.KIND -> P2POrderEvent(id, pubKey, createdAt, tags, content, sig)
|
P2POrderEvent.KIND -> P2POrderEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
PictureEvent.KIND -> PictureEvent(id, pubKey, createdAt, tags, content, sig)
|
PictureEvent.KIND -> PictureEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
PinListEvent.KIND -> PinListEvent(id, pubKey, createdAt, tags, content, sig)
|
PinListEvent.KIND -> PinListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
@@ -463,6 +488,7 @@ class EventFactory {
|
|||||||
RelayAuthEvent.KIND -> RelayAuthEvent(id, pubKey, createdAt, tags, content, sig)
|
RelayAuthEvent.KIND -> RelayAuthEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
RelayDiscoveryEvent.KIND -> RelayDiscoveryEvent(id, pubKey, createdAt, tags, content, sig)
|
RelayDiscoveryEvent.KIND -> RelayDiscoveryEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
RelayMonitorEvent.KIND -> RelayMonitorEvent(id, pubKey, createdAt, tags, content, sig)
|
RelayMonitorEvent.KIND -> RelayMonitorEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
ReleaseArtifactSetEvent.KIND -> ReleaseArtifactSetEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
RelaySetEvent.KIND -> RelaySetEvent(id, pubKey, createdAt, tags, content, sig)
|
RelaySetEvent.KIND -> RelaySetEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
ReportEvent.KIND -> ReportEvent(id, pubKey, createdAt, tags, content, sig)
|
ReportEvent.KIND -> ReportEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
RootSiteEvent.KIND -> RootSiteEvent(id, pubKey, createdAt, tags, content, sig)
|
RootSiteEvent.KIND -> RootSiteEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
@@ -470,6 +496,7 @@ class EventFactory {
|
|||||||
RequestToVanishEvent.KIND -> RequestToVanishEvent(id, pubKey, createdAt, tags, content, sig)
|
RequestToVanishEvent.KIND -> RequestToVanishEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
SealedRumorEvent.KIND -> SealedRumorEvent(id, pubKey, createdAt, tags, content, sig)
|
SealedRumorEvent.KIND -> SealedRumorEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
SearchRelayListEvent.KIND -> SearchRelayListEvent(id, pubKey, createdAt, tags, content, sig)
|
SearchRelayListEvent.KIND -> SearchRelayListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
SimpleGroupListEvent.KIND -> SimpleGroupListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
StallEvent.KIND -> StallEvent(id, pubKey, createdAt, tags, content, sig)
|
StallEvent.KIND -> StallEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
StatusEvent.KIND -> StatusEvent(id, pubKey, createdAt, tags, content, sig)
|
StatusEvent.KIND -> StatusEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
TextNoteEvent.KIND -> TextNoteEvent(id, pubKey, createdAt, tags, content, sig)
|
TextNoteEvent.KIND -> TextNoteEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
@@ -480,6 +507,7 @@ class EventFactory {
|
|||||||
TorrentCommentEvent.KIND -> TorrentCommentEvent(id, pubKey, createdAt, tags, content, sig)
|
TorrentCommentEvent.KIND -> TorrentCommentEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
TrustedRelayListEvent.KIND -> TrustedRelayListEvent(id, pubKey, createdAt, tags, content, sig)
|
TrustedRelayListEvent.KIND -> TrustedRelayListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
TrustProviderListEvent.KIND -> TrustProviderListEvent(id, pubKey, createdAt, tags, content, sig)
|
TrustProviderListEvent.KIND -> TrustProviderListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
VideoCurationSetEvent.KIND -> VideoCurationSetEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
VideoHorizontalEvent.KIND -> VideoHorizontalEvent(id, pubKey, createdAt, tags, content, sig)
|
VideoHorizontalEvent.KIND -> VideoHorizontalEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
VideoVerticalEvent.KIND -> VideoVerticalEvent(id, pubKey, createdAt, tags, content, sig)
|
VideoVerticalEvent.KIND -> VideoVerticalEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
VideoNormalEvent.KIND -> VideoNormalEvent(id, pubKey, createdAt, tags, content, sig)
|
VideoNormalEvent.KIND -> VideoNormalEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
|||||||
Reference in New Issue
Block a user