Merge pull request #2074 from vitorpamplona/claude/add-ephemeral-giftwrap-event-PiIXY
Add support for NIP-59 Ephemeral Gift Wrap events (kind 21059)
This commit is contained in:
+2
-1
@@ -24,6 +24,7 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.EphemeralGiftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@@ -39,7 +40,7 @@ fun filterGiftWrapsToPubkey(
|
||||
relay = relay,
|
||||
filter =
|
||||
Filter(
|
||||
kinds = listOf(GiftWrapEvent.KIND),
|
||||
kinds = listOf(GiftWrapEvent.KIND, EphemeralGiftWrapEvent.KIND),
|
||||
tags = mapOf("p" to listOf(pubkey)),
|
||||
since = since?.minus(TimeUtils.twoDays()),
|
||||
),
|
||||
|
||||
+3
-1
@@ -238,6 +238,7 @@ import com.vitorpamplona.quartz.nip58Badges.award.BadgeAwardEvent
|
||||
import com.vitorpamplona.quartz.nip58Badges.definition.BadgeDefinitionEvent
|
||||
import com.vitorpamplona.quartz.nip58Badges.profile.ProfileBadgesEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.EphemeralGiftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip62RequestToVanish.RequestToVanishEvent
|
||||
import com.vitorpamplona.quartz.nip64Chess.challenge.accept.LiveChessGameAcceptEvent
|
||||
@@ -615,6 +616,7 @@ fun kindDisplayName(kind: Int): Int =
|
||||
GenericRepostEvent.KIND -> R.string.kind_reposts_16
|
||||
GeohashListEvent.KIND -> R.string.kind_geohash_follows
|
||||
GiftWrapEvent.KIND -> R.string.kind_gift_wraps
|
||||
EphemeralGiftWrapEvent.KIND -> R.string.kind_gift_wraps
|
||||
GitIssueEvent.KIND -> R.string.kind_git_issue
|
||||
GitPatchEvent.KIND -> R.string.kind_git_patch
|
||||
GitRepositoryEvent.KIND -> R.string.kind_git_repo
|
||||
@@ -693,7 +695,7 @@ fun kindDisplayName(kind: Int): Int =
|
||||
|
||||
val posts = setOf(0, 1, 6, 7, 16, 30023)
|
||||
val settings = setOf(3, 10002, 10000, 10001, 10003, 10004, 30000)
|
||||
val dms = setOf(4, 1059, 10050)
|
||||
val dms = setOf(4, GiftWrapEvent.KIND, EphemeralGiftWrapEvent.KIND, 10050)
|
||||
val zaps = setOf(9734, 9735, 9041, 17375, 23194, 23195)
|
||||
val reports = setOf(ReportEvent.KIND, MuteListEvent.KIND, DeletionEvent.KIND, RequestToVanishEvent.KIND)
|
||||
|
||||
|
||||
+2
-1
@@ -27,6 +27,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.CountFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.EphemeralGiftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
||||
|
||||
@Stable
|
||||
@@ -43,7 +44,7 @@ class DMRelayListViewModel : BasicRelaySetupInfoModel() {
|
||||
label = R.string.dms,
|
||||
filter =
|
||||
Filter(
|
||||
kinds = listOf(GiftWrapEvent.KIND, PrivateDmEvent.KIND),
|
||||
kinds = listOf(GiftWrapEvent.KIND, EphemeralGiftWrapEvent.KIND, PrivateDmEvent.KIND),
|
||||
tags = mapOf("p" to listOf(account.pubKey)),
|
||||
),
|
||||
),
|
||||
|
||||
+4
-2
@@ -38,6 +38,8 @@ import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.ReqCmd
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.EphemeralGiftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
@@ -55,7 +57,7 @@ import kotlin.coroutines.cancellation.CancellationException
|
||||
* Syncs the user's events across all known relays:
|
||||
* 1. Downloads all events authored by the user and sends them to their outbox relays.
|
||||
* 2. Downloads all events that p-tag the user (non-DM) and sends them to their inbox relays.
|
||||
* 3. Downloads kind-4 and kind-1059 events that p-tag the user and sends them to DM relays.
|
||||
* 3. Downloads kind-4, kind-1059 and kind-21059 events that p-tag the user and sends them to DM relays.
|
||||
*
|
||||
* Up to [MAX_CONCURRENT_RELAYS] relays are queried in parallel. As soon as one relay is fully
|
||||
* exhausted (all pages retrieved) the next relay from the list starts immediately, keeping the
|
||||
@@ -512,7 +514,7 @@ class EventSync(
|
||||
onEvent = { event, sourceRelay ->
|
||||
val isMyEvent = event.pubKey == myPubKey
|
||||
val mentionsMe = event.tags.isTaggedUser(myPubKey)
|
||||
val isDmKind = event.kind == 4 || event.kind == 1059
|
||||
val isDmKind = event.kind == 4 || event.kind == GiftWrapEvent.KIND || event.kind == EphemeralGiftWrapEvent.KIND
|
||||
|
||||
val live = liveActivity.value
|
||||
|
||||
|
||||
+3
-1
@@ -21,6 +21,8 @@
|
||||
package com.vitorpamplona.amethyst.desktop.subscriptions
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.EphemeralGiftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
||||
|
||||
/**
|
||||
* Type-safe builders for common Nostr filter patterns.
|
||||
@@ -540,7 +542,7 @@ object FilterBuilders {
|
||||
since: Long? = null,
|
||||
): Filter =
|
||||
Filter(
|
||||
kinds = listOf(1059), // GiftWrapEvent.KIND
|
||||
kinds = listOf(GiftWrapEvent.KIND, EphemeralGiftWrapEvent.KIND),
|
||||
tags = mapOf("p" to listOf(pubKeyHex)),
|
||||
since = since,
|
||||
)
|
||||
|
||||
+2
-1
@@ -25,6 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.EphemeralGiftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@@ -126,7 +127,7 @@ object FilterDMs {
|
||||
since: Long? = null,
|
||||
): Filter =
|
||||
Filter(
|
||||
kinds = listOf(GiftWrapEvent.KIND),
|
||||
kinds = listOf(GiftWrapEvent.KIND, EphemeralGiftWrapEvent.KIND),
|
||||
tags = mapOf("p" to listOf(userPubKeyHex)),
|
||||
since = since?.minus(TimeUtils.twoDays()),
|
||||
)
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.nip59Giftwrap.wraps
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
|
||||
import com.vitorpamplona.quartz.nip40Expiration.ExpirationTag
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable
|
||||
class EphemeralGiftWrapEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : GiftWrapEvent(id, pubKey, createdAt, tags, content, sig, KIND) {
|
||||
override fun copyNoContent(): EphemeralGiftWrapEvent {
|
||||
val copy =
|
||||
EphemeralGiftWrapEvent(
|
||||
id,
|
||||
pubKey,
|
||||
createdAt,
|
||||
tags,
|
||||
"",
|
||||
sig,
|
||||
)
|
||||
|
||||
copy.innerEventId = innerEventId
|
||||
|
||||
return copy
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KIND = 21059
|
||||
const val ALT = "Ephemeral encrypted event"
|
||||
|
||||
fun create(
|
||||
event: Event,
|
||||
recipientPubKey: HexKey,
|
||||
expirationDelta: Long? = null,
|
||||
createdAt: Long = TimeUtils.randomWithTwoDays(),
|
||||
): EphemeralGiftWrapEvent {
|
||||
val signer = NostrSignerSync(KeyPair())
|
||||
|
||||
val tags =
|
||||
expirationDelta?.let {
|
||||
arrayOf(
|
||||
PTag.assemble(recipientPubKey, null),
|
||||
ExpirationTag.assemble(createdAt + it + TimeUtils.twoDays()),
|
||||
)
|
||||
} ?: arrayOf(
|
||||
PTag.assemble(recipientPubKey, null),
|
||||
)
|
||||
|
||||
return signer.sign(
|
||||
createdAt = createdAt,
|
||||
kind = KIND,
|
||||
tags = tags,
|
||||
content = signer.nip44Encrypt(event.toJson(), recipientPubKey),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -36,19 +36,20 @@ import com.vitorpamplona.quartz.utils.Log
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable
|
||||
class GiftWrapEvent(
|
||||
open class GiftWrapEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
kind: Int = KIND,
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
@kotlinx.serialization.Transient
|
||||
@kotlin.jvm.Transient
|
||||
var innerEventId: HexKey? = null
|
||||
|
||||
fun copyNoContent(): GiftWrapEvent {
|
||||
open fun copyNoContent(): GiftWrapEvent {
|
||||
val copy =
|
||||
GiftWrapEvent(
|
||||
id,
|
||||
|
||||
@@ -159,6 +159,7 @@ import com.vitorpamplona.quartz.nip58Badges.award.BadgeAwardEvent
|
||||
import com.vitorpamplona.quartz.nip58Badges.definition.BadgeDefinitionEvent
|
||||
import com.vitorpamplona.quartz.nip58Badges.profile.ProfileBadgesEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.EphemeralGiftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip5aStaticWebsites.NamedSiteEvent
|
||||
import com.vitorpamplona.quartz.nip5aStaticWebsites.RootSiteEvent
|
||||
@@ -384,6 +385,7 @@ class EventFactory {
|
||||
GenericRepostEvent.KIND -> GenericRepostEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
GeohashListEvent.KIND -> GeohashListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
GiftWrapEvent.KIND -> GiftWrapEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
EphemeralGiftWrapEvent.KIND -> EphemeralGiftWrapEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
GitAuthorListEvent.KIND -> GitAuthorListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
GitRepositoryListEvent.KIND -> GitRepositoryListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
GitIssueEvent.KIND -> GitIssueEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
|
||||
Reference in New Issue
Block a user