client.go raw
1 // Package directory_client provides a client library for the Distributed
2 // Directory Consensus Protocol (NIP-XX).
3 //
4 // This package offers a high-level API for working with directory events,
5 // managing identity resolution, tracking key delegations, and computing
6 // trust scores. It builds on the lower-level directory protocol package.
7 //
8 // # Basic Usage
9 //
10 // // Create an identity resolver
11 // resolver := directory_client.NewIdentityResolver()
12 //
13 // // Parse and track events
14 // event := getDirectoryEvent()
15 // resolver.ProcessEvent(event)
16 //
17 // // Resolve identity behind a delegate key
18 // actualIdentity := resolver.ResolveIdentity(delegateKey)
19 //
20 // // Check if a key is a delegate
21 // isDelegate := resolver.IsDelegateKey(pubkey)
22 //
23 // # Trust Management
24 //
25 // // Create a trust calculator
26 // calculator := directory_client.NewTrustCalculator()
27 //
28 // // Add trust acts
29 // trustAct := directory.ParseTrustAct(event)
30 // calculator.AddAct(trustAct)
31 //
32 // // Calculate aggregate trust score
33 // score := calculator.CalculateTrust(targetPubkey)
34 //
35 // # Replication Filtering
36 //
37 // // Create a replication filter
38 // filter := directory_client.NewReplicationFilter(50) // min trust score of 50
39 //
40 // // Add trust acts to influence replication decisions
41 // filter.AddTrustAct(trustAct)
42 //
43 // // Check if should replicate from a relay
44 // if filter.ShouldReplicate(relayPubkey) {
45 // // Proceed with replication
46 // }
47 //
48 // # Event Filtering
49 //
50 // // Filter directory events from a stream
51 // events := getEvents()
52 // directoryEvents := directory_client.FilterDirectoryEvents(events)
53 //
54 // // Check if an event is a directory event
55 // if directory_client.IsDirectoryEvent(event) {
56 // // Handle directory event
57 // }
58 package directory_client
59
60 import (
61 "next.orly.dev/pkg/lol/errorf"
62 "next.orly.dev/pkg/nostr/encoders/event"
63 "next.orly.dev/pkg/protocol/directory"
64 )
65
66 // IsDirectoryEvent checks if an event is a directory consensus event.
67 func IsDirectoryEvent(ev *event.E) bool {
68 if ev == nil {
69 return false
70 }
71 k := uint16(ev.Kind)
72 return k == 39100 || k == 39101 || k == 39102 ||
73 k == 39103 || k == 39104 || k == 39105
74 }
75
76 // FilterDirectoryEvents filters a slice of events to only directory events.
77 func FilterDirectoryEvents(events []*event.E) (filtered []*event.E) {
78 filtered = make([]*event.E, 0)
79 for _, ev := range events {
80 if IsDirectoryEvent(ev) {
81 filtered = append(filtered, ev)
82 }
83 }
84 return
85 }
86
87 // NormalizeRelayURL ensures a relay URL has the canonical format with trailing slash.
88 func NormalizeRelayURL(url string) string {
89 if url == "" {
90 return ""
91 }
92 if url[len(url)-1] != '/' {
93 return url + "/"
94 }
95 return url
96 }
97
98 // ParseDirectoryEvent parses any directory event based on its kind.
99 func ParseDirectoryEvent(ev *event.E) (parsed interface{}, err error) {
100 if !IsDirectoryEvent(ev) {
101 return nil, errorf.E("not a directory event: kind %d", uint16(ev.Kind))
102 }
103
104 switch uint16(ev.Kind) {
105 case 39100:
106 return directory.ParseRelayIdentityAnnouncement(ev)
107 case 39101:
108 return directory.ParseTrustAct(ev)
109 case 39102:
110 return directory.ParseGroupTagAct(ev)
111 case 39103:
112 return directory.ParsePublicKeyAdvertisement(ev)
113 case 39104:
114 return directory.ParseDirectoryEventReplicationRequest(ev)
115 case 39105:
116 return directory.ParseDirectoryEventReplicationResponse(ev)
117 default:
118 return nil, errorf.E("unknown directory event kind: %d", uint16(ev.Kind))
119 }
120 }
121