package zego import ( "crypto/md5" "crypto/rand" "encoding/hex" "encoding/json" "fmt" "io" "net/http" "net/url" "strconv" "time" "bet24.com/log" ) var ( _appId uint32 _secret string ) func InitZego(appId uint32, secret string) { _appId = appId _secret = secret log.Release("zego.InitZego OK") } type common_response struct { Code int Message string RequestId string } type ZegoUser struct { UserId string UserName string } const ( UserStatus_In = iota // 在房间内 UserStatus_Out // 不在房间内 ) const ( UserRole_Invalid = iota UserRole_Broadcaster // 主播 UserRold_Audience // 观众 ) const ( MessageCategory_Invalid = iota // 无效 MessageCategory_Sys // 系统消息 MessageCategory_Chat // 聊天消息(一般为聊天消息) ) type ZegoUserStatus struct { UserId string Status int LoginTime int // 登录时间戳,单位ms UserRole int } // 生成签名 // Signature=md5(AppId + SignatureNonce + ServerSecret + Timestamp) func generateSignature(appId uint32, serverSecret, signatureNonce string, timeStamp int64) string { data := fmt.Sprintf("%d%s%s%d", appId, signatureNonce, serverSecret, timeStamp) h := md5.New() h.Write([]byte(data)) return hex.EncodeToString(h.Sum(nil)) } func generateNounce() string { // 生成16进制随机字符串(16位) nonce := make([]byte, 8) rand.Read(nonce) return hex.EncodeToString(nonce) } func generateAuthParams() string { timestamp := time.Now().Unix() hexNonce := generateNounce() signature := generateSignature(_appId, _secret, hexNonce, timestamp) authParams := url.Values{} authParams.Set("AppId", fmt.Sprintf("%d", _appId)) //公共参数中的随机数和生成签名的随机数要一致 authParams.Set("SignatureNonce", hexNonce) authParams.Set("SignatureVersion", "2.0") //公共参数中的时间戳和生成签名的时间戳要一致 authParams.Set("Timestamp", fmt.Sprintf("%d", timestamp)) authParams.Set("Signature", signature) return authParams.Encode() } func zegoGet(action string, params string) []byte { addr := fmt.Sprintf("https://rtc-api.zego.im?Action=%s&%s&%s", action, generateAuthParams(), params) rsp, err := http.Get(addr) if err != nil { log.Debug("zegoGet http.Get err:%+v", err) return []byte{} } defer rsp.Body.Close() body, err := io.ReadAll(rsp.Body) if err != nil { log.Debug("zegoGet io.ReadAll err:%+v", err) return []byte{} } log.Debug("zegoGet:request==> %+v response ==> %+v", addr, string(body)) return body } func GetUserNum(roomId string) int { queryParams := url.Values{} queryParams.Add("RoomId[]", roomId) ret := zegoGet("DescribeUserNum", queryParams.Encode()) type usercount struct { RoomId string UserCount int } type data struct { UserCountList []usercount } var rsp struct { common_response Data data } err := json.Unmarshal(ret, &rsp) if err != nil { log.Release("zego.GetUserNum return Unmarshal failed %s", string(ret)) return 0 } if rsp.Code != 0 { log.Release("zego.GetUserNum failed Code[%d] Message[%s]", rsp.Code, rsp.Message) return 0 } for _, v := range rsp.Data.UserCountList { if v.RoomId == roomId { return v.UserCount } } return 0 } func GetUserList(roomId string) []ZegoUser { queryParams := url.Values{} queryParams.Add("RoomId", roomId) ret := zegoGet("DescribeUserList", queryParams.Encode()) type data struct { Marker string UserList []ZegoUser } var rsp struct { common_response Data data } err := json.Unmarshal(ret, &rsp) if err != nil { log.Release("zego.GetUserList return Unmarshal failed %s", string(ret)) return nil } if rsp.Code != 0 { log.Release("zego.GetUserList failed Code[%d] Message[%s]", rsp.Code, rsp.Message) return nil } return rsp.Data.UserList } /* 调用本服务端接口时: 不建议使用与房间内实际用户相同的 UserId,避免与客户端 SDK 的流删除行为产生冲突。可以使用特定的名称标识为服务端行为,例如:userId = “Server-Administrator”。 如果使用了与房间内实际用户相同的 UserId(不建议)时,相应操作人 UserId 的客户端不会收到本服务端接口触发的流删除回调。 */ func MuteUser(roomId string, userId string, streamId string) (code int, message string) { queryParams := url.Values{} queryParams.Add("RoomId", roomId) queryParams.Add("UserId", userId) queryParams.Add("StreamId", streamId) ret := zegoGet("DeleteStream", queryParams.Encode()) var rsp common_response err := json.Unmarshal(ret, &rsp) if err != nil { log.Release("zego.MuteUser return Unmarshal failed %s", string(ret)) return } code = rsp.Code message = rsp.Message return } /* 调用本接口踢出房间内的指定用户,该用户当前的推流和拉流会全部停止。多用于内容审核场景下,开发者后台发现某个用户的直播内容违规时,可通过本接口将该用户踢出房间。 */ func KickoutUser(roomId, userId string) (code int, message string) { queryParams := url.Values{} queryParams.Add("RoomId", roomId) queryParams.Add("UserId[]", userId) ret := zegoGet("KickoutUser", queryParams.Encode()) var rsp common_response err := json.Unmarshal(ret, &rsp) if err != nil { log.Release("zego.KickoutUser return Unmarshal failed %s", string(ret)) return } code = rsp.Code message = rsp.Message return } func GetUserStatus(roomId string, userId string) *ZegoUserStatus { queryParams := url.Values{} queryParams.Add("RoomId", roomId) queryParams.Add("UserId[]", userId) ret := zegoGet("DescribeUsers", queryParams.Encode()) type data struct { UserStatusList []ZegoUserStatus } var rsp struct { common_response Data data } err := json.Unmarshal(ret, &rsp) if err != nil { log.Release("zego.GetUserStatus return Unmarshal failed %s", string(ret)) return nil } if rsp.Code != 0 { log.Release("zego.GetUserStatus failed Code[%d] Message[%s]", rsp.Code, rsp.Message) return nil } for _, v := range rsp.Data.UserStatusList { if v.UserId == userId { return &v } } return nil } // 推送广播消息接口 func SendBroadcastMessage(roomId string, userId string, messageCategory int, messageContent string) (code int, message string) { queryParams := url.Values{} queryParams.Add("RoomId", roomId) queryParams.Add("UserId", userId) queryParams.Add("MessageCategory", strconv.Itoa(messageCategory)) queryParams.Add("MessageContent", messageContent) ret := zegoGet("SendBroadcastMessage", queryParams.Encode()) var rsp common_response err := json.Unmarshal(ret, &rsp) if err != nil { log.Release("zego.SendBroadcastMessage return Unmarshal failed %s", string(ret)) return } code = rsp.Code message = rsp.Message return }