175 lines
4.9 KiB
Go
175 lines
4.9 KiB
Go
package huya
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
|
|
"huya-go/internal/taf"
|
|
"huya-go/internal/wup"
|
|
)
|
|
|
|
const DefaultCDNWSEndpoint = "https://cdnws.api.huya.com/"
|
|
|
|
type HTTPClient struct {
|
|
Endpoint string
|
|
Client *http.Client
|
|
Logger func(string)
|
|
}
|
|
|
|
type CallOptions struct {
|
|
UID int64
|
|
GUID string
|
|
Cookie string
|
|
RequestID int32
|
|
Timeout time.Duration
|
|
}
|
|
|
|
func NewHTTPClient(logger func(string)) *HTTPClient {
|
|
return &HTTPClient{
|
|
Endpoint: DefaultCDNWSEndpoint,
|
|
Client: &http.Client{
|
|
Timeout: 15 * time.Second,
|
|
},
|
|
Logger: logger,
|
|
}
|
|
}
|
|
|
|
func (c *HTTPClient) logf(format string, args ...any) {
|
|
if c.Logger != nil {
|
|
c.Logger(fmt.Sprintf(format, args...))
|
|
}
|
|
}
|
|
|
|
func (c *HTTPClient) PrewarmActivity(ctx context.Context, uid int64, cookie string, sid int32) error {
|
|
return nil
|
|
}
|
|
|
|
func (c *HTTPClient) CallRPC(ctx context.Context, service, method string, reqStruct taf.Marshaler, respStruct taf.Unmarshaler, opt CallOptions) (*wup.Response, error) {
|
|
if opt.RequestID == 0 {
|
|
opt.RequestID = 1
|
|
}
|
|
reqWup := wup.NewRequest(service, method, opt.RequestID)
|
|
if err := reqWup.WriteStruct("tReq", reqStruct); err != nil {
|
|
return nil, err
|
|
}
|
|
body, err := reqWup.Encode()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
baseinfo, err := GenerateHTTPBaseinfo(opt.UID, opt.GUID, opt.Cookie)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
endpoint := c.Endpoint
|
|
if endpoint == "" {
|
|
endpoint = DefaultCDNWSEndpoint
|
|
}
|
|
url := endpoint + "?baseinfo=" + baseinfo
|
|
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
httpReq.Header.Set("User-Agent", PCUserAgent)
|
|
httpReq.Header.Set("Origin", "https://zt.huya.com")
|
|
httpReq.Header.Set("Referer", "https://zt.huya.com/")
|
|
httpReq.Header.Set("Content-Type", "application/octet-stream")
|
|
httpReq.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
|
|
|
|
client := c.Client
|
|
if client == nil {
|
|
client = http.DefaultClient
|
|
}
|
|
if opt.Timeout > 0 {
|
|
var cancel context.CancelFunc
|
|
ctx, cancel = context.WithTimeout(ctx, opt.Timeout)
|
|
defer cancel()
|
|
httpReq = httpReq.WithContext(ctx)
|
|
}
|
|
|
|
c.logf("[HTTP] POST %s.%s body=%dB", service, method, len(body))
|
|
httpResp, err := client.Do(httpReq)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer httpResp.Body.Close()
|
|
respBody, err := io.ReadAll(httpResp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(respBody) >= 2 && respBody[0] == 0x1f && respBody[1] == 0x8b {
|
|
gz, err := gzip.NewReader(bytes.NewReader(respBody))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
respBody, err = io.ReadAll(gz)
|
|
_ = gz.Close()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 {
|
|
return nil, fmt.Errorf("http status %d: %s", httpResp.StatusCode, string(respBody[:min(len(respBody), 200)]))
|
|
}
|
|
|
|
var resp wup.Response
|
|
if err := resp.Decode(respBody); err != nil {
|
|
return nil, err
|
|
}
|
|
if respStruct != nil {
|
|
ok, err := resp.ReadStruct("tRsp", respStruct)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !ok {
|
|
return nil, fmt.Errorf("%s.%s 响应缺少 tRsp/tResp", service, method)
|
|
}
|
|
}
|
|
return &resp, nil
|
|
}
|
|
|
|
func (c *HTTPClient) buildUser(uid int64, cookie, ua string) ActivityUserID {
|
|
return NewActivityUserID(uid, cookie, ua)
|
|
}
|
|
|
|
func (c *HTTPClient) QueryUserScore(ctx context.Context, uid int64, cookie string, sid int32) (*GetUserScoreResp, error) {
|
|
req := &GetUserScoreReq{User: c.buildUser(uid, cookie, ActivityWebUA), SID: sid}
|
|
var resp GetUserScoreResp
|
|
_, err := c.CallRPC(ctx, ServantWebActUI, FuncGetUserScore, req, &resp, CallOptions{UID: uid, Cookie: cookie, RequestID: 2})
|
|
return &resp, err
|
|
}
|
|
|
|
func (c *HTTPClient) GetActPrizeList(ctx context.Context, uid int64, cookie string, sid int32) (*GetActPrizeListResp, error) {
|
|
req := &GetActPrizeListReq{User: c.buildUser(uid, cookie, ActivityWebUA), SID: sid}
|
|
var resp GetActPrizeListResp
|
|
_, err := c.CallRPC(ctx, ServantWebActUI, FuncPrizeList, req, &resp, CallOptions{UID: uid, Cookie: cookie, RequestID: 1})
|
|
return &resp, err
|
|
}
|
|
|
|
func (c *HTTPClient) GetUserPrizeRecords(ctx context.Context, uid int64, cookie string, sid int32) (*GetUserPrizeRecordsResp, error) {
|
|
user := c.buildUser(uid, cookie, ActivityRecordUA)
|
|
user.GUID = CookieValue(cookie, "guid")
|
|
req := &GetUserPrizeRecordsReq{User: user, SID: sid}
|
|
var resp GetUserPrizeRecordsResp
|
|
_, err := c.CallRPC(ctx, ServantWebActUI, FuncPrizeRecords, req, &resp, CallOptions{UID: uid, Cookie: cookie, RequestID: 6})
|
|
return &resp, err
|
|
}
|
|
|
|
func (c *HTTPClient) ScoreExchangePrize(ctx context.Context, uid int64, cookie string, sid, pid int32) (*ScoreExchangePrizeResp, error) {
|
|
req := &ScoreExchangePrizeReq{
|
|
User: c.buildUser(uid, cookie, ActivityWebUA),
|
|
SID: sid,
|
|
PID: pid,
|
|
ClientEnv: map[string]string{},
|
|
IsRole: 1,
|
|
}
|
|
var resp ScoreExchangePrizeResp
|
|
_, err := c.CallRPC(ctx, ServantWebActUI, FuncExchangePrize, req, &resp, CallOptions{UID: uid, Cookie: cookie, RequestID: 1})
|
|
return &resp, err
|
|
}
|