Compare commits

..

2 Commits

Author SHA1 Message Date
Abner Coimbre 4f10099b41 Final changes for v3.0.0
- Add animation when bloggifying
- JSON payload should include city, author, and email address
- Revert to original return types for bloggify()
2026-01-25 15:35:45 -08:00
Abner Coimbre c8fec23964 bloggify() should return error messages 2026-01-25 11:35:42 -08:00
1 changed files with 59 additions and 12 deletions

71
main.go
View File

@ -194,7 +194,6 @@ func sendTest(cfg *Config, markdownFile string) {
/* *** */ /* *** */
url, valid := bloggify(cfg, subject, body, false) url, valid := bloggify(cfg, subject, body, false)
if !valid { if !valid {
fmt.Printf("Error converting '%s' into a blog post\n", markdownFile)
return return
} }
@ -218,21 +217,24 @@ func sendTest(cfg *Config, markdownFile string) {
os.WriteFile(sha1File, []byte(sumString), 0666) os.WriteFile(sha1File, []byte(sumString), 0666)
} }
func bloggify(cfg *Config, subject, body string, isLive bool) (url string, valid bool) { func bloggify(cfg *Config, subject, body string, isLive bool) (string, bool) {
if !isLive { if !isLive {
return "https://handmadecities.com/meetup-invite-test", true return "https://handmadecities.com/meetupinvite2000", true
} }
/* Prepare request payload */ /* Prepare request payload */
/* *** */ /* *** */
payload := map[string]string{ payload := map[string]string{
"city": cfg.HMC.City,
"author": cfg.Postmark.SenderName,
"email": cfg.Postmark.SenderEmail,
"subject": subject, "subject": subject,
"body": body, "body": body,
} }
payloadBytes, err := json.Marshal(payload) payloadBytes, err := json.Marshal(payload)
if err != nil { if err != nil {
fmt.Printf("Error encoding bloggify payload: %v\n", err) fmt.Printf("Error encoding payload: %v\n", err)
return "", false return "", false
} }
@ -240,19 +242,41 @@ func bloggify(cfg *Config, subject, body string, isLive bool) (url string, valid
/* *** */ /* *** */
req, err := http.NewRequest("POST", "https://deploy.handmadecities.com/v1/bloggify", bytes.NewReader(payloadBytes)) req, err := http.NewRequest("POST", "https://deploy.handmadecities.com/v1/bloggify", bytes.NewReader(payloadBytes))
if err != nil { if err != nil {
fmt.Printf("Error creating bloggify request: %v\n", err) fmt.Printf("Error creating request: %v\n", err)
return "", false return "", false
} }
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", cfg.HMC.SharedSecret) req.Header.Set("Authorization", cfg.HMC.SharedSecret)
/* Spinner while request runs */
/* *** */
stop := make(chan struct{})
done := make(chan struct{})
go func() {
frames := []rune{'⠁', '⠂', '⠄', '⡀', '⢀', '⠠', '⠐', '⠈'}
i := 0
for {
select {
case <-stop:
close(done)
return
default:
fmt.Printf("\r📝 Bloggifying your email... %c", frames[i%len(frames)])
i++
time.Sleep(100 * time.Millisecond)
}
}
}()
/* Execute request */ /* Execute request */
/* *** */ /* *** */
client := http.Client{} client := http.Client{}
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
fmt.Printf("Error making bloggify request: %v\n", err) close(stop)
<-done
fmt.Printf("\r\033[31m📝 Failed to bloggify: %v\033[0m\n", err)
return "", false return "", false
} }
defer resp.Body.Close() defer resp.Body.Close()
@ -260,7 +284,9 @@ func bloggify(cfg *Config, subject, body string, isLive bool) (url string, valid
/* Fail if status code is not in the 2xx range */ /* Fail if status code is not in the 2xx range */
/* *** */ /* *** */
if resp.StatusCode < 200 || resp.StatusCode >= 300 { if resp.StatusCode < 200 || resp.StatusCode >= 300 {
fmt.Printf("Bloggify error: HTTP status code %d\n", resp.StatusCode) close(stop)
<-done
fmt.Printf("\r\033[31m📝 Bloggify error: HTTP status code %d\033[0m\n", resp.StatusCode)
return "", false return "", false
} }
@ -268,7 +294,9 @@ func bloggify(cfg *Config, subject, body string, isLive bool) (url string, valid
/* *** */ /* *** */
respBody, err := ioutil.ReadAll(resp.Body) respBody, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
fmt.Printf("Error reading bloggify response body: %v\n", err) close(stop)
<-done
fmt.Printf("\r\033[31m📝 Error reading bloggify response: %v\033[0m\n", err)
return "", false return "", false
} }
@ -276,7 +304,9 @@ func bloggify(cfg *Config, subject, body string, isLive bool) (url string, valid
/* *** */ /* *** */
var data map[string]interface{} var data map[string]interface{}
if err := json.Unmarshal(respBody, &data); err != nil { if err := json.Unmarshal(respBody, &data); err != nil {
fmt.Printf("Error unmarshalling bloggify JSON: %v\n", err) close(stop)
<-done
fmt.Printf("\r\033[31m📝 Error parsing bloggify JSON: %v\033[0m\n", err)
return "", false return "", false
} }
@ -284,10 +314,16 @@ func bloggify(cfg *Config, subject, body string, isLive bool) (url string, valid
/* *** */ /* *** */
url, ok := data["url"].(string) url, ok := data["url"].(string)
if !ok || url == "" { if !ok || url == "" {
fmt.Println("Bloggify response format error: 'url' field not found or empty") close(stop)
<-done
fmt.Printf("\r\033[31m📝 Bloggify response missing 'url' field\033[0m\n")
return "", false return "", false
} }
close(stop)
<-done
fmt.Printf("\r\033[32m📝 Your email is a blog post at %s\033[0m\n", url)
return url, true return url, true
} }
@ -315,7 +351,7 @@ func sendNews(cfg *Config, markdownFile string) {
/* *** */ /* *** */
url, valid := bloggify(cfg, subject, body, true) url, valid := bloggify(cfg, subject, body, true)
if !valid { if !valid {
fmt.Printf("Error converting '%s' into a blog post\n", markdownFile) fmt.Printf("Error converting '%s' into a blog post\n%v\n", markdownFile, err)
return return
} }
@ -1057,7 +1093,7 @@ func main() {
* Batch sends your invite using our Email API (Postmark) * Batch sends your invite using our Email API (Postmark)
* Produces .track file that tracks email addresses we attempted to send to * Produces .track file that tracks email addresses we attempted to send to
* Produces .log file with information received back from Postmark * Produces .log file with information received back from Postmark
* You can always blast again: useful when emailing new subscribers. Addresses already in .track file will be skipped * You can always blast again: addresses in .track files are skipped
--- ---
@ -1110,6 +1146,17 @@ func main() {
cmd.Usage() cmd.Usage()
os.Exit(1) os.Exit(1)
} }
reader := bufio.NewReader(os.Stdin)
fmt.Printf("Ready to blast to everyone in %s? (Y/n): ", config.HMC.City)
resp, _ := reader.ReadString('\n')
resp = strings.TrimSpace(strings.ToLower(resp))
if resp != "" && resp != "y" && resp != "yes" {
fmt.Println("Blast cancelled.")
return
}
updateFile := args[0] updateFile := args[0]
sendNews(config, updateFile) sendNews(config, updateFile)
}, },