package main import ( "errors" "fmt" "log" "os" "strconv" "strings" "sync/atomic" "time" "git.ophivana.moe/cat/rpcfetch" ) var ( pidS = strconv.Itoa(os.Getpid()) hostname string launchTime = time.Now().Unix() replCounter atomic.Uint64 ) var substitute = map[string]func(s *applyState) string{ "pid": func(_ *applyState) string { return pidS }, "hostname": func(_ *applyState) string { return hostname }, "1min": func(s *applyState) string { s.populateLoadavg() return s.loadavg[0] }, "5min": func(s *applyState) string { s.populateLoadavg() return s.loadavg[1] }, "15min": func(s *applyState) string { s.populateLoadavg() return s.loadavg[2] }, "used": func(s *applyState) string { s.populateMem() return fmt.Sprintf("%.1f GiB", float64(s.mem[1]-s.mem[0])/(1<<20)) }, "total": func(s *applyState) string { s.populateMem() return fmt.Sprintf("%.1f GiB", float64(s.mem[1])/(1<<20)) }, } type applyState struct { loadavg *[3]string mem *[2]int } func (s *applyState) replace(t string) string { for k, f := range substitute { t = strings.ReplaceAll(t, "%"+k, f(s)) go func() { replCounter.Add(uint64(len(t))) statusReplaces.SetText(strconv.Itoa(int(replCounter.Load())) + " bytes") }() } return t } func apply() { prof := conf.profile() // open new client if ID changes if d.ID() != prof.ID { // close previous client if err := d.Close(); err != nil { log.Fatalf("error closing previous client: %s", err) } // open new client d = rpcfetch.New(prof.ID) } confLock.RLock() act := *prof.Activity s := &applyState{} act.State = s.replace(act.State) act.Details = s.replace(act.Details) act.Timestamps = &rpcfetch.ActivityTimestamps{Start: &launchTime} act.Assets = &rpcfetch.ActivityAssets{ LargeImage: act.Assets.LargeImage, LargeText: s.replace(act.Assets.LargeText), SmallImage: act.Assets.SmallImage, SmallText: s.replace(act.Assets.SmallText), } confLock.RUnlock() if nonce, err := retry(&act, s); err != nil { log.Fatalf("error setting activity: %s", err) } else { log.Printf("activity updated with nonce %s", nonce) } } func retry(act *rpcfetch.Activity, s *applyState) (string, error) { try: nonce, err := d.SetActivity(act) if errors.Is(err, rpcfetch.ErrAgain) { failureState(false) log.Println("retrying in 5 seconds...") time.Sleep(5 * time.Second) goto try } failureState(true) // update GUI updateClientInfo(s) return nonce, err }