From d40dccde117001b7e6feed81f47f3d314b24c8d1 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 28 Mar 2026 13:59:12 +0000 Subject: [PATCH] feat: complete NIP-85 Trusted Assertions implementation in quartz Add full compliance with NIP-85 by implementing all four assertion event kinds and their associated tag types in the nip85TrustedAssertions package: - Kind 30382 (User Assertions): Add all 15 missing tag parsers and builders (first_created_at, post_cnt, reply_cnt, reactions_cnt, all zap/report/topic/active_hours tags) to ContactCardEvent - Kind 30383 (Event Assertions): New EventAssertionEvent with rank, comment_cnt, quote_cnt, repost_cnt, reaction_cnt, zap_cnt, zap_amount - Kind 30384 (Addressable Event Assertions): New AddressableAssertionEvent with same tags as 30383 - Kind 30385 (External ID Assertions): New ExternalIdAssertionEvent with rank, comment_cnt, reaction_cnt - Add ProviderTypes for all new assertion kinds (30383/30384/30385) - Register all new event kinds in EventFactory - Add comprehensive tests for all assertion event types https://claude.ai/code/session_01XZQsSnkHJ6tFiPS7fRLzPJ --- .../addressables/AddressableAssertionEvent.kt | 78 ++++++ .../addressables/TagArrayBuilderExt.kt | 44 ++++ .../events/EventAssertionEvent.kt | 78 ++++++ .../events/TagArrayBuilderExt.kt | 44 ++++ .../externalIds/ExternalIdAssertionEvent.kt | 66 +++++ .../externalIds/TagArrayBuilderExt.kt | 32 +++ .../list/tags/ServiceType.kt | 25 +- .../tags/CommentCountTag.kt | 39 +++ .../tags/QuoteCountTag.kt | 39 +++ .../tags/ReactionCountTag.kt | 39 +++ .../tags/RepostCountTag.kt | 39 +++ .../tags/ZapAmountTag.kt | 39 +++ .../tags/ZapCountTag.kt | 39 +++ .../users/ContactCardEvent.kt | 45 ++++ .../users/TagArrayBuilderExt.kt | 48 ++++ .../users/tags/ActiveHoursEndTag.kt | 39 +++ .../users/tags/ActiveHoursStartTag.kt | 39 +++ .../users/tags/FirstCreatedAtTag.kt | 39 +++ .../users/tags/PostCountTag.kt | 39 +++ .../users/tags/ReactionsCountTag.kt | 39 +++ .../users/tags/ReplyCountTag.kt | 39 +++ .../users/tags/ReportsCountReceivedTag.kt | 39 +++ .../users/tags/ReportsCountSentTag.kt | 39 +++ .../users/tags/TopicTag.kt | 39 +++ .../users/tags/ZapAmountReceivedTag.kt | 39 +++ .../users/tags/ZapAmountSentTag.kt | 39 +++ .../users/tags/ZapAvgAmountDayReceivedTag.kt | 39 +++ .../users/tags/ZapAvgAmountDaySentTag.kt | 39 +++ .../users/tags/ZapCountReceivedTag.kt | 39 +++ .../users/tags/ZapCountSentTag.kt | 39 +++ .../quartz/utils/EventFactory.kt | 6 + .../AssertionEventTest.kt | 236 ++++++++++++++++++ 32 files changed, 1520 insertions(+), 1 deletion(-) create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/addressables/AddressableAssertionEvent.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/addressables/TagArrayBuilderExt.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/events/EventAssertionEvent.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/events/TagArrayBuilderExt.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/externalIds/ExternalIdAssertionEvent.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/externalIds/TagArrayBuilderExt.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/CommentCountTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/QuoteCountTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/ReactionCountTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/RepostCountTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/ZapAmountTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/ZapCountTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ActiveHoursEndTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ActiveHoursStartTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/FirstCreatedAtTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/PostCountTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ReactionsCountTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ReplyCountTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ReportsCountReceivedTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ReportsCountSentTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/TopicTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapAmountReceivedTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapAmountSentTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapAvgAmountDayReceivedTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapAvgAmountDaySentTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapCountReceivedTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapCountSentTag.kt create mode 100644 quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/experimental/nip85TrustedAssertions/AssertionEventTest.kt diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/addressables/AddressableAssertionEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/addressables/AddressableAssertionEvent.kt new file mode 100644 index 000000000..4a113de32 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/addressables/AddressableAssertionEvent.kt @@ -0,0 +1,78 @@ +/* + * 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.nip85TrustedAssertions.addressables + +import androidx.compose.runtime.Immutable +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.signers.eventTemplate +import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag +import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.CommentCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.QuoteCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.ReactionCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.RepostCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.ZapAmountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.ZapCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.RankTag +import com.vitorpamplona.quartz.utils.TimeUtils + +@Immutable +class AddressableAssertionEvent( + id: HexKey, + pubKey: HexKey, + createdAt: Long, + tags: Array>, + content: String, + sig: HexKey, +) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { + fun aboutAddress() = tags.dTag() + + fun rank() = tags.firstNotNullOfOrNull(RankTag::parse) + + fun commentCount() = tags.firstNotNullOfOrNull(CommentCountTag::parse) + + fun quoteCount() = tags.firstNotNullOfOrNull(QuoteCountTag::parse) + + fun repostCount() = tags.firstNotNullOfOrNull(RepostCountTag::parse) + + fun reactionCount() = tags.firstNotNullOfOrNull(ReactionCountTag::parse) + + fun zapCount() = tags.firstNotNullOfOrNull(ZapCountTag::parse) + + fun zapAmount() = tags.firstNotNullOfOrNull(ZapAmountTag::parse) + + companion object { + const val KIND = 30384 + const val ALT = "Addressable Event Assertion" + + fun build( + targetEventAddress: String, + createdAt: Long = TimeUtils.now(), + initializer: TagArrayBuilder.() -> Unit = {}, + ) = eventTemplate(KIND, "", createdAt) { + dTag(targetEventAddress) + alt(ALT) + initializer() + } + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/addressables/TagArrayBuilderExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/addressables/TagArrayBuilderExt.kt new file mode 100644 index 000000000..233524a44 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/addressables/TagArrayBuilderExt.kt @@ -0,0 +1,44 @@ +/* + * 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.nip85TrustedAssertions.addressables + +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.CommentCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.QuoteCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.ReactionCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.RepostCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.ZapAmountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.ZapCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.RankTag + +fun TagArrayBuilder.rank(rank: Int) = add(RankTag.assemble(rank)) + +fun TagArrayBuilder.commentCount(count: Int) = add(CommentCountTag.assemble(count)) + +fun TagArrayBuilder.quoteCount(count: Int) = add(QuoteCountTag.assemble(count)) + +fun TagArrayBuilder.repostCount(count: Int) = add(RepostCountTag.assemble(count)) + +fun TagArrayBuilder.reactionCount(count: Int) = add(ReactionCountTag.assemble(count)) + +fun TagArrayBuilder.zapCount(count: Int) = add(ZapCountTag.assemble(count)) + +fun TagArrayBuilder.zapAmount(sats: Long) = add(ZapAmountTag.assemble(sats)) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/events/EventAssertionEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/events/EventAssertionEvent.kt new file mode 100644 index 000000000..b0a0791b3 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/events/EventAssertionEvent.kt @@ -0,0 +1,78 @@ +/* + * 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.nip85TrustedAssertions.events + +import androidx.compose.runtime.Immutable +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.signers.eventTemplate +import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag +import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.CommentCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.QuoteCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.ReactionCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.RepostCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.ZapAmountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.ZapCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.RankTag +import com.vitorpamplona.quartz.utils.TimeUtils + +@Immutable +class EventAssertionEvent( + id: HexKey, + pubKey: HexKey, + createdAt: Long, + tags: Array>, + content: String, + sig: HexKey, +) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { + fun aboutEvent() = tags.dTag() + + fun rank() = tags.firstNotNullOfOrNull(RankTag::parse) + + fun commentCount() = tags.firstNotNullOfOrNull(CommentCountTag::parse) + + fun quoteCount() = tags.firstNotNullOfOrNull(QuoteCountTag::parse) + + fun repostCount() = tags.firstNotNullOfOrNull(RepostCountTag::parse) + + fun reactionCount() = tags.firstNotNullOfOrNull(ReactionCountTag::parse) + + fun zapCount() = tags.firstNotNullOfOrNull(ZapCountTag::parse) + + fun zapAmount() = tags.firstNotNullOfOrNull(ZapAmountTag::parse) + + companion object { + const val KIND = 30383 + const val ALT = "Event Assertion" + + fun build( + targetEventId: HexKey, + createdAt: Long = TimeUtils.now(), + initializer: TagArrayBuilder.() -> Unit = {}, + ) = eventTemplate(KIND, "", createdAt) { + dTag(targetEventId) + alt(ALT) + initializer() + } + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/events/TagArrayBuilderExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/events/TagArrayBuilderExt.kt new file mode 100644 index 000000000..f1a6f23c9 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/events/TagArrayBuilderExt.kt @@ -0,0 +1,44 @@ +/* + * 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.nip85TrustedAssertions.events + +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.CommentCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.QuoteCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.ReactionCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.RepostCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.ZapAmountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.ZapCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.RankTag + +fun TagArrayBuilder.rank(rank: Int) = add(RankTag.assemble(rank)) + +fun TagArrayBuilder.commentCount(count: Int) = add(CommentCountTag.assemble(count)) + +fun TagArrayBuilder.quoteCount(count: Int) = add(QuoteCountTag.assemble(count)) + +fun TagArrayBuilder.repostCount(count: Int) = add(RepostCountTag.assemble(count)) + +fun TagArrayBuilder.reactionCount(count: Int) = add(ReactionCountTag.assemble(count)) + +fun TagArrayBuilder.zapCount(count: Int) = add(ZapCountTag.assemble(count)) + +fun TagArrayBuilder.zapAmount(sats: Long) = add(ZapAmountTag.assemble(sats)) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/externalIds/ExternalIdAssertionEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/externalIds/ExternalIdAssertionEvent.kt new file mode 100644 index 000000000..68d2f877b --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/externalIds/ExternalIdAssertionEvent.kt @@ -0,0 +1,66 @@ +/* + * 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.nip85TrustedAssertions.externalIds + +import androidx.compose.runtime.Immutable +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.signers.eventTemplate +import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag +import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.CommentCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.ReactionCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.RankTag +import com.vitorpamplona.quartz.utils.TimeUtils + +@Immutable +class ExternalIdAssertionEvent( + id: HexKey, + pubKey: HexKey, + createdAt: Long, + tags: Array>, + content: String, + sig: HexKey, +) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { + fun aboutExternalId() = tags.dTag() + + fun rank() = tags.firstNotNullOfOrNull(RankTag::parse) + + fun commentCount() = tags.firstNotNullOfOrNull(CommentCountTag::parse) + + fun reactionCount() = tags.firstNotNullOfOrNull(ReactionCountTag::parse) + + companion object { + const val KIND = 30385 + const val ALT = "External Identifier Assertion" + + fun build( + targetIdentifier: String, + createdAt: Long = TimeUtils.now(), + initializer: TagArrayBuilder.() -> Unit = {}, + ) = eventTemplate(KIND, "", createdAt) { + dTag(targetIdentifier) + alt(ALT) + initializer() + } + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/externalIds/TagArrayBuilderExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/externalIds/TagArrayBuilderExt.kt new file mode 100644 index 000000000..7f7031d9e --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/externalIds/TagArrayBuilderExt.kt @@ -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.nip85TrustedAssertions.externalIds + +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.CommentCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.tags.ReactionCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.RankTag + +fun TagArrayBuilder.rank(rank: Int) = add(RankTag.assemble(rank)) + +fun TagArrayBuilder.commentCount(count: Int) = add(CommentCountTag.assemble(count)) + +fun TagArrayBuilder.reactionCount(count: Int) = add(ReactionCountTag.assemble(count)) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/list/tags/ServiceType.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/list/tags/ServiceType.kt index f94960fe6..dafea6d8c 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/list/tags/ServiceType.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/list/tags/ServiceType.kt @@ -46,9 +46,9 @@ data class ServiceType( } object ProviderTypes { + // Kind 30382: User assertions val rank = ServiceType(30382, "rank") val followerCount = ServiceType(30382, "followers") - val firstPost = ServiceType(30382, "first_created_at") val postCount = ServiceType(30382, "post_cnt") val replyCount = ServiceType(30382, "reply_cnt") @@ -64,4 +64,27 @@ object ProviderTypes { val topics = ServiceType(30382, "t") val activeFrom = ServiceType(30382, "active_hours_start") val activeTo = ServiceType(30382, "active_hours_end") + + // Kind 30383: Event assertions + val eventRank = ServiceType(30383, "rank") + val eventCommentCount = ServiceType(30383, "comment_cnt") + val eventQuoteCount = ServiceType(30383, "quote_cnt") + val eventRepostCount = ServiceType(30383, "repost_cnt") + val eventReactionCount = ServiceType(30383, "reaction_cnt") + val eventZapCount = ServiceType(30383, "zap_cnt") + val eventZapAmount = ServiceType(30383, "zap_amount") + + // Kind 30384: Addressable event assertions + val addressRank = ServiceType(30384, "rank") + val addressCommentCount = ServiceType(30384, "comment_cnt") + val addressQuoteCount = ServiceType(30384, "quote_cnt") + val addressRepostCount = ServiceType(30384, "repost_cnt") + val addressReactionCount = ServiceType(30384, "reaction_cnt") + val addressZapCount = ServiceType(30384, "zap_cnt") + val addressZapAmount = ServiceType(30384, "zap_amount") + + // Kind 30385: External identifier assertions + val externalIdRank = ServiceType(30385, "rank") + val externalIdCommentCount = ServiceType(30385, "comment_cnt") + val externalIdReactionCount = ServiceType(30385, "reaction_cnt") } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/CommentCountTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/CommentCountTag.kt new file mode 100644 index 000000000..d8cba997a --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/CommentCountTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class CommentCountTag { + companion object { + const val TAG_NAME = "comment_cnt" + + fun parse(tag: Array): Int? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toIntOrNull() + } + + fun assemble(count: Int) = arrayOf(TAG_NAME, count.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/QuoteCountTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/QuoteCountTag.kt new file mode 100644 index 000000000..8b5955e6b --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/QuoteCountTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class QuoteCountTag { + companion object { + const val TAG_NAME = "quote_cnt" + + fun parse(tag: Array): Int? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toIntOrNull() + } + + fun assemble(count: Int) = arrayOf(TAG_NAME, count.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/ReactionCountTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/ReactionCountTag.kt new file mode 100644 index 000000000..5f383c0d0 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/ReactionCountTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class ReactionCountTag { + companion object { + const val TAG_NAME = "reaction_cnt" + + fun parse(tag: Array): Int? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toIntOrNull() + } + + fun assemble(count: Int) = arrayOf(TAG_NAME, count.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/RepostCountTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/RepostCountTag.kt new file mode 100644 index 000000000..717707eb3 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/RepostCountTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class RepostCountTag { + companion object { + const val TAG_NAME = "repost_cnt" + + fun parse(tag: Array): Int? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toIntOrNull() + } + + fun assemble(count: Int) = arrayOf(TAG_NAME, count.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/ZapAmountTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/ZapAmountTag.kt new file mode 100644 index 000000000..655b52c5f --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/ZapAmountTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class ZapAmountTag { + companion object { + const val TAG_NAME = "zap_amount" + + fun parse(tag: Array): Long? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toLongOrNull() + } + + fun assemble(sats: Long) = arrayOf(TAG_NAME, sats.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/ZapCountTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/ZapCountTag.kt new file mode 100644 index 000000000..46ebb492d --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/tags/ZapCountTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class ZapCountTag { + companion object { + const val TAG_NAME = "zap_cnt" + + fun parse(tag: Array): Int? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toIntOrNull() + } + + fun assemble(count: Int) = arrayOf(TAG_NAME, count.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/ContactCardEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/ContactCardEvent.kt index ea0871164..f71f10178 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/ContactCardEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/ContactCardEvent.kt @@ -31,10 +31,25 @@ import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip51Lists.PrivateTagArrayEvent import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ActiveHoursEndTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ActiveHoursStartTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.FirstCreatedAtTag import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.FollowerCountTag import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.PetNameTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.PostCountTag import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.RankTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ReactionsCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ReplyCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ReportsCountReceivedTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ReportsCountSentTag import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.SummaryTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.TopicTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ZapAmountReceivedTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ZapAmountSentTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ZapAvgAmountDayReceivedTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ZapAvgAmountDaySentTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ZapCountReceivedTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ZapCountSentTag import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -52,6 +67,36 @@ class ContactCardEvent( fun followerCount() = tags.firstNotNullOfOrNull(FollowerCountTag::parse) + fun firstCreatedAt() = tags.firstNotNullOfOrNull(FirstCreatedAtTag::parse) + + fun postCount() = tags.firstNotNullOfOrNull(PostCountTag::parse) + + fun replyCount() = tags.firstNotNullOfOrNull(ReplyCountTag::parse) + + fun reactionsCount() = tags.firstNotNullOfOrNull(ReactionsCountTag::parse) + + fun zapAmountReceived() = tags.firstNotNullOfOrNull(ZapAmountReceivedTag::parse) + + fun zapAmountSent() = tags.firstNotNullOfOrNull(ZapAmountSentTag::parse) + + fun zapCountReceived() = tags.firstNotNullOfOrNull(ZapCountReceivedTag::parse) + + fun zapCountSent() = tags.firstNotNullOfOrNull(ZapCountSentTag::parse) + + fun zapAvgAmountDayReceived() = tags.firstNotNullOfOrNull(ZapAvgAmountDayReceivedTag::parse) + + fun zapAvgAmountDaySent() = tags.firstNotNullOfOrNull(ZapAvgAmountDaySentTag::parse) + + fun reportsCountReceived() = tags.firstNotNullOfOrNull(ReportsCountReceivedTag::parse) + + fun reportsCountSent() = tags.firstNotNullOfOrNull(ReportsCountSentTag::parse) + + fun topics() = tags.mapNotNull(TopicTag::parse) + + fun activeHoursStart() = tags.firstNotNullOfOrNull(ActiveHoursStartTag::parse) + + fun activeHoursEnd() = tags.firstNotNullOfOrNull(ActiveHoursEndTag::parse) + fun petName() = tags.firstNotNullOfOrNull(PetNameTag::parse) fun summary() = tags.firstNotNullOfOrNull(SummaryTag::parse) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/TagArrayBuilderExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/TagArrayBuilderExt.kt index f33a8472e..5c050c482 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/TagArrayBuilderExt.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/TagArrayBuilderExt.kt @@ -21,12 +21,60 @@ package com.vitorpamplona.quartz.nip85TrustedAssertions.users import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ActiveHoursEndTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ActiveHoursStartTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.FirstCreatedAtTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.FollowerCountTag import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.PetNameTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.PostCountTag import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.RankTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ReactionsCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ReplyCountTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ReportsCountReceivedTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ReportsCountSentTag import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.SummaryTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.TopicTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ZapAmountReceivedTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ZapAmountSentTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ZapAvgAmountDayReceivedTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ZapAvgAmountDaySentTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ZapCountReceivedTag +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.ZapCountSentTag fun TagArrayBuilder.rank(rank: Int) = add(RankTag.assemble(rank)) +fun TagArrayBuilder.followers(count: Int) = add(FollowerCountTag.assemble(count)) + +fun TagArrayBuilder.firstCreatedAt(timestamp: Long) = add(FirstCreatedAtTag.assemble(timestamp)) + +fun TagArrayBuilder.postCount(count: Int) = add(PostCountTag.assemble(count)) + +fun TagArrayBuilder.replyCount(count: Int) = add(ReplyCountTag.assemble(count)) + +fun TagArrayBuilder.reactionsCount(count: Int) = add(ReactionsCountTag.assemble(count)) + +fun TagArrayBuilder.zapAmountReceived(sats: Long) = add(ZapAmountReceivedTag.assemble(sats)) + +fun TagArrayBuilder.zapAmountSent(sats: Long) = add(ZapAmountSentTag.assemble(sats)) + +fun TagArrayBuilder.zapCountReceived(count: Int) = add(ZapCountReceivedTag.assemble(count)) + +fun TagArrayBuilder.zapCountSent(count: Int) = add(ZapCountSentTag.assemble(count)) + +fun TagArrayBuilder.zapAvgAmountDayReceived(sats: Long) = add(ZapAvgAmountDayReceivedTag.assemble(sats)) + +fun TagArrayBuilder.zapAvgAmountDaySent(sats: Long) = add(ZapAvgAmountDaySentTag.assemble(sats)) + +fun TagArrayBuilder.reportsCountReceived(count: Int) = add(ReportsCountReceivedTag.assemble(count)) + +fun TagArrayBuilder.reportsCountSent(count: Int) = add(ReportsCountSentTag.assemble(count)) + +fun TagArrayBuilder.topic(topic: String) = add(TopicTag.assemble(topic)) + +fun TagArrayBuilder.activeHoursStart(hour: Int) = add(ActiveHoursStartTag.assemble(hour)) + +fun TagArrayBuilder.activeHoursEnd(hour: Int) = add(ActiveHoursEndTag.assemble(hour)) + fun TagArrayBuilder.petName(name: String) = add(PetNameTag.assemble(name)) fun TagArrayBuilder.summary(summary: String) = add(SummaryTag.assemble(summary)) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ActiveHoursEndTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ActiveHoursEndTag.kt new file mode 100644 index 000000000..a97d20fba --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ActiveHoursEndTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.users.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class ActiveHoursEndTag { + companion object { + const val TAG_NAME = "active_hours_end" + + fun parse(tag: Array): Int? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toIntOrNull() + } + + fun assemble(count: Int) = arrayOf(TAG_NAME, count.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ActiveHoursStartTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ActiveHoursStartTag.kt new file mode 100644 index 000000000..6378b65a5 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ActiveHoursStartTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.users.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class ActiveHoursStartTag { + companion object { + const val TAG_NAME = "active_hours_start" + + fun parse(tag: Array): Int? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toIntOrNull() + } + + fun assemble(count: Int) = arrayOf(TAG_NAME, count.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/FirstCreatedAtTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/FirstCreatedAtTag.kt new file mode 100644 index 000000000..5434ae074 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/FirstCreatedAtTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.users.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class FirstCreatedAtTag { + companion object { + const val TAG_NAME = "first_created_at" + + fun parse(tag: Array): Long? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toLongOrNull() + } + + fun assemble(value: Long) = arrayOf(TAG_NAME, value.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/PostCountTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/PostCountTag.kt new file mode 100644 index 000000000..e32e16af6 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/PostCountTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.users.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class PostCountTag { + companion object { + const val TAG_NAME = "post_cnt" + + fun parse(tag: Array): Int? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toIntOrNull() + } + + fun assemble(count: Int) = arrayOf(TAG_NAME, count.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ReactionsCountTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ReactionsCountTag.kt new file mode 100644 index 000000000..dc874df20 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ReactionsCountTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.users.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class ReactionsCountTag { + companion object { + const val TAG_NAME = "reactions_cnt" + + fun parse(tag: Array): Int? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toIntOrNull() + } + + fun assemble(count: Int) = arrayOf(TAG_NAME, count.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ReplyCountTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ReplyCountTag.kt new file mode 100644 index 000000000..50ef8985d --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ReplyCountTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.users.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class ReplyCountTag { + companion object { + const val TAG_NAME = "reply_cnt" + + fun parse(tag: Array): Int? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toIntOrNull() + } + + fun assemble(count: Int) = arrayOf(TAG_NAME, count.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ReportsCountReceivedTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ReportsCountReceivedTag.kt new file mode 100644 index 000000000..788f321bb --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ReportsCountReceivedTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.users.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class ReportsCountReceivedTag { + companion object { + const val TAG_NAME = "reports_cnt_recd" + + fun parse(tag: Array): Int? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toIntOrNull() + } + + fun assemble(count: Int) = arrayOf(TAG_NAME, count.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ReportsCountSentTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ReportsCountSentTag.kt new file mode 100644 index 000000000..6baa791a9 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ReportsCountSentTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.users.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class ReportsCountSentTag { + companion object { + const val TAG_NAME = "reports_cnt_sent" + + fun parse(tag: Array): Int? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toIntOrNull() + } + + fun assemble(count: Int) = arrayOf(TAG_NAME, count.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/TopicTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/TopicTag.kt new file mode 100644 index 000000000..92fe6d7ab --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/TopicTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.users.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class TopicTag { + companion object { + const val TAG_NAME = "t" + + fun parse(tag: Array): String? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1] + } + + fun assemble(topic: String) = arrayOf(TAG_NAME, topic) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapAmountReceivedTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapAmountReceivedTag.kt new file mode 100644 index 000000000..6fa82b65d --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapAmountReceivedTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.users.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class ZapAmountReceivedTag { + companion object { + const val TAG_NAME = "zap_amt_recd" + + fun parse(tag: Array): Long? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toLongOrNull() + } + + fun assemble(value: Long) = arrayOf(TAG_NAME, value.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapAmountSentTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapAmountSentTag.kt new file mode 100644 index 000000000..2e2166f53 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapAmountSentTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.users.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class ZapAmountSentTag { + companion object { + const val TAG_NAME = "zap_amt_sent" + + fun parse(tag: Array): Long? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toLongOrNull() + } + + fun assemble(value: Long) = arrayOf(TAG_NAME, value.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapAvgAmountDayReceivedTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapAvgAmountDayReceivedTag.kt new file mode 100644 index 000000000..085a9c8af --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapAvgAmountDayReceivedTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.users.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class ZapAvgAmountDayReceivedTag { + companion object { + const val TAG_NAME = "zap_avg_amt_day_recd" + + fun parse(tag: Array): Long? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toLongOrNull() + } + + fun assemble(value: Long) = arrayOf(TAG_NAME, value.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapAvgAmountDaySentTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapAvgAmountDaySentTag.kt new file mode 100644 index 000000000..0d2c064e8 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapAvgAmountDaySentTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.users.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class ZapAvgAmountDaySentTag { + companion object { + const val TAG_NAME = "zap_avg_amt_day_sent" + + fun parse(tag: Array): Long? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toLongOrNull() + } + + fun assemble(value: Long) = arrayOf(TAG_NAME, value.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapCountReceivedTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapCountReceivedTag.kt new file mode 100644 index 000000000..336d6df98 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapCountReceivedTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.users.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class ZapCountReceivedTag { + companion object { + const val TAG_NAME = "zap_cnt_recd" + + fun parse(tag: Array): Int? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toIntOrNull() + } + + fun assemble(count: Int) = arrayOf(TAG_NAME, count.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapCountSentTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapCountSentTag.kt new file mode 100644 index 000000000..3bbfea964 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/users/tags/ZapCountSentTag.kt @@ -0,0 +1,39 @@ +/* + * 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.nip85TrustedAssertions.users.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class ZapCountSentTag { + companion object { + const val TAG_NAME = "zap_cnt_sent" + + fun parse(tag: Array): Int? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toIntOrNull() + } + + fun assemble(count: Int) = arrayOf(TAG_NAME, count.toString()) + } +} 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 1fb36fd39..cc54631a6 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/EventFactory.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/EventFactory.kt @@ -139,6 +139,9 @@ import com.vitorpamplona.quartz.nip72ModCommunities.follow.CommunityListEvent import com.vitorpamplona.quartz.nip75ZapGoals.GoalEvent import com.vitorpamplona.quartz.nip78AppData.AppSpecificDataEvent import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent +import com.vitorpamplona.quartz.nip85TrustedAssertions.addressables.AddressableAssertionEvent +import com.vitorpamplona.quartz.nip85TrustedAssertions.events.EventAssertionEvent +import com.vitorpamplona.quartz.nip85TrustedAssertions.externalIds.ExternalIdAssertionEvent import com.vitorpamplona.quartz.nip85TrustedAssertions.list.TrustProviderListEvent import com.vitorpamplona.quartz.nip85TrustedAssertions.users.ContactCardEvent import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent @@ -302,6 +305,9 @@ class EventFactory { PublicMessageEvent.KIND -> PublicMessageEvent(id, pubKey, createdAt, tags, content, sig) ReactionEvent.KIND -> ReactionEvent(id, pubKey, createdAt, tags, content, sig) ContactCardEvent.KIND -> ContactCardEvent(id, pubKey, createdAt, tags, content, sig) + EventAssertionEvent.KIND -> EventAssertionEvent(id, pubKey, createdAt, tags, content, sig) + AddressableAssertionEvent.KIND -> AddressableAssertionEvent(id, pubKey, createdAt, tags, content, sig) + ExternalIdAssertionEvent.KIND -> ExternalIdAssertionEvent(id, pubKey, createdAt, tags, content, sig) RelayAuthEvent.KIND -> RelayAuthEvent(id, pubKey, createdAt, tags, content, sig) RelayDiscoveryEvent.KIND -> RelayDiscoveryEvent(id, pubKey, createdAt, tags, content, sig) RelayMonitorEvent.KIND -> RelayMonitorEvent(id, pubKey, createdAt, tags, content, sig) diff --git a/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/experimental/nip85TrustedAssertions/AssertionEventTest.kt b/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/experimental/nip85TrustedAssertions/AssertionEventTest.kt new file mode 100644 index 000000000..7eccdaf3d --- /dev/null +++ b/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/experimental/nip85TrustedAssertions/AssertionEventTest.kt @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.experimental.nip85TrustedAssertions + +import com.vitorpamplona.quartz.nip85TrustedAssertions.addressables.AddressableAssertionEvent +import com.vitorpamplona.quartz.nip85TrustedAssertions.events.EventAssertionEvent +import com.vitorpamplona.quartz.nip85TrustedAssertions.externalIds.ExternalIdAssertionEvent +import com.vitorpamplona.quartz.nip85TrustedAssertions.users.ContactCardEvent +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +class AssertionEventTest { + val servicePubKey = "4fd5e210530e4f6b2cb083795834bfe5108324f1ed9f00ab73b9e8fcfe5f12fe" + val dummySig = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + @Test + fun parseUserAssertionWithAllTags() { + val targetUser = "e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411" + val event = + ContactCardEvent( + id = "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234", + pubKey = servicePubKey, + createdAt = 1700000000, + tags = + arrayOf( + arrayOf("d", targetUser), + arrayOf("rank", "89"), + arrayOf("followers", "15000"), + arrayOf("first_created_at", "1609459200"), + arrayOf("post_cnt", "5000"), + arrayOf("reply_cnt", "3200"), + arrayOf("reactions_cnt", "12000"), + arrayOf("zap_amt_recd", "5000000"), + arrayOf("zap_amt_sent", "2000000"), + arrayOf("zap_cnt_recd", "500"), + arrayOf("zap_cnt_sent", "200"), + arrayOf("zap_avg_amt_day_recd", "10000"), + arrayOf("zap_avg_amt_day_sent", "5000"), + arrayOf("reports_cnt_recd", "3"), + arrayOf("reports_cnt_sent", "1"), + arrayOf("t", "bitcoin"), + arrayOf("t", "nostr"), + arrayOf("active_hours_start", "8"), + arrayOf("active_hours_end", "22"), + ), + content = "", + sig = dummySig, + ) + + assertEquals(targetUser, event.aboutUser()) + assertEquals(89, event.rank()) + assertEquals(15000, event.followerCount()) + assertEquals(1609459200L, event.firstCreatedAt()) + assertEquals(5000, event.postCount()) + assertEquals(3200, event.replyCount()) + assertEquals(12000, event.reactionsCount()) + assertEquals(5000000L, event.zapAmountReceived()) + assertEquals(2000000L, event.zapAmountSent()) + assertEquals(500, event.zapCountReceived()) + assertEquals(200, event.zapCountSent()) + assertEquals(10000L, event.zapAvgAmountDayReceived()) + assertEquals(5000L, event.zapAvgAmountDaySent()) + assertEquals(3, event.reportsCountReceived()) + assertEquals(1, event.reportsCountSent()) + assertEquals(listOf("bitcoin", "nostr"), event.topics()) + assertEquals(8, event.activeHoursStart()) + assertEquals(22, event.activeHoursEnd()) + } + + @Test + fun parseUserAssertionMinimalTags() { + val event = + ContactCardEvent( + id = "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234", + pubKey = servicePubKey, + createdAt = 1700000000, + tags = + arrayOf( + arrayOf("d", "e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411"), + arrayOf("rank", "42"), + ), + content = "", + sig = dummySig, + ) + + assertEquals(42, event.rank()) + assertNull(event.followerCount()) + assertNull(event.firstCreatedAt()) + assertNull(event.postCount()) + assertNull(event.zapAmountReceived()) + assertEquals(emptyList(), event.topics()) + } + + @Test + fun parseEventAssertion() { + val targetEventId = "f00dcafe00000000000000000000000000000000000000000000000000000000" + val event = + EventAssertionEvent( + id = "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234", + pubKey = servicePubKey, + createdAt = 1700000000, + tags = + arrayOf( + arrayOf("d", targetEventId), + arrayOf("rank", "75"), + arrayOf("comment_cnt", "42"), + arrayOf("quote_cnt", "10"), + arrayOf("repost_cnt", "25"), + arrayOf("reaction_cnt", "300"), + arrayOf("zap_cnt", "15"), + arrayOf("zap_amount", "150000"), + ), + content = "", + sig = dummySig, + ) + + assertEquals(targetEventId, event.aboutEvent()) + assertEquals(75, event.rank()) + assertEquals(42, event.commentCount()) + assertEquals(10, event.quoteCount()) + assertEquals(25, event.repostCount()) + assertEquals(300, event.reactionCount()) + assertEquals(15, event.zapCount()) + assertEquals(150000L, event.zapAmount()) + } + + @Test + fun parseAddressableAssertion() { + val targetAddress = "30023:e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411:my-article" + val event = + AddressableAssertionEvent( + id = "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234", + pubKey = servicePubKey, + createdAt = 1700000000, + tags = + arrayOf( + arrayOf("d", targetAddress), + arrayOf("rank", "92"), + arrayOf("comment_cnt", "100"), + arrayOf("quote_cnt", "20"), + arrayOf("repost_cnt", "50"), + arrayOf("reaction_cnt", "500"), + arrayOf("zap_cnt", "30"), + arrayOf("zap_amount", "300000"), + ), + content = "", + sig = dummySig, + ) + + assertEquals(targetAddress, event.aboutAddress()) + assertEquals(92, event.rank()) + assertEquals(100, event.commentCount()) + assertEquals(20, event.quoteCount()) + assertEquals(50, event.repostCount()) + assertEquals(500, event.reactionCount()) + assertEquals(30, event.zapCount()) + assertEquals(300000L, event.zapAmount()) + } + + @Test + fun parseExternalIdAssertion() { + val targetId = "isbn:978-0-13-468599-1" + val event = + ExternalIdAssertionEvent( + id = "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234", + pubKey = servicePubKey, + createdAt = 1700000000, + tags = + arrayOf( + arrayOf("d", targetId), + arrayOf("rank", "85"), + arrayOf("comment_cnt", "50"), + arrayOf("reaction_cnt", "200"), + ), + content = "", + sig = dummySig, + ) + + assertEquals(targetId, event.aboutExternalId()) + assertEquals(85, event.rank()) + assertEquals(50, event.commentCount()) + assertEquals(200, event.reactionCount()) + } + + @Test + fun parseEventAssertionWithMissingTags() { + val event = + EventAssertionEvent( + id = "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234", + pubKey = servicePubKey, + createdAt = 1700000000, + tags = + arrayOf( + arrayOf("d", "f00dcafe00000000000000000000000000000000000000000000000000000000"), + arrayOf("rank", "50"), + ), + content = "", + sig = dummySig, + ) + + assertEquals(50, event.rank()) + assertNull(event.commentCount()) + assertNull(event.quoteCount()) + assertNull(event.repostCount()) + assertNull(event.reactionCount()) + assertNull(event.zapCount()) + assertNull(event.zapAmount()) + } + + @Test + fun eventKindsAreCorrect() { + assertEquals(30382, ContactCardEvent.KIND) + assertEquals(30383, EventAssertionEvent.KIND) + assertEquals(30384, AddressableAssertionEvent.KIND) + assertEquals(30385, ExternalIdAssertionEvent.KIND) + } +}