first commit
This commit is contained in:
81
internal/nostr/profile.go
Normal file
81
internal/nostr/profile.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package nostr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
gn "github.com/nbd-wtf/go-nostr"
|
||||
)
|
||||
|
||||
// Metadata mirrors the kind:0 profile JSON. We accept both snake_case
|
||||
// (`display_name`, NIP-24) and the deprecated camelCase `displayName` since
|
||||
// some clients still publish only the latter. `Username` is also deprecated
|
||||
// but appears in older profiles; per NIP-24 it should be ignored in favor of
|
||||
// `Name`, but we surface it as a last-resort fallback.
|
||||
type Metadata struct {
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
DisplayNameAlt string `json:"displayName"`
|
||||
Username string `json:"username"`
|
||||
NIP05 string `json:"nip05"`
|
||||
About string `json:"about"`
|
||||
Picture string `json:"picture"`
|
||||
}
|
||||
|
||||
// ParseMetadata decodes a kind:0 content payload.
|
||||
func ParseMetadata(content string) (*Metadata, error) {
|
||||
var md Metadata
|
||||
if err := json.Unmarshal([]byte(content), &md); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &md, nil
|
||||
}
|
||||
|
||||
// FetchMetadata pulls the most recent kind:0 event for a hex pubkey across the pool.
|
||||
// Each relay may return multiple replacement events; we keep the one with the
|
||||
// highest CreatedAt across every relay reached before timeout.
|
||||
func FetchMetadata(ctx context.Context, p *Pool, hexpk string) (*Metadata, error) {
|
||||
filter := gn.Filter{
|
||||
Kinds: []int{0},
|
||||
Authors: []string{hexpk},
|
||||
Limit: 100,
|
||||
}
|
||||
|
||||
var newest *gn.Event
|
||||
for _, url := range p.URLs() {
|
||||
r, err := p.Connect(ctx, url)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
subCtx, cancel := context.WithTimeout(ctx, 8*time.Second)
|
||||
sub, err := r.Subscribe(subCtx, gn.Filters{filter})
|
||||
if err != nil {
|
||||
cancel()
|
||||
continue
|
||||
}
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case ev, ok := <-sub.Events:
|
||||
if !ok {
|
||||
break loop
|
||||
}
|
||||
if newest == nil || ev.CreatedAt > newest.CreatedAt {
|
||||
newest = ev
|
||||
}
|
||||
case <-sub.EndOfStoredEvents:
|
||||
break loop
|
||||
case <-subCtx.Done():
|
||||
break loop
|
||||
}
|
||||
}
|
||||
sub.Unsub()
|
||||
cancel()
|
||||
}
|
||||
|
||||
if newest == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return ParseMetadata(newest.Content)
|
||||
}
|
||||
Reference in New Issue
Block a user