diff --git a/damus-c/damus.c b/damus-c/damus.c index b13b4916..2992ae81 100644 --- a/damus-c/damus.c +++ b/damus-c/damus.c @@ -180,9 +180,9 @@ static int parse_invoice(struct cursor *cur, struct note_block *block) { static int parse_mention_bech32(struct cursor *cur, struct note_block *block) { u8 *start = cur->p; - if (!parse_str(cur, "nostr:")) - return 0; - + parse_char(cur, '@'); + parse_str(cur, "nostr:"); + block->block.str.start = (const char *)cur->p; if (!parse_nostr_bech32(cur, &block->block.mention_bech32.bech32)) { @@ -231,7 +231,7 @@ int damus_parse_content(struct note_blocks *blocks, const char *content) { } pre_mention = cur.p; - if (cp == -1 || is_whitespace(cp) || c == '#') { + if (cp == -1 || is_boundary(cp) || c == '#') { if (c == '#' && (parse_mention_index(&cur, &block) || parse_hashtag(&cur, &block))) { if (!add_text_then_block(&cur, blocks, block, &start, pre_mention)) return 0; @@ -244,7 +244,7 @@ int damus_parse_content(struct note_blocks *blocks, const char *content) { if (!add_text_then_block(&cur, blocks, block, &start, pre_mention)) return 0; continue; - } else if (c == 'n' && parse_mention_bech32(&cur, &block)) { + } else if ((c == 'n' || c == '@') && parse_mention_bech32(&cur, &block)) { if (!add_text_then_block(&cur, blocks, block, &start, pre_mention)) return 0; continue; diff --git a/damus-c/nostr_bech32.c b/damus-c/nostr_bech32.c index 27874315..9710fbe2 100644 --- a/damus-c/nostr_bech32.c +++ b/damus-c/nostr_bech32.c @@ -91,6 +91,9 @@ static int parse_nostr_bech32_type(const char *prefix, enum nostr_bech32_type *t } else if (strcmp(prefix, "npub") == 0) { *type = NOSTR_BECH32_NPUB; return 1; + } else if (strcmp(prefix, "nsec") == 0) { + *type = NOSTR_BECH32_NSEC; + return 1; } else if (strcmp(prefix, "nprofile") == 0) { *type = NOSTR_BECH32_NPROFILE; return 1; @@ -116,6 +119,10 @@ static int parse_nostr_bech32_npub(struct cursor *cur, struct bech32_npub *npub) return pull_bytes(cur, 32, &npub->pubkey); } +static int parse_nostr_bech32_nsec(struct cursor *cur, struct bech32_nsec *nsec) { + return pull_bytes(cur, 32, &nsec->nsec); +} + static int tlvs_to_relays(struct nostr_tlvs *tlvs, struct relays *relays) { struct nostr_tlv *tlv; struct str_block *str; @@ -268,6 +275,10 @@ int parse_nostr_bech32(struct cursor *cur, struct nostr_bech32 *obj) { if (!parse_nostr_bech32_npub(&bcur, &obj->data.npub)) goto fail; break; + case NOSTR_BECH32_NSEC: + if (!parse_nostr_bech32_nsec(&bcur, &obj->data.nsec)) + goto fail; + break; case NOSTR_BECH32_NEVENT: if (!parse_nostr_bech32_nevent(&bcur, &obj->data.nevent)) goto fail; diff --git a/damus-c/nostr_bech32.h b/damus-c/nostr_bech32.h index c6b79543..23a381d9 100644 --- a/damus-c/nostr_bech32.h +++ b/damus-c/nostr_bech32.h @@ -26,6 +26,7 @@ enum nostr_bech32_type { NOSTR_BECH32_NEVENT = 4, NOSTR_BECH32_NRELAY = 5, NOSTR_BECH32_NADDR = 6, + NOSTR_BECH32_NSEC = 7, }; struct bech32_note { @@ -36,6 +37,10 @@ struct bech32_npub { const u8 *pubkey; }; +struct bech32_nsec { + const u8 *nsec; +}; + struct bech32_nevent { struct relays relays; const u8 *event_id; @@ -65,6 +70,7 @@ typedef struct nostr_bech32 { union { struct bech32_note note; struct bech32_npub npub; + struct bech32_nsec nsec; struct bech32_nevent nevent; struct bech32_nprofile nprofile; struct bech32_naddr naddr; diff --git a/damus/Models/Mentions.swift b/damus/Models/Mentions.swift index 9d8ea5a0..eadbf1c3 100644 --- a/damus/Models/Mentions.swift +++ b/damus/Models/Mentions.swift @@ -25,6 +25,14 @@ struct Mention: Equatable { let index: Int? let type: MentionType let ref: ReferencedId + + static func note(_ id: String) -> Mention { + return Mention(index: nil, type: .event, ref: .e(id)) + } + + static func pubkey(_ pubkey: String) -> Mention { + return Mention(index: nil, type: .pubkey, ref: .p(pubkey)) + } } typealias Invoice = LightningInvoice @@ -114,12 +122,12 @@ enum Block: Equatable { return mention.type == .event } - - var is_mention: Bool { - if case .mention = self { - return true + + var is_mention: Mention? { + if case .mention(let m) = self { + return m } - return false + return nil } } @@ -332,7 +340,13 @@ func convert_mention_bech32_block(_ b: mention_bech32_block) -> Block? let pubkey = hex_encode(Data(bytes: npub.pubkey, count: 32)) let pubkey_ref = ReferencedId(ref_id: pubkey, relay_id: nil, key: "p") return .mention(Mention(index: nil, type: .pubkey, ref: pubkey_ref)) - + + case NOSTR_BECH32_NSEC: + let nsec = b.bech32.data.nsec + let nsec_bytes = Data(bytes: nsec.nsec, count: 32) + let pubkey = privkey_to_pubkey_raw(sec: nsec_bytes.bytes) ?? hex_encode(nsec_bytes) + return .mention(.pubkey(pubkey)) + case NOSTR_BECH32_NPROFILE: let nprofile = b.bech32.data.nprofile let pubkey = hex_encode(Data(bytes: nprofile.pubkey, count: 32)) @@ -394,65 +408,6 @@ func convert_mention_index_block(ind: Int32, tags: [[String]]) -> Block? return .mention(Mention(index: ind, type: mention_type, ref: ref)) } -func parse_while(_ p: Parser, match: (Character) -> Bool) -> String? { - var i: Int = 0 - let sub = substring(p.str, start: p.pos, end: p.str.count) - let start = p.pos - for c in sub { - if match(c) { - p.pos += 1 - } else { - break - } - i += 1 - } - - let end = start + i - if start == end { - return nil - } - return String(substring(p.str, start: start, end: end)) -} - -func is_hashtag_char(_ c: Character) -> Bool { - return (c.isLetter || c.isNumber || c.isASCII) && (!c.isPunctuation && !c.isWhitespace) -} - -func prev_char(_ p: Parser, n: Int) -> Character? { - if p.pos - n < 0 { - return nil - } - - let ind = p.str.index(p.str.startIndex, offsetBy: p.pos - n) - return p.str[ind] -} - -func is_punctuation(_ c: Character) -> Bool { - return c.isWhitespace || c.isPunctuation -} - -func parse_hashtag(_ p: Parser) -> String? { - let start = p.pos - - if !parse_char(p, "#") { - return nil - } - - if let prev = prev_char(p, n: 2) { - // we don't allow adjacent hashtags - if !is_punctuation(prev) { - return nil - } - } - - guard let str = parse_while(p, match: is_hashtag_char) else { - p.pos = start - return nil - } - - return str -} - func find_tag_ref(type: String, id: String, tags: [[String]]) -> Int? { var i: Int = 0 for tag in tags { @@ -483,47 +438,31 @@ func parse_mention_type(_ c: String) -> MentionType? { } /// Convert -func make_post_tags(post_blocks: [PostBlock], tags: [[String]], silent_mentions: Bool) -> PostTags { +func make_post_tags(post_blocks: [Block], tags: [[String]], silent_mentions: Bool) -> PostTags { var new_tags = tags - var blocks: [Block] = [] - + for post_block in post_blocks { switch post_block { - case .ref(let ref): - guard let mention_type = parse_mention_type(ref.key) else { - continue - } - + case .mention(let mention): + let mention_type = mention.type + if silent_mentions || mention_type == .event { - let mention = Mention(index: nil, type: mention_type, ref: ref) - let block = Block.mention(mention) - blocks.append(block) continue } - - if find_tag_ref(type: ref.key, id: ref.ref_id, tags: tags) != nil { - // Mention index is nil because indexed mentions from NIP-08 is deprecated. - // It has been replaced with NIP-27 text note references with nostr: prefixed URIs. - let mention = Mention(index: nil, type: mention_type, ref: ref) - let block = Block.mention(mention) - blocks.append(block) - } else { - new_tags.append(refid_to_tag(ref)) - // Mention index is nil because indexed mentions from NIP-08 is deprecated. - // It has been replaced with NIP-27 text note references with nostr: prefixed URIs. - let mention = Mention(index: nil, type: mention_type, ref: ref) - let block = Block.mention(mention) - blocks.append(block) - } + + new_tags.append(refid_to_tag(mention.ref)) case .hashtag(let hashtag): new_tags.append(["t", hashtag.lowercased()]) - blocks.append(.hashtag(hashtag)) - case .text(let txt): - blocks.append(Block.text(txt)) + case .text: break + case .invoice: break + case .relay: break + case .url(let url): + new_tags.append(["r", url.absoluteString]) + break } } - return PostTags(blocks: blocks, tags: new_tags) + return PostTags(blocks: post_blocks, tags: new_tags) } func post_to_event(post: NostrPost, privkey: String, pubkey: String) -> NostrEvent { diff --git a/damus/Models/Post.swift b/damus/Models/Post.swift index 211fa275..d68650d2 100644 --- a/damus/Models/Post.swift +++ b/damus/Models/Post.swift @@ -109,32 +109,7 @@ func parse_post_bech32_mention(_ p: Parser) -> ReferencedId? { } /// Return a list of tags -func parse_post_blocks(content: String) -> [PostBlock] { - let p = Parser(pos: 0, str: content) - var blocks: [PostBlock] = [] - var starting_from: Int = 0 - - if content.count == 0 { - return [] - } - - while p.pos < content.count { - let pre_mention = p.pos - if let reference = parse_post_reference(p) { - blocks.append(parse_post_textblock(str: p.str, from: starting_from, to: pre_mention)) - blocks.append(.ref(reference)) - starting_from = p.pos - } else if let hashtag = parse_hashtag(p) { - blocks.append(parse_post_textblock(str: p.str, from: starting_from, to: pre_mention)) - blocks.append(.hashtag(hashtag)) - starting_from = p.pos - } else { - p.pos += 1 - } - } - - blocks.append(parse_post_textblock(str: content, from: starting_from, to: content.count)) - - return blocks +func parse_post_blocks(content: String) -> [Block] { + return parse_mentions(content: content, tags: []).blocks } diff --git a/damus/Nostr/NostrEvent.swift b/damus/Nostr/NostrEvent.swift index 81484765..951cef9e 100644 --- a/damus/Nostr/NostrEvent.swift +++ b/damus/Nostr/NostrEvent.swift @@ -36,6 +36,10 @@ struct ReferencedId: Identifiable, Hashable, Equatable { static func e(_ id: String, relay_id: String? = nil) -> ReferencedId { return ReferencedId(ref_id: id, relay_id: relay_id, key: "e") } + + static func p(_ pk: String, relay_id: String? = nil) -> ReferencedId { + return ReferencedId(ref_id: pk, relay_id: relay_id, key: "p") + } } class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable, Hashable, Comparable { diff --git a/damus/Util/Keys.swift b/damus/Util/Keys.swift index 6545331c..da09f652 100644 --- a/damus/Util/Keys.swift +++ b/damus/Util/Keys.swift @@ -93,16 +93,18 @@ func generate_new_keypair() -> Keypair { return Keypair(pubkey: pubkey, privkey: privkey) } -func privkey_to_pubkey(privkey: String) -> String? { - guard let sec = hex_decode(privkey) else { - return nil - } +func privkey_to_pubkey_raw(sec: [UInt8]) -> String? { guard let key = try? secp256k1.Signing.PrivateKey(rawRepresentation: sec) else { return nil } return hex_encode(Data(key.publicKey.xonly.bytes)) } +func privkey_to_pubkey(privkey: String) -> String? { + guard let sec = hex_decode(privkey) else { return nil } + return privkey_to_pubkey_raw(sec: sec) +} + func save_pubkey(pubkey: String) { UserDefaults.standard.set(pubkey, forKey: "pubkey") } diff --git a/damusTests/HashtagTests.swift b/damusTests/HashtagTests.swift index 0bb6d511..ab2aa659 100644 --- a/damusTests/HashtagTests.swift +++ b/damusTests/HashtagTests.swift @@ -39,15 +39,37 @@ final class HashtagTests: XCTestCase { } func testHashtagWithEmoji() { - let parsed = parse_mentions(content: "some hashtag #bitcoin☕️ cool", tags: []).blocks - + let content = "some hashtag #bitcoin☕️ cool" + let parsed = parse_mentions(content: content, tags: []).blocks + let post_blocks = parse_post_blocks(content: content) + XCTAssertNotNil(parsed) XCTAssertEqual(parsed.count, 3) XCTAssertEqual(parsed[0].is_text, "some hashtag ") XCTAssertEqual(parsed[1].is_hashtag, "bitcoin☕️") XCTAssertEqual(parsed[2].is_text, " cool") + + XCTAssertEqual(post_blocks.count, 3) + XCTAssertEqual(post_blocks[0].is_text, "some hashtag ") + XCTAssertEqual(post_blocks[1].is_hashtag, "bitcoin☕️") + XCTAssertEqual(post_blocks[2].is_text, " cool") } - + + func testPowHashtag() { + let content = "pow! #ぽわ〜" + let parsed = parse_mentions(content: content, tags: []).blocks + let post_blocks = parse_post_blocks(content: content) + + XCTAssertNotNil(parsed) + XCTAssertEqual(parsed.count, 2) + XCTAssertEqual(parsed[0].is_text, "pow! ") + XCTAssertEqual(parsed[1].is_hashtag, "ぽわ〜") + + XCTAssertEqual(post_blocks.count, 2) + XCTAssertEqual(post_blocks[0].is_text, "pow! ") + XCTAssertEqual(post_blocks[1].is_hashtag, "ぽわ〜") + } + func testHashtagWithAccents() { let parsed = parse_mentions(content: "hello from #türkiye", tags: []).blocks diff --git a/damusTests/Models/DamusParseContentTests.swift b/damusTests/Models/DamusParseContentTests.swift index 099da5d3..50b0c13a 100644 --- a/damusTests/Models/DamusParseContentTests.swift +++ b/damusTests/Models/DamusParseContentTests.swift @@ -52,7 +52,7 @@ class DamusParseContentTests: XCTestCase { return } - if currentBlock.is_mention { + if currentBlock.is_mention != nil { XCTAssert(isMentionBlockSet.contains(i)) } else { XCTAssert(!isMentionBlockSet.contains(i)) diff --git a/damusTests/ReplyTests.swift b/damusTests/ReplyTests.swift index 2bd68f85..72a64940 100644 --- a/damusTests/ReplyTests.swift +++ b/damusTests/ReplyTests.swift @@ -39,8 +39,10 @@ class ReplyTests: XCTestCase { let content = "this is my link: https://jb55.com/index.html#buybitcoin this is not a hashtag!" let blocks = parse_post_blocks(content: content) - XCTAssertEqual(blocks.count, 1) - XCTAssertEqual(blocks[0].is_text != nil, true) + XCTAssertEqual(blocks.count, 3) + XCTAssertEqual(blocks[0].is_text, "this is my link: ") + XCTAssertEqual(blocks[1].is_url, URL(string: "https://jb55.com/index.html#buybitcoin")!) + XCTAssertEqual(blocks[2].is_text, " this is not a hashtag!") } func testLinkIsNotAHashtag() { @@ -49,8 +51,10 @@ class ReplyTests: XCTestCase { let content = "my \(link) link" let blocks = parse_post_blocks(content: content) - XCTAssertEqual(blocks.count, 1) - XCTAssertEqual(blocks[0].is_text, content) + XCTAssertEqual(blocks.count, 3) + XCTAssertEqual(blocks[0].is_text, "my ") + XCTAssertEqual(blocks[1].is_url, URL(string: link)!) + XCTAssertEqual(blocks[2].is_text, " link") } func testAtAtEnd() { @@ -74,23 +78,17 @@ class ReplyTests: XCTestCase { func testHashtagAtStartWorks() { let content = "#hashtag" let blocks = parse_post_blocks(content: content) - XCTAssertEqual(blocks.count, 3) - XCTAssertEqual(blocks[1].is_hashtag, "hashtag") + XCTAssertEqual(blocks.count, 1) + XCTAssertEqual(blocks[0].is_hashtag, "hashtag") } func testGroupOfHashtags() { let content = "#hashtag#what#nope" let blocks = parse_post_blocks(content: content) XCTAssertEqual(blocks.count, 3) - XCTAssertEqual(blocks[1].is_hashtag, "hashtag") - XCTAssertEqual(blocks[2].is_text, "#what#nope") - - switch blocks[1] { - case .hashtag(let htag): - XCTAssertEqual(htag, "hashtag") - default: - break - } + XCTAssertEqual(blocks[0].is_hashtag, "hashtag") + XCTAssertEqual(blocks[1].is_hashtag, "what") + XCTAssertEqual(blocks[2].is_hashtag, "nope") } func testRootReplyWithMention() throws { @@ -124,32 +122,12 @@ class ReplyTests: XCTestCase { XCTAssertEqual(post_tags.tags.count, 0) XCTAssertEqual(post_blocks.count, 1) } - - func testManyPostMentions() throws { - let content = """ -@38bc54a8f675564058b987056fc27fe3d40ca34404586933a115d9e0baeaccb9 -@774734fad6c318799149c35008c356352b8bfc1791d9e41c803bd412b23143be -@d64266d4bbf3cbcb773d074ee5ffe9ae557425cce0521e102dfde88a7223fb4c -@9f936cfb57374c95c4b8f2d5e640d978e4c59ccbe7783d434f434a8cc69bfa07 -@29080a53a6cef22b28dd8c9a25684cb9c2691f8f0c98651d20c65e1a2cd5cef1 -@dcdc52ec631c4034b0766a49865ec2e7fc0cdb2ba071aff4050eba343e7ba0fe -@136f15a6e4c5f046a71ddaf014bbca51408041d5d0ec2a0154be4b089e6f0249 -@5d994e704a4d3edf0163a708f69cb821f5a9caefeb79c17c1507e11e8a238f36 -@d76951e648f1b00715fe55003fcfb6fe91a7bf73fca5b6fd3e5bbe6845a5a0b1 -@3e999f94e2cb34ef44a64b351141ac4e51b5121b2d31aed4a6c84602a1144692 -""" - //let tags: [[String]] = [] - let blocks = parse_post_blocks(content: content) - - let mentions = blocks.filter { $0.is_ref != nil } - XCTAssertEqual(mentions.count, 10) - } - + func testManyMentions() throws { let content = "#[10]" let tags: [[String]] = [[],[],[],[],[],[],[],[],[],[],["p", "3e999f94e2cb34ef44a64b351141ac4e51b5121b2d31aed4a6c84602a1144692"]] let blocks = parse_mentions(content: content, tags: tags).blocks - let mentions = blocks.filter { $0.is_mention } + let mentions = blocks.filter { $0.is_mention != nil } XCTAssertEqual(mentions.count, 1) } @@ -213,11 +191,10 @@ class ReplyTests: XCTestCase { let content = "@\(pk) hello there" let blocks = parse_post_blocks(content: content) - XCTAssertEqual(blocks.count, 3) - XCTAssertEqual(blocks[0].is_text, "") - XCTAssertEqual(blocks[1].is_ref, ReferencedId(ref_id: hex_pk, relay_id: nil, key: "p")) - XCTAssertEqual(blocks[2].is_text, " hello there") - + XCTAssertEqual(blocks.count, 2) + XCTAssertEqual(blocks[0].is_mention, .pubkey(hex_pk)) + XCTAssertEqual(blocks[1].is_text, " hello there") + } func testBech32MentionAtEnd() throws { @@ -226,11 +203,9 @@ class ReplyTests: XCTestCase { let content = "this is a @\(pk)" let blocks = parse_post_blocks(content: content) - XCTAssertEqual(blocks.count, 3) - XCTAssertEqual(blocks[1].is_ref, ReferencedId(ref_id: hex_pk, relay_id: nil, key: "p")) + XCTAssertEqual(blocks.count, 2) + XCTAssertEqual(blocks[1].is_mention, .pubkey(hex_pk)) XCTAssertEqual(blocks[0].is_text, "this is a ") - XCTAssertEqual(blocks[2].is_text, "") - } func testNpubMention() throws { @@ -245,27 +220,10 @@ class ReplyTests: XCTestCase { XCTAssertEqual(ev.tags.count, 2) XCTAssertEqual(blocks.count, 3) - XCTAssertEqual(blocks[1].is_ref, ReferencedId(ref_id: hex_pk, relay_id: nil, key: "p")) + XCTAssertEqual(blocks[1].is_mention, .pubkey(hex_pk)) XCTAssertEqual(ev.content, "this is a nostr:npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s mention") } - func testNoteMention() throws { - let evid = "0000000000000000000000000000000000000000000000000000000000000005" - let pk = "note154fwmp6hdxqnmqdzkt5jeay8l4kxdsrpn02vw9kp4gylkxxur5fsq3ckpy" - let hex_note_id = "a552ed875769813d81a2b2e92cf487fd6c66c0619bd4c716c1aa09fb18dc1d13" - let content = "this is a @\(pk) &\(pk) mention" - let reply_ref = ReferencedId(ref_id: evid, relay_id: nil, key: "e") - let blocks = parse_post_blocks(content: content) - let post = NostrPost(content: content, references: [reply_ref]) - let ev = post_to_event(post: post, privkey: evid, pubkey: pk) - - XCTAssertEqual(ev.tags.count, 1) - XCTAssertEqual(blocks.count, 5) - XCTAssertEqual(blocks[1].is_ref, ReferencedId(ref_id: hex_note_id, relay_id: nil, key: "e")) - XCTAssertEqual(blocks[3].is_ref, ReferencedId(ref_id: hex_note_id, relay_id: nil, key: "e")) - XCTAssertEqual(ev.content, "this is a nostr:\(pk) nostr:\(pk) mention") - } - func testNsecMention() throws { let evid = "0000000000000000000000000000000000000000000000000000000000000005" let pk = "nsec1jmzdz7d0ldqctdxwm5fzue277ttng2pk28n2u8wntc2r4a0w96ssnyukg7" @@ -278,49 +236,25 @@ class ReplyTests: XCTestCase { XCTAssertEqual(ev.tags.count, 2) XCTAssertEqual(blocks.count, 3) - XCTAssertEqual(blocks[1].is_ref, ReferencedId(ref_id: hex_pk, relay_id: nil, key: "p")) + XCTAssertEqual(blocks[1].is_mention, .pubkey(hex_pk)) XCTAssertEqual(ev.content, "this is a nostr:npub1enu46e5x2qtcmm72ttzsx6fmve5wkauftassz78l3mvluh8efqhqejf3v4 mention") } - func testPostWithMentions() throws { - let evid = "0000000000000000000000000000000000000000000000000000000000000005" - let pk = "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245" - let content = "this is a @\(pk) mention" - let reply_ref = ReferencedId(ref_id: evid, relay_id: nil, key: "e") - let post = NostrPost(content: content, references: [reply_ref]) - let ev = post_to_event(post: post, privkey: evid, pubkey: pk) - - XCTAssertEqual(ev.tags.count, 2) - XCTAssertEqual(ev.content, "this is a nostr:npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s mention") - } - - func testPostTags() throws { - let pk = "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245" - let content = "this is a @\(pk) mention" - let parsed = parse_post_blocks(content: content) - let post_tags = make_post_tags(post_blocks: parsed, tags: [], silent_mentions: false) - - XCTAssertEqual(post_tags.blocks.count, 3) - XCTAssertEqual(post_tags.tags.count, 1) - XCTAssertEqual(post_tags.tags[0].count, 2) - XCTAssertEqual(post_tags.tags[0][0], "p") - XCTAssertEqual(post_tags.tags[0][1], pk) - } - func testReplyMentions() throws { let privkey = "0fc2092231f958f8d57d66f5e238bb45b6a2571f44c0ce024bbc6f3a9c8a15fe" let pubkey = "30c6d1dc7f7c156794fa15055e651b758a61b99f50fcf759de59386050bf6ae2" - + let npub = "npub1xrrdrhrl0s2k0986z5z4uegmwk9xrwvl2r70wkw7tyuxq59ldt3qh09eay" + let refs = [ ReferencedId(ref_id: "thread_id", relay_id: nil, key: "e"), ReferencedId(ref_id: "reply_id", relay_id: nil, key: "e"), ReferencedId(ref_id: pubkey, relay_id: nil, key: "p"), ] - let post = NostrPost(content: "this is a (@\(pubkey)) mention", references: refs) + let post = NostrPost(content: "this is a (@\(npub)) mention", references: refs) let ev = post_to_event(post: post, privkey: privkey, pubkey: pubkey) - XCTAssertEqual(ev.content, "this is a (nostr:npub1xrrdrhrl0s2k0986z5z4uegmwk9xrwvl2r70wkw7tyuxq59ldt3qh09eay) mention") + XCTAssertEqual(ev.content, "this is a (nostr:\(npub)) mention") XCTAssertEqual(ev.tags[2][1], pubkey) } @@ -347,38 +281,6 @@ class ReplyTests: XCTestCase { XCTAssertEqual(txt, content) } - func testFunnyUriReference() throws { - let id = "6fec2ee6cfff779fe8560976b3d9df782b74577f0caefa7a77c0ed4c3749b5de" - let content = "this is a nostr:&\(id):\(id) event mention" - let parsed = parse_post_blocks(content: content) - - XCTAssertNotNil(parsed) - XCTAssertEqual(parsed.count, 3) - XCTAssertEqual(parsed[0].is_text, "this is a nostr:") - XCTAssertTrue(parsed[1].is_ref != nil) - XCTAssertEqual(parsed[2].is_text, ":\(id) event mention") - - guard case .ref(let ref) = parsed[1] else { - XCTAssertTrue(false) - return - } - XCTAssertEqual(ref.ref_id, id) - XCTAssertEqual(ref.key, "e") - XCTAssertNil(ref.relay_id) - - guard case .text(let t1) = parsed[0] else { - XCTAssertTrue(false) - return - } - XCTAssertEqual(t1, "this is a nostr:") - - guard case .text(let t2) = parsed[2] else { - XCTAssertTrue(false) - return - } - XCTAssertEqual(t2, ":\(id) event mention") - } - func testInvalidUriReference() throws { let id = "6fec2ee6cfff779fe8560976b3d9df782b74577f0caefa7a77c0ed4c3749b5de" let content = "this is a nostr:z:\(id) event mention" @@ -403,17 +305,9 @@ class ReplyTests: XCTestCase { XCTAssertNotNil(parsed) XCTAssertEqual(parsed.count, 3) XCTAssertEqual(parsed[0].is_text, "this is a ") - XCTAssertNotNil(parsed[1].is_ref) + XCTAssertEqual(parsed[1].is_mention, .pubkey(id)) XCTAssertEqual(parsed[2].is_text, " event mention") - guard case .ref(let ref) = parsed[1] else { - XCTAssertTrue(false) - return - } - XCTAssertEqual(ref.ref_id, id) - XCTAssertEqual(ref.key, "p") - XCTAssertNil(ref.relay_id) - guard case .text(let t1) = parsed[0] else { XCTAssertTrue(false) return @@ -435,17 +329,9 @@ class ReplyTests: XCTestCase { XCTAssertNotNil(parsed) XCTAssertEqual(parsed.count, 3) XCTAssertEqual(parsed[0].is_text, "this is a ") - XCTAssertNotNil(parsed[1].is_ref) + XCTAssertEqual(parsed[1].is_mention, .note(id)) XCTAssertEqual(parsed[2].is_text, " event mention") - - guard case .ref(let ref) = parsed[1] else { - XCTAssertTrue(false) - return - } - XCTAssertEqual(ref.ref_id, id) - XCTAssertEqual(ref.key, "e") - XCTAssertNil(ref.relay_id) - + guard case .text(let t1) = parsed[0] else { XCTAssertTrue(false) return @@ -459,68 +345,6 @@ class ReplyTests: XCTestCase { XCTAssertEqual(t2, " event mention") } - func testParsePostEventReference() throws { - let pk = "6fec2ee6cfff779fe8560976b3d9df782b74577f0caefa7a77c0ed4c3749b5de" - let parsed = parse_post_blocks(content: "this is a &\(pk) event mention") - - XCTAssertNotNil(parsed) - XCTAssertEqual(parsed.count, 3) - XCTAssertEqual(parsed[0].is_text, "this is a ") - XCTAssertNotNil(parsed[1].is_ref) - XCTAssertEqual(parsed[2].is_text, " event mention") - - guard case .ref(let ref) = parsed[1] else { - XCTAssertTrue(false) - return - } - XCTAssertEqual(ref.ref_id, pk) - XCTAssertEqual(ref.key, "e") - XCTAssertNil(ref.relay_id) - - guard case .text(let t1) = parsed[0] else { - XCTAssertTrue(false) - return - } - XCTAssertEqual(t1, "this is a ") - - guard case .text(let t2) = parsed[2] else { - XCTAssertTrue(false) - return - } - XCTAssertEqual(t2, " event mention") - } - - func testParsePostPubkeyReference() throws { - let pk = "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245" - let parsed = parse_post_blocks(content: "this is a @\(pk) mention") - - XCTAssertNotNil(parsed) - XCTAssertEqual(parsed.count, 3) - XCTAssertEqual(parsed[0].is_text, "this is a ") - XCTAssertNotNil(parsed[1].is_ref) - XCTAssertEqual(parsed[2].is_text, " mention") - - guard case .ref(let ref) = parsed[1] else { - XCTAssertTrue(false) - return - } - XCTAssertEqual(ref.ref_id, pk) - XCTAssertEqual(ref.key, "p") - XCTAssertNil(ref.relay_id) - - guard case .text(let t1) = parsed[0] else { - XCTAssertTrue(false) - return - } - XCTAssertEqual(t1, "this is a ") - - guard case .text(let t2) = parsed[2] else { - XCTAssertTrue(false) - return - } - XCTAssertEqual(t2, " mention") - } - func testParseInvalidMention() throws { let parsed = parse_mentions(content: "this is #[0] a mention", tags: []).blocks diff --git a/damusTests/damusTests.swift b/damusTests/damusTests.swift index 1e61898b..b0590bcc 100644 --- a/damusTests/damusTests.swift +++ b/damusTests/damusTests.swift @@ -74,8 +74,10 @@ class damusTests: XCTestCase { let parsed = parse_mentions(content: md, tags: []).blocks XCTAssertNotNil(parsed) - XCTAssertEqual(parsed.count, 1) + XCTAssertEqual(parsed.count, 3) XCTAssertNotNil(parsed[0].is_text) + XCTAssertNotNil(parsed[1].is_url) + XCTAssertNotNil(parsed[2].is_text) } func testParseUrlUpper() {