main.go raw

   1  package main
   2  
   3  import (
   4  	"context"
   5  	"fmt"
   6  	"os"
   7  	"time"
   8  
   9  	"next.orly.dev/pkg/nostr/crypto/encryption"
  10  	"next.orly.dev/pkg/nostr/crypto/keys"
  11  	"next.orly.dev/pkg/nostr/encoders/event"
  12  	"next.orly.dev/pkg/nostr/encoders/hex"
  13  	"next.orly.dev/pkg/nostr/encoders/tag"
  14  	"next.orly.dev/pkg/nostr/ws"
  15  	"next.orly.dev/pkg/lol/log"
  16  )
  17  
  18  func main() {
  19  	if len(os.Args) < 4 {
  20  		fmt.Fprintf(os.Stderr, "usage: send-dm <relay-url> <recipient-pubkey-hex> <message>\n")
  21  		fmt.Fprintf(os.Stderr, "  set NOSTR_SECRET_KEY=<hex> to use a specific key\n")
  22  		os.Exit(1)
  23  	}
  24  	relayURL := os.Args[1]
  25  	recipientHex := os.Args[2]
  26  	message := os.Args[3]
  27  
  28  	var secretBytes []byte
  29  	var err error
  30  	if sk := os.Getenv("NOSTR_SECRET_KEY"); sk != "" {
  31  		secretBytes, err = hex.Dec(sk)
  32  		if err != nil {
  33  			log.F.F("decode secret key: %v", err)
  34  		}
  35  	} else {
  36  		secretBytes, err = keys.GenerateSecretKey()
  37  		if err != nil {
  38  			log.F.F("generate key: %v", err)
  39  		}
  40  	}
  41  	signer, err := keys.SecretBytesToSigner(secretBytes)
  42  	if err != nil {
  43  		log.F.F("create signer: %v", err)
  44  	}
  45  	defer signer.Zero()
  46  
  47  	pubHex := hex.Enc(signer.Pub())
  48  	fmt.Printf("sender pubkey: %s\n", pubHex)
  49  
  50  	// Encrypt content with NIP-44
  51  	recipientPub, err := hex.Dec(recipientHex)
  52  	if err != nil {
  53  		log.F.F("decode recipient: %v", err)
  54  	}
  55  	convKey, err := encryption.GenerateConversationKey(signer.Sec(), recipientPub)
  56  	if err != nil {
  57  		log.F.F("conversation key: %v", err)
  58  	}
  59  	ciphertext, err := encryption.Encrypt(convKey, []byte(message), nil)
  60  	if err != nil {
  61  		log.F.F("encrypt: %v", err)
  62  	}
  63  
  64  	// Build kind 4 DM
  65  	ev := &event.E{
  66  		Content:   []byte(ciphertext),
  67  		CreatedAt: time.Now().Unix(),
  68  		Kind:      4,
  69  		Tags:      tag.NewS(tag.NewFromAny("p", recipientHex)),
  70  	}
  71  	if err := ev.Sign(signer); err != nil {
  72  		log.F.F("sign: %v", err)
  73  	}
  74  
  75  	fmt.Printf("event id: %s\n", hex.Enc(ev.ID))
  76  
  77  	// Connect and publish
  78  	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  79  	defer cancel()
  80  
  81  	conn, err := ws.RelayConnect(ctx, relayURL)
  82  	if err != nil {
  83  		log.F.F("dial: %v", err)
  84  	}
  85  	defer conn.Close()
  86  
  87  	if err := conn.Publish(ctx, ev); err != nil {
  88  		log.F.F("publish: %v", err)
  89  	}
  90  
  91  	fmt.Println("DM sent successfully")
  92  }
  93