Go fmt and add missing `toml:"hmc"` tag
parent
b3fa772152
commit
b59ba24b35
296
main.go
296
main.go
|
@ -21,124 +21,124 @@ import (
|
|||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/russross/blackfriday/v2"
|
||||
"github.com/russross/blackfriday/v2"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
BatchSize int `toml:"batch_size"`
|
||||
TestEmail string `toml:"test_email"`
|
||||
HMC struct {
|
||||
ApiUrl string `toml:"api_url"`
|
||||
SharedSecret string `toml:"shared_secret"`
|
||||
City string `toml:"city"`
|
||||
}
|
||||
BatchSize int `toml:"batch_size"`
|
||||
TestEmail string `toml:"test_email"`
|
||||
HMC struct {
|
||||
ApiUrl string `toml:"api_url"`
|
||||
SharedSecret string `toml:"shared_secret"`
|
||||
City string `toml:"city"`
|
||||
} `toml:"hmc"`
|
||||
Postmark struct {
|
||||
ServerToken string `toml:"server_token"`
|
||||
ApiUrl string `toml:"api_url"`
|
||||
TemplateId int `toml:"template_id"`
|
||||
SenderName string `toml:"sender_name"`
|
||||
SenderName string `toml:"sender_name"`
|
||||
SenderEmail string `toml:"sender_email"`
|
||||
MessageStream string `toml:"message_stream"`
|
||||
} `toml:"postmark"`
|
||||
}
|
||||
|
||||
func retrieveSubscribers(cfg *Config) ([]string, error) {
|
||||
/* Prepare request payload */
|
||||
/* *** */
|
||||
payload := map[string]string{
|
||||
"city": cfg.HMC.City,
|
||||
}
|
||||
/* Prepare request payload */
|
||||
/* *** */
|
||||
payload := map[string]string{
|
||||
"city": cfg.HMC.City,
|
||||
}
|
||||
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error encoding payload: %v", err)
|
||||
}
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error encoding payload: %v", err)
|
||||
}
|
||||
|
||||
/* Prepare request */
|
||||
/* *** */
|
||||
req, err := http.NewRequest("POST", cfg.HMC.ApiUrl, bytes.NewReader(payloadBytes))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating request: %v", err)
|
||||
}
|
||||
/* Prepare request */
|
||||
/* *** */
|
||||
req, err := http.NewRequest("POST", cfg.HMC.ApiUrl, bytes.NewReader(payloadBytes))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating request: %v", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", cfg.HMC.SharedSecret)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", cfg.HMC.SharedSecret)
|
||||
|
||||
/* Execute request */
|
||||
/* *** */
|
||||
client := http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making request: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
/* Execute request */
|
||||
/* *** */
|
||||
client := http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making request: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
/* Fail if status code is not in the 2xx range */
|
||||
/* *** */
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return nil, fmt.Errorf("error: HTTP status code %d", resp.StatusCode)
|
||||
}
|
||||
/* Fail if status code is not in the 2xx range */
|
||||
/* *** */
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return nil, fmt.Errorf("error: HTTP status code %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
/* Read response's body */
|
||||
/* *** */
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading response body: %v", err)
|
||||
}
|
||||
/* Read response's body */
|
||||
/* *** */
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading response body: %v", err)
|
||||
}
|
||||
|
||||
/* Parse JSON response */
|
||||
/* *** */
|
||||
var data map[string]interface{}
|
||||
if err := json.Unmarshal(body, &data); err != nil {
|
||||
return nil, fmt.Errorf("error unmarshalling JSON: %v", err)
|
||||
}
|
||||
/* Parse JSON response */
|
||||
/* *** */
|
||||
var data map[string]interface{}
|
||||
if err := json.Unmarshal(body, &data); err != nil {
|
||||
return nil, fmt.Errorf("error unmarshalling JSON: %v", err)
|
||||
}
|
||||
|
||||
/* Extract the emails from the response */
|
||||
/* *** */
|
||||
emails, ok := data["emails"].([]interface{})
|
||||
if !ok {
|
||||
return nil, errors.New("response format error: 'emails' field not found")
|
||||
}
|
||||
/* Extract the emails from the response */
|
||||
/* *** */
|
||||
emails, ok := data["emails"].([]interface{})
|
||||
if !ok {
|
||||
return nil, errors.New("response format error: 'emails' field not found")
|
||||
}
|
||||
|
||||
/* Convert emails to []string */
|
||||
/* *** */
|
||||
var subscribers []string
|
||||
for _, email := range emails {
|
||||
if emailStr, ok := email.(string); ok {
|
||||
subscribers = append(subscribers, emailStr)
|
||||
}
|
||||
}
|
||||
/* Convert emails to []string */
|
||||
/* *** */
|
||||
var subscribers []string
|
||||
for _, email := range emails {
|
||||
if emailStr, ok := email.(string); ok {
|
||||
subscribers = append(subscribers, emailStr)
|
||||
}
|
||||
}
|
||||
|
||||
return subscribers, nil
|
||||
return subscribers, nil
|
||||
}
|
||||
|
||||
func parseMailFile(contents string) (subject string, body string, valid bool) {
|
||||
/* Remove leading and trailing newlines */
|
||||
/* *** */
|
||||
/* *** */
|
||||
contents = strings.TrimSpace(contents)
|
||||
lines := strings.Split(contents, "\n")
|
||||
|
||||
/* Check if there is at least one line */
|
||||
/* *** */
|
||||
/* *** */
|
||||
if len(lines) == 0 {
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
/* Check if the first line is a title with the '#' Markdown symbol */
|
||||
/* *** */
|
||||
/* *** */
|
||||
if strings.HasPrefix(lines[0], "#") { // Extract subject without the '#' symbol and leading/trailing spaces
|
||||
subject = strings.TrimSpace(strings.TrimPrefix(lines[0], "#"))
|
||||
} else {
|
||||
|
@ -147,24 +147,24 @@ func parseMailFile(contents string) (subject string, body string, valid bool) {
|
|||
}
|
||||
|
||||
/* Concatenate the remaining lines to form the body */
|
||||
/* *** */
|
||||
/* *** */
|
||||
body = strings.Join(lines[1:], "\n")
|
||||
|
||||
/* Check if the body is not empty */
|
||||
/* *** */
|
||||
/* *** */
|
||||
if body == "" {
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
/* If all checks pass, set valid to true */
|
||||
/* *** */
|
||||
/* *** */
|
||||
return subject, body, true
|
||||
}
|
||||
|
||||
func sendTest(cfg *Config, markdownFile string) {
|
||||
/* Open and parse Markdown file */
|
||||
/* *** */
|
||||
members := []string{cfg.TestEmail}
|
||||
/* Open and parse Markdown file */
|
||||
/* *** */
|
||||
members := []string{cfg.TestEmail}
|
||||
contents, err := os.ReadFile(markdownFile)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
|
@ -174,20 +174,20 @@ func sendTest(cfg *Config, markdownFile string) {
|
|||
}
|
||||
return
|
||||
}
|
||||
contents = bytes.ReplaceAll(contents, []byte("\r"), []byte(""))
|
||||
contents = bytes.ReplaceAll(contents, []byte("\r"), []byte(""))
|
||||
|
||||
subject, body, valid := parseMailFile(string(contents))
|
||||
if !valid {
|
||||
fmt.Printf("Error parsing file %s: invalid format\n", markdownFile)
|
||||
fmt.Printf("Error parsing file %s: invalid format\n", markdownFile)
|
||||
return
|
||||
}
|
||||
|
||||
/* Convert Markdown to HTML */
|
||||
/* *** */
|
||||
html := blackfriday.Run([]byte(body))
|
||||
/* Convert Markdown to HTML */
|
||||
/* *** */
|
||||
html := blackfriday.Run([]byte(body))
|
||||
|
||||
/* Send the email */
|
||||
/* *** */
|
||||
/* Send the email */
|
||||
/* *** */
|
||||
trackingFile := markdownFile + ".test.track"
|
||||
logFile := markdownFile + "." + time.Now().Format("20060102T150405") + ".test.log"
|
||||
os.Truncate(trackingFile, 0)
|
||||
|
@ -201,8 +201,8 @@ func sendTest(cfg *Config, markdownFile string) {
|
|||
}
|
||||
|
||||
func sendNews(cfg *Config, markdownFile string) {
|
||||
/* Open and parse Markdown file */
|
||||
/* *** */
|
||||
/* Open and parse Markdown file */
|
||||
/* *** */
|
||||
contents, err := os.ReadFile(markdownFile)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
|
@ -210,22 +210,22 @@ func sendNews(cfg *Config, markdownFile string) {
|
|||
} else {
|
||||
fmt.Printf("Error reading file %s: %v\n", markdownFile, err)
|
||||
}
|
||||
return
|
||||
return
|
||||
}
|
||||
contents = bytes.ReplaceAll(contents, []byte("\r"), []byte(""))
|
||||
contents = bytes.ReplaceAll(contents, []byte("\r"), []byte(""))
|
||||
|
||||
subject, body, valid := parseMailFile(string(contents))
|
||||
if !valid {
|
||||
fmt.Printf("Error parsing file %s: invalid format\n", markdownFile)
|
||||
return
|
||||
fmt.Printf("Error parsing file %s: invalid format\n", markdownFile)
|
||||
return
|
||||
}
|
||||
|
||||
/* Convert Markdown to HTML */
|
||||
/* *** */
|
||||
html := blackfriday.Run([]byte(body))
|
||||
/* Convert Markdown to HTML */
|
||||
/* *** */
|
||||
html := blackfriday.Run([]byte(body))
|
||||
|
||||
/* Checksum test */
|
||||
/* *** */
|
||||
/* Checksum test */
|
||||
/* *** */
|
||||
sha1File := markdownFile + ".sha1"
|
||||
sum := sha1.Sum(contents)
|
||||
sumString := fmt.Sprintf("%x", sum)
|
||||
|
@ -237,19 +237,19 @@ func sendNews(cfg *Config, markdownFile string) {
|
|||
} else {
|
||||
fmt.Printf("Error reading file %s: %v\n", sha1File, err)
|
||||
}
|
||||
return
|
||||
return
|
||||
}
|
||||
if string(sha1) != sumString {
|
||||
fmt.Printf("Hash doesn't match. Did you change the newsletter's contents? Rerun the test command please!\n")
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
/* Retrieve the mailing list from the city listed in the config file */
|
||||
/* *** */
|
||||
subscribers, err := retrieveSubscribers(cfg)
|
||||
/* Retrieve the mailing list from the city listed in the config file */
|
||||
/* *** */
|
||||
subscribers, err := retrieveSubscribers(cfg)
|
||||
|
||||
/* Blast the member update! */
|
||||
/* *** */
|
||||
/* Blast the member update! */
|
||||
/* *** */
|
||||
trackingFile := markdownFile + ".track"
|
||||
logFile := markdownFile + "." + time.Now().Format("20060102T150405") + ".log"
|
||||
|
||||
|
@ -324,7 +324,7 @@ var postmarkClient = http.Client{}
|
|||
type PostmarkTemplateModel struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Subject string `json:"subject"`
|
||||
Subject string `json:"subject"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
|
@ -356,14 +356,14 @@ func sendMail(cfg *Config, recipients []string, subject, html string) ([]Postmar
|
|||
body := PostmarkBatchWithTemplateBody{}
|
||||
for _, r := range recipients {
|
||||
body.Messages = append(body.Messages, PostmarkTemplateMessage{
|
||||
From: from,
|
||||
To: r,
|
||||
TemplateId: cfg.Postmark.TemplateId,
|
||||
From: from,
|
||||
To: r,
|
||||
TemplateId: cfg.Postmark.TemplateId,
|
||||
TemplateModel: PostmarkTemplateModel{
|
||||
Name: cfg.Postmark.SenderName,
|
||||
Email: cfg.Postmark.SenderEmail,
|
||||
Subject: subject,
|
||||
Body: html,
|
||||
Name: cfg.Postmark.SenderName,
|
||||
Email: cfg.Postmark.SenderEmail,
|
||||
Subject: subject,
|
||||
Body: html,
|
||||
},
|
||||
MessageStream: cfg.Postmark.MessageStream,
|
||||
})
|
||||
|
@ -405,30 +405,30 @@ func sendMail(cfg *Config, recipients []string, subject, html string) ([]Postmar
|
|||
}
|
||||
|
||||
func main() {
|
||||
/* Read in credentials into Config struct */
|
||||
/* *** */
|
||||
/* Read in credentials into Config struct */
|
||||
/* *** */
|
||||
configFile := "config.toml"
|
||||
|
||||
data, err := ioutil.ReadFile(configFile)
|
||||
if err != nil {
|
||||
fmt.Println("Error reading TOML file:", err)
|
||||
os.Exit(1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
config := &Config{}
|
||||
|
||||
if err := toml.Unmarshal(data, config); err != nil {
|
||||
fmt.Println("Error unmarshaling TOML:", err)
|
||||
os.Exit(1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
/* Setup Cobra */
|
||||
/* *** */
|
||||
/* Setup Cobra */
|
||||
/* *** */
|
||||
cmd := &cobra.Command{
|
||||
Use: "meetupinvite2000",
|
||||
Short: "MeetupInvite2000",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf(`Instructions:
|
||||
fmt.Printf(`Instructions:
|
||||
1. Create an email file
|
||||
* Name it whatever you want. Markdown is expected
|
||||
* Any newlines at the start or end of the file will be removed
|
||||
|
@ -455,26 +455,26 @@ func main() {
|
|||
},
|
||||
}
|
||||
|
||||
dumpCmd := &cobra.Command{
|
||||
Use: "dump",
|
||||
Short: "Dump the list of subscribers",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
subscribers, err := retrieveSubscribers(config)
|
||||
if err != nil {
|
||||
fmt.Printf("Error retrieving subscribers: %v\n", err)
|
||||
return
|
||||
}
|
||||
dumpCmd := &cobra.Command{
|
||||
Use: "dump",
|
||||
Short: "Dump the list of subscribers",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
subscribers, err := retrieveSubscribers(config)
|
||||
if err != nil {
|
||||
fmt.Printf("Error retrieving subscribers: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Handmade Meetups - \033[1m%s\033[0m\n\n", config.HMC.City)
|
||||
maxIndexWidth := len(strconv.Itoa(len(subscribers)))
|
||||
for index, email := range subscribers {
|
||||
indexStr := strconv.Itoa(index + 1)
|
||||
indexStr = strings.Repeat(" ", maxIndexWidth - len(indexStr)) + indexStr // Right-align the index
|
||||
fmt.Printf("%s. \033[1m%s\033[0m\n", indexStr, email)
|
||||
}
|
||||
fmt.Printf("\nMailing list grows automatically as more subscribe at \033[1mhandmadecities.com/meetups\033[0m\n")
|
||||
},
|
||||
}
|
||||
fmt.Printf("Handmade Meetups - \033[1m%s\033[0m\n\n", config.HMC.City)
|
||||
maxIndexWidth := len(strconv.Itoa(len(subscribers)))
|
||||
for index, email := range subscribers {
|
||||
indexStr := strconv.Itoa(index + 1)
|
||||
indexStr = strings.Repeat(" ", maxIndexWidth-len(indexStr)) + indexStr // Right-align the index
|
||||
fmt.Printf("%s. \033[1m%s\033[0m\n", indexStr, email)
|
||||
}
|
||||
fmt.Printf("\nMailing list grows automatically as more subscribe at \033[1mhandmadecities.com/meetups\033[0m\n")
|
||||
},
|
||||
}
|
||||
|
||||
testCmd := &cobra.Command{
|
||||
Use: "test [email file]",
|
||||
|
@ -502,11 +502,11 @@ func main() {
|
|||
},
|
||||
}
|
||||
|
||||
/* Install commands and serve them to the user */
|
||||
/* *** */
|
||||
cmd.AddCommand(dumpCmd)
|
||||
cmd.AddCommand(testCmd)
|
||||
cmd.AddCommand(blastCmd)
|
||||
/* Install commands and serve them to the user */
|
||||
/* *** */
|
||||
cmd.AddCommand(dumpCmd)
|
||||
cmd.AddCommand(testCmd)
|
||||
cmd.AddCommand(blastCmd)
|
||||
|
||||
cmd.Execute()
|
||||
cmd.Execute()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue