Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add DeletionEvent #102

Merged
merged 2 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Sources/NostrSDK/EventCreating.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,26 @@ public extension EventCreating {
return try DirectMessageEvent(content: encryptedMessage, tags: [recipientTag], signedBy: keypair)
}

/// Creates a ``DeletionEvent`` (kind 5) and signs it with the provided ``Keypair``.
/// - Parameters:
/// - events: The events the signer would like to request deletion for.
/// - keypair: The Keypair to sign with.
/// - Returns: The signed ``DeletionEvent``.
///
/// > Important: Events can only be deleted using the same keypair that was used to create them.
/// See [NIP-09 Specification](https://github.com/nostr-protocol/nips/blob/master/09.md)
func delete(events: [NostrEvent], reason: String? = nil, signedBy keypair: Keypair) throws -> DeletionEvent {
// Verify that the events being deleted were created with the same keypair.
let creatorValidatedEvents = events.filter { $0.pubkey == keypair.publicKey.hex }

guard !creatorValidatedEvents.isEmpty else {
throw EventCreatingError.invalidInput
}

let tags = creatorValidatedEvents.map { Tag(name: .event, value: $0.id) }
return try DeletionEvent(content: reason ?? "", tags: tags, signedBy: keypair)
}

/// Creates a ``TextNoteRepostEvent`` (kind 6) or ``GenericRepostEvent`` (kind 16) based on the kind of the event being reposted and signs it with the provided ``Keypair``.
/// - Parameters:
/// - event: The event to repost.
Expand Down
10 changes: 10 additions & 0 deletions Sources/NostrSDK/EventKind.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ public enum EventKind: RawRepresentable, CaseIterable, Codable, Equatable {
/// See [NIP-04 - Direct Messages](https://github.com/nostr-protocol/nips/blob/master/04.md)
case directMessage

/// This kind of event indicates that the author requests that the events in the included
/// tags should be deleted.
/// > Note: This event can only *request* that the listed events be deleted. In reality, they
/// may not be deleted by all clients or relays.
///
/// See [NIP-09 - Event Deletion](https://github.com/nostr-protocol/nips/blob/master/09.md)
case deletion

/// This kind of note is used to signal to followers that another event is worth reading.
///
/// > Note: The reposted event must be a kind 1 text note.
Expand Down Expand Up @@ -70,6 +78,7 @@ public enum EventKind: RawRepresentable, CaseIterable, Codable, Equatable {
.recommendServer,
.contactList,
.directMessage,
.deletion,
.repost,
.reaction,
.genericRepost,
Expand All @@ -88,6 +97,7 @@ public enum EventKind: RawRepresentable, CaseIterable, Codable, Equatable {
case .recommendServer: return 2
case .contactList: return 3
case .directMessage: return 4
case .deletion: return 5
case .repost: return 6
case .reaction: return 7
case .genericRepost: return 16
Expand Down
38 changes: 38 additions & 0 deletions Sources/NostrSDK/Events/DeletionEvent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// DeletionEvent.swift
//
//
// Created by Bryan Montz on 10/29/23.
//

import Foundation

/// An event that contains one or more references to other events that the
/// event creator would like to delete.
///
/// > Note: [NIP-09 Specification](https://github.com/nostr-protocol/nips/blob/master/09.md)
public final class DeletionEvent: NostrEvent {

public required init(from decoder: Decoder) throws {
try super.init(from: decoder)
}

@available(*, unavailable, message: "This initializer is unavailable for this class.")
override init(kind: EventKind, content: String, tags: [Tag] = [], createdAt: Int64 = Int64(Date.now.timeIntervalSince1970), signedBy keypair: Keypair) throws {
try super.init(kind: kind, content: content, tags: tags, createdAt: createdAt, signedBy: keypair)
}

init(content: String, tags: [Tag] = [], createdAt: Int64 = Int64(Date.now.timeIntervalSince1970), signedBy keypair: Keypair) throws {
try super.init(kind: .deletion, content: content, tags: tags, createdAt: createdAt, signedBy: keypair)
}

/// The reason the creator of the event gave for deleting the included events.
public var reason: String {
content
}

/// The event ids that the creator requests deletion for.
public var deletedEventIds: [String] {
tags.filter { $0.name == .event }.map { $0.value }
}
}
21 changes: 21 additions & 0 deletions Tests/NostrSDKTests/EventCreatingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,28 @@ final class EventCreatingTests: XCTestCase, EventCreating, EventVerifying, Fixtu

try verifyEvent(event)
}

func testDeletionEvent() throws {
let noteToDelete: TextNoteEvent = try decodeFixture(filename: "text_note_deletable")
let reason = "Didn't mean to post"

let event = try delete(events: [noteToDelete], reason: reason, signedBy: Keypair.test)

XCTAssertEqual(event.kind, .deletion)

XCTAssertEqual(event.reason, "Didn't mean to post")
XCTAssertEqual(event.deletedEventIds, ["fa5ed84fc8eeb959fd39ad8e48388cfc33075991ef8e50064cfcecfd918bb91b"])

try verifyEvent(event)
}

func testDeletionEventFailsWithMismatchedKey() throws {
let noteToDelete: TextNoteEvent = try decodeFixture(filename: "text_note")
let reason = "Didn't mean to post"

XCTAssertThrowsError(try delete(events: [noteToDelete], reason: reason, signedBy: Keypair.test))
}

func testRepostTextNoteEvent() throws {
let noteToRepost: TextNoteEvent = try decodeFixture(filename: "text_note")

Expand Down
18 changes: 18 additions & 0 deletions Tests/NostrSDKTests/Fixtures/text_note_deletable.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"id": "fa5ed84fc8eeb959fd39ad8e48388cfc33075991ef8e50064cfcecfd918bb91b",
"pubkey": "9947f9659dd80c3682402b612f5447e28249997fb3709500c32a585eb0977340",
"created_at": 1682080184,
"kind": 1,
"tags": [
[
"e",
"93930d65435d49db723499335473920795e7f13c45600dcfad922135cf44bd63"
],
[
"p",
"f8e6c64342f1e052480630e27e1016dce35fc3a614e60434fef4aa2503328ca9"
]
],
"content": "I think it stays persistent on your profile, but interface setting doesn’t persist. Bug. ",
"sig": "96e6667348b2b1fc5f6e73e68fb1605f571ad044077dda62a35c15eb8290f2c4559935db461f8466df3dcf39bc2e11984c5344f65aabee4520dd6653d74cdc09"
}
Loading