commit 215855af08447b16b4fbe0c0ea88540d4e105596 Author: tursom <tursom@foxmail.com> Date: Wed Nov 23 20:30:16 2022 +0800 create project diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..087c9d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# idea +.idea/ + +# python venv +venv/ + +# test cookie file +live/Room_test.cookie.txt + +vendor/ diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..264acdc --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module libbili + +go 1.19 + +require ( + github.com/go-resty/resty/v2 v2.7.0 + github.com/tursom/GoCollections v0.1.4 +) + +require ( + github.com/timandy/routine v1.0.5 // indirect + golang.org/x/net v0.2.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c896f0a --- /dev/null +++ b/go.sum @@ -0,0 +1,18 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= +github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/timandy/routine v1.0.5 h1:JBVo2mOZlYVsod/v1jjmjSzxTH84x3vS9b6k4ZBTvdc= +github.com/timandy/routine v1.0.5/go.mod h1:BT7/+zFFZkugUiUUClwDgZjQ/PNcivY1hqRLXQAftGU= +github.com/tursom/GoCollections v0.1.4 h1:NV3vK2jmkzseEgpcKv716DlUEl8BzCyk3wM1njr0Cp8= +github.com/tursom/GoCollections v0.1.4/go.mod h1:kwFG//g3vbmDShsnY8Vm47PsTUBWgk83RcEPYZLgmZU= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= diff --git a/live/Room.go b/live/Room.go new file mode 100644 index 0000000..13430e7 --- /dev/null +++ b/live/Room.go @@ -0,0 +1,252 @@ +package live + +import ( + "bytes" + "encoding/json" + "fmt" + "mime/multipart" + "strconv" + "time" + + "github.com/go-resty/resty/v2" + "github.com/tursom/GoCollections/exceptions" +) + +type ( + Room interface { + SetCookie(cookie string) + ID() uint32 + Send(msg string) (*DanmuResp, exceptions.Exception) + SendDanmu(danmu *Danmu) (*DanmuResp, exceptions.Exception) + GetDanmuColors() (*DanmuColors, exceptions.Exception) + } + + roomImpl struct { + id uint32 + cookie string + } + + Danmu struct { + Bubble int32 `json:"bubble,omitempty"` + Msg string `json:"msg,omitempty"` + Color string `json:"color,omitempty"` + Mode int32 `json:"mode,omitempty"` + Fontsize int32 `json:"fontsize,omitempty"` + Rnd int64 `json:"rnd,omitempty"` + RoomId uint32 `json:"roomid,omitempty"` + Csrf string `json:"csrf,omitempty"` + CsrfToken string `json:"csrf_token,omitempty"` + } + + DanmuResp struct { + Code int `json:"code"` + Data struct { + ModeInfo struct { + Mode int `json:"mode"` + ShowPlayerType int `json:"show_player_type"` + Extra string `json:"extra"` + } `json:"mode_info"` + } `json:"data"` + Message string `json:"message"` + Msg string `json:"msg"` + } + + DanmuColors struct { + Code int `json:"code"` + Data struct { + Group []struct { + Name string `json:"name"` + Sort int `json:"sort"` + Color []struct { + Name string `json:"name"` + Color string `json:"color"` + ColorHex string `json:"color_hex"` + Status int `json:"status"` + Weight int `json:"weight"` + ColorId int `json:"color_id"` + Origin int `json:"origin"` + } `json:"color"` + } `json:"group"` + Mode []struct { + Name string `json:"name"` + Mode int `json:"mode"` + Type string `json:"type"` + Status int `json:"status"` + } `json:"mode"` + } `json:"data"` + Message string `json:"message"` + Msg string `json:"msg"` + } +) + +var ( + client = resty.New() +) + +func NewRoom(id uint32) Room { + return &roomImpl{ + id: id, + } +} + +func (r *roomImpl) SetCookie(cookie string) { + r.cookie = cookie +} + +func (r *roomImpl) ID() uint32 { + return r.id +} + +func (r *roomImpl) Send(msg string) (*DanmuResp, exceptions.Exception) { + if r.cookie == "" { + // return err that no cookie set + return nil, exceptions.NewIllegalParameterException("cookie not set", nil) + } + + return r.SendDanmu(&Danmu{ + Bubble: 0, + Msg: msg, + Color: "16777215", + Mode: 1, + Fontsize: 25, + Rnd: time.Now().Unix(), + RoomId: r.id, + Csrf: "c1b21617a15daf838f505271ff8f5204", + CsrfToken: "c1b21617a15daf838f505271ff8f5204", + }) +} + +func (r *roomImpl) SendDanmu(danmu *Danmu) (*DanmuResp, exceptions.Exception) { + if r.cookie == "" { + // return err that no cookie set + return nil, exceptions.NewIllegalParameterException("cookie not set", nil) + } + + request := client.R() + + form, boundary, exception := multipartForm(danmu) + if exception != nil { + return nil, exception + } + + request.SetBody(form) + + //request, err := http.NewRequest("POST", "https://api.live.bilibili.com/msg/send", form) + //if err != nil { + // return nil, exceptions.Package(err) + //} + + //request.Header.Add("Accept", "*/*") + request.Header.Set("Cookie", r.cookie) + //request.Header.Set("Origin", "https://live.bilibili.com") + //request.Header.Set("Referer", fmt.Sprintf("https://li|ve.bilibili.com/%d?spm_id_from=444.41.live_users.item.click", r.id)) + //request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36") + request.Header.Set("Content-Type", fmt.Sprintf("multipart/form-data; boundary=%s", boundary)) + //request.Header.Set("Sec-Ch-Ua", "\"Google Chrome\";v=\"107\", \"Chromium\";v=\"107\", \"Not=A?Brand\";v=\"24\"") + //request.Header.Set("Sec-Ch-Ua-Mobile", "?0") + //request.Header.Set("Sec-Ch-Ua-Platform", "\"Windows\"") + //request.Header.Set("Sec-Fetch-Dest", "empty") + //request.Header.Set("Sec-Fetch-Mode", "cors") + //request.Header.Set("Sec-Fetch-Site", "same-site") + //request.Header.Set("content-length", strconv.Itoa(len(form))) + + //do, err := http.DefaultClient.Do(request) + do, err := request.Post("https://api.live.bilibili.com/msg/send") + if do.StatusCode() != 200 { + // return err + fmt.Println(string(do.Body())) + return nil, exceptions.NewPackageException(fmt.Sprintf("send response status failed: %d", do.StatusCode()), + exceptions.Cfg().SetCause(do.StatusCode)) + } + + var resp DanmuResp + err = json.Unmarshal(do.Body(), &resp) + if err != nil { + return nil, exceptions.Package(err) + } + + return &resp, nil +} + +func multipartForm(danmu *Danmu) (formData []byte, boundary string, exception exceptions.Exception) { + formBuffer := bytes.NewBuffer(nil) + formWriter := multipart.NewWriter(formBuffer) + + err := formWriter.WriteField("bubble", strconv.Itoa(int(danmu.Bubble))) + if err != nil { + return nil, "", exceptions.Package(err) + } + + err = formWriter.WriteField("msg", danmu.Msg) + if err != nil { + return nil, "", exceptions.Package(err) + } + + err = formWriter.WriteField("color", danmu.Color) + if err != nil { + return nil, "", exceptions.Package(err) + } + + err = formWriter.WriteField("mode", strconv.Itoa(int(danmu.Mode))) + if err != nil { + return nil, "", exceptions.Package(err) + } + + err = formWriter.WriteField("fontsize", strconv.Itoa(int(danmu.Fontsize))) + if err != nil { + return nil, "", exceptions.Package(err) + } + + err = formWriter.WriteField("rnd", strconv.FormatInt(danmu.Rnd, 10)) + if err != nil { + return nil, "", exceptions.Package(err) + } + + err = formWriter.WriteField("roomid", strconv.Itoa(int(danmu.RoomId))) + if err != nil { + return nil, "", exceptions.Package(err) + } + + err = formWriter.WriteField("csrf", danmu.Csrf) + if err != nil { + return nil, "", exceptions.Package(err) + } + + err = formWriter.WriteField("csrf_token", danmu.CsrfToken) + if err != nil { + return nil, "", exceptions.Package(err) + } + + err = formWriter.Close() + if err != nil { + return nil, "", exceptions.Package(err) + } + + formBytes := formBuffer.Bytes() + fmt.Println(string(formBytes)) + + return formBytes, formWriter.Boundary(), nil +} + +func (r *roomImpl) GetDanmuColors() (*DanmuColors, exceptions.Exception) { + url := fmt.Sprintf("https://api.live.bilibili.com/xlive/web-room/v1/dM/GetDMConfigByGroup?room_id=%d", r.id) + + request := client.R() + + if r.cookie != "" { + request.Header.Set("Cookie", r.cookie) + } + + response, err := request.Get(url) + if err != nil { + return nil, exceptions.Package(err) + } + + var colors DanmuColors + err = json.Unmarshal(response.Body(), &colors) + if err != nil { + return nil, exceptions.Package(err) + } + + return &colors, nil +} diff --git a/live/Room.py b/live/Room.py new file mode 100644 index 0000000..27ce3ca --- /dev/null +++ b/live/Room.py @@ -0,0 +1,66 @@ +import http.client +import logging +import time + +import httpx + +logging.basicConfig() +logging.getLogger().setLevel(logging.DEBUG) + +http.client.HTTPConnection.debuglevel = 1 + +# session.proxies = { +# 'http': 'http://127.0.0.1:2080', +# 'https': 'http://127.0.0.1:2080', +# } + +id = 917818 + +with open("Room_test.cookie.txt") as cookie_txt: + cookie = cookie_txt.read() + +session = httpx.Client(http2=True) + +resp = session.post( + "https://api.live.bilibili.com/msg/send", + # files=( + # ("bubble", (None, "0")), + # ("msg", (None, "弹幕测试")), + # ("color", (None, "16777215")), + # ("mode", (None, "1")), + # ("fontsize", (None, "25")), + # ("rnd", (None, str(int(time.time())))), + # ("roomid", (None, str(id))), + # ("csrf", (None, "c1b21617a15daf838f505271ff8f5204")), + # ("csrf_token", (None, "c1b21617a15daf838f505271ff8f5204")), + # ), + data={ + "bubble", "0", + "msg", "弹幕测试", + "color", "16777215", + "mode", "1", + "fontsize", "25", + "rnd", str(int(time.time())), + "roomid", str(id), + "csrf", "c1b21617a15daf838f505271ff8f5204", + "csrf_token", "c1b21617a15daf838f505271ff8f5204", + }, + headers={ + "Accept": "*/*", + "Cookie": cookie, + # "Origin": "https://live.bilibili.com", + # "Referer": f"https://li|ve.bilibili.com/{id}?spm_id_from=444.41.live_users.item.click", + # "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36", + # "Sec-Ch-Ua": "\"Google Chrome\";v=\"107\", \"Chromium\";v=\"107\", \"Not=A?Brand\";v=\"24\"", + # "Sec-Ch-Ua-Mobile": "?0", + # "Sec-Ch-Ua-Platform": "\"Windows\"", + # "Sec-Fetch-Dest": "empty", + # "Sec-Fetch-Mode": "cors", + # "Sec-Fetch-Site": "same-site", + }, + # cookies={ + # "Cookie": cookie, + # }, +) + +print(resp) diff --git a/live/Room_test.go b/live/Room_test.go new file mode 100644 index 0000000..305de1c --- /dev/null +++ b/live/Room_test.go @@ -0,0 +1,50 @@ +package live + +import ( + "encoding/json" + "os" + "testing" + + "github.com/tursom/GoCollections/exceptions" +) + +func getRoomImpl() (Room, exceptions.Exception) { + cookieBytes, err := os.ReadFile("Room_test.cookie.txt") + if err != nil { + return nil, exceptions.NewPackageException("read cookie file failed", exceptions.Cfg().SetCause(err)) + } + + room := NewRoom(917818) + room.SetCookie(string(cookieBytes)) + + return room, nil +} + +func Test_roomImpl_Send(t *testing.T) { + room, err := getRoomImpl() + if err != nil { + t.Fatalf("get room impl failed: %s", err) + } + + resp, err := room.Send("弹幕测试") + if err != nil { + t.Fatalf("test send danmu failed: %s", exceptions.GetStackTraceString(err.(exceptions.Exception))) + } + + t.Logf("send danmu resp: %v", resp) +} + +func Test_roomImpl_GetDanmuColors(t *testing.T) { + room, err := getRoomImpl() + if err != nil { + t.Fatalf("get room impl failed: %s", err) + } + + colors, exception := room.GetDanmuColors() + if exception != nil { + t.Fatalf("get danmu colors failed: %s", exceptions.GetStackTraceString(exception)) + } + + marshal, _ := json.Marshal(colors) + t.Logf("get colors: %s", string(marshal)) +}