mirror of
https://github.com/FloatTech/ZeroBot-Plugin.git
synced 2025-12-18 20:50:12 +08:00
Some checks failed
自动更新 nix 依赖 / gomod2nix update (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
723 lines
22 KiB
Go
723 lines
22 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"
|
|
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/v4/cpu"
|
|
"github.com/shirou/gopsutil/v4/disk"
|
|
"github.com/shirou/gopsutil/v4/host"
|
|
"github.com/shirou/gopsutil/v4/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://pic.re/image"
|
|
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.DrawRoundedRectangle(1, 1, float64(titlecard.W()-1*2), float64(titlecardh-1*2), 16)
|
|
titlecard.ClipPreserve()
|
|
titlecard.DrawImage(blurback, -70, -70)
|
|
titlecard.SetColor(colorswitch(140))
|
|
titlecard.FillPreserve()
|
|
|
|
titlecard.SetLineWidth(3)
|
|
titlecard.SetColor(colorswitch(100))
|
|
titlecard.ResetClip()
|
|
titlecard.Stroke()
|
|
|
|
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 = titlecard.Image()
|
|
}()
|
|
go func() {
|
|
defer wg.Done()
|
|
basiccard := gg.NewContext(cardw, basiccardh)
|
|
bwg.Wait()
|
|
|
|
basiccard.DrawRoundedRectangle(1, 1, float64(basiccard.W()-1*2), float64(basiccardh-1*2), 16)
|
|
basiccard.ClipPreserve()
|
|
basiccard.DrawImage(blurback, -70, -70-titlecardh-40)
|
|
basiccard.SetColor(colorswitch(140))
|
|
basiccard.FillPreserve()
|
|
|
|
basiccard.SetLineWidth(3)
|
|
basiccard.SetColor(colorswitch(100))
|
|
basiccard.ResetClip()
|
|
basiccard.Stroke()
|
|
|
|
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 = basiccard.Image()
|
|
}()
|
|
go func() {
|
|
defer wg.Done()
|
|
diskcard := gg.NewContext(cardw, diskcardh)
|
|
bwg.Wait()
|
|
|
|
diskcard.DrawRoundedRectangle(1, 1, float64(diskcard.W()-1*2), float64(diskcardh-1*2), 16)
|
|
diskcard.ClipPreserve()
|
|
diskcard.DrawImage(blurback, -70, -70-titlecardh-40-basiccardh-40)
|
|
diskcard.SetColor(colorswitch(140))
|
|
diskcard.FillPreserve()
|
|
|
|
diskcard.SetLineWidth(3)
|
|
diskcard.SetColor(colorswitch(100))
|
|
diskcard.ResetClip()
|
|
diskcard.Stroke()
|
|
|
|
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.ClipPreserve()
|
|
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.ResetClip()
|
|
|
|
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 = diskcard.Image()
|
|
}()
|
|
go func() {
|
|
defer wg.Done()
|
|
moreinfocard := gg.NewContext(cardw, moreinfocardh)
|
|
bwg.Wait()
|
|
|
|
moreinfocard.DrawRoundedRectangle(1, 1, float64(moreinfocard.W()-1*2), float64(moreinfocard.H()-1*2), 16)
|
|
moreinfocard.ClipPreserve()
|
|
moreinfocard.DrawImage(blurback, -70, -70-titlecardh-40-basiccardh-40-diskcardh-40)
|
|
moreinfocard.SetColor(colorswitch(140))
|
|
moreinfocard.FillPreserve()
|
|
|
|
moreinfocard.SetLineWidth(3)
|
|
moreinfocard.SetColor(colorswitch(100))
|
|
moreinfocard.ResetClip()
|
|
moreinfocard.Stroke()
|
|
|
|
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 = moreinfocard.Image()
|
|
}()
|
|
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.Alloc))
|
|
|
|
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]}
|
|
}
|