package delay import ( "errors" "fmt" "sync/atomic" "time" ) var GLOBAL_MAILCOUNT *mailqueue type mailer struct { subject string to string body string } // probably easier to do this as sqlite storage selections type mailqueue struct { dirty atomic.Bool size int instant chan mailer // priority zero reminder chan mailer // priority one maintenance chan mailer // priority two marketing chan mailer // priority 3 errors int maxerrors int } func init() { var err error GLOBAL_MAILCOUNT, err = NewMailqueue(100, 60, 90, 5) if err != nil { fmt.Println(err) } } func NewMailqueue(size int, resetseconds int, sendseconds int, maxerrors int) (*mailqueue, error) { m := mailqueue{ size: size, instant: make(chan mailer, size), reminder: make(chan mailer, size), maintenance: make(chan mailer, size), marketing: make(chan mailer, size), maxerrors: maxerrors, } if resetseconds < 60 { return nil, errors.New("too small of a value") } if sendseconds < 90 { return nil, errors.New("too large of a value") } go m.BucketDrip(resetseconds) go m.BackgroundMail(sendseconds) return &m, nil } func (m *mailqueue) BucketDrip(resetseconds int) { // every 60 seconds for range time.Tick(time.Second * time.Duration(resetseconds)) { m.dirty.Store(false) } } func (m *mailqueue) BackgroundMail(sendseconds int) { // every 90 seconds (.75*60*24, 1080) for range time.Tick(time.Second * time.Duration(sendseconds)) { if m.errors > m.maxerrors { fmt.Println("too many errors, not sending.") return } var err error if m.dirty.Load() != true { select { case e := <-m.maintenance: m.dirty.Store(true) err = Send(e) default: select { case e := <-m.maintenance: m.dirty.Store(true) err = Send(e) case e := <-m.reminder: m.dirty.Store(true) err = Send(e) default: select { case e := <-m.maintenance: m.dirty.Store(true) err = Send(e) case e := <-m.reminder: m.dirty.Store(true) err = Send(e) case e := <-m.marketing: m.dirty.Store(true) err = Send(e) default: fmt.Println("queue empty") } } } } if err != nil { // add to failed queue? store as failed? m.errors += 1 } err = nil } } func (m *mailqueue) AddSend(mail mailer, queue string) error { var target chan mailer switch queue { case "maintenance": target = m.maintenance case "reminder": target = m.reminder case "marketing": target = m.marketing default: return errors.New("no valid queue") } if m.size-1 < len(target) { return errors.New("mail queue full!") } select { case target <- mail: default: return errors.New("mail queue full!") } return nil } func Send(m mailer) error { fmt.Println(m.to, m.subject, m.body) return nil }