diff --git a/src/plugins/sign/auto_sign.go b/src/plugins/sign/auto_sign.go index c639ffa..1a210a6 100644 --- a/src/plugins/sign/auto_sign.go +++ b/src/plugins/sign/auto_sign.go @@ -48,13 +48,8 @@ func sign(user UserSign) { skAccount.Skland.Token = userAccount.SklandToken skAccount.Skland.Cred = userAccount.SklandCred - var skPlayer skland.Player - skPlayer.NickName = player.PlayerName - skPlayer.ChannelName = player.ServerName - skPlayer.Uid = player.Uid - // 执行签到 - record, err := skland.SignGamePlayer(&skPlayer, skAccount) + award, hasSigned, err := skland.SignGamePlayer(player.Uid, skAccount) if err != nil { // 签到失败 sendMessage := tgbotapi.NewMessage(user.UserNumber, fmt.Sprintf("角色 %s 签到失败!\nmsg:%s", player.PlayerName, err.Error())) @@ -63,13 +58,13 @@ func sign(user UserSign) { return } // 今日已完成签到 - if record.HasSigned { + if hasSigned { sendMessage := tgbotapi.NewMessage(user.UserNumber, fmt.Sprintf("角色 %s 今天已经签到过了", player.PlayerName)) bot.Arknights.Send(sendMessage) return } // 签到成功 - sendMessage := tgbotapi.NewMessage(user.UserNumber, fmt.Sprintf("角色 %s 签到成功!\n今日奖励:%s", player.PlayerName, record.Award)) + sendMessage := tgbotapi.NewMessage(user.UserNumber, fmt.Sprintf("角色 %s 签到成功!\n今日奖励:%s", player.PlayerName, award)) bot.Arknights.Send(sendMessage) } } diff --git a/src/plugins/sign/sign_handle.go b/src/plugins/sign/sign_handle.go index 4d88644..760d35f 100644 --- a/src/plugins/sign/sign_handle.go +++ b/src/plugins/sign/sign_handle.go @@ -80,12 +80,8 @@ func SignHandle(update tgbotapi.Update) error { } func Sign(player account.UserPlayer, account account.UserAccount, chatId int64) error { - var skPlayer skland.Player var skAccount skland.Account playerName := player.PlayerName - skPlayer.NickName = playerName - skPlayer.ChannelName = player.ServerName - skPlayer.Uid = player.Uid skAccount.Hypergryph.Token = account.HypergryphToken skAccount.Skland.Token = account.SklandToken skAccount.Skland.Cred = account.SklandCred @@ -93,7 +89,7 @@ func Sign(player account.UserPlayer, account account.UserAccount, chatId int64) sendAction := tgbotapi.NewChatAction(chatId, "typing") bot.Arknights.Send(sendAction) - record, err := skland.SignGamePlayer(&skPlayer, skAccount) + award, hasSigned, err := skland.SignGamePlayer(player.Uid, skAccount) if err != nil { sendMessage := tgbotapi.NewMessage(chatId, fmt.Sprintf("角色 %s 签到失败!\nmsg:%s", playerName, err.Error())) msg, _ := bot.Arknights.Send(sendMessage) @@ -102,13 +98,13 @@ func Sign(player account.UserPlayer, account account.UserAccount, chatId int64) return err } // 今日已完成签到 - if record.HasSigned { + if hasSigned { sendMessage := tgbotapi.NewMessage(chatId, fmt.Sprintf("角色 %s 今天已经签到过了", playerName)) bot.Arknights.Send(sendMessage) return nil } // 签到成功 - sendMessage := tgbotapi.NewMessage(chatId, fmt.Sprintf("角色 %s 签到成功!\n今日奖励:%s", playerName, record.Award)) + sendMessage := tgbotapi.NewMessage(chatId, fmt.Sprintf("角色 %s 签到成功!\n今日奖励:%s", playerName, award)) bot.Arknights.Send(sendMessage) return nil } diff --git a/src/plugins/skland/auth.go b/src/plugins/skland/auth.go index e17c4c5..e979c74 100644 --- a/src/plugins/skland/auth.go +++ b/src/plugins/skland/auth.go @@ -64,7 +64,7 @@ func Login(token string) (Account, error) { } account.Hypergryph.Token = token - res, err := grantApp(token, AppCodeSKLAND) + res, err := grantApp(token, "4ca99fa6b56cc2ba") if err != nil { return account, fmt.Errorf("grant app error: %w", err) } @@ -127,12 +127,12 @@ func RefreshToken(account Account) (Account, error) { return account, nil } -// 获取用户信息 +// GetUser 获取用户信息 func GetUser(skland AccountSkland) (*User, error) { return SklandRequest[*User](SKR(), "GET", "/api/v1/user", skland) } -// 检查token有效性 +// CheckToken 检查token有效性 func CheckToken(token string) error { req := SKR().SetQueryParam("token", token) _, err := HypergryphRequest[any](req, "GET", "/user/info/v1/basic") @@ -177,9 +177,3 @@ func ArknightsPlayers(skland AccountSkland) ([]*Player, error) { } return players, nil } - -// GenTokenByUid 根据Oauth token和uid生成应用token -func GenTokenByUid(uid string, token string) (*GenTokenByUidData, error) { - req := HR().SetBody(gh.M{"uid": uid, "token": token}) - return HypergryphBindingAPIRequest[*GenTokenByUidData](req, "POST", "/account/binding/v1/token_by_uid") -} diff --git a/src/plugins/skland/config.go b/src/plugins/skland/config.go deleted file mode 100644 index a6fdb99..0000000 --- a/src/plugins/skland/config.go +++ /dev/null @@ -1,22 +0,0 @@ -package skland - -const ( - HypergryphAddr = "https://as.hypergryph.com" - HypergryphAKAddr = "https://ak.hypergryph.com" - HypergryphBindingAPIAddr = "https://binding-api-account-prod.hypergryph.com" - HypergryphUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" - AppCodeSKLAND = "4ca99fa6b56cc2ba" - - SklandAddr = "https://zonai.skland.com" - SklandUserAgent = "Skland/1.5.1 (com.hypergryph.skland; build:100501001; Android 33; ) Okhttp/4.11.0" - Platform = "1" - VName = "1.5.1" - DId = "743a446c83032899" - - GameCodeArknights = "1" // 明日方舟 - GameCodeExastris = "2" // 来自星尘 - GameCodeEndfleld = "3" // 终末地 - GameCodePopucom = "4" // 泡姆泡姆 - GameCodeNest = "100" // 纳斯特港 - GameAppCodeArknights = "arknights" -) diff --git a/src/plugins/skland/game_sign.go b/src/plugins/skland/game_sign.go new file mode 100644 index 0000000..af2a843 --- /dev/null +++ b/src/plugins/skland/game_sign.go @@ -0,0 +1,67 @@ +package skland + +import ( + "fmt" + "github.com/starudream/go-lib/core/v2/gh" + "github.com/starudream/go-lib/resty/v2" + "strconv" + "strings" +) + +type SignGameData struct { + Ts string `json:"ts"` + Awards SignGameAwards `json:"awards"` +} + +type SignGameAward struct { + Type string `json:"type"` + Count int `json:"count"` + Resource *SignGameRes `json:"resource"` +} + +type SignGameRes struct { + Id string `json:"id"` + Type string `json:"type"` + Name string `json:"name"` + Rarity int `json:"rarity"` +} + +type SignGameAwards []*SignGameAward + +func SignGamePlayer(uid string, account Account) (award string, hasSigned bool, err error) { + account, err = RefreshToken(account) + if err != nil { + return + } + signGameData, err := signGame("1", uid, account.Skland) + if err != nil { + e, ok1 := resty.AsRespErr(err) + if ok1 { + t, ok2 := e.Response.Error().(*SKBaseResp[interface{}]) + if ok2 && t.Message == "请勿重复签到!" { + err = nil + hasSigned = true + } + } else { + err = fmt.Errorf("sign game error: %w", err) + return + } + } else { + award = signGameData.Awards.shortString() + } + return +} + +// 签到 +func signGame(gid, uid string, skland AccountSkland) (*SignGameData, error) { + req := SKR().SetBody(gh.M{"gameId": gid, "uid": uid}) + return SklandRequest[*SignGameData](req, "POST", "/api/v1/game/attendance", skland) +} + +func (t SignGameAwards) shortString() string { + v := make([]string, len(t)) + for i, a := range t { + v[i] = a.Resource.Name + "*" + strconv.Itoa(a.Count) + } + return strings.Join(v, ", ") +} diff --git a/src/plugins/skland/generate_sign.go b/src/plugins/skland/generate_sign.go new file mode 100644 index 0000000..6e9e119 --- /dev/null +++ b/src/plugins/skland/generate_sign.go @@ -0,0 +1,59 @@ +package skland + +import ( + "crypto/hmac" + "crypto/md5" + "crypto/sha256" + "encoding/hex" + "github.com/starudream/go-lib/core/v2/codec/json" + "github.com/starudream/go-lib/core/v2/utils/structutil" + "github.com/starudream/go-lib/resty/v2" + "net/url" + "strconv" + "time" +) + +func addSign(r *resty.Request, method, path string, skland AccountSkland) { + ts := strconv.FormatInt(time.Now().Unix(), 10) + + headers := signHeaders{Platform: "1", Timestamp: ts, DId: "743a446c83032899", VName: "1.5.1"} + + r.SetHeaders(tom(headers)) + + _, signature := sign(headers, method, path, skland.Token, r.QueryParam, r.Body) + + r.SetHeader("cred", skland.Cred) + r.SetHeader("sign", signature) +} + +func tom(s any) map[string]string { + t := structutil.New(s) + t.TagName = "json" + m := map[string]string{} + for k, v := range t.Map() { + m[k] = v.(string) + } + return m +} + +func sign(headers signHeaders, method, path, token string, query url.Values, body any) (string, string) { + str := query.Encode() + if method != "GET" { + str = json.MustMarshalString(body) + } + + content := path + str + headers.Timestamp + json.MustMarshalString(headers) + + b1 := hmac256(token, content) + s1 := hex.EncodeToString(b1) + b2 := md5.Sum([]byte(s1)) + s2 := hex.EncodeToString(b2[:]) + + return content, s2 +} + +func hmac256(key, content string) []byte { + h := hmac.New(sha256.New, []byte(key)) + h.Write([]byte(content)) + return h.Sum(nil) +} diff --git a/src/plugins/skland/hypergryph_request.go b/src/plugins/skland/hypergryph_request.go index ef62b74..e6fb108 100644 --- a/src/plugins/skland/hypergryph_request.go +++ b/src/plugins/skland/hypergryph_request.go @@ -1,19 +1,14 @@ package skland import ( - "crypto/hmac" - "crypto/md5" - "crypto/sha256" - "encoding/hex" "fmt" - "github.com/starudream/go-lib/core/v2/codec/json" - "github.com/starudream/go-lib/core/v2/utils/structutil" + "github.com/spf13/viper" "github.com/starudream/go-lib/resty/v2" - "net/url" - "strconv" - "time" ) +var HypergryphAddr = "https://as.hypergryph.com" +var HypergryphAKAddr = "https://ak.hypergryph.com" + type HBaseResp[T any] struct { StatusCode *int `json:"statusCode"` Error string `json:"error"` @@ -26,12 +21,6 @@ type HBaseResp[T any] struct { Data T `json:"data,omitempty"` } -type SKBaseResp[T any] struct { - Code *int `json:"code"` - Message string `json:"message"` - Data T `json:"data,omitempty"` -} - type signHeaders struct { Platform string `json:"platform"` Timestamp string `json:"timestamp"` @@ -43,10 +32,6 @@ func (t *HBaseResp[T]) IsSuccess() bool { return t != nil && t.Status != nil && *t.Status == 0 } -func (t *SKBaseResp[T]) IsSuccess() bool { - return t != nil && t.Code != nil && *t.Code == 0 -} - func (t *HBaseResp[T]) String() string { if t != nil && t.StatusCode != nil { return fmt.Sprintf("status: %d, error: %s, message: %s", *t.StatusCode, t.Error, t.Message) @@ -56,16 +41,8 @@ func (t *HBaseResp[T]) String() string { return "" } -func (t *SKBaseResp[T]) String() string { - return fmt.Sprintf("code: %d, message: %s", *t.Code, t.Message) -} - func HR() *resty.Request { - return resty.R().SetHeader("User-Agent", HypergryphUserAgent).SetHeader("Accept-Encoding", "gzip") -} - -func SKR() *resty.Request { - return resty.R().SetHeader("User-Agent", SklandUserAgent).SetHeader("Accept-Encoding", "gzip") + return resty.R().SetHeader("User-Agent", viper.GetString("api.user_agent")).SetHeader("Accept-Encoding", "gzip") } func IsUnauthorized(err error) bool { @@ -86,16 +63,6 @@ func HypergryphRequest[T any](r *resty.Request, method, path string) (t T, _ err return res.Data, nil } -func HypergryphBindingAPIRequest[T any](r *resty.Request, method, path string) (t T, _ error) { - res, err := resty.ParseResp[*HBaseResp[any], *HBaseResp[T]]( - r.SetError(&HBaseResp[any]{}).SetResult(&HBaseResp[T]{}).Execute(method, HypergryphBindingAPIAddr+path), - ) - if err != nil { - return t, fmt.Errorf("[hypergryph] %w", err) - } - return res.Data, nil -} - func HypergryphAKRequest(r *resty.Request, method, path string) (d string, _ error) { res, err := r.Execute(method, HypergryphAKAddr+path) if err != nil { @@ -103,80 +70,3 @@ func HypergryphAKRequest(r *resty.Request, method, path string) (d string, _ err } return string(res.Body()), nil } - -func SklandRequest[T any](r *resty.Request, method, path string, vs ...any) (t T, _ error) { - for i := 0; i < len(vs); i++ { - switch v := vs[i].(type) { - case AccountSkland: - addSign(r, method, path, v) - } - } - - res, err := resty.ParseResp[*SKBaseResp[any], *SKBaseResp[T]]( - r.SetError(&SKBaseResp[any]{}).SetResult(&SKBaseResp[T]{}).Execute(method, SklandAddr+path), - ) - if err != nil { - return t, fmt.Errorf("[skland] %w", err) - } - return res.Data, nil -} - -func SklandRequestPlayerData(r *resty.Request, method, path string, vs ...any) (d string, _ error) { - for i := 0; i < len(vs); i++ { - switch v := vs[i].(type) { - case AccountSkland: - addSign(r, method, path, v) - } - } - - res, err := r.Execute(method, SklandAddr+path) - if err != nil { - return d, fmt.Errorf("[skland] %w", err) - } - return string(res.Body()), nil -} - -func addSign(r *resty.Request, method, path string, skland AccountSkland) { - ts := strconv.FormatInt(time.Now().Unix(), 10) - - headers := signHeaders{Platform: Platform, Timestamp: ts, DId: DId, VName: VName} - - r.SetHeaders(tom(headers)) - - _, signature := sign(headers, method, path, skland.Token, r.QueryParam, r.Body) - - r.SetHeader("cred", skland.Cred) - r.SetHeader("sign", signature) -} - -func tom(s any) map[string]string { - t := structutil.New(s) - t.TagName = "json" - m := map[string]string{} - for k, v := range t.Map() { - m[k] = v.(string) - } - return m -} - -func sign(headers signHeaders, method, path, token string, query url.Values, body any) (string, string) { - str := query.Encode() - if method != "GET" { - str = json.MustMarshalString(body) - } - - content := path + str + headers.Timestamp + json.MustMarshalString(headers) - - b1 := hmac256(token, content) - s1 := hex.EncodeToString(b1) - b2 := md5.Sum([]byte(s1)) - s2 := hex.EncodeToString(b2[:]) - - return content, s2 -} - -func hmac256(key, content string) []byte { - h := hmac.New(sha256.New, []byte(key)) - h.Write([]byte(content)) - return h.Sum(nil) -} diff --git a/src/plugins/skland/player_info.go b/src/plugins/skland/player_info.go index c3550e1..7ed77eb 100644 --- a/src/plugins/skland/player_info.go +++ b/src/plugins/skland/player_info.go @@ -24,11 +24,6 @@ func GetPlayerInfo(uid string, account Account) (*PlayerData, Account, error) { return playerData, account, nil } -func getPlayerInfo(uid string, skland AccountSkland) (*PlayerData, error) { - req := SKR().SetQueryParams(gh.MS{"uid": uid}) - return SklandRequest[*PlayerData](req, "GET", "/api/v1/game/player/info", skland) -} - func getPlayerInfoStr(uid string, skland AccountSkland) (string, error) { req := SKR().SetQueryParams(gh.MS{"uid": uid}) return SklandRequestPlayerData(req, "GET", "/api/v1/game/player/info", skland) diff --git a/src/plugins/skland/player_redeem.go b/src/plugins/skland/player_redeem.go index 8b06e48..e149982 100644 --- a/src/plugins/skland/player_redeem.go +++ b/src/plugins/skland/player_redeem.go @@ -51,7 +51,7 @@ func getPlayerRedeem(token, cdk, channelId string) (string, error) { } } headers["X-Csrf-Token"] = csrfToken - req1 := SKR().SetHeaders(headers).SetBody(gh.M{"token": token, "giftCode": cdk, "channelId": channelId}).SetCookies(resp.Cookies()) + req1 := HR().SetHeaders(headers).SetBody(gh.M{"token": token, "giftCode": cdk, "channelId": channelId}).SetCookies(resp.Cookies()) res, err := HypergryphAKRequest(req1, "POST", path) if err != nil { return "", fmt.Errorf("发送兑换请求失败") diff --git a/src/plugins/skland/sign.go b/src/plugins/skland/sign.go deleted file mode 100644 index cada178..0000000 --- a/src/plugins/skland/sign.go +++ /dev/null @@ -1,149 +0,0 @@ -package skland - -import ( - "fmt" - "github.com/starudream/go-lib/core/v2/gh" - "strconv" - "strings" - "time" -) - -var signGameCodeByAppCode = map[string]string{ - GameAppCodeArknights: GameCodeArknights, -} - -type SignGameData struct { - Ts string `json:"ts"` - Awards SignGameAwards `json:"awards"` -} - -type SignGameRecord struct { - GameId string - GameName string - PlayerName string - PlayerUid string - PlayerChannel string - HasSigned bool - Award string -} - -type ListAttendanceData struct { - CurrentTs string `json:"currentTs"` - Calendar []*Calendar `json:"calendar"` - Records CalendarRecords `json:"records"` - ResourceInfoMap map[string]*SignGameRes `json:"resourceInfoMap"` -} - -type SignGameAward struct { - Type string `json:"type"` - Count int `json:"count"` - Resource *SignGameRes `json:"resource"` -} - -type SignGameRes struct { - Id string `json:"id"` - Type string `json:"type"` - Name string `json:"name"` - Rarity int `json:"rarity"` -} - -type CalendarRecord struct { - Ts string `json:"ts"` - ResourceId string `json:"resourceId"` - Type string `json:"type"` - Count int `json:"count"` -} - -type Calendar struct { - ResourceId string `json:"resourceId"` - Type string `json:"type"` - Count int `json:"count"` - Available bool `json:"available"` - Done bool `json:"done"` -} - -type CalendarRecords []*CalendarRecord -type SignGameAwards []*SignGameAward - -func SignGamePlayer(player *Player, account Account) (record SignGameRecord, err error) { - account, err = RefreshToken(account) - if err != nil { - return - } - record.GameName = "明日方舟" - record.PlayerName = player.NickName - record.PlayerUid = player.Uid - record.PlayerChannel = player.ChannelName - - gameId := signGameCodeByAppCode["arknights"] - - record.GameId = gameId - - /*list, err := listSignGame(gameId, player.Uid, account.Skland) - if err != nil { - err = fmt.Errorf("list sign game error: %w", err) - return - } - - today := list.Records.today() - if len(today) > 0 { - record.HasSigned = true - record.Award = today.shortString(list.ResourceInfoMap) - return - }*/ - - signGameData, err := signGame(gameId, player.Uid, account.Skland) - if err != nil { - if err.Error() == "[skland] response status: 403 Forbidden, error: code: 10001, message: 请勿重复签到!" { - err = nil - record.HasSigned = true - } else { - err = fmt.Errorf("sign game error: %w", err) - return - } - } else { - record.Award = signGameData.Awards.shortString() - } - - return -} - -// 签到信息 -func listSignGame(gid, uid string, skland AccountSkland) (*ListAttendanceData, error) { - req := SKR().SetQueryParams(gh.MS{"gameId": gid, "uid": uid}) - return SklandRequest[*ListAttendanceData](req, "GET", "/api/v1/game/attendance", skland) -} - -// 签到 -func signGame(gid, uid string, skland AccountSkland) (*SignGameData, error) { - req := SKR().SetBody(gh.M{"gameId": gid, "uid": uid}) - return SklandRequest[*SignGameData](req, "POST", "/api/v1/game/attendance", skland) -} - -func (v1 CalendarRecords) today() (v2 CalendarRecords) { - now := time.Now() - zero := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) - zeroTs := strconv.FormatInt(zero.Unix(), 10) - for _, r := range v1 { - if r.Ts == zeroTs { - v2 = append(v2, r) - } - } - return -} - -func (v1 CalendarRecords) shortString(m map[string]*SignGameRes) string { - v2 := make([]string, len(v1)) - for i, v := range v1 { - v2[i] = m[v.ResourceId].Name + "*" + strconv.Itoa(v.Count) - } - return strings.Join(v2, ", ") -} - -func (t SignGameAwards) shortString() string { - v := make([]string, len(t)) - for i, a := range t { - v[i] = a.Resource.Name + "*" + strconv.Itoa(a.Count) - } - return strings.Join(v, ", ") -} diff --git a/src/plugins/skland/skland_request.go b/src/plugins/skland/skland_request.go new file mode 100644 index 0000000..0c3b2e6 --- /dev/null +++ b/src/plugins/skland/skland_request.go @@ -0,0 +1,58 @@ +package skland + +import ( + "fmt" + "github.com/starudream/go-lib/resty/v2" +) + +var sklandAddr = "https://zonai.skland.com" + +type SKBaseResp[T any] struct { + Code *int `json:"code"` + Message string `json:"message"` + Data T `json:"data,omitempty"` +} + +func (t *SKBaseResp[T]) IsSuccess() bool { + return t != nil && t.Code != nil && *t.Code == 0 +} + +func (t *SKBaseResp[T]) String() string { + return fmt.Sprintf("code: %d, message: %s", *t.Code, t.Message) +} + +func SKR() *resty.Request { + return resty.R().SetHeader("User-Agent", "Skland/1.5.1 (com.hypergryph.skland; build:100501001; Android 33; ) Okhttp/4.11.0").SetHeader("Accept-Encoding", "gzip") +} + +func SklandRequest[T any](r *resty.Request, method, path string, vs ...any) (t T, _ error) { + for i := 0; i < len(vs); i++ { + switch v := vs[i].(type) { + case AccountSkland: + addSign(r, method, path, v) + } + } + + res, err := resty.ParseResp[*SKBaseResp[any], *SKBaseResp[T]]( + r.SetError(&SKBaseResp[any]{}).SetResult(&SKBaseResp[T]{}).Execute(method, sklandAddr+path), + ) + if err != nil { + return t, fmt.Errorf("[skland] %w", err) + } + return res.Data, nil +} + +func SklandRequestPlayerData(r *resty.Request, method, path string, vs ...any) (d string, _ error) { + for i := 0; i < len(vs); i++ { + switch v := vs[i].(type) { + case AccountSkland: + addSign(r, method, path, v) + } + } + + res, err := r.Execute(method, sklandAddr+path) + if err != nil { + return d, fmt.Errorf("[skland] %w", err) + } + return string(res.Body()), nil +}