mirror of
https://github.com/FloatTech/ZeroBot-Plugin.git
synced 2025-12-19 22:00:11 +08:00
508 lines
15 KiB
Go
508 lines
15 KiB
Go
// 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)
|
||
}
|
||
}
|