rpcfetch: add template program
Since documentation does not yet exist a template program is added showcasing the intended usage of this library. A configuration interface will be added in a future commit. Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
This commit is contained in:
parent
802ad250ca
commit
2c859c1a46
112
fetch/apply.go
Normal file
112
fetch/apply.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/gob"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.ophivana.moe/cat/rpcfetch"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
pidS = strconv.Itoa(os.Getpid())
|
||||||
|
launchTime = time.Now().Unix()
|
||||||
|
)
|
||||||
|
|
||||||
|
var substitute = map[string]func(s *applyState) string{
|
||||||
|
"pid": func(_ *applyState) string {
|
||||||
|
return pidS
|
||||||
|
},
|
||||||
|
"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))
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func apply() {
|
||||||
|
act := *conf.Activity
|
||||||
|
s := &applyState{}
|
||||||
|
|
||||||
|
act.State = s.replace(act.State)
|
||||||
|
act.Details = s.replace(act.Details)
|
||||||
|
act.Assets = &rpcfetch.ActivityAssets{
|
||||||
|
LargeImage: act.Assets.LargeImage,
|
||||||
|
LargeText: s.replace(act.Assets.LargeText),
|
||||||
|
SmallImage: act.Assets.SmallImage,
|
||||||
|
SmallText: s.replace(act.Assets.SmallText),
|
||||||
|
}
|
||||||
|
|
||||||
|
if nonce, err := retry(&act); err != nil {
|
||||||
|
log.Fatalf("error setting activity: %s", err)
|
||||||
|
} else {
|
||||||
|
log.Printf("activity updated with nonce %s", nonce)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func save() {
|
||||||
|
nf, err := os.CreateTemp(path.Dir(confPath), ".rpcfetch.conf.*")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error creating temporary configuration file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = gob.NewEncoder(nf).Encode(defaultConfig); err != nil {
|
||||||
|
fmt.Printf("error writing configuration file: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = nf.Close(); err != nil {
|
||||||
|
log.Fatalf("error closing temporary configuration file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = os.Rename(nf.Name(), confPath); err != nil {
|
||||||
|
log.Fatalf("error renaming configuration file: %s", err)
|
||||||
|
}
|
||||||
|
log.Printf("saved configuration to %s", confPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func retry(act *rpcfetch.Activity) (string, error) {
|
||||||
|
try:
|
||||||
|
nonce, err := d.SetActivity(act)
|
||||||
|
if errors.Is(err, rpcfetch.ErrAgain) {
|
||||||
|
log.Println("retrying in 5 seconds...")
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
goto try
|
||||||
|
}
|
||||||
|
|
||||||
|
return nonce, err
|
||||||
|
}
|
74
fetch/apply_linux.go
Normal file
74
fetch/apply_linux.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *applyState) populateLoadavg() {
|
||||||
|
if s.loadavg != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, err := os.ReadFile("/proc/loadavg"); err != nil {
|
||||||
|
log.Printf("error reading loadavg: %s", err)
|
||||||
|
} else {
|
||||||
|
res := strings.SplitN(string(b), " ", 4)
|
||||||
|
if len(res) >= 3 {
|
||||||
|
s.loadavg = (*[3]string)(res[:3])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("unexpected loadavg raw: %s", string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
s.loadavg = &[3]string{"?", "?", "?"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *applyState) populateMem() {
|
||||||
|
if s.mem != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// failure defaults
|
||||||
|
s.mem = &[2]int{-1, -1}
|
||||||
|
|
||||||
|
if f, err := os.Open("/proc/meminfo"); err != nil {
|
||||||
|
log.Printf("error reading meminfo: %s", err)
|
||||||
|
} else {
|
||||||
|
defer func() {
|
||||||
|
if err = f.Close(); err != nil {
|
||||||
|
log.Fatalf("error closing meminfo: %s", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
p := bufio.NewScanner(f)
|
||||||
|
for p.Scan() {
|
||||||
|
res := strings.SplitN(strings.ReplaceAll(p.Text(), " ", ""), ":", 2)
|
||||||
|
if len(res) != 2 {
|
||||||
|
fmt.Printf("unexpected meminfo line: %s", p.Text())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
format := func(r string) int {
|
||||||
|
var v int
|
||||||
|
if v, err = strconv.Atoi(r[:len(r)-2]); err != nil {
|
||||||
|
log.Printf("error parsing meminfo value: %s", err)
|
||||||
|
v = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
switch res[0] {
|
||||||
|
case "MemFree":
|
||||||
|
s.mem[0] = format(res[1])
|
||||||
|
case "MemTotal":
|
||||||
|
s.mem[1] = format(res[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
fetch/config.go
Normal file
26
fetch/config.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.ophivana.moe/cat/rpcfetch"
|
||||||
|
)
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
ID string
|
||||||
|
Activity *rpcfetch.Activity
|
||||||
|
}
|
||||||
|
|
||||||
|
// sample config so the program works out of the box
|
||||||
|
var defaultConfig = config{
|
||||||
|
ID: "1252927154480611351",
|
||||||
|
Activity: &rpcfetch.Activity{
|
||||||
|
State: "%used / %total",
|
||||||
|
Details: "%1min %5min %15min",
|
||||||
|
Timestamps: &rpcfetch.ActivityTimestamps{Start: &launchTime},
|
||||||
|
Assets: &rpcfetch.ActivityAssets{
|
||||||
|
LargeImage: "yorha",
|
||||||
|
LargeText: "1252927154480611351",
|
||||||
|
SmallImage: "flan",
|
||||||
|
SmallText: "PID: %pid",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
63
fetch/main.go
Normal file
63
fetch/main.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/gob"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.ophivana.moe/cat/rpcfetch"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
d *rpcfetch.Client
|
||||||
|
|
||||||
|
conf config
|
||||||
|
confPath string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.StringVar(&confPath, "conf", "rpcfetch.conf", "path to rpcfetch configuration file")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if cf, err := os.Open(confPath); err != nil {
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
fmt.Printf("error opening configuration file: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// use defaults
|
||||||
|
log.Print("configuration file does not exist, using defaults")
|
||||||
|
conf = defaultConfig
|
||||||
|
} else {
|
||||||
|
// decode from existing file
|
||||||
|
if err = gob.NewDecoder(cf).Decode(&conf); err != nil {
|
||||||
|
fmt.Printf("error reading configuration: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if err = cf.Close(); err != nil {
|
||||||
|
log.Fatalf("error closing configuration file: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d = rpcfetch.New(conf.ID)
|
||||||
|
defer func() {
|
||||||
|
if err := d.Close(); err != nil {
|
||||||
|
log.Printf("error closing client: %s", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// restore activity from configuration
|
||||||
|
apply()
|
||||||
|
|
||||||
|
// update periodically
|
||||||
|
for {
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user