all repos — honk @ b9f3da80576aab1db76a5aac89da57290f28fc81

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	"math/rand"
 21	"time"
 22)
 23
 24type Doover struct {
 25	ID   int64
 26	When time.Time
 27}
 28
 29func sayitagain(goarounds int, username string, rcpt string, msg []byte) {
 30	var drift time.Duration
 31	switch goarounds {
 32	case 1:
 33		drift = 5 * time.Minute
 34	case 2:
 35		drift = 1 * time.Hour
 36	case 3:
 37		drift = 12 * time.Hour
 38	case 4:
 39		drift = 24 * time.Hour
 40	default:
 41		log.Printf("he's dead jim: %s", rcpt)
 42		return
 43	}
 44	drift += time.Duration(rand.Int63n(int64(drift / 10)))
 45	when := time.Now().UTC().Add(drift)
 46	stmtAddDoover.Exec(when.Format(dbtimeformat), goarounds, username, rcpt, msg)
 47	select {
 48	case pokechan <- 0:
 49	default:
 50	}
 51}
 52
 53func deliverate(goarounds int, username string, rcpt string, msg []byte) {
 54	keyname, key := ziggy(username)
 55	inbox, _, err := getboxes(rcpt)
 56	if err != nil {
 57		log.Printf("error getting inbox %s: %s", rcpt, err)
 58		sayitagain(goarounds+1, username, rcpt, msg)
 59		return
 60	}
 61	err = PostMsg(keyname, key, inbox, msg)
 62	if err != nil {
 63		log.Printf("failed to post json to %s: %s", inbox, err)
 64		sayitagain(goarounds+1, username, rcpt, msg)
 65		return
 66	}
 67}
 68
 69var pokechan = make(chan int)
 70
 71func redeliverator() {
 72	sleeper := time.NewTimer(0)
 73	for {
 74		select {
 75		case <-pokechan:
 76			if !sleeper.Stop() {
 77				<-sleeper.C
 78			}
 79			time.Sleep(1 * time.Minute)
 80		case <-sleeper.C:
 81		}
 82
 83		rows, err := stmtGetDoovers.Query()
 84		if err != nil {
 85			log.Printf("wat?")
 86			time.Sleep(1 * time.Minute)
 87			continue
 88		}
 89		var doovers []Doover
 90		for rows.Next() {
 91			var d Doover
 92			var dt string
 93			rows.Scan(&d.ID, &dt)
 94			d.When, _ = time.Parse(dbtimeformat, dt)
 95			doovers = append(doovers, d)
 96		}
 97		rows.Close()
 98		now := time.Now().UTC()
 99		nexttime := now.Add(24 * time.Hour)
100		for _, d := range doovers {
101			if d.When.Before(now) {
102				var goarounds int
103				var username, rcpt string
104				var msg []byte
105				row := stmtLoadDoover.QueryRow(d.ID)
106				row.Scan(&goarounds, &username, &rcpt, &msg)
107				stmtZapDoover.Exec(d.ID)
108				log.Printf("redeliverating %s try %d", rcpt, goarounds)
109				deliverate(goarounds, username, rcpt, msg)
110			} else if d.When.Before(nexttime) {
111				nexttime = d.When
112			}
113		}
114		dur := nexttime.Sub(now).Round(time.Second) + 1*time.Minute
115		sleeper.Reset(dur)
116	}
117}