From ce3de5caf7fa4d14ff25ef221492198446931a1b Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 30 Mar 2026 02:22:54 +0000 Subject: [PATCH] refactor: restructure NIP90 DVMs to match NIP88 Polls pattern Each event kind now gets its own subpackage with dedicated tag classes, TagArrayExt for parsing, and TagArrayBuilderExt for building. Events use the eventTemplate pattern instead of manual tag construction. New structure: - status/ (kind 7000) with StatusTag, AmountTag - contentDiscoveryRequest/ (kind 5300) with RelaysTag, ParamTag - contentDiscoveryResponse/ (kind 6300) - userDiscoveryRequest/ (kind 5301) - userDiscoveryResponse/ (kind 6301) https://claude.ai/code/session_01JeB9nAK4SKuwKZBEd6EAPd --- .../vitorpamplona/amethyst/model/Account.kt | 4 +- .../amethyst/model/LocalCache.kt | 10 ++-- .../FilterRepliesAndReactionsToNotes.kt | 4 +- .../amethyst/ui/note/NoteCompose.kt | 4 +- .../types/NIP90ContentDiscoveryResponse.kt | 2 +- .../amethyst/ui/note/types/NIP90Status.kt | 2 +- .../ui/screen/loggedIn/AccountViewModel.kt | 2 +- .../dvms/DvmContentDiscoveryScreen.kt | 4 +- .../NIP90ContentDiscoveryResponseFilter.kt | 2 +- .../loggedIn/relays/RelayInformationScreen.kt | 10 ++-- .../NIP90ContentDiscoveryRequestEvent.kt | 27 +++++---- .../TagArrayBuilderExt.kt | 36 ++++++++++++ .../contentDiscoveryRequest/TagArrayExt.kt | 31 ++++++++++ .../contentDiscoveryRequest/tags/ParamTag.kt | 53 +++++++++++++++++ .../contentDiscoveryRequest/tags/RelaysTag.kt | 46 +++++++++++++++ .../NIP90ContentDiscoveryResponseEvent.kt | 20 +++---- .../{ => status}/NIP90StatusEvent.kt | 57 ++++-------------- .../nip90Dvms/status/TagArrayBuilderExt.kt | 37 ++++++++++++ .../quartz/nip90Dvms/status/TagArrayExt.kt | 29 ++++++++++ .../quartz/nip90Dvms/status/tags/AmountTag.kt | 57 ++++++++++++++++++ .../quartz/nip90Dvms/status/tags/StatusTag.kt | 58 +++++++++++++++++++ .../NIP90UserDiscoveryRequestEvent.kt | 22 ++++--- .../NIP90UserDiscoveryResponseEvent.kt | 22 ++++--- .../quartz/utils/EventFactory.kt | 10 ++-- 24 files changed, 427 insertions(+), 122 deletions(-) rename quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/{ => contentDiscoveryRequest}/NIP90ContentDiscoveryRequestEvent.kt (73%) create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/TagArrayBuilderExt.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/TagArrayExt.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/tags/ParamTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/tags/RelaysTag.kt rename quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/{ => contentDiscoveryResponse}/NIP90ContentDiscoveryResponseEvent.kt (85%) rename quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/{ => status}/NIP90StatusEvent.kt (54%) create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/TagArrayBuilderExt.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/TagArrayExt.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/tags/AmountTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/tags/StatusTag.kt rename quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/{ => userDiscoveryRequest}/NIP90UserDiscoveryRequestEvent.kt (77%) rename quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/{ => userDiscoveryResponse}/NIP90UserDiscoveryResponseEvent.kt (77%) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index aabc4be72..7fa36f404 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -200,7 +200,7 @@ import com.vitorpamplona.quartz.nip72ModCommunities.approval.CommunityPostApprov import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent import com.vitorpamplona.quartz.nip88Polls.response.PollResponseEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryRequestEvent +import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryRequest.NIP90ContentDiscoveryRequestEvent import com.vitorpamplona.quartz.nip92IMeta.IMetaTag import com.vitorpamplona.quartz.nip92IMeta.imetas import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent @@ -1881,7 +1881,7 @@ class Account( onReady: (event: NIP90ContentDiscoveryRequestEvent) -> Unit, ) { val relays = nip65RelayList.inboxFlow.value.toSet() - val request = NIP90ContentDiscoveryRequestEvent.create(dvmPublicKey.pubkeyHex, signer.pubKey, relays, signer) + val request = signer.sign(NIP90ContentDiscoveryRequestEvent.build(dvmPublicKey.pubkeyHex, signer.pubKey, relays)) val relayList = dvmPublicKey.inboxRelays()?.toSet()?.ifEmpty { null } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index 97c9e4234..c138c431f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -203,11 +203,11 @@ import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent import com.vitorpamplona.quartz.nip88Polls.response.PollResponseEvent import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent import com.vitorpamplona.quartz.nip89AppHandlers.recommendation.AppRecommendationEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryRequestEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryResponseEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90StatusEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90UserDiscoveryRequestEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90UserDiscoveryResponseEvent +import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryRequest.NIP90ContentDiscoveryRequestEvent +import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent +import com.vitorpamplona.quartz.nip90Dvms.status.NIP90StatusEvent +import com.vitorpamplona.quartz.nip90Dvms.userDiscoveryRequest.NIP90UserDiscoveryRequestEvent +import com.vitorpamplona.quartz.nip90Dvms.userDiscoveryResponse.NIP90UserDiscoveryResponseEvent import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent import com.vitorpamplona.quartz.nip96FileStorage.config.FileServersEvent import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/event/watchers/FilterRepliesAndReactionsToNotes.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/event/watchers/FilterRepliesAndReactionsToNotes.kt index 57faeb9f0..0160962bb 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/event/watchers/FilterRepliesAndReactionsToNotes.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/event/watchers/FilterRepliesAndReactionsToNotes.kt @@ -41,8 +41,8 @@ import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent import com.vitorpamplona.quartz.nip56Reports.ReportEvent import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip88Polls.response.PollResponseEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryResponseEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90StatusEvent +import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent +import com.vitorpamplona.quartz.nip90Dvms.status.NIP90StatusEvent import com.vitorpamplona.quartz.utils.mapOfSet val RepliesAndReactionsKinds = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt index aa1518e82..928c8ebb5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt @@ -282,8 +282,8 @@ import com.vitorpamplona.quartz.nip87Ecash.fedimint.FedimintEvent import com.vitorpamplona.quartz.nip87Ecash.recommendation.MintRecommendationEvent import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryResponseEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90StatusEvent +import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent +import com.vitorpamplona.quartz.nip90Dvms.status.NIP90StatusEvent import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent import com.vitorpamplona.quartz.nipA0VoiceMessages.BaseVoiceEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/NIP90ContentDiscoveryResponse.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/NIP90ContentDiscoveryResponse.kt index 894dfacab..5f73c4c0c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/NIP90ContentDiscoveryResponse.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/NIP90ContentDiscoveryResponse.kt @@ -35,7 +35,7 @@ import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hasHashtags -import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryResponseEvent +import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent @Composable fun RenderNIP90ContentDiscoveryResponse( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/NIP90Status.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/NIP90Status.kt index 16e1e66c2..c039390d3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/NIP90Status.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/NIP90Status.kt @@ -25,7 +25,7 @@ import androidx.compose.runtime.Composable import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.quartz.nip90Dvms.NIP90StatusEvent +import com.vitorpamplona.quartz.nip90Dvms.status.NIP90StatusEvent @Composable fun RenderNIP90Status( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt index dddd78535..bca58da33 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt @@ -141,7 +141,7 @@ import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryResponseEvent +import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag import com.vitorpamplona.quartz.utils.Hex import com.vitorpamplona.quartz.utils.Log diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmContentDiscoveryScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmContentDiscoveryScreen.kt index b7268d473..0d81a4cc8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmContentDiscoveryScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmContentDiscoveryScreen.kt @@ -79,8 +79,8 @@ import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip47WalletConnect.rpc.PayInvoiceErrorResponse import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppMetadata -import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryResponseEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90StatusEvent +import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent +import com.vitorpamplona.quartz.nip90Dvms.status.NIP90StatusEvent @Composable fun DvmContentDiscoveryScreen( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/dal/NIP90ContentDiscoveryResponseFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/dal/NIP90ContentDiscoveryResponseFilter.kt index 8a1ac91e3..ec880e6a4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/dal/NIP90ContentDiscoveryResponseFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/dal/NIP90ContentDiscoveryResponseFilter.kt @@ -27,7 +27,7 @@ import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.quartz.nip01Core.tags.events.isTaggedEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryResponseEvent +import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent open class NIP90ContentDiscoveryResponseFilter( val account: Account, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/RelayInformationScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/RelayInformationScreen.kt index 81ac4d826..2b00709cd 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/RelayInformationScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/RelayInformationScreen.kt @@ -266,11 +266,11 @@ import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent import com.vitorpamplona.quartz.nip88Polls.response.PollResponseEvent import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent import com.vitorpamplona.quartz.nip89AppHandlers.recommendation.AppRecommendationEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryRequestEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryResponseEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90StatusEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90UserDiscoveryRequestEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90UserDiscoveryResponseEvent +import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryRequest.NIP90ContentDiscoveryRequestEvent +import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent +import com.vitorpamplona.quartz.nip90Dvms.status.NIP90StatusEvent +import com.vitorpamplona.quartz.nip90Dvms.userDiscoveryRequest.NIP90UserDiscoveryRequestEvent +import com.vitorpamplona.quartz.nip90Dvms.userDiscoveryResponse.NIP90UserDiscoveryResponseEvent import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent import com.vitorpamplona.quartz.nip96FileStorage.config.FileServersEvent import com.vitorpamplona.quartz.nip98HttpAuth.HTTPAuthorizationEvent diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90ContentDiscoveryRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/NIP90ContentDiscoveryRequestEvent.kt similarity index 73% rename from quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90ContentDiscoveryRequestEvent.kt rename to quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/NIP90ContentDiscoveryRequestEvent.kt index 3751f33e4..00ca00088 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90ContentDiscoveryRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/NIP90ContentDiscoveryRequestEvent.kt @@ -18,15 +18,16 @@ * 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.nip90Dvms +package com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryRequest import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl -import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner -import com.vitorpamplona.quartz.nip31Alts.AltTag +import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate +import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.utils.TimeUtils @Stable @@ -43,21 +44,19 @@ class NIP90ContentDiscoveryRequestEvent( const val KIND = 5300 const val ALT = "NIP90 Content Discovery request" - suspend fun create( + fun build( dvmPublicKey: HexKey, forUser: HexKey, relays: Set, - signer: NostrSigner, createdAt: Long = TimeUtils.now(), - ): NIP90ContentDiscoveryRequestEvent { - val content = "" - val tags = mutableListOf>() - tags.add(arrayOf("p", dvmPublicKey)) - tags.add(AltTag.assemble(ALT)) - tags.add(arrayOf("relays") + relays.map { it.url }.toTypedArray()) - tags.add(arrayOf("param", "max_results", "200")) - tags.add(arrayOf("param", "user", forUser)) - return signer.sign(createdAt, KIND, tags.toTypedArray(), content) + initializer: TagArrayBuilder.() -> Unit = {}, + ) = eventTemplate(KIND, "", createdAt) { + alt(ALT) + dvmPubKey(dvmPublicKey) + relays(relays) + param("max_results", "200") + param("user", forUser) + initializer() } } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/TagArrayBuilderExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/TagArrayBuilderExt.kt new file mode 100644 index 000000000..b9ca5e65d --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/TagArrayBuilderExt.kt @@ -0,0 +1,36 @@ +/* + * 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.nip90Dvms.contentDiscoveryRequest + +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl +import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryRequest.tags.ParamTag +import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryRequest.tags.RelaysTag + +fun TagArrayBuilder.dvmPubKey(pubKey: HexKey) = addUnique(arrayOf("p", pubKey)) + +fun TagArrayBuilder.relays(relays: Set) = addUnique(RelaysTag.assemble(relays)) + +fun TagArrayBuilder.param( + key: String, + value: String, +) = add(ParamTag.assemble(key, value)) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/TagArrayExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/TagArrayExt.kt new file mode 100644 index 000000000..09ee9c4a8 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/TagArrayExt.kt @@ -0,0 +1,31 @@ +/* + * 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.nip90Dvms.contentDiscoveryRequest + +import com.vitorpamplona.quartz.nip01Core.core.TagArray +import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryRequest.tags.ParamTag +import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryRequest.tags.RelaysTag + +fun TagArray.relays() = firstNotNullOfOrNull(RelaysTag::parse) + +fun TagArray.params() = mapNotNull(ParamTag::parse) + +fun TagArray.param(key: String) = params().firstOrNull { it.key == key } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/tags/ParamTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/tags/ParamTag.kt new file mode 100644 index 000000000..d323f840c --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/tags/ParamTag.kt @@ -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.nip90Dvms.contentDiscoveryRequest.tags + +import androidx.compose.runtime.Stable +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +@Stable +class ParamTag( + val key: String, + val value: String, +) { + companion object { + const val TAG_NAME = "param" + + fun isTag(tag: Array) = tag.has(2) && tag[0] == TAG_NAME && tag[1].isNotEmpty() + + fun parse(tag: Array): ParamTag? { + ensure(tag.has(2)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return ParamTag(tag[1], tag[2]) + } + + fun assemble( + key: String, + value: String, + ) = arrayOf(TAG_NAME, key, value) + + fun assemble(param: ParamTag) = assemble(param.key, param.value) + + fun assemble(params: List) = params.map { assemble(it) } + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/tags/RelaysTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/tags/RelaysTag.kt new file mode 100644 index 000000000..3bca17fa3 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryRequest/tags/RelaysTag.kt @@ -0,0 +1,46 @@ +/* + * 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.nip90Dvms.contentDiscoveryRequest.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl +import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer +import com.vitorpamplona.quartz.utils.ensure + +class RelaysTag( + val relays: List, +) { + companion object { + const val TAG_NAME = "relays" + + fun isTag(tag: Array) = tag.has(1) && tag[0] == TAG_NAME + + fun parse(tag: Array): RelaysTag? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + val relays = tag.drop(1).mapNotNull { RelayUrlNormalizer.normalizeOrNull(it) } + ensure(relays.isNotEmpty()) { return null } + return RelaysTag(relays) + } + + fun assemble(relays: Set) = arrayOf(TAG_NAME) + relays.map { it.url }.toTypedArray() + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90ContentDiscoveryResponseEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryResponse/NIP90ContentDiscoveryResponseEvent.kt similarity index 85% rename from quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90ContentDiscoveryResponseEvent.kt rename to quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryResponse/NIP90ContentDiscoveryResponseEvent.kt index 8d97e0f00..9333bfc1f 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90ContentDiscoveryResponseEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentDiscoveryResponse/NIP90ContentDiscoveryResponseEvent.kt @@ -18,14 +18,15 @@ * 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.nip90Dvms +package com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.OptimizedJsonMapper -import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner -import com.vitorpamplona.quartz.nip31Alts.AltTag +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate +import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.utils.Log import com.vitorpamplona.quartz.utils.TimeUtils @@ -71,15 +72,12 @@ class NIP90ContentDiscoveryResponseEvent( const val KIND = 6300 const val ALT = "NIP90 Content Discovery reply" - suspend fun create( - signer: NostrSigner, + fun build( createdAt: Long = TimeUtils.now(), - ): NIP90ContentDiscoveryResponseEvent { - val tags = - arrayOf( - AltTag.assemble(ALT), - ) - return signer.sign(createdAt, KIND, tags, "") + initializer: TagArrayBuilder.() -> Unit = {}, + ) = eventTemplate(KIND, "", createdAt) { + alt(ALT) + initializer() } } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90StatusEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/NIP90StatusEvent.kt similarity index 54% rename from quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90StatusEvent.kt rename to quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/NIP90StatusEvent.kt index 7191bb667..0b157e3fc 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90StatusEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/NIP90StatusEvent.kt @@ -18,13 +18,14 @@ * 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.nip90Dvms +package com.vitorpamplona.quartz.nip90Dvms.status import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey -import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner -import com.vitorpamplona.quartz.nip31Alts.AltTag +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate +import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -36,56 +37,20 @@ class NIP90StatusEvent( content: String, sig: HexKey, ) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { - class StatusCode( - val code: String, - val description: String, - ) + fun status() = tags.status(content) - class AmountInvoice( - val amount: Long?, - val lnInvoice: String?, - ) - - fun status(): StatusCode? = - tags.firstOrNull { it.size > 1 && it[0] == "status" }?.let { - if (it.size > 2 && content == "") { - StatusCode(it[1], it[2]) - } else { - StatusCode(it[1], content) - } - } - - fun firstAmount(): AmountInvoice? = - tags.firstOrNull { it.size > 1 && it[0] == "amount" }?.let { - val amount = it[1].toLongOrNull() - if (it.size > 2) { - if (it[2].isNotBlank()) { - AmountInvoice(amount, it[2]) - } else { - null - } - } else { - if (amount != null) { - AmountInvoice(amount, null) - } else { - null - } - } - } + fun firstAmount() = tags.firstAmount() companion object { const val KIND = 7000 const val ALT = "NIP90 Status update" - suspend fun create( - signer: NostrSigner, + fun build( createdAt: Long = TimeUtils.now(), - ): NIP90StatusEvent { - val tags = - arrayOf( - AltTag.assemble(ALT), - ) - return signer.sign(createdAt, KIND, tags, "") + initializer: TagArrayBuilder.() -> Unit = {}, + ) = eventTemplate(KIND, "", createdAt) { + alt(ALT) + initializer() } } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/TagArrayBuilderExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/TagArrayBuilderExt.kt new file mode 100644 index 000000000..ff12777c7 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/TagArrayBuilderExt.kt @@ -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.nip90Dvms.status + +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip90Dvms.status.tags.AmountTag +import com.vitorpamplona.quartz.nip90Dvms.status.tags.StatusTag + +fun TagArrayBuilder.status( + code: String, + description: String, +) = addUnique(StatusTag.assemble(code, description)) + +fun TagArrayBuilder.amount(amount: Long) = addUnique(AmountTag.assemble(amount)) + +fun TagArrayBuilder.amount( + amount: Long, + lnInvoice: String, +) = addUnique(AmountTag.assemble(amount, lnInvoice)) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/TagArrayExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/TagArrayExt.kt new file mode 100644 index 000000000..1c5847f2c --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/TagArrayExt.kt @@ -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.nip90Dvms.status + +import com.vitorpamplona.quartz.nip01Core.core.TagArray +import com.vitorpamplona.quartz.nip90Dvms.status.tags.AmountTag +import com.vitorpamplona.quartz.nip90Dvms.status.tags.StatusTag + +fun TagArray.status(content: String) = firstNotNullOfOrNull { StatusTag.parse(it, content) } + +fun TagArray.firstAmount() = firstNotNullOfOrNull(AmountTag::parse) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/tags/AmountTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/tags/AmountTag.kt new file mode 100644 index 000000000..7ce290028 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/tags/AmountTag.kt @@ -0,0 +1,57 @@ +/* + * 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.nip90Dvms.status.tags + +import androidx.compose.runtime.Stable +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +@Stable +class AmountTag( + val amount: Long?, + val lnInvoice: String?, +) { + companion object { + const val TAG_NAME = "amount" + + fun isTag(tag: Array) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty() + + fun parse(tag: Array): AmountTag? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + val amount = tag[1].toLongOrNull() + return if (tag.has(2) && tag[2].isNotBlank()) { + AmountTag(amount, tag[2]) + } else if (amount != null) { + AmountTag(amount, null) + } else { + null + } + } + + fun assemble(amount: Long) = arrayOf(TAG_NAME, amount.toString()) + + fun assemble( + amount: Long, + lnInvoice: String, + ) = arrayOf(TAG_NAME, amount.toString(), lnInvoice) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/tags/StatusTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/tags/StatusTag.kt new file mode 100644 index 000000000..17dadac6b --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/status/tags/StatusTag.kt @@ -0,0 +1,58 @@ +/* + * 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.nip90Dvms.status.tags + +import androidx.compose.runtime.Stable +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +@Stable +class StatusTag( + val code: String, + val description: String, +) { + companion object { + const val TAG_NAME = "status" + + fun isTag(tag: Array) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty() + + fun parse( + tag: Array, + content: String, + ): StatusTag? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return if (tag.has(2) && content.isEmpty()) { + StatusTag(tag[1], tag[2]) + } else { + StatusTag(tag[1], content) + } + } + + fun assemble( + code: String, + description: String, + ) = arrayOf(TAG_NAME, code, description) + + fun assemble(status: StatusTag) = assemble(status.code, status.description) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90UserDiscoveryRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/userDiscoveryRequest/NIP90UserDiscoveryRequestEvent.kt similarity index 77% rename from quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90UserDiscoveryRequestEvent.kt rename to quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/userDiscoveryRequest/NIP90UserDiscoveryRequestEvent.kt index 7ca2ea6fa..0acdb60cf 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90UserDiscoveryRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/userDiscoveryRequest/NIP90UserDiscoveryRequestEvent.kt @@ -18,13 +18,14 @@ * 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.nip90Dvms +package com.vitorpamplona.quartz.nip90Dvms.userDiscoveryRequest import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey -import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner -import com.vitorpamplona.quartz.nip31Alts.AltTag +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate +import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -38,17 +39,14 @@ class NIP90UserDiscoveryRequestEvent( ) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { companion object { const val KIND = 5301 - const val ALT = "NIP90 Content Discovery request" + const val ALT = "NIP90 User Discovery request" - suspend fun create( - signer: NostrSigner, + fun build( createdAt: Long = TimeUtils.now(), - ): NIP90UserDiscoveryRequestEvent { - val tags = - arrayOf( - AltTag.assemble(ALT), - ) - return signer.sign(createdAt, KIND, tags, "") + initializer: TagArrayBuilder.() -> Unit = {}, + ) = eventTemplate(KIND, "", createdAt) { + alt(ALT) + initializer() } } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90UserDiscoveryResponseEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/userDiscoveryResponse/NIP90UserDiscoveryResponseEvent.kt similarity index 77% rename from quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90UserDiscoveryResponseEvent.kt rename to quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/userDiscoveryResponse/NIP90UserDiscoveryResponseEvent.kt index bc26106dd..4c078a7a9 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/NIP90UserDiscoveryResponseEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/userDiscoveryResponse/NIP90UserDiscoveryResponseEvent.kt @@ -18,13 +18,14 @@ * 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.nip90Dvms +package com.vitorpamplona.quartz.nip90Dvms.userDiscoveryResponse import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey -import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner -import com.vitorpamplona.quartz.nip31Alts.AltTag +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate +import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -38,17 +39,14 @@ class NIP90UserDiscoveryResponseEvent( ) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { companion object { const val KIND = 6301 - const val ALT = "NIP90 Content Discovery reply" + const val ALT = "NIP90 User Discovery reply" - suspend fun create( - signer: NostrSigner, + fun build( createdAt: Long = TimeUtils.now(), - ): NIP90UserDiscoveryResponseEvent { - val tags = - arrayOf( - AltTag.assemble(ALT), - ) - return signer.sign(createdAt, KIND, tags, "") + initializer: TagArrayBuilder.() -> Unit = {}, + ) = eventTemplate(KIND, "", createdAt) { + alt(ALT) + initializer() } } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/EventFactory.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/EventFactory.kt index b12d62f90..c897002ec 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/EventFactory.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/EventFactory.kt @@ -190,11 +190,11 @@ import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent import com.vitorpamplona.quartz.nip88Polls.response.PollResponseEvent import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent import com.vitorpamplona.quartz.nip89AppHandlers.recommendation.AppRecommendationEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryRequestEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryResponseEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90StatusEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90UserDiscoveryRequestEvent -import com.vitorpamplona.quartz.nip90Dvms.NIP90UserDiscoveryResponseEvent +import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryRequest.NIP90ContentDiscoveryRequestEvent +import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent +import com.vitorpamplona.quartz.nip90Dvms.status.NIP90StatusEvent +import com.vitorpamplona.quartz.nip90Dvms.userDiscoveryRequest.NIP90UserDiscoveryRequestEvent +import com.vitorpamplona.quartz.nip90Dvms.userDiscoveryResponse.NIP90UserDiscoveryResponseEvent import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent import com.vitorpamplona.quartz.nip96FileStorage.config.FileServersEvent import com.vitorpamplona.quartz.nip98HttpAuth.HTTPAuthorizationEvent