From 9b5c3913989754370bd7d03ac8cf2e32a6172afb Mon Sep 17 00:00:00 2001 From: Keuin Date: Sat, 29 Jul 2023 19:43:27 +0800 Subject: Simulate real web app cookies & WebSocket fields --- bilibili/client.go | 8 +++++++- bilibili/connect.go | 13 +++++++++++++ bilibili/danmaku_server_info.go | 43 +++++++++++++++++++++++++++++++++++++++-- bilibili/request.go | 14 ++++++++++++-- 4 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 bilibili/connect.go (limited to 'bilibili') diff --git a/bilibili/client.go b/bilibili/client.go index 4abad60..f4b64fd 100644 --- a/bilibili/client.go +++ b/bilibili/client.go @@ -10,6 +10,7 @@ import ( "github.com/keuin/slbr/types" "net" "net/http" + "net/http/cookiejar" ) const ( @@ -39,10 +40,15 @@ func NewBilibiliWithContext(ctx context.Context, netTypes []types.IpNetType, log transport.DialTLSContext = nil transport.DialContext, _ = np.NextNetworkType(dialer) + cookieJar, _ := cookiejar.New(nil) + httpClient := &http.Client{ + Jar: cookieJar, + } + return &Bilibili{ logger: logger, userAgent: userAgent, - http: http.DefaultClient, + http: httpClient, ctx: ctx, netTypes: nets, } diff --git a/bilibili/connect.go b/bilibili/connect.go new file mode 100644 index 0000000..472c979 --- /dev/null +++ b/bilibili/connect.go @@ -0,0 +1,13 @@ +package bilibili + +import ( + "context" + "nhooyr.io/websocket" +) + +func (b Bilibili) DialWebSocket(ctx context.Context, url string) (*websocket.Conn, error) { + ws, _, err := websocket.Dial(ctx, url, &websocket.DialOptions{ + HTTPClient: b.http, + }) + return ws, err +} diff --git a/bilibili/danmaku_server_info.go b/bilibili/danmaku_server_info.go index 4d26873..8c79d51 100644 --- a/bilibili/danmaku_server_info.go +++ b/bilibili/danmaku_server_info.go @@ -1,11 +1,50 @@ package bilibili import ( + "errors" "fmt" "github.com/keuin/slbr/types" + "net/url" ) +type WebBannerResponse = BaseResponse[interface{}] + +const apiUrlPrefix = "https://api.live.bilibili.com" + func (b *Bilibili) GetDanmakuServerInfo(roomId types.RoomId) (resp types.DanmakuServerInfoResponse, err error) { - url := fmt.Sprintf("https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo?id=%d&type=0", roomId) - return callGet[types.DanmakuServerInfoResponse](b, url) + u := fmt.Sprintf("https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo?id=%d&type=0", roomId) + return callGet[types.DanmakuServerInfoResponse](b, u) +} + +// GetBUVID initializes cookie `buvid3`. If success, returns its value. +func (b *Bilibili) GetBUVID() (string, error) { + const u = "https://data.bilibili.com/v/web/web_page_view" + _, _, err := callGetRaw(b, u) + if err != nil { + return "", err + } + uu, _ := url.Parse(apiUrlPrefix) + cookies := b.http.Jar.Cookies(uu) + var buvid3 *string + for _, c := range cookies { + if c.Name == "buvid3" { + buvid3 = &c.Value + } + } + if buvid3 == nil { + return "", errors.New("failed to get buvid3") + } + return *buvid3, nil +} + +// GetLiveBUVID initializes cookie `LIVE_BUVID`. This should be called before GetDanmakuServerInfo. +func (b *Bilibili) GetLiveBUVID(roomId types.RoomId) (resp WebBannerResponse, err error) { + u := fmt.Sprintf("https://api.live.bilibili.com/activity/v1/Common/webBanner?"+ + "platform=web&position=6&roomid=%d&area_v2_parent_id=0&area_v2_id=0&from=", roomId) + resp, err = callGet[WebBannerResponse](b, u) + if err == nil { + uu, _ := url.Parse(apiUrlPrefix) + b.logger.Info("Cookie info: %v", b.http.Jar.Cookies(uu)) + } + return resp, err } diff --git a/bilibili/request.go b/bilibili/request.go index 11ff995..613530c 100644 --- a/bilibili/request.go +++ b/bilibili/request.go @@ -29,8 +29,8 @@ func (b *Bilibili) newGet(url string) (req *http.Request, err error) { return b.newRequest("GET", url, strings.NewReader("")) } -// callGet make a GET request and parse response as a JSON document with given model. -func callGet[T types.BaseResponse[V], V any](b *Bilibili, url string) (resp T, err error) { +// callGetRaw make a GET request and returns the raw response body. +func callGetRaw(b *Bilibili, url string) (resp *http.Response, respBody []byte, err error) { req, err := b.newGet(url) if err != nil { b.logger.Error("Cannot create HTTP request instance on API %v: %v", url, err) @@ -56,6 +56,16 @@ func callGet[T types.BaseResponse[V], V any](b *Bilibili, url string) (resp T, e return } + return r, data, err +} + +// callGet make a GET request and parse response as a JSON document with given model. +func callGet[T BaseResponse[V], V any](b Bilibili, url string) (resp T, err error) { + r, data, err := callGetRaw(b, url) + if err != nil { + return + } + err = json.Unmarshal(data, &resp) if err != nil { b.logger.Error("Invalid JSON body of HTTP response on API %v: %v. Text: \"%v\"", -- cgit v1.2.3