8 changed files with 206 additions and 11 deletions
@ -0,0 +1,117 @@ |
|||||||
|
package nostr |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"encoding/json" |
||||||
|
"fmt" |
||||||
|
"log" |
||||||
|
|
||||||
|
"github.com/nbd-wtf/go-nostr" |
||||||
|
"github.com/nbd-wtf/go-nostr/nip19" |
||||||
|
) |
||||||
|
|
||||||
|
// Profile represents a parsed kind 0 profile event
|
||||||
|
type Profile struct { |
||||||
|
Event *nostr.Event |
||||||
|
Pubkey string |
||||||
|
Name string |
||||||
|
DisplayName string |
||||||
|
About string |
||||||
|
Picture string |
||||||
|
Website string |
||||||
|
NIP05 string |
||||||
|
Lud16 string |
||||||
|
Banner string |
||||||
|
RawJSON map[string]interface{} // Store all other fields
|
||||||
|
} |
||||||
|
|
||||||
|
// FetchProfile fetches a kind 0 profile event from an npub
|
||||||
|
func (c *Client) FetchProfile(ctx context.Context, npub string) (*Profile, error) { |
||||||
|
// Decode npub to get pubkey
|
||||||
|
prefix, value, err := nip19.Decode(npub) |
||||||
|
if err != nil { |
||||||
|
return nil, fmt.Errorf("failed to decode npub: %w", err) |
||||||
|
} |
||||||
|
|
||||||
|
if prefix != "npub" { |
||||||
|
return nil, fmt.Errorf("invalid npub prefix: expected 'npub', got '%s'", prefix) |
||||||
|
} |
||||||
|
|
||||||
|
pubkey, ok := value.(string) |
||||||
|
if !ok { |
||||||
|
return nil, fmt.Errorf("failed to parse npub: unexpected type") |
||||||
|
} |
||||||
|
|
||||||
|
// Create filter for kind 0 profile event
|
||||||
|
filter := nostr.Filter{ |
||||||
|
Kinds: []int{0}, |
||||||
|
Authors: []string{pubkey}, |
||||||
|
Limit: 1, |
||||||
|
} |
||||||
|
|
||||||
|
log.Printf("Fetching profile for npub %s (pubkey: %s)", npub, pubkey) |
||||||
|
|
||||||
|
// Fetch the event
|
||||||
|
event, err := c.FetchEvent(ctx, filter) |
||||||
|
if err != nil { |
||||||
|
return nil, fmt.Errorf("failed to fetch profile event: %w", err) |
||||||
|
} |
||||||
|
|
||||||
|
// Parse the profile
|
||||||
|
profile, err := ParseProfile(event) |
||||||
|
if err != nil { |
||||||
|
return nil, fmt.Errorf("failed to parse profile: %w", err) |
||||||
|
} |
||||||
|
|
||||||
|
return profile, nil |
||||||
|
} |
||||||
|
|
||||||
|
// ParseProfile parses a kind 0 profile event
|
||||||
|
func ParseProfile(event *nostr.Event) (*Profile, error) { |
||||||
|
if event.Kind != 0 { |
||||||
|
return nil, fmt.Errorf("expected kind 0, got %d", event.Kind) |
||||||
|
} |
||||||
|
|
||||||
|
profile := &Profile{ |
||||||
|
Event: event, |
||||||
|
Pubkey: event.PubKey, |
||||||
|
RawJSON: make(map[string]interface{}), |
||||||
|
} |
||||||
|
|
||||||
|
// Parse JSON content
|
||||||
|
var content map[string]interface{} |
||||||
|
if err := json.Unmarshal([]byte(event.Content), &content); err != nil { |
||||||
|
return nil, fmt.Errorf("failed to parse profile JSON: %w", err) |
||||||
|
} |
||||||
|
|
||||||
|
// Extract common fields
|
||||||
|
if name, ok := content["name"].(string); ok { |
||||||
|
profile.Name = name |
||||||
|
} |
||||||
|
if displayName, ok := content["display_name"].(string); ok { |
||||||
|
profile.DisplayName = displayName |
||||||
|
} |
||||||
|
if about, ok := content["about"].(string); ok { |
||||||
|
profile.About = about |
||||||
|
} |
||||||
|
if picture, ok := content["picture"].(string); ok { |
||||||
|
profile.Picture = picture |
||||||
|
} |
||||||
|
if website, ok := content["website"].(string); ok { |
||||||
|
profile.Website = website |
||||||
|
} |
||||||
|
if nip05, ok := content["nip05"].(string); ok { |
||||||
|
profile.NIP05 = nip05 |
||||||
|
} |
||||||
|
if lud16, ok := content["lud16"].(string); ok { |
||||||
|
profile.Lud16 = lud16 |
||||||
|
} |
||||||
|
if banner, ok := content["banner"].(string); ok { |
||||||
|
profile.Banner = banner |
||||||
|
} |
||||||
|
|
||||||
|
// Store all fields in RawJSON for access to any other fields
|
||||||
|
profile.RawJSON = content |
||||||
|
|
||||||
|
return profile, nil |
||||||
|
} |
||||||
Loading…
Reference in new issue