package main import ( "log" "net/http" "strings" "os" "context" "time" ) // ELIGIBLE_APPOINTMENTS is a comma-separated list of appointment type // labels, matched case-insensitively. func loadEligible() map[string]bool { raw := os.Getenv("ELIGIBLE_APPOINTMENTS") if raw == "" { raw = "senate,recall" } set := map[string]bool{} for _, s := range strings.Split(raw, ",") { if s = strings.ToLower(strings.TrimSpace(s)); s != "" { set[s] = true } } log.Printf("eligible appointment types: %v", set) return set } // get other variables, like api key and webhook secret func main() { secret := os.Getenv("WEBHOOK_SECRET") if secret == "" { log.Fatal("WEBHOOK_SECRET not set") } dbPath := os.Getenv("DB_PATH") if dbPath == "" { dbPath = "worker.db" } store, err := OpenStore(dbPath) if err != nil { log.Fatalf("opening store: %v", err) } // Any run still marked running belongs to a dead process. if err := store.SweepStaleLocks(); err != nil { log.Fatalf("sweeping stale locks: %v", err) } apiKey := os.Getenv("ST_API_KEY") if apiKey == "" { log.Fatal("ST_API_KEY not set") } dryRun := os.Getenv("DRY_RUN") != "" if dryRun { log.Println("DRY_RUN set: no writes will be sent to Solidarity Tech") } srv := NewServer(NewClient(apiKey, dryRun), store, secret, loadEligible()) // Prime the cache: a fresh container starts empty. { ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) if err := srv.SyncRoster(ctx); err != nil { log.Printf("initial sync failed: %v", err) } cancel() } go func() { t := time.NewTicker(time.Hour) defer t.Stop() for range t.C { ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) if err := srv.SyncRoster(ctx); err != nil { log.Printf("scheduled sync failed: %v", err) } cancel() } }() log.Println("listening on :8080") log.Fatal(http.ListenAndServe(":8080", srv.Routes())) }