-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.go
166 lines (139 loc) · 4.22 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"math/rand/v2"
"net/http"
"os"
"time"
"github.com/joho/godotenv"
)
// AuthResponse represents the authentication response from Bluesky
type AuthResponse struct {
AccessJwt string `json:"accessJwt"`
Did string `json:"did"`
}
// ErrorResponse represents the error response structure from Bluesky
type ErrorResponse struct {
Error string `json:"error"`
Message string `json:"message"`
}
const (
authURL = "https://bsky.social/xrpc/com.atproto.server.createSession"
postURL = "https://bsky.social/xrpc/com.atproto.repo.createRecord"
)
func main() {
// Only load .env file in development environment
if os.Getenv("ENVIRONMENT") != "production" {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
}
// Load environment variables
username := os.Getenv("BLUESKY_USERNAME")
if username == "" {
log.Fatal("BLUESKY_USERNAME environment variable not set")
}
password := os.Getenv("BLUESKY_PASSWORD")
if password == "" {
log.Fatal("BLUESKY_PASSWORD environment variable not set")
}
// Authenticate and obtain access token
authResponse, err := authenticate(username, password)
if err != nil {
log.Fatalf("Authentication failed: %v", err)
}
scream := getScream()
// Post message using access token
err = postMessage(authResponse.AccessJwt, authResponse.Did, scream)
if err != nil {
log.Fatalf("Failed to post message: %v", err)
}
fmt.Println("Message posted successfully!")
}
func authenticate(identifier string, password string) (*AuthResponse, error) {
authBody := map[string]string{
"identifier": identifier,
"password": password,
}
bodyBytes, err := json.Marshal(authBody)
if err != nil {
return nil, fmt.Errorf("failed to marshal auth request body: %w", err)
}
req, err := http.NewRequest("POST", authURL, bytes.NewBuffer(bodyBytes))
if err != nil {
return nil, fmt.Errorf("failed to create auth request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("auth request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
var authResponse AuthResponse
if err := json.NewDecoder(resp.Body).Decode(&authResponse); err != nil {
return nil, fmt.Errorf("failed to decode auth response: %w", err)
}
fmt.Println("Authentication successful!")
return &authResponse, nil
}
var errResponse ErrorResponse
if err := json.NewDecoder(resp.Body).Decode(&errResponse); err != nil {
return nil, fmt.Errorf("failed to decode error response: %w", err)
}
return nil, fmt.Errorf("auth error (%d): %s - %s", resp.StatusCode, errResponse.Error, errResponse.Message)
}
func postMessage(accessToken, did, message string) error {
postBody := map[string]interface{}{
"repo": did,
"collection": "app.bsky.feed.post",
"record": map[string]interface{}{
"$type": "app.bsky.feed.post",
"text": message,
"createdAt": time.Now().UTC().Format(time.RFC3339),
},
}
bodyBytes, err := json.Marshal(postBody)
if err != nil {
return fmt.Errorf("failed to marshal post request body: %w", err)
}
req, err := http.NewRequest("POST", postURL, bytes.NewBuffer(bodyBytes))
if err != nil {
return fmt.Errorf("failed to create post request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("post request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
fmt.Println("Post successful!")
return nil
}
var errResponse ErrorResponse
if err := json.NewDecoder(resp.Body).Decode(&errResponse); err != nil {
return fmt.Errorf("failed to decode error response: %w", err)
}
return fmt.Errorf("post error (%d): %s - %s", resp.StatusCode, errResponse.Error, errResponse.Message)
}
func getScream() string {
numAs := 1 + rand.IntN(20) // Random number of A's (1-20)
numHs := 1 + rand.IntN(100) // Random number of H's (1-100)
scream := "A"
for i := 1; i < numAs; i++ {
scream += "A"
}
for i := 0; i < numHs; i++ {
scream += "H"
}
fmt.Println("Scream:", scream)
return scream
}