parent
b35ac045f3
commit
320bb5c34a
82
main.go
82
main.go
|
|
@ -1,6 +1,6 @@
|
||||||
// Meetup Invite 2000
|
// Meetup Invite 2000
|
||||||
// @author: Abner Coimbre <abner@handmadecities.com>
|
// @author: Abner Coimbre <abner@handmadecities.com>
|
||||||
// Copyright (c) 2024 Handmade Cities
|
// Copyright (c) Handmade Cities LLC
|
||||||
|
|
||||||
// Based off News Blaster 2000 from the Handmade Network: https://git.handmade.network/hmn/newsblaster2000
|
// Based off News Blaster 2000 from the Handmade Network: https://git.handmade.network/hmn/newsblaster2000
|
||||||
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
// Jack Punter (@TarriestPython)
|
// Jack Punter (@TarriestPython)
|
||||||
//
|
//
|
||||||
// Emotional Support
|
// Emotional Support
|
||||||
// Cucui Ganon Rosario
|
// Cucui Ganon Rosario: https://www.youtube.com/watch?v=bvCsTca0uBc
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
@ -306,14 +306,18 @@ func blastMail(cfg *Config, logFile string, trackingFile string, audience []stri
|
||||||
if len(group) == cfg.BatchSize {
|
if len(group) == cfg.BatchSize {
|
||||||
results, err := sendMail(cfg, group, subject, body)
|
results, err := sendMail(cfg, group, subject, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error while sending mail: %v\n", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sentToAddresses = append(sentToAddresses, group...)
|
sentToAddresses = append(sentToAddresses, group...)
|
||||||
os.WriteFile(trackingFile, []byte(strings.Join(sentToAddresses, "\n")), 0666)
|
os.WriteFile(trackingFile, []byte(strings.Join(sentToAddresses, "\n")), 0666)
|
||||||
|
var failed []string
|
||||||
for i, res := range results {
|
for i, res := range results {
|
||||||
log.WriteString(fmt.Sprintf("%s: %s\n", group[i], res.Message))
|
log.WriteString(fmt.Sprintf("%s: %s\n", group[i], res.Message))
|
||||||
|
if res.ErrorCode != 0 {
|
||||||
|
failed = append(failed, group[i])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
printBatchSummary(group, failed)
|
||||||
newReaderCount += len(group)
|
newReaderCount += len(group)
|
||||||
group = group[0:0]
|
group = group[0:0]
|
||||||
}
|
}
|
||||||
|
|
@ -322,14 +326,18 @@ func blastMail(cfg *Config, logFile string, trackingFile string, audience []stri
|
||||||
if len(group) > 0 {
|
if len(group) > 0 {
|
||||||
results, err := sendMail(cfg, group, subject, body)
|
results, err := sendMail(cfg, group, subject, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error while sending mail: %v\n", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sentToAddresses = append(sentToAddresses, group...)
|
sentToAddresses = append(sentToAddresses, group...)
|
||||||
os.WriteFile(trackingFile, []byte(strings.Join(sentToAddresses, "\n")), 0666)
|
os.WriteFile(trackingFile, []byte(strings.Join(sentToAddresses, "\n")), 0666)
|
||||||
|
var failed []string
|
||||||
for i, res := range results {
|
for i, res := range results {
|
||||||
log.WriteString(fmt.Sprintf("%s: %s\n", group[i], res.Message))
|
log.WriteString(fmt.Sprintf("%s: %s\n", group[i], res.Message))
|
||||||
|
if res.ErrorCode != 0 {
|
||||||
|
failed = append(failed, group[i])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
printBatchSummary(group, failed)
|
||||||
newReaderCount += len(group)
|
newReaderCount += len(group)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -369,8 +377,47 @@ type PostmarkBatchResult struct {
|
||||||
Message string `json:"Message"`
|
Message string `json:"Message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func printBatchSummary(sent, failed []string) {
|
||||||
|
if len(failed) == 0 {
|
||||||
|
fmt.Printf("\033[32m✅ Sent %d messages\033[0m\n", len(sent))
|
||||||
|
} else {
|
||||||
|
successCount := len(sent) - len(failed)
|
||||||
|
if successCount < 0 {
|
||||||
|
successCount = 0
|
||||||
|
}
|
||||||
|
fmt.Printf("\033[31m❌ Sent %d, failed %d\033[0m\n", successCount, len(failed))
|
||||||
|
for _, f := range failed {
|
||||||
|
fmt.Printf(" - %s\n", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func sendMail(cfg *Config, recipients []string, subject, html string) ([]PostmarkBatchResult, error) {
|
func sendMail(cfg *Config, recipients []string, subject, html string) ([]PostmarkBatchResult, error) {
|
||||||
fmt.Printf("Sending batch [%d recipients]...", len(recipients))
|
/* Start a tiny spinner while the HTTP call runs.
|
||||||
|
* Uses \r to update the same line. Stops as soon as the network call returns.
|
||||||
|
*/
|
||||||
|
|
||||||
|
stop := make(chan struct{})
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
frames := []rune{'|', '/', '-', '\\'}
|
||||||
|
i := 0
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stop:
|
||||||
|
close(done)
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
fmt.Printf("\rSending batch [%d recipients]... %c", len(recipients), frames[i%len(frames)])
|
||||||
|
i++
|
||||||
|
time.Sleep(120 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare and make the network call
|
||||||
|
*/
|
||||||
|
|
||||||
from := cfg.Postmark.SenderEmail
|
from := cfg.Postmark.SenderEmail
|
||||||
if cfg.Postmark.SenderName != "" {
|
if cfg.Postmark.SenderName != "" {
|
||||||
|
|
@ -400,13 +447,21 @@ func sendMail(cfg *Config, recipients []string, subject, html string) ([]Postmar
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodPost, "https://api.postmarkapp.com/email/batchWithTemplates", bytes.NewReader(reqBody))
|
req, err := http.NewRequest(http.MethodPost, "https://api.postmarkapp.com/email/batchWithTemplates", bytes.NewReader(reqBody))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// stop spinner before returning
|
||||||
|
close(stop)
|
||||||
|
<-done
|
||||||
|
fmt.Printf("\r")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Accept", "application/json")
|
req.Header.Set("Accept", "application/json")
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
req.Header.Set("X-Postmark-Server-Token", cfg.Postmark.ServerToken)
|
req.Header.Set("X-Postmark-Server-Token", cfg.Postmark.ServerToken)
|
||||||
res, err := postmarkClient.Do(req)
|
res, err := postmarkClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
close(stop)
|
||||||
|
<-done
|
||||||
|
fmt.Printf("\r\033[31m❌ Sending batch [%d recipients]... error: %v\033[0m\n", len(recipients), err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -414,17 +469,30 @@ func sendMail(cfg *Config, recipients []string, subject, html string) ([]Postmar
|
||||||
resBody, err := io.ReadAll(res.Body)
|
resBody, err := io.ReadAll(res.Body)
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
close(stop)
|
||||||
|
<-done
|
||||||
|
fmt.Printf("\r")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if res.StatusCode != 200 {
|
if res.StatusCode != 200 {
|
||||||
|
close(stop)
|
||||||
|
<-done
|
||||||
|
fmt.Printf("\r\033[31m❌ Bad response from postmark: %d\033[0m\n", res.StatusCode)
|
||||||
return nil, fmt.Errorf("Bad response from postmark: %d", res.StatusCode)
|
return nil, fmt.Errorf("Bad response from postmark: %d", res.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.Unmarshal(resBody, &results)
|
err = json.Unmarshal(resBody, &results)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Batch sent successfully, but failed to parse response from postmark.\n")
|
close(stop)
|
||||||
|
<-done
|
||||||
|
fmt.Printf("\r\033[33m⚠️ Batch sent but failed to parse Postmark response\033[0m\n")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
fmt.Printf("Done.\n")
|
|
||||||
|
close(stop)
|
||||||
|
<-done
|
||||||
|
fmt.Printf("\r\033[32m✅ Emailed batch of %d recipients\033[0m\n", len(recipients))
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue