package dm import ( "context" "errors" "fmt" gn "github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr/keyer" "github.com/nbd-wtf/go-nostr/nip04" "github.com/nbd-wtf/go-nostr/nip59" ) var ErrUnsupportedKind = errors.New("unsupported DM kind") // BuildEvent builds the kind-4 or kind-1059 event(s) for a DM. // For kind 1059 it returns the gift wrap addressed to the recipient. // For kind 4 it returns a single signed event. func BuildEvent(kind int, senderSk, recipientHex, content string) ([]gn.Event, error) { switch kind { case 4: return buildNip04(senderSk, recipientHex, content) case 1059: return buildNip17(senderSk, recipientHex, content) default: return nil, fmt.Errorf("%w: %d", ErrUnsupportedKind, kind) } } func buildNip04(sk, recipient, content string) ([]gn.Event, error) { shared, err := nip04.ComputeSharedSecret(recipient, sk) if err != nil { return nil, fmt.Errorf("nip04 shared: %w", err) } enc, err := nip04.Encrypt(content, shared) if err != nil { return nil, fmt.Errorf("nip04 encrypt: %w", err) } pk, err := gn.GetPublicKey(sk) if err != nil { return nil, err } ev := gn.Event{ PubKey: pk, CreatedAt: gn.Now(), Kind: 4, Tags: gn.Tags{{"p", recipient}}, Content: enc, } if err := ev.Sign(sk); err != nil { return nil, fmt.Errorf("sign: %w", err) } return []gn.Event{ev}, nil } func buildNip17(sk, recipient, content string) ([]gn.Event, error) { ks, err := keyer.NewPlainKeySigner(sk) if err != nil { return nil, fmt.Errorf("keyer: %w", err) } ctx := context.Background() ourPubkey, err := ks.GetPublicKey(ctx) if err != nil { return nil, err } rumor := gn.Event{ Kind: gn.KindDirectMessage, Content: content, Tags: gn.Tags{{"p", recipient}}, CreatedAt: gn.Now(), PubKey: ourPubkey, } rumor.ID = rumor.GetID() wrap, err := nip59.GiftWrap( rumor, recipient, func(s string) (string, error) { return ks.Encrypt(ctx, s, recipient) }, func(e *gn.Event) error { return ks.SignEvent(ctx, e) }, nil, ) if err != nil { return nil, fmt.Errorf("nip17 giftwrap: %w", err) } return []gn.Event{wrap}, nil }