fix: align WakeUp handling with spec semantics (p-tags = authors)

A previous commit misread the spec: p-tags on a WakeUp identify the
AUTHORS of the referenced events (the people whose events are the
subject of the wake-up), not the recipients. The consumer's existing
npub-in-p-tag match is therefore correct — "is this logged-in account
the author of a referenced event?".

- Revert WakeUpEvent.build() back to notify(about.toPTag()) and replace
  the comment with a spec-accurate one.
- In wakeUpFor, source author pubkeys from event.authorKeys() (p-tags,
  canonical) first, merge in the e-tag author hints, and fall back to
  the WakeUp signer only when both are empty. Bound by MAX_WAKEUP_REFS.

computeReplyTo and the referenced-event fetch path remain untouched —
those fixes are orthogonal to the p-tag semantic.
This commit is contained in:
Claude
2026-04-24 01:54:48 +00:00
parent 3c6b2bec9f
commit 9574f4b68d
2 changed files with 14 additions and 13 deletions
@@ -204,16 +204,17 @@ class EventNotificationConsumer(
return return
} }
// The referenced event's author is who the user will see in the final // Per spec, p-tags on a WakeUp are the authors of the referenced
// notification ("Alice zapped you"). The WakeUp's own pubKey is typically // events; those are whose metadata we need to render the notification.
// a push bot and not useful. Fall back to it only when the `e` tag omits // Fall back to e-tag author hints and finally to the WakeUp signer.
// the author hint.
val referencedNotes = referencedTags.map { LocalCache.getOrCreateNote(it.eventId) } val referencedNotes = referencedTags.map { LocalCache.getOrCreateNote(it.eventId) }
val authorCandidates = val authorKeys =
referencedTags (event.authorKeys() + referencedTags.mapNotNull { it.author })
.mapNotNull { it.author }
.distinct() .distinct()
.ifEmpty { listOf(event.pubKey) } .ifEmpty { listOf(event.pubKey) }
val authorCandidates =
authorKeys
.take(MAX_WAKEUP_REFS)
.map { LocalCache.getOrCreateUser(it) } .map { LocalCache.getOrCreateUser(it) }
coroutineScope { coroutineScope {
@@ -35,7 +35,7 @@ import com.vitorpamplona.quartz.nip01Core.tags.kinds.kind
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag.Companion.parse import com.vitorpamplona.quartz.nip01Core.tags.people.PTag.Companion.parse
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag.Companion.parseKey import com.vitorpamplona.quartz.nip01Core.tags.people.PTag.Companion.parseKey
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers import com.vitorpamplona.quartz.nip01Core.tags.people.toPTag
import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import kotlinx.serialization.json.JsonNull.content import kotlinx.serialization.json.JsonNull.content
@@ -68,10 +68,10 @@ class WakeUpEvent(
const val KIND = 23903 const val KIND = 23903
const val ALT_DESCRIPTION = "WakeUp" const val ALT_DESCRIPTION = "WakeUp"
// Tags the audience of [about] (its `p` tags) as the recipients to wake up. // p-tags on a WakeUp identify the AUTHORS of the referenced events —
// The about event's author is NOT tagged: a WakeUp about a zap from Alice // the people whose events are the subject of the wake-up and who should
// to Bob should notify Bob, not Alice. Callers can add more recipients via // come online to handle new activity on them. Callers can add extra
// [initializer]. // e/p tags via [initializer] when waking up about multiple events.
fun build( fun build(
about: EventHintBundle<Event>, about: EventHintBundle<Event>,
createdAt: Long = TimeUtils.now(), createdAt: Long = TimeUtils.now(),
@@ -79,7 +79,7 @@ class WakeUpEvent(
) = eventTemplate(KIND, content, createdAt) { ) = eventTemplate(KIND, content, createdAt) {
alt(ALT_DESCRIPTION) alt(ALT_DESCRIPTION)
about(about) about(about)
notify(about.event.tags.taggedUsers()) notify(about.toPTag())
kind(about.event.kind) kind(about.event.kind)
initializer() initializer()
} }