From 65c34e68113e931cd0ede66a51cda4fb41e61066 Mon Sep 17 00:00:00 2001 From: Danny Morabito Date: Tue, 18 Mar 2025 16:04:38 +0100 Subject: [PATCH] create CCN replaceable events --- index.ts | 67 +++++++++++++++++++++++++++++++++++++++++++------------- utils.ts | 16 +++++++++----- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/index.ts b/index.ts index 46962c6..4186bc0 100644 --- a/index.ts +++ b/index.ts @@ -9,6 +9,7 @@ import { getCCNPubkey, isAddressableEvent, isArray, + isCCNReplaceableEvent, isLocalhost, isReplaceableEvent, isValidJSON, @@ -207,6 +208,25 @@ function addEventToDb( } } + if (isCCNReplaceableEvent(decryptedEvent.kind)) { + const dTag = decryptedEvent.tags.find((tag) => tag[0] === "d")?.[1]; + sql` + UPDATE events + SET replaced = 1 + WHERE kind = ${decryptedEvent.kind} + AND created_at < ${decryptedEvent.created_at} + AND id IN ( + SELECT event_id FROM event_tags + WHERE tag_name = 'd' + AND tag_id IN ( + SELECT tag_id FROM event_tags_values + WHERE value_position = 1 + AND value = ${dTag} + ) + ) + `(db); + } + sql` INSERT INTO events (id, original_id, pubkey, created_at, kind, content, sig, first_seen) VALUES ( ${decryptedEvent.id}, @@ -472,21 +492,38 @@ function handleRequest(connection: UserConnection, request: NostrClientREQ) { const aTagInfo = parseATagQuery(tagValue); if (aTagInfo.dTag && aTagInfo.dTag !== "") { - // Addressable event reference - query = mixQuery( - query, - sqlPartial`id IN ( - SELECT e.id - FROM events e - JOIN event_tags t ON e.id = t.event_id - JOIN event_tags_values v ON t.tag_id = v.tag_id - WHERE e.kind = ${aTagInfo.kind} - AND e.pubkey = ${aTagInfo.pubkey} - AND t.tag_name = 'd' - AND v.value_position = 1 - AND v.value = ${aTagInfo.dTag} - )`, - ); + if (isCCNReplaceableEvent(aTagInfo.kind)) { + // CCN replaceable event reference + query = mixQuery( + query, + sqlPartial`id IN ( + SELECT e.id + FROM events e + JOIN event_tags t ON e.id = t.event_id + JOIN event_tags_values v ON t.tag_id = v.tag_id + WHERE e.kind = ${aTagInfo.kind} + AND t.tag_name = 'd' + AND v.value_position = 1 + AND v.value = ${aTagInfo.dTag} + )`, + ); + } else { + // Addressable event reference + query = mixQuery( + query, + sqlPartial`id IN ( + SELECT e.id + FROM events e + JOIN event_tags t ON e.id = t.event_id + JOIN event_tags_values v ON t.tag_id = v.tag_id + WHERE e.kind = ${aTagInfo.kind} + AND e.pubkey = ${aTagInfo.pubkey} + AND t.tag_name = 'd' + AND v.value_position = 1 + AND v.value = ${aTagInfo.dTag} + )`, + ); + } } else { // Replaceable event reference query = mixQuery( diff --git a/utils.ts b/utils.ts index 5c7c5c6..aecc1fd 100644 --- a/utils.ts +++ b/utils.ts @@ -83,13 +83,19 @@ export function isRegularEvent(kind: number): boolean { kind === 2; } -export function parseATagQuery(aTagValue: string): { kind: number, pubkey: string, dTag?: string } { - const parts = aTagValue.split(':'); - if (parts.length < 2) return { kind: 0, pubkey: '' }; +export function isCCNReplaceableEvent(kind: number): boolean { + return (kind >= 60000 && kind < 65536) || kind === 0; +} + +export function parseATagQuery( + aTagValue: string, +): { kind: number; pubkey: string; dTag?: string } { + const parts = aTagValue.split(":"); + if (parts.length < 2) return { kind: 0, pubkey: "" }; return { - kind: parseInt(parts[0], 10), + kind: Number.parseInt(parts[0], 10), pubkey: parts[1], - dTag: parts.length > 2 ? parts[2] : undefined + dTag: parts.length > 2 ? parts[2] : undefined, }; }