From ff452ebe772e8ff8decca20583aa430105d4c56c Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:40:10 -0400 Subject: [PATCH] Stop default view events from adding legacy tags --- docs/nostr-event-schemas.md | 2 +- js/nostrEventSchemas.js | 28 ++++++++++++++++++++------- tests/view-counter.test.mjs | 38 +++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/docs/nostr-event-schemas.md b/docs/nostr-event-schemas.md index a5c88dd9..158359b2 100644 --- a/docs/nostr-event-schemas.md +++ b/docs/nostr-event-schemas.md @@ -48,7 +48,7 @@ read/write split. | Video post (`NOTE_TYPES.VIDEO_POST`) | `30078` | `['t','video']`, `['d', ]` plus optional schema append tags | JSON payload using Content Schema v3 (`version`, `title`, optional `url`, `magnet`, `thumbnail`, `description`, `mode`, `videoRootId`, `deleted`, `isPrivate`, `enableComments`, `ws`, `xs`) | | NIP-94 mirror (`NOTE_TYPES.VIDEO_MIRROR`) | `1063` | Tags forwarded from `publishVideo` (URL, mime type, thumbnail, alt text, magnet) | Plain text alt description | | Relay list (`NOTE_TYPES.RELAY_LIST`) | `10002` | Repeating `['r', ]` tags, optionally with a marker of `'read'` or `'write'` to scope the relay; marker omitted for read/write relays | Empty content | -| View counter (`NOTE_TYPES.VIEW_EVENT`) | `WATCH_HISTORY_KIND` (default `30078`) | `['t','view']`, `['video', ]`, pointer tag (`['a', ...]` or `['e', ...]`), optional dedupe `['d', ]`, optional `['session','true']` when a session actor signs, plus any extra debugging tags | Optional plaintext message | +| View counter (`NOTE_TYPES.VIEW_EVENT`) | `WATCH_HISTORY_KIND` (default `30078`) | Canonical tag set: `['t','view']` plus either `['e', ]` or `['a',
]`, with optional `['session','true']` when a session actor signs; schema overrides may append extra tags. `['video', ...]` is supported for legacy overrides only. | Optional plaintext message | | Watch history index (`NOTE_TYPES.WATCH_HISTORY_INDEX`) | `WATCH_HISTORY_KIND` | `['d', WATCH_HISTORY_LIST_IDENTIFIER]`, `['snapshot', ]`, `['chunks', ]`, repeated `['a',
]` pointers to each chunk event plus schema append tags | JSON payload `{ snapshot, totalChunks }` | | Watch history chunk (`NOTE_TYPES.WATCH_HISTORY_CHUNK`) | `WATCH_HISTORY_KIND` | `['d', ]`, `['encrypted','nip04']`, `['snapshot', ]`, `['chunk', , ]`, pointer tags for each entry, plus schema append tags | NIP-04 encrypted JSON chunk (`{ version, snapshot, chunkIndex, totalChunks, items[] }`) | | Subscription list (`NOTE_TYPES.SUBSCRIPTION_LIST`) | `30002` | `['d', 'subscriptions']` | NIP-04 encrypted JSON `{ subPubkeys: string[] }` | diff --git a/js/nostrEventSchemas.js b/js/nostrEventSchemas.js index 96ff9d25..869bf1b3 100644 --- a/js/nostrEventSchemas.js +++ b/js/nostrEventSchemas.js @@ -83,8 +83,6 @@ const BASE_SCHEMAS = { label: "View counter", kind: WATCH_HISTORY_KIND, topicTag: { name: "t", value: "view" }, - pointerTagName: "video", - identifierTag: { name: "d" }, sessionTag: { name: "session", value: "true" }, appendTags: DEFAULT_APPEND_TAGS, content: { @@ -418,6 +416,7 @@ export function buildViewEvent({ created_at, pointerValue, pointerTag, + pointerTags = [], dedupeTag, includeSessionTag = false, additionalTags = [], @@ -429,13 +428,28 @@ export function buildViewEvent({ tags.push([schema.topicTag.name, schema.topicTag.value]); } - const pointerTagName = schema?.pointerTagName || "video"; - if (pointerValue) { + const pointerTagName = schema?.pointerTagName; + if (pointerValue && pointerTagName) { tags.push([pointerTagName, pointerValue]); } + const normalizedPointerTags = []; if (Array.isArray(pointerTag) && pointerTag.length >= 2) { - tags.push(pointerTag.map((value) => (typeof value === "string" ? value : String(value)))); + normalizedPointerTags.push( + pointerTag.map((value) => (typeof value === "string" ? value : String(value))) + ); } + if (Array.isArray(pointerTags)) { + pointerTags.forEach((tag) => { + if (Array.isArray(tag) && tag.length >= 2) { + normalizedPointerTags.push( + tag.map((value) => (typeof value === "string" ? value : String(value))) + ); + } + }); + } + normalizedPointerTags.forEach((tag) => { + tags.push(tag); + }); if (Array.isArray(additionalTags)) { additionalTags.forEach((tag) => { if (Array.isArray(tag) && tag.length >= 2) { @@ -444,8 +458,8 @@ export function buildViewEvent({ }); } - if (dedupeTag) { - const identifierName = schema?.identifierTag?.name || "d"; + if (dedupeTag && schema?.identifierTag?.name) { + const identifierName = schema.identifierTag.name; const hasDedupe = tags.some( (tag) => tag[0] === identifierName && tag[1] === dedupeTag ); diff --git a/tests/view-counter.test.mjs b/tests/view-counter.test.mjs index 897ccdda..d627b8eb 100644 --- a/tests/view-counter.test.mjs +++ b/tests/view-counter.test.mjs @@ -9,6 +9,10 @@ const { VIEW_COUNT_CACHE_TTL_MS, } = await import("../js/config.js"); +const { buildViewEvent, setNostrEventSchemaOverrides } = await import( + "../js/nostrEventSchemas.js" +); + const VIEW_COUNTER_STORAGE_KEY = "bitvid:view-counter:v1"; const CACHE_TTL_TEST_POINTER = { type: "e", value: "view-counter-cache-ttl" }; @@ -42,6 +46,40 @@ if (!globalThis.window.NostrTools) { globalThis.window.NostrTools = {}; } +setNostrEventSchemaOverrides({}); + +const canonicalViewEvent = buildViewEvent({ + pubkey: "actor-canonical", + created_at: 1000, + pointerValue: CACHE_TTL_TEST_POINTER.value, + pointerTag: [CACHE_TTL_TEST_POINTER.type, CACHE_TTL_TEST_POINTER.value], + dedupeTag: "ignore-dedupe", +}); + +assert.deepEqual( + canonicalViewEvent.tags, + [["t", "view"], [CACHE_TTL_TEST_POINTER.type, CACHE_TTL_TEST_POINTER.value]], + "view event should only include topic and supplied pointer tags by default" +); + +const sessionViewEvent = buildViewEvent({ + pubkey: "actor-session", + created_at: 2000, + pointerValue: "kind:1234:actor-session", + pointerTag: ["a", "kind:1234:actor-session"], + includeSessionTag: true, +}); + +assert.deepEqual( + sessionViewEvent.tags, + [ + ["t", "view"], + ["a", "kind:1234:actor-session"], + ["session", "true"], + ], + "view event should append the session tag when requested" +); + const { nostrClient } = await import("../js/nostr.js"); function createMockNostrHarness() {