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