You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

291 lines
7.3 KiB
Go

2 years ago
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
}