package Netease import ( "context" "encoding/json" "fmt" "strings" "gitea.ddegame.cn/open/servicebase/pkg/common" "gitea.ddegame.cn/open/servicebase/pkg/common/HyTools" yunxin "gitea.ddegame.cn/open/servicebase/pkg/common/netease/dto" "gitea.ddegame.cn/open/servicebase/pkg/htools" "gitea.ddegame.cn/open/servicebase/pkg/log" "github.com/pkg/errors" "github.com/spf13/cast" "math/rand" "strconv" "time" "github.com/anxpp/beego/logs" "github.com/spf13/viper" ) type ImClient struct { AppKey string AppSecret string } type CreateImRes struct { Code int Desc string } type BroadcastNotificationReq struct { FromAccountId string `json:"from_account_id"` Content string `json:"content"` TargetOs []string `json:"target_os"` } func NewImClient() *ImClient { fmt.Printf("NewImClient: %s\n", viper.GetString("netease.im.key")) key := viper.GetString("netease.im.key") secret := viper.GetString("netease.im.secret") return &ImClient{AppKey: key, AppSecret: secret} } // 生成云信header func (client *ImClient) generateHeader() map[string]string { nonce := randNumber() t := time.Now() currentTime := strconv.FormatInt(t.UTC().Unix(), 10) result := make(map[string]string) result["AppKey"] = client.AppKey result["Nonce"] = nonce result["CurTime"] = currentTime result["CheckSum"] = client.generateCheckSum(nonce, currentTime) result["Content-Type"] = "application/x-www-form-urlencoded;charset=utf-8" return result } func (client *ImClient) generateJsonHeader() map[string]string { nonce := randNumber() t := time.Now() currentTime := strconv.FormatInt(t.UTC().Unix(), 10) result := make(map[string]string) result["AppKey"] = client.AppKey result["Nonce"] = nonce result["CurTime"] = currentTime result["CheckSum"] = client.generateCheckSum(nonce, currentTime) result["Content-Type"] = "application/json" return result } // 生成云信签名 func (client *ImClient) generateCheckSum(nonce, currentTime string) string { waitSignStr := client.AppSecret + nonce + currentTime result := HyTools.StringToSHA1(waitSignStr) return result } // 创建云信账户 // imService.CreateImUser(common.SYNC_DATA_USER_ID,"数据同步",common.PHOTO_DOMAIN_URL+"assets/sys/guanfanggonggao.png") func (client *ImClient) CreateImUser(userId, nickName, avatar string) error { url := "https://api.netease.im/nimserver/user/create.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := HyTools.NewStringBuilder() sb.Append("accid=" + userId) sb.Append("&") sb.Append("token=123456") sb.Append("&") sb.Append("name=" + nickName) sb.Append("&") sb.Append("icon=" + avatar) res, err := HyTools.HttpDo(httpMethod, url, header, sb.ToString()) if err != nil { // {"desc":"already register","code":414} logs.Info("YunXin_SignUp_Fail_AccID_%s:"+err.Error(), userId) return err } else { var resDTO CreateImRes json.Unmarshal([]byte(res), &resDTO) if resDTO.Code != 200 { logs.Info("YunXin_SignUp_Fail_AccID_%s:"+resDTO.Desc, userId) } else { logs.Info("YunXin_SignUp_Success_AccID_%s:"+res, userId) } } var body map[string]any if errJson := json.Unmarshal([]byte(res), &body); errJson != nil { return errJson } if int(body["code"].(float64)) != 200 { return errors.New(body["desc"].(string)) } return err } // 查询用户信息 func (client *ImClient) GetImUserInfo(userId string) error { url := "https://api.netease.im/nimserver/user/getUinfos.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := HyTools.NewStringBuilder() accids := []string{userId} byteData, _ := json.Marshal(accids) sb.Append("accids=" + string(byteData)) res, err := HyTools.HttpDo(httpMethod, url, header, sb.ToString()) logs.Info("#" + "查询云信用户信息结果:" + res + "#") var body map[string]any if errJson := json.Unmarshal([]byte(res), &body); errJson != nil { return errJson } if int(body["code"].(float64)) != 200 { return errors.New(body["desc"].(string)) } return err } // 更新云信信息 func (client *ImClient) UpdateImUserInfo(accid, nickName, avatar string) error { url := "https://api.netease.im/nimserver/user/updateUinfo.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := htools.NewStringBuilder() sb.Append("accid=" + accid) sb.Append("&") sb.Append("name=" + nickName) sb.Append("&") sb.Append("icon=" + avatar) res, err := htools.HttpDo(httpMethod, url, header, sb.ToString()) logs.Info(common.LOG_QUOTE_STRING + "云信更新用户信息结果:" + res + common.LOG_QUOTE_STRING) return err } // 更新云信用户扩展信息 func (client *ImClient) UpdateImUserExt(accid, ext string) error { url := "https://api.netease.im/nimserver/user/updateUinfo.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := htools.NewStringBuilder() sb.Append("accid=" + accid) sb.Append("&") sb.Append("ex=" + ext) logs.Info("update_im_user userID[%s] ext:%s", accid, ext) res, err := htools.HttpDo(httpMethod, url, header, sb.ToString()) logs.Info(common.LOG_QUOTE_STRING+"云信更新用户ext%s信息结果:"+res+common.LOG_QUOTE_STRING, ext) return err } // 封禁云信帐号 func (client *ImClient) BlockImUser(accid string) error { url := "https://api.netease.im/nimserver/user/block.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := htools.NewStringBuilder() sb.Append("accid=" + accid) sb.Append("&") sb.Append("needkick=true") res, err := htools.HttpDo(httpMethod, url, header, sb.ToString()) logs.Info(common.LOG_QUOTE_STRING + "封禁云信帐号结果:" + res + common.LOG_QUOTE_STRING) return err } // 解禁云信帐号 func (client *ImClient) UnBlockImUser(accid string) error { url := "https://api.netease.im/nimserver/user/unblock.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := htools.NewStringBuilder() sb.Append("accid=" + accid) res, err := htools.HttpDo(httpMethod, url, header, sb.ToString()) logs.Info(common.LOG_QUOTE_STRING + "解禁云信帐号结果:" + res + common.LOG_QUOTE_STRING) return err } // 创建云信聊天室 func (client *ImClient) CreateChatroom(ownerUserId string, roomName string) (chatroomModel NeteaseChatroomDTO) { logs.Info("CreateChatroom ownerUserId=%s roomName=%s", ownerUserId, roomName) url := "https://api.netease.im/nimserver/chatroom/create.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := htools.NewStringBuilder() sb.Append("creator=" + ownerUserId) sb.Append("&") sb.Append("name=" + roomName) res, err := htools.HttpDo(httpMethod, url, header, sb.ToString()) if err != nil { logs.Info(common.LOG_QUOTE_STRING + "云信创建聊天室请求失败:" + err.Error() + common.LOG_QUOTE_STRING) return } logs.Info(common.LOG_QUOTE_STRING + "云信创建聊天室结果:" + res + common.LOG_QUOTE_STRING) var createRes CreateChatroomResponse err1 := json.Unmarshal([]byte(res), &createRes) if err1 != nil { logs.Info(common.LOG_QUOTE_STRING + "创建聊天室失败,解析结果错误:" + res + common.LOG_QUOTE_STRING) panic(err1) } if createRes.Code != 200 { logs.Info(common.LOG_QUOTE_STRING + "创建Im聊天室失败:code= " + strconv.Itoa(createRes.Code) + common.LOG_QUOTE_STRING) panic(errors.New("创建云信Im聊天室失败")) } chatroomModel = createRes.Chatroom return } // IM发送消息 func (client *ImClient) SendMsg(parameters map[string]string) error { url := "https://api.netease.im/nimserver/msg/sendMsg.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := htools.NewStringBuilder() parametersCount := len(parameters) i := 0 for k, v := range parameters { sb.Append(k + "=" + v) if i < parametersCount-1 { sb.Append("&") } i++ } logs.Info(common.LOG_QUOTE_STRING + "云信IM发消息请求:" + sb.ToString() + common.LOG_QUOTE_STRING) res, err := htools.HttpDo(httpMethod, url, header, sb.ToString()) logs.Info(common.LOG_QUOTE_STRING + "云信IM发消息结果:" + res + common.LOG_QUOTE_STRING) return err } // 添加机器人 func (client *ImClient) AddRobot(parameters map[string]string) error { url := "https://api.netease.im/nimserver/chatroom/addRobot.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := htools.NewStringBuilder() parametersCount := len(parameters) i := 0 for k, v := range parameters { sb.Append(k + "=" + v) if i < parametersCount-1 { sb.Append("&") } i++ } logs.Info(common.LOG_QUOTE_STRING + "添加机器人:" + sb.ToString() + common.LOG_QUOTE_STRING) res, err := htools.HttpDo(httpMethod, url, header, sb.ToString()) logs.Info(common.LOG_QUOTE_STRING + "添加机器人:" + res + common.LOG_QUOTE_STRING) return err } // 移除机器人 func (client *ImClient) RemoveRobot(parameters map[string]string) error { url := "https://api.netease.im/nimserver/chatroom/removeRobot.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := htools.NewStringBuilder() parametersCount := len(parameters) i := 0 for k, v := range parameters { sb.Append(k + "=" + v) if i < parametersCount-1 { sb.Append("&") } i++ } logs.Info(common.LOG_QUOTE_STRING + "移除机器人:" + sb.ToString() + common.LOG_QUOTE_STRING) res, err := htools.HttpDo(httpMethod, url, header, sb.ToString()) logs.Info(common.LOG_QUOTE_STRING + "移除机器人:" + res + common.LOG_QUOTE_STRING) return err } // 发送聊天室消息 func (client *ImClient) SendChatroomMsg(parameters map[string]string) error { url := "https://api.netease.im/nimserver/chatroom/sendMsg.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := htools.NewStringBuilder() parametersCount := len(parameters) i := 0 for k, v := range parameters { sb.Append(k + "=" + v) if i < parametersCount-1 { sb.Append("&") } i++ } logs.Info(common.LOG_QUOTE_STRING + "聊天室发消息请求:" + sb.ToString() + common.LOG_QUOTE_STRING) res, err := htools.HttpDo(httpMethod, url, header, sb.ToString()) logs.Info(common.LOG_QUOTE_STRING + "聊天室发消息结果:" + res + common.LOG_QUOTE_STRING) return err } // 获取在线成员信息 func (client *ImClient) GetChatroomMembers(parameters map[string]string) (imResponse ImResponse, err error) { url := "https://api.netease.im/nimserver/chatroom/queryMembers.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() body := generateBodyData(parameters) logs.Info(common.LOG_QUOTE_STRING + "获取在线成员请求:" + body + common.LOG_QUOTE_STRING) res, err1 := htools.HttpDo(httpMethod, url, header, body) if err1 != nil { logs.Error(common.LOG_QUOTE_STRING + "获取在线成员失败:" + err1.Error() + common.LOG_QUOTE_STRING) err = err1 return } logs.Info(common.LOG_QUOTE_STRING + "获取在线成员结果:" + res + common.LOG_QUOTE_STRING) var response ImResponse if err2 := json.Unmarshal([]byte(res), &response); err2 != nil { logs.Error(common.LOG_QUOTE_STRING + "获取在线成员解析失败:" + err2.Error() + common.LOG_QUOTE_STRING) err = err2 return } imResponse = response return } // 分页获取聊天室在线用户列表 func (client *ImClient) GetChatroomOnlineUserListByPage(parameters map[string]string) (imResponse ImResponse, err error) { url := "https://api.netease.im/nimserver/chatroom/membersByPage.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() body := generateBodyData(parameters) logs.Info(common.LOG_QUOTE_STRING + "分页获取聊天室在线列表:" + body + common.LOG_QUOTE_STRING) res, err1 := htools.HttpDo(httpMethod, url, header, body) if err1 != nil { logs.Error(common.LOG_QUOTE_STRING + "分页获取聊天室在线列表失败:" + err1.Error() + common.LOG_QUOTE_STRING) err = err1 return } logs.Info(common.LOG_QUOTE_STRING + "分页获取聊天室在线列表结果:" + res + common.LOG_QUOTE_STRING) var response ImResponse if err2 := json.Unmarshal([]byte(res), &response); err2 != nil { logs.Error(common.LOG_QUOTE_STRING + "分页获取聊天室在线列表失败:" + err2.Error() + common.LOG_QUOTE_STRING) err = err2 return } imResponse = response return } // 更新聊天室房间信息 func (client *ImClient) UpdateRoomInfo(request yunxin.UpdateRoomInfoRequest) (err error) { url := "https://api.netease.im/nimserver/chatroom/update.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := htools.NewStringBuilder() sb.Append("roomid=" + request.Roomid) sb.Append("&") sb.Append("name=") sb.Append("&") sb.Append("announcement=") sb.Append("&") sb.Append("ext=") sb.Append("&") sb.Append("notifyExt=" + request.NotifyExt) logs.Info(common.LOG_QUOTE_STRING + "更新聊天室信息请求:" + sb.ToString() + common.LOG_QUOTE_STRING) res, httpErr := htools.HttpDo(httpMethod, url, header, sb.ToString()) logs.Info(common.LOG_QUOTE_STRING + "更新聊天室信息结果:" + res + common.LOG_QUOTE_STRING) if httpErr != nil { logs.Info(common.LOG_QUOTE_STRING + "更新聊天室信息失败:" + httpErr.Error() + common.LOG_QUOTE_STRING) err = httpErr return } resMap := make(map[string]any) jsonErr := json.Unmarshal([]byte(res), &resMap) if jsonErr != nil { logs.Info(common.LOG_QUOTE_STRING + "解析更新房间信息结果失败:" + jsonErr.Error() + common.LOG_QUOTE_STRING) err = jsonErr return } code := resMap["code"].(float64) codeStr := htools.Float64ToString(code) if codeStr != "200" { logs.Info(common.LOG_QUOTE_STRING + "更新房间信息结果失败: code=" + codeStr + common.LOG_QUOTE_STRING) err = errors.New("更新失败 code=" + codeStr) return } return } // 聊天室临时禁言 func (client *ImClient) ChatroomMuteUser(parameters map[string]string) error { url := "https://api.netease.im/nimserver/chatroom/temporaryMute.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := htools.NewStringBuilder() parametersCount := len(parameters) i := 0 for k, v := range parameters { sb.Append(k + "=" + v) if i < parametersCount-1 { sb.Append("&") } i++ } logs.Info(common.LOG_QUOTE_STRING + "聊天室临时禁言请求:" + sb.ToString() + common.LOG_QUOTE_STRING) res, err := htools.HttpDo(httpMethod, url, header, sb.ToString()) logs.Info(common.LOG_QUOTE_STRING + "聊天室临时禁言结果:" + res + common.LOG_QUOTE_STRING) return err } // 切换聊天室状态 func (client *ImClient) ChangeChatroomStat(parameters map[string]string) error { url := "https://api.netease.im/nimserver/chatroom/toggleCloseStat.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := htools.NewStringBuilder() parametersCount := len(parameters) i := 0 for k, v := range parameters { sb.Append(k + "=" + v) if i < parametersCount-1 { sb.Append("&") } i++ } requestId := HyTools.GetUUID() logs.Info(common.LOG_QUOTE_STRING + "聊天室切换聊天室开关状态请求:requestId " + requestId + " body " + sb.ToString() + common.LOG_QUOTE_STRING) res, err := htools.HttpDo(httpMethod, url, header, sb.ToString()) if len(res) == 0 { logs.Info(common.LOG_QUOTE_STRING + "聊天室切换聊天室开关状态结果:失败 res为空 requestId " + requestId + common.LOG_QUOTE_STRING) return errors.New("操作失败") } if err != nil { logs.Info(common.LOG_QUOTE_STRING + "聊天室切换聊天室开关状态结果:失败 返回error requestId " + requestId + "error " + err.Error() + common.LOG_QUOTE_STRING) return err } var resData struct { Code int `json:"code"` Desc string `json:"desc"` } _ = json.Unmarshal([]byte(res), &resData) if resData.Code != 200 { if resData.Code != 417 { // 重复操作 直接忽略 logs.Info(fmt.Sprintf(common.LOG_QUOTE_STRING+"聊天室切换聊天室开关状态结果:失败 code[%d] requestId "+requestId+" desc[%s] "+common.LOG_QUOTE_STRING, resData.Code, resData.Desc)) return errors.New("操作失败") } } logs.Info(common.LOG_QUOTE_STRING + "聊天室切换聊天室开关状态结果: requestId " + requestId + " response " + res + common.LOG_QUOTE_STRING) return err } // 设置聊天室成员角色 func (client *ImClient) SetChatroomMemberRole(parameters map[string]string) error { url := "https://api.netease.im/nimserver/chatroom/setMemberRole.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := htools.NewStringBuilder() parametersCount := len(parameters) i := 0 for k, v := range parameters { sb.Append(k + "=" + v) if i < parametersCount-1 { sb.Append("&") } i++ } logs.Info(common.LOG_QUOTE_STRING + "设置聊天室成员角色请求:" + sb.ToString() + common.LOG_QUOTE_STRING) res, err := htools.HttpDo(httpMethod, url, header, sb.ToString()) logs.Info(common.LOG_QUOTE_STRING + "设置聊天室成员角色结果:" + res + common.LOG_QUOTE_STRING) return err } // 发送广播消息 func (client *ImClient) SendBroadCastMsg(parameters map[string]string) error { url := "https://api.netease.im/nimserver/msg/broadcastMsg.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := htools.NewStringBuilder() parametersCount := len(parameters) i := 0 for k, v := range parameters { sb.Append(k + "=" + v) if i < parametersCount-1 { sb.Append("&") } i++ } logs.Info(common.LOG_QUOTE_STRING + "发送广播请求:" + sb.ToString() + common.LOG_QUOTE_STRING) res, err := htools.HttpDo(httpMethod, url, header, sb.ToString()) logs.Info(common.LOG_QUOTE_STRING + "发送广播结果:" + res + common.LOG_QUOTE_STRING) return err } // 发送全员广播 V2 func (client *ImClient) SendBroadCastMsgV2(req BroadcastNotificationReq) error { url := "https://open.yunxinapi.com/im/v2/broadcast_notification" httpMethod := common.HTTP_METHOD_POST header := client.generateJsonHeader() bodyStr := HyTools.JsonStr(req) logs.Info(common.LOG_QUOTE_STRING + "发送广播请求V2:" + bodyStr + common.LOG_QUOTE_STRING) res, err := htools.HttpDo(httpMethod, url, header, bodyStr) logs.Info(common.LOG_QUOTE_STRING + "发送广播结果V2:" + res + common.LOG_QUOTE_STRING) return err } // 踢出用户登录 V2 https://{endpoint}/im/v2/accounts/{account_id}/actions/kick func (client *ImClient) KickOutUserV2(userId string) error { url := "https://open.yunxinapi.com/im/v2/accounts/" + userId + "/actions/kick" httpMethod := common.HTTP_METHOD_POST header := client.generateJsonHeader() req := make(map[string]any, 3) req["type"] = 1 req["device_id_list"] = []string{} req["kick_notify_extension"] = "" bodyStr := HyTools.JsonStr(req) logs.Info(common.LOG_QUOTE_STRING + "发送踢下线请求V2:" + bodyStr + common.LOG_QUOTE_STRING) res, err := htools.HttpDo(httpMethod, url, header, bodyStr) logs.Info(common.LOG_QUOTE_STRING + "发送踢下线结果V2:" + res + common.LOG_QUOTE_STRING) return err } // 设置/取消 拉黑 静音 func (client *ImClient) SetSpecialRelation(parameters map[string]string) error { url := "https://api.netease.im/nimserver/user/setSpecialRelation.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := htools.NewStringBuilder() parametersCount := len(parameters) i := 0 for k, v := range parameters { sb.Append(k + "=" + v) if i < parametersCount-1 { sb.Append("&") } i++ } logs.Info(common.LOG_QUOTE_STRING + "设置/取消 拉黑请求:" + sb.ToString() + common.LOG_QUOTE_STRING) res, err := htools.HttpDo(httpMethod, url, header, sb.ToString()) logs.Info(common.LOG_QUOTE_STRING + "设置/取消 拉黑结果:" + res + common.LOG_QUOTE_STRING) return err } // 发送自定义系统通知 func (client *ImClient) BatchSendMsg(parameters map[string]string) error { url := "https://api.netease.im/nimserver/msg/sendBatchMsg.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := htools.NewStringBuilder() parametersCount := len(parameters) i := 0 for k, v := range parameters { sb.Append(k + "=" + v) if i < parametersCount-1 { sb.Append("&") } i++ } logs.Info(common.LOG_QUOTE_STRING + "云信IM批量发消息请求:" + sb.ToString() + common.LOG_QUOTE_STRING) res, err := htools.HttpDo(httpMethod, url, header, sb.ToString()) logs.Info(common.LOG_QUOTE_STRING + "云信IM批量发消息结果:" + res + common.LOG_QUOTE_STRING) return err } // 批量发送消息 func (client *ImClient) SendAttachMsg(parameters map[string]string) error { url := "https://api.netease.im/nimserver/msg/sendAttachMsg.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := htools.NewStringBuilder() parametersCount := len(parameters) i := 0 for k, v := range parameters { sb.Append(k + "=" + v) if i < parametersCount-1 { sb.Append("&") } i++ } logs.Info(common.LOG_QUOTE_STRING + "云信IM发系统通知请求:" + sb.ToString() + common.LOG_QUOTE_STRING) res, err := htools.HttpDo(httpMethod, url, header, sb.ToString()) logs.Info(common.LOG_QUOTE_STRING + "云信IM发系统通知结果:" + res + common.LOG_QUOTE_STRING) if parameters["to"] == "b260b07ff3d1411f926e2d767cb9d6f3" { log.Info("完成给老板发接单消息") } return err } // CreateChatGroup 创建群组 func (client *ImClient) CreateChatGroup(ctx context.Context, ownerUserId string, groupName string, inviteMem []string) (string, error) { url := "https://open.yunxinapi.com/im/v2.1/teams" httpMethod := common.HTTP_METHOD_POST header := client.generateJsonHeader() reqBody := &CreateChatGroupReq{ OwnerAccountID: ownerUserId, TeamType: 1, Name: groupName, MembersLimit: 3000, InviteAccountIDs: inviteMem, InviteMsg: "管理", Configuration: &TeamConfiguration{ JoinMode: 1, }, AntispamConfiguration: nil, } bodyBytes, err := json.Marshal(reqBody) if err != nil { return "", errors.WithStack(err) } res, err := HyTools.HttpDo(httpMethod, url, header, string(bodyBytes)) if err != nil { // {"desc":"already register","code":414} logs.Info("YunXin_CreateChatGroup_Fail_AccID_%s:"+err.Error(), ownerUserId) return "", errors.WithStack(err) } log.InfoF("create group chat resp: %s", res) var body BaseRes[CreateChatGroupRsp] err = json.Unmarshal([]byte(res), &body) if err != nil { return "", errors.WithStack(err) } if body.Code != 200 { return "", errors.Errorf("YunXin_CreateChatGroup code(%d) not 200,msg: %s", body.Code, body.Msg) } if len(body.Data.FailedList) > 0 { var errInfo []string for _, member := range body.Data.FailedList { errInfo = append(errInfo, fmt.Sprintf("account: %s ,code: %d ,msg: %s", member.AccountID, member.ErrorCode, member.ErrorMsg)) } return "", errors.Errorf("YunXin_CreateChatGroup err: %s", strings.Join(errInfo, ";")) } return cast.ToString(body.Data.TeamInfo.TeamID), nil } // 创建云信账户 // 创建高级群,创建时即可通过设置群成员列表邀请用户入群。 // 建群成功会返回 tid,网易云信服务器产生,群唯一标识,该字段需要保存,以便于加人与踢人等后续操作。 // 如果创建时邀请的成员中存在加群数量超过限制的情况,会返回 faccid(加群失败成员的 IM 账号)。 // 每个用户可创建的群数量有限制,限制值由 IM 套餐的群组配置决定,具体可前往 网易云信控制台 查看。 func (client *ImClient) CreateProGroup(tname, owner, announcement, avatar, msg string) (string, error) { url := "https://api.netease.im/nimserver/team/create.action" httpMethod := common.HTTP_METHOD_POST header := client.generateHeader() sb := HyTools.NewStringBuilder() sb.Append("tname=" + tname) sb.Append("&") sb.Append("owner=" + owner) sb.Append("&") sb.Append("announcement=" + announcement) sb.Append("&") sb.Append("msg=" + msg) sb.Append("&") sb.Append("icon=" + avatar) sb.Append("&") sb.Append("members=[]") sb.Append("&") sb.Append("joinmode=" + "0") sb.Append("&") sb.Append("beinvitemode=" + "1") sb.Append("&") sb.Append("invitemode=" + "1") // 邀请权限,即谁可以邀请他人入群0,群主和管理员(默认)。1,所有人 sb.Append("&") sb.Append("teamMemberLimit=" + "3000") // 最大群成员数(包含群主),[2,200(默认)] sb.Append("&") sb.Append("isNotifyCloseOnline=" + "0") // 是否关闭群通知消息在线发送0,否。1,是 sb.Append("&") sb.Append("isNotifyClosePersistent=" + "0") // 是否关闭存储离线/漫游/历史的群通知消息0,否。1,是 res, err := HyTools.HttpDo(httpMethod, url, header, sb.ToString()) // code Integer 状态码 // tid Long 网易云信服务器产生,群唯一标识 // faccid String 入群失败的账号(accid)列表,格式为 JSONArray,如果创建时邀请的成员中存在加群数量超过限制的情况,会返回入群失败的 accid 以及附言(msg) if err != nil { // {"desc":"already register","code":414} log.Info("CreateProGroup %s:" + err.Error()) return "", err } else { var resDTO CreateImRes json.Unmarshal([]byte(res), &resDTO) if resDTO.Code != 200 { log.Info("CreateProGroup_%s:" + resDTO.Desc) } else { log.Info("CreateProGroup_%s:" + res) } } var body map[string]any if errJson := json.Unmarshal([]byte(res), &body); errJson != nil { return "", errJson } if int(body["code"].(float64)) != 200 { return "", errors.New(body["desc"].(string)) } return body["tid"].(string), nil } // GroupUpdate 更新群组 func (client *ImClient) GroupUpdate(ctx context.Context, id, operator_id, name, announcement, icon string) error { url := "https://open.yunxinapi.com/im/v2.1/teams/" + id httpMethod := common.HTTP_METHOD_PATCH header := client.generateJsonHeader() reqBody := map[string]any{ "operator_id": operator_id, "team_type": 1, "name": name, "announcement": announcement, // "intro": intro, // "members_limit": members_limit, // "join_mode": join_mode, 通过 SDK 侧操作申请入群的验证方式。 0(默认):无需验证,直接入群。 1:需要群主或管理员验证通过才能入群。 2:不允许任何人申请入群。 // "agree_mode": agree_mode, 邀请入群时是否需要被邀请人的同意。0(默认):需要被邀请人同意才能入群。1:不需要被邀请人同意,直接入群。 // "invite_mode": invite_mode, 邀请权限,即谁可以邀请他人入群。0(默认):群主和管理员。1:所有人。 } bodyBytes, err := json.Marshal(reqBody) if err != nil { return errors.WithStack(err) } res, err := HyTools.HttpDo(httpMethod, url, header, string(bodyBytes)) if err != nil { log.InfoF("GroupUpdate %s:"+err.Error(), operator_id) return errors.WithStack(err) } log.InfoF("update group chat resp: %s", res) var body BaseRes[CreateChatGroupRsp] err = json.Unmarshal([]byte(res), &body) if err != nil { return errors.WithStack(err) } if body.Code != 200 { return errors.Errorf("GroupUpdate code(%d) not 200,msg: %s", body.Code, body.Msg) } return nil } // GroupTransfer 转让群组 func (client *ImClient) GroupTransfer(ctx context.Context, id, new_owner, extension string) error { url := "https://open.yunxinapi.com/im/v2.1/teams/" + id + "/actions/transfer_owner" httpMethod := common.HTTP_METHOD_PATCH header := client.generateJsonHeader() reqBody := map[string]any{ "team_type": 1, "new_owner_account_id": new_owner, "leave": 2, "extension": extension, } bodyBytes, err := json.Marshal(reqBody) if err != nil { return errors.WithStack(err) } res, err := HyTools.HttpDo(httpMethod, url, header, string(bodyBytes)) if err != nil { log.InfoF("GroupTransfer %s:"+err.Error(), id) return errors.WithStack(err) } log.InfoF("GroupTransfer group chat resp: %s", res) var body BaseRes[CreateChatGroupRsp] err = json.Unmarshal([]byte(res), &body) if err != nil { return errors.WithStack(err) } if body.Code != 200 { return errors.Errorf("GroupTransfer code(%d) not 200,msg: %s", body.Code, body.Msg) } return nil } // GroupManagerAdd func (client *ImClient) GroupManagerAdd(ctx context.Context, id, operator_id, extension string, managers []string) error { url := "https://open.yunxinapi.com/im/v2.1/teams/" + id + "/actions/add_manager" httpMethod := common.HTTP_METHOD_POST header := client.generateJsonHeader() reqBody := map[string]any{ "team_type": 1, "managers": managers, "operator_id": operator_id, "extension": extension, } bodyBytes, err := json.Marshal(reqBody) if err != nil { return errors.WithStack(err) } res, err := HyTools.HttpDo(httpMethod, url, header, string(bodyBytes)) if err != nil { log.InfoF("GroupManagerAdd %s:"+err.Error(), operator_id) return errors.WithStack(err) } log.InfoF("GroupManagerAdd group chat resp: %s", res) var body BaseRes[CreateChatGroupRsp] err = json.Unmarshal([]byte(res), &body) if err != nil { return errors.WithStack(err) } if body.Code != 200 { return errors.Errorf("GroupManagerAdd code(%d) not 200,msg: %s", body.Code, body.Msg) } return nil } // GroupManagerRemove func (client *ImClient) GroupManagerRemove(ctx context.Context, id, operator_id, extension string, managers []string) error { url := "https://open.yunxinapi.com/im/v2.1/teams/" + id + "/actions/remove_manager" httpMethod := common.HTTP_METHOD_DELETE header := client.generateJsonHeader() reqBody := map[string]any{ "team_type": 1, "managers": managers, "operator_id": operator_id, "extension": extension, } bodyBytes, err := json.Marshal(reqBody) if err != nil { return errors.WithStack(err) } res, err := HyTools.HttpDo(httpMethod, url, header, string(bodyBytes)) if err != nil { log.InfoF("GroupManagerRemove %s:"+err.Error(), operator_id) return errors.WithStack(err) } log.InfoF("GroupManagerRemove group chat resp: %s", res) var body BaseRes[CreateChatGroupRsp] err = json.Unmarshal([]byte(res), &body) if err != nil { return errors.WithStack(err) } if body.Code != 200 { return errors.Errorf("GroupManagerRemove code(%d) not 200,msg: %s", body.Code, body.Msg) } return nil } // GroupRemove func (client *ImClient) GroupRemove(ctx context.Context, id, operator_id, extension string) error { url := "https://open.yunxinapi.com/im/v2.1/teams/" + id httpMethod := common.HTTP_METHOD_DELETE header := client.generateJsonHeader() reqBody := map[string]any{ "team_type": 1, "operator_id": operator_id, "extension": extension, } bodyBytes, err := json.Marshal(reqBody) if err != nil { return errors.WithStack(err) } res, err := HyTools.HttpDo(httpMethod, url, header, string(bodyBytes)) if err != nil { log.InfoF("GroupRemove %s:"+err.Error(), operator_id) return errors.WithStack(err) } log.InfoF("GroupRemove group chat resp: %s", res) var body BaseRes[CreateChatGroupRsp] err = json.Unmarshal([]byte(res), &body) if err != nil { return errors.WithStack(err) } if body.Code != 200 { return errors.Errorf("GroupRemove code(%d) not 200,msg: %s", body.Code, body.Msg) } return nil } // GroupMemberKick func (client *ImClient) GroupMemberKick(ctx context.Context, tid, operator_id, extension string, members []string) error { url := "https://open.yunxinapi.com/im/v2/team_members/actions/kick_member" httpMethod := common.HTTP_METHOD_DELETE header := client.generateJsonHeader() reqBody := map[string]any{ "team_type": 1, "operator_id": operator_id, "team_id": tid, "kick_account_ids": members, "extension": extension, } bodyBytes, err := json.Marshal(reqBody) if err != nil { return errors.WithStack(err) } res, err := HyTools.HttpDo(httpMethod, url, header, string(bodyBytes)) if err != nil { log.InfoF("GroupMemberKick %s:"+err.Error(), operator_id) return errors.WithStack(err) } log.InfoF("GroupMemberKick group chat resp: %s", res) var body BaseRes[CreateChatGroupRsp] err = json.Unmarshal([]byte(res), &body) if err != nil { return errors.WithStack(err) } if body.Code != 200 { return errors.Errorf("GroupMemberKick code(%d) not 200,msg: %s", body.Code, body.Msg) } return nil } // GroupMemberLeave func (client *ImClient) GroupMemberLeave(ctx context.Context, tid, account_id, extension string) error { url := "https://open.yunxinapi.com/im/v2/team_members/actions/leave" httpMethod := common.HTTP_METHOD_DELETE header := client.generateJsonHeader() reqBody := map[string]any{ "team_type": 1, "account_id": account_id, "team_id": tid, "extension": extension, } bodyBytes, err := json.Marshal(reqBody) if err != nil { return errors.WithStack(err) } res, err := HyTools.HttpDo(httpMethod, url, header, string(bodyBytes)) if err != nil { log.InfoF("GroupMemberLeave %s:"+err.Error(), tid) return errors.WithStack(err) } log.InfoF("GroupMemberLeave group chat resp: %s", res) var body BaseRes[CreateChatGroupRsp] err = json.Unmarshal([]byte(res), &body) if err != nil { return errors.WithStack(err) } if body.Code != 200 { return errors.Errorf("GroupMemberLeave code(%d) not 200,msg: %s", body.Code, body.Msg) } return nil } // GroupMemberJoin func (client *ImClient) GroupMemberJoin(ctx context.Context, tid, operator_id, msg, extension string, invite_account_ids []string) error { url := "https://open.yunxinapi.com/im/v2/team_members" httpMethod := common.HTTP_METHOD_POST header := client.generateJsonHeader() reqBody := map[string]any{ "team_type": 1, "operator_id": operator_id, "team_id": tid, "msg": msg, "invite_account_ids": invite_account_ids, "extension": extension, } bodyBytes, err := json.Marshal(reqBody) if err != nil { return errors.WithStack(err) } res, err := HyTools.HttpDo(httpMethod, url, header, string(bodyBytes)) if err != nil { log.InfoF("GroupMemberJoin %s:"+err.Error(), tid) return errors.WithStack(err) } log.InfoF("GroupMemberJoin group chat resp: %s", res) var body BaseRes[CreateChatGroupRsp] err = json.Unmarshal([]byte(res), &body) if err != nil { return errors.WithStack(err) } if body.Code != 200 { return errors.Errorf("GroupMemberJoin code(%d) not 200,msg: %s", body.Code, body.Msg) } return nil } // ================ 私有方法 ======================== func generateBodyData(parameters map[string]string) string { sb := htools.NewStringBuilder() parametersCount := len(parameters) i := 0 for k, v := range parameters { sb.Append(k + "=" + v) if i < parametersCount-1 { sb.Append("&") } i++ } return sb.ToString() } func randNumber() string { str := "0123456789" bytes := []byte(str) var result []byte r := rand.New(rand.NewSource(time.Now().UnixNano())) for i := 0; i < 4; i++ { result = append(result, bytes[r.Intn(len(bytes))]) } return string(result) }