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