Skip to content

Commit be1290c

Browse files
committed
feat: add purplepag.es as profile-only relay
Add wss://purplepag.es as a system relay that's always included for all users but only used for profile metadata queries (kinds 0, 3, 10002). - Add `profilesOnly` variant to RelayVariant enum - Add `isProfileRelated` check to NostrRequestType to detect profile queries - Filter profiles-only relays in send_raw to only receive profile requests - Always include purplepag.es in relay list via UserRelayListManager Closes: #1331 Changelog-Added: Added purplepag.es as profile-only relay for improved profile metadata fetching Signed-off-by: alltheseas
1 parent 5058fb3 commit be1290c

File tree

4 files changed

+64
-15
lines changed

4 files changed

+64
-15
lines changed

damus/Core/Networking/NostrNetworkManager/UserRelayListManager.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,17 @@ extension NostrNetworkManager {
3535
}
3636

3737
private func computeRelaysToConnectTo(with relayList: NIP65.RelayList) -> [RelayPool.RelayDescriptor] {
38-
let regularRelayDescriptorList = relayList.toRelayDescriptors()
38+
var descriptors = relayList.toRelayDescriptors()
39+
40+
// Always add profiles-only relays for profile metadata
41+
if let purplePagesURL = RelayURL("wss://purplepag.es") {
42+
descriptors.append(.profilesOnly(url: purplePagesURL))
43+
}
44+
3945
if let nwcWallet = delegate.nwcWallet {
40-
return regularRelayDescriptorList + [.nwc(url: nwcWallet.relay)]
46+
descriptors.append(.nwc(url: nwcWallet.relay))
4147
}
42-
return regularRelayDescriptorList
48+
return descriptors
4349
}
4450

4551
// MARK: - Getting the user's relay list

damus/Core/Nostr/NostrRequest.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,28 @@ enum NostrRequestType {
3333
guard case .typical(let req) = self else {
3434
return true
3535
}
36-
36+
3737
return req.is_read
3838
}
3939
}
4040

41+
extension NostrRequestType {
42+
/// Profile-related kinds that should be queried on profiles-only relays
43+
static let profileKinds: Set<NostrKind> = [.metadata, .contacts, .relay_list]
44+
45+
/// Whether this request is for profile-related data only
46+
var isProfileRelated: Bool {
47+
guard case .typical(let req) = self else { return false }
48+
guard case .subscribe(let sub) = req else { return false }
49+
50+
// Check if ALL filters contain ONLY profile-related kinds
51+
return sub.filters.allSatisfy { filter in
52+
guard let kinds = filter.kinds else { return false } // No kinds specified = could be anything
53+
return kinds.allSatisfy { Self.profileKinds.contains($0) }
54+
}
55+
}
56+
}
57+
4158
/// Models a standard request/message that is sent to a Nostr relay.
4259
enum NostrRequest {
4360
/// Subscribes to receive information from the relay

damus/Core/Nostr/Relay.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ enum RelayVariant {
3232
case regular
3333
case ephemeral
3434
case nwc
35+
case profilesOnly // Only used for profile metadata queries (kind 0, 3, 10002)
3536
}
3637

3738
extension RelayPool {
@@ -55,12 +56,22 @@ extension RelayPool {
5556
return true
5657
case .nwc:
5758
return true
59+
case .profilesOnly:
60+
return false // Not ephemeral - filtering is handled separately via isProfileRelated check
5861
}
5962
}
60-
63+
64+
var isProfilesOnly: Bool {
65+
variant == .profilesOnly
66+
}
67+
6168
static func nwc(url: RelayURL) -> RelayDescriptor {
6269
return RelayDescriptor(url: url, info: .readWrite, variant: .nwc)
6370
}
71+
72+
static func profilesOnly(url: RelayURL) -> RelayDescriptor {
73+
return RelayDescriptor(url: url, info: .read, variant: .profilesOnly)
74+
}
6475
}
6576
}
6677

damus/Core/Nostr/RelayPool.swift

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,19 @@ actor RelayPool {
425425
if relay.descriptor.ephemeral && skip_ephemeral {
426426
continue // Do not send requests to ephemeral relays if we want to skip them
427427
}
428-
428+
429+
if relay.descriptor.isProfilesOnly && !req.isProfileRelated {
430+
continue // Only send profile-related requests to profiles-only relays
431+
}
432+
433+
// TEMP DEBUG: Log what's being sent to purplepag.es
434+
if relay.descriptor.isProfilesOnly,
435+
case .typical(let nostrReq) = req,
436+
case .subscribe(let sub) = nostrReq {
437+
let kinds = sub.filters.compactMap { $0.kinds }.flatMap { $0 }.map { $0.rawValue }
438+
print("🟣 PURPLEPAGES: sending subscription with kinds: \(kinds)")
439+
}
440+
429441
guard relay.connection.isConnected else {
430442
Task { await queue_req(r: req, relay: relay.id, skip_ephemeral: skip_ephemeral) }
431443
continue
@@ -465,16 +477,19 @@ actor RelayPool {
465477
}
466478

467479
func record_seen(relay_id: RelayURL, event: NostrConnectionEvent) {
468-
if case .nostr_event(let ev) = event {
469-
if case .event(_, let nev) = ev {
470-
if seen[nev.id]?.contains(relay_id) == true {
471-
return
472-
}
473-
seen[nev.id, default: Set()].insert(relay_id)
474-
counts[relay_id, default: 0] += 1
475-
notify(.update_stats(note_id: nev.id))
476-
}
480+
guard case .nostr_event(let ev) = event else { return }
481+
guard case .event(_, let nev) = ev else { return }
482+
483+
// TEMP DEBUG: Log events received from purplepag.es
484+
if relay_id.absoluteString.contains("purplepag") {
485+
print("🟣 PURPLEPAGES: received kind \(nev.kind) event")
477486
}
487+
488+
guard seen[nev.id]?.contains(relay_id) != true else { return }
489+
490+
seen[nev.id, default: Set()].insert(relay_id)
491+
counts[relay_id, default: 0] += 1
492+
notify(.update_stats(note_id: nev.id))
478493
}
479494

480495
func resubscribeAll(relayId: RelayURL) async {

0 commit comments

Comments
 (0)