From 295ceec3de248a8f77f35c6113d43831569dc38a Mon Sep 17 00:00:00 2001 From: ChaoticByte Date: Thu, 12 Jun 2025 00:00:52 +0200 Subject: [PATCH] Improve config file structure by combining multiple recipients into lists (breaking) --- README.md | 21 +++++++++++---------- config.go | 36 +++++++++++++++++++++--------------- mail.go | 21 +++++++++++---------- main.go | 35 ++++++++++++++++++++++++----------- 4 files changed, 67 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 1c6ddbd..e04f337 100644 --- a/README.md +++ b/README.md @@ -34,27 +34,28 @@ Example: ```json { "api_fetch_interval": 600, - "datafile": "data.json", "enabled_api_endpoints": [ "bay", "bund" ], + "datafile": "data.json", "loglevel": 2, - "recipients": [ + "lists": [ { - "address": "guenther@example.org", - "include": [ - {"classification": "kritisch"}, - {"title_contains": "jQuery"} + "name": "Example List", + "recipients": ["someone@example.org"], + "filter": [ + {"classification": "hoch", "title_contains": "Microsoft"}, + {"classification": "kritisch"} ] } ], "smtp": { - "from": "from@example.org", - "host": "example.org", + "from": "user@localhost", + "host": "127.0.0.1", "port": 587, - "user": "from@example.org", - "password": "SiEhAbEnMiChInSgEsIcHtGeFiLmTdAsDüRfEnSiEnIcHt" + "user": "user@localhost", + "password": "change me :)" }, "template": { "subject": "", diff --git a/config.go b/config.go index f808b22..8785375 100644 --- a/config.go +++ b/config.go @@ -11,7 +11,7 @@ type Config struct { EnabledApiEndpoints []string `json:"enabled_api_endpoints"` PersistentDataFilePath string `json:"datafile"` LogLevel int `json:"loglevel"` - Recipients []Recipient `json:"recipients"` + Lists *[]NotifyList `json:"lists"` SmtpConfiguration SmtpSettings `json:"smtp"` Template MailTemplateConfig `json:"template"` } @@ -23,12 +23,16 @@ func NewConfig() Config { EnabledApiEndpoints: []string{"bay", "bund"}, PersistentDataFilePath: "data.json", LogLevel: 2, - Recipients: []Recipient{}, + Lists: &[]NotifyList{ + { Name: "Example List", + Recipients: []string{}, + Filter: []Filter{},}, + }, SmtpConfiguration: SmtpSettings{ - From: "from@example.org", - User: "from@example.org", - Password: "SiEhAbEnMiChInSgEsIcHtGeFiLmTdAsDüRfEnSiEnIcHt", - ServerHost: "example.org", + From: "user@localhost", + User: "user@localhost", + Password: "change me :)", + ServerHost: "127.0.0.1", ServerPort: 587}, Template: MailTemplateConfig{ SubjectTemplate: "", @@ -39,18 +43,20 @@ func NewConfig() Config { } func checkConfig(config Config) { - if len(config.Recipients) < 1 { + if len(*config.Lists) < 1 { logger.error("Configuration is incomplete") - panic(errors.New("no recipients are configured")) + panic(errors.New("no lists are configured")) } - for _, r := range config.Recipients { - if !mailAddressIsValid(r.Address) { - logger.error("Configuration includes invalid data") - panic(errors.New("'" + r.Address + "' is not a valid e-mail address")) - } - if len(r.Filters) < 1 { + for _, l := range *config.Lists { + if len(l.Filter) < 1 { logger.error("Configuration is incomplete") - panic(errors.New("recipient " + r.Address + " has no filter defined - at least {'any': true/false} should be configured")) + panic(errors.New("list " + l.Name + " has no filter defined - at least [{'any': true/false}] should be configured")) + } + for _, r := range l.Recipients { + if !mailAddressIsValid(r) { + logger.error("Configuration includes invalid data") + panic(errors.New("'" + r + "' is not a valid e-mail address")) + } } } if !mailAddressIsValid(config.SmtpConfiguration.From) { diff --git a/mail.go b/mail.go index 07477ba..d2f6ae4 100644 --- a/mail.go +++ b/mail.go @@ -23,21 +23,22 @@ func (c MailContent) serializeValidMail(from string, to string) []byte { // and I'm too lazy to encode ä into =E4 and so on subjectEncoded := base64.StdEncoding.EncodeToString([]byte(c.Subject)) bodyEncoded := base64.StdEncoding.EncodeToString([]byte(c.Body)) - data := []byte(fmt.Sprintf( + data := fmt.Appendf(nil, "Content-Type: text/plain; charset=\"utf-8\"\r\nContent-Transfer-Encoding: base64\r\nFrom: %v%vTo: %v%vSubject: =?utf-8?b?%v?=%v%v%v", from, MAIL_LINE_SEP, to, MAIL_LINE_SEP, subjectEncoded, MAIL_LINE_SEP, MAIL_LINE_SEP, - bodyEncoded)) + bodyEncoded) // done, I guess return data } -type Recipient struct { - Address string `json:"address"` +type NotifyList struct { + Name string `json:"name"` + Recipients []string `json:"recipients"` // Must be a configured filter id - Filters []Filter `json:"include"` + Filter []Filter `json:"filter"` } type SmtpSettings struct { @@ -48,8 +49,8 @@ type SmtpSettings struct { Password string `json:"password"` } -func (r Recipient) sendNotices(notices []WidNotice, template MailTemplate, auth smtp.Auth, smtpConfig SmtpSettings, cache *map[string][]byte) error { - logger.debug("Generating and sending mails to " + r.Address + " ...") +func sendNotices(recipient string, notices []WidNotice, template MailTemplate, auth smtp.Auth, smtpConfig SmtpSettings, cache *map[string][]byte) error { + logger.debug("Generating and sending mails for recipient " + recipient + " ...") cacheHits := 0 cacheMisses := 0 mails := [][]byte{} @@ -67,7 +68,7 @@ func (r Recipient) sendNotices(notices []WidNotice, template MailTemplate, auth logger.error(err) } // serialize mail - data = mailContent.serializeValidMail(smtpConfig.From, r.Address) + data = mailContent.serializeValidMail(smtpConfig.From, recipient) // add to cache (*cache)[n.Uuid] = data } @@ -77,11 +78,11 @@ func (r Recipient) sendNotices(notices []WidNotice, template MailTemplate, auth err := sendMails( smtpConfig, auth, - r.Address, + recipient, mails, ) if err != nil { return err } - logger.debug("Successfully sent all mails to " + r.Address) + logger.debug("Successfully sent all mails to " + recipient) return nil } diff --git a/main.go b/main.go index 62de84a..89456e3 100644 --- a/main.go +++ b/main.go @@ -120,22 +120,35 @@ func main() { logger.debug(fmt.Sprintf("Got %v new notices", len(newNotices))) if len(newNotices) > 0 { logger.info("Sending email notifications ...") + // mail recipient : pointer to slice of wid notices to be sent + noticesToBeSent := map[string][]WidNotice{} recipientsNotified := 0 var err error - for _, r := range config.Recipients { - // Filter notices for this recipient - filteredNotices := []WidNotice{} - for _, f := range r.Filters { + for _, l := range *config.Lists { + // Filter notices for this list + for _, f := range l.Filter { for _, n := range f.filter(newNotices) { - if !noticeSliceContains(filteredNotices, n) { - filteredNotices = append(filteredNotices, n) + for _, r := range l.Recipients { + if !noticeSliceContains(noticesToBeSent[r], n) { + noticesToBeSent[r] = append(noticesToBeSent[r], n) + } } } } - slices.Reverse(filteredNotices) - logger.debug(fmt.Sprintf("Including %v of %v notices for recipient %v", len(filteredNotices), len(newNotices), r.Address)) - // Send notices - err = r.sendNotices(filteredNotices, mailTemplate, mailAuth, config.SmtpConfiguration, &cache) + } + for r, notices := range noticesToBeSent { + // sort by publish date + slices.SortFunc(notices, func(a WidNotice, b WidNotice) int { + if a.Published == b.Published { + return 0 + } else if a.Published.After(b.Published) { + return 1 + } else { + return -1 + } + }) + // send + err = sendNotices(r, notices, mailTemplate, mailAuth, config.SmtpConfiguration, &cache) if err != nil { logger.error(err) } else { @@ -149,7 +162,7 @@ func main() { persistent.data.(PersistentData).LastPublished[id] = t persistent.save() } - logger.info(fmt.Sprintf("Email notifications sent to %v of %v recipients", recipientsNotified, len(config.Recipients))) + logger.info(fmt.Sprintf("Email notifications sent to %v of %v recipients", recipientsNotified, len(noticesToBeSent))) } } dt := int(time.Now().UnixMilli() - t1)