Merge branch 'master' into user-cache

This commit is contained in:
Bryan Montz
2023-05-26 06:46:47 -05:00
85 changed files with 2379 additions and 299 deletions

View File

@@ -10,7 +10,7 @@ import Foundation
class Profile: Codable {
var value: [String: AnyCodable]
init (name: String?, display_name: String?, about: String?, picture: String?, banner: String?, website: String?, lud06: String?, lud16: String?, nip05: String?) {
init (name: String?, display_name: String?, about: String?, picture: String?, banner: String?, website: String?, lud06: String?, lud16: String?, nip05: String?, damus_donation: Int?) {
self.value = [:]
self.name = name
self.display_name = display_name
@@ -21,6 +21,7 @@ class Profile: Codable {
self.lud06 = lud06
self.lud16 = lud16
self.nip05 = nip05
self.damus_donation = damus_donation
}
convenience init(persisted_profile: PersistedProfile) {
@@ -39,6 +40,10 @@ class Profile: Codable {
return get_val(str)
}
private func int(_ key: String) -> Int? {
return get_val(key)
}
private func get_val<T>(_ v: String) -> T? {
guard let val = self.value[v] else{
return nil
@@ -64,6 +69,10 @@ class Profile: Codable {
set_val(key, val)
}
private func set_int(_ key: String, _ val: Int?) {
set_val(key, val)
}
var reactions: Bool? {
get { return get_val("reactions"); }
set(s) { set_val("reactions", s) }
@@ -89,6 +98,11 @@ class Profile: Codable {
set(s) { set_str("about", s) }
}
var damus_donation: Int? {
get { return int("damus_donation"); }
set(s) { set_int("damus_donation", s) }
}
var picture: String? {
get { return str("picture"); }
set(s) { set_str("picture", s) }
@@ -192,7 +206,7 @@ class Profile: Codable {
}
func make_test_profile() -> Profile {
return Profile(name: "jb55", display_name: "Will", about: "Its a me", picture: "https://cdn.jb55.com/img/red-me.jpg", banner: "https://pbs.twimg.com/profile_banners/9918032/1531711830/600x200", website: "jb55.com", lud06: "jb55@jb55.com", lud16: nil, nip05: "jb55@jb55.com")
return Profile(name: "jb55", display_name: "Will", about: "Its a me", picture: "https://cdn.jb55.com/img/red-me.jpg", banner: "https://pbs.twimg.com/profile_banners/9918032/1531711830/600x200", website: "jb55.com", lud06: "jb55@jb55.com", lud16: nil, nip05: "jb55@jb55.com", damus_donation: 1)
}
func make_ln_url(_ str: String?) -> URL? {

View File

@@ -492,11 +492,11 @@ func make_boost_event(pubkey: String, privkey: String, boosted: NostrEvent) -> N
return ev
}
func make_like_event(pubkey: String, privkey: String, liked: NostrEvent) -> NostrEvent {
func make_like_event(pubkey: String, privkey: String, liked: NostrEvent, content: String = "🤙") -> NostrEvent {
var tags: [[String]] = liked.tags.filter { tag in tag.count >= 2 && (tag[0] == "e" || tag[0] == "p") }
tags.append(["e", liked.id])
tags.append(["p", liked.pubkey])
let ev = NostrEvent(content: "🤙", pubkey: pubkey, kind: 7, tags: tags)
let ev = NostrEvent(content: content, pubkey: pubkey, kind: 7, tags: tags)
ev.calculate_id()
ev.sign(privkey: privkey)
@@ -512,7 +512,12 @@ func zap_target_to_tags(_ target: ZapTarget) -> [[String]] {
}
}
func make_private_zap_request_event(identity: FullKeypair, enc_key: FullKeypair, target: ZapTarget, message: String) -> String? {
struct PrivateZapRequest {
let req: ZapRequest
let enc: String
}
func make_private_zap_request_event(identity: FullKeypair, enc_key: FullKeypair, target: ZapTarget, message: String) -> PrivateZapRequest? {
// target tags must be the same as zap request target tags
let tags = zap_target_to_tags(target)
@@ -520,10 +525,13 @@ func make_private_zap_request_event(identity: FullKeypair, enc_key: FullKeypair,
note.id = calculate_event_id(ev: note)
note.sig = sign_event(privkey: identity.privkey, ev: note)
guard let note_json = encode_json(note) else {
guard let note_json = encode_json(note),
let enc = encrypt_message(message: note_json, privkey: enc_key.privkey, to_pk: target.pubkey, encoding: .bech32)
else {
return nil
}
return encrypt_message(message: note_json, privkey: enc_key.privkey, to_pk: target.pubkey, encoding: .bech32)
return PrivateZapRequest(req: ZapRequest(ev: note), enc: enc)
}
func decrypt_private_zap(our_privkey: String, zapreq: NostrEvent, target: ZapTarget) -> NostrEvent? {
@@ -587,7 +595,30 @@ func generate_private_keypair(our_privkey: String, id: String, created_at: Int64
return FullKeypair(pubkey: pubkey, privkey: privkey)
}
func make_zap_request_event(keypair: FullKeypair, content: String, relays: [RelayDescriptor], target: ZapTarget, zap_type: ZapType) -> NostrEvent? {
enum MakeZapRequest {
case priv(ZapRequest, PrivateZapRequest)
case normal(ZapRequest)
var private_inner_request: ZapRequest {
switch self {
case .priv(_, let pzr):
return pzr.req
case .normal(let zr):
return zr
}
}
var potentially_anon_outer_request: ZapRequest {
switch self {
case .priv(let zr, _):
return zr
case .normal(let zr):
return zr
}
}
}
func make_zap_request_event(keypair: FullKeypair, content: String, relays: [RelayDescriptor], target: ZapTarget, zap_type: ZapType) -> MakeZapRequest? {
var tags = zap_target_to_tags(target)
var relay_tag = ["relays"]
relay_tag.append(contentsOf: relays.map { $0.url.id })
@@ -597,6 +628,8 @@ func make_zap_request_event(keypair: FullKeypair, content: String, relays: [Rela
let now = Int64(Date().timeIntervalSince1970)
var privzap_req: PrivateZapRequest?
var message = content
switch zap_type {
case .pub:
@@ -614,14 +647,20 @@ func make_zap_request_event(keypair: FullKeypair, content: String, relays: [Rela
guard let privreq = make_private_zap_request_event(identity: keypair, enc_key: kp, target: target, message: message) else {
return nil
}
tags.append(["anon", privreq])
tags.append(["anon", privreq.enc])
message = ""
privzap_req = privreq
}
let ev = NostrEvent(content: message, pubkey: kp.pubkey, kind: 9734, tags: tags, createdAt: now)
ev.id = calculate_event_id(ev: ev)
ev.sig = sign_event(privkey: kp.privkey, ev: ev)
return ev
let zapreq = ZapRequest(ev: ev)
if let privzap_req {
return .priv(zapreq, privzap_req)
} else {
return .normal(zapreq)
}
}
func uniq<T: Hashable>(_ xs: [T]) -> [T] {
@@ -927,6 +966,28 @@ func first_eref_mention(ev: NostrEvent, privkey: String?) -> Mention? {
return nil
}
/**
Transforms a `NostrEvent` of known kind `NostrKind.like`to a human-readable emoji.
If the known kind is not a `NostrKind.like`, it will return `nil`.
If the event content is an empty string or `+`, it will map that to a heart emoji.
If the event content is a "-", it will map that to a dislike 👎 emoji.
Otherwise, it will return the event content at face value without transforming it.
*/
func to_reaction_emoji(ev: NostrEvent) -> String? {
guard ev.known_kind == NostrKind.like else {
return nil
}
switch ev.content {
case "", "+":
return "❤️"
case "-":
return "👎"
default:
return ev.content
}
}
extension [ReferencedId] {
var pRefs: [ReferencedId] {
get {

View File

@@ -22,4 +22,6 @@ enum NostrKind: Int {
case list = 30000
case zap = 9735
case zap_request = 9734
case nwc_request = 23194
case nwc_response = 23195
}

View File

@@ -17,7 +17,7 @@ class Profiles {
qos: .userInteractive,
attributes: .concurrent)
var profiles: [String: TimestampedProfile] = [:]
private var profiles: [String: TimestampedProfile] = [:]
var validated: [String: NIP05] = [:]
var nip05_pubkey: [String: String] = [:]
var zappers: [String: String] = [:]
@@ -28,6 +28,12 @@ class Profiles {
validated[pk]
}
func enumerated() -> EnumeratedSequence<[String: TimestampedProfile]> {
return queue.sync {
return profiles.enumerated()
}
}
func lookup_zapper(pubkey: String) -> String? {
zappers[pubkey]
}
@@ -77,3 +83,9 @@ class Profiles {
return Date.now.timeIntervalSince(pull_date) < Profiles.db_freshness_threshold
}
}
func invalidate_zapper_cache(pubkey: String, profiles: Profiles, lnurl: LNUrls) {
profiles.zappers.removeValue(forKey: pubkey)
lnurl.endpoints.removeValue(forKey: pubkey)
}

View File

@@ -10,21 +10,46 @@ import Foundation
public struct RelayInfo: Codable {
let read: Bool?
let write: Bool?
let ephemeral: Bool?
init(read: Bool, write: Bool, ephemeral: Bool = false) {
init(read: Bool, write: Bool) {
self.read = read
self.write = write
self.ephemeral = ephemeral
}
static let rw = RelayInfo(read: true, write: true, ephemeral: false)
static let ephemeral = RelayInfo(read: true, write: true, ephemeral: true)
static let rw = RelayInfo(read: true, write: true)
}
enum RelayVariant {
case regular
case ephemeral
case nwc
}
public struct RelayDescriptor {
public let url: RelayURL
public let info: RelayInfo
let url: RelayURL
let info: RelayInfo
let variant: RelayVariant
init(url: RelayURL, info: RelayInfo, variant: RelayVariant = .regular) {
self.url = url
self.info = info
self.variant = variant
}
var ephemeral: Bool {
switch variant {
case .regular:
return false
case .ephemeral:
return true
case .nwc:
return true
}
}
static func nwc(url: RelayURL) -> RelayDescriptor {
return RelayDescriptor(url: url, info: .rw, variant: .nwc)
}
}
enum RelayFlags: Int {

View File

@@ -43,7 +43,7 @@ class RelayPool {
}
var our_descriptors: [RelayDescriptor] {
return all_descriptors.filter { d in !(d.info.ephemeral ?? false) }
return all_descriptors.filter { d in !d.ephemeral }
}
var all_descriptors: [RelayDescriptor] {
@@ -91,7 +91,8 @@ class RelayPool {
}
}
func add_relay(_ url: RelayURL, info: RelayInfo) throws {
func add_relay(_ desc: RelayDescriptor) throws {
let url = desc.url
let relay_id = get_relay_id(url)
if get_relay(relay_id) != nil {
throw RelayError.RelayAlreadyExists
@@ -99,8 +100,7 @@ class RelayPool {
let conn = RelayConnection(url: url) { event in
self.handle_event(relay_id: relay_id, event: event)
}
let descriptor = RelayDescriptor(url: url, info: info)
let relay = Relay(descriptor: descriptor, connection: conn)
let relay = Relay(descriptor: desc, connection: conn)
self.relays.append(relay)
}
@@ -196,7 +196,7 @@ class RelayPool {
continue
}
if (relay.descriptor.info.ephemeral ?? false) && skip_ephemeral {
if relay.descriptor.ephemeral && skip_ephemeral {
continue
}
@@ -266,7 +266,7 @@ func add_rw_relay(_ pool: RelayPool, _ url: String) {
guard let url = RelayURL(url) else {
return
}
try? pool.add_relay(url, info: RelayInfo.rw)
try? pool.add_relay(RelayDescriptor(url: url, info: .rw))
}