package main import ( "bytes" "crypto/tls" "encoding/csv" "fmt" "log" "net/smtp" "os" "strings" "time" ) type Alum struct { First, Last, FirstPref, LastPref string WMClass, WMHouse string Address1, Address2, City, State, Zip string PhoneCell, PhoneWork string Email, DOB string UndergradSchool, Major, GradYear, GradSchool string NoMail, DoNotSolicit bool } func NewAlum(record []string) Alum { a := Alum{} a.First = record[0] a.Last = record[1] a.FirstPref = record[2] a.LastPref = record[3] a.WMClass = record[4] a.Address1 = record[5] a.Address2 = record[6] a.City = record[7] a.State = record[8] a.Zip = record[9] a.PhoneCell = record[10] a.PhoneWork = record[11] a.Email = strings.TrimSpace(record[12]) a.DOB = record[13] a.UndergradSchool = record[14] a.GradYear = record[15] a.Major = record[16] a.WMHouse = record[17] a.GradSchool = record[18] a.NoMail = len(record[26]) > 0 a.DoNotSolicit = len(record[27]) > 0 return a } func (alum Alum) mapping(word string) string { switch word { case "FIRST": return alum.First case "FIRST_PREF": return alum.FirstPref case "LAST": return alum.Last case "LAST_PREF": return alum.LastPref case "WM_CLASS": return alum.WMClass case "ADDRESS_1": return alum.Address1 case "ADDRESS_2": return alum.Address2 case "CITY": return alum.City case "STATE": return alum.State case "ZIP": return alum.Zip case "PHONE_CELL": return alum.PhoneCell case "PHONE_WORK": return alum.PhoneWork case "EMAIL": return alum.Email case "DOB": return alum.DOB case "UNDERGRAD_SCHOOL": return alum.UndergradSchool case "GRAD_YEAR": return alum.GradYear case "MAJOR": return alum.Major case "WM_HOUSE": return alum.WMHouse case "GRAD_SCHOOL": return alum.GradSchool } return "" } func main() { template := GetTemplate() f, err := os.Open("data/partial.csv") if err != nil { log.Fatal(err) } r := csv.NewReader(f) records, err := r.ReadAll() if err != nil { log.Fatal(err) } for i := 0; i < len(records); i += 40 { cli := InitMail() for j := 0; j < 50 && i+j < len(records); j++ { a := NewAlum(records[i+j]) println(i + j) if a.Email == "" || a.NoMail || a.DoNotSolicit { continue } else { msg := FillTemplate(template, a) fmt.Println(msg) SendMail(cli, FROM, a.Email, msg) } } CloseMail(cli) time.Sleep(2 * time.Minute) } } func GetTemplate() string { headFile, err := os.Open("data/ahoy.head") if err != nil { log.Fatal("error while reading template", err) } htmlFile, err := os.Open("data/ahoy.html") if err != nil { log.Fatal("error while reading template", err) } buf := new(bytes.Buffer) _, err = buf.ReadFrom(headFile) if err != nil { log.Fatal("error while reading template", err) } _, err = buf.ReadFrom(htmlFile) if err != nil { log.Fatal("error while reading template", err) } return buf.String() } func FillTemplate(template string, alum Alum) string { return os.Expand(template, alum.mapping) } const FROM string = "shw1@williams.edu" const SERVER string = "smtp.gmail.com" func InitMail() *smtp.Client { cli, err := smtp.Dial(SERVER + ":587") if err != nil { log.Fatal("A ", err) } config := &tls.Config{ServerName: SERVER} if err = cli.StartTLS(config); err != nil { log.Fatal("AA ", err) } err = cli.Auth( smtp.PlainAuth("", "username@williams.edu", "password", SERVER)) if err != nil { log.Fatal("B ", err) } return cli } func SendMail(cli *smtp.Client, from, to, msg string) { if err := cli.Mail(from); err != nil { log.Fatal(err) } if err := cli.Rcpt(to); err != nil { log.Fatal(err) } writeCloser, err := cli.Data() if err != nil { log.Fatal(err) } writeCloser.Write([]byte(msg)) if err := writeCloser.Close(); err != nil { log.Fatal(err) } } func CloseMail(cli *smtp.Client) { if err := cli.Close(); err != nil { log.Fatal(err) } println("closed connection") }