refactor(notifications): per-kind Event.notifies(HexKey) routing
The notification pipeline previously hard-coded a lowercase-`p`-tag match in two places (the observer predicate and consumeFromCache via taggedUserIds). That's correct for most kinds but wrong for two: - NIP-22 CommentEvent: a comment several levels deep only tags the root author via uppercase `P` (RootAuthorTag). Pure lowercase-p routing missed "someone replied deep in your thread" notifications. - Experimental WakeUpEvent (kind 23903): its `p` tags are the authors of the subject events it references — Bob reacting to Alice's post yields a WakeUpEvent with p=Bob, even though Alice's device is the one that needs to wake up. Transport-layer routing (push/relay subscription) already delivered the event to the right device, so the in-event routing has to be permissive. Introduce `open fun Event.notifies(userHex: HexKey): Boolean` with a lowercase-`p` default that covers NIP-01/04/17/25/28/34/57/68/71/84/AC/ chess/wiki/long-form/poll mentions. Each kind with distinct semantics overrides: - CommentEvent.notifies: super.notifies(u) || rootAuthorKeys().contains(u) — picks up uppercase P root-author routing on top of lowercase p. - WakeUpEvent.notifies: true — every logged-in account is a valid wake target once the event has reached LocalCache on this device. NotificationDispatcher's observer predicate and EventNotificationConsumer. consumeFromCache both now ask `event.notifies(accountHex)` instead of extracting taggedUserIds themselves. The zap path's redundant isTaggedUser re-check is gone too (it was duplicating what the outer routing already enforced). https://claude.ai/code/session_01GQDJxiHPogdzCNhUBN7Pjc
This commit is contained in:
+17
@@ -64,6 +64,23 @@ class WakeUpEvent(
|
||||
|
||||
fun kinds() = tags.kinds()
|
||||
|
||||
/**
|
||||
* A WakeUpEvent's `p` tags point to the authors of the referenced subject
|
||||
* events (see [authorKeys]), **not** to the account the wake-up should be
|
||||
* delivered to. Example: Bob reacts to Alice's note, a WakeUpEvent about
|
||||
* Bob's reaction p-tags Bob — but it's Alice's device that needs to wake
|
||||
* up to process the reaction.
|
||||
*
|
||||
* WakeUpEvents reach this device through transport-level routing (push,
|
||||
* relay subscription). By the time one lands in [LocalCache], it is
|
||||
* already "for us" — so every logged-in signing account is a valid
|
||||
* recipient to kick the relay wakeup on behalf of. Returning true here
|
||||
* means the dispatcher invokes [com.vitorpamplona.quartz.experimental.
|
||||
* notifications.wake.WakeUpEvent]-handling for each logged-in account,
|
||||
* which is fine because keeping relay connections alive is idempotent.
|
||||
*/
|
||||
override fun notifies(userHex: HexKey): Boolean = true
|
||||
|
||||
companion object {
|
||||
const val KIND = 23903
|
||||
const val ALT_DESCRIPTION = "WakeUp"
|
||||
|
||||
@@ -45,6 +45,27 @@ open class Event(
|
||||
*/
|
||||
open fun isContentEncoded() = false
|
||||
|
||||
/**
|
||||
* Returns true when this event is intended to notify [userHex].
|
||||
*
|
||||
* The default uses the lowercase `p` tag as the notification channel,
|
||||
* which is the convention for most kinds that address a single recipient
|
||||
* or a set of mentions (NIP-01 mentions, NIP-04/17 DMs, NIP-25 reactions,
|
||||
* NIP-28 chat messages, NIP-34 git issues/patches, NIP-57 zap receipts,
|
||||
* NIP-68 pictures, NIP-71 videos, NIP-84 highlights, NIP-AC calls, chess,
|
||||
* WakeUps, wiki/long-form/poll mentions).
|
||||
*
|
||||
* Subclasses override when the NIP defines additional notification tags
|
||||
* — e.g. NIP-22 comments use uppercase `P` for the root author in
|
||||
* addition to lowercase `p` for the direct-reply author.
|
||||
*/
|
||||
open fun notifies(userHex: HexKey): Boolean {
|
||||
for (tag in tags) {
|
||||
if (tag.size >= 2 && tag[0] == "p" && tag[1] == userHex) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun toJson(): String = OptimizedJsonMapper.toJson(this)
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -197,6 +197,15 @@ class CommentEvent(
|
||||
|
||||
override fun unmarkedReplyTos() = emptyList<String>()
|
||||
|
||||
/**
|
||||
* NIP-22 addresses two distinct recipients: the direct-reply author
|
||||
* (lowercase `p` via [ReplyAuthorTag]) and the root-scope author
|
||||
* (uppercase `P` via [RootAuthorTag]). A comment several levels deep
|
||||
* only tags the root author with uppercase `P`, so the base-class
|
||||
* lowercase-only default would miss them.
|
||||
*/
|
||||
override fun notifies(userHex: HexKey): Boolean = super.notifies(userHex) || rootAuthorKeys().contains(userHex)
|
||||
|
||||
override fun replyingTo(): HexKey? =
|
||||
tags.lastNotNullOfOrNull(ReplyEventTag::parseKey)
|
||||
?: tags.lastNotNullOfOrNull(RootEventTag::parseKey)
|
||||
|
||||
Reference in New Issue
Block a user