import type { Database } from '@db/sqlite'; import type * as nostrTools from '@nostr/tools'; import { isAddressableEvent, isReplaceableEvent } from '../utils/eventTypes.ts'; import { log } from '../utils/logs.ts'; import { parseATagQuery } from '../utils/parseATagQuery.ts'; import { sql } from '../utils/queries.ts'; export function handleDeletionEvent( db: Database, deletionEvent: nostrTools.VerifiedEvent, encryptedEvent: nostrTools.VerifiedEvent, ccnPubkey: string, ): void { log.debug('start', { tag: 'handleDeletionEvent', decryptId: deletionEvent.id, encryptedId: encryptedEvent.id, kind: deletionEvent.kind, ccnPubkey, }); try { db.run('BEGIN TRANSACTION'); log.debug('begin transaction', { tag: 'handleDeletionEvent' }); sql` INSERT INTO events (id, original_id, pubkey, created_at, kind, content, sig, first_seen, ccn_pubkey) VALUES ( ${deletionEvent.id}, ${encryptedEvent.id}, ${deletionEvent.pubkey}, ${deletionEvent.created_at}, ${deletionEvent.kind}, ${deletionEvent.content}, ${deletionEvent.sig}, unixepoch(), ${ccnPubkey} ) `(db); if (deletionEvent.tags) { for (let i = 0; i < deletionEvent.tags.length; i++) { const tag = sql` INSERT INTO event_tags(event_id, tag_name, tag_index) VALUES ( ${deletionEvent.id}, ${deletionEvent.tags[i][0]}, ${i} ) RETURNING tag_id `(db)[0]; for (let j = 1; j < deletionEvent.tags[i].length; j++) { sql` INSERT INTO event_tags_values(tag_id, value_position, value) VALUES ( ${tag.tag_id}, ${j}, ${deletionEvent.tags[i][j]} ) `(db); } } } for (const tag of deletionEvent.tags) { if (tag[0] === 'e' && tag[1]) { sql` UPDATE events SET deleted = 1 WHERE id = ${tag[1]} AND pubkey = ${deletionEvent.pubkey} AND ccn_pubkey = ${ccnPubkey} `(db); log.debug('deleted event by id', { tag: 'handleDeletionEvent', eventId: tag[1], }); } else if (tag[0] === 'a' && tag[1]) { const { kind, pubkey, dTag } = parseATagQuery(tag[1]); if (!kind || !pubkey) continue; if (pubkey !== deletionEvent.pubkey) continue; if (isReplaceableEvent(kind)) { sql` UPDATE events SET deleted = 1 WHERE kind = ${kind} AND pubkey = ${pubkey} AND ccn_pubkey = ${ccnPubkey} AND created_at <= ${deletionEvent.created_at} `(db); log.debug('deleted replaceable event', { tag: 'handleDeletionEvent', kind, pubkey, }); } else if (isAddressableEvent(kind) && dTag) { sql` UPDATE events SET deleted = 1 WHERE kind = ${kind} AND pubkey = ${pubkey} AND ccn_pubkey = ${ccnPubkey} AND created_at <= ${deletionEvent.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); log.debug('deleted addressable event', { tag: 'handleDeletionEvent', kind, pubkey, dTag, }); } } else if (tag[0] === 'k') { sql` UPDATE events SET deleted = 1 WHERE kind = ${tag[1]} AND pubkey = ${deletionEvent.pubkey} AND ccn_pubkey = ${ccnPubkey} AND created_at <= ${deletionEvent.created_at} `(db); log.debug('deleted events of kind', { tag: 'handleDeletionEvent', kind: tag[1], }); } } db.run('COMMIT TRANSACTION'); log.debug('committed transaction', { tag: 'handleDeletionEvent' }); } catch (e) { db.run('ROLLBACK TRANSACTION'); log.error('transaction rolled back', { tag: 'handleDeletionEvent', error: e, }); throw e; } log.debug('end', { tag: 'handleDeletionEvent', id: deletionEvent.id }); }