|  |  |  |  | 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 | 
					
						
							|  |  |  |  | } |