commit fa06e419dad551ba4a5402741b963584cdcaf78d Author: dustoair <107600816+dustoair@users.noreply.github.com> Date: Mon Jul 25 21:24:27 2022 +0800 open src diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..c2fa602 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,21 @@ +kind: pipeline +type: ssh # 使用SSH +name: baidu + + +server: + host: 172.16.0.10 + user: root + password: 123456 + +clone: + disable: true + +steps: + - name: docker + commands: + - echo Drone start + - date + - /root/sre/build_genshin.sh + - date + - echo Drone end \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5a78226 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.idea +go.sum +main.exe +eth.exe +log.txt +logs +*.log +uploads +gin.log \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bf0d491 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +################################################################################ +## MAIN STAGE ## +################################################################################ +# Copy the manager into the distroless image. +#FROM scratch +#FROM hub.sre.ink/base/distroless-static:nonroot-20210710 +#FROM mirror.ccs.tencentyun.com/library/alpine:3.13 +FROM alpine:3.15.0 +#FROM hub.sre.ink/base/alpine:3.15.0 +LABEL Description="原神工具箱" +MAINTAINER sre +RUN echo 'https://mirrors.cloud.tencent.com/alpine/v3.13/main' > /etc/apk/repositories \ + && echo 'https://mirrors.cloud.tencent.com/alpine/v3.13/community' >>/etc/apk/repositories \ + && apk update && apk add tzdata && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ + && echo "Asia/Shanghai" > /etc/timezone +WORKDIR /app +COPY genshin.bin /app/genshin.bin +RUN chmod 755 /app/genshin.bin +#USER nonroot:nonroot +ENTRYPOINT ["/app/genshin.bin"] \ No newline at end of file diff --git a/global/char.go b/global/char.go new file mode 100644 index 0000000..fb9f6eb --- /dev/null +++ b/global/char.go @@ -0,0 +1,22 @@ +package global + +var ( + + //标识 char + LastRare = 0 + LastEpic = 0 + //大保底标识 char + LastEpicCommon = false + + //当期up角色 + UpEpicChar = "夜兰" + + //当前UP的4星角色 + UpRareChar = []string{"闪耀偶像·芭芭拉(水)", "智明无邪·烟绯(火)", "未授勋之花·诺艾尔(岩)"} + + //常驻5星角色 + EpicCommonCharactorPool = []string{"迪卢克", "七七", "刻晴", "琴", "莫娜"} + + //常驻4星角色 + RareChar = []string{"班尼特", "行秋", "迪奥娜", "北斗", "芭芭拉", "烟绯", "诺艾尔"} +) diff --git a/global/var.go b/global/var.go new file mode 100644 index 0000000..ad48ba1 --- /dev/null +++ b/global/var.go @@ -0,0 +1,11 @@ +package global + +const ( + UseOldApi = "no" + MiCookie = "UM_distincb5801; _MHYUUID=502d6e78-e5ec-480c-98d6-0cfdd5cf60da; mi18nLang=zh-cn; account_id=285923115; ltuid=285923115; _ga_R8CG4VZ69C=GS1.1.1650932768.1.1.1650933672.0; _ga_KPXDK6J368=GS1.1.1653121921.1.1.1653122857.0; _ga_BL22GL4FMD=GS1.1.1655136657.3.1.1655136670.0; _ga_9TTX3TE5YL=GS1.1.1657123701.4.0.1657123701.0; _ga_5DECE6NN1T=GS1.1.1657422258.3.1.1657422260.0; _ga=GA1.2.822707536.1650932589; _ga_N90YRBX6FW=GS1.1.1657422283.15.1.1657423868.0; CNZZDATA1275023096=1119969765-1650922518-%7C1657883174; _gid=GA1.2.1840959498.1657885859; ltoken=31BhDecafgXeaoakn2R9uwwwXckMEkplK7dlbBTq; cookie_token=vugqhRqh5vTcTxIc15gPMH9NYeOsy8Lnwba0mUrJ; _gat=1" + MiUid = "10000" + MiServer = "cn_gf01" + NoticeResinLimit = 120 + NoticeHomeCoinLimit = 2000 + RequestRate = 8 * 60 //8分钟恢复1点树脂 +) diff --git a/global/weapon.go b/global/weapon.go new file mode 100644 index 0000000..5b3e560 --- /dev/null +++ b/global/weapon.go @@ -0,0 +1,28 @@ +package global + +var ( + //标识 weapon + LastRareWeapon = 0 + LastEpicWeapon = 0 + //大保底标识 weapon + LastEpicCommonWeapon = false + + //武器定轨开关 + WeaponTargetSwitch = true + //武器定轨武器 + WeaponTarget = "弓·若水" + //武器定轨数量 + WeaponTargetCount = 0 + + //当期up武器 + UpEpicWeapon = []string{"弓·若水", "长柄武器·和璞鸢"} + //当前UP的4星武器 + UpRareWeapon = []string{"长柄武器·千岩长枪", "单手剑·祭礼剑", "双手剑·西风大剑", "法器·昭心", "弓·祭礼弓"} + + //常驻5星武器 + EpicCommonWeaponPool = []string{"狼的末路", "风鹰剑", "天空之卷", "阿莫斯之弓", "天空之翼"} + //常驻4星武器 + RareWeapon = []string{"单手剑·祭礼剑", "弓·祭礼弓", "长柄武器·西风长枪", "双手剑·西风大剑", "双手剑·雨裁", "双手剑·钟剑", "法器·昭心"} + //常驻3星武器 + CommonItem = []string{"双手剑·以理服人", "双手剑·浴血龙剑", "法器·讨龙", "法器·翡翠法环", "长柄武器·白樱枪", "长柄武器·黑樱枪", "旅行剑"} +) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..74d7e88 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module GenshinImpact + +go 1.18 + +require github.com/robfig/cron v1.2.0 // indirect diff --git a/lottery/charPool.go b/lottery/charPool.go new file mode 100644 index 0000000..51e6457 --- /dev/null +++ b/lottery/charPool.go @@ -0,0 +1,69 @@ +package lottery + +import ( + "GenshinImpact/global" + "math/rand" +) + +// 角色up池 +func PickCharUp() string { + if global.LastRare >= 10 { + global.LastRare -= 10 + } + if global.LastEpic >= 90 { + global.LastEpic -= 90 + } + if global.LastEpic == 89 { + //必出橙色 + //fmt.Println("必出橙色-----------------------------------------------------------------------------------------------------------------------") + return RandomEpic() + } + if global.LastRare == 9 { + //必出紫色 + //fmt.Println("必出紫色") + return RandomRare() + } + //单抽 + //5星概率为0.6% 最多90次必出5星 0-59 + //4星概率为5.1% 其中角色2.55% 武器2.55% 最多10次必出4星 255 255 + //https://www.bilibili.com/read/cv12616453 + //原神抽卡概率工具表 超过73抽后概率增加 + //模型五星综合概率为1.6052%,期望62.297抽,概率在第74抽开始上升。 + //作者:一棵平衡树 https://www.bilibili.com/read/cv12616453 出处:bilibili + dice := rand.Intn(10000) + adjustRate := 59 + if global.LastEpic > 72 { + adjustRate = 660 + 600*(global.LastEpic-72) + } + adjustRateRare := 255 + 255 + if global.LastRare == 8 { + //模型四星综合概率为13.057%,期望7.6589抽,概率在第9抽开始上升。 + //作者:一棵平衡树 https://www.bilibili.com/read/cv12616453 出处:bilibili + adjustRateRare += 5000 + } + switch { + case dice <= adjustRate: + //epic + return RandomEpic() + case dice <= adjustRate+adjustRateRare: + //rare + return RandomRare() + default: + //common item + return RandomCommonItem() + } + +} + +// 随机取字符串 +func RandomStr(sls []string) string { + return sls[rand.Intn(len(sls))] +} + +// 常驻3星武器 角色池 +func RandomCommonItem() string { + global.LastEpic += 1 + global.LastRare += 1 + commonItem := global.CommonItem + return commonItem[rand.Intn(len(commonItem))] +} diff --git a/lottery/epic.go b/lottery/epic.go new file mode 100644 index 0000000..f4ee563 --- /dev/null +++ b/lottery/epic.go @@ -0,0 +1,33 @@ +package lottery + +import ( + "GenshinImpact/global" + "math/rand" +) + +// 5星 +func RandomEpic() string { + global.LastEpic = 0 + global.LastRare = 0 + if global.LastEpicCommon == true { + global.LastEpicCommon = false + return RandomEpicUPCharator() + } + diceUp := rand.Intn(10) + if diceUp <= 4 { + return RandomEpicUPCharator() + } + return RandomEpicCommonCharactor() +} + +// 常驻5星角色 +func RandomEpicCommonCharactor() string { + global.LastEpicCommon = true + epicCommonCharactorPool := global.EpicCommonCharactorPool + return epicCommonCharactorPool[rand.Intn(len(epicCommonCharactorPool))] +} + +// 当期up角色 +func RandomEpicUPCharator() string { + return global.UpEpicChar +} diff --git a/lottery/epicWeapon.go b/lottery/epicWeapon.go new file mode 100644 index 0000000..c1815e6 --- /dev/null +++ b/lottery/epicWeapon.go @@ -0,0 +1,46 @@ +package lottery + +import ( + "GenshinImpact/global" + "math/rand" +) + +// 5星 +func RandomEpicWeapon() string { + global.LastEpicWeapon = 0 + global.LastRareWeapon = 0 + if global.LastEpicCommonWeapon == true { + //当期upWeapon + return RandomEpicUpWeapon() + } + diceUp := rand.Intn(100) + //up5占5星出货75% + if diceUp <= 74 { + //当期upWeapon + return RandomEpicUpWeapon() + } + return RandomEpicCommonWeapon() +} + +// UP5星Weapon +func RandomEpicUpWeapon() string { + global.LastEpicCommonWeapon = false + if global.WeaponTargetSwitch { + if global.WeaponTargetCount == 2 { + global.WeaponTargetCount = 0 + return global.WeaponTarget + } + global.WeaponTargetCount += 1 + } + + return RandomStr(global.UpEpicWeapon) +} + +// 常驻5星Weapon +func RandomEpicCommonWeapon() string { + global.LastEpicCommonWeapon = true + if global.WeaponTargetSwitch && global.WeaponTargetCount < 2 { + global.WeaponTargetCount += 1 + } + return RandomStr(global.EpicCommonWeaponPool) +} diff --git a/lottery/init.go b/lottery/init.go new file mode 100644 index 0000000..8ed37af --- /dev/null +++ b/lottery/init.go @@ -0,0 +1,10 @@ +package lottery + +import ( + "math/rand" + "time" +) + +func init() { + rand.Seed(time.Now().UnixNano()) // 纳秒时间戳 +} diff --git a/lottery/lottery_test.go b/lottery/lottery_test.go new file mode 100644 index 0000000..abd9dcb --- /dev/null +++ b/lottery/lottery_test.go @@ -0,0 +1,69 @@ +package lottery + +import ( + "GenshinImpact/global" + "fmt" + "math/rand" + "strconv" + "testing" +) + +func TestPickWeapon(t *testing.T) { + totalRare := 0 + totalEpic := 0 + epicList := []string{} + preEpicNo := 0 //保存上一个epic号 + for i := 1; i <= 240; i++ { + preEpicNo = global.LastEpicWeapon + res := PickWeaponUp() + if global.LastRareWeapon == 0 || global.LastEpicWeapon == 0 { + fmt.Println(i, global.LastRareWeapon, global.LastEpicWeapon, res) + if global.LastRareWeapon == 0 { + totalRare += 1 + } + if global.LastEpicWeapon == 0 { + epicList = append(epicList, strconv.Itoa(preEpicNo+1)+res) + totalEpic += 1 + } + } + + } + fmt.Println("totalRare", totalRare) + fmt.Println("totalEpic", totalEpic) + fmt.Println("epicList", epicList) + +} + +func TestPickChar(t *testing.T) { + totalRare := 0 + totalEpic := 0 + epicList := []string{} + preEpicNo := 0 //保存上一个epic号 + for i := 1; i <= 10000000; i++ { + preEpicNo = global.LastEpic + res := PickCharUp() + if global.LastRare == 0 || global.LastEpic == 0 { + //fmt.Println(i, global.LastRare, global.LastEpic, res) + if global.LastRare == 0 { + totalRare += 1 + } + if global.LastEpic == 0 { + epicList = append(epicList, strconv.Itoa(preEpicNo+1)+res) + totalEpic += 1 + } + } + + } + fmt.Println("totalRare", totalRare) + fmt.Println("totalEpic", totalEpic) + fmt.Println("totalEpic/total", 10000000.00/totalEpic) + fmt.Println("epicList", epicList) + +} + +func TestRandom(t *testing.T) { + for { + fmt.Println(rand.Intn(10)) + } + +} diff --git a/lottery/rare.go b/lottery/rare.go new file mode 100644 index 0000000..93aad72 --- /dev/null +++ b/lottery/rare.go @@ -0,0 +1,24 @@ +package lottery + +import ( + "GenshinImpact/global" + "math/rand" +) + +// 4星物品 +func RandomRare() string { + global.LastRare = 0 + global.LastEpic += 1 + diceUp := rand.Intn(10) + if diceUp <= 4 { + //当前UP的4星角色 + return RandomStr(global.UpRareChar) + } + diceChar := rand.Intn(10) + if diceChar <= 4 { + //常驻4星角色 + return RandomStr(global.RareChar) + } + //常驻4星武器 + return RandomStr(global.RareWeapon) +} diff --git a/lottery/rareWeapon.go b/lottery/rareWeapon.go new file mode 100644 index 0000000..28ff98b --- /dev/null +++ b/lottery/rareWeapon.go @@ -0,0 +1,26 @@ +package lottery + +import ( + "GenshinImpact/global" + "math/rand" +) + +// 4星Weapon +func RandomRareWeapon() string { + global.LastRareWeapon = 0 + global.LastEpicWeapon += 1 + diceUp := rand.Intn(100) + //up4占4星出货75% + if diceUp <= 74 { + //当前UP的4星Weapon + return RandomStr(global.UpRareWeapon) + } + diceChar := rand.Intn(10) + //4星概率为6% 其中角色3% 武器3% + if diceChar <= 4 { + //常驻4星角色 + return RandomStr(global.RareChar) + } + //常驻4星武器 + return RandomStr(global.RareWeapon) +} diff --git a/lottery/weaponPool.go b/lottery/weaponPool.go new file mode 100644 index 0000000..80ca66a --- /dev/null +++ b/lottery/weaponPool.go @@ -0,0 +1,68 @@ +package lottery + +import ( + "GenshinImpact/global" + "math/rand" +) + +// 武器up池 +func PickWeaponUp() string { + if global.LastRareWeapon >= 9 { + global.LastRareWeapon -= 9 + } + if global.LastEpicWeapon >= 80 { + global.LastEpicWeapon -= 80 + } + //保底 + if global.LastEpicWeapon == 79 { + //必出橙色 + //fmt.Println("必出橙色-----------------------------------------------------------------------------------------------------------------------") + return RandomEpicWeapon() + } + if global.LastRareWeapon == 9 { + //必出紫色 + //fmt.Println("必出紫色") + return RandomRareWeapon() + } + //单抽 + //up5占5星出货75% + //up4占4星出货75% + //5星概率为0.7% 最多80次必出5星 + //4星概率为6% 其中角色3% 武器3% 最多10次必出4星 + //当起源到4星时 75%为up武器 + //如果本次不是当期up 下一个一定是up + //https://www.bilibili.com/read/cv12616453 + //模型五星综合概率为1.8779%,期望53.250抽,概率在第63抽开始上升,保底位置实为第77抽而非公示的第80抽。 + //作者:一棵平衡树 https://www.bilibili.com/read/cv12616453 出处:bilibili + //模型四星综合概率为14.857%,期望6.7309抽,概率在第8抽开始上升,保底位置实为第9抽而非公示的第10抽。 + //作者:一棵平衡树 https://www.bilibili.com/read/cv12616453 出处:bilibili + dice := rand.Intn(10000) + adjustRateWeapon := 69 + if global.LastEpicWeapon > 61 { + adjustRateWeapon = 770 + 700*(global.LastEpicWeapon-61) + } + adjustRateRareWeapon := 300 + 300 + if global.LastRareWeapon == 7 { + adjustRateRareWeapon += 6000 + } + switch { + case dice <= adjustRateWeapon: + //epic + return RandomEpicWeapon() + case dice <= adjustRateWeapon+adjustRateRareWeapon: + //rare + return RandomRareWeapon() + default: + //常驻3星武器 + return RandomCommonItemWeapon() + } + +} + +// 常驻3星武器 武器池 +func RandomCommonItemWeapon() string { + global.LastEpicWeapon += 1 + global.LastRareWeapon += 1 + commonItem := global.CommonItem + return commonItem[rand.Intn(len(commonItem))] +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..ef03590 --- /dev/null +++ b/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "GenshinImpact/mihoyo" + "fmt" + "github.com/robfig/cron" +) + +func main() { + c := cron.New() + //树脂每8分钟恢复1点 + //晚七点到晚上24点前提醒 工作日 + c.AddFunc("0 */8 19-23 * * 1-5", func() { go mihoyo.BeginWorkDay() }) + //早七点到晚上24点前提醒 周末 + c.AddFunc("0 */8 7-23 * * 0,6", func() { go mihoyo.BeginWorkEnd() }) + fmt.Println("任务注册成功") + c.Start() + select {} +} diff --git a/mihoyo/core.go b/mihoyo/core.go new file mode 100644 index 0000000..bdfd144 --- /dev/null +++ b/mihoyo/core.go @@ -0,0 +1,58 @@ +package mihoyo + +import ( + "encoding/json" + "fmt" + "net/http" + "time" +) + +type MiyoRequest struct { + url string + headers map[string]string +} + +type MiyoResponse struct { + RetCode int `json:"retcode"` + Message string `json:"message"` + Data json.RawMessage `json:"data"` +} + +func NewMiyoRequest(url string) *MiyoRequest { + return &MiyoRequest{ + url, + map[string]string{ + "Accept": "application/json", + }, + } +} + +func (th *MiyoRequest) Execute() ([]byte, error) { + client := &http.Client{Timeout: 60 * time.Second} + req, err := http.NewRequest(http.MethodGet, th.url, nil) + if err != nil { + return []byte{}, err + } + for k, v := range th.headers { + req.Header.Set(k, v) + } + resp, err := client.Do(req) + if err != nil { + return []byte{}, err + } + defer resp.Body.Close() + dec := json.NewDecoder(resp.Body) + var miyoResp MiyoResponse + err = dec.Decode(&miyoResp) + if err != nil { + return []byte{}, err + } + if miyoResp.RetCode != 0 { + return []byte{}, fmt.Errorf("errcode: %d, errmsg: %s", miyoResp.RetCode, miyoResp.Message) + } + return miyoResp.Data, nil +} + +func (th *MiyoRequest) SetHeader(k, v string) { + th.headers[k] = v +} diff --git a/mihoyo/noteJSON.go b/mihoyo/noteJSON.go new file mode 100644 index 0000000..f866bf7 --- /dev/null +++ b/mihoyo/noteJSON.go @@ -0,0 +1,42 @@ +package mihoyo + +type GenshinDailyNote struct { + CurrentResin int `json:"current_resin"` + MaxResin int `json:"max_resin"` + ResinRecoveryTime string `json:"resin_recovery_time"` + FinishedTaskNum int `json:"finished_task_num"` + TotalTaskNum int `json:"total_task_num"` + IsExtraTaskRewardReceived bool `json:"is_extra_task_reward_received"` + RemainResinDiscountNum int `json:"remain_resin_discount_num"` + ResinDiscountNumLimit int `json:"resin_discount_num_limit"` + CurrentExpeditionNum int `json:"current_expedition_num"` + MaxExpeditionNum int `json:"max_expedition_num"` + Expeditions []GameRoleExpedition `json:"expeditions"` + CurrentHomeCoin int `json:"current_home_coin"` + MaxHomeCoin int `json:"max_home_coin"` + HomeCoinRecoveryTime string `json:"home_coin_recovery_time"` + CalendarUrl string `json:"calendar_url"` + Transformer TransformerInfo `json:"transformer"` +} + +type GameRoleExpedition struct { + AvatarSideIconLink string `json:"avatar_side_icon"` + Status string `json:"status"` + RemainedTime string `json:"remained_time"` +} + +type TransformerInfo struct { + Obtained bool `json:"obtained"` + RecTime TransformerRecTime `json:"recovery_time"` + Wiki string `json:"wiki"` + Noticed bool `json:"noticed"` + LatestJobId string `json:"latest_job_id"` +} + +type TransformerRecTime struct { + Day int `json:"Day"` + Hour int `json:"Hour"` + Minute int `json:"Minute"` + Second int `json:"Second"` + Reached bool `json:"reached"` +} diff --git a/mihoyo/notice.go b/mihoyo/notice.go new file mode 100644 index 0000000..fafc837 --- /dev/null +++ b/mihoyo/notice.go @@ -0,0 +1,77 @@ +package mihoyo + +import ( + "GenshinImpact/global" + "GenshinImpact/thirdpart" + "fmt" + "strconv" +) + +func BeginWorkDay() { + note, err := GetGenshinDailyNote(global.MiCookie, global.MiUid, global.MiServer) + if err != nil { + fmt.Println(err.Error()) + thirdpart.SendWechat("请求米哈游服务器失败:" + err.Error()) + } + if note.CurrentResin >= global.NoticeResinLimit { + thirdpart.SendWechat("树脂达到限制:" + strconv.Itoa(note.CurrentResin)) + NoticeTransformer(note) + NoticeHomeCoin(note) + } + + if !note.IsExtraTaskRewardReceived { + if note.FinishedTaskNum < 4 { + thirdpart.SendWechat("今日委托任务未完成") + } else { + thirdpart.SendWechat("今日委托奖励未领取") + } + NoticeTransformer(note) + NoticeHomeCoin(note) + + } + +} + +func BeginWorkEnd() { + note, err := GetGenshinDailyNote(global.MiCookie, global.MiUid, global.MiServer) + if err != nil { + fmt.Println(err.Error()) + thirdpart.SendWechat("请求米哈游服务器失败:" + err.Error()) + } + if note.CurrentResin >= global.NoticeResinLimit { + thirdpart.SendWechat("树脂达到限制:" + strconv.Itoa(note.CurrentResin)) + NoticeTransformer(note) + NoticeHomeCoin(note) + } + + if !note.IsExtraTaskRewardReceived { + if note.FinishedTaskNum < 4 { + thirdpart.SendWechat("今日委托任务未完成") + } else { + thirdpart.SendWechat("今日委托奖励未领取") + } + NoticeTransformer(note) + NoticeHomeCoin(note) + + } + //周末再统计boss周本信息 + NoticeWeeklyBoos(note) + +} + +func NoticeHomeCoin(note *GenshinDailyNote) { + if note.CurrentHomeCoin >= global.NoticeHomeCoinLimit { + thirdpart.SendWechat("洞天宝钱达到限制:" + strconv.Itoa(note.CurrentHomeCoin)) + } +} +func NoticeWeeklyBoos(note *GenshinDailyNote) { + if note.RemainResinDiscountNum > 0 { + thirdpart.SendWechat("本周BOOS未刷完,剩余" + strconv.Itoa(note.RemainResinDiscountNum) + "次") + } + +} +func NoticeTransformer(note *GenshinDailyNote) { + if note.Transformer.RecTime.Reached { + thirdpart.SendWechat("参量质变仪已就绪") + } +} diff --git a/mihoyo/service.go b/mihoyo/service.go new file mode 100644 index 0000000..d0bc858 --- /dev/null +++ b/mihoyo/service.go @@ -0,0 +1,78 @@ +package mihoyo + +import ( + "GenshinImpact/global" + "GenshinImpact/utils" + "encoding/json" + "errors" + "fmt" + "strings" +) + +type GameRoleList struct { + List []GameRole `json:"list"` +} +type GameRole struct { + Uid string `json:"game_uid"` + NickName string `json:"nickname"` + Region string `json:"region"` + RegionName string `json:"region_name"` +} + +func GetUserGameRoles(cookie string) ([]GameRole, error) { + mr := NewMiyoRequest("https://api-takumi.mihoyo.com/binding/api/getUserGameRolesByCookie?game_biz=hk4e_cn") + mr.SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) miHoYoBBS/2.11.1") + mr.SetHeader("Referer", "https://webstatic.mihoyo.com/app/community-game-records/index.html?v=6") + mr.SetHeader("Cookie", cookie) + mr.SetHeader("X-Requested-With", "com.mihoyo.hyperion") + data, err := mr.Execute() + if err != nil { + return nil, err + } + roleList := GameRoleList{} + json.Unmarshal(data, &roleList) + if len(roleList.List) == 0 { + return nil, errors.New("没有找到绑定的角色") + } + return roleList.List, nil +} + +func GetUserGameRoleByUid(cookie string, uid string) (*GameRole, error) { + roles, err := GetUserGameRoles(cookie) + if err != nil { + return nil, err + } + for i := range roles { + if roles[i].Uid == uid { + return &roles[i], nil + } + } + return nil, errors.New("没有找到UID为" + uid + "的角色") +} + +func GetGenshinDailyNote(cookie, uid, server string) (*GenshinDailyNote, error) { + dailyNoteApi := func(useOld string) string { + if strings.EqualFold(useOld, "yes") { + return "https://api-takumi.mihoyo.com/game_record/app/genshin/api/dailyNote" + } else { + return "https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/dailyNote" + } + }(global.UseOldApi) + url := fmt.Sprintf("%s?server=%s&role_id=%s", dailyNoteApi, server, uid) + mr := NewMiyoRequest(url) + mr.SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) miHoYoBBS/2.11.1") + mr.SetHeader("Referer", "https://webstatic.mihoyo.com/app/community-game-records/index.html?v=6") + mr.SetHeader("Cookie", cookie) + mr.SetHeader("X-Requested-With", "com.mihoyo.hyperion") + appType, appVersion, DS := utils.GetDS(mr.url, "") + mr.SetHeader("x-rpc-client_type", appType) + mr.SetHeader("x-rpc-app_version", appVersion) + mr.SetHeader("DS", DS) + data, err := mr.Execute() + if err != nil { + return nil, err + } + dailyNote := GenshinDailyNote{} + json.Unmarshal(data, &dailyNote) + return &dailyNote, nil +} diff --git a/mihoyo/service_test.go b/mihoyo/service_test.go new file mode 100644 index 0000000..36745bb --- /dev/null +++ b/mihoyo/service_test.go @@ -0,0 +1,91 @@ +package mihoyo + +import ( + "GenshinImpact/global" + "fmt" + "testing" +) + +func TestGetUserGameRoles(t *testing.T) { + //cookie := "_MHYUUc0da99476341-03ea061453bd79-b7a193d-1fa400-17c0da994774b4; mi18nLang=zh-cn; _ga_6BQ6ZYZ4YR=GS1.1.1633508240.1.1.1633508446.0; _ga_YQPW66MJ73=GS1.1.1634521273.2.1.1634521348.0; _gid=GA1.2.1068721196.1635730970; ltoken=Idy4cRzSH6WpeJ8FumxXkQTSwanMDNrOGRJmNXtW; ltuid=285923115;cookie_token=8PnCRXkDdLuOO3ARsZ2R4q026Rz0zSuXHtYtzxlF; account_id=285923115; _ga_9TTX3TE5YL=GS1.1.1635824302.10.0.1635824302.0;CNZZDATA1275023096=469636296-1634047771-%7C1635902038; _ga=GA1.2.601055352.1632719707; _ga_4PPV2TWM03=GS1.1.1635905077.4.1.1635905272.0; _gat_gtag_UA_133007358_5=1" + //cookie := "UM_distin4-4f52-4efa-86fe-0fc38bab41bd; CNZZDATA1274689524=1398771365-1657883224-%7C1657883224; DEVICEFP_SEED_ID=79a5168fe5fe2d89; DEVICEFP_SEED_TIME=1657883475835; DEVICEFP=38d7eab17bad9; login_uid=285923115; login_ticket=mnve82eLl77Crj48JvUMSrOR20b9qSCD5TJLPIPe" + roles, err := GetUserGameRoles(global.MiCookie) + if err != nil { + fmt.Println(err.Error()) + return + } + for index, role := range roles { + fmt.Println(index) + fmt.Println(role.Region) + fmt.Println(role.RegionName) + fmt.Println(role.NickName) + fmt.Println(role.Uid) + } + //0 + //cn_gf01 + //天空岛 + //十里坡 + //186044778 + +} +func TestGetUserGameRoleByUid(t *testing.T) { + cookie := "_MHYa994774b4; mi18nLang=zh-cn; _ga_6BQ6ZYZ4YR=GS1.1.1633508240.1.1.1633508446.0; _ga_YQPW66MJ73=GS1.1.1634521273.2.1.1634521348.0; _gid=GA1.2.1068721196.1635730970; ltoken=Idy4cRzSH6WpeJ8FumxXkQTSwanMDNrOGRJmNXtW; ltuid=285923115;cookie_token=8PnCRXkDdLuOO3ARsZ2R4q026Rz0zSuXHtYtzxlF; account_id=285923115; _ga_9TTX3TE5YL=GS1.1.1635824302.10.0.1635824302.0;CNZZDATA1275023096=469636296-1634047771-%7C1635902038; _ga=GA1.2.601055352.1632719707; _ga_4PPV2TWM03=GS1.1.1635905077.4.1.1635905272.0; _gat_gtag_UA_133007358_5=1" + uid := "1000000" + role, err := GetUserGameRoleByUid(cookie, uid) + if err != nil { + fmt.Println(err.Error()) + return + } + fmt.Println(role.Region) + fmt.Println(role.RegionName) + fmt.Println(role.NickName) + fmt.Println(role.Uid) + +} + +// test for GenshinDailyNote +func TestAlert(t *testing.T) { + //test for Alert + note, err := GetGenshinDailyNote(global.MiCookie, global.MiUid, global.MiServer) + if err != nil { + fmt.Println(err.Error()) + return + } + fmt.Println(note) + fmt.Println(note.CurrentResin) + +} +func TestGetGenshinDailyNote(t *testing.T) { + cookie := "_MHYU.1.1.1633508446.0; _ga_YQPW66MJ73=GS1.1.1634521273.2.1.1634521348.0; _gid=GA1.2.1068721196.1635730970; ltoken=Idy4cRzSH6WpeJ8FumxXkQTSwanMDNrOGRJmNXtW; ltuid=285923115;cookie_token=8PnCRXkDdLuOO3ARsZ2R4q026Rz0zSuXHtYtzxlF; account_id=285923115; _ga_9TTX3TE5YL=GS1.1.1635824302.10.0.1635824302.0;CNZZDATA1275023096=469636296-1634047771-%7C1635902038; _ga=GA1.2.601055352.1632719707; _ga_4PPV2TWM03=GS1.1.1635905077.4.1.1635905272.0; _gat_gtag_UA_133007358_5=1" + uid := "100000" + server := "cn_gf01" + note, err := GetGenshinDailyNote(cookie, uid, server) + if err != nil { + fmt.Println(err.Error()) + return + } + //fmt.Println(note.CurrentResin) //当前树脂 + //fmt.Println(note.MaxResin) //树脂容量 + //fmt.Println(note.ResinRecoveryTime) //树脂充满时间s + //fmt.Println(note.FinishedTaskNum) //树脂充满时间s + //fmt.Println(note.TotalTaskNum) //树脂充满时间s + //fmt.Println(note.IsExtraTaskRewardReceived) //树脂充满时间s + //fmt.Println(note.RemainResinDiscountNum) //树脂充满时间s + //fmt.Println(note.ResinDiscountNumLimit) //树脂充满时间s + //fmt.Println(note.CurrentExpeditionNum) //5 + //fmt.Println(note.MaxExpeditionNum) //5 + //fmt.Println(note.Expeditions) //[{https://upload-bbs.mihoyo.com/game_record/genshin/character_side_icon/UI_AvatarIcon_Side_Ambor.png Finished 0} {https://upload-bbs.mihoyo.com/game_record/genshin/character_side_icon/UI_AvatarIcon_Side_Fischl.png Finished 0} {https://upload-bbs.mihoyo.com/game_record/genshin/character_side_icon/UI_AvatarIcon_Side_Bennett.png Finished 0} {https://upload-bbs.mihoyo.com/game_record/genshin/character_side_icon/UI_AvatarIcon_Side_Sara.png Finished 0} {https://upload-bbs.mihoyo.com/game_record/genshin/character_side_icon/UI_AvatarIcon_Side_Shinobu.png Finished 0}] + // + //fmt.Println(note.CurrentHomeCoin) //洞天宝钱 + //fmt.Println(note.MaxHomeCoin) //洞天宝钱 + //fmt.Println(note.HomeCoinRecoveryTime) //充满时间s + //fmt.Println(note.CalendarUrl) //充满时间s + fmt.Println(note.Transformer) //{true {2 0 0 0 false}} 转化仪 + fmt.Println(note.Transformer.RecTime.Reached) + NoticeTransformer(note) + //"transformer":{"obtained":true,"recovery_time":{"Day":2,"Hour":0,"Minute":0,"Second":0,"reached":false} + //{true {0 0 0 0 true} https://bbs.mihoyo.com/ys/obc/content/1562/detail?bbs_presentation_style=no_header false 0} + + //{"current_resin":117,"max_resin":160,"resin_recovery_time":"20216","finished_task_num":0,"total_task_num":4,"is_extra_task_reward_received":false,"remain_resin_discount_num":0,"resin_discount_num_limit":3,"current_expedition_num":5,"max_expedition_num":5,"expeditions":[{"avatar_side_icon":"https://upload-bbs.mihoyo.com/game_record/genshin/character_side_icon/UI_AvatarIcon_Side_Ambor.png","status":"Finished","remained_time":"0"},{"avatar_side_icon":"https://upload-bbs.mihoyo.com/game_record/genshin/character_side_icon/UI_AvatarIcon_Side_Fischl.png","status":"Finished","remained_time":"0"},{"avatar_side_icon":"https://upload-bbs.mihoyo.com/game_record/genshin/character_side_icon/UI_AvatarIcon_Side_Bennett.png","status":"Finished","remained_time":"0"},{"avatar_side_icon":"https://upload-bbs.mihoyo.com/game_record/genshin/character_side_icon/UI_AvatarIcon_Side_Sara.png","status":"Finished","remained_time":"0"},{"avatar_side_icon":"https://upload-bbs.mihoyo.com/game_record/genshin/character_side_icon/UI_AvatarIcon_Side_Shinobu.png","status":"Finished","remained_time":"0"}],"current_home_coin":810,"max_home_coin":2400,"home_coin_recovery_time":"190773","calendar_url":"","transformer":{"obtained":true,"recovery_time":{"Day":2,"Hour":0,"Minute":0,"Second":0,"reached":false},"wiki":"https://bbs.mihoyo.com/ys/obc/content/1562/detail?bbs_presentation_style=no_header","noticed":false,"latest_job_id":"0"}} + +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..8111972 --- /dev/null +++ b/readme.md @@ -0,0 +1,62 @@ +[![Build Status](https://xxx/api/badges/sre/GenshinImpact/status.svg)](https://xxx/GenshinImpact) + +原神工具箱 +*** + +## build amd64 on tx ecs +```bash +cd /root +rm -rf GenshinImpact +git clone https://git.sre.ink/go/GenshinImpact.git +cd GenshinImpact +CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on GOPROXY="https://goproxy.cn,direct" go mod tidy +CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on GOPROXY="https://goproxy.cn,direct" go build -v -a -o genshin.bin main.go +docker build -t sre/genshin:amd64 . +docker rm -f genshin +docker run --name genshin --restart always -d sre/genshin:amd64 +docker logs -f genshin +``` + + +## build arm64 on oracle +```bash +cd /root +rm -rf GenshinImpact +git clone https://git.sre.ink/go/GenshinImpact.git +cd GenshinImpact +CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GO111MODULE=on go mod tidy +CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GO111MODULE=on go build -v -a -o genshin.bin main.go +docker build -t sre/genshin:arm64 . +docker rm -f genshin +docker run --name genshin --restart always -d sre/genshin:arm64 +docker logs -f genshin +``` + +# mihoyo bbs get cookie 获取米游社Cookie +将下列地址存为书签 +```javascript +var cookie=document.cookie;var ask=confirm('Cookie:'+cookie+'\n\nDo you want to copy the cookie to the clipboard?');if(ask==true){copy(cookie);msg=cookie}else{msg='Cancel'} +``` +登录后点击即可。 + + +# up char +当期up占5星出率的50% 1个 +当期up占4星出率的50% 3个 + +5星概率为0.6% 最多90次必出5星 +4星概率为5.1% 其中角色2.55% 武器2.55% 最多10次必出4星 + +# up weapon +up5占5星出货75% +up4占4星出货75% +5星概率为0.7% 最多80次必出5星 +4星概率为6% 其中角色3% 武器3% 最多10次必出4星 +当起源到4星时 75%为up武器 +如果本次不是当期up 下一个一定是up + +# notice +https://github.com/dustoair/genshin-daily-notice + +# Golang 定时任务 github/robfig/cron/v3 +https://blog.csdn.net/zjbyough/article/details/113853582 diff --git a/thirdpart/WeChat.go b/thirdpart/WeChat.go new file mode 100644 index 0000000..f397f8b --- /dev/null +++ b/thirdpart/WeChat.go @@ -0,0 +1,114 @@ +package thirdpart + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "reflect" +) + +var WECOM_CID string = GetEnvDefault("WECOM_CID", "eeeeeeeee") +var WECOM_SECRET string = GetEnvDefault("WECOM_SECRET", "eeeeeeeeeeeeeeeeeeeeeeeee") +var WECOM_AID string = GetEnvDefault("WECOM_AID", "100000000") +var WECOM_TOUID string = GetEnvDefault("WECOM_TOUID", "eeeeeee") +var REDIS_STAT string = GetEnvDefault("REDIS_STAT", "OFF") +var REDIS_PASSWORD string = GetEnvDefault("REDIS_PASSWORD", "") +var ctx = context.Background() + +func GetEnvDefault(key, defVal string) string { + val, ex := os.LookupEnv(key) + if !ex { + return defVal + } + return val +} + +func praser_json(json_str string) map[string]interface{} { + var wecom_response map[string]interface{} + if string(json_str) != "" { + err := json.Unmarshal([]byte(string(json_str)), &wecom_response) + if err != nil { + log.Println("生成json字符串错误") + } + } + return wecom_response +} + +func get_token(corpid, app_secret string) string { + resp, err := http.Get("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=" + corpid + "&corpsecret=" + app_secret) + if err != nil { + log.Println(err) + } + defer resp.Body.Close() + resp_data, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Println(err) + } + token_response := praser_json(string(resp_data)) + return token_response["access_token"].(string) +} + +func post_msg(text_msg, msg_type, post_url string) string { + type msg struct { + Content string `json:"content"` + } + type JsonData struct { + Touser string `json:"touser"` + Agentid string `json:"agentid"` + Msgtype string `json:"msgtype"` + Text msg `json:"text"` + Duplicate_check_interval int `json:"duplicate_check_interval"` + } + post_data := JsonData{ + Touser: WECOM_TOUID, + Agentid: WECOM_AID, + Msgtype: msg_type, + Duplicate_check_interval: 600, + Text: msg{Content: text_msg}, + } + + post_json, _ := json.Marshal(post_data) + log.Println(string(post_json)) + msg_req, err := http.NewRequest("POST", post_url, bytes.NewBuffer(post_json)) + if err != nil { + log.Println(err) + } + msg_req.Header.Set("Content-Type", "application/json") + client := &http.Client{} + resp, err := client.Do(msg_req) + if err != nil { + panic(err) + } + defer msg_req.Body.Close() + body, _ := ioutil.ReadAll(resp.Body) + return string(body) +} + +func IsZero(v interface{}) (bool, error) { + t := reflect.TypeOf(v) + if !t.Comparable() { + return false, fmt.Errorf("type is not comparable: %v", t) + } + return v == reflect.Zero(t).Interface(), nil +} + +func SendWechat(msg string) { + access_token := get_token(WECOM_CID, WECOM_SECRET) + post_url := "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + access_token + msg_type := "text" + post_status := post_msg(msg, msg_type, post_url) + log.Println(post_status) + post_response := praser_json(string(post_status)) + log.Println(post_response) + errcode := post_response["errcode"] + _, err := IsZero(errcode) + if err != nil { + fmt.Printf("%v", err) + } + +} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..92829bd --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,111 @@ +package utils + +import ( + "crypto/md5" + "encoding/hex" + "fmt" + "math/rand" + "regexp" + "sort" + "strings" + "time" +) + +func toHexDigest(str string) string { + h := md5.New() + h.Write([]byte(str)) + + return hex.EncodeToString(h.Sum(nil)) +} + +/* contributor: https://github.com/Azure99/GenshinPlayerQuery/issues/20 @lulu666lulu */ +func GetDS(url, data string) (t, v, ds string) { + // 5: mobile web + const appType = "5" + const apiSalt = "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs" + const appVersion = "2.11.1" + rand.Seed(time.Now().UnixNano()) + + // unix timestamp + i := time.Now().Unix() + // random number 100k - 200k + r := rand.Intn(100000) + 100000 + // body + b := data + // query + q := func() string { + urlParts := strings.Split(url, "?") + if len(urlParts) == 2 { + querys := strings.Split(urlParts[1], "&") + sort.Strings(querys) + return strings.Join(querys, "&") + } + return "" + }() + c := toHexDigest(fmt.Sprintf("salt=%s&t=%d&r=%d&b=%s&q=%s", apiSalt, i, r, b, q)) + ds = fmt.Sprintf("%d,%d,%s", i, r, c) + + return appType, appVersion, ds +} + +/* + * t1, t2: string, HH:MM + * t1 > t2 return 1 + * t1 = t2 return 0 + * t1 < t2 return -1 + */ +func timeCmp(t1, t2 string) int { + hh1, mm1, hh2, mm2 := 0, 0, 0, 0 + fmt.Sscanf(t1, "%d:%d", &hh1, &mm1) + fmt.Sscanf(t2, "%d:%d", &hh2, &mm2) + + switch { + case hh1 > hh2: + return 1 + case hh2 > hh1: + return -1 + case mm1 > mm2: + return 1 + case mm1 < mm2: + return -1 + default: + return 0 + } +} + +/* + * 判断当前时刻是否在某个事件段内, 分钟级 + * timeRangeStr: string, HH:MM-HH:MM + * 返回true or false, 若结果true,第二个返回值为距离结束时间段结束的时间,否则返回0 + * 如果timeRangeStr格式错误,返回false, 0 + */ +func NowInTimeRange(timeRangeStr string) (bool, time.Duration) { + rc := regexp.MustCompile("^((0?[0-9]|1[0-9]|2[0-3]):(0?[0-9]|[1-5][0-9])) *- *((0?[0-9]|1[0-9]|2[0-3]):(0?[0-9]|[1-5][0-9]))$") + if timeRangeStr == "" || !rc.Match([]byte(timeRangeStr)) { + /* 无效的时间段 */ + return false, 0 + } + waitTime := func(now, end time.Time) time.Duration { + return time.Duration((end.Unix() - now.Unix())) * time.Second + } + timeRange := regexp.MustCompile("((0?[0-9]|1[0-9]|2[0-3]):([1-5][0-9]|0?[0-9]))").FindAllString(timeRangeStr, 2) + now := time.Now() + end, _ := time.ParseInLocation("2006-01-02 15:04", fmt.Sprintf("%d-%02d-%02d %s", now.Year(), now.Month(), now.Day(), timeRange[1]), time.Local) + nowHHMM := fmt.Sprint(now.Hour(), ":", now.Minute()) + if timeCmp(timeRange[0], timeRange[1]) > 0 { + /* 时间范围跨过0点 */ + if timeCmp(nowHHMM, timeRange[0]) >= 0 && timeCmp(nowHHMM, "23:59") <= 0 { + /* 0点前 */ + return true, waitTime(now, end.AddDate(0, 0, 1)) + } + if timeCmp(nowHHMM, "00:00") >= 0 && timeCmp(nowHHMM, timeRange[1]) < 0 { + return true, waitTime(now, end) + } + } else { + if timeCmp(nowHHMM, timeRange[0]) >= 0 && timeCmp(nowHHMM, timeRange[1]) < 0 { + return true, waitTime(now, end) + } + } + + return false, 0 +}