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
This commit is contained in:
Claude
2026-03-28 13:59:12 +00:00
parent 3ad42d2d8b
commit d40dccde11
32 changed files with 1520 additions and 1 deletions
@@ -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<Array<String>>,
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<AddressableAssertionEvent>.() -> Unit = {},
) = eventTemplate(KIND, "", createdAt) {
dTag(targetEventAddress)
alt(ALT)
initializer()
}
}
}
@@ -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<AddressableAssertionEvent>.rank(rank: Int) = add(RankTag.assemble(rank))
fun TagArrayBuilder<AddressableAssertionEvent>.commentCount(count: Int) = add(CommentCountTag.assemble(count))
fun TagArrayBuilder<AddressableAssertionEvent>.quoteCount(count: Int) = add(QuoteCountTag.assemble(count))
fun TagArrayBuilder<AddressableAssertionEvent>.repostCount(count: Int) = add(RepostCountTag.assemble(count))
fun TagArrayBuilder<AddressableAssertionEvent>.reactionCount(count: Int) = add(ReactionCountTag.assemble(count))
fun TagArrayBuilder<AddressableAssertionEvent>.zapCount(count: Int) = add(ZapCountTag.assemble(count))
fun TagArrayBuilder<AddressableAssertionEvent>.zapAmount(sats: Long) = add(ZapAmountTag.assemble(sats))
@@ -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<Array<String>>,
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<EventAssertionEvent>.() -> Unit = {},
) = eventTemplate(KIND, "", createdAt) {
dTag(targetEventId)
alt(ALT)
initializer()
}
}
}
@@ -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<EventAssertionEvent>.rank(rank: Int) = add(RankTag.assemble(rank))
fun TagArrayBuilder<EventAssertionEvent>.commentCount(count: Int) = add(CommentCountTag.assemble(count))
fun TagArrayBuilder<EventAssertionEvent>.quoteCount(count: Int) = add(QuoteCountTag.assemble(count))
fun TagArrayBuilder<EventAssertionEvent>.repostCount(count: Int) = add(RepostCountTag.assemble(count))
fun TagArrayBuilder<EventAssertionEvent>.reactionCount(count: Int) = add(ReactionCountTag.assemble(count))
fun TagArrayBuilder<EventAssertionEvent>.zapCount(count: Int) = add(ZapCountTag.assemble(count))
fun TagArrayBuilder<EventAssertionEvent>.zapAmount(sats: Long) = add(ZapAmountTag.assemble(sats))
@@ -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<Array<String>>,
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<ExternalIdAssertionEvent>.() -> Unit = {},
) = eventTemplate(KIND, "", createdAt) {
dTag(targetIdentifier)
alt(ALT)
initializer()
}
}
}
@@ -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<ExternalIdAssertionEvent>.rank(rank: Int) = add(RankTag.assemble(rank))
fun TagArrayBuilder<ExternalIdAssertionEvent>.commentCount(count: Int) = add(CommentCountTag.assemble(count))
fun TagArrayBuilder<ExternalIdAssertionEvent>.reactionCount(count: Int) = add(ReactionCountTag.assemble(count))
@@ -46,9 +46,9 @@ data class ServiceType(
} }
object ProviderTypes { object ProviderTypes {
// Kind 30382: User assertions
val rank = ServiceType(30382, "rank") val rank = ServiceType(30382, "rank")
val followerCount = ServiceType(30382, "followers") val followerCount = ServiceType(30382, "followers")
val firstPost = ServiceType(30382, "first_created_at") val firstPost = ServiceType(30382, "first_created_at")
val postCount = ServiceType(30382, "post_cnt") val postCount = ServiceType(30382, "post_cnt")
val replyCount = ServiceType(30382, "reply_cnt") val replyCount = ServiceType(30382, "reply_cnt")
@@ -64,4 +64,27 @@ object ProviderTypes {
val topics = ServiceType(30382, "t") val topics = ServiceType(30382, "t")
val activeFrom = ServiceType(30382, "active_hours_start") val activeFrom = ServiceType(30382, "active_hours_start")
val activeTo = ServiceType(30382, "active_hours_end") 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")
} }
@@ -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<String>): 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())
}
}
@@ -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<String>): 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())
}
}
@@ -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<String>): 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())
}
}
@@ -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<String>): 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())
}
}
@@ -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<String>): 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())
}
}
@@ -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<String>): 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())
}
}
@@ -31,10 +31,25 @@ import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nip51Lists.PrivateTagArrayEvent import com.vitorpamplona.quartz.nip51Lists.PrivateTagArrayEvent
import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent 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.FollowerCountTag
import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.PetNameTag 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.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.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 import com.vitorpamplona.quartz.utils.TimeUtils
@Immutable @Immutable
@@ -52,6 +67,36 @@ class ContactCardEvent(
fun followerCount() = tags.firstNotNullOfOrNull(FollowerCountTag::parse) 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 petName() = tags.firstNotNullOfOrNull(PetNameTag::parse)
fun summary() = tags.firstNotNullOfOrNull(SummaryTag::parse) fun summary() = tags.firstNotNullOfOrNull(SummaryTag::parse)
@@ -21,12 +21,60 @@
package com.vitorpamplona.quartz.nip85TrustedAssertions.users package com.vitorpamplona.quartz.nip85TrustedAssertions.users
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder 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.PetNameTag
import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.PostCountTag
import com.vitorpamplona.quartz.nip85TrustedAssertions.users.tags.RankTag 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.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<ContactCardEvent>.rank(rank: Int) = add(RankTag.assemble(rank)) fun TagArrayBuilder<ContactCardEvent>.rank(rank: Int) = add(RankTag.assemble(rank))
fun TagArrayBuilder<ContactCardEvent>.followers(count: Int) = add(FollowerCountTag.assemble(count))
fun TagArrayBuilder<ContactCardEvent>.firstCreatedAt(timestamp: Long) = add(FirstCreatedAtTag.assemble(timestamp))
fun TagArrayBuilder<ContactCardEvent>.postCount(count: Int) = add(PostCountTag.assemble(count))
fun TagArrayBuilder<ContactCardEvent>.replyCount(count: Int) = add(ReplyCountTag.assemble(count))
fun TagArrayBuilder<ContactCardEvent>.reactionsCount(count: Int) = add(ReactionsCountTag.assemble(count))
fun TagArrayBuilder<ContactCardEvent>.zapAmountReceived(sats: Long) = add(ZapAmountReceivedTag.assemble(sats))
fun TagArrayBuilder<ContactCardEvent>.zapAmountSent(sats: Long) = add(ZapAmountSentTag.assemble(sats))
fun TagArrayBuilder<ContactCardEvent>.zapCountReceived(count: Int) = add(ZapCountReceivedTag.assemble(count))
fun TagArrayBuilder<ContactCardEvent>.zapCountSent(count: Int) = add(ZapCountSentTag.assemble(count))
fun TagArrayBuilder<ContactCardEvent>.zapAvgAmountDayReceived(sats: Long) = add(ZapAvgAmountDayReceivedTag.assemble(sats))
fun TagArrayBuilder<ContactCardEvent>.zapAvgAmountDaySent(sats: Long) = add(ZapAvgAmountDaySentTag.assemble(sats))
fun TagArrayBuilder<ContactCardEvent>.reportsCountReceived(count: Int) = add(ReportsCountReceivedTag.assemble(count))
fun TagArrayBuilder<ContactCardEvent>.reportsCountSent(count: Int) = add(ReportsCountSentTag.assemble(count))
fun TagArrayBuilder<ContactCardEvent>.topic(topic: String) = add(TopicTag.assemble(topic))
fun TagArrayBuilder<ContactCardEvent>.activeHoursStart(hour: Int) = add(ActiveHoursStartTag.assemble(hour))
fun TagArrayBuilder<ContactCardEvent>.activeHoursEnd(hour: Int) = add(ActiveHoursEndTag.assemble(hour))
fun TagArrayBuilder<ContactCardEvent>.petName(name: String) = add(PetNameTag.assemble(name)) fun TagArrayBuilder<ContactCardEvent>.petName(name: String) = add(PetNameTag.assemble(name))
fun TagArrayBuilder<ContactCardEvent>.summary(summary: String) = add(SummaryTag.assemble(summary)) fun TagArrayBuilder<ContactCardEvent>.summary(summary: String) = add(SummaryTag.assemble(summary))
@@ -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<String>): 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())
}
}
@@ -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<String>): 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())
}
}
@@ -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<String>): 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())
}
}
@@ -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<String>): 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())
}
}
@@ -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<String>): 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())
}
}
@@ -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<String>): 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())
}
}
@@ -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<String>): 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())
}
}
@@ -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<String>): 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())
}
}
@@ -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>): 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)
}
}
@@ -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<String>): 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())
}
}
@@ -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<String>): 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())
}
}
@@ -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<String>): 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())
}
}
@@ -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<String>): 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())
}
}
@@ -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<String>): 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())
}
}
@@ -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<String>): 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())
}
}
@@ -139,6 +139,9 @@ import com.vitorpamplona.quartz.nip72ModCommunities.follow.CommunityListEvent
import com.vitorpamplona.quartz.nip75ZapGoals.GoalEvent import com.vitorpamplona.quartz.nip75ZapGoals.GoalEvent
import com.vitorpamplona.quartz.nip78AppData.AppSpecificDataEvent import com.vitorpamplona.quartz.nip78AppData.AppSpecificDataEvent
import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent 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.list.TrustProviderListEvent
import com.vitorpamplona.quartz.nip85TrustedAssertions.users.ContactCardEvent import com.vitorpamplona.quartz.nip85TrustedAssertions.users.ContactCardEvent
import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent
@@ -302,6 +305,9 @@ class EventFactory {
PublicMessageEvent.KIND -> PublicMessageEvent(id, pubKey, createdAt, tags, content, sig) PublicMessageEvent.KIND -> PublicMessageEvent(id, pubKey, createdAt, tags, content, sig)
ReactionEvent.KIND -> ReactionEvent(id, pubKey, createdAt, tags, content, sig) ReactionEvent.KIND -> ReactionEvent(id, pubKey, createdAt, tags, content, sig)
ContactCardEvent.KIND -> ContactCardEvent(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) RelayAuthEvent.KIND -> RelayAuthEvent(id, pubKey, createdAt, tags, content, sig)
RelayDiscoveryEvent.KIND -> RelayDiscoveryEvent(id, pubKey, createdAt, tags, content, sig) RelayDiscoveryEvent.KIND -> RelayDiscoveryEvent(id, pubKey, createdAt, tags, content, sig)
RelayMonitorEvent.KIND -> RelayMonitorEvent(id, pubKey, createdAt, tags, content, sig) RelayMonitorEvent.KIND -> RelayMonitorEvent(id, pubKey, createdAt, tags, content, sig)
@@ -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)
}
}