qq_bot/service/scoresaber/user_info.go

202 lines
6.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package scoresaber
import (
"compress/gzip"
"encoding/json"
"fmt"
"io"
"net/http"
)
// fetchPlayerData 函数请求 Scoresaber API并解析完整的玩家信息
func FetchPlayerData(ssID string) (*PlayerData, error) {
url := fmt.Sprintf("https://scoresaber.com/api/player/%s/full", ssID)
// 创建请求
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
// 设置请求头
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0")
req.Header.Set("Accept", "application/json, text/plain, */*")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2")
req.Header.Set("Accept-Encoding", "gzip")
req.Header.Set("DNT", "1")
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Referer", fmt.Sprintf("https://scoresaber.com/u/%s", ssID))
req.Header.Set("Sec-Fetch-Dest", "empty")
req.Header.Set("Sec-Fetch-Mode", "cors")
req.Header.Set("Sec-Fetch-Site", "same-origin")
req.Header.Set("Sec-GPC", "1")
req.Header.Set("Pragma", "no-cache")
req.Header.Set("Cache-Control", "no-cache")
req.Header.Set("TE", "trailers")
// 发送请求失败则重试至多3次
client := &http.Client{}
resp, err := client.Do(req)
for i := 0; i < 3 && err != nil; i++ {
resp, err = client.Do(req)
}
if err != nil {
return nil, err
}
defer resp.Body.Close()
// 处理压缩响应
var reader io.Reader
if resp.Header.Get("Content-Encoding") == "gzip" {
reader, err = gzip.NewReader(resp.Body)
if err != nil {
return nil, err
}
defer reader.(*gzip.Reader).Close()
} else {
reader = resp.Body
}
// 解析响应体
var playerData PlayerData
err = json.NewDecoder(reader).Decode(&playerData)
if err != nil {
// log.Printf("got body %v", reader.)
return nil, err
}
return &playerData, nil
}
// {
// "players": [
// {
// "id": "76561199001767132",
// "name": "ViSi",
// "profilePicture": "https://cdn.scoresaber.com/avatars/76561199001767132.jpg",
// "bio": "<p>a girl using index cons</p>\n<p>7/1/2022 i beat ov sacrament</p>\n<p>7/28/2022 i hit 1k hours, i suppose im the worst 1k hours player atm</p>\n<p><a href=\"https://space.bilibili.com/36819692?spm_id_from=333.1007.0.0\">BiliBili</a></p>\n<p><a href=\"https://www.youtube.com/channel/UCS8Ljbehybj7G2RHV7S-kKA\">YouTube</a></p>\n<p><a href=\"https://www.twitch.tv/visi_bs\">Twitch</a></p>\n<p><a href=\"https://twitter.com/VioletSilence1\">Twitter</a></p>",
// "country": "CN",
// "pp": 15113.41,
// "rank": 120,
// "countryRank": 1,
// "role": null,
// "badges": null,
// "histories": "103,103,104,104,104,104,104,105,104,104,104,104,105,105,105,106,107,107,107,108,111,112,113,113,113,113,113,113,113,115,114,115,115,114,114,114,118,118,118,118,119,119,119,119,119,119,121,121,121",
// "permissions": 0,
// "banned": false,
// "inactive": false,
// "scoreStats": {
// "totalScore": 4525384588,
// "totalRankedScore": 1648718461,
// "averageRankedAccuracy": 96.73215,
// "totalPlayCount": 4001,
// "rankedPlayCount": 1351,
// "replaysWatched": 482
// },
// "firstSeen": "2021-04-15T06:56:45.000Z"
// },
// ...
// {
// "id": "76561198796873048",
// "name": "Jia_Yue 甩尾の嘉玥",
// "profilePicture": "https://cdn.scoresaber.com/avatars/76561198796873048.jpg",
// "bio": null,
// "country": "CN",
// "pp": 9984.614,
// "rank": 2127,
// "countryRank": 50,
// "role": null,
// "badges": null,
// "histories": "2202,2170,2176,2182,2184,2183,2183,2224,2196,2197,2192,2210,2206,2210,2197,2195,2190,2186,2187,2187,2190,2191,2187,2190,2186,2186,2184,2183,2178,2202,2167,2163,2163,2151,2150,2145,2164,2132,2135,2130,2126,2128,2124,2120,2122,2125,2125,2129,2125",
// "permissions": 0,
// "banned": false,
// "inactive": false,
// "scoreStats": {
// "totalScore": 2377399561,
// "totalRankedScore": 1304174761,
// "averageRankedAccuracy": 82.02609,
// "totalPlayCount": 3001,
// "rankedPlayCount": 1410,
// "replaysWatched": 19
// },
// "firstSeen": "2022-03-24T06:11:27.000Z"
// }
// ],
// "metadata": {
// "total": 1927,
// "page": 1,
// "itemsPerPage": 50
// }
// }
type LeaderboardData struct {
Players []PlayerData `json:"players"`
Metadata struct {
Total int `json:"total"`
Page int `json:"page"`
ItemsPerPage int `json:"itemsPerPage"`
} `json:"metadata"`
}
func FetchCountryLeaderboard(country string, offset int, ssID string) (LeaderboardData, error) {
// 根据偏移量计算页数
if offset < 1 {
offset = 1
}
url := fmt.Sprintf("https://scoresaber.com/api/players?countries=%s&page=%d", country, (offset-1)/50+1)
// 创建请求
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return LeaderboardData{}, err
}
// 设置请求头
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0")
req.Header.Set("Accept", "application/json, text/plain, */*")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2")
req.Header.Set("Accept-Encoding", "gzip")
req.Header.Set("DNT", "1")
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Referer", fmt.Sprintf("https://scoresaber.com/u/%s", ssID))
req.Header.Set("Sec-Fetch-Dest", "empty")
req.Header.Set("Sec-Fetch-Mode", "cors")
req.Header.Set("Sec-Fetch-Site", "same-origin")
req.Header.Set("Sec-GPC", "1")
req.Header.Set("Pragma", "no-cache")
req.Header.Set("Cache-Control", "no-cache")
req.Header.Set("TE", "trailers")
// 发送请求失败则重试至多3次
client := &http.Client{}
resp, err := client.Do(req)
for i := 0; i < 3 && err != nil; i++ {
resp, err = client.Do(req)
}
if err != nil {
return LeaderboardData{}, err
}
defer resp.Body.Close()
// 处理压缩响应
var reader io.Reader
if resp.Header.Get("Content-Encoding") == "gzip" {
reader, err = gzip.NewReader(resp.Body)
if err != nil {
return LeaderboardData{}, err
}
defer reader.(*gzip.Reader).Close()
} else {
reader = resp.Body
}
// 解析响应体
var leaderboardData LeaderboardData
err = json.NewDecoder(reader).Decode(&leaderboardData)
if err != nil {
return LeaderboardData{}, err
}
return leaderboardData, nil
}