ZeroBot-Plugin/plugin/aifalse/main.go
2024-08-05 20:15:08 +08:00

710 lines
21 KiB
Go

// Package aifalse 暂时只有服务器监控
package aifalse
import (
"bytes"
"errors"
"image"
"image/color"
"math"
"runtime"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"unsafe"
"github.com/FloatTech/AnimeAPI/bilibili"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/floatbox/web"
"github.com/FloatTech/gg"
"github.com/FloatTech/imgfactory"
"github.com/FloatTech/rendercard"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/img/text"
"github.com/disintegration/imaging"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/host"
"github.com/shirou/gopsutil/v3/mem"
"github.com/sirupsen/logrus"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"github.com/FloatTech/ZeroBot-Plugin/kanban/banner"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
const (
backgroundURL = "https://iw233.cn/api.php?sort=mp"
referer = "https://weibo.com/"
)
var (
boottime = time.Now()
bgdata *[]byte
bgcount uintptr
isday bool
lightcolor = [3][4]uint8{{255, 70, 0, 255}, {255, 165, 0, 255}, {145, 240, 145, 255}}
darkcolor = [3][4]uint8{{215, 50, 0, 255}, {205, 135, 0, 255}, {115, 200, 115, 255}}
)
func init() { // 插件主体
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "自检, 全局限速",
Help: "- 查询计算机当前活跃度: [检查身体 | 自检 | 启动自检 | 系统状态]\n" +
"- 设置默认限速为每 m [分钟 | 秒] n 次触发",
})
c, ok := control.Lookup("aifalse")
if !ok {
panic("register aifalse error")
}
m := c.GetData(0)
n := (m >> 16) & 0xffff
m &= 0xffff
if m != 0 || n != 0 {
ctxext.SetDefaultLimiterManagerParam(time.Duration(m)*time.Second, int(n))
logrus.Infoln("设置默认限速为每", m, "秒触发", n, "次")
}
engine.OnFullMatchGroup([]string{"检查身体", "自检", "启动自检", "系统状态"}, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
now := time.Now().Hour()
isday = now > 7 && now < 19
botrunstatus := ctx.CallAction("get_status", zero.Params{}).Data
botverisoninfo := ctx.GetVersionInfo()
sb := &strings.Builder{}
sb.WriteString("在线(")
sb.WriteString(botverisoninfo.Get("app_name").String())
sb.WriteString("-")
sb.WriteString(botverisoninfo.Get("app_version").String())
sb.WriteString(") | 收")
sb.WriteString(botrunstatus.Get("stat").Get("message_received").String())
sb.WriteString(" | 发")
sb.WriteString(botrunstatus.Get("stat").Get("message_sent").String())
sb.WriteString(" | 群")
sb.WriteString(strconv.Itoa(len(ctx.GetGroupList().Array())))
sb.WriteString(" | 好友")
sb.WriteString(strconv.Itoa(len(ctx.GetFriendList().Array())))
img, err := drawstatus(ctx.State["manager"].(*ctrl.Control[*zero.Ctx]), ctx.Event.SelfID, zero.BotConfig.NickName[0], sb.String())
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
sendimg, err := imgfactory.ToBytes(img)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if id := ctx.SendChain(message.ImageBytes(sendimg)); id.ID() == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
})
engine.OnRegex(`^设置默认限速为每\s*(\d+)\s*(分钟|秒)\s*(\d+)\s*次触发$`, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if !ok {
ctx.SendChain(message.Text("ERROR: no such plugin"))
return
}
m, err := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if ctx.State["regex_matched"].([]string)[2] == "分钟" {
m *= 60
}
if m >= 65536 || m <= 0 {
ctx.SendChain(message.Text("ERROR: interval too big"))
return
}
n, err := strconv.ParseInt(ctx.State["regex_matched"].([]string)[3], 10, 64)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if n >= 65536 || n <= 0 {
ctx.SendChain(message.Text("ERROR: burst too big"))
return
}
ctxext.SetDefaultLimiterManagerParam(time.Duration(m)*time.Second, int(n))
err = c.SetData(0, (m&0xffff)|((n<<16)&0xffff0000))
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("设置默认限速为每", m, "秒触发", n, "次"))
})
}
func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string, botrunstatus string) (sendimg image.Image, err error) {
diskstate, err := diskstate()
if err != nil {
return
}
diskcardh := 40 + (20+50)*len(diskstate) + 40 - 20
moreinfo, err := moreinfo(m)
if err != nil {
return
}
moreinfocardh := 30 + (20+32*72/96)*len(moreinfo) + 30 - 20
basicstate, err := basicstate()
if err != nil {
return
}
dldata := (*[]byte)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&bgdata))))
if dldata == (*[]byte)(nil) || uintptr(time.Since(boottime).Hours()/24) >= atomic.LoadUintptr(&bgcount) {
url, err1 := bilibili.GetRealURL(backgroundURL)
if err1 != nil {
return nil, err1
}
data, err1 := web.RequestDataWith(web.NewDefaultClient(), url, "", referer, "", nil)
if err1 != nil {
return nil, err1
}
atomic.AddUintptr(&bgcount, 1)
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&bgdata)), unsafe.Pointer(&data))
dldata = &data
}
data := *dldata
back, _, err := image.Decode(bytes.NewReader(data))
if err != nil {
return
}
data, err = web.GetData("https://q4.qlogo.cn/g?b=qq&nk=" + strconv.FormatInt(uid, 10) + "&s=640")
if err != nil {
return
}
avatar, _, err := image.Decode(bytes.NewReader(data))
if err != nil {
return
}
avatarf := imgfactory.Size(avatar, 200, 200)
fontbyte, err := file.GetLazyData(text.GlowSansFontFile, control.Md5File, true)
if err != nil {
return
}
canvas := gg.NewContext(1280, 70+250+40+380+diskcardh+40+moreinfocardh+40+70)
bh, bw, ch, cw := float64(back.Bounds().Dy()), float64(back.Bounds().Dx()), float64(canvas.H()), float64(canvas.W())
if bh/bw < ch/cw {
back = imgfactory.Size(back, int(bw*ch/bh), int(bh*ch/bh)).Image()
canvas.DrawImageAnchored(back, canvas.W()/2, canvas.H()/2, 0.5, 0.5)
} else {
back = imgfactory.Size(back, int(bw*cw/bw), int(bh*cw/bw)).Image()
canvas.DrawImage(back, 0, 0)
}
var blurback image.Image
bwg := &sync.WaitGroup{}
bwg.Add(1)
go func() {
defer bwg.Done()
blurback = imaging.Blur(canvas.Image(), 8)
}()
if !isday {
canvas.SetRGBA255(0, 0, 0, 50)
canvas.DrawRectangle(0, 0, cw, ch)
canvas.Fill()
}
wg := &sync.WaitGroup{}
wg.Add(5)
cardw := canvas.W() - 70 - 70
titlecardh := 250
basiccardh := 380
var titleimg, basicimg, diskimg, moreinfoimg, shadowimg image.Image
go func() {
defer wg.Done()
titlecard := gg.NewContext(cardw, titlecardh)
bwg.Wait()
titlecard.DrawImage(blurback, -70, -70)
titlecard.DrawRoundedRectangle(1, 1, float64(titlecard.W()-1*2), float64(titlecardh-1*2), 16)
titlecard.SetLineWidth(3)
titlecard.SetColor(colorswitch(100))
titlecard.StrokePreserve()
titlecard.SetColor(colorswitch(140))
titlecard.Fill()
titlecard.DrawImage(avatarf.Circle(0).Image(), (titlecardh-avatarf.H())/2, (titlecardh-avatarf.H())/2)
err = titlecard.ParseFontFace(fontbyte, 72)
if err != nil {
return
}
fw, _ := titlecard.MeasureString(botname)
titlecard.SetColor(fontcolorswitch())
titlecard.DrawStringAnchored(botname, float64(titlecardh)+fw/2, float64(titlecardh)*0.5/2, 0.5, 0.5)
err = titlecard.ParseFontFace(fontbyte, 24)
if err != nil {
return
}
titlecard.SetColor(fontcolorswitch())
titlecard.NewSubPath()
titlecard.MoveTo(float64(titlecardh), float64(titlecardh)/2)
titlecard.LineTo(float64(titlecard.W()-titlecardh), float64(titlecardh)/2)
titlecard.Stroke()
fw, _ = titlecard.MeasureString(botrunstatus)
titlecard.DrawStringAnchored(botrunstatus, float64(titlecardh)+fw/2, float64(titlecardh)*(0.5+0.25/2), 0.5, 0.5)
brt, err := botruntime()
if err != nil {
return
}
fw, _ = titlecard.MeasureString(brt)
titlecard.DrawStringAnchored(brt, float64(titlecardh)+fw/2, float64(titlecardh)*(0.5+0.5/2), 0.5, 0.5)
bs, err := botstatus()
if err != nil {
return
}
fw, _ = titlecard.MeasureString(bs)
titlecard.DrawStringAnchored(bs, float64(titlecardh)+fw/2, float64(titlecardh)*(0.5+0.75/2), 0.5, 0.5)
titleimg = rendercard.Fillet(titlecard.Image(), 16)
}()
go func() {
defer wg.Done()
basiccard := gg.NewContext(cardw, basiccardh)
bwg.Wait()
basiccard.DrawImage(blurback, -70, -70-titlecardh-40)
basiccard.DrawRoundedRectangle(1, 1, float64(basiccard.W()-1*2), float64(basiccardh-1*2), 16)
basiccard.SetLineWidth(3)
basiccard.SetColor(colorswitch(100))
basiccard.StrokePreserve()
basiccard.SetColor(colorswitch(140))
basiccard.Fill()
bslen := len(basicstate)
for i, v := range basicstate {
offset := float64(i) * ((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1) + 200)
basiccard.SetRGBA255(57, 57, 57, 255)
if isday {
basiccard.SetRGBA255(235, 235, 235, 255)
}
basiccard.DrawCircle((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200/2, 100)
basiccard.Fill()
colors := darkcolor
if isday {
colors = lightcolor
}
switch {
case v.precent > 90:
basiccard.SetColor(slice2color(colors[0]))
case v.precent > 70:
basiccard.SetColor(slice2color(colors[1]))
default:
basiccard.SetColor(slice2color(colors[2]))
}
basiccard.NewSubPath()
basiccard.MoveTo((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200/2)
basiccard.DrawEllipticalArc((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200/2, 100, 100, -0.5*math.Pi, -0.5*math.Pi+2*v.precent*0.01*math.Pi)
basiccard.Fill()
basiccard.SetColor(colorswitch(255))
basiccard.DrawCircle((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200/2, 80)
basiccard.Fill()
err = basiccard.ParseFontFace(fontbyte, 42)
if err != nil {
return
}
basiccard.SetRGBA255(213, 213, 213, 255)
basiccard.DrawStringAnchored(strconv.FormatFloat(v.precent, 'f', 0, 64)+"%", (float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200/2, 0.5, 0.5)
basiccard.SetColor(fontcolorswitch())
_, fw := basiccard.MeasureString(v.name)
basiccard.DrawStringAnchored(v.name, (float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200+15+basiccard.FontHeight()/2, 0.5, 0.5)
err = basiccard.ParseFontFace(fontbyte, 20)
if err != nil {
return
}
basiccard.SetColor(fontcolorswitch())
textoffsety := basiccard.FontHeight() + 10
for k, s := range v.text {
basiccard.DrawStringAnchored(s, (float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200+15+fw+15+basiccard.FontHeight()/2+float64(k)*textoffsety, 0.5, 0.5)
}
}
basicimg = rendercard.Fillet(basiccard.Image(), 16)
}()
go func() {
defer wg.Done()
diskcard := gg.NewContext(cardw, diskcardh)
bwg.Wait()
diskcard.DrawImage(blurback, -70, -70-titlecardh-40-basiccardh-40)
diskcard.DrawRoundedRectangle(1, 1, float64(diskcard.W()-1*2), float64(diskcardh-1*2), 16)
diskcard.SetLineWidth(3)
diskcard.SetColor(colorswitch(100))
diskcard.StrokePreserve()
diskcard.SetColor(colorswitch(140))
diskcard.Fill()
err = diskcard.ParseFontFace(fontbyte, 32)
if err != nil {
return
}
dslen := len(diskstate)
if dslen == 1 {
diskcard.SetRGBA255(57, 57, 57, 255)
if isday {
diskcard.SetRGBA255(192, 192, 192, 255)
}
diskcard.DrawRoundedRectangle(40, 40, float64(diskcard.W())-40-100, 50, 12)
diskcard.ClipPreserve()
diskcard.Fill()
colors := darkcolor
if isday {
colors = lightcolor
}
switch {
case diskstate[0].precent > 90:
diskcard.SetColor(slice2color(colors[0]))
case diskstate[0].precent > 70:
diskcard.SetColor(slice2color(colors[1]))
default:
diskcard.SetColor(slice2color(colors[2]))
}
diskcard.DrawRoundedRectangle(40, 40, (float64(diskcard.W())-40-100)*diskstate[0].precent*0.01, 50, 12)
diskcard.Fill()
diskcard.ResetClip()
diskcard.SetColor(fontcolorswitch())
fw, _ := diskcard.MeasureString(diskstate[0].name)
fw1, _ := diskcard.MeasureString(diskstate[0].text[0])
diskcard.DrawStringAnchored(diskstate[0].name, 40+10+fw/2, 40+50/2, 0.5, 0.5)
diskcard.DrawStringAnchored(diskstate[0].text[0], (float64(diskcard.W())-100-10)-fw1/2, 40+50/2, 0.5, 0.5)
diskcard.DrawStringAnchored(strconv.FormatFloat(diskstate[0].precent, 'f', 0, 64)+"%", float64(diskcard.W())-100/2, 40+50/2, 0.5, 0.5)
} else {
for i, v := range diskstate {
offset := float64(i)*(50+20) - 20
diskcard.SetRGBA255(57, 57, 57, 255)
if isday {
diskcard.SetRGBA255(192, 192, 192, 255)
}
diskcard.DrawRoundedRectangle(40, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+offset, float64(diskcard.W())-40-100, 50, 12)
diskcard.Fill()
colors := darkcolor
if isday {
colors = lightcolor
}
switch {
case v.precent > 90:
diskcard.SetColor(slice2color(colors[0]))
case v.precent > 70:
diskcard.SetColor(slice2color(colors[1]))
default:
diskcard.SetColor(slice2color(colors[2]))
}
diskcard.DrawRoundedRectangle(40, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+offset, (float64(diskcard.W())-40-100)*v.precent*0.01, 50, 12)
diskcard.Fill()
diskcard.SetColor(fontcolorswitch())
fw, _ := diskcard.MeasureString(v.name)
fw1, _ := diskcard.MeasureString(v.text[0])
diskcard.DrawStringAnchored(v.name, 40+10+fw/2, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+50/2+offset, 0.5, 0.5)
diskcard.DrawStringAnchored(v.text[0], (float64(diskcard.W())-100-10)-fw1/2, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+50/2+offset, 0.5, 0.5)
diskcard.DrawStringAnchored(strconv.FormatFloat(v.precent, 'f', 0, 64)+"%", float64(diskcard.W())-100/2, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+50/2+offset, 0.5, 0.5)
}
}
diskimg = rendercard.Fillet(diskcard.Image(), 16)
}()
go func() {
defer wg.Done()
moreinfocard := gg.NewContext(cardw, moreinfocardh)
bwg.Wait()
moreinfocard.DrawImage(blurback, -70, -70-titlecardh-40-basiccardh-40-diskcardh-40)
moreinfocard.DrawRoundedRectangle(1, 1, float64(moreinfocard.W()-1*2), float64(moreinfocard.H()-1*2), 16)
moreinfocard.SetLineWidth(3)
moreinfocard.SetColor(colorswitch(100))
moreinfocard.StrokePreserve()
moreinfocard.SetColor(colorswitch(140))
moreinfocard.Fill()
err = moreinfocard.ParseFontFace(fontbyte, 32)
if err != nil {
return
}
milen := len(moreinfo)
for i, v := range moreinfo {
offset := float64(i)*(20+moreinfocard.FontHeight()) - 20
moreinfocard.SetColor(fontcolorswitch())
fw, _ := moreinfocard.MeasureString(v.name)
fw1, _ := moreinfocard.MeasureString(v.text[0])
moreinfocard.DrawStringAnchored(v.name, 20+fw/2, 30+(float64(moreinfocardh-30*2)-moreinfocard.FontHeight()*float64(milen))/float64(milen-1)+moreinfocard.FontHeight()/2+offset, 0.5, 0.5)
moreinfocard.DrawStringAnchored(v.text[0], float64(moreinfocard.W())-20-fw1/2, 30+(float64(moreinfocardh-30*2)-moreinfocard.FontHeight()*float64(milen))/float64(milen-1)+moreinfocard.FontHeight()/2+offset, 0.5, 0.5)
}
moreinfoimg = rendercard.Fillet(moreinfocard.Image(), 16)
}()
go func() {
defer wg.Done()
shadow := gg.NewContext(canvas.W(), canvas.H())
shadow.SetRGBA255(0, 0, 0, 100)
shadow.SetLineWidth(12)
shadow.DrawRoundedRectangle(70, 70, float64(cardw), float64(titlecardh), 16)
shadow.Stroke()
shadow.DrawRoundedRectangle(70, float64(70+titlecardh+40), float64(cardw), float64(basiccardh), 16)
shadow.Stroke()
shadow.DrawRoundedRectangle(70, float64(70+titlecardh+40+basiccardh+40), float64(cardw), float64(diskcardh), 16)
shadow.Stroke()
shadow.DrawRoundedRectangle(70, float64(70+titlecardh+40+basiccardh+40+diskcardh+40), float64(cardw), float64(moreinfocardh), 16)
shadow.Stroke()
shadowimg = imaging.Blur(shadow.Image(), 24)
}()
wg.Wait()
if shadowimg == nil || titleimg == nil || basicimg == nil || diskimg == nil || moreinfoimg == nil {
err = errors.New("图片渲染失败")
return
}
canvas.DrawImage(shadowimg, 0, 0)
canvas.DrawImage(titleimg, 70, 70)
canvas.DrawImage(basicimg, 70, 70+titlecardh+40)
canvas.DrawImage(diskimg, 70, 70+titlecardh+40+basiccardh+40)
canvas.DrawImage(moreinfoimg, 70, 70+titlecardh+40+basiccardh+40+diskcardh+40)
err = canvas.ParseFontFace(fontbyte, 28)
if err != nil {
return
}
canvas.SetRGBA255(0, 0, 0, 255)
canvas.DrawStringAnchored("Created By ZeroBot-Plugin "+banner.Version, float64(canvas.W())/2+3, float64(canvas.H())-70/2+3, 0.5, 0.5)
canvas.SetRGBA255(255, 255, 255, 255)
canvas.DrawStringAnchored("Created By ZeroBot-Plugin "+banner.Version, float64(canvas.W())/2, float64(canvas.H())-70/2, 0.5, 0.5)
sendimg = canvas.Image()
return
}
func botruntime() (string, error) {
hostinfo, err := host.Info()
if err != nil {
return "", err
}
t := &strings.Builder{}
t.WriteString("ZeroBot-Plugin 已运行 ")
t.WriteString(strconv.FormatInt((time.Now().Unix()-boottime.Unix())/86400, 10))
t.WriteString(" 天 ")
t.WriteString(time.Unix(time.Now().Unix()-boottime.Unix(), 0).UTC().Format("15:04:05"))
t.WriteString(" | 系统运行 ")
t.WriteString(strconv.FormatInt(int64(hostinfo.Uptime)/86400, 10))
t.WriteString(" 天 ")
t.WriteString(time.Unix(int64(hostinfo.Uptime), 0).UTC().Format("15:04:05"))
return t.String(), nil
}
func botstatus() (string, error) {
hostinfo, err := host.Info()
if err != nil {
return "", err
}
t := &strings.Builder{}
t.WriteString(time.Now().Format("2006-01-02 15:04:05"))
t.WriteString(" | Compiled by ")
t.WriteString(runtime.Version())
t.WriteString(" | ")
t.WriteString(cases.Title(language.English).String(hostinfo.OS))
t.WriteString(" ")
t.WriteString(runtime.GOARCH)
return t.String(), nil
}
type status struct {
precent float64
name string
text []string
}
func basicstate() (stateinfo [3]*status, err error) {
percent, err := cpu.Percent(time.Second, true)
if err != nil {
return
}
cpuinfo, err := cpu.Info()
if err != nil {
return
}
cpucore, err := cpu.Counts(false)
if err != nil {
return
}
cputhread, err := cpu.Counts(true)
if err != nil {
return
}
cores := strconv.Itoa(cpucore) + "C" + strconv.Itoa(cputhread) + "T"
times := "最大 " + strconv.FormatFloat(cpuinfo[0].Mhz/1000, 'f', 1, 64) + "Ghz"
stateinfo[0] = &status{
precent: math.Round(percent[0]),
name: "CPU",
text: []string{cores, times},
}
raminfo, err := mem.VirtualMemory()
if err != nil {
return
}
total := "总共 " + storagefmt(float64(raminfo.Total))
used := "已用 " + storagefmt(float64(raminfo.Used))
free := "剩余 " + storagefmt(float64(raminfo.Free))
stateinfo[1] = &status{
precent: math.Round(raminfo.UsedPercent),
name: "RAM",
text: []string{total, used, free},
}
swapinfo, err := mem.SwapMemory()
if err != nil {
return
}
total = "总共 " + storagefmt(float64(swapinfo.Total))
used = "已用 " + storagefmt(float64(swapinfo.Used))
free = "剩余 " + storagefmt(float64(swapinfo.Free))
stateinfo[2] = &status{
precent: math.Round(swapinfo.UsedPercent),
name: "SWAP",
text: []string{total, used, free},
}
return
}
func storagefmt(num float64) string {
if num /= 1024; num < 1 {
return strconv.FormatFloat(num*1024, 'f', 2, 64) + "B"
}
if num /= 1024; num < 1 {
return strconv.FormatFloat(num*1024, 'f', 2, 64) + "KB"
}
if num /= 1024; num < 1 {
return strconv.FormatFloat(num*1024, 'f', 2, 64) + "MB"
}
if num /= 1024; num < 1 {
return strconv.FormatFloat(num*1024, 'f', 2, 64) + "GB"
}
return strconv.FormatFloat(num, 'f', 2, 64) + "TB"
}
func diskstate() (stateinfo []*status, err error) {
parts, err := disk.Partitions(false)
if err != nil {
return
}
stateinfo = make([]*status, 0, len(parts))
for _, v := range parts {
mp := v.Mountpoint
if strings.HasPrefix(mp, "/snap/") || strings.HasPrefix(mp, "/apex/") {
continue
}
diskusage, err := disk.Usage(mp)
if err != nil {
continue
}
stateinfo = append(stateinfo, &status{
precent: math.Round(diskusage.UsedPercent),
name: mp,
text: []string{storagefmt(float64(diskusage.Used)) + " / " + storagefmt(float64(diskusage.Total))},
})
}
return stateinfo, nil
}
func moreinfo(m *ctrl.Control[*zero.Ctx]) (stateinfo []*status, err error) {
var mems runtime.MemStats
runtime.ReadMemStats(&mems)
fmtmem := storagefmt(float64(mems.Sys))
hostinfo, err := host.Info()
if err != nil {
return
}
cpuinfo, err := cpu.Info()
if err != nil {
return
}
count := len(m.Manager.M)
stateinfo = []*status{
{name: "OS", text: []string{hostinfo.Platform}},
{name: "CPU", text: []string{strings.TrimSpace(cpuinfo[0].ModelName)}},
{name: "Version", text: []string{hostinfo.PlatformVersion}},
{name: "Plugin", text: []string{"共 " + strconv.Itoa(count) + " 个"}},
{name: "Memory", text: []string{"已用 " + fmtmem}},
}
return
}
func colorswitch(a uint8) color.Color {
if isday {
return color.NRGBA{255, 255, 255, a}
}
return color.NRGBA{0, 0, 0, a}
}
func fontcolorswitch() color.Color {
if isday {
return color.NRGBA{30, 30, 30, 255}
}
return color.NRGBA{235, 235, 235, 255}
}
func slice2color(c [4]uint8) color.Color {
return color.NRGBA{c[0], c[1], c[2], c[3]}
}