mirror of
https://github.com/FloatTech/ZeroBot-Plugin.git
synced 2025-12-19 13:59:39 +08:00
aifalse add nightstyle & chess use resvg (#782)
* aifalse add nightstyle & chess use resvg * make lint happy * make lint happy * chore(lint): 改进代码样式 (#25) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
b062198fae
commit
148f271586
2
go.mod
2
go.mod
@ -29,6 +29,7 @@ require (
|
|||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||||
github.com/jinzhu/gorm v1.9.16
|
github.com/jinzhu/gorm v1.9.16
|
||||||
github.com/jozsefsallai/gophersauce v1.0.1
|
github.com/jozsefsallai/gophersauce v1.0.1
|
||||||
|
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5
|
||||||
github.com/lithammer/fuzzysearch v1.1.5
|
github.com/lithammer/fuzzysearch v1.1.5
|
||||||
github.com/mroth/weightedrand v1.0.0
|
github.com/mroth/weightedrand v1.0.0
|
||||||
github.com/notnil/chess v1.9.0
|
github.com/notnil/chess v1.9.0
|
||||||
@ -76,6 +77,7 @@ require (
|
|||||||
github.com/quic-go/qpack v0.4.0 // indirect
|
github.com/quic-go/qpack v0.4.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-20 v0.3.3 // indirect
|
github.com/quic-go/qtls-go1-20 v0.3.3 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||||
|
github.com/tetratelabs/wazero v1.5.0 // indirect
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
github.com/tidwall/pretty v1.2.0 // indirect
|
github.com/tidwall/pretty v1.2.0 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||||
|
|||||||
4
go.sum
4
go.sum
@ -129,6 +129,8 @@ github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
|
|||||||
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/jozsefsallai/gophersauce v1.0.1 h1:BA3ovtQRrAb1qYU9JoRLbDHpxnDunlNcEkEfhCvDDCM=
|
github.com/jozsefsallai/gophersauce v1.0.1 h1:BA3ovtQRrAb1qYU9JoRLbDHpxnDunlNcEkEfhCvDDCM=
|
||||||
github.com/jozsefsallai/gophersauce v1.0.1/go.mod h1:YVEI7djliMTmZ1Vh01YPF8bUHi+oKhe3yXgKf1T49vg=
|
github.com/jozsefsallai/gophersauce v1.0.1/go.mod h1:YVEI7djliMTmZ1Vh01YPF8bUHi+oKhe3yXgKf1T49vg=
|
||||||
|
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5 h1:BXnB1Gz4y/zwQh+ZFNy7rgd+ZfMOrwRr4uZSHEI+ieY=
|
||||||
|
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5/go.mod h1:c9+VS9GaommgIOzNWb5ze4lYwfT8BZ2UDyGiuQTT7yc=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
@ -192,6 +194,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/tetratelabs/wazero v1.5.0 h1:Yz3fZHivfDiZFUXnWMPUoiW7s8tC1sjdBtlJn08qYa0=
|
||||||
|
github.com/tetratelabs/wazero v1.5.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A=
|
||||||
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
|
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
|
||||||
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"image"
|
"image"
|
||||||
|
"image/color"
|
||||||
"math"
|
"math"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -45,9 +46,12 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
boottime = time.Now()
|
boottime = time.Now()
|
||||||
bgdata *[]byte
|
bgdata *[]byte
|
||||||
bgcount uintptr
|
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() { // 插件主体
|
func init() { // 插件主体
|
||||||
@ -70,7 +74,26 @@ func init() { // 插件主体
|
|||||||
}
|
}
|
||||||
engine.OnFullMatchGroup([]string{"检查身体", "自检", "启动自检", "系统状态"}, zero.AdminPermission).SetBlock(true).
|
engine.OnFullMatchGroup([]string{"检查身体", "自检", "启动自检", "系统状态"}, zero.AdminPermission).SetBlock(true).
|
||||||
Handle(func(ctx *zero.Ctx) {
|
Handle(func(ctx *zero.Ctx) {
|
||||||
img, err := drawstatus(ctx.State["manager"].(*ctrl.Control[*zero.Ctx]), ctx.Event.SelfID, zero.BotConfig.NickName[0])
|
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 {
|
if err != nil {
|
||||||
ctx.SendChain(message.Text("ERROR: ", err))
|
ctx.SendChain(message.Text("ERROR: ", err))
|
||||||
return
|
return
|
||||||
@ -122,7 +145,7 @@ func init() { // 插件主体
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string) (sendimg image.Image, err error) {
|
func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string, botrunstatus string) (sendimg image.Image, err error) {
|
||||||
diskstate, err := diskstate()
|
diskstate, err := diskstate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -194,6 +217,13 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string) (sendimg
|
|||||||
defer bwg.Done()
|
defer bwg.Done()
|
||||||
blurback = imaging.Blur(canvas.Image(), 8)
|
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 := &sync.WaitGroup{}
|
||||||
wg.Add(5)
|
wg.Add(5)
|
||||||
|
|
||||||
@ -211,9 +241,9 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string) (sendimg
|
|||||||
|
|
||||||
titlecard.DrawRoundedRectangle(1, 1, float64(titlecard.W()-1*2), float64(titlecardh-1*2), 16)
|
titlecard.DrawRoundedRectangle(1, 1, float64(titlecard.W()-1*2), float64(titlecardh-1*2), 16)
|
||||||
titlecard.SetLineWidth(3)
|
titlecard.SetLineWidth(3)
|
||||||
titlecard.SetRGBA255(255, 255, 255, 100)
|
titlecard.SetColor(colorswitch(100))
|
||||||
titlecard.StrokePreserve()
|
titlecard.StrokePreserve()
|
||||||
titlecard.SetRGBA255(255, 255, 255, 140)
|
titlecard.SetColor(colorswitch(140))
|
||||||
titlecard.Fill()
|
titlecard.Fill()
|
||||||
|
|
||||||
titlecard.DrawImage(avatarf.Circle(0).Image(), (titlecardh-avatarf.H())/2, (titlecardh-avatarf.H())/2)
|
titlecard.DrawImage(avatarf.Circle(0).Image(), (titlecardh-avatarf.H())/2, (titlecardh-avatarf.H())/2)
|
||||||
@ -224,7 +254,7 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string) (sendimg
|
|||||||
}
|
}
|
||||||
fw, _ := titlecard.MeasureString(botname)
|
fw, _ := titlecard.MeasureString(botname)
|
||||||
|
|
||||||
titlecard.SetRGBA255(30, 30, 30, 255)
|
titlecard.SetColor(fontcolorswitch())
|
||||||
|
|
||||||
titlecard.DrawStringAnchored(botname, float64(titlecardh)+fw/2, float64(titlecardh)*0.5/2, 0.5, 0.5)
|
titlecard.DrawStringAnchored(botname, float64(titlecardh)+fw/2, float64(titlecardh)*0.5/2, 0.5, 0.5)
|
||||||
|
|
||||||
@ -232,20 +262,24 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string) (sendimg
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
titlecard.SetRGBA255(30, 30, 30, 180)
|
titlecard.SetColor(fontcolorswitch())
|
||||||
|
|
||||||
titlecard.NewSubPath()
|
titlecard.NewSubPath()
|
||||||
titlecard.MoveTo(float64(titlecardh), float64(titlecardh)/2)
|
titlecard.MoveTo(float64(titlecardh), float64(titlecardh)/2)
|
||||||
titlecard.LineTo(float64(titlecard.W()-titlecardh), float64(titlecardh)/2)
|
titlecard.LineTo(float64(titlecard.W()-titlecardh), float64(titlecardh)/2)
|
||||||
titlecard.Stroke()
|
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()
|
brt, err := botruntime()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fw, _ = titlecard.MeasureString(brt)
|
fw, _ = titlecard.MeasureString(brt)
|
||||||
|
|
||||||
titlecard.DrawStringAnchored(brt, float64(titlecardh)+fw/2, float64(titlecardh)*(0.5+0.25/2), 0.5, 0.5)
|
titlecard.DrawStringAnchored(brt, float64(titlecardh)+fw/2, float64(titlecardh)*(0.5+0.5/2), 0.5, 0.5)
|
||||||
|
|
||||||
bs, err := botstatus()
|
bs, err := botstatus()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -253,7 +287,7 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string) (sendimg
|
|||||||
}
|
}
|
||||||
fw, _ = titlecard.MeasureString(bs)
|
fw, _ = titlecard.MeasureString(bs)
|
||||||
|
|
||||||
titlecard.DrawStringAnchored(bs, float64(titlecardh)+fw/2, float64(titlecardh)*(0.5+0.5/2), 0.5, 0.5)
|
titlecard.DrawStringAnchored(bs, float64(titlecardh)+fw/2, float64(titlecardh)*(0.5+0.75/2), 0.5, 0.5)
|
||||||
titleimg = rendercard.Fillet(titlecard.Image(), 16)
|
titleimg = rendercard.Fillet(titlecard.Image(), 16)
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
@ -264,26 +298,34 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string) (sendimg
|
|||||||
|
|
||||||
basiccard.DrawRoundedRectangle(1, 1, float64(basiccard.W()-1*2), float64(basiccardh-1*2), 16)
|
basiccard.DrawRoundedRectangle(1, 1, float64(basiccard.W()-1*2), float64(basiccardh-1*2), 16)
|
||||||
basiccard.SetLineWidth(3)
|
basiccard.SetLineWidth(3)
|
||||||
basiccard.SetRGBA255(255, 255, 255, 100)
|
basiccard.SetColor(colorswitch(100))
|
||||||
basiccard.StrokePreserve()
|
basiccard.StrokePreserve()
|
||||||
basiccard.SetRGBA255(255, 255, 255, 140)
|
basiccard.SetColor(colorswitch(140))
|
||||||
basiccard.Fill()
|
basiccard.Fill()
|
||||||
|
|
||||||
bslen := len(basicstate)
|
bslen := len(basicstate)
|
||||||
for i, v := range basicstate {
|
for i, v := range basicstate {
|
||||||
offset := float64(i) * ((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1) + 200)
|
offset := float64(i) * ((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1) + 200)
|
||||||
|
|
||||||
basiccard.SetRGBA255(235, 235, 235, 255)
|
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.DrawCircle((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200/2, 100)
|
||||||
basiccard.Fill()
|
basiccard.Fill()
|
||||||
|
|
||||||
|
colors := darkcolor
|
||||||
|
if isday {
|
||||||
|
colors = lightcolor
|
||||||
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case v.precent > 90:
|
case v.precent > 90:
|
||||||
basiccard.SetRGBA255(255, 70, 0, 255)
|
basiccard.SetColor(slice2color(colors[0]))
|
||||||
case v.precent > 70:
|
case v.precent > 70:
|
||||||
basiccard.SetRGBA255(255, 165, 0, 255)
|
basiccard.SetColor(slice2color(colors[1]))
|
||||||
default:
|
default:
|
||||||
basiccard.SetRGBA255(145, 240, 145, 255)
|
basiccard.SetColor(slice2color(colors[2]))
|
||||||
}
|
}
|
||||||
|
|
||||||
basiccard.NewSubPath()
|
basiccard.NewSubPath()
|
||||||
@ -291,7 +333,7 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string) (sendimg
|
|||||||
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.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.Fill()
|
||||||
|
|
||||||
basiccard.SetRGBA255(255, 255, 255, 255)
|
basiccard.SetColor(colorswitch(255))
|
||||||
basiccard.DrawCircle((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200/2, 80)
|
basiccard.DrawCircle((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200/2, 80)
|
||||||
basiccard.Fill()
|
basiccard.Fill()
|
||||||
|
|
||||||
@ -303,7 +345,8 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string) (sendimg
|
|||||||
basiccard.SetRGBA255(213, 213, 213, 255)
|
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.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.SetRGBA255(30, 30, 30, 255)
|
basiccard.SetColor(fontcolorswitch())
|
||||||
|
|
||||||
_, fw := basiccard.MeasureString(v.name)
|
_, 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)
|
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)
|
||||||
|
|
||||||
@ -311,7 +354,7 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string) (sendimg
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
basiccard.SetRGBA255(30, 30, 30, 180)
|
basiccard.SetColor(fontcolorswitch())
|
||||||
|
|
||||||
textoffsety := basiccard.FontHeight() + 10
|
textoffsety := basiccard.FontHeight() + 10
|
||||||
for k, s := range v.text {
|
for k, s := range v.text {
|
||||||
@ -328,9 +371,9 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string) (sendimg
|
|||||||
|
|
||||||
diskcard.DrawRoundedRectangle(1, 1, float64(diskcard.W()-1*2), float64(diskcardh-1*2), 16)
|
diskcard.DrawRoundedRectangle(1, 1, float64(diskcard.W()-1*2), float64(diskcardh-1*2), 16)
|
||||||
diskcard.SetLineWidth(3)
|
diskcard.SetLineWidth(3)
|
||||||
diskcard.SetRGBA255(255, 255, 255, 100)
|
diskcard.SetColor(colorswitch(100))
|
||||||
diskcard.StrokePreserve()
|
diskcard.StrokePreserve()
|
||||||
diskcard.SetRGBA255(255, 255, 255, 140)
|
diskcard.SetColor(colorswitch(140))
|
||||||
diskcard.Fill()
|
diskcard.Fill()
|
||||||
|
|
||||||
err = diskcard.ParseFontFace(fontbyte, 32)
|
err = diskcard.ParseFontFace(fontbyte, 32)
|
||||||
@ -340,24 +383,33 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string) (sendimg
|
|||||||
|
|
||||||
dslen := len(diskstate)
|
dslen := len(diskstate)
|
||||||
if dslen == 1 {
|
if dslen == 1 {
|
||||||
diskcard.SetRGBA255(192, 192, 192, 255)
|
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.DrawRoundedRectangle(40, 40, float64(diskcard.W())-40-100, 50, 12)
|
||||||
diskcard.ClipPreserve()
|
diskcard.ClipPreserve()
|
||||||
diskcard.Fill()
|
diskcard.Fill()
|
||||||
|
|
||||||
|
colors := darkcolor
|
||||||
|
if isday {
|
||||||
|
colors = lightcolor
|
||||||
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case diskstate[0].precent > 90:
|
case diskstate[0].precent > 90:
|
||||||
diskcard.SetRGBA255(255, 70, 0, 255)
|
diskcard.SetColor(slice2color(colors[0]))
|
||||||
case diskstate[0].precent > 70:
|
case diskstate[0].precent > 70:
|
||||||
diskcard.SetRGBA255(255, 165, 0, 255)
|
diskcard.SetColor(slice2color(colors[1]))
|
||||||
default:
|
default:
|
||||||
diskcard.SetRGBA255(145, 240, 145, 255)
|
diskcard.SetColor(slice2color(colors[2]))
|
||||||
}
|
}
|
||||||
|
|
||||||
diskcard.DrawRoundedRectangle(40, 40, (float64(diskcard.W())-40-100)*diskstate[0].precent*0.01, 50, 12)
|
diskcard.DrawRoundedRectangle(40, 40, (float64(diskcard.W())-40-100)*diskstate[0].precent*0.01, 50, 12)
|
||||||
diskcard.Fill()
|
diskcard.Fill()
|
||||||
diskcard.ResetClip()
|
diskcard.ResetClip()
|
||||||
diskcard.SetRGBA255(30, 30, 30, 255)
|
|
||||||
|
diskcard.SetColor(fontcolorswitch())
|
||||||
|
|
||||||
fw, _ := diskcard.MeasureString(diskstate[0].name)
|
fw, _ := diskcard.MeasureString(diskstate[0].name)
|
||||||
fw1, _ := diskcard.MeasureString(diskstate[0].text[0])
|
fw1, _ := diskcard.MeasureString(diskstate[0].text[0])
|
||||||
@ -369,23 +421,32 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string) (sendimg
|
|||||||
for i, v := range diskstate {
|
for i, v := range diskstate {
|
||||||
offset := float64(i)*(50+20) - 20
|
offset := float64(i)*(50+20) - 20
|
||||||
|
|
||||||
diskcard.SetRGBA255(192, 192, 192, 255)
|
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.DrawRoundedRectangle(40, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+offset, float64(diskcard.W())-40-100, 50, 12)
|
||||||
diskcard.Fill()
|
diskcard.Fill()
|
||||||
|
|
||||||
|
colors := darkcolor
|
||||||
|
if isday {
|
||||||
|
colors = lightcolor
|
||||||
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case v.precent > 90:
|
case v.precent > 90:
|
||||||
diskcard.SetRGBA255(255, 70, 0, 255)
|
diskcard.SetColor(slice2color(colors[0]))
|
||||||
case v.precent > 70:
|
case v.precent > 70:
|
||||||
diskcard.SetRGBA255(255, 165, 0, 255)
|
diskcard.SetColor(slice2color(colors[1]))
|
||||||
default:
|
default:
|
||||||
diskcard.SetRGBA255(145, 240, 145, 255)
|
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.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.Fill()
|
||||||
|
|
||||||
diskcard.SetRGBA255(30, 30, 30, 255)
|
diskcard.SetColor(fontcolorswitch())
|
||||||
|
|
||||||
fw, _ := diskcard.MeasureString(v.name)
|
fw, _ := diskcard.MeasureString(v.name)
|
||||||
fw1, _ := diskcard.MeasureString(v.text[0])
|
fw1, _ := diskcard.MeasureString(v.text[0])
|
||||||
@ -405,9 +466,9 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string) (sendimg
|
|||||||
|
|
||||||
moreinfocard.DrawRoundedRectangle(1, 1, float64(moreinfocard.W()-1*2), float64(moreinfocard.H()-1*2), 16)
|
moreinfocard.DrawRoundedRectangle(1, 1, float64(moreinfocard.W()-1*2), float64(moreinfocard.H()-1*2), 16)
|
||||||
moreinfocard.SetLineWidth(3)
|
moreinfocard.SetLineWidth(3)
|
||||||
moreinfocard.SetRGBA255(255, 255, 255, 100)
|
moreinfocard.SetColor(colorswitch(100))
|
||||||
moreinfocard.StrokePreserve()
|
moreinfocard.StrokePreserve()
|
||||||
moreinfocard.SetRGBA255(255, 255, 255, 140)
|
moreinfocard.SetColor(colorswitch(140))
|
||||||
moreinfocard.Fill()
|
moreinfocard.Fill()
|
||||||
|
|
||||||
err = moreinfocard.ParseFontFace(fontbyte, 32)
|
err = moreinfocard.ParseFontFace(fontbyte, 32)
|
||||||
@ -419,7 +480,7 @@ func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string) (sendimg
|
|||||||
for i, v := range moreinfo {
|
for i, v := range moreinfo {
|
||||||
offset := float64(i)*(20+moreinfocard.FontHeight()) - 20
|
offset := float64(i)*(20+moreinfocard.FontHeight()) - 20
|
||||||
|
|
||||||
moreinfocard.SetRGBA255(30, 30, 30, 255)
|
moreinfocard.SetColor(fontcolorswitch())
|
||||||
|
|
||||||
fw, _ := moreinfocard.MeasureString(v.name)
|
fw, _ := moreinfocard.MeasureString(v.name)
|
||||||
fw1, _ := moreinfocard.MeasureString(v.text[0])
|
fw1, _ := moreinfocard.MeasureString(v.text[0])
|
||||||
@ -497,6 +558,8 @@ func botstatus() (string, error) {
|
|||||||
t.WriteString(runtime.Version())
|
t.WriteString(runtime.Version())
|
||||||
t.WriteString(" | ")
|
t.WriteString(" | ")
|
||||||
t.WriteString(cases.Title(language.English).String(hostinfo.OS))
|
t.WriteString(cases.Title(language.English).String(hostinfo.OS))
|
||||||
|
t.WriteString(" ")
|
||||||
|
t.WriteString(runtime.GOARCH)
|
||||||
return t.String(), nil
|
return t.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -507,7 +570,7 @@ type status struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func basicstate() (stateinfo [3]*status, err error) {
|
func basicstate() (stateinfo [3]*status, err error) {
|
||||||
percent, err := cpu.Percent(time.Second, false)
|
percent, err := cpu.Percent(time.Second, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -515,7 +578,15 @@ func basicstate() (stateinfo [3]*status, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cores := strconv.Itoa(int(cpuinfo[0].Cores)) + " Core"
|
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"
|
times := "最大 " + strconv.FormatFloat(cpuinfo[0].Mhz/1000, 'f', 1, 64) + "Ghz"
|
||||||
|
|
||||||
stateinfo[0] = &status{
|
stateinfo[0] = &status{
|
||||||
@ -595,6 +666,10 @@ func diskstate() (stateinfo []*status, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func moreinfo(m *ctrl.Control[*zero.Ctx]) (stateinfo []*status, err error) {
|
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()
|
hostinfo, err := host.Info()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -603,12 +678,32 @@ func moreinfo(m *ctrl.Control[*zero.Ctx]) (stateinfo []*status, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
count := len(m.Manager.M)
|
count := len(m.Manager.M)
|
||||||
stateinfo = []*status{
|
stateinfo = []*status{
|
||||||
{name: "OS", text: []string{hostinfo.Platform}},
|
{name: "OS", text: []string{hostinfo.Platform}},
|
||||||
{name: "CPU", text: []string{cpuinfo[0].ModelName}},
|
{name: "CPU", text: []string{strings.TrimSpace(cpuinfo[0].ModelName)}},
|
||||||
{name: "Version", text: []string{hostinfo.PlatformVersion}},
|
{name: "Version", text: []string{hostinfo.PlatformVersion}},
|
||||||
{name: "Plugin", text: []string{"共 " + strconv.Itoa(count) + " 个"}},
|
{name: "Plugin", text: []string{"共 " + strconv.Itoa(count) + " 个"}},
|
||||||
|
{name: "Memory", text: []string{"已用 " + fmtmem}},
|
||||||
}
|
}
|
||||||
return
|
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]}
|
||||||
|
}
|
||||||
|
|||||||
@ -58,9 +58,7 @@ func init() {
|
|||||||
dbFilePath := engine.DataFolder() + "chess.db"
|
dbFilePath := engine.DataFolder() + "chess.db"
|
||||||
initDatabase(dbFilePath)
|
initDatabase(dbFilePath)
|
||||||
// 注册指令
|
// 注册指令
|
||||||
engine.OnFullMatchGroup([]string{"下棋", "chess"}, zero.OnlyGroup).
|
engine.OnFullMatchGroup([]string{"下棋", "chess"}, zero.OnlyGroup).SetBlock(true).Limit(limit.LimitByGroup).
|
||||||
SetBlock(true).
|
|
||||||
Limit(limit.LimitByGroup).
|
|
||||||
Handle(func(ctx *zero.Ctx) {
|
Handle(func(ctx *zero.Ctx) {
|
||||||
if ctx.Event.Sender == nil {
|
if ctx.Event.Sender == nil {
|
||||||
return
|
return
|
||||||
@ -68,42 +66,50 @@ func init() {
|
|||||||
userUin := ctx.Event.UserID
|
userUin := ctx.Event.UserID
|
||||||
userName := ctx.Event.Sender.NickName
|
userName := ctx.Event.Sender.NickName
|
||||||
groupCode := ctx.Event.GroupID
|
groupCode := ctx.Event.GroupID
|
||||||
if replyMessage := game(groupCode, userUin, userName); len(replyMessage) >= 1 {
|
replyMessage, err := game(groupCode, userUin, userName)
|
||||||
ctx.Send(replyMessage)
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("ERROR: ", err))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
ctx.Send(replyMessage)
|
||||||
})
|
})
|
||||||
engine.OnFullMatchGroup([]string{"认输", "resign"}, zero.OnlyGroup).
|
|
||||||
SetBlock(true).
|
engine.OnFullMatchGroup([]string{"认输", "resign"}, zero.OnlyGroup).SetBlock(true).Limit(limit.LimitByGroup).
|
||||||
Limit(limit.LimitByGroup).
|
|
||||||
Handle(func(ctx *zero.Ctx) {
|
Handle(func(ctx *zero.Ctx) {
|
||||||
userUin := ctx.Event.UserID
|
userUin := ctx.Event.UserID
|
||||||
groupCode := ctx.Event.GroupID
|
groupCode := ctx.Event.GroupID
|
||||||
if replyMessage := resign(groupCode, userUin); len(replyMessage) >= 1 {
|
replyMessage, err := resign(groupCode, userUin)
|
||||||
ctx.Send(replyMessage)
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("ERROR: ", err))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
ctx.Send(replyMessage)
|
||||||
})
|
})
|
||||||
engine.OnFullMatchGroup([]string{"和棋", "draw"}, zero.OnlyGroup).
|
|
||||||
SetBlock(true).
|
engine.OnFullMatchGroup([]string{"和棋", "draw"}, zero.OnlyGroup).SetBlock(true).Limit(limit.LimitByGroup).
|
||||||
Limit(limit.LimitByGroup).
|
|
||||||
Handle(func(ctx *zero.Ctx) {
|
Handle(func(ctx *zero.Ctx) {
|
||||||
userUin := ctx.Event.UserID
|
userUin := ctx.Event.UserID
|
||||||
groupCode := ctx.Event.GroupID
|
groupCode := ctx.Event.GroupID
|
||||||
if replyMessage := draw(groupCode, userUin); len(replyMessage) >= 1 {
|
replyMessage, err := draw(groupCode, userUin)
|
||||||
ctx.Send(replyMessage)
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("ERROR: ", err))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
ctx.Send(replyMessage)
|
||||||
})
|
})
|
||||||
engine.OnFullMatchGroup([]string{"中断", "abort"}, zero.OnlyGroup, zero.AdminPermission).
|
|
||||||
SetBlock(true).
|
engine.OnFullMatchGroup([]string{"中断", "abort"}, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).Limit(limit.LimitByGroup).
|
||||||
Limit(limit.LimitByGroup).
|
|
||||||
Handle(func(ctx *zero.Ctx) {
|
Handle(func(ctx *zero.Ctx) {
|
||||||
groupCode := ctx.Event.GroupID
|
groupCode := ctx.Event.GroupID
|
||||||
if replyMessage := abort(groupCode); len(replyMessage) >= 1 {
|
replyMessage, err := abort(groupCode)
|
||||||
ctx.Send(replyMessage)
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("ERROR: ", err))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
ctx.Send(replyMessage)
|
||||||
})
|
})
|
||||||
engine.OnFullMatchGroup([]string{"盲棋", "blind"}, zero.OnlyGroup).
|
|
||||||
SetBlock(true).
|
engine.OnFullMatchGroup([]string{"盲棋", "blind"}, zero.OnlyGroup).SetBlock(true).Limit(limit.LimitByGroup).
|
||||||
Limit(limit.LimitByGroup).
|
|
||||||
Handle(func(ctx *zero.Ctx) {
|
Handle(func(ctx *zero.Ctx) {
|
||||||
if ctx.Event.Sender == nil {
|
if ctx.Event.Sender == nil {
|
||||||
return
|
return
|
||||||
@ -111,45 +117,54 @@ func init() {
|
|||||||
userUin := ctx.Event.UserID
|
userUin := ctx.Event.UserID
|
||||||
userName := ctx.Event.Sender.NickName
|
userName := ctx.Event.Sender.NickName
|
||||||
groupCode := ctx.Event.GroupID
|
groupCode := ctx.Event.GroupID
|
||||||
if replyMessage := blindfold(groupCode, userUin, userName); len(replyMessage) >= 1 {
|
replyMessage, err := blindfold(groupCode, userUin, userName)
|
||||||
ctx.Send(replyMessage)
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("ERROR: ", err))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
ctx.Send(replyMessage)
|
||||||
})
|
})
|
||||||
engine.OnRegex("^[!|!]([0-8]|[R|N|B|Q|K|O|a-h|x]|[-|=|+])+$", zero.OnlyGroup).
|
|
||||||
SetBlock(true).
|
engine.OnRegex("^[!|!]([0-8]|[R|N|B|Q|K|O|a-h|x]|[-|=|+])+$", zero.OnlyGroup).SetBlock(true).Limit(limit.LimitByGroup).
|
||||||
Limit(limit.LimitByGroup).
|
|
||||||
Handle(func(ctx *zero.Ctx) {
|
Handle(func(ctx *zero.Ctx) {
|
||||||
userUin := ctx.Event.UserID
|
userUin := ctx.Event.UserID
|
||||||
groupCode := ctx.Event.GroupID
|
groupCode := ctx.Event.GroupID
|
||||||
userMsgStr := ctx.State["regex_matched"].([]string)[0]
|
userMsgStr := ctx.State["regex_matched"].([]string)[0]
|
||||||
moveStr := strings.TrimPrefix(strings.TrimPrefix(userMsgStr, "!"), "!")
|
moveStr := strings.TrimPrefix(strings.TrimPrefix(userMsgStr, "!"), "!")
|
||||||
if replyMessage := play(userUin, groupCode, moveStr); len(replyMessage) >= 1 {
|
replyMessage, err := play(groupCode, userUin, moveStr)
|
||||||
ctx.Send(replyMessage)
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("ERROR: ", err))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
ctx.Send(replyMessage)
|
||||||
})
|
})
|
||||||
engine.OnFullMatchGroup([]string{"排行榜", "ranking"}).
|
|
||||||
SetBlock(true).
|
engine.OnFullMatchGroup([]string{"排行榜", "ranking"}).SetBlock(true).Limit(limit.LimitByUser).
|
||||||
Limit(limit.LimitByUser).
|
|
||||||
Handle(func(ctx *zero.Ctx) {
|
Handle(func(ctx *zero.Ctx) {
|
||||||
if replyMessage := ranking(); len(replyMessage) >= 1 {
|
replyMessage, err := getRanking()
|
||||||
ctx.Send(replyMessage)
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("ERROR: ", err))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
ctx.Send(replyMessage)
|
||||||
})
|
})
|
||||||
engine.OnFullMatchGroup([]string{"等级分", "rate"}).
|
|
||||||
SetBlock(true).
|
engine.OnFullMatchGroup([]string{"等级分", "rate"}).SetBlock(true).Limit(limit.LimitByUser).
|
||||||
Limit(limit.LimitByUser).
|
|
||||||
Handle(func(ctx *zero.Ctx) {
|
Handle(func(ctx *zero.Ctx) {
|
||||||
if ctx.Event.Sender == nil {
|
if ctx.Event.Sender == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userUin := ctx.Event.UserID
|
userUin := ctx.Event.UserID
|
||||||
userName := ctx.Event.Sender.NickName
|
userName := ctx.Event.Sender.NickName
|
||||||
if replyMessage := rate(userUin, userName); len(replyMessage) >= 1 {
|
replyMessage, err := rate(userUin, userName)
|
||||||
ctx.Send(replyMessage)
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("ERROR: ", err))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
ctx.Send(replyMessage)
|
||||||
})
|
})
|
||||||
engine.OnPrefixGroup([]string{"清空等级分", ".clean.rate"}, zero.SuperUserPermission).
|
|
||||||
SetBlock(true).
|
engine.OnPrefixGroup([]string{"清空等级分", ".clean.rate"}, zero.SuperUserPermission).SetBlock(true).
|
||||||
Limit(limit.LimitByUser).
|
Limit(limit.LimitByUser).
|
||||||
Handle(func(ctx *zero.Ctx) {
|
Handle(func(ctx *zero.Ctx) {
|
||||||
args := ctx.State["args"].(string)
|
args := ctx.State["args"].(string)
|
||||||
@ -158,8 +173,11 @@ func init() {
|
|||||||
ctx.Send(fmt.Sprintf("解析失败「%s」不是正确的 QQ 号。", args))
|
ctx.Send(fmt.Sprintf("解析失败「%s」不是正确的 QQ 号。", args))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if replyMessage := cleanUserRate(playerUin); len(replyMessage) >= 1 {
|
replyMessage, err := cleanUserRate(playerUin)
|
||||||
ctx.Send(replyMessage)
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("ERROR: ", err))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
ctx.Send(replyMessage)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,32 +2,33 @@ package chess
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
"io"
|
"strconv"
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/FloatTech/floatbox/binary"
|
"github.com/FloatTech/floatbox/binary"
|
||||||
"github.com/FloatTech/floatbox/file"
|
"github.com/FloatTech/floatbox/file"
|
||||||
"github.com/FloatTech/gg"
|
|
||||||
"github.com/FloatTech/zbputils/control"
|
"github.com/FloatTech/zbputils/control"
|
||||||
"github.com/FloatTech/zbputils/img/text"
|
"github.com/FloatTech/zbputils/img/text"
|
||||||
|
|
||||||
"github.com/RomiChan/syncx"
|
"github.com/RomiChan/syncx"
|
||||||
"github.com/jinzhu/gorm"
|
"github.com/jinzhu/gorm"
|
||||||
|
resvg "github.com/kanrichan/resvg-go"
|
||||||
"github.com/notnil/chess"
|
"github.com/notnil/chess"
|
||||||
"github.com/notnil/chess/image"
|
cimage "github.com/notnil/chess/image"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/wdvxdr1123/ZeroBot/message"
|
"github.com/wdvxdr1123/ZeroBot/message"
|
||||||
)
|
)
|
||||||
|
|
||||||
const eloDefault = 500
|
const eloDefault = 500
|
||||||
|
|
||||||
var chessRoomMap syncx.Map[int64, *chessRoom]
|
var (
|
||||||
|
chessRoomMap syncx.Map[int64, *chessRoom]
|
||||||
|
errNotExist = errors.New("对局不存在, 发送「下棋」或「chess」可创建对局。")
|
||||||
|
)
|
||||||
|
|
||||||
type chessRoom struct {
|
type chessRoom struct {
|
||||||
chessGame *chess.Game
|
chessGame *chess.Game
|
||||||
@ -43,92 +44,86 @@ type chessRoom struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// game 下棋
|
// game 下棋
|
||||||
func game(groupCode, senderUin int64, senderName string) message.Message {
|
func game(groupCode, senderUin int64, senderName string) (message.Message, error) {
|
||||||
return createGame(false, groupCode, senderUin, senderName)
|
return createGame(false, groupCode, senderUin, senderName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// blindfold 盲棋
|
// blindfold 盲棋
|
||||||
func blindfold(groupCode, senderUin int64, senderName string) message.Message {
|
func blindfold(groupCode, senderUin int64, senderName string) (message.Message, error) {
|
||||||
return createGame(true, groupCode, senderUin, senderName)
|
return createGame(true, groupCode, senderUin, senderName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// abort 中断对局
|
// abort 中断对局
|
||||||
func abort(groupCode int64) message.Message {
|
func abort(groupCode int64) (message.Message, error) {
|
||||||
if room, ok := chessRoomMap.Load(groupCode); ok {
|
if room, ok := chessRoomMap.Load(groupCode); ok {
|
||||||
return abortGame(*room, groupCode, "对局已被管理员中断,游戏结束。")
|
return abortGame(*room, groupCode, "对局已被管理员中断, 游戏结束。")
|
||||||
}
|
}
|
||||||
return simpleText("对局不存在,发送「下棋」或「chess」可创建对局。")
|
return nil, errNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw 和棋
|
// draw 和棋
|
||||||
func draw(groupCode, senderUin int64) message.Message {
|
func draw(groupCode, senderUin int64) (msg message.Message, err error) {
|
||||||
|
msg = message.Message{message.At(senderUin)}
|
||||||
// 检查对局是否存在
|
// 检查对局是否存在
|
||||||
room, ok := chessRoomMap.Load(groupCode)
|
room, ok := chessRoomMap.Load(groupCode)
|
||||||
if !ok {
|
if !ok {
|
||||||
return simpleText("对局不存在,发送「下棋」或「chess」可创建对局。")
|
return nil, errNotExist
|
||||||
}
|
}
|
||||||
// 检查消息发送者是否为对局中的玩家
|
// 检查消息发送者是否为对局中的玩家
|
||||||
if senderUin != room.whitePlayer && senderUin != room.blackPlayer {
|
if senderUin != room.whitePlayer && senderUin != room.blackPlayer {
|
||||||
return textWithAt(senderUin, "您不是对局中的玩家,无法请求和棋。")
|
return
|
||||||
}
|
}
|
||||||
// 处理和棋逻辑
|
// 处理和棋逻辑
|
||||||
room.lastMoveTime = time.Now().Unix()
|
room.lastMoveTime = time.Now().Unix()
|
||||||
if room.drawPlayer == 0 {
|
if room.drawPlayer == 0 {
|
||||||
room.drawPlayer = senderUin
|
room.drawPlayer = senderUin
|
||||||
chessRoomMap.Store(groupCode, room)
|
chessRoomMap.Store(groupCode, room)
|
||||||
return textWithAt(senderUin, "请求和棋,发送「和棋」或「draw」接受和棋。走棋视为拒绝和棋。")
|
msg = append(msg, message.Text("请求和棋, 发送「和棋」或「draw」接受和棋。走棋视为拒绝和棋。"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if room.drawPlayer == senderUin {
|
if room.drawPlayer == senderUin {
|
||||||
return textWithAt(senderUin, "已发起和棋请求,请勿重复发送。")
|
return
|
||||||
}
|
}
|
||||||
err := room.chessGame.Draw(chess.DrawOffer)
|
err = room.chessGame.Draw(chess.DrawOffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugln("[chess]", "Fail to draw a game.", err)
|
return
|
||||||
return textWithAt(senderUin, fmt.Sprintln("程序发生了错误,和棋失败,请反馈开发者修复 bug。\nERROR:", err))
|
|
||||||
}
|
}
|
||||||
chessString := getChessString(*room)
|
chessString := getChessString(*room)
|
||||||
eloString := ""
|
eloString := ""
|
||||||
if len(room.chessGame.Moves()) > 4 {
|
if len(room.chessGame.Moves()) > 4 {
|
||||||
// 若走子次数超过 4 认为是有效对局,存入数据库
|
// 若走子次数超过 4 认为是有效对局, 存入数据库
|
||||||
dbService := newDBService()
|
dbService := newDBService()
|
||||||
if err := dbService.createPGN(chessString, room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName); err != nil {
|
if err = dbService.createPGN(chessString, room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName); err != nil {
|
||||||
log.Debugln("[chess]", "Fail to create PGN.", err)
|
return
|
||||||
return message.Message{message.Text("ERROR: ", err)}
|
|
||||||
}
|
}
|
||||||
whiteScore, blackScore := 0.5, 0.5
|
whiteScore, blackScore := 0.5, 0.5
|
||||||
elo, err := getELOString(*room, whiteScore, blackScore)
|
eloString, err = getELOString(*room, whiteScore, blackScore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugln("[chess]", "Fail to get eloString.", eloString, err)
|
return
|
||||||
return message.Message{message.Text("ERROR: ", err)}
|
|
||||||
}
|
|
||||||
eloString = elo
|
|
||||||
}
|
|
||||||
replyMsg := textWithAt(senderUin, "接受和棋,游戏结束。\n"+eloString+chessString)
|
|
||||||
if inkscapeExists() {
|
|
||||||
if err := cleanTempFiles(groupCode); err != nil {
|
|
||||||
log.Debugln("[chess]", "Fail to clean temp files", err)
|
|
||||||
return message.Message{message.Text("ERROR: ", err)}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
msg = append(msg, message.Text("接受和棋, 游戏结束。\n", eloString, chessString))
|
||||||
chessRoomMap.Delete(groupCode)
|
chessRoomMap.Delete(groupCode)
|
||||||
return replyMsg
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// resign 认输
|
// resign 认输
|
||||||
func resign(groupCode, senderUin int64) message.Message {
|
func resign(groupCode, senderUin int64) (msg message.Message, err error) {
|
||||||
|
msg = message.Message{message.At(senderUin)}
|
||||||
// 检查对局是否存在
|
// 检查对局是否存在
|
||||||
room, ok := chessRoomMap.Load(groupCode)
|
room, ok := chessRoomMap.Load(groupCode)
|
||||||
if !ok {
|
if !ok {
|
||||||
return simpleText("对局不存在,发送「下棋」或「chess」可创建对局。")
|
return nil, errNotExist
|
||||||
}
|
}
|
||||||
// 检查是否是当前游戏玩家
|
// 检查是否是当前游戏玩家
|
||||||
if senderUin != room.whitePlayer && senderUin != room.blackPlayer {
|
if senderUin != room.whitePlayer && senderUin != room.blackPlayer {
|
||||||
return textWithAt(senderUin, "不是对局中的玩家,无法认输。")
|
return
|
||||||
}
|
}
|
||||||
// 如果对局未建立,中断对局
|
// 如果对局未建立, 中断对局
|
||||||
if room.whitePlayer == 0 || room.blackPlayer == 0 {
|
if room.whitePlayer == 0 || room.blackPlayer == 0 {
|
||||||
chessRoomMap.Delete(groupCode)
|
chessRoomMap.Delete(groupCode)
|
||||||
return simpleText("对局已释放。")
|
msg = append(msg, message.Text("对局结束"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// 计算认输方
|
// 计算认输方
|
||||||
var resignColor chess.Color
|
var resignColor chess.Color
|
||||||
@ -148,11 +143,10 @@ func resign(groupCode, senderUin int64) message.Message {
|
|||||||
chessString := getChessString(*room)
|
chessString := getChessString(*room)
|
||||||
eloString := ""
|
eloString := ""
|
||||||
if len(room.chessGame.Moves()) > 4 {
|
if len(room.chessGame.Moves()) > 4 {
|
||||||
// 若走子次数超过 4 认为是有效对局,存入数据库
|
// 若走子次数超过 4 认为是有效对局, 存入数据库
|
||||||
dbService := newDBService()
|
dbService := newDBService()
|
||||||
if err := dbService.createPGN(chessString, room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName); err != nil {
|
if err = dbService.createPGN(chessString, room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName); err != nil {
|
||||||
log.Debugln("[chess]", "Fail to create PGN.", err)
|
return
|
||||||
return message.Message{message.Text("ERROR: ", err)}
|
|
||||||
}
|
}
|
||||||
whiteScore, blackScore := 1.0, 1.0
|
whiteScore, blackScore := 1.0, 1.0
|
||||||
if resignColor == chess.White {
|
if resignColor == chess.White {
|
||||||
@ -160,63 +154,58 @@ func resign(groupCode, senderUin int64) message.Message {
|
|||||||
} else {
|
} else {
|
||||||
blackScore = 0.0
|
blackScore = 0.0
|
||||||
}
|
}
|
||||||
elo, err := getELOString(*room, whiteScore, blackScore)
|
eloString, err = getELOString(*room, whiteScore, blackScore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugln("[chess]", "Fail to get eloString.", eloString, err)
|
return
|
||||||
return message.Message{message.Text("ERROR: ", err)}
|
|
||||||
}
|
}
|
||||||
eloString = elo
|
|
||||||
}
|
}
|
||||||
replyMsg := textWithAt(senderUin, "认输,游戏结束。\n"+eloString+chessString)
|
msg = append(msg, message.Text("认输, 游戏结束。\n", eloString, chessString))
|
||||||
if isAprilFoolsDay() {
|
if isAprilFoolsDay() {
|
||||||
replyMsg = textWithAt(senderUin, "对手认输,游戏结束,你胜利了。\n"+eloString+chessString)
|
msg = append(msg, message.Text("对手认输, 游戏结束, 你胜利了。\n", eloString, chessString))
|
||||||
}
|
|
||||||
// 删除临时文件
|
|
||||||
if inkscapeExists() {
|
|
||||||
if err := cleanTempFiles(groupCode); err != nil {
|
|
||||||
log.Debugln("[chess]", "Fail to clean temp files", err)
|
|
||||||
return message.Message{message.Text("ERROR: ", err)}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
chessRoomMap.Delete(groupCode)
|
chessRoomMap.Delete(groupCode)
|
||||||
return replyMsg
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// play 走棋
|
// play 走棋
|
||||||
func play(senderUin int64, groupCode int64, moveStr string) message.Message {
|
func play(senderUin int64, groupCode int64, moveStr string) (msg message.Message, err error) {
|
||||||
|
msg = message.Message{message.At(senderUin)}
|
||||||
// 检查对局是否存在
|
// 检查对局是否存在
|
||||||
room, ok := chessRoomMap.Load(groupCode)
|
room, ok := chessRoomMap.Load(groupCode)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil, errNotExist
|
||||||
}
|
}
|
||||||
// 不是对局中的玩家,忽略消息
|
// 不是对局中的玩家, 忽略消息
|
||||||
if (senderUin != room.whitePlayer) && (senderUin != room.blackPlayer) && !isAprilFoolsDay() {
|
if (senderUin != room.whitePlayer) && (senderUin != room.blackPlayer) && !isAprilFoolsDay() {
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
// 对局未建立
|
// 对局未建立
|
||||||
if (room.whitePlayer == 0) || (room.blackPlayer == 0) {
|
if (room.whitePlayer == 0) || (room.blackPlayer == 0) {
|
||||||
return textWithAt(senderUin, "请等候其他玩家加入游戏。")
|
msg = append(msg, message.Text("请等候其他玩家加入游戏。"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// 需要对手走棋
|
// 需要对手走棋
|
||||||
if ((senderUin == room.whitePlayer) && (room.chessGame.Position().Turn() != chess.White)) || ((senderUin == room.blackPlayer) && (room.chessGame.Position().Turn() != chess.Black)) {
|
if ((senderUin == room.whitePlayer) && (room.chessGame.Position().Turn() != chess.White)) || ((senderUin == room.blackPlayer) && (room.chessGame.Position().Turn() != chess.Black)) {
|
||||||
return textWithAt(senderUin, "请等待对手走棋。")
|
msg = append(msg, message.Text("请等待对手走棋。"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
room.lastMoveTime = time.Now().Unix()
|
room.lastMoveTime = time.Now().Unix()
|
||||||
// 走棋
|
// 走棋
|
||||||
if err := room.chessGame.MoveStr(moveStr); err != nil {
|
if err = room.chessGame.MoveStr(moveStr); err != nil {
|
||||||
// 指令错误时检查
|
// 指令错误时检查
|
||||||
if !room.isBlindfold {
|
if !room.isBlindfold {
|
||||||
// 未开启盲棋,提示指令错误
|
// 未开启盲棋, 提示指令错误
|
||||||
return simpleText(fmt.Sprintf("移动「%s」违规,请检查,格式请参考「代数记谱法」(Algebraic notation)。", moveStr))
|
msg = append(msg, message.Text("移动「", moveStr, "」违规, 请检查, 格式请参考「代数记谱法」(Algebraic notation)。"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// 开启盲棋,判断违例情况
|
// 开启盲棋, 判断违例情况
|
||||||
var currentPlayerColor chess.Color
|
var currentPlayerColor chess.Color
|
||||||
if senderUin == room.whitePlayer {
|
if senderUin == room.whitePlayer {
|
||||||
currentPlayerColor = chess.White
|
currentPlayerColor = chess.White
|
||||||
} else {
|
} else {
|
||||||
currentPlayerColor = chess.Black
|
currentPlayerColor = chess.Black
|
||||||
}
|
}
|
||||||
// 第一次违例,提示
|
// 第一次违例, 提示
|
||||||
_flag := false
|
_flag := false
|
||||||
if (currentPlayerColor == chess.White) && !room.whiteErr {
|
if (currentPlayerColor == chess.White) && !room.whiteErr {
|
||||||
room.whiteErr = true
|
room.whiteErr = true
|
||||||
@ -229,23 +218,18 @@ func play(senderUin int64, groupCode int64, moveStr string) message.Message {
|
|||||||
_flag = true
|
_flag = true
|
||||||
}
|
}
|
||||||
if _flag {
|
if _flag {
|
||||||
return simpleText(fmt.Sprintf("移动「%s」违例,再次违例会立即判负。", moveStr))
|
msg = append(msg, message.Text("移动「", moveStr, "」违规, 再次违规会立即判负。"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// 出现多次违例,判负
|
// 出现多次违例, 判负
|
||||||
room.chessGame.Resign(currentPlayerColor)
|
room.chessGame.Resign(currentPlayerColor)
|
||||||
chessString := getChessString(*room)
|
chessString := getChessString(*room)
|
||||||
replyMsg := textWithAt(senderUin, "违例两次,游戏结束。\n"+chessString)
|
msg = append(msg, message.Text("违规两次,游戏结束。\n", chessString))
|
||||||
// 删除临时文件
|
|
||||||
if inkscapeExists() {
|
|
||||||
if err := cleanTempFiles(groupCode); err != nil {
|
|
||||||
log.Debugln("[chess]", "Fail to clean temp files", err)
|
|
||||||
return message.Message{message.Text("ERROR: ", err)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chessRoomMap.Delete(groupCode)
|
chessRoomMap.Delete(groupCode)
|
||||||
return replyMsg
|
return
|
||||||
}
|
}
|
||||||
// 走子之后,视为拒绝和棋
|
// 走子之后, 视为拒绝和棋
|
||||||
if room.drawPlayer != 0 {
|
if room.drawPlayer != 0 {
|
||||||
room.drawPlayer = 0
|
room.drawPlayer = 0
|
||||||
chessRoomMap.Store(groupCode, room)
|
chessRoomMap.Store(groupCode, room)
|
||||||
@ -253,26 +237,25 @@ func play(senderUin int64, groupCode int64, moveStr string) message.Message {
|
|||||||
// 生成棋盘图片
|
// 生成棋盘图片
|
||||||
var boardImgEle message.MessageSegment
|
var boardImgEle message.MessageSegment
|
||||||
if !room.isBlindfold {
|
if !room.isBlindfold {
|
||||||
boardMsg, ok, errMsg := getBoardElement(groupCode)
|
boardImgEle, err = getBoardElement(groupCode)
|
||||||
boardImgEle = boardMsg
|
if err != nil {
|
||||||
if !ok {
|
return
|
||||||
return errorText(errMsg)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 检查游戏是否结束
|
// 检查游戏是否结束
|
||||||
if room.chessGame.Method() != chess.NoMethod {
|
if room.chessGame.Method() != chess.NoMethod {
|
||||||
whiteScore, blackScore := 0.5, 0.5
|
whiteScore, blackScore := 0.5, 0.5
|
||||||
var msgBuilder strings.Builder
|
var msgBuilder strings.Builder
|
||||||
msgBuilder.WriteString("游戏结束,")
|
msgBuilder.WriteString("游戏结束, ")
|
||||||
switch room.chessGame.Method() {
|
switch room.chessGame.Method() {
|
||||||
case chess.FivefoldRepetition:
|
case chess.FivefoldRepetition:
|
||||||
msgBuilder.WriteString("和棋,因为五次重复走子。\n")
|
msgBuilder.WriteString("和棋, 因为五次重复走子。\n")
|
||||||
case chess.SeventyFiveMoveRule:
|
case chess.SeventyFiveMoveRule:
|
||||||
msgBuilder.WriteString("和棋,因为七十五步规则。\n")
|
msgBuilder.WriteString("和棋, 因为七十五步规则。\n")
|
||||||
case chess.InsufficientMaterial:
|
case chess.InsufficientMaterial:
|
||||||
msgBuilder.WriteString("和棋,因为不可能将死。\n")
|
msgBuilder.WriteString("和棋, 因为不可能将死。\n")
|
||||||
case chess.Stalemate:
|
case chess.Stalemate:
|
||||||
msgBuilder.WriteString("和棋,因为逼和(无子可动和棋)。\n")
|
msgBuilder.WriteString("和棋, 因为逼和(无子可动和棋)。\n")
|
||||||
case chess.Checkmate:
|
case chess.Checkmate:
|
||||||
var winner string
|
var winner string
|
||||||
if room.chessGame.Position().Turn() == chess.White {
|
if room.chessGame.Position().Turn() == chess.White {
|
||||||
@ -285,7 +268,7 @@ func play(senderUin int64, groupCode int64, moveStr string) message.Message {
|
|||||||
winner = "白方"
|
winner = "白方"
|
||||||
}
|
}
|
||||||
msgBuilder.WriteString(winner)
|
msgBuilder.WriteString(winner)
|
||||||
msgBuilder.WriteString("胜利,因为将杀。\n")
|
msgBuilder.WriteString("胜利, 因为将杀。\n")
|
||||||
case chess.NoMethod:
|
case chess.NoMethod:
|
||||||
case chess.Resignation:
|
case chess.Resignation:
|
||||||
case chess.DrawOffer:
|
case chess.DrawOffer:
|
||||||
@ -296,34 +279,26 @@ func play(senderUin int64, groupCode int64, moveStr string) message.Message {
|
|||||||
chessString := getChessString(*room)
|
chessString := getChessString(*room)
|
||||||
eloString := ""
|
eloString := ""
|
||||||
if len(room.chessGame.Moves()) > 4 {
|
if len(room.chessGame.Moves()) > 4 {
|
||||||
// 若走子次数超过 4 认为是有效对局,存入数据库
|
// 若走子次数超过 4 认为是有效对局, 存入数据库
|
||||||
dbService := newDBService()
|
dbService := newDBService()
|
||||||
if err := dbService.createPGN(chessString, room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName); err != nil {
|
if err = dbService.createPGN(chessString, room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName); err != nil {
|
||||||
log.Debugln("[chess]", "Fail to create PGN.", err)
|
return
|
||||||
return message.Message{message.Text("ERROR: ", err)}
|
|
||||||
}
|
}
|
||||||
// 仅有效对局才会计算等级分
|
// 仅有效对局才会计算等级分
|
||||||
elo, err := getELOString(*room, whiteScore, blackScore)
|
eloString, err = getELOString(*room, whiteScore, blackScore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugln("[chess]", "Fail to get eloString.", eloString, err)
|
return
|
||||||
return message.Message{message.Text("ERROR: ", err)}
|
|
||||||
}
|
}
|
||||||
eloString = elo
|
|
||||||
}
|
}
|
||||||
msgBuilder.WriteString(eloString)
|
msgBuilder.WriteString(eloString)
|
||||||
msgBuilder.WriteString(chessString)
|
msgBuilder.WriteString(chessString)
|
||||||
replyMsg := simpleText(msgBuilder.String())
|
msg = append(msg, message.Text(msgBuilder.String()))
|
||||||
if !room.isBlindfold {
|
if !room.isBlindfold {
|
||||||
replyMsg = append(replyMsg, boardImgEle)
|
msg = append(msg, boardImgEle)
|
||||||
}
|
|
||||||
if inkscapeExists() {
|
|
||||||
if err := cleanTempFiles(groupCode); err != nil {
|
|
||||||
log.Debugln("[chess]", "Fail to clean temp files", err)
|
|
||||||
return message.Message{message.Text("ERROR: ", err)}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chessRoomMap.Delete(groupCode)
|
chessRoomMap.Delete(groupCode)
|
||||||
return replyMsg
|
return
|
||||||
}
|
}
|
||||||
// 提示玩家继续游戏
|
// 提示玩家继续游戏
|
||||||
var currentPlayer int64
|
var currentPlayer int64
|
||||||
@ -332,49 +307,43 @@ func play(senderUin int64, groupCode int64, moveStr string) message.Message {
|
|||||||
} else {
|
} else {
|
||||||
currentPlayer = room.blackPlayer
|
currentPlayer = room.blackPlayer
|
||||||
}
|
}
|
||||||
return append(textWithAt(currentPlayer, "对手已走子,游戏继续。"), boardImgEle)
|
msg = message.Message{message.At(currentPlayer), message.Text("对手已走子, 游戏继续。"), boardImgEle}
|
||||||
}
|
return
|
||||||
|
|
||||||
// ranking 排行榜
|
|
||||||
func ranking() message.Message {
|
|
||||||
ranking, err := getRankingString()
|
|
||||||
if err != nil {
|
|
||||||
log.Debugln("[chess]", "Fail to get player ranking.", err)
|
|
||||||
return simpleText(fmt.Sprintln("服务器错误,无法获取排行榜信息。请联系开发者修 bug。", err))
|
|
||||||
}
|
|
||||||
return simpleText(ranking)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// rate 获取等级分
|
// rate 获取等级分
|
||||||
func rate(senderUin int64, senderName string) message.Message {
|
func rate(senderUin int64, senderName string) (msg message.Message, err error) {
|
||||||
|
rate := 0
|
||||||
dbService := newDBService()
|
dbService := newDBService()
|
||||||
rate, err := dbService.getELORateByUin(senderUin)
|
rate, err = dbService.getELORateByUin(senderUin)
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return simpleText("没有查找到等级分信息。请至少进行一局对局。")
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugln("[chess]", "Fail to get player rank.", err)
|
if err != gorm.ErrRecordNotFound {
|
||||||
return simpleText(fmt.Sprintln("服务器错误,无法获取等级分信息。请联系开发者修 bug。", err))
|
err = errors.New("无法获取等级分信息。")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = errors.New("没有查找到等级分信息, 请至少进行一局对局。")
|
||||||
}
|
}
|
||||||
return simpleText(fmt.Sprintf("玩家「%s」目前的等级分:%d", senderName, rate))
|
msg = append(msg, message.Text("玩家「", senderName, "」目前的等级分: ", rate))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanUserRate 清空用户等级分
|
// cleanUserRate 清空用户等级分
|
||||||
func cleanUserRate(senderUin int64) message.Message {
|
func cleanUserRate(senderUin int64) (msg message.Message, err error) {
|
||||||
dbService := newDBService()
|
dbService := newDBService()
|
||||||
err := dbService.cleanELOByUin(senderUin)
|
err = dbService.cleanELOByUin(senderUin)
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return simpleText("没有查找到等级分信息。请检查用户 uid 是否正确。")
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugln("[chess]", "Fail to clean player rank.", err)
|
if err != gorm.ErrRecordNotFound {
|
||||||
return simpleText(fmt.Sprintln("服务器错误,无法清空等级分。请联系开发者修 bug。", err))
|
err = errors.New("无法清空等级分。")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = errors.New("没有查找到等级分信息, 请检查用户 uid 是否正确。")
|
||||||
}
|
}
|
||||||
return simpleText(fmt.Sprintf("已清空用户「%d」的等级分。", senderUin))
|
msg = append(msg, message.Text("已清空用户「", senderUin, "」的等级分。"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// createGame 创建游戏
|
// createGame 创建游戏
|
||||||
func createGame(isBlindfold bool, groupCode int64, senderUin int64, senderName string) message.Message {
|
func createGame(isBlindfold bool, groupCode int64, senderUin int64, senderName string) (msg message.Message, err error) {
|
||||||
room, ok := chessRoomMap.Load(groupCode)
|
room, ok := chessRoomMap.Load(groupCode)
|
||||||
if !ok {
|
if !ok {
|
||||||
chessRoomMap.Store(groupCode, &chessRoom{
|
chessRoomMap.Store(groupCode, &chessRoom{
|
||||||
@ -389,79 +358,79 @@ func createGame(isBlindfold bool, groupCode int64, senderUin int64, senderName s
|
|||||||
whiteErr: false,
|
whiteErr: false,
|
||||||
blackErr: false,
|
blackErr: false,
|
||||||
})
|
})
|
||||||
|
text := "已创建新的对局, 发送「下棋」或「chess」可加入对局。"
|
||||||
if isBlindfold {
|
if isBlindfold {
|
||||||
return simpleText("已创建新的盲棋对局,发送「盲棋」或「blind」可加入对局。")
|
text = "已创建新的盲棋对局, 发送「盲棋」或「blind」可加入对局。"
|
||||||
}
|
}
|
||||||
return simpleText("已创建新的对局,发送「下棋」或「chess」可加入对局。")
|
msg = append(msg, message.Text(text))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
msg = message.Message{message.At(senderUin)}
|
||||||
if room.blackPlayer != 0 {
|
if room.blackPlayer != 0 {
|
||||||
// 检测对局是否已存在超过 6 小时
|
// 检测对局是否已存在超过 6 小时
|
||||||
if (time.Now().Unix() - room.lastMoveTime) > 21600 {
|
if (time.Now().Unix() - room.lastMoveTime) > 21600 {
|
||||||
autoAbortMsg := abortGame(*room, groupCode, "对局已存在超过 6 小时,游戏结束。")
|
msg, err = abortGame(*room, groupCode, "对局已存在超过 6 小时, 游戏结束。")
|
||||||
autoAbortMsg = append(autoAbortMsg, message.Text("\n\n已有对局已被中断,如需创建新对局请重新发送指令。"))
|
msg = append(msg, message.Text("\n\n已有对局已被中断, 如需创建新对局请重新发送指令。"))
|
||||||
autoAbortMsg = append(autoAbortMsg, message.At(senderUin))
|
msg = append(msg, message.At(senderUin))
|
||||||
return autoAbortMsg
|
return
|
||||||
}
|
}
|
||||||
// 对局在进行
|
// 对局在进行
|
||||||
msg := textWithAt(senderUin, "对局已在进行中,无法创建或加入对局,当前对局玩家为:")
|
msg = append(msg, message.Text("对局已在进行中, 无法创建或加入对局, 当前对局玩家为: "))
|
||||||
if room.whitePlayer != 0 {
|
if room.whitePlayer != 0 {
|
||||||
msg = append(msg, message.At(room.whitePlayer))
|
msg = append(msg, message.At(room.whitePlayer))
|
||||||
}
|
}
|
||||||
if room.blackPlayer != 0 {
|
if room.blackPlayer != 0 {
|
||||||
msg = append(msg, message.At(room.blackPlayer))
|
msg = append(msg, message.At(room.blackPlayer))
|
||||||
}
|
}
|
||||||
msg = append(msg, message.Text(",群主或管理员发送「中断」或「abort」可中断对局(自动判和)。"))
|
msg = append(msg, message.Text(", 群主或管理员发送「中断」或「abort」可中断对局(自动判和)。"))
|
||||||
return msg
|
return
|
||||||
}
|
}
|
||||||
if senderUin == room.whitePlayer {
|
if senderUin == room.whitePlayer {
|
||||||
return textWithAt(senderUin, "请等候其他玩家加入游戏。")
|
msg = append(msg, message.Text("请等候其他玩家加入游戏。"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if room.isBlindfold && !isBlindfold {
|
if room.isBlindfold && !isBlindfold {
|
||||||
return simpleText("已创建盲棋对局,请加入或等待盲棋对局结束之后创建普通对局。")
|
msg = append(msg, message.Text("已创建盲棋对局, 请加入或等待盲棋对局结束之后创建普通对局。"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if !room.isBlindfold && isBlindfold {
|
if !room.isBlindfold && isBlindfold {
|
||||||
return simpleText("已创建普通对局,请加入或等待普通对局结束之后创建盲棋对局。")
|
msg = append(msg, message.Text("已创建普通对局, 请加入或等待普通对局结束之后创建盲棋对局。"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
room.blackPlayer = senderUin
|
room.blackPlayer = senderUin
|
||||||
room.blackName = senderName
|
room.blackName = senderName
|
||||||
chessRoomMap.Store(groupCode, room)
|
chessRoomMap.Store(groupCode, room)
|
||||||
var boardImgEle message.MessageSegment
|
var boardImgEle message.MessageSegment
|
||||||
if !room.isBlindfold {
|
if !room.isBlindfold {
|
||||||
boardMsg, ok, errMsg := getBoardElement(groupCode)
|
boardImgEle, err = getBoardElement(groupCode)
|
||||||
if !ok {
|
if err != nil {
|
||||||
return errorText(errMsg)
|
return
|
||||||
}
|
}
|
||||||
boardImgEle = boardMsg
|
|
||||||
}
|
}
|
||||||
if isBlindfold {
|
msg = append(msg, message.Text("黑棋已加入对局, 请白方下棋。"), message.At(room.whitePlayer))
|
||||||
return append(simpleText("黑棋已加入对局,请白方下棋。"), message.At(room.whitePlayer))
|
if !isBlindfold {
|
||||||
|
msg = append(msg, boardImgEle)
|
||||||
}
|
}
|
||||||
return append(simpleText("黑棋已加入对局,请白方下棋。"), message.At(room.whitePlayer), boardImgEle)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// abortGame 中断游戏
|
// abortGame 中断游戏
|
||||||
func abortGame(room chessRoom, groupCode int64, hint string) message.Message {
|
func abortGame(room chessRoom, groupCode int64, hint string) (message.Message, error) {
|
||||||
|
var msg message.Message
|
||||||
err := room.chessGame.Draw(chess.DrawOffer)
|
err := room.chessGame.Draw(chess.DrawOffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugln("[chess]", "Fail to draw a game.", err)
|
return nil, err
|
||||||
return simpleText(fmt.Sprintln("程序发生了错误,和棋失败,请反馈开发者修复 bug。", err))
|
|
||||||
}
|
}
|
||||||
chessString := getChessString(room)
|
chessString := getChessString(room)
|
||||||
if len(room.chessGame.Moves()) > 4 {
|
if len(room.chessGame.Moves()) > 4 {
|
||||||
dbService := newDBService()
|
dbService := newDBService()
|
||||||
if err := dbService.createPGN(chessString, room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName); err != nil {
|
if err := dbService.createPGN(chessString, room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName); err != nil {
|
||||||
log.Debugln("[chess]", "Fail to create PGN.", err)
|
return nil, err
|
||||||
return message.Message{message.Text("ERROR: ", err)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if inkscapeExists() {
|
|
||||||
if err := cleanTempFiles(groupCode); err != nil {
|
|
||||||
log.Debugln("[chess]", "Fail to clean temp files", err)
|
|
||||||
return message.Message{message.Text("ERROR: ", err)}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chessRoomMap.Delete(groupCode)
|
chessRoomMap.Delete(groupCode)
|
||||||
msg := simpleText(hint)
|
msg = append(msg, message.Text(hint))
|
||||||
if room.whitePlayer != 0 {
|
if room.whitePlayer != 0 {
|
||||||
msg = append(msg, message.At(room.whitePlayer))
|
msg = append(msg, message.At(room.whitePlayer))
|
||||||
}
|
}
|
||||||
@ -469,26 +438,18 @@ func abortGame(room chessRoom, groupCode int64, hint string) message.Message {
|
|||||||
msg = append(msg, message.At(room.blackPlayer))
|
msg = append(msg, message.At(room.blackPlayer))
|
||||||
}
|
}
|
||||||
msg = append(msg, message.Text("\n\n"+chessString))
|
msg = append(msg, message.Text("\n\n"+chessString))
|
||||||
return msg
|
return msg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getBoardElement 获取棋盘图片的消息内容
|
// getBoardElement 获取棋盘图片的消息内容
|
||||||
func getBoardElement(groupCode int64) (message.MessageSegment, bool, string) {
|
func getBoardElement(groupCode int64) (imgMsg message.MessageSegment, err error) {
|
||||||
|
fontdata, err := file.GetLazyData(text.GNUUnifontFontFile, control.Md5File, true)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
room, ok := chessRoomMap.Load(groupCode)
|
room, ok := chessRoomMap.Load(groupCode)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Debugln(fmt.Sprintf("No room for groupCode %d.", groupCode))
|
return imgMsg, errNotExist
|
||||||
return message.MessageSegment{}, false, "对局不存在"
|
|
||||||
}
|
|
||||||
// 未安装 inkscape 直接返回对局字符串
|
|
||||||
// TODO: 使用原生 go 库渲染 svg
|
|
||||||
if !inkscapeExists() {
|
|
||||||
boardString := room.chessGame.Position().Board().Draw()
|
|
||||||
boardImageB64, err := generateCharBoardImage(boardString)
|
|
||||||
if err != nil {
|
|
||||||
return message.MessageSegment{}, false, "生成棋盘图片时发生错误"
|
|
||||||
}
|
|
||||||
replyMsg := message.Image("base64://" + boardImageB64)
|
|
||||||
return replyMsg, true, ""
|
|
||||||
}
|
}
|
||||||
// 获取高亮方块
|
// 获取高亮方块
|
||||||
highlightSquare := make([]chess.Square, 0, 2)
|
highlightSquare := make([]chess.Square, 0, 2)
|
||||||
@ -499,27 +460,72 @@ func getBoardElement(groupCode int64) (message.MessageSegment, bool, string) {
|
|||||||
highlightSquare = append(highlightSquare, lastMove.S2())
|
highlightSquare = append(highlightSquare, lastMove.S2())
|
||||||
}
|
}
|
||||||
// 生成棋盘 svg 文件
|
// 生成棋盘 svg 文件
|
||||||
svgFilePath := path.Join(tempFileDir, fmt.Sprintf("%d.svg", groupCode))
|
buf := bytes.NewBuffer([]byte{})
|
||||||
fenStr := room.chessGame.FEN()
|
fenStr := room.chessGame.FEN()
|
||||||
gameTurn := room.chessGame.Position().Turn()
|
gameTurn := room.chessGame.Position().Turn()
|
||||||
if err := generateBoardSVG(svgFilePath, fenStr, gameTurn, highlightSquare...); err != nil {
|
pos := &chess.Position{}
|
||||||
log.Debugln("[chess]", "Unable to generate svg file.", err)
|
if err = pos.UnmarshalText(binary.StringToBytes(fenStr)); err != nil {
|
||||||
return message.MessageSegment{}, false, "无法生成 svg 图片,请检查后台日志。"
|
return
|
||||||
}
|
}
|
||||||
// 调用 inkscape 将 svg 图片转化为 png 图片
|
yellow := color.RGBA{255, 255, 0, 1}
|
||||||
pngFilePath := path.Join(tempFileDir, fmt.Sprintf("%d.png", groupCode))
|
mark := cimage.MarkSquares(yellow, highlightSquare...)
|
||||||
if err := exec.Command("inkscape", "-w", "720", "-h", "720", svgFilePath, "-o", pngFilePath).Run(); err != nil {
|
board := pos.Board()
|
||||||
log.Debugln("[chess]", "Unable to convert to png.", err)
|
fromBlack := cimage.Perspective(gameTurn)
|
||||||
return message.MessageSegment{}, false, "无法生成 png 图片,请检查 inkscape 安装情况及其依赖 libfuse。"
|
err = cimage.SVG(buf, board, fromBlack, mark)
|
||||||
}
|
|
||||||
// 尝试读取 png 图片
|
|
||||||
imgData, err := os.ReadFile(pngFilePath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugln("[chess]", fmt.Sprintf("Unable to read image file in %s.", pngFilePath), err)
|
return
|
||||||
return message.MessageSegment{}, false, "无法读取 png 图片"
|
|
||||||
}
|
}
|
||||||
imgMsg := message.Image("base64://" + base64.StdEncoding.EncodeToString(imgData))
|
|
||||||
return imgMsg, true, ""
|
worker, err := resvg.NewDefaultWorker(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer worker.Close()
|
||||||
|
|
||||||
|
tree, err := worker.NewTreeFromData(buf.Bytes(), &resvg.Options{
|
||||||
|
Dpi: 96,
|
||||||
|
FontFamily: "Unifont",
|
||||||
|
FontSize: 24.0,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tree.Close()
|
||||||
|
|
||||||
|
fontdb, err := worker.NewFontDBDefault()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer fontdb.Close()
|
||||||
|
|
||||||
|
err = fontdb.LoadFontData(fontdata)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tree.ConvertText(fontdb)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pixmap, err := worker.NewPixmap(720, 720)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer pixmap.Close()
|
||||||
|
|
||||||
|
err = tree.Render(resvg.TransformFromScale(2, 2), pixmap)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := pixmap.EncodePNG()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
imgMsg = message.ImageBytes(out)
|
||||||
|
return imgMsg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getELOString 获得玩家等级分的文本内容
|
// getELOString 获得玩家等级分的文本内容
|
||||||
@ -528,55 +534,46 @@ func getELOString(room chessRoom, whiteScore, blackScore float64) (string, error
|
|||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
var msgBuilder strings.Builder
|
var msgBuilder strings.Builder
|
||||||
msgBuilder.WriteString("玩家等级分:\n")
|
msgBuilder.WriteString("玩家等级分: \n")
|
||||||
dbService := newDBService()
|
dbService := newDBService()
|
||||||
if err := updateELORate(room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName, whiteScore, blackScore, dbService); err != nil {
|
if err := updateELORate(room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName, whiteScore, blackScore, dbService); err != nil {
|
||||||
msgBuilder.WriteString("发生错误,无法更新等级分。")
|
return "", err
|
||||||
msgBuilder.WriteString(err.Error())
|
|
||||||
return msgBuilder.String(), err
|
|
||||||
}
|
}
|
||||||
whiteRate, blackRate, err := getELORate(room.whitePlayer, room.blackPlayer, dbService)
|
whiteRate, blackRate, err := getELORate(room.whitePlayer, room.blackPlayer, dbService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msgBuilder.WriteString("发生错误,无法获取等级分。")
|
return "", err
|
||||||
msgBuilder.WriteString(err.Error())
|
|
||||||
return msgBuilder.String(), err
|
|
||||||
}
|
}
|
||||||
msgBuilder.WriteString(fmt.Sprintf("%s:%d\n%s:%d\n\n", room.whiteName, whiteRate, room.blackName, blackRate))
|
msgBuilder.WriteString(room.whiteName)
|
||||||
|
msgBuilder.WriteString(": ")
|
||||||
|
msgBuilder.WriteString(strconv.Itoa(whiteRate))
|
||||||
|
msgBuilder.WriteString("\n")
|
||||||
|
msgBuilder.WriteString(room.blackName)
|
||||||
|
msgBuilder.WriteString(": ")
|
||||||
|
msgBuilder.WriteString(strconv.Itoa(blackRate))
|
||||||
|
msgBuilder.WriteString("\n\n")
|
||||||
return msgBuilder.String(), nil
|
return msgBuilder.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getRankingString 获取等级分排行榜的文本内容
|
// getRankingString 获取等级分排行榜的文本内容
|
||||||
func getRankingString() (string, error) {
|
func getRanking() (message.Message, error) {
|
||||||
dbService := newDBService()
|
dbService := newDBService()
|
||||||
eloList, err := dbService.getHighestRateList()
|
eloList, err := dbService.getHighestRateList()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
var msgBuilder strings.Builder
|
var msgBuilder strings.Builder
|
||||||
msgBuilder.WriteString("当前等级分排行榜:\n\n")
|
msgBuilder.WriteString("当前等级分排行榜: \n\n")
|
||||||
for _, elo := range eloList {
|
for _, elo := range eloList {
|
||||||
msgBuilder.WriteString(fmt.Sprintf("%s: %d\n", elo.Name, elo.Rate))
|
msgBuilder.WriteString(elo.Name)
|
||||||
|
msgBuilder.WriteString(": ")
|
||||||
|
msgBuilder.WriteString(strconv.Itoa(elo.Rate))
|
||||||
|
msgBuilder.WriteString("\n")
|
||||||
}
|
}
|
||||||
return msgBuilder.String(), nil
|
return message.Message{message.Text(msgBuilder.String())}, nil
|
||||||
}
|
|
||||||
|
|
||||||
func simpleText(msg string) message.Message {
|
|
||||||
return []message.MessageSegment{message.Text(msg)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func textWithAt(target int64, msg string) message.Message {
|
|
||||||
if target == 0 {
|
|
||||||
return simpleText("@全体成员 " + msg)
|
|
||||||
}
|
|
||||||
return []message.MessageSegment{message.At(target), message.Text(msg)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func errorText(errMsg string) message.Message {
|
|
||||||
return simpleText("发生错误,请联系开发者修 bug。\n错误信息:" + errMsg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateELORate 更新 elo 等级分
|
// updateELORate 更新 elo 等级分
|
||||||
// 当数据库中没有玩家的等级分信息时,自动新建一条记录
|
// 当数据库中没有玩家的等级分信息时, 自动新建一条记录
|
||||||
func updateELORate(whiteUin, blackUin int64, whiteName, blackName string, whiteScore, blackScore float64, dbService *chessDBService) error {
|
func updateELORate(whiteUin, blackUin int64, whiteName, blackName string, whiteScore, blackScore float64, dbService *chessDBService) error {
|
||||||
whiteRate, err := dbService.getELORateByUin(whiteUin)
|
whiteRate, err := dbService.getELORateByUin(whiteUin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -609,77 +606,6 @@ func updateELORate(whiteUin, blackUin int64, whiteName, blackName string, whiteS
|
|||||||
return dbService.updateELOByUin(blackUin, blackName, blackRate)
|
return dbService.updateELOByUin(blackUin, blackName, blackRate)
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanTempFiles 清理临时文件
|
|
||||||
func cleanTempFiles(groupCode int64) error {
|
|
||||||
svgFilePath := path.Join(tempFileDir, fmt.Sprintf("%d.svg", groupCode))
|
|
||||||
if err := os.Remove(svgFilePath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
pngFilePath := path.Join(tempFileDir, fmt.Sprintf("%d.png", groupCode))
|
|
||||||
return os.Remove(pngFilePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// generateCharBoardImage 生成文字版的棋盘
|
|
||||||
func generateCharBoardImage(boardString string) (string, error) {
|
|
||||||
boardString = strings.Trim(boardString, "\n")
|
|
||||||
const FontSize = 72
|
|
||||||
h := FontSize*8 + 36
|
|
||||||
w := FontSize*9 + 24
|
|
||||||
dc := gg.NewContext(h, w)
|
|
||||||
dc.SetRGB(1, 1, 1)
|
|
||||||
dc.Clear()
|
|
||||||
dc.SetRGB(0, 0, 0)
|
|
||||||
fontdata, err := file.GetLazyData(text.GNUUnifontFontFile, control.Md5File, true)
|
|
||||||
if err != nil {
|
|
||||||
// TODO: err solve
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if err := dc.ParseFontFace(fontdata, FontSize); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
lines := strings.Split(boardString, "\n")
|
|
||||||
if len(lines) != 9 {
|
|
||||||
lines = make([]string, 9)
|
|
||||||
lines[0] = "ERROR [500]"
|
|
||||||
lines[1] = "程序内部错误"
|
|
||||||
lines[2] = "棋盘字符串不合法"
|
|
||||||
lines[3] = "请反馈开发者修复"
|
|
||||||
}
|
|
||||||
for i := 0; i < 9; i++ {
|
|
||||||
dc.DrawString(lines[i], 18, float64(FontSize*(i+1)))
|
|
||||||
}
|
|
||||||
imgBuffer := bytes.NewBuffer([]byte{})
|
|
||||||
if err := dc.EncodePNG(imgBuffer); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
imgData, err := io.ReadAll(imgBuffer)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
imgB64 := base64.StdEncoding.EncodeToString(imgData)
|
|
||||||
return imgB64, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// generateBoardSVG 生成棋盘 SVG 图片
|
|
||||||
func generateBoardSVG(svgFilePath, fenStr string, gameTurn chess.Color, sqs ...chess.Square) error {
|
|
||||||
os.Remove(svgFilePath)
|
|
||||||
f, err := os.Create(svgFilePath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
pos := &chess.Position{}
|
|
||||||
if err := pos.UnmarshalText(binary.StringToBytes(fenStr)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
yellow := color.RGBA{255, 255, 0, 1}
|
|
||||||
mark := image.MarkSquares(yellow, sqs...)
|
|
||||||
board := pos.Board()
|
|
||||||
fromBlack := image.Perspective(gameTurn)
|
|
||||||
return image.SVG(f, board, fromBlack, mark)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getChessString 获取 PGN 字符串
|
// getChessString 获取 PGN 字符串
|
||||||
func getChessString(room chessRoom) string {
|
func getChessString(room chessRoom) string {
|
||||||
game := room.chessGame
|
game := room.chessGame
|
||||||
@ -704,12 +630,6 @@ func getELORate(whiteUin, blackUin int64, dbService *chessDBService) (whiteRate
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// inkscapeExists 判断 inkscape 是否存在
|
|
||||||
func inkscapeExists() bool {
|
|
||||||
_, err := exec.LookPath("inkscape")
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// isAprilFoolsDay 判断当前时间是否为愚人节期间
|
// isAprilFoolsDay 判断当前时间是否为愚人节期间
|
||||||
func isAprilFoolsDay() bool {
|
func isAprilFoolsDay() bool {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user