diff --git a/README.md b/README.md
index a8a17754af..b283fd7360 100644
--- a/README.md
+++ b/README.md
@@ -1289,6 +1289,23 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] 来份网易云热评
+
+
+ WarframeAPI
+
+ `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/warframeapi"`
+
+ - [x] wf时间同步
+
+ - [x] [金星|地球|火卫二]平原状态
+
+ - [x] .wm [物品名称]
+
+ - [x] 仲裁
+
+ - [x] 警报
+
+ - [x] 每日特惠
天气/拼音查询-名言
diff --git a/go.mod b/go.mod
index a1b74c8565..42d9b4cd56 100644
--- a/go.mod
+++ b/go.mod
@@ -15,6 +15,7 @@ require (
github.com/RomiChan/websocket v1.4.3-0.20220227141055-9b2c6168c9c5
github.com/antchfx/htmlquery v1.2.5
github.com/corona10/goimagehash v1.1.0
+ github.com/davidscholberg/go-durationfmt v0.0.0-20170122144659-64843a2083d3
github.com/fumiama/ahsai v0.1.0
github.com/fumiama/cron v1.3.0
github.com/fumiama/go-base16384 v1.6.4
@@ -25,6 +26,7 @@ require (
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/jinzhu/gorm v1.9.16
github.com/jozsefsallai/gophersauce v1.0.1
+ github.com/lithammer/fuzzysearch v1.1.5
github.com/lucas-clemente/quic-go v0.31.1
github.com/mroth/weightedrand v1.0.0
github.com/pkg/errors v0.9.1
diff --git a/go.sum b/go.sum
index 6bd88e83da..baf20eef3b 100644
--- a/go.sum
+++ b/go.sum
@@ -38,6 +38,8 @@ github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b/go.mod h1:Oozb
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davidscholberg/go-durationfmt v0.0.0-20170122144659-64843a2083d3 h1:qshMBFxVjYjzI+kwvWvgoByF3uMCvnJiaK8KslWAbr8=
+github.com/davidscholberg/go-durationfmt v0.0.0-20170122144659-64843a2083d3/go.mod h1:M9fx6rAdHSYLKxXPgUXGgblb586CA7ceNrpu4DEc2No=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
@@ -126,6 +128,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lithammer/fuzzysearch v1.1.5 h1:Ag7aKU08wp0R9QCfF4GoGST9HbmAIeLP7xwMrOBEp1c=
+github.com/lithammer/fuzzysearch v1.1.5/go.mod h1:1R1LRNk7yKid1BaQkmuLQaHruxcC4HmAH30Dh61Ih1Q=
github.com/lucas-clemente/quic-go v0.31.1 h1:O8Od7hfioqq0PMYHDyBkxU2aA7iZ2W9pjbrWuja2YR4=
github.com/lucas-clemente/quic-go v0.31.1/go.mod h1:0wFbizLgYzqHqtlyxyCaJKlE7bYgE6JQ+54TLd/Dq2g=
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
diff --git a/main.go b/main.go
index a3be4c50a4..012bcd174d 100644
--- a/main.go
+++ b/main.go
@@ -132,6 +132,7 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/vtb_quotation" // vtb语录
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wallet" // 钱包
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wangyiyun" // 网易云音乐热评
+ _ "github.com/FloatTech/ZeroBot-Plugin/plugin/warframeapi" // warframeAPI插件
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wenben" // 文本指令大全
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wenxinAI" // 百度文心AI画图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/word_count" // 聊天热词
@@ -261,7 +262,6 @@ func init() {
logrus.Infoln("[main] 从", *runcfg, "读取配置文件")
return
}
-
config.W = []*driver.WSClient{driver.NewWebSocketClient(*url, *token)}
config.Z = zero.Config{
NickName: append([]string{*adana}, "ATRI", "atri", "亚托莉", "アトリ"),
diff --git a/plugin/warframeapi/gametime.go b/plugin/warframeapi/gametime.go
new file mode 100644
index 0000000000..c62f0bd190
--- /dev/null
+++ b/plugin/warframeapi/gametime.go
@@ -0,0 +1,78 @@
+package warframeapi
+
+import (
+ "github.com/davidscholberg/go-durationfmt"
+ "sync"
+ "time"
+)
+
+// 游戏时间模拟
+type gameTime struct {
+ rwm sync.RWMutex
+ Name string `json:"name"` //时间名称
+ NextTime time.Time `json:"time"` //下次更新时间
+ Status bool `json:"status"` //状态
+ StatusTrueDes string `json:"true_des"` //状态说明
+ StatusFalseDes string `json:"false_des"` //状态说明
+ DayTime int `json:"day"` //白天时长
+ NightTime int `json:"night"` //夜间时长
+}
+
+var (
+ gameTimes [3]*gameTime
+)
+
+// TimeString 根据传入的世界编号,获取对应的游戏时间文本
+func (t *gameTime) String() string {
+ return "平原时间:" + t.daynight() + "\n" +
+ "下次更新:" + t.remaintime()
+}
+
+// 获取当前游戏时间状态(白天/夜晚)
+func (t *gameTime) daynight() string {
+ t.rwm.RLock()
+ defer t.rwm.RUnlock()
+ if t.Status {
+ return t.StatusTrueDes
+ }
+ return t.StatusFalseDes
+}
+
+// 获取下一次时间状态更新的剩余游戏时间(x分x秒)
+func (t *gameTime) remaintime() string {
+ t.rwm.RLock()
+ d := time.Until(t.NextTime)
+ t.rwm.RUnlock()
+ durStr, _ := durationfmt.Format(d, "%m分%s秒后")
+ return durStr
+}
+
+// 根据API返回内容修正游戏时间
+func loadTime(api wfAPI) {
+ gameTimes = [3]*gameTime{
+ {Name: "地球平原", NextTime: api.CetusCycle.Expiry.Local(), Status: api.CetusCycle.IsDay, StatusTrueDes: "白天", StatusFalseDes: "夜晚", DayTime: 100 * 60, NightTime: 50 * 60},
+ {Name: "金星平原", NextTime: api.VallisCycle.Expiry.Local(), Status: api.VallisCycle.IsWarm, StatusTrueDes: "温暖", StatusFalseDes: "寒冷", DayTime: 400, NightTime: 20 * 60},
+ {Name: "火卫二平原", NextTime: api.CambionCycle.Expiry.Local(), Status: api.CambionCycle.Active == "fass", StatusTrueDes: "fass", StatusFalseDes: "vome", DayTime: 100 * 60, NightTime: 50 * 60},
+ }
+}
+
+// timeDet游戏时间更新
+func timeDet() {
+ for _, v := range gameTimes {
+ //当前时间对比下一次游戏状态更新时间,看看还剩多少秒
+ nt := time.Until(v.NextTime).Seconds()
+ //已经过了游戏时间状态更新时间
+ if nt < 0 {
+ v.rwm.Lock()
+ //更新游戏状态,如果是白天就切换到晚上,反之亦然
+ if v.Status {
+ //计算下次的晚上更新时间
+ v.NextTime = v.NextTime.Add(time.Duration(v.NightTime) * time.Second)
+ } else {
+ //计算下次的白天更新时间
+ v.NextTime = v.NextTime.Add(time.Duration(v.DayTime) * time.Second)
+ }
+ v.rwm.Unlock()
+ }
+ }
+}
diff --git a/plugin/warframeapi/main.go b/plugin/warframeapi/main.go
new file mode 100644
index 0000000000..913cb98806
--- /dev/null
+++ b/plugin/warframeapi/main.go
@@ -0,0 +1,507 @@
+// Package warframeapi 百度内容审核
+package warframeapi
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/FloatTech/floatbox/binary"
+ "github.com/FloatTech/floatbox/web"
+ ctrl "github.com/FloatTech/zbpctrl"
+ "github.com/FloatTech/zbputils/control"
+ "github.com/FloatTech/zbputils/img/text"
+ "github.com/lithammer/fuzzysearch/fuzzy"
+ zero "github.com/wdvxdr1123/ZeroBot"
+ "github.com/wdvxdr1123/ZeroBot/message"
+ "net/http"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+var (
+ wmitems map[string]items //WarFrame市场的中文名称对应的物品的字典
+ itmeNames []string //物品名称列表
+ rt runtime
+)
+
+// 时间同步状态
+type runtime struct {
+ rwm sync.RWMutex
+ enable bool //是否启动
+}
+
+const wfapiurl = "https://api.warframestat.us/pc" //星际战甲API
+const wfitemurl = "https://api.warframe.market/v1/items" //星际战甲游戏品信息列表URL
+
+func init() {
+ eng := control.Register("warframeapi", &ctrl.Options[*zero.Ctx]{
+ DisableOnDefault: false,
+ Help: "warframeapi\n" +
+ "- wf时间同步\n" +
+ "- [金星|地球|火卫二]平原时间\n" +
+ "- .wm [物品名称]\n" +
+ "- 仲裁\n" +
+ "- 警报\n" +
+ "- 每日特惠",
+ PrivateDataFolder: "warframeapi",
+ })
+ updateWM()
+
+ //获取具体的平原时间,在触发后,会启动持续时间按5分钟的时间更新模拟,以此处理短时间内请求时,时间不会变化的问题
+ eng.OnSuffix("平原时间").SetBlock(true).
+ Handle(func(ctx *zero.Ctx) {
+ if !rt.enable { //没有进行同步,就拉取一次服务器状态
+ wfapi, err := wfapiGetData()
+ if err != nil {
+ ctx.SendChain(message.Text("Error:获取服务器时间失败"))
+ }
+ loadTime(wfapi)
+ }
+ switch ctx.State["args"].(string) {
+ case "地球", "夜灵":
+ ctx.SendChain(message.Text(gameTimes[0]))
+ case "金星", "奥布山谷":
+ ctx.SendChain(message.Text(gameTimes[1]))
+ case "魔胎之境", "火卫二", "火卫":
+ ctx.SendChain(message.Text(gameTimes[2]))
+ default:
+ ctx.SendChain(message.Text("ERROR: 平原不存在"))
+ }
+ // 是否正在进行同步,没有就开启同步,有就不开启
+ if !rt.enable {
+ // 设置标志位
+ rt.rwm.Lock()
+ if rt.enable { //预检测,防止其他线程同时进来
+ return
+ }
+ rt.enable = true
+ rt.rwm.Unlock()
+
+ go func() {
+ //30*10=300=5分钟
+ for i := 0; i < 30; i++ {
+ time.Sleep(10 * time.Second)
+ timeDet() //5分钟内每隔10秒更新一下时间
+ }
+ //5分钟时间同步结束
+ rt.rwm.Lock()
+ rt.enable = false
+ rt.rwm.Unlock()
+ }()
+
+ }
+ })
+ eng.OnFullMatch("警报").SetBlock(true).
+ Handle(func(ctx *zero.Ctx) {
+ wfapi, err := wfapiGetData()
+ if err != nil {
+ ctx.SendChain(message.Text("ERROR:", err.Error()))
+ return
+ }
+ //如果返回的wfapi中,警报数量>0
+ if len(wfapi.Alerts) > 0 {
+ //遍历警报数据,打印警报信息
+ for _, v := range wfapi.Alerts {
+ //如果警报处于激活状态
+ if v.Active {
+ ctx.SendChain(stringArrayToImage([]string{
+ "节点:" + v.Mission.Node,
+ "类型:" + v.Mission.Type,
+ "敌人Lv:" + fmt.Sprint(v.Mission.MinEnemyLevel) + "~" + fmt.Sprint(v.Mission.MaxEnemyLevel),
+ "奖励:" + v.Mission.Reward.AsString,
+ "剩余时间:" + v.Eta,
+ }))
+
+ }
+ }
+ }
+
+ })
+ //TODO:订阅功能-等待重做
+ //eng.OnRegex(`^(订阅|取消订阅)(.*)平原(.*)$`).SetBlock(true).
+ // Handle(func(ctx *zero.Ctx) {
+ // args := ctx.State["regex_matched"].([]string)
+ // var isEnable bool
+ // if args[1] == "订阅" {
+ // isEnable = true
+ // }
+ // updateWFAPI()
+ // status := false
+ // switch args[3] {
+ // case "fass", "白天", "温暖":
+ // status = true
+ // }
+ // switch args[2] {
+ // case "金星", "奥布山谷":
+ // //sublist = append(sublist, subList{ctx.Event.GroupID, ctx.Event.UserID, 1, status, false})
+ // if isEnable {
+ // addUseSub(ctx.Event.UserID, ctx.Event.GroupID, 1, status)
+ // } else {
+ // removeUseSub(ctx.Event.UserID, ctx.Event.GroupID, 1)
+ // }
+ // ctx.SendChain(
+ // message.At(ctx.Event.UserID),
+ // message.Text("已成功", args[1]),
+ // message.Text(gameTimes[1].Name),
+ // message.Text(status),
+ // )
+ // case "地球", "夜灵":
+ // if isEnable {
+ // addUseSub(ctx.Event.UserID, ctx.Event.GroupID, 0, status)
+ // } else {
+ // removeUseSub(ctx.Event.UserID, ctx.Event.GroupID, 0)
+ // }
+ // ctx.SendChain(
+ // message.At(ctx.Event.UserID),
+ // message.Text("已成功", args[1]),
+ // message.Text(gameTimes[0].Name),
+ // message.Text(status),
+ // )
+ // case "魔胎之境", "火卫", "火卫二":
+ // if isEnable {
+ // addUseSub(ctx.Event.UserID, ctx.Event.GroupID, 2, status)
+ // } else {
+ // removeUseSub(ctx.Event.UserID, ctx.Event.GroupID, 2)
+ // }
+ // ctx.SendChain(
+ // message.At(ctx.Event.UserID),
+ // message.Text("已成功", args[1]),
+ // message.Text(gameTimes[2].Name),
+ // message.Text(status),
+ // )
+ // default:
+ // ctx.SendChain(message.Text("ERROR: 平原不存在"))
+ // return
+ // }
+ // })
+ //eng.OnFullMatch(`wf订阅检测`).SetBlock(true).Handle(func(ctx *zero.Ctx) {
+ // rwm.Lock()
+ // var msg []message.MessageSegment
+ // for i, v := range gameTimes {
+ // nt := time.Until(v.NextTime).Seconds()
+ // switch {
+ // case nt < 0:
+ // if v.Status {
+ // v.NextTime = v.NextTime.Add(time.Duration(v.NightTime) * time.Second)
+ // } else {
+ // v.NextTime = v.NextTime.Add(time.Duration(v.DayTime) * time.Second)
+ // }
+ // v.Status = !v.Status
+ //
+ // msg = callUser(i, v.Status, 0)
+ // case nt < float64(5)*60:
+ // msg = callUser(i, !v.Status, 5)
+ // case nt < float64(15)*60:
+ // if i == 2 && !v.Status {
+ // return
+ // }
+ // msg = callUser(i, !v.Status, 15)
+ // }
+ // }
+ // rwm.Unlock()
+ // if msg != nil && len(msg) > 0 {
+ // ctx.SendChain(msg...)
+ // }
+ //})
+ eng.OnFullMatch("仲裁").SetBlock(true).
+ Handle(func(ctx *zero.Ctx) {
+ //通过wfapi获取仲裁信息
+ wfapi, err := wfapiGetData()
+ if err != nil {
+ ctx.SendChain(message.Text("ERROR:", err.Error()))
+ return
+ }
+ ctx.SendChain(stringArrayToImage([]string{
+ "节点:" + wfapi.Arbitration.Node,
+ "类型:" + wfapi.Arbitration.Type,
+ "阵营:" + wfapi.Arbitration.Enemy,
+ "剩余时间:" + fmt.Sprint(int(wfapi.Arbitration.Expiry.Sub(time.Now().UTC()).Minutes())) + "m",
+ }))
+ })
+ eng.OnFullMatch("每日特惠").SetBlock(true).
+ Handle(func(ctx *zero.Ctx) {
+ wfapi, err := wfapiGetData()
+
+ if err != nil {
+ ctx.SendChain(message.Text("ERROR:", err.Error()))
+ return
+ }
+ for _, dd := range wfapi.DailyDeals {
+ ctx.SendChain(
+ message.Text(
+ "物品:", dd.Item, "\n",
+ "价格:", dd.OriginalPrice, "→", dd.SalePrice, "\n",
+ "数量:(", dd.Total, "/", dd.Sold, ")\n",
+ "时间:", dd.Eta,
+ ),
+ )
+ }
+ })
+ // eng.OnRegex(`^入侵$`).SetBlock(true).
+ // Handle(func(ctx *zero.Ctx) {
+ // updateWFAPI(ctx)
+ // for _, dd := range wfapi.dailyDeals {
+ // imagebuild.DrawTextSend([]string{
+ // "节点:" + wfapi.arbitration.Node,
+ // "类型:" + wfapi.arbitration.Type,
+ // "阵营:" + wfapi.arbitration.Enemy,
+ // "剩余时间:" + fmt.Sprint(int(wfapi.arbitration.Expiry.Sub(time.Now().UTC()).Minutes())) + "m",
+ // }, ctx)
+ // }
+ // })
+ eng.OnFullMatch("wf时间同步").SetBlock(true).
+ Handle(func(ctx *zero.Ctx) {
+ wfapi, err := wfapiGetData()
+ if err != nil {
+ ctx.SendChain(message.Text("ERROR:", err.Error()))
+ return
+ }
+ loadTime(wfapi)
+ ctx.SendChain(message.Text("已拉取服务器时间并同步到本地模拟"))
+ })
+ // 根据名称从Warframe市场查询物品售价
+ eng.OnPrefix(".wm ").SetBlock(true).
+ Handle(func(ctx *zero.Ctx) {
+ //根据输入的名称,从游戏物品名称列表中进行模糊搜索
+ sol := fuzzy.FindNormalizedFold(ctx.State["args"].(string), itmeNames)
+ var msg []string
+ //物品名称
+ var name string
+
+ //根据搜搜结果,打印找到的物品
+ switch len(sol) {
+ case 0: //没有搜索到任何东西
+ ctx.SendChain(message.Text("无法查询到该物品"))
+ return
+ case 1: //如果只搜索到了一个
+ name = sol[0]
+ default: //如果搜搜到了多个
+ //遍历搜索结果,并打印为图片展出
+ for i, v := range sol {
+ msg = append(msg, fmt.Sprintf("[%d] %s", i, v))
+ }
+ msg = append(msg, "包含多个结果,请输入编号查看(15s内),输入c直接结束会话")
+ ctx.SendChain(stringArrayToImage(msg))
+ msg = []string{}
+
+ itemIndex := itemNameFutureEvent(ctx, 2)
+ if itemIndex == -1 {
+ return
+ }
+ name = sol[itemIndex]
+ }
+ Mf := false
+ GETWM:
+ if Mf {
+ msg = []string{}
+ }
+ sells, itmeinfo, txt, err := wmItemOrders(wmitems[name].URLName, Mf)
+ if !Mf {
+ if itmeinfo.ZhHans.WikiLink == "" {
+ ctx.Send([]message.MessageSegment{
+ message.Image("https://warframe.market/static/assets/" + wmitems[name].Thumb),
+ message.Text(wmitems[name].ItemName, "\n"),
+ })
+ } else {
+ ctx.Send([]message.MessageSegment{
+ message.Image("https://warframe.market/static/assets/" + wmitems[name].Thumb),
+ message.Text(wmitems[name].ItemName, "\n"),
+ message.Text("wiki:", itmeinfo.ZhHans.WikiLink),
+ })
+ }
+ }
+
+ msg = append(msg, wmitems[name].ItemName)
+
+ if err != nil {
+ ctx.Send(message.Text("Error:", err.Error()))
+ return
+ }
+ if sells == nil {
+ ctx.Send(message.Text("无可购买对象"))
+ return
+ }
+
+ ismod := false
+ if itmeinfo.ModMaxRank != 0 {
+ ismod = true
+ }
+
+ max := 5
+ if len(sells) <= max {
+ max = len(sells)
+ }
+ for i := 0; i < max; i++ {
+ if ismod {
+ msg = append(msg, fmt.Sprintf("[%d](Rank:%d/%d) %dP - %s\n", i, sells[i].ModRank, itmeinfo.ModMaxRank, sells[i].Platinum, sells[i].User.IngameName))
+ } else {
+ msg = append(msg, fmt.Sprintf("[%d] %dP -%s\n", i, sells[i].Platinum, sells[i].User.IngameName))
+ }
+ }
+
+ if ismod && !Mf {
+ msg = append(msg, "请输入编号选择,或输入r获取满级报价(30s内)\n输入c直接结束会话")
+ } else {
+ msg = append(msg, "请输入编号选择(30s内)\n输入c直接结束会话")
+ }
+ ctx.SendChain(stringArrayToImage(msg))
+
+ GETNUM3:
+ next := zero.NewFutureEvent("message", 999, false, ctx.CheckSession()).Next()
+ select {
+ case <-time.After(time.Second * 30):
+ ctx.SendChain(message.Text("会话已结束!"))
+ return
+ case e := <-next:
+ msg := e.Event.Message.ExtractPlainText()
+ // 重新获取报价
+ if msg == "r" {
+ Mf = true
+ goto GETWM
+ }
+ // 主动结束会话
+ if msg == "c" {
+ ctx.SendChain(message.Text("会话已结束!"))
+ return
+ }
+ i, err := strconv.Atoi(msg)
+ if err != nil {
+ ctx.SendChain(message.Text("请输入数字!(输入c结束会话)"))
+ goto GETNUM3
+ }
+ if err == nil {
+ if ismod {
+ ctx.Send(message.Text("/w ", sells[i].User.IngameName, " Hi! I want to buy: ", txt, "(Rank:", sells[i].ModRank, ") for ", sells[i].Platinum, " platinum. (warframe.market)"))
+ } else {
+ ctx.Send(message.Text("/w ", sells[i].User.IngameName, " Hi! I want to buy: ", txt, " for ", sells[i].Platinum, " platinum. (warframe.market)"))
+ }
+ }
+ }
+ })
+}
+
+// 获取搜索结果中的物品具体名称index的FutureEvent,传入ctx和一个递归次数上限,返回一个int,如果为返回内容为-1,说明会话超时,或主动结束,或超出递归
+func itemNameFutureEvent(ctx *zero.Ctx, count int) int {
+ next := zero.NewFutureEvent("message", 999, false, ctx.CheckSession()).Next()
+ select {
+ case <-time.After(time.Second * 15):
+ //超时15秒处理
+ ctx.SendChain(message.Text("会话已超时!"))
+ return -1
+ case e := <-next:
+ msg := e.Event.Message.ExtractPlainText()
+ //输入c主动结束的处理
+ if msg == "c" {
+ ctx.SendChain(message.Text("会话已结束!"))
+ return -1
+ }
+ //尝试对输入进行数字转换
+ num, err := strconv.Atoi(msg)
+ //如果出错,说明输入的并非数字,则重新触发该内容
+ if err != nil {
+ //查看是否超时
+ if count == 0 {
+ ctx.SendChain(message.Text("连续输入错误,会话已结束!"))
+ return -1
+ }
+ ctx.SendChain(message.Text("请输入数字!(输入c结束会话)[", count, "]"))
+ count--
+ return itemNameFutureEvent(ctx, count)
+
+ }
+ return num
+ }
+}
+
+// 数组字符串转图片
+func stringArrayToImage(texts []string) message.MessageSegment {
+ b, err := text.RenderToBase64(strings.Join(texts, "\n"), text.FontFile, 400, 20)
+ if err != nil {
+ return message.Text("ERROR: ", err)
+ }
+ return message.Image("base64://" + binary.BytesToString(b))
+}
+
+// 从WFapi获取数据
+func wfapiGetData() (wfAPI, error) {
+ var wfapi wfAPI //WarFrameAPI的数据实例
+ var data []byte
+ var err error
+ data, err = web.GetData(wfapiurl)
+ if err != nil {
+ return wfapi, err
+ }
+ err = json.Unmarshal(data, &wfapi)
+ if err != nil {
+ return wfapi, err
+ }
+ return wfapi, nil
+}
+
+// 从WF市场获取物品数据信息
+func updateWM() {
+ var itmeapi wfAPIItem //WarFrame市场的数据实例
+
+ data, err := web.RequestDataWithHeaders(&http.Client{}, wfitemurl, "GET", func(request *http.Request) error {
+ request.Header.Add("Accept", "application/json")
+ request.Header.Add("Language", "zh-hans")
+ return nil
+ }, nil)
+ if err != nil {
+ panic(err)
+ }
+ err = json.Unmarshal(data, &itmeapi)
+ if err != nil {
+ panic(err)
+ }
+ loadToFuzzy(itmeapi)
+}
+
+// 获取Warframe市场的售价表,并进行排序,cn_name为物品中文名称,onlyMaxRank表示只取最高等级的物品,返回物品售价表,物品信息,物品英文
+func wmItemOrders(cnName string, onlyMaxRank bool) (orders, itemsInSet, string, error) {
+
+ var wfapiio wfAPIItemsOrders
+ data, err := web.RequestDataWithHeaders(&http.Client{}, fmt.Sprintf("https://api.warframe.market/v1/items/%s/orders?include=item", cnName), "GET", func(request *http.Request) error {
+ request.Header.Add("Accept", "application/json")
+ request.Header.Add("Platform", "pc")
+ return nil
+ }, nil)
+ if err != nil {
+ return nil, itemsInSet{}, "", err
+ }
+ err = json.Unmarshal(data, &wfapiio)
+ var sellOrders orders
+ //遍历市场物品列表
+ for _, v := range wfapiio.Payload.Orders {
+ //取其中类型为售卖,且去掉不在线的玩家
+ if v.OrderType == "sell" && v.User.Status != "offline" {
+ //如果需要满级报价
+ if onlyMaxRank && v.ModRank == wfapiio.Include.Item.ItemsInSet[0].ModMaxRank {
+ sellOrders = append(sellOrders, v)
+ } else if !onlyMaxRank {
+ sellOrders = append(sellOrders, v)
+ }
+
+ }
+ }
+ //对报价表进行排序,由低到高
+ sort.Sort(sellOrders)
+ //获取物品信息
+ for i, v := range wfapiio.Include.Item.ItemsInSet {
+ if v.URLName == cnName {
+ return sellOrders, wfapiio.Include.Item.ItemsInSet[i], wfapiio.Include.Item.ItemsInSet[i].En.ItemName, err
+ }
+ }
+ return sellOrders, wfapiio.Include.Item.ItemsInSet[0], wfapiio.Include.Item.ItemsInSet[0].En.ItemName, err
+}
+
+func loadToFuzzy(wminfo wfAPIItem) {
+ wmitems = make(map[string]items)
+ itmeNames = []string{}
+ for _, v := range wminfo.Payload.Items {
+ wmitems[v.ItemName] = v
+ itmeNames = append(itmeNames, v.ItemName)
+ }
+}
diff --git a/plugin/warframeapi/wfdata.go b/plugin/warframeapi/wfdata.go
new file mode 100644
index 0000000000..89c822d5ac
--- /dev/null
+++ b/plugin/warframeapi/wfdata.go
@@ -0,0 +1,630 @@
+package warframeapi
+
+import "time"
+
+type wfAPI struct {
+ Timestamp time.Time `json:"timestamp"`
+ News []news `json:"news"`
+ Events []events `json:"events"`
+ Alerts []alerts `json:"alerts"`
+ Sortie sortie `json:"sortie"`
+ SyndicateMissions []syndicateMissions `json:"syndicateMissions"`
+ Fissures []fissures `json:"fissures"`
+ GlobalUpgrades []interface{} `json:"globalUpgrades"`
+ FlashSales []flashSales `json:"flashSales"`
+ Invasions []invasions `json:"invasions"`
+ DarkSectors []interface{} `json:"darkSectors"`
+ VoidTrader voidTrader `json:"voidTrader"`
+ DailyDeals []dailyDeals `json:"dailyDeals"`
+ Simaris simaris `json:"simaris"`
+ ConclaveChallenges []conclaveChallenges `json:"conclaveChallenges"`
+ PersistentEnemies []interface{} `json:"persistentEnemies"`
+ EarthCycle earthCycle `json:"earthCycle"`
+ CetusCycle cetusCycle `json:"cetusCycle"`
+ CambionCycle cambionCycle `json:"cambionCycle"`
+ ZarimanCycle zarimanCycle `json:"zarimanCycle"`
+ WeeklyChallenges []interface{} `json:"weeklyChallenges"`
+ ConstructionProgress constructionProgress `json:"constructionProgress"`
+ VallisCycle vallisCycle `json:"vallisCycle"`
+ Nightwave nightwave `json:"nightwave"`
+ Kuva []interface{} `json:"kuva"`
+ Arbitration arbitration `json:"arbitration"`
+ SentientOutposts sentientOutposts `json:"sentientOutposts"`
+ SteelPath steelPath `json:"steelPath"`
+ VaultTrader vaultTrader `json:"vaultTrader"`
+}
+type translations struct {
+ En string `json:"en"`
+ Fr string `json:"fr"`
+ It string `json:"it"`
+ De string `json:"de"`
+ Es string `json:"es"`
+ Pt string `json:"pt"`
+ Ru string `json:"ru"`
+ Pl string `json:"pl"`
+ Uk string `json:"uk"`
+ Tr string `json:"tr"`
+ Ja string `json:"ja"`
+ Zh string `json:"zh"`
+ Ko string `json:"ko"`
+ Tc string `json:"tc"`
+}
+type news struct {
+ ID string `json:"id"`
+ Message string `json:"message"`
+ Link string `json:"link"`
+ ImageLink string `json:"imageLink"`
+ Priority bool `json:"priority"`
+ Date time.Time `json:"date"`
+ Eta string `json:"eta"`
+ Update bool `json:"update"`
+ PrimeAccess bool `json:"primeAccess"`
+ Stream bool `json:"stream"`
+ Translations translations `json:"translations"`
+ AsString string `json:"asString"`
+}
+type metadata struct {
+}
+type nextAlt struct {
+ Expiry time.Time `json:"expiry"`
+ Activation time.Time `json:"activation"`
+}
+type events struct {
+ ID string `json:"id"`
+ Activation time.Time `json:"activation"`
+ StartString string `json:"startString"`
+ Expiry time.Time `json:"expiry"`
+ Active bool `json:"active"`
+ MaximumScore int `json:"maximumScore"`
+ CurrentScore int `json:"currentScore"`
+ SmallInterval interface{} `json:"smallInterval"`
+ LargeInterval interface{} `json:"largeInterval"`
+ Faction string `json:"faction"`
+ Description string `json:"description"`
+ Tooltip string `json:"tooltip"`
+ Node string `json:"node"`
+ ConcurrentNodes []interface{} `json:"concurrentNodes"`
+ Rewards []interface{} `json:"rewards"`
+ Expired bool `json:"expired"`
+ InterimSteps []interface{} `json:"interimSteps"`
+ ProgressSteps []interface{} `json:"progressSteps"`
+ IsPersonal bool `json:"isPersonal"`
+ RegionDrops []interface{} `json:"regionDrops"`
+ ArchwingDrops []interface{} `json:"archwingDrops"`
+ AsString string `json:"asString"`
+ Metadata metadata `json:"metadata"`
+ CompletionBonuses []interface{} `json:"completionBonuses"`
+ AltExpiry time.Time `json:"altExpiry"`
+ AltActivation time.Time `json:"altActivation"`
+ NextAlt nextAlt `json:"nextAlt"`
+}
+type variants struct {
+ MissionType string `json:"missionType"`
+ Modifier string `json:"modifier"`
+ ModifierDescription string `json:"modifierDescription"`
+ Node string `json:"node"`
+}
+type sortie struct {
+ ID string `json:"id"`
+ Activation time.Time `json:"activation"`
+ StartString string `json:"startString"`
+ Expiry time.Time `json:"expiry"`
+ Active bool `json:"active"`
+ RewardPool string `json:"rewardPool"`
+ Variants []variants `json:"variants"`
+ Boss string `json:"boss"`
+ Faction string `json:"faction"`
+ Expired bool `json:"expired"`
+ Eta string `json:"eta"`
+}
+type jobs struct {
+ ID string `json:"id"`
+ RewardPool []string `json:"rewardPool"`
+ Type string `json:"type"`
+ EnemyLevels []int `json:"enemyLevels"`
+ StandingStages []int `json:"standingStages"`
+ MinMR int `json:"minMR"`
+ Expiry time.Time `json:"expiry"`
+ TimeBound string `json:"timeBound,omitempty"`
+}
+type syndicateMissions struct {
+ ID string `json:"id"`
+ Activation time.Time `json:"activation"`
+ StartString string `json:"startString"`
+ Expiry time.Time `json:"expiry"`
+ Active bool `json:"active"`
+ Syndicate string `json:"syndicate"`
+ SyndicateKey string `json:"syndicateKey"`
+ Nodes []interface{} `json:"nodes"`
+ Jobs []jobs `json:"jobs"`
+ Eta string `json:"eta"`
+}
+type fissures struct {
+ ID string `json:"id"`
+ Activation time.Time `json:"activation"`
+ StartString string `json:"startString"`
+ Expiry time.Time `json:"expiry"`
+ Active bool `json:"active"`
+ Node string `json:"node"`
+ MissionType string `json:"missionType"`
+ MissionKey string `json:"missionKey"`
+ Enemy string `json:"enemy"`
+ EnemyKey string `json:"enemyKey"`
+ NodeKey string `json:"nodeKey"`
+ Tier string `json:"tier"`
+ TierNum int `json:"tierNum"`
+ Expired bool `json:"expired"`
+ Eta string `json:"eta"`
+ IsStorm bool `json:"isStorm"`
+}
+type flashSales struct {
+ Item string `json:"item"`
+ Expiry time.Time `json:"expiry"`
+ Activation time.Time `json:"activation"`
+ Discount int `json:"discount"`
+ RegularOverride int `json:"regularOverride"`
+ PremiumOverride int `json:"premiumOverride"`
+ IsShownInMarket bool `json:"isShownInMarket"`
+ IsFeatured bool `json:"isFeatured"`
+ IsPopular bool `json:"isPopular"`
+ ID string `json:"id"`
+ Expired bool `json:"expired"`
+ Eta string `json:"eta"`
+}
+type countedItems struct {
+ Count int `json:"count"`
+ Type string `json:"type"`
+ Key string `json:"key"`
+}
+type attackerReward struct {
+ Items []interface{} `json:"items"`
+ CountedItems []countedItems `json:"countedItems"`
+ Credits int `json:"credits"`
+ AsString string `json:"asString"`
+ ItemString string `json:"itemString"`
+ Thumbnail string `json:"thumbnail"`
+ Color int `json:"color"`
+}
+type reward struct {
+ Items []interface{} `json:"items"`
+ CountedItems []countedItems `json:"countedItems"`
+ Credits int `json:"credits"`
+ AsString string `json:"asString"`
+ ItemString string `json:"itemString"`
+ Thumbnail string `json:"thumbnail"`
+ Color int `json:"color"`
+}
+type attacker struct {
+ Reward reward `json:"reward"`
+ Faction string `json:"faction"`
+ FactionKey string `json:"factionKey"`
+}
+type defenderReward struct {
+ Items []interface{} `json:"items"`
+ CountedItems []countedItems `json:"countedItems"`
+ Credits int `json:"credits"`
+ AsString string `json:"asString"`
+ ItemString string `json:"itemString"`
+ Thumbnail string `json:"thumbnail"`
+ Color int `json:"color"`
+}
+type defender struct {
+ Reward reward `json:"reward"`
+ Faction string `json:"faction"`
+ FactionKey string `json:"factionKey"`
+}
+type invasions struct {
+ ID string `json:"id"`
+ Activation time.Time `json:"activation"`
+ StartString string `json:"startString"`
+ Node string `json:"node"`
+ NodeKey string `json:"nodeKey"`
+ Desc string `json:"desc"`
+ AttackerReward attackerReward `json:"attackerReward"`
+ AttackingFaction string `json:"attackingFaction"`
+ Attacker attacker `json:"attacker"`
+ DefenderReward defenderReward `json:"defenderReward"`
+ DefendingFaction string `json:"defendingFaction"`
+ Defender defender `json:"defender"`
+ VsInfestation bool `json:"vsInfestation"`
+ Count int `json:"count"`
+ RequiredRuns int `json:"requiredRuns"`
+ Completion float64 `json:"completion"`
+ Completed bool `json:"completed"`
+ Eta string `json:"eta"`
+ RewardTypes []string `json:"rewardTypes"`
+}
+type voidTrader struct {
+ ID string `json:"id"`
+ Activation time.Time `json:"activation"`
+ StartString string `json:"startString"`
+ Expiry time.Time `json:"expiry"`
+ Active bool `json:"active"`
+ Character string `json:"character"`
+ Location string `json:"location"`
+ Inventory []interface{} `json:"inventory"`
+ PsID string `json:"psId"`
+ EndString string `json:"endString"`
+ InitialStart time.Time `json:"initialStart"`
+ Schedule []interface{} `json:"schedule"`
+}
+type dailyDeals struct {
+ Item string `json:"item"`
+ Expiry time.Time `json:"expiry"`
+ Activation time.Time `json:"activation"`
+ OriginalPrice int `json:"originalPrice"`
+ SalePrice int `json:"salePrice"`
+ Total int `json:"total"`
+ Sold int `json:"sold"`
+ ID string `json:"id"`
+ Eta string `json:"eta"`
+ Discount int `json:"discount"`
+}
+type simaris struct {
+ Target string `json:"target"`
+ IsTargetActive bool `json:"isTargetActive"`
+ AsString string `json:"asString"`
+}
+type conclaveChallenges struct {
+ ID string `json:"id"`
+ Expiry time.Time `json:"expiry"`
+ Activation time.Time `json:"activation"`
+ Amount int `json:"amount"`
+ Mode string `json:"mode"`
+ Category string `json:"category"`
+ Eta string `json:"eta"`
+ Expired bool `json:"expired"`
+ Daily bool `json:"daily"`
+ RootChallenge bool `json:"rootChallenge"`
+ EndString string `json:"endString"`
+ Description string `json:"description"`
+ Title string `json:"title"`
+ Standing int `json:"standing"`
+ AsString string `json:"asString"`
+}
+type earthCycle struct {
+ ID string `json:"id"`
+ Expiry time.Time `json:"expiry"`
+ Activation time.Time `json:"activation"`
+ IsDay bool `json:"isDay"`
+ State string `json:"state"`
+ TimeLeft string `json:"timeLeft"`
+}
+type cetusCycle struct {
+ ID string `json:"id"`
+ Expiry time.Time `json:"expiry"`
+ Activation time.Time `json:"activation"`
+ IsDay bool `json:"isDay"`
+ State string `json:"state"`
+ TimeLeft string `json:"timeLeft"`
+ IsCetus bool `json:"isCetus"`
+ ShortString string `json:"shortString"`
+}
+type cambionCycle struct {
+ ID string `json:"id"`
+ Activation time.Time `json:"activation"`
+ Expiry time.Time `json:"expiry"`
+ TimeLeft string `json:"timeLeft"`
+ Active string `json:"active"`
+}
+type zarimanCycle struct {
+ ID string `json:"id"`
+ BountiesEndDate time.Time `json:"bountiesEndDate"`
+ Expiry time.Time `json:"expiry"`
+ Activation time.Time `json:"activation"`
+ IsCorpus bool `json:"isCorpus"`
+ State string `json:"state"`
+ TimeLeft string `json:"timeLeft"`
+ ShortString string `json:"shortString"`
+}
+type constructionProgress struct {
+ ID string `json:"id"`
+ FomorianProgress string `json:"fomorianProgress"`
+ RazorbackProgress string `json:"razorbackProgress"`
+ UnknownProgress string `json:"unknownProgress"`
+}
+type vallisCycle struct {
+ ID string `json:"id"`
+ Expiry time.Time `json:"expiry"`
+ IsWarm bool `json:"isWarm"`
+ State string `json:"state"`
+ Activation time.Time `json:"activation"`
+ TimeLeft string `json:"timeLeft"`
+ ShortString string `json:"shortString"`
+}
+type params struct {
+}
+type activeChallenges struct {
+ ID string `json:"id"`
+ Activation time.Time `json:"activation"`
+ StartString string `json:"startString"`
+ Expiry time.Time `json:"expiry"`
+ Active bool `json:"active"`
+ IsDaily bool `json:"isDaily,omitempty"`
+ IsElite bool `json:"isElite"`
+ Desc string `json:"desc"`
+ Title string `json:"title"`
+ Reputation int `json:"reputation"`
+}
+type nightwave struct {
+ ID string `json:"id"`
+ Activation time.Time `json:"activation"`
+ StartString string `json:"startString"`
+ Expiry time.Time `json:"expiry"`
+ Active bool `json:"active"`
+ Season int `json:"season"`
+ Tag string `json:"tag"`
+ Phase int `json:"phase"`
+ Params params `json:"params"`
+ PossibleChallenges []interface{} `json:"possibleChallenges"`
+ ActiveChallenges []activeChallenges `json:"activeChallenges"`
+ RewardTypes []string `json:"rewardTypes"`
+}
+type arbitration struct {
+ Activation time.Time `json:"activation"`
+ Expiry time.Time `json:"expiry"`
+ Enemy string `json:"enemy"`
+ Type string `json:"type"`
+ Archwing bool `json:"archwing"`
+ Sharkwing bool `json:"sharkwing"`
+ Node string `json:"node"`
+ NodeKey string `json:"nodeKey"`
+ TypeKey string `json:"typeKey"`
+ ID string `json:"id"`
+ Expired bool `json:"expired"`
+}
+type mission struct {
+ Node string `json:"node"`
+ Faction string `json:"faction"`
+ Type string `json:"type"`
+}
+type sentientOutposts struct {
+ Mission mission `json:"mission"`
+ Activation time.Time `json:"activation"`
+ Expiry time.Time `json:"expiry"`
+ Active bool `json:"active"`
+ ID string `json:"id"`
+}
+type currentReward struct {
+ Name string `json:"name"`
+ Cost int `json:"cost"`
+}
+type rotation struct {
+ Name string `json:"name"`
+ Cost int `json:"cost"`
+}
+type evergreens struct {
+ Name string `json:"name"`
+ Cost int `json:"cost"`
+}
+type incursions struct {
+ ID string `json:"id"`
+ Activation time.Time `json:"activation"`
+ Expiry time.Time `json:"expiry"`
+}
+type steelPath struct {
+ CurrentReward currentReward `json:"currentReward"`
+ Activation time.Time `json:"activation"`
+ Expiry time.Time `json:"expiry"`
+ Remaining string `json:"remaining"`
+ Rotation []rotation `json:"rotation"`
+ Evergreens []evergreens `json:"evergreens"`
+ Incursions incursions `json:"incursions"`
+}
+type inventory struct {
+ Item string `json:"item"`
+ Ducats int `json:"ducats"`
+ Credits interface{} `json:"credits"`
+}
+type schedule struct {
+ Expiry time.Time `json:"expiry"`
+ Item string `json:"item"`
+}
+type vaultTrader struct {
+ ID string `json:"id"`
+ Activation time.Time `json:"activation"`
+ StartString string `json:"startString"`
+ Expiry time.Time `json:"expiry"`
+ Active bool `json:"active"`
+ Character string `json:"character"`
+ Location string `json:"location"`
+ Inventory []inventory `json:"inventory"`
+ PsID string `json:"psId"`
+ EndString string `json:"endString"`
+ InitialStart time.Time `json:"initialStart"`
+ Completed bool `json:"completed"`
+ Schedule []schedule `json:"schedule"`
+}
+type alerts struct {
+ ID string `json:"id"`
+ Activation time.Time `json:"activation"`
+ StartString string `json:"startString"`
+ Expiry time.Time `json:"expiry"`
+ Active bool `json:"active"`
+ Mission struct {
+ Description string `json:"description"`
+ Node string `json:"node"`
+ NodeKey string `json:"nodeKey"`
+ Type string `json:"type"`
+ TypeKey string `json:"typeKey"`
+ Faction string `json:"faction"`
+ Reward struct {
+ Items []interface{} `json:"items"`
+ CountedItems []struct {
+ Count int `json:"count"`
+ Type string `json:"type"`
+ Key string `json:"key"`
+ } `json:"countedItems"`
+ Credits int `json:"credits"`
+ AsString string `json:"asString"`
+ ItemString string `json:"itemString"`
+ Thumbnail string `json:"thumbnail"`
+ Color int `json:"color"`
+ } `json:"reward"`
+ MinEnemyLevel int `json:"minEnemyLevel"`
+ MaxEnemyLevel int `json:"maxEnemyLevel"`
+ MaxWaveNum int `json:"maxWaveNum"`
+ Nightmare bool `json:"nightmare"`
+ ArchwingRequired bool `json:"archwingRequired"`
+ IsSharkwing bool `json:"isSharkwing"`
+ LevelOverride string `json:"levelOverride"`
+ EnemySpec string `json:"enemySpec"`
+ AdvancedSpawners []interface{} `json:"advancedSpawners"`
+ RequiredItems []interface{} `json:"requiredItems"`
+ LevelAuras []interface{} `json:"levelAuras"`
+ } `json:"mission"`
+ Eta string `json:"eta"`
+ RewardTypes []string `json:"rewardTypes"`
+ Tag string `json:"tag"`
+}
+type wfAPIItem struct {
+ Payload payload `json:"payload"`
+}
+type items struct {
+ URLName string `json:"url_name"`
+ Thumb string `json:"thumb"`
+ ItemName string `json:"item_name"`
+ ID string `json:"id"`
+ Vaulted bool `json:"vaulted,omitempty"`
+}
+type payload struct {
+ Items []items `json:"items"`
+ Orders orders `json:"orders"`
+}
+
+type wfAPIItemsOrders struct {
+ Payload payload `json:"payload"`
+ Include include `json:"include"`
+}
+type user struct {
+ IngameName string `json:"ingame_name"`
+ LastSeen time.Time `json:"last_seen"`
+ Reputation int `json:"reputation"`
+ Region string `json:"region"`
+ ID string `json:"id"`
+ Avatar interface{} `json:"avatar"`
+ Status string `json:"status"`
+}
+type orders []struct {
+ OrderType string `json:"order_type"`
+ LastUpdate time.Time `json:"last_update"`
+ Region string `json:"region"`
+ Quantity int `json:"quantity"`
+ Visible bool `json:"visible"`
+ CreationDate time.Time `json:"creation_date"`
+ Platinum int `json:"platinum"`
+ Platform string `json:"platform"`
+ User user `json:"user"`
+ ID string `json:"id"`
+ ModRank int `json:"mod_rank"`
+}
+
+func (a orders) Len() int { // 重写 Len() 方法
+ return len(a)
+}
+func (a orders) Swap(i, j int) { // 重写 Swap() 方法
+ a[i], a[j] = a[j], a[i]
+}
+func (a orders) Less(i, j int) bool { // 重写 Less() 方法, 从大到小排序
+ return a[i].Platinum < a[j].Platinum
+}
+
+type en struct {
+ ItemName string `json:"item_name"`
+ Description string `json:"description"`
+ WikiLink string `json:"wiki_link"`
+ Drop []interface{} `json:"drop"`
+}
+type ru struct {
+ ItemName string `json:"item_name"`
+ Description string `json:"description"`
+ WikiLink string `json:"wiki_link"`
+ Drop []interface{} `json:"drop"`
+}
+type ko struct {
+ ItemName string `json:"item_name"`
+ Description string `json:"description"`
+ WikiLink string `json:"wiki_link"`
+ Drop []interface{} `json:"drop"`
+}
+type fr struct {
+ ItemName string `json:"item_name"`
+ Description string `json:"description"`
+ WikiLink string `json:"wiki_link"`
+ Drop []interface{} `json:"drop"`
+}
+type sv struct {
+ ItemName string `json:"item_name"`
+ Description string `json:"description"`
+ WikiLink string `json:"wiki_link"`
+ Drop []interface{} `json:"drop"`
+}
+type de struct {
+ ItemName string `json:"item_name"`
+ Description string `json:"description"`
+ WikiLink string `json:"wiki_link"`
+ Drop []interface{} `json:"drop"`
+}
+type zhHant struct {
+ ItemName string `json:"item_name"`
+ Description string `json:"description"`
+ WikiLink string `json:"wiki_link"`
+ Drop []interface{} `json:"drop"`
+}
+type zhHans struct {
+ ItemName string `json:"item_name"`
+ Description string `json:"description"`
+ WikiLink string `json:"wiki_link"`
+ Drop []interface{} `json:"drop"`
+}
+type pt struct {
+ ItemName string `json:"item_name"`
+ Description string `json:"description"`
+ WikiLink string `json:"wiki_link"`
+ Drop []interface{} `json:"drop"`
+}
+type es struct {
+ ItemName string `json:"item_name"`
+ Description string `json:"description"`
+ WikiLink string `json:"wiki_link"`
+ Drop []interface{} `json:"drop"`
+}
+type pl struct {
+ ItemName string `json:"item_name"`
+ Description string `json:"description"`
+ WikiLink string `json:"wiki_link"`
+ Drop []interface{} `json:"drop"`
+}
+type itemsInSet struct {
+ Icon string `json:"icon"`
+ URLName string `json:"url_name"`
+ SubIcon string `json:"sub_icon"`
+ ModMaxRank int `json:"mod_max_rank"`
+ Thumb string `json:"thumb"`
+ SetRoot bool `json:"set_root"`
+ QuantityForSet int `json:"quantity_for_set,omitempty"`
+ ID string `json:"id"`
+ TradingTax int `json:"trading_tax"`
+ Tags []string `json:"tags"`
+ MasteryLevel int `json:"mastery_level"`
+ Ducats int `json:"ducats"`
+ IconFormat string `json:"icon_format"`
+ En en `json:"en"`
+ Ru ru `json:"ru"`
+ Ko ko `json:"ko"`
+ Fr fr `json:"fr"`
+ Sv sv `json:"sv"`
+ De de `json:"de"`
+ ZhHant zhHant `json:"zh-hant"`
+ ZhHans zhHans `json:"zh-hans"`
+ Pt pt `json:"pt"`
+ Es es `json:"es"`
+ Pl pl `json:"pl"`
+}
+type item struct {
+ ID string `json:"id"`
+ ItemsInSet []itemsInSet `json:"items_in_set"`
+}
+type include struct {
+ Item item `json:"item"`
+}