all repos — honk @ 60e7bb8fbd9f0ad289060bdd9eaa19c280d58583

my fork of honk

deliverator.go (view raw)

  1//
  2// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com>
  3//
  4// Permission to use, copy, modify, and distribute this software for any
  5// purpose with or without fee is hereby granted, provided that the above
  6// copyright notice and this permission notice appear in all copies.
  7//
  8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 11// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 12// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 15
 16package main
 17
 18import (
 19	"log"
 20	notrand "math/rand"
 21	"time"
 22)
 23
 24func init() {
 25	notrand.Seed(time.Now().Unix())
 26}
 27
 28type Doover struct {
 29	ID   int64
 30	When time.Time
 31}
 32
 33func sayitagain(goarounds int, username string, rcpt string, msg []byte) {
 34	var drift time.Duration
 35	switch goarounds {
 36	case 1:
 37		drift = 5 * time.Minute
 38	case 2:
 39		drift = 1 * time.Hour
 40	case 3:
 41		drift = 12 * time.Hour
 42	case 4:
 43		drift = 24 * time.Hour
 44	default:
 45		log.Printf("he's dead jim: %s", rcpt)
 46		return
 47	}
 48	drift += time.Duration(notrand.Int63n(int64(drift / 10)))
 49	when := time.Now().UTC().Add(drift)
 50	stmtAddDoover.Exec(when.Format(dbtimeformat), goarounds, username, rcpt, msg)
 51	select {
 52	case pokechan <- 0:
 53	default:
 54	}
 55}
 56
 57func deliverate(goarounds int, username string, rcpt string, msg []byte) {
 58	keyname, key := ziggy(username)
 59	var inbox string
 60	// already did the box indirection
 61	if rcpt[0] == '%' {
 62		inbox = rcpt[1:]
 63	} else {
 64		box, err := getboxes(rcpt)
 65		if err != nil {
 66			log.Printf("error getting inbox %s: %s", rcpt, err)
 67			sayitagain(goarounds+1, username, rcpt, msg)
 68			return
 69		}
 70		inbox = box.In
 71		if box.Shared != "" {
 72			inbox = box.Shared
 73		}
 74	}
 75	err := PostMsg(keyname, key, inbox, msg)
 76	if err != nil {
 77		log.Printf("failed to post json to %s: %s", inbox, err)
 78		sayitagain(goarounds+1, username, rcpt, msg)
 79		return
 80	}
 81}
 82
 83var pokechan = make(chan int)
 84
 85func redeliverator() {
 86	sleeper := time.NewTimer(0)
 87	for {
 88		select {
 89		case <-pokechan:
 90			if !sleeper.Stop() {
 91				<-sleeper.C
 92			}
 93			time.Sleep(1 * time.Minute)
 94		case <-sleeper.C:
 95		}
 96
 97		rows, err := stmtGetDoovers.Query()
 98		if err != nil {
 99			log.Printf("wat?")
100			time.Sleep(1 * time.Minute)
101			continue
102		}
103		var doovers []Doover
104		for rows.Next() {
105			var d Doover
106			var dt string
107			rows.Scan(&d.ID, &dt)
108			d.When, _ = time.Parse(dbtimeformat, dt)
109			doovers = append(doovers, d)
110		}
111		rows.Close()
112		now := time.Now().UTC()
113		nexttime := now.Add(24 * time.Hour)
114		for _, d := range doovers {
115			if d.When.Before(now) {
116				var goarounds int
117				var username, rcpt string
118				var msg []byte
119				row := stmtLoadDoover.QueryRow(d.ID)
120				row.Scan(&goarounds, &username, &rcpt, &msg)
121				stmtZapDoover.Exec(d.ID)
122				log.Printf("redeliverating %s try %d", rcpt, goarounds)
123				deliverate(goarounds, username, rcpt, msg)
124			} else if d.When.Before(nexttime) {
125				nexttime = d.When
126			}
127		}
128		dur := nexttime.Sub(now).Round(time.Second) + 1*time.Minute
129		sleeper.Reset(dur)
130	}
131}