From 4f10099b4173ab5d113004c1f48308c5b6159671 Mon Sep 17 00:00:00 2001 From: Abner Coimbre Date: Sun, 25 Jan 2026 15:35:45 -0800 Subject: [PATCH] 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() --- main.go | 87 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/main.go b/main.go index 1bb7b1b..3f17810 100644 --- a/main.go +++ b/main.go @@ -192,9 +192,8 @@ func sendTest(cfg *Config, markdownFile string) { /* Convert Markdown to Blog Post */ /* *** */ - url, err := bloggify(cfg, subject, body, false) - if err != nil { - fmt.Printf("Error converting '%s' into a blog post\n%v\n", markdownFile, err) + url, valid := bloggify(cfg, subject, body, false) + if !valid { return } @@ -218,71 +217,114 @@ func sendTest(cfg *Config, markdownFile string) { os.WriteFile(sha1File, []byte(sumString), 0666) } -func bloggify(cfg *Config, subject, body string, isLive bool) (string, error) { +func bloggify(cfg *Config, subject, body string, isLive bool) (string, bool) { if !isLive { - return "https://handmadecities.com/meetup-invite-test", nil + return "https://handmadecities.com/meetupinvite2000", true } /* Prepare request payload */ /* *** */ payload := map[string]string{ + "city": cfg.HMC.City, + "author": cfg.Postmark.SenderName, + "email": cfg.Postmark.SenderEmail, "subject": subject, "body": body, } payloadBytes, err := json.Marshal(payload) if err != nil { - return "", fmt.Errorf("error encoding payload: %v", err) + fmt.Printf("Error encoding payload: %v\n", err) + return "", false } /* Prepare request */ /* *** */ req, err := http.NewRequest("POST", "https://deploy.handmadecities.com/v1/bloggify", bytes.NewReader(payloadBytes)) if err != nil { - return "", fmt.Errorf("error creating request: %v", err) + fmt.Printf("Error creating request: %v\n", err) + return "", false } req.Header.Set("Content-Type", "application/json") 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 */ /* *** */ client := http.Client{} resp, err := client.Do(req) if err != nil { - return "", fmt.Errorf("error making request: %v", err) + close(stop) + <-done + fmt.Printf("\r\033[31m📝 Failed to bloggify: %v\033[0m\n", err) + return "", false } defer resp.Body.Close() /* Fail if status code is not in the 2xx range */ /* *** */ if resp.StatusCode < 200 || resp.StatusCode >= 300 { - return "", fmt.Errorf("error: HTTP status code %d", resp.StatusCode) + close(stop) + <-done + fmt.Printf("\r\033[31m📝 Bloggify error: HTTP status code %d\033[0m\n", resp.StatusCode) + return "", false } /* Read response's body */ /* *** */ respBody, err := ioutil.ReadAll(resp.Body) if err != nil { - return "", fmt.Errorf("error reading response body: %v", err) + close(stop) + <-done + fmt.Printf("\r\033[31m📝 Error reading bloggify response: %v\033[0m\n", err) + return "", false } /* Parse JSON response */ /* *** */ var data map[string]interface{} if err := json.Unmarshal(respBody, &data); err != nil { - fmt.Printf("Error unmarshalling bloggify JSON: %v\n", err) - return "", fmt.Errorf("error unmarshalling JSON: %v", err) + close(stop) + <-done + fmt.Printf("\r\033[31m📝 Error parsing bloggify JSON: %v\033[0m\n", err) + return "", false } /* Extract the URL from the response */ /* *** */ url, ok := data["url"].(string) if !ok || url == "" { - return "", fmt.Errorf("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 url, nil + close(stop) + <-done + fmt.Printf("\r\033[32m📝 Your email is a blog post at %s\033[0m\n", url) + + return url, true } func sendNews(cfg *Config, markdownFile string) { @@ -307,8 +349,8 @@ func sendNews(cfg *Config, markdownFile string) { /* Convert Markdown to Blog Post */ /* *** */ - url, err := bloggify(cfg, subject, body, true) - if err != nil { + url, valid := bloggify(cfg, subject, body, true) + if !valid { fmt.Printf("Error converting '%s' into a blog post\n%v\n", markdownFile, err) return } @@ -1051,7 +1093,7 @@ func main() { * Batch sends your invite using our Email API (Postmark) * Produces .track file that tracks email addresses we attempted to send to * 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 --- @@ -1104,6 +1146,17 @@ func main() { cmd.Usage() 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] sendNews(config, updateFile) },