package main import ( "bytes" "context" "flag" "fmt" "image" "image/color" "image/png" "log" "net/http" "os" "sync" "time" "github.com/pkg/errors" ) var port = flag.Int("port", 8000, "port") var freq = flag.Duration("d", 15*time.Second, "refresh interval") type Now struct { sync.RWMutex cache *bytes.Buffer } func NewNow(ctx context.Context, d time.Duration) *Now { n := &Now{ cache: &bytes.Buffer{}, } n.Update() go func() { t := time.NewTicker(d) for { select { case <-t.C: n.Update() case <-ctx.Done(): return } } }() return n } func (n *Now) ServeHTTP(w http.ResponseWriter, req *http.Request) { n.RLock() w.Write(n.cache.Bytes()) n.RUnlock() } func (n *Now) Update() error { resp, err := http.Get("http://imgs.xkcd.com/comics/now.png") if err != nil { return errors.Wrap(err, "get") } defer resp.Body.Close() src, err := png.Decode(resp.Body) if err != nil { return errors.Wrap(err, "png decode") } bounds := src.Bounds() dest := image.NewNRGBA(bounds) for y := bounds.Min.Y; y < bounds.Max.Y; y++ { for x := bounds.Min.X; x < bounds.Max.X; x++ { r, g, b, _ := src.At(x, y).RGBA() if r == 0xFFFF && g == 0xFFFF && b == 0xFFFF { dest.Set(x, y, color.NRGBA{0, 0, 0, 0}) } else { dest.Set(x, y, src.At(x, y)) } } } n.Lock() png.Encode(n.cache, dest) n.Unlock() return nil } func main() { flag.Parse() hostname, err := os.Hostname() if err != nil { log.Fatal("problem getting hostname:", err) } log.Printf("serving on: http://%s:%d/", hostname, *port) addr := fmt.Sprintf(":%d", *port) n := NewNow(context.Background(), *freq) http.Handle("/", n) err = http.ListenAndServe(addr, nil) if err != nil { log.Fatal(err) } }