LNUrls.swift (2374B)
1 // 2 // LNUrls.swift 3 // damus 4 // 5 // Created by William Casarin on 2023-01-17. 6 // 7 8 import Foundation 9 10 enum LNUrlState { 11 case not_fetched 12 case fetching(Task<LNUrlPayRequest?, Never>) 13 case fetched(LNUrlPayRequest) 14 case failed(tries: Int) 15 } 16 17 class LNUrls { 18 var endpoints: [Pubkey: LNUrlState] 19 20 init() { 21 self.endpoints = [:] 22 } 23 24 @MainActor 25 func lookup_or_fetch(pubkey: Pubkey, lnurl: String) async -> LNUrlPayRequest? { 26 switch lookup(pubkey: pubkey) { 27 case .failed(let tries): 28 print("lnurls.lookup_or_fetch failed \(tries) \(lnurl)") 29 guard tries < 5 else { return nil } 30 self.endpoints[pubkey] = .failed(tries: tries + 1) 31 case .fetched(let pr): 32 //print("lnurls.lookup_or_fetch fetched \(lnurl)") 33 return pr 34 case .fetching(let task): 35 //print("lnurls.lookup_or_fetch already fetching \(lnurl)") 36 return await task.value 37 case .not_fetched: 38 print("lnurls.lookup_or_fetch not fetched \(lnurl)") 39 break 40 } 41 42 let task = Task { 43 let v = await fetch_static_payreq(lnurl) 44 return v 45 } 46 47 self.endpoints[pubkey] = .fetching(task) 48 49 let v = await task.value 50 51 if let v { 52 self.endpoints[pubkey] = .fetched(v) 53 } else { 54 self.endpoints[pubkey] = .failed(tries: 1) 55 } 56 57 return v 58 } 59 60 func lookup(pubkey: Pubkey) -> LNUrlState { 61 return self.endpoints[pubkey] ?? .not_fetched 62 } 63 } 64 65 func fetch_static_payreq(_ lnurl: String) async -> LNUrlPayRequest? { 66 print("fetching static payreq \(lnurl)") 67 68 guard let url = decode_lnurl(lnurl) else { 69 return nil 70 } 71 72 guard let ret = try? await URLSession.shared.data(from: url) else { 73 return nil 74 } 75 76 let json_str = String(decoding: ret.0, as: UTF8.self) 77 78 guard let endpoint: LNUrlPayRequest = decode_json(json_str) else { 79 return nil 80 } 81 82 return endpoint 83 } 84 85 func decode_lnurl(_ lnurl: String) -> URL? { 86 guard let decoded = try? bech32_decode(lnurl) else { 87 return nil 88 } 89 guard decoded.hrp == "lnurl" else { 90 return nil 91 } 92 guard let url = URL(string: String(decoding: decoded.data, as: UTF8.self)) else { 93 return nil 94 } 95 return url 96 }