Nostr.swift (9371B)
1 // 2 // Nostr.swift 3 // damus 4 // 5 // Created by William Casarin on 2022-04-07. 6 // 7 8 import Foundation 9 10 typealias Profile = NdbProfile 11 typealias ProfileKey = UInt64 12 //typealias ProfileRecord = NdbProfileRecord 13 14 class ProfileRecord { 15 let data: NdbProfileRecord 16 17 init(data: NdbProfileRecord, key: ProfileKey) { 18 self.data = data 19 self.profileKey = key 20 } 21 22 let profileKey: ProfileKey 23 var profile: Profile? { return data.profile } 24 var receivedAt: UInt64 { data.receivedAt } 25 var noteKey: UInt64 { data.noteKey } 26 27 private var _lnurl: String? = nil 28 var lnurl: String? { 29 if let _lnurl { 30 return _lnurl 31 } 32 33 guard let profile = data.profile, 34 let addr = profile.lud16 ?? profile.lud06 else { 35 return nil; 36 } 37 38 if addr.contains("@") { 39 // this is a heavy op and is used a lot in views, cache it! 40 let addr = lnaddress_to_lnurl(addr); 41 self._lnurl = addr 42 return addr 43 } 44 45 if !addr.lowercased().hasPrefix("lnurl") { 46 return nil 47 } 48 49 return addr; 50 } 51 52 } 53 54 extension NdbProfile { 55 var display_name: String? { 56 return displayName 57 } 58 59 static func displayName(profile: Profile?, pubkey: Pubkey) -> DisplayName { 60 return parse_display_name(profile: profile, pubkey: pubkey) 61 } 62 63 var damus_donation: Int? { 64 return Int(damusDonation) 65 } 66 67 var damus_donation_v2: Int { 68 return Int(damusDonationV2) 69 } 70 71 var website_url: URL? { 72 if self.website?.trimmingCharacters(in: .whitespacesAndNewlines) == "" { 73 return nil 74 } 75 return self.website.flatMap { url in 76 let trim = url.trimmingCharacters(in: .whitespacesAndNewlines) 77 if !(trim.hasPrefix("http://") || trim.hasPrefix("https://")) { 78 return URL(string: "https://" + trim) 79 } 80 return URL(string: trim) 81 } 82 } 83 84 init(name: String? = nil, display_name: String? = nil, about: String? = nil, picture: String? = nil, banner: String? = nil, website: String? = nil, lud06: String? = nil, lud16: String? = nil, nip05: String? = nil, damus_donation: Int? = nil, reactions: Bool = true) { 85 86 var fbb = FlatBufferBuilder() 87 88 let name_off = fbb.create(string: name) 89 let display_name_off = fbb.create(string: display_name) 90 let about_off = fbb.create(string: about) 91 let picture_off = fbb.create(string: picture) 92 let banner_off = fbb.create(string: banner) 93 let website_off = fbb.create(string: website) 94 let lud06_off = fbb.create(string: lud06) 95 let lud16_off = fbb.create(string: lud16) 96 let nip05_off = fbb.create(string: nip05) 97 98 let profile_data = NdbProfile.createNdbProfile(&fbb, 99 nameOffset: name_off, 100 websiteOffset: website_off, 101 aboutOffset: about_off, 102 lud16Offset: lud16_off, 103 bannerOffset: banner_off, 104 displayNameOffset: display_name_off, 105 reactions: reactions, 106 pictureOffset: picture_off, 107 nip05Offset: nip05_off, 108 damusDonation: 0, 109 damusDonationV2: damus_donation.map({ Int32($0) }) ?? 0, 110 lud06Offset: lud06_off) 111 112 fbb.finish(offset: profile_data) 113 114 var buf = ByteBuffer(bytes: fbb.sizedByteArray) 115 let profile: Profile = try! getCheckedRoot(byteBuffer: &buf) 116 self = profile 117 } 118 } 119 120 /* 121 class Profile: Codable { 122 var value: [String: AnyCodable] 123 124 init(name: String? = nil, display_name: String? = nil, about: String? = nil, picture: String? = nil, banner: String? = nil, website: String? = nil, lud06: String? = nil, lud16: String? = nil, nip05: String? = nil, damus_donation: Int? = nil) { 125 self.value = [:] 126 self.name = name 127 self.display_name = display_name 128 self.about = about 129 self.picture = picture 130 self.banner = banner 131 self.website = website 132 self.lud06 = lud06 133 self.lud16 = lud16 134 self.nip05 = nip05 135 self.damus_donation = damus_donation 136 } 137 138 private func str(_ str: String) -> String? { 139 return get_val(str) 140 } 141 142 private func int(_ key: String) -> Int? { 143 return get_val(key) 144 } 145 146 private func get_val<T>(_ v: String) -> T? { 147 guard let val = self.value[v] else{ 148 return nil 149 } 150 151 guard let s = val.value as? T else { 152 return nil 153 } 154 155 return s 156 } 157 158 private func set_val<T>(_ key: String, _ val: T?) { 159 if val == nil { 160 self.value.removeValue(forKey: key) 161 return 162 } 163 164 self.value[key] = AnyCodable.init(val) 165 } 166 167 private func set_str(_ key: String, _ val: String?) { 168 set_val(key, val) 169 } 170 171 private func set_int(_ key: String, _ val: Int?) { 172 set_val(key, val) 173 } 174 175 var reactions: Bool? { 176 get { return get_val("reactions"); } 177 set(s) { set_val("reactions", s) } 178 } 179 180 var display_name: String? { 181 get { return str("display_name"); } 182 set(s) { set_str("display_name", s) } 183 } 184 185 var name: String? { 186 get { return str("name"); } 187 set(s) { set_str("name", s) } 188 } 189 190 var about: String? { 191 get { return str("about"); } 192 set(s) { set_str("about", s) } 193 } 194 195 var damus_donation: Int? { 196 get { return int("damus_donation_v2"); } 197 set(s) { set_int("damus_donation_v2", s) } 198 } 199 200 var picture: String? { 201 get { return str("picture"); } 202 set(s) { set_str("picture", s) } 203 } 204 205 var banner: String? { 206 get { return str("banner"); } 207 set(s) { set_str("banner", s) } 208 } 209 210 var website: String? { 211 get { return str("website"); } 212 set(s) { set_str("website", s) } 213 } 214 215 var lud06: String? { 216 get { return str("lud06"); } 217 set(s) { set_str("lud06", s) } 218 } 219 220 var lud16: String? { 221 get { return str("lud16"); } 222 set(s) { set_str("lud16", s) } 223 } 224 225 var website_url: URL? { 226 if self.website?.trimmingCharacters(in: .whitespacesAndNewlines) == "" { 227 return nil 228 } 229 return self.website.flatMap { url in 230 let trim = url.trimmingCharacters(in: .whitespacesAndNewlines) 231 if !(trim.hasPrefix("http://") || trim.hasPrefix("https://")) { 232 return URL(string: "https://" + trim) 233 } 234 return URL(string: trim) 235 } 236 } 237 238 func cache_lnurl() { 239 guard self._lnurl == nil else { 240 return 241 } 242 243 guard let addr = lud16 ?? lud06 else { 244 return 245 } 246 247 self._lnurl = lnaddress_to_lnurl(addr) 248 } 249 250 private var _lnurl: String? = nil 251 var lnurl: String? { 252 if let _lnurl { 253 return _lnurl 254 } 255 256 guard let addr = lud16 ?? lud06 else { 257 return nil; 258 } 259 260 if addr.contains("@") { 261 // this is a heavy op and is used a lot in views, cache it! 262 let addr = lnaddress_to_lnurl(addr); 263 self._lnurl = addr 264 return addr 265 } 266 267 if !addr.lowercased().hasPrefix("lnurl") { 268 return nil 269 } 270 271 return addr; 272 } 273 274 var nip05: String? { 275 get { return str("nip05"); } 276 set(s) { set_str("nip05", s) } 277 } 278 279 var lightning_uri: URL? { 280 return make_ln_url(self.lnurl) 281 } 282 283 init() { 284 self.value = [:] 285 } 286 287 required init(from decoder: Decoder) throws { 288 let container = try decoder.singleValueContainer() 289 self.value = try container.decode([String: AnyCodable].self) 290 } 291 292 func encode(to encoder: Encoder) throws { 293 var container = encoder.singleValueContainer() 294 try container.encode(value) 295 } 296 297 static func displayName(profile: Profile?, pubkey: Pubkey) -> DisplayName { 298 return parse_display_name(profile: profile, pubkey: pubkey) 299 } 300 } 301 */ 302 303 func make_test_profile() -> Profile { 304 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) 305 } 306 307 func make_ln_url(_ str: String?) -> URL? { 308 return str.flatMap { URL(string: "lightning:" + $0) } 309 } 310 311 func lnaddress_to_lnurl(_ lnaddr: String) -> String? { 312 let parts = lnaddr.split(separator: "@") 313 guard parts.count == 2 else { 314 return nil 315 } 316 317 let url = "https://\(parts[1])/.well-known/lnurlp/\(parts[0])"; 318 guard let dat = url.data(using: .utf8) else { 319 return nil 320 } 321 322 return bech32_encode(hrp: "lnurl", Array(dat)) 323 } 324