Skip to content

Commit

Permalink
支持查询两名角色
Browse files Browse the repository at this point in the history
  • Loading branch information
CuteReimu committed Jun 21, 2024
1 parent 24609aa commit d269968
Show file tree
Hide file tree
Showing 2 changed files with 251 additions and 19 deletions.
33 changes: 25 additions & 8 deletions maplebot/bots.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime/debug"
"slices"
"strconv"
"strings"
Expand Down Expand Up @@ -132,15 +133,31 @@ func handleGroupMessage(message *GroupMessage) bool {
return true
} else if strings.HasPrefix(text.Text, "查询 ") {
name := strings.TrimSpace(text.Text[len("查询"):])
if !slices.ContainsFunc([]byte(name), func(b byte) bool { return (b < '0' || b > '9') && (b < 'a' || b > 'z') && (b < 'A' || b > 'Z') }) {
go func() {
defer func() {
if err := recover(); err != nil {
slog.Error("panic recovered", "error", err)
}
parts := strings.Split(name, " ")
if len(parts) <= 1 {
if !slices.ContainsFunc([]byte(name), func(b byte) bool { return (b < '0' || b > '9') && (b < 'a' || b > 'z') && (b < 'A' || b > 'Z') }) {
go func() {
defer func() {
if err := recover(); err != nil {
slog.Error("panic recovered", "error", err)
}
}()
sendGroupMessage(message, findRole(name)...)
}()
sendGroupMessage(message, findRole(name)...)
}()
}
} else {
name1, name2 := parts[0], parts[1]
if !slices.ContainsFunc([]byte(name1), func(b byte) bool { return (b < '0' || b > '9') && (b < 'a' || b > 'z') && (b < 'A' || b > 'Z') }) &&
!slices.ContainsFunc([]byte(name2), func(b byte) bool { return (b < '0' || b > '9') && (b < 'a' || b > 'z') && (b < 'A' || b > 'Z') }) {
go func() {
defer func() {
if err := recover(); err != nil {
slog.Error("panic recovered", "error", err, "stack", debug.Stack())
}
}()
sendGroupMessage(message, findRole2(name1, name2)...)
}()
}
}
return true
} else if strings.HasPrefix(text.Text, "绑定 ") {
Expand Down
237 changes: 226 additions & 11 deletions maplebot/find_role.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,21 @@ func init() {
}
}

type graphData struct {
CurrentEXP int64 `json:"CurrentEXP"`
DateLabel string `json:"DateLabel"`
Level int `json:"Level"`
}

type findRoleReturnData struct {
CharacterData struct {
CharacterImageURL string `json:"CharacterImageURL"`
Class string `json:"Class"`
EXPPercent float64 `json:"EXPPercent"`
GraphData []struct {
CurrentEXP int64 `json:"CurrentEXP"`
DateLabel string `json:"DateLabel"`
Level int `json:"Level"`
} `json:"GraphData"`
LegionLevel int `json:"LegionLevel"`
Level int `json:"Level"`
Name string `json:"Name"`
CharacterImageURL string `json:"CharacterImageURL"`
Class string `json:"Class"`
EXPPercent float64 `json:"EXPPercent"`
GraphData []graphData `json:"GraphData"`
LegionLevel int `json:"LegionLevel"`
Level int `json:"Level"`
Name string `json:"Name"`
} `json:"CharacterData"`
}

Expand Down Expand Up @@ -272,3 +274,216 @@ func findRole(name string) MessageChain {
}
return messageChain
}

func findRole2(name1, name2 string) MessageChain {
slog.Debug("查询角色", "name1", name1, "name2", name2)
resp, err := restyClient.R().Get("https://api.maplestory.gg/v2/public/character/gms/" + name1)
if err != nil {
slog.Error("请求失败", "error", err)
return nil
}
switch resp.StatusCode() {
case 404:
return MessageChain{&Text{Text: name1 + "已身死道消"}}
case 200:
default:
slog.Error("请求失败", "status", resp.StatusCode())
return nil
}
body := resp.Body()
var data1 *findRoleReturnData
if err = json.Unmarshal(body, &data1); err != nil {
slog.Error("json解析失败", "error", err, "body", body)
return nil
}
a := data1.CharacterData.GraphData
if len(a) == 0 {
return MessageChain{&Text{Text: "查询不到" + name1 + "数据"}}
}

resp, err = restyClient.R().Get("https://api.maplestory.gg/v2/public/character/gms/" + name2)
if err != nil {
slog.Error("请求失败", "error", err)
return nil
}
switch resp.StatusCode() {
case 404:
return MessageChain{&Text{Text: name2 + "已身死道消"}}
case 200:
default:
slog.Error("请求失败", "status", resp.StatusCode())
return nil
}
body = resp.Body()
var data2 *findRoleReturnData
if err = json.Unmarshal(body, &data2); err != nil {
slog.Error("json解析失败", "error", err, "body", body)
return nil
}
b := data2.CharacterData.GraphData
if len(b) == 0 {
return MessageChain{&Text{Text: "查询不到" + name2 + "数据"}}
}

expValues1 := make([]float64, 0, 14)
levelValues1 := make([]float64, 0, 15)
expValues2 := make([]float64, 0, 14)
levelValues2 := make([]float64, 0, 15)
labels := make([]string, 0, 15)
for _, d := range a {
labels = append(labels, d.DateLabel)
}
for _, d := range b {
if !slices.Contains(labels, d.DateLabel) {
labels = append(labels, d.DateLabel)
}
}
if len(labels) < 15 {
t, err := time.Parse("2006-01-02", labels[0])
for len(labels) < 15 {
if err != nil {
labels = append([]string{""}, labels...)
} else {
t = t.Add(-24 * time.Hour)
labels = append([]string{t.Format("2006-01-02")}, labels...)
}
}
}
slices.Sort(labels)
calLevel := func(label string, a []graphData) float64 {
var level float64
index := slices.IndexFunc(a, func(s graphData) bool {
return s.DateLabel == label
})
if index >= 0 {
level = float64(a[index].Level)
}
if level < 220 {
level = 220
} else if levelExp := levelExpData.GetFloat64(fmt.Sprintf("data.%d", int(level))); levelExp > 0 {
level += float64(a[index].CurrentEXP) / levelExp
}
return level
}
for _, label := range labels {
levelValues1 = append(levelValues1, calLevel(label, a))
levelValues2 = append(levelValues2, calLevel(label, b))
}
// 处理一下,有可能有的数据是0级
dealData := func(levelValues []float64, expValues *[]float64) {
for i := 1; i < len(levelValues); i++ {
if levelValues[i] == 0 {
levelValues[i] = levelValues[i-1]
}
}
for i := len(levelValues) - 2; i >= 0; i-- {
if levelValues[i] == 0 {
levelValues[i] = levelValues[i+1]
}
}
for i := 1; i < len(levelValues); i++ {
level0, level1 := int(levelValues[i-1]), int(levelValues[i])
expPercent0, expPercent1 := levelValues[i-1]-float64(level0), levelValues[i]-float64(level1)
var totalExp float64
for j := level0; j < level1; j++ {
totalExp += levelExpData.GetFloat64(fmt.Sprintf("data.%d", j))
}
totalExp -= expPercent0 * levelExpData.GetFloat64(fmt.Sprintf("data.%d", level0))
totalExp += expPercent1 * levelExpData.GetFloat64(fmt.Sprintf("data.%d", level1))
*expValues = append(*expValues, max(math.Round(totalExp), 0))
}
}
dealData(levelValues1, &expValues1)
dealData(levelValues2, &expValues2)
labels = labels[1:]
for i := range labels {
labels[i] = labels[i][5:]
}
levelValues1 = levelValues1[1:]
levelValues2 = levelValues2[1:]
maxValue := max(slices.Max(expValues1), slices.Max(expValues2))
digits := int(math.Floor(math.Log10(maxValue))) + 1
factor := math.Pow(10, float64(digits-1))
highest := math.Floor(maxValue / factor)
if highest < 2 {
factor /= 5
} else if highest < 5 {
factor /= 2
}
maxValue = math.Ceil(maxValue/factor) * factor
divideCount := int(maxValue / factor)
var yAxisOptions []YAxisOption
yAxisOptions = append(yAxisOptions, YAxisOption{Min: NewFloatPoint(0), Max: &maxValue, DivideCount: divideCount, Unit: 1})
for _, diff := range []float64{0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, 100} {
minLevel := math.Floor(min(slices.Min(levelValues1), slices.Min(levelValues2))/diff) * diff
maxLevel := math.Ceil(max(slices.Max(levelValues1), slices.Max(levelValues2))/diff) * diff
remainCount := divideCount - int(math.Round((maxLevel-minLevel)/diff))
if remainCount >= 0 {
if remainCount > 0 {
maxLevel += diff * float64(remainCount-remainCount/2)
minLevel -= diff * float64(remainCount/2)
}
if minLevel <= 220 { // 为了图表好看,最低显示220级
maxLevel -= minLevel - 220
minLevel = 220
}
yAxisOptions = append(yAxisOptions, YAxisOption{Min: NewFloatPoint(minLevel), Max: NewFloatPoint(maxLevel), DivideCount: divideCount, Unit: 1})
break
}
}
if slices.ContainsFunc(expValues1, func(f float64) bool { return f != 0 }) ||
slices.ContainsFunc(expValues2, func(f float64) bool { return f != 0 }) {
var seriesList []Series
seriesList = append(seriesList, NewSeriesFromValues(expValues1, ChartTypeBar))
seriesList = append(seriesList, NewSeriesFromValues(expValues2, ChartTypeBar))
if len(levelValues1) == len(expValues1) {
seriesList = append(seriesList, NewSeriesFromValues(levelValues1, ChartTypeLine))
seriesList[2].AxisIndex = 1
}
if len(levelValues2) == len(expValues2) {
seriesList = append(seriesList, NewSeriesFromValues(levelValues2, ChartTypeLine))
seriesList[3].AxisIndex = 1
}
p, err := Render(
ChartOption{SeriesList: seriesList},
ThemeOptionFunc(ThemeDark),
PaddingOptionFunc(Box{Top: 40, Left: 10, Right: 10, Bottom: 10}),
XAxisDataOptionFunc(labels),
LegendOptionFunc(LegendOption{
Data: []string{name1, name2 + " ", name1 + " ", name2},
Top: "-30",
}),
YAxisOptionFunc(yAxisOptions...),
func(opt *ChartOption) {
opt.XAxis.TextRotation = -math.Pi / 4
opt.XAxis.LabelOffset = Box{Top: -5, Left: 7}
opt.XAxis.FontSize = 7.5
opt.ValueFormatter = func(f float64) string {
switch {
case f == 220.0:
return "\u3000" // 不显示220级的坐标,用全角空格代替
case f < 1000.0:
return fmt.Sprintf("%g", math.Round(f*1000)/1000)
case f < 1000000.0:
return fmt.Sprintf("%gK", math.Round(f)/1000)
case f < 1000000000.0:
return fmt.Sprintf("%gM", math.Round(f)/1000000)
case f < 1000000000000.0:
return fmt.Sprintf("%gB", math.Round(f)/1000000000)
default:
return fmt.Sprintf("%gT", math.Round(f)/1000000000000)
}
}
},
)
if err != nil {
slog.Error("render chart failed", "error", err)
} else if buf, err := p.Bytes(); err != nil {
slog.Error("render chart failed", "error", err)
} else {
return MessageChain{&Image{File: "base64://" + base64.StdEncoding.EncodeToString(buf)}}
}
return MessageChain{&Text{Text: "生成图片失败"}}
}
return MessageChain{&Text{Text: "近日无经验变化"}}
}

0 comments on commit d269968

Please sign in to comment.