Add Damus client tag emission

- Add ClientTagMetadata struct with parsing helpers and documentation
- Append Damus client tags when posting across app, share, and drafts flows
- Gate the behavior behind a new publish_client_tag setting (default on)

Changelog-Added: Add client tag to published events to identify Damus
Ref: https://github.com/damus-io/damus/issues/3323
Signed-off-by: alltheseas <alltheseas@users.noreply.github.com>
This commit is contained in:
alltheseas
2026-02-02 12:15:11 -06:00
committed by Daniel D’Aquino
parent 795fce1b65
commit ec28822451
11 changed files with 126 additions and 11 deletions

View File

@@ -40,4 +40,18 @@ final class NostrEventTests: XCTestCase {
let urlInContent2 = "https://cdn.nostr.build/i/5c1d3296f66c2630131bf123106486aeaf051ed8466031c0e0532d70b33cddb2.jpg"
XCTAssert(testEvent2.content.contains(urlInContent2), "Issue parsing event. Expected to see '\(urlInContent2)' inside \(testEvent2.content)")
}
func testClientTagParsing() {
let tags = [["client", "Custom Client", "addr", "wss://relay.example"], ["p", test_pubkey.hex()]]
let event = NostrEvent(content: "hi", keypair: test_keypair, kind: 1, tags: tags)!
let metadata = event.clientTag
XCTAssertEqual(metadata?.name, "Custom Client")
XCTAssertEqual(metadata?.handlerAddress, "addr")
XCTAssertEqual(metadata?.relayHint, "wss://relay.example")
}
func testClientTagNilWhenMissing() {
let event = NostrEvent(content: "hi", keypair: test_keypair, kind: 1, tags: [])!
XCTAssertNil(event.clientTag)
}
}

View File

@@ -220,7 +220,7 @@ final class PostViewTests: XCTestCase {
XCTAssertEqual(post.content, "nostr:\(test_pubkey.npub)")
}
/// Tests that pasting an npub converts it to a mention link (issue #2289)
/// Tests that pasting an npub converts it to a mention link (issue #2289)
func testPastedNpubConvertsToMention() {
let content = NSMutableAttributedString(string: "Hello ")
var resultContent: NSMutableAttributedString?
@@ -318,6 +318,25 @@ final class PostViewTests: XCTestCase {
XCTAssertTrue(shouldChange, "shouldChangeTextIn should return true for regular text")
}
/// Tests that client tags are added to events when provided.
func testToEventAddsClientTagWhenProvided() {
let post = NostrPost(content: "gm")
let clientTag = ["client", "Damus"]
let event = post.to_event(keypair: test_keypair_full, clientTag: clientTag)
XCTAssertTrue(event?.tags.contains(where: { $0 == clientTag }) ?? false)
}
/// Tests that existing client tags are not duplicated.
func testToEventDoesNotDuplicateExistingClientTag() {
let existingTags = [["client", "Custom"]]
let post = NostrPost(content: "gm", tags: existingTags)
let clientTag = ["client", "Damus"]
let event = post.to_event(keypair: test_keypair_full, clientTag: clientTag)
let clientTagCount = event?.tags.filter { $0.first == "client" }.count
XCTAssertEqual(clientTagCount, 1)
XCTAssertEqual(event?.tags.first(where: { $0.first == "client" }), existingTags.first)
}
}
func checkMentionLinkEditorHandling(
@@ -354,4 +373,3 @@ func testAddingStringAfterLink(str: String) {
})
}