timer/main.go

108 lines
2.2 KiB
Go

package main
import (
"context"
"flag"
"fmt"
"log"
"net/http"
"net/url"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/gen2brain/beeep"
"github.com/schollz/progressbar/v3"
)
func main() {
ctx, can := signal.NotifyContext(context.Background(), syscall.SIGINT)
defer can()
fs := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
gcal := fs.Bool("gcal", false, "wait for google cal events")
ntfy := fs.String("http", "", "curl -X POST -d YO")
if err := fs.Parse(os.Args[1:]); err != nil {
panic(err)
}
if *gcal {
if err := alertGCal(ctx, *ntfy); err != nil {
panic(err)
}
} else {
if err := alertAfter(ctx, *ntfy, fs.Args()[0]); err != nil {
panic(err)
}
}
}
func alertGCal(ctx context.Context, ntfy string) error {
gcal := NewGCal()
if err := gcal.Login(ctx); err != nil {
return err
}
events, err := gcal.EventsToday(ctx)
if err != nil {
return err
}
for _, event := range events {
until := event.Time.Add(-1 * time.Minute)
if until.Sub(time.Now()) < 1 {
continue
}
log.Println("alerting about", event.Name, "at", until.Format("15:04"), "(", time.Until(until), ")")
if err := alertAt(ctx, ntfy, until, event.Name, until.Format("15:04")); err != nil {
return err
}
}
return nil
}
func alertAfter(ctx context.Context, ntfy string, duration string) error {
d, err := time.ParseDuration(duration)
if err != nil {
return err
}
return alertAt(ctx, ntfy, time.Now().Add(d), os.Args[0], "alerting after "+d.String())
}
func alertAt(ctx context.Context, ntfy string, deadline time.Time, title, msg string) error {
n := int64(time.Until(deadline) / time.Second)
bar := progressbar.Default(n)
c := time.NewTicker(time.Second)
defer c.Stop()
func() {
ctx, can := context.WithDeadline(ctx, deadline)
defer can()
for {
select {
case <-c.C:
case <-ctx.Done():
return
}
bar.Add(1)
}
}()
bar.Finish()
bar.Exit()
if err := ctx.Err(); err == nil {
beeep.Alert(title, msg, "/dev/null")
if ntfy != "" {
u, _ := url.Parse(ntfy)
req, _ := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(fmt.Sprintf("%s: %s", title, msg)))
req = req.WithContext(ctx)
http.DefaultClient.Do(req)
}
}
return ctx.Err()
}