|
|
@ -53,6 +53,10 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible { |
|
|
|
return parse_mentions(content: self.content, tags: self.tags) |
|
|
|
}() |
|
|
|
|
|
|
|
lazy var event_refs: [EventRef] = { |
|
|
|
return interpret_event_refs(blocks: self.blocks, tags: self.tags) |
|
|
|
}() |
|
|
|
|
|
|
|
var description: String { |
|
|
|
let p = pow.map { String($0) } ?? "?" |
|
|
|
return "NostrEvent { id: \(id) pubkey \(pubkey) kind \(kind) tags \(tags) pow \(p) content '\(content)' }" |
|
|
@ -79,28 +83,12 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible { |
|
|
|
return true |
|
|
|
} |
|
|
|
|
|
|
|
/// find a non-root reply |
|
|
|
public func find_direct_reply() -> String? { |
|
|
|
var i = tags.count - 1 |
|
|
|
var first: String? = nil |
|
|
|
var matches: Int = 0 |
|
|
|
|
|
|
|
while i >= 0 { |
|
|
|
let tag = tags[i] |
|
|
|
if tag.count >= 2 && tag[0] == "e" { |
|
|
|
if first == nil { |
|
|
|
first = tag[1] |
|
|
|
} |
|
|
|
matches += 1 |
|
|
|
public func direct_replies() -> [ReferencedId] { |
|
|
|
return event_refs.reduce(into: []) { acc, evref in |
|
|
|
if let direct_reply = evref.is_direct_reply { |
|
|
|
acc.append(direct_reply) |
|
|
|
} |
|
|
|
i -= 1 |
|
|
|
} |
|
|
|
|
|
|
|
if matches <= 1 { |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
return first |
|
|
|
} |
|
|
|
|
|
|
|
public func last_refid() -> ReferencedId? { |
|
|
@ -120,29 +108,6 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible { |
|
|
|
return tag_to_refid(tags[last]) |
|
|
|
} |
|
|
|
|
|
|
|
public func directly_references(_ id: String) -> Bool { |
|
|
|
// conditions: if it only has 1 e ref |
|
|
|
// OR it has more than 1 e ref, ignoring the first |
|
|
|
|
|
|
|
var nrefs = 0 |
|
|
|
var i = 0 |
|
|
|
var first_matched = false |
|
|
|
var matched = true |
|
|
|
|
|
|
|
for tag in tags { |
|
|
|
if tag.count >= 2 && tag[0] == "e" { |
|
|
|
nrefs += 1 |
|
|
|
if tag[1] == id { |
|
|
|
matched = true |
|
|
|
first_matched = nrefs == 1 |
|
|
|
} |
|
|
|
} |
|
|
|
i += 1 |
|
|
|
} |
|
|
|
|
|
|
|
return (nrefs == 1 && matched) || (nrefs > 1 && matched && !first_matched) |
|
|
|
} |
|
|
|
|
|
|
|
public func references(id: String, key: String) -> Bool { |
|
|
|
for tag in tags { |
|
|
|
if tag.count >= 2 && tag[0] == key { |
|
|
@ -156,13 +121,7 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible { |
|
|
|
} |
|
|
|
|
|
|
|
public var is_reply: Bool { |
|
|
|
for tag in tags { |
|
|
|
if tag[0] == "e" { |
|
|
|
return true |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return false |
|
|
|
return event_is_reply(self) |
|
|
|
} |
|
|
|
|
|
|
|
public var referenced_ids: [ReferencedId] { |
|
|
@ -438,3 +397,146 @@ func gather_reply_ids(our_pubkey: String, from: NostrEvent) -> [ReferencedId] { |
|
|
|
return ids |
|
|
|
} |
|
|
|
|
|
|
|
enum EventRef { |
|
|
|
case mention(Mention) |
|
|
|
case thread_id(ReferencedId) |
|
|
|
case reply(ReferencedId) |
|
|
|
case reply_to_root(ReferencedId) |
|
|
|
|
|
|
|
var is_mention: Mention? { |
|
|
|
if case .mention(let m) = self { |
|
|
|
return m |
|
|
|
} |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
var is_direct_reply: ReferencedId? { |
|
|
|
switch self { |
|
|
|
case .mention: |
|
|
|
return nil |
|
|
|
case .thread_id: |
|
|
|
return nil |
|
|
|
case .reply(let refid): |
|
|
|
return refid |
|
|
|
case .reply_to_root(let refid): |
|
|
|
return refid |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var is_thread_id: ReferencedId? { |
|
|
|
switch self { |
|
|
|
case .mention(let mention): |
|
|
|
return nil |
|
|
|
case .thread_id(let referencedId): |
|
|
|
return referencedId |
|
|
|
case .reply(let referencedId): |
|
|
|
return nil |
|
|
|
case .reply_to_root(let referencedId): |
|
|
|
return referencedId |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var is_reply: ReferencedId? { |
|
|
|
switch self { |
|
|
|
case .mention: |
|
|
|
return nil |
|
|
|
case .thread_id: |
|
|
|
return nil |
|
|
|
case .reply(let refid): |
|
|
|
return refid |
|
|
|
case .reply_to_root(let refid): |
|
|
|
return refid |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func has_any_e_refs(_ tags: [[String]]) -> Bool { |
|
|
|
for tag in tags { |
|
|
|
if tag.count >= 2 && tag[0] == "e" { |
|
|
|
return true |
|
|
|
} |
|
|
|
} |
|
|
|
return false |
|
|
|
} |
|
|
|
|
|
|
|
func build_mention_indices(_ blocks: [Block], type: MentionType) -> Set<Int> { |
|
|
|
return blocks.reduce(into: []) { acc, block in |
|
|
|
switch block { |
|
|
|
case .mention(let m): |
|
|
|
if m.type == type { |
|
|
|
acc.insert(m.index) |
|
|
|
} |
|
|
|
case .text: |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func interp_event_refs_without_mentions(_ refs: [ReferencedId]) -> [EventRef] { |
|
|
|
if refs.count == 0 { |
|
|
|
return [] |
|
|
|
} |
|
|
|
|
|
|
|
if refs.count == 1 { |
|
|
|
return [.reply_to_root(refs[0])] |
|
|
|
} |
|
|
|
|
|
|
|
var evrefs: [EventRef] = [] |
|
|
|
var first: Bool = true |
|
|
|
for ref in refs { |
|
|
|
if first { |
|
|
|
evrefs.append(.thread_id(ref)) |
|
|
|
first = false |
|
|
|
} else { |
|
|
|
evrefs.append(.reply(ref)) |
|
|
|
} |
|
|
|
} |
|
|
|
return evrefs |
|
|
|
} |
|
|
|
|
|
|
|
func interp_event_refs_with_mentions(tags: [[String]], mention_indices: Set<Int>) -> [EventRef] { |
|
|
|
var mentions: [EventRef] = [] |
|
|
|
var ev_refs: [ReferencedId] = [] |
|
|
|
var i: Int = 0 |
|
|
|
|
|
|
|
for tag in tags { |
|
|
|
if tag.count >= 2 && tag[0] == "e" { |
|
|
|
let ref = tag_to_refid(tag)! |
|
|
|
if mention_indices.contains(i) { |
|
|
|
let mention = Mention(index: i, type: .event, ref: ref) |
|
|
|
mentions.append(.mention(mention)) |
|
|
|
} else { |
|
|
|
ev_refs.append(ref) |
|
|
|
} |
|
|
|
} |
|
|
|
i += 1 |
|
|
|
} |
|
|
|
|
|
|
|
var replies = interp_event_refs_without_mentions(ev_refs) |
|
|
|
replies.append(contentsOf: mentions) |
|
|
|
return replies |
|
|
|
} |
|
|
|
|
|
|
|
func interpret_event_refs(blocks: [Block], tags: [[String]]) -> [EventRef] { |
|
|
|
if tags.count == 0 { |
|
|
|
return [] |
|
|
|
} |
|
|
|
|
|
|
|
/// build a set of indices for each event mention |
|
|
|
let mention_indices = build_mention_indices(blocks, type: .event) |
|
|
|
|
|
|
|
/// simpler case with no mentions |
|
|
|
if mention_indices.count == 0 { |
|
|
|
let ev_refs = get_referenced_ids(tags: tags, key: "e") |
|
|
|
return interp_event_refs_without_mentions(ev_refs) |
|
|
|
} |
|
|
|
|
|
|
|
return interp_event_refs_with_mentions(tags: tags, mention_indices: mention_indices) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func event_is_reply(_ ev: NostrEvent) -> Bool { |
|
|
|
return ev.event_refs.contains { evref in |
|
|
|
return evref.is_reply != nil |
|
|
|
} |
|
|
|
} |
|
|
|