package douyin import ( "bytes" "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "sync" "time" log "github.com/sirupsen/logrus" ) type IApp interface { StartRefreshToken() GetRoomId(token string) (string, string, string, error) StartTask(roomid string, msg_type string) (string, error) StopTask(roomid string, msg_type string) (string, error) } func NewApp(appid, secret string) IApp { return &App{ appid: appid, secret: secret, } } type App struct { appid string secret string accessToken string refreshTokenChan chan string refreshTokenMutex sync.Mutex } func (s *App) StartRefreshToken() { go func() { s.refreshTokenChan = make(chan string, 1) for { var err error s.accessToken, err = s.getAccessToken() if err != nil { // handle error log.Errorf("Failed to refresh access token: %v", err) } select { case m := <-s.refreshTokenChan: log.Info(m) // Timeout case <-time.After(time.Hour): // log.Info("RefreshToken after a hour") } } }() } func (s *App) StartTask(roomid string, msg_type string) (string, error) { url := "https://webcast.bytedance.com/api/live_data/task/start" data := map[string]string{ "roomid": roomid, "appid": s.appid, "msg_type": msg_type, } jsonData, err := json.Marshal(data) if err != nil { return "", err } req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) if err != nil { return "", err } req.Header.Set("access-token", s.accessToken) req.Header.Set("content-type", "application/json") client := &http.Client{} resp, err := client.Do(req) if err != nil { return "", err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return "", err } var result map[string]interface{} err = json.Unmarshal(body, &result) if err != nil { return "", err } if result["err_no"].(float64) != 0 { if int64(result["err_no"].(float64)) == 40022 { s.needRefreshToken(fmt.Sprintf("RefreshToken:StartTask%f%v", result["err_no"].(float64), result["err_msg"].(string))) } return "", errors.New(result["err_msg"].(string)) } return result["data"].(map[string]interface{})["task_id"].(string), nil } func (s *App) StopTask(roomid string, msg_type string) (string, error) { url := "https://webcast.bytedance.com/api/live_data/task/stop" data := map[string]string{ "roomid": roomid, "appid": s.appid, "msg_type": msg_type, } jsonData, err := json.Marshal(data) if err != nil { return "", err } req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) if err != nil { return "", err } req.Header.Set("access-token", s.accessToken) req.Header.Set("content-type", "application/json") client := &http.Client{} resp, err := client.Do(req) if err != nil { return "", err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return "", err } var result map[string]interface{} err = json.Unmarshal(body, &result) if err != nil { return "", err } if result["err_no"].(float64) != 0 { if int64(result["err_no"].(float64)) == 40022 { s.needRefreshToken(fmt.Sprintf("RefreshToken:StopTask%f%v", result["err_no"].(float64), result["err_msg"].(string))) } return "", errors.New(result["err_msg"].(string)) } return "", nil } func (s *App) SendGiftPostRequest(roomid string, appid string, sec_gift_id_list []string) ([]string, error) { url := "https://webcast.bytedance.com/api/gift/top_gift" data := map[string]interface{}{ "room_id": roomid, "app_id": appid, "sec_gift_id_list": sec_gift_id_list, } jsonData, err := json.Marshal(data) if err != nil { return nil, err } req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) if err != nil { return nil, err } req.Header.Set("x-token", s.accessToken) req.Header.Set("content-type", "application/json") client := &http.Client{} resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } var result map[string]interface{} err = json.Unmarshal(body, &result) if err != nil { return nil, err } if errcode, ok := result["errcode"]; ok { if errcode.(float64) != 0 { if int64(errcode.(float64)) == 40022 { s.needRefreshToken(fmt.Sprintf("RefreshToken:SendGiftPostRequest%f%v", result["errcode"].(float64), result["err_msg"].(string))) } return nil, errors.New(result["errmsg"].(string)) } } success_top_gift_id_list := result["data"].(map[string]interface{})["success_top_gift_id_list"].([]interface{}) var res []string for _, v := range success_top_gift_id_list { res = append(res, v.(string)) } return res, nil } func (s *App) GetRoomId(token string) (string, string, string, error) { url := "http://webcast.bytedance.com/api/webcastmate/info" data := map[string]string{ "token": token, } jsonData, err := json.Marshal(data) if err != nil { return "", "", "", err } req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) if err != nil { return "", "", "", err } req.Header.Set("X-Token", s.accessToken) req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(req) if err != nil { return "", "", "", err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return "", "", "", err } var result = &struct { Data struct { Info struct { RoomId string `json:"room_id"` Uid string `json:"anchor_open_id"` Nickname string `json:"nick_name"` } `json:"info"` } `json:"data"` ErrCode int64 `json:"errcode"` ErrMsg string `json:"errmsg"` }{} err = json.Unmarshal(body, result) if err != nil { return "", "", "", err } if result.ErrCode != 0 { if result.ErrCode == 40022 { s.needRefreshToken(fmt.Sprintf("RefreshToken:GetRoomId%d%s", result.ErrCode, result.ErrMsg)) } return "", "", "", errors.New(result.ErrMsg) } if result.Data.Info.RoomId == "" { return "", "", "", errors.New("no data in response") } roomId := result.Data.Info.RoomId uid := result.Data.Info.Uid nickname := result.Data.Info.Nickname return roomId, uid, nickname, nil } func (s *App) needRefreshToken(reason string) { s.refreshTokenMutex.Lock() defer s.refreshTokenMutex.Unlock() if len(s.refreshTokenChan) == 0 { s.refreshTokenChan <- reason } } // 新建一个golang func,参数appid secret grant_type 发送以json格式http请求返回asccess_token func (s *App) getAccessToken() (string, error) { url := "https://developer.toutiao.com/api/apps/v2/token" data := map[string]string{ "appid": s.appid, "secret": s.secret, "grant_type": "client_credential", } jsonData, err := json.Marshal(data) if err != nil { return "", err } resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData)) if err != nil { return "", err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return "", err } var result map[string]interface{} err = json.Unmarshal(body, &result) if err != nil { return "", err } if result["err_no"].(float64) != 0 { return "", errors.New(result["err_tips"].(string)) } s.accessToken = result["data"].(map[string]interface{})["access_token"].(string) return s.accessToken, nil } func (s *App) GetAccessToken() string { return s.accessToken }