package tencentyun import ( "bytes" "encoding/json" "errors" "fmt" "git.hilo.cn/hilo-common/domain" "git.hilo.cn/hilo-common/mylogrus" "git.hilo.cn/hilo-common/resource/config" "git.hilo.cn/hilo-common/utils" "github.com/sirupsen/logrus" "github.com/tencentyun/tls-sig-api-v2-golang/tencentyun" "io/ioutil" "math/rand" "net/http" "strconv" "strings" "time" ) const ( timUrl = "https://console.tim.qq.com/v4" overseaUrl = "https://adminapiger.im.qcloud.com/v4" loginUrl = timUrl + "/im_open_login_svc/account_import?sdkappid={appid}&identifier={externalId}&usersig={userSin}&random={random}&contenttype=json" overseaLoginUrl = overseaUrl + "/im_open_login_svc/account_import?sdkappid={appid}&identifier={externalId}&usersig={userSin}&random={random}&contenttype=json" blackAddUrl = timUrl + "/sns/black_list_add?sdkappid={appid}&identifier={externalId}&usersig={userSin}&random={random}&contenttype=json" oversearBlackAddUrl = overseaUrl + "/sns/black_list_add?sdkappid={appid}&identifier={externalId}&usersig={userSin}&random={random}&contenttype=json" blackCancelUrl = timUrl + "/sns/black_list_delete?sdkappid={appid}&identifier={externalId}&usersig={userSin}&random={random}&contenttype=json" overseaBlackCancelUrl = overseaUrl + "/sns/black_list_delete?sdkappid={appid}&identifier={externalId}&usersig={userSin}&random={random}&contenttype=json" blackListGetUrl = timUrl + "/sns/black_list_get?sdkappid={appid}&identifier={externalId}&usersig={userSin}&random={random}&contenttype=json" overseaBlackListGetUrl = overseaUrl + "/sns/black_list_get?sdkappid={appid}&identifier={externalId}&usersig={userSin}&random={random}&contenttype=json" blackListCheck = timUrl + "/sns/black_list_check?sdkappid={appid}&identifier={externalId}&usersig={userSin}&random={random}&contenttype=json" overseaBlackListCheck = overseaUrl + "/sns/black_list_check?sdkappid={appid}&identifier={externalId}&usersig={userSin}&random={random}&contenttype=json" msgReadUrl = timUrl + "/openim/admin_set_msg_read?sdkappid={appid}&identifier={externalId}&usersig={userSin}&random={random}&contenttype=json" overseaMsgReadUrl = overseaUrl + "/openim/admin_set_msg_read?sdkappid={appid}&identifier={externalId}&usersig={userSin}&random={random}&contenttype=json" basicParameters = "sdkappid={appid}&identifier=administrator&usersig={userSig}&random={random}&contenttype=json" // 账号相关 // fixme: 哪边为准呢? queryStateUrl = timUrl + "/openim/query_online_status?" + basicParameters overseaQueryStateUrl = overseaUrl + "/openim/query_online_status?" + basicParameters deleteAccountUrl = timUrl + "/im_open_login_svc/account_delete?" + basicParameters // 用户资料 // fixme: 哪边为准呢? getProfileUrl = timUrl + "/profile/portrait_get?" + basicParameters overseaGetProfileUrl = overseaUrl + "/profile/portrait_get?" + basicParameters setProfileUrl = timUrl + "/profile/portrait_set?" + basicParameters overseaSetProfileUrl = overseaUrl + "/profile/portrait_set?" + basicParameters // 群组相关 getAllGroupList = timUrl + "/group_open_http_svc/get_appid_group_list?" + basicParameters overseaGetAllGroupList = overseaUrl + "/group_open_http_svc/get_appid_group_list?" + basicParameters createGroupUrl = timUrl + "/group_open_http_svc/create_group?" + basicParameters overseaCreateGroupUrl = overseaUrl + "/group_open_http_svc/create_group?" + basicParameters destroyGroupUrl = timUrl + "/group_open_http_svc/destroy_group?" + basicParameters overseaGetDestroyGroupUrl = overseaUrl + "/group_open_http_svc/destroy_group?" + basicParameters getGroupInfoUrl = timUrl + "/group_open_http_svc/get_group_info?" + basicParameters overseaGetGroupInfoUrl = overseaUrl + "/group_open_http_svc/get_group_info?" + basicParameters joinGroupUrl = timUrl + "/group_open_http_svc/add_group_member?" + basicParameters leaveGroupUrl = timUrl + "/group_open_http_svc/delete_group_member?" + basicParameters getJoinedGroupUrl = timUrl + "/group_open_http_svc/get_joined_group_list?" + basicParameters modifyGroupInfoUrl = timUrl + "/group_open_http_svc/modify_group_base_info?" + basicParameters overseaModifyGroupInfoUrl = overseaUrl + "/group_open_http_svc/modify_group_base_info?" + basicParameters getGroupMemberUrl = timUrl + "/group_open_http_svc/get_group_member_info?" + basicParameters overseaGetGroupMemberUrl = overseaUrl + "/group_open_http_svc/get_group_member_info?" + basicParameters sendNormalMsgUrl = timUrl + "/group_open_http_svc/send_group_msg?" + basicParameters overseaSendNormalMsgUrl = overseaUrl + "/group_open_http_svc/send_group_msg?" + basicParameters sendSystemMsgUrl = timUrl + "/group_open_http_svc/send_group_system_notification?" + basicParameters overseaSendSystemMsgUrl = overseaUrl + "/group_open_http_svc/send_group_system_notification?" + basicParameters // -------- 单聊相关 -------- c2cSendMsg = timUrl + "/openim/sendmsg?" + basicParameters // 单聊 overseaC2cSendMsg = overseaUrl + "/openim/sendmsg?" + basicParameters // 单聊 c2cBatchSendMsg = timUrl + "/openim/batchsendmsg?" + basicParameters // 群发单聊 overseaC2cBatchSendMsg = overseaUrl + "/openim/batchsendmsg?" + basicParameters // 群发单聊 c2cGetMsg = timUrl + "/openim/admin_getroammsg?" + basicParameters // 查询聊天记录 overseaC2cGetMsg = overseaUrl + "/openim/admin_getroammsg?" + basicParameters // 查询聊天记录 // -------- 设置用户属性 -------- setAttrUrl = timUrl + "/all_member_push/im_set_attr?" + basicParameters overseaSetAttrUrl = overseaUrl + "/all_member_push/im_set_attr?" + basicParameters // -------- 会话 -------- recentcontactdDleteMsg = timUrl + "/recentcontact/delete?" + basicParameters //删除 ) func GenSig(externalId string) (string, error) { return tencentyun.GenSig(config.GetTencentyunAppId(), config.GetTencentyunKey(), externalId, 86400*30) } func getAdminSig() (string, error) { return tencentyun.GenSig(config.GetTencentyunAppId(), config.GetTencentyunKey(), "administrator", 86400*180) } func GenOverseaSig(externalId string) (string, error) { return tencentyun.GenSig(config.GetTxOverSeaAppId(), config.GetTxOverSeaAppKey(), externalId, 86400*30) } func getOverseaAdminSig() (string, error) { return tencentyun.GenSig(config.GetTxOverSeaAppId(), config.GetTxOverSeaAppKey(), "administrator", 86400*180) } func UserRegister(externalId string, nick string, avatar string) error { if config.AppIsRelease() { return UserRegisterBy(externalId, nick, avatar, overseaLoginUrl, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } else { return UserRegisterBy(externalId, nick, avatar, loginUrl, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } } func UserRegisterBy(externalId string, nick string, avatar string, reqUrl string, getAdminSig func() (string, error), appId int) error { type BodyStruct struct { Identifier string Nick string FaceUrl string } type RespStruct struct { ActionStatus string ErrorInfo string ErrorCode int } sig, err := getAdminSig() if err != nil { return err } //url := strings.Replace(strings.Replace(strings.Replace(strings.Replace(loginUrl, "{appid}", strconv.Itoa(config.GetTencentyunAppId()), 1), "{externalId}", "administrator", 1), "{userSin}", sig, 1), "{random}", strconv.Itoa(rand.Intn(10000)), 1) url := strings.Replace(strings.Replace(strings.Replace(strings.Replace(reqUrl, "{appid}", strconv.Itoa(appId), 1), "{externalId}", "administrator", 1), "{userSin}", sig, 1), "{random}", strconv.Itoa(rand.Intn(10000)), 1) body := BodyStruct{ Identifier: externalId, Nick: nick, FaceUrl: avatar, } mylogrus.MyLog.Infof("url: %s, body: %+v", url, body) jsonStr, err := json.Marshal(body) if err != nil { return err } resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return err } defer resp.Body.Close() result, err := ioutil.ReadAll(resp.Body) if err != nil { return err } respStruct := RespStruct{} if err := json.Unmarshal(result, &respStruct); err != nil { return err } mylogrus.MyLog.Infof("body: %+v, resp:%+v", body, respStruct) if respStruct.ErrorCode == 0 { return nil } else { return fmt.Errorf("tencentyun UserRegister resp ErrorCode:%v, ErrorInfo:%v", respStruct.ErrorCode, respStruct.ErrorInfo) } } //测试用 func BlackCheckList(externalId string, beBlockExternalIds []string) error { type BodyStruct struct { From_Account string To_Account []string CheckType string } type Item struct { To_Account string Relation string ResultCode int ResultInfo string } type RespStruct struct { BlackListCheckItem []Item Fail_Account []string ActionStatus string ErrorCode int ErrorInfo string ErrorDisplay string } sig, err := getOverseaAdminSig() if err != nil { return err } url := strings.Replace(strings.Replace(strings.Replace(strings.Replace(overseaBlackListCheck, "{appid}", strconv.Itoa(config.GetTxOverSeaAppId()), 1), "{externalId}", "administrator", 1), "{userSin}", sig, 1), "{random}", strconv.Itoa(rand.Intn(10000)), 1) body := BodyStruct{ From_Account: externalId, To_Account: beBlockExternalIds, CheckType: "BlackCheckResult_Type_Single", } jsonStr, err := json.Marshal(body) if err != nil { return err } resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return err } defer resp.Body.Close() result, err := ioutil.ReadAll(resp.Body) if err != nil { return err } respStruct := RespStruct{} if err := json.Unmarshal(result, &respStruct); err != nil { return err } if respStruct.ErrorCode == 0 { mylogrus.MyLog.Infof("tencentyun BlackCheckList resp data:%v", respStruct) return nil } else { return fmt.Errorf(fmt.Sprintf("tencentyun BlackCheckList resp data:%v, ErrorCode:%v, ErrorInfo:%v", respStruct, respStruct.ErrorCode, respStruct.ErrorInfo)) } } func BlackCancel(externalId string, beBlockExternalId string) error { if config.AppIsRelease() { return BlackCancelBy(externalId, beBlockExternalId, overseaBlackCancelUrl, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } else { return BlackCancelBy(externalId, beBlockExternalId, blackCancelUrl, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } } func BlackCancelBy(externalId string, beBlockExternalId string, reqUrl string, getAdminSig func() (string, error), appId int) error { type BodyStruct struct { From_Account string To_Account []string } //保留部分,因为接口只提供了一个,没有抽象返回值,因为接口没几个。 type RespStruct struct { ActionStatus string ErrorInfo string ErrorCode int } sig, err := getAdminSig() if err != nil { return err } //url := strings.Replace(strings.Replace(strings.Replace(strings.Replace(blackCancelUrl, "{appid}", strconv.Itoa(config.GetTencentyunAppId()), 1), "{externalId}", "administrator", 1), "{userSin}", sig, 1), "{random}", strconv.Itoa(rand.Intn(10000)), 1) url := strings.Replace(strings.Replace(strings.Replace(strings.Replace(reqUrl, "{appid}", strconv.Itoa(appId), 1), "{externalId}", "administrator", 1), "{userSin}", sig, 1), "{random}", strconv.Itoa(rand.Intn(10000)), 1) body := BodyStruct{ From_Account: externalId, To_Account: []string{beBlockExternalId}, } jsonStr, err := json.Marshal(body) if err != nil { return err } resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return err } defer resp.Body.Close() result, err := ioutil.ReadAll(resp.Body) if err != nil { return err } respStruct := RespStruct{} if err := json.Unmarshal(result, &respStruct); err != nil { return err } if respStruct.ErrorCode == 0 { return nil } else { return fmt.Errorf(fmt.Sprintf("tencentyun BlackAdd resp ErrorCode:%v, ErrorInfo:%v", respStruct.ErrorCode, respStruct.ErrorInfo)) } } //测试而已 func BlackListGet(externalId string) ([]string, error) { type BodyStruct struct { From_Account string StartIndex int MaxLimited int LastSequence int } type RespItem struct { To_Account string AddBlackTimeStamp int } type RespStruct struct { BlackListItem []RespItem StartIndex int CurruentSequence int ActionStatus string ErrorInfo string ErrorCode int ErrorDisplay string } sig, err := getOverseaAdminSig() if err != nil { return nil, err } url := strings.Replace(strings.Replace(strings.Replace(strings.Replace(overseaBlackListGetUrl, "{appid}", strconv.Itoa(config.GetTxOverSeaAppId()), 1), "{externalId}", "administrator", 1), "{userSin}", sig, 1), "{random}", strconv.Itoa(rand.Intn(10000)), 1) body := BodyStruct{ From_Account: externalId, StartIndex: 0, MaxLimited: 100, LastSequence: 0, } jsonStr, err := json.Marshal(body) if err != nil { return nil, err } resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return nil, err } defer resp.Body.Close() result, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } respStruct := RespStruct{} if err := json.Unmarshal(result, &respStruct); err != nil { return nil, err } if respStruct.ErrorCode == 0 { strs := []string{} for _, r := range respStruct.BlackListItem { strs = append(strs, r.To_Account) } return strs, nil } else { return nil, fmt.Errorf(fmt.Sprintf("tencentyun BlackListGet resp ErrorCode:%v, ErrorInfo:%v", respStruct.ErrorCode, respStruct.ErrorInfo)) } } func BlackAdd(externalId string, beBlockExternalId string) error { if config.AppIsRelease() { return BlackAddBy(externalId, beBlockExternalId, oversearBlackAddUrl, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } else { return BlackAddBy(externalId, beBlockExternalId, blackAddUrl, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } } func BlackAddOverSea(externalId string, beBlockExternalId string) error { if err := BlackAddBy(externalId, beBlockExternalId, oversearBlackAddUrl, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()); err != nil { return err } return nil } func BlackAddBy(externalId string, beBlockExternalId string, reqUrl string, getAdminSig func() (string, error), appId int) error { type BodyStruct struct { From_Account string To_Account []string } type RespStruct struct { ActionStatus string ErrorInfo string ErrorCode int } sig, err := getAdminSig() if err != nil { return err } //url := strings.Replace(strings.Replace(strings.Replace(strings.Replace(blackAddUrl, "{appid}", strconv.Itoa(config.GetTencentyunAppId()), 1), "{externalId}", "administrator", 1), "{userSin}", sig, 1), "{random}", strconv.Itoa(rand.Intn(10000)), 1) url := strings.Replace(strings.Replace(strings.Replace(strings.Replace(reqUrl, "{appid}", strconv.Itoa(appId), 1), "{externalId}", "administrator", 1), "{userSin}", sig, 1), "{random}", strconv.Itoa(rand.Intn(10000)), 1) body := BodyStruct{ From_Account: externalId, To_Account: []string{beBlockExternalId}, } jsonStr, err := json.Marshal(body) if err != nil { return err } resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return err } defer resp.Body.Close() result, err := ioutil.ReadAll(resp.Body) if err != nil { return err } respStruct := RespStruct{} if err := json.Unmarshal(result, &respStruct); err != nil { return err } mylogrus.MyLog.Infof("BlackAdd, rsp: %+v, req: %+v", resp, body) if respStruct.ErrorCode == 0 { return nil } else { return fmt.Errorf(fmt.Sprintf("tencentyun BlackAdd resp ErrorCode:%v, ErrorInfo:%v", respStruct.ErrorCode, respStruct.ErrorInfo)) } } //两个人互相已读 func AdminSetMsgRead(externalId1 string, externalId2 string) error { if config.AppIsRelease() { return AdminSetMsgReadBy(externalId1, externalId2, overseaMsgReadUrl, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } else { return AdminSetMsgReadBy(externalId1, externalId2, msgReadUrl, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } } //两个人互相已读 func AdminSetMsgReadBy(externalId1 string, externalId2 string, reqUrl string, getAdminSig func() (string, error), appId int) error { type BodyStruct struct { Report_Account string Peer_Account string } //保留部分,因为接口只提供了一个,没有抽象返回值,因为接口没几个。 type RespStruct struct { ActionStatus string ErrorInfo string ErrorCode int } sig, err := getAdminSig() if err != nil { return err } //url := strings.Replace(strings.Replace(strings.Replace(strings.Replace(msgReadUrl, "{appid}", strconv.Itoa(config.GetTencentyunAppId()), 1), "{externalId}", "administrator", 1), "{userSin}", sig, 1), "{random}", strconv.Itoa(rand.Intn(10000)), 1) url := strings.Replace(strings.Replace(strings.Replace(strings.Replace(reqUrl, "{appid}", strconv.Itoa(appId), 1), "{externalId}", "administrator", 1), "{userSin}", sig, 1), "{random}", strconv.Itoa(rand.Intn(10000)), 1) body := BodyStruct{ Report_Account: externalId1, Peer_Account: externalId2, } jsonStr, err := json.Marshal(body) if err != nil { return err } resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return err } defer resp.Body.Close() result, err := ioutil.ReadAll(resp.Body) if err != nil { return err } respStruct := RespStruct{} if err := json.Unmarshal(result, &respStruct); err != nil { return err } if respStruct.ErrorCode == 0 { return nil } else { return fmt.Errorf(fmt.Sprintf("tencentyun AdminSetMsgRead resp ErrorCode:%v, ErrorInfo:%v", respStruct.ErrorCode, respStruct.ErrorInfo)) } } // 基本的返回结构 type RespStruct struct { ActionStatus string ErrorCode int ErrorInfo string } // 私聊发送返回结构 type C2cMsgRsp struct { RespStruct MsgTime int64 MsgKey string } func GetAllGroups() ([]string, uint, error) { type getAllGroupReq struct { GroupType string } type groupIdItem struct { GroupId string } type getAllGroupRsp struct { RespStruct TotalCount uint GroupIdList []groupIdItem } sig, err := getOverseaAdminSig() if err != nil { return nil, 0, err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(overseaGetAllGroupList, "{appid}", strconv.Itoa(config.GetTxOverSeaAppId())), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := getAllGroupReq{ GroupType: "ChatRoom", } jsonStr, err := json.Marshal(body) if err != nil { return nil, 0, err } mylogrus.MyLog.Info("GetAllGroups json: ", string(jsonStr)) rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return nil, 0, err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return nil, 0, err } response := getAllGroupRsp{} if err := json.Unmarshal(result, &response); err != nil { return nil, 0, err } mylogrus.MyLog.Info("GetAllGroups: ", response) if response.ErrorCode == 0 { result := make([]string, 0) for _, i := range response.GroupIdList { result = append(result, i.GroupId) } return result, response.TotalCount, nil } else { return nil, 0, fmt.Errorf(fmt.Sprintf("tencentyun GetAllGroups rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)) } } type GroupEditable struct { Name string // 群名称(选填) // Introduction string // 群简介(选填) NOTE:腾讯限制字节数太短,弃用 // Notification string // 群公告(选填) NOTE:腾讯限制字节数太短,弃用 // FaceUrl string // 群头像 URL(选填) NOTE:腾讯限制最长100个字节太短,弃用 // "MaxMemberNum": 500, // 最大群成员数量(选填) // "ApplyJoinOption": "NeedPermission", // 申请加群方式(选填) // "MuteAllMember": "On" // 设置全员禁言(选填):"On"开启,"Off"关闭 } func CreateGroup(name string, groupId string) (string, error) { if config.AppIsRelease() { return CreateGroupBy(name, groupId, overseaCreateGroupUrl, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } else { return CreateGroupBy(name, groupId, createGroupUrl, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } } func CreateGroupBy(name string, txGroupId string, reqUrl string, getAdminSig func() (string, error), appId int) (string, error) { logger := mylogrus.MyLog.WithField("appId", appId).WithField("txGroupId", txGroupId) type createGroupReq struct { Type string GroupId *string GroupEditable } type CreateGroupRsp struct { RespStruct GroupId string } sig, err := getAdminSig() if err != nil { return "", err } /* url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(createGroupUrl, "{appid}", strconv.Itoa(config.GetTencentyunAppId())), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10))*/ url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(reqUrl, "{appid}", strconv.Itoa(appId)), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) // 限制字符数,避免超过腾讯的30字节限制 groupName := name if len(name) > 7 { groupName = name[0:7] } body := createGroupReq{ Type: "AVChatRoom", GroupEditable: GroupEditable{ Name: groupName, }, } if len(txGroupId) > 0 { body.GroupId = &txGroupId } jsonStr, err := json.Marshal(body) if err != nil { return "", err } logger.Info("CreateGroup json: ", string(jsonStr)) rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return "", err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return "", err } response := CreateGroupRsp{} if err := json.Unmarshal(result, &response); err != nil { return "", err } logger.Info("CreateGroup: ", response) if response.ErrorCode == 0 { return response.GroupId, nil } else { return "", fmt.Errorf(fmt.Sprintf("tencentyun CreateGroup rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)) } } func DestroyGroup(groupId string, localOnly bool) error { err := DestroyGroupBy(groupId, destroyGroupUrl, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) if !localOnly { DestroyGroupBy(groupId, overseaGetDestroyGroupUrl, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } return err } func DestroyGroupBy(txGroupId string, reqUrl string, getAdminSig func() (string, error), appId int) error { logger := mylogrus.MyLog.WithField("appId", appId).WithField("txGroupId", txGroupId) type BodyStruct struct { GroupId string } sig, err := getAdminSig() if err != nil { return err } /* url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(destroyGroupUrl, "{appid}", strconv.Itoa(config.GetTencentyunAppId())), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10))*/ url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(reqUrl, "{appid}", strconv.Itoa(appId)), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := BodyStruct{ GroupId: txGroupId, } jsonStr, err := json.Marshal(body) if err != nil { return err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return err } response := RespStruct{} if err := json.Unmarshal(result, &response); err != nil { return err } logger.Info("DestroyGroup: ", response) if response.ErrorCode == 0 || response.ErrorCode == 10010 { return nil } else { return fmt.Errorf(fmt.Sprintf("tencentyun DestroyGroup rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)) } } type MemberListInfo struct { Member_Account string // 成员 ID Role string // 群内角色 JoinTime int // 入群时间(UTC 时间) //MsgSeq uint // MsgFlag string // 消息屏蔽选项 LastSendMsgTime int64 // 最后发言时间(UTC 时间) ShutUpUntil int // 禁言截止时间(UTC 时间) // AppMemberDefinedData": [ // 群成员自定义字段 【暂时不用】 } type GroupBasicInfo struct { GroupId string Type string Owner_Account string CreateTime int LastInfoTime int LastMsgTime int NextMsgSeq uint MemberNum uint MaxMemberNum uint ShutUpAllMember string } type GroupInfo struct { ErrorCode int ErrorInfo string Appid uint GroupBasicInfo // "AppDefinedData": 群组维度的自定义字段 【暂时不用】 MemberList []MemberListInfo } const GROUP_GET_BATCHSIZE = 50 const IM_GET_BATCHSIZE = 500 //海外国内是同步的,只要查询一个则可 // 分批查询群信息,保证不会溢出 func BatchGetGroupInfo(model *domain.Model, groupIds []string, requireMemberList bool) ([]GroupInfo, error) { result := make([]GroupInfo, 0, len(groupIds)) for start := 0; start < len(groupIds); start += GROUP_GET_BATCHSIZE { end := start + GROUP_GET_BATCHSIZE if end > len(groupIds) { end = len(groupIds) } mylogrus.MyLog.Info("tencentyun.BatchGetGroupInfo: ", start, ":", end) g := groupIds[start:end] gi, err := GetGroupInfo(model, g, requireMemberList) if err == nil { result = append(result, gi...) } else { return nil, err } } return result, nil } // 查询群信息,有溢出可能,只适用于少量的情况 func GetGroupInfo(model *domain.Model, groupIds []string, requireMemberList bool) ([]GroupInfo, error) { type responseFilter struct { GroupBaseInfoFilter []string MemberInfoFilter []string } type BodyStruct struct { GroupIdList []string ResponseFilter responseFilter } sig, err := getOverseaAdminSig() if err != nil { return nil, err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(overseaGetGroupInfoUrl, "{appid}", strconv.Itoa(config.GetTxOverSeaAppId())), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) model.Log.Info("tencentyun.GetGroupInfo: ", url) beginTime := time.Now() body := BodyStruct{ GroupIdList: groupIds, ResponseFilter: responseFilter{ GroupBaseInfoFilter: []string{"Type", "CreateTime", "Owner_Account", "LastMsgTime", "NextMsgSeq", "MemberNum", "MaxMemberNum"}, }, } if requireMemberList { body.ResponseFilter.MemberInfoFilter = append(body.ResponseFilter.MemberInfoFilter, "Account", "JoinTime") } jsonStr, err := json.Marshal(body) if err != nil { return nil, err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return nil, err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return nil, err } type getGroupInfoRsp struct { RespStruct GroupInfo []GroupInfo } response := getGroupInfoRsp{} if err := json.Unmarshal(result, &response); err != nil { return nil, err } endTime := time.Now() model.Log.Info("tencentyun.GetGroupInfo takes ", endTime.Sub(beginTime).Milliseconds(), "ms") model.Log.Info("tencentyun.GetGroupInfo: ", response) if response.ErrorCode == 0 { result := make([]GroupInfo, 0) for _, i := range response.GroupInfo { if i.Owner_Account == IM_INVALID_USER { model.Log.Warnf("Skip Group %s with invalid owner", i.GroupId) } else { result = append(result, i) } } return result, nil } else { return nil, fmt.Errorf(fmt.Sprintf("tencentyun GetGroupInfo rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)) } } //已丢弃 func AddGroup(model *domain.Model, groupId string, members []string) (map[string]uint, error, int) { beginTime := time.Now() type Member struct { Member_Account string } type BodyStruct struct { GroupId string MemberList []Member } sig, err := getAdminSig() if err != nil { return nil, err, 0 } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(joinGroupUrl, "{appid}", strconv.Itoa(config.GetTencentyunAppId())), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := BodyStruct{ GroupId: groupId, MemberList: []Member{}, } for _, i := range members { body.MemberList = append(body.MemberList, Member{Member_Account: i}) } jsonStr, err := json.Marshal(body) if err != nil { return nil, err, 0 } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return nil, err, 0 } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return nil, err, 0 } type memberResult struct { Member_Account string Result uint } type addGroupRsp struct { RespStruct MemberList []memberResult } response := addGroupRsp{} if err := json.Unmarshal(result, &response); err != nil { return nil, err, 0 } endTime := time.Now() model.Log.Info("tencentyun.AddGroup takes ", endTime.Sub(beginTime).Milliseconds(), "ms") model.Log.Info("AddGroup: ", response) if response.ErrorCode == 0 { result := make(map[string]uint) for _, i := range response.MemberList { result[i.Member_Account] = i.Result } return result, nil, 0 } else { return nil, fmt.Errorf(fmt.Sprintf("tencentyun AddGroup rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)), response.ErrorCode } } //已丢弃 func LeaveGroup(groupId string, members []string) { type BodyStruct struct { GroupId string MemberToDel_Account []string } sig, err := getAdminSig() if err != nil { mylogrus.MyLog.Errorln(err) return } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(leaveGroupUrl, "{appid}", strconv.Itoa(config.GetTencentyunAppId())), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := BodyStruct{ GroupId: groupId, MemberToDel_Account: members, } jsonStr, err := json.Marshal(body) if err != nil { mylogrus.MyLog.Errorln(err) return } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { mylogrus.MyLog.Errorln(err) return } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { mylogrus.MyLog.Error(err) return } response := RespStruct{} if err := json.Unmarshal(result, &response); err != nil { mylogrus.MyLog.Errorln(err) return } mylogrus.MyLog.Info("LeaveGroup: ", response) if response.ErrorCode == 0 { return } else { mylogrus.MyLog.Errorln(fmt.Errorf(fmt.Sprintf("tencentyun LeaveGroup rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo))) return } } type SelfGroupInfo struct { JoinTime int Role string MsgFlag string LastSendMsgTime int NameCard string } type JoinedGroupInfo struct { GroupBasicInfo SelfInfo SelfGroupInfo } func ShutUpAllMember(groupId string, shutUp bool) error { if config.AppIsRelease() { return ShutUpAllMemberBy(groupId, shutUp, overseaModifyGroupInfoUrl, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } else { return ShutUpAllMemberBy(groupId, shutUp, modifyGroupInfoUrl, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } } func ShutUpAllMemberBy(groupId string, shutUp bool, reqUrl string, getAdminSig func() (string, error), appId int) error { type ModifyGroupInfoReq struct { GroupId string MuteAllMember string } sig, err := getAdminSig() if err != nil { return err } /* url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(modifyGroupInfoUrl, "{appid}", strconv.Itoa(config.GetTencentyunAppId())), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10))*/ url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(reqUrl, "{appid}", strconv.Itoa(appId)), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := ModifyGroupInfoReq{ GroupId: groupId, } if shutUp { body.MuteAllMember = "On" } else { body.MuteAllMember = "Off" } jsonStr, err := json.Marshal(body) if err != nil { return err } mylogrus.MyLog.Info("ShutUpAllMember json: ", string(jsonStr)) rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return err } response := RespStruct{} if err := json.Unmarshal(result, &response); err != nil { return err } mylogrus.MyLog.Info("ShutUpAllMember: ", response) if response.ErrorCode == 0 { return nil } else { return fmt.Errorf(fmt.Sprintf("tencentyun ShutUpAllMember rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)) } } //丢弃 func SetGroupMaxMemberNum(groupId string, maxMemberNum uint) error { type modifyGroupInfoReq struct { GroupId string MaxMemberNum uint } sig, err := getOverseaAdminSig() if err != nil { return err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(overseaModifyGroupInfoUrl, "{appid}", strconv.Itoa(config.GetTxOverSeaAppId())), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := modifyGroupInfoReq{ GroupId: groupId, MaxMemberNum: maxMemberNum, } jsonStr, err := json.Marshal(body) if err != nil { return err } mylogrus.MyLog.Info("SetGroupMaxMemberNum json: ", string(jsonStr)) rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return err } response := RespStruct{} if err := json.Unmarshal(result, &response); err != nil { return err } mylogrus.MyLog.Info("SetGroupMaxMemberNum: ", response) if response.ErrorCode == 0 { return nil } else { return fmt.Errorf(fmt.Sprintf("tencentyun SetGroupMaxMemberNum rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)) } } func GetGroupMemberInfo(model *domain.Model, groupId string) (uint, []MemberListInfo, error) { beginTime := time.Now() type BodyStruct struct { GroupId string MemberInfoFilter []string } sig, err := getOverseaAdminSig() if err != nil { return 0, nil, err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(overseaGetGroupMemberUrl, "{appid}", strconv.Itoa(config.GetTxOverSeaAppId())), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := BodyStruct{ GroupId: groupId, MemberInfoFilter: []string{"Role", "LastSendMsgTime"}, } jsonStr, err := json.Marshal(body) if err != nil { return 0, nil, err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return 0, nil, err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return 0, nil, err } type getGroupMemberRsp struct { RespStruct MemberNum uint MemberList []MemberListInfo } response := getGroupMemberRsp{} if err := json.Unmarshal(result, &response); err != nil { return 0, nil, err } endTime := time.Now() model.Log.Info("tencentyun.GetGroupMemberInfo takes ", endTime.Sub(beginTime).Milliseconds(), "ms") model.Log.Debug("GetGroupMemberInfo ", groupId, " : ", response) if response.ErrorCode == 0 { return response.MemberNum, response.MemberList, nil } else { return 0, nil, fmt.Errorf(fmt.Sprintf("tencentyun GetGroupMemberInfo rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)) } } // 海外版和国内版之间交叉发群消息 func CrossSendGroupMsg(appId int, txGroupId string, fromAccount *string, cloudCustomData string, msgType string, msgContent json.RawMessage) (uint, error) { if appId == config.GetTencentyunAppId() { return SendGroupMsgBy(txGroupId, fromAccount, cloudCustomData, msgType, msgContent, overseaSendNormalMsgUrl, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } else if appId == config.GetTxOverSeaAppId() { return SendGroupMsgBy(txGroupId, fromAccount, cloudCustomData, msgType, msgContent, sendNormalMsgUrl, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } return 0, errors.New("illegal appid") } func SendGroupMsgBy(txGroupId string, fromAccount *string, cloudCustomData string, msgType string, msgContent json.RawMessage, reqUrl string, getAdminSig func() (string, error), appId int) (uint, error) { beginTime := time.Now() type msgBodyItem struct { MsgType string MsgContent json.RawMessage } type BodyStruct struct { GroupId string From_Account *string Random uint32 ForbidCallbackControl []string MsgBody []msgBodyItem CloudCustomData string } sig, err := getAdminSig() if err != nil { return 0, err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(reqUrl, "{appid}", strconv.Itoa(appId)), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := BodyStruct{ GroupId: txGroupId, Random: rand.Uint32(), ForbidCallbackControl: []string{"ForbidAfterSendMsgCallback"}, MsgBody: []msgBodyItem{{MsgType: msgType, MsgContent: msgContent}}, CloudCustomData: cloudCustomData, } if fromAccount != nil { body.From_Account = fromAccount } mylogrus.MyLog.Infof("SendGroupMsgBy on Group %s, url:%s, body: %+v", txGroupId, url, body) jsonStr, err := json.Marshal(body) if err != nil { return 0, err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return 0, err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return 0, err } type sendNormalMsgRsp struct { RespStruct MsgTime int64 MsgSeq uint } response := sendNormalMsgRsp{} if err := json.Unmarshal(result, &response); err != nil { return 0, err } endTime := time.Now() mylogrus.MyLog.Infof("SendGroupMsgBy on Group %s, result: %v, takes %v ms", txGroupId, response, endTime.Sub(beginTime).Milliseconds()) if response.ErrorCode == 0 { return response.MsgSeq, nil } else { return 0, fmt.Errorf(fmt.Sprintf("tencentyun SendGroupMsgBy rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)) } } func SendCustomMsg(logger *logrus.Entry, txGroupId string, fromAccount *string, content string, cloudCustomData string) (uint, error) { if config.AppIsRelease() { return SendCustomMsgBy(logger, txGroupId, fromAccount, content, cloudCustomData, overseaSendNormalMsgUrl, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } else { return SendCustomMsgBy(logger, txGroupId, fromAccount, content, cloudCustomData, sendNormalMsgUrl, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } } // 发送(自定义)群聊天消息 // fixme: 异步化 func SendCustomMsgBy(logger *logrus.Entry, txGroupId string, fromAccount *string, content string, cloudCustomData string, reqUrl string, getAdminSig func() (string, error), appId int) (uint, error) { logger = logger.WithField("appId", appId).WithField("txGroupId", txGroupId) logger.Infof("from %v, content: %s", fromAccount, content) beginTime := time.Now() type msgContentItem struct { Data string } type msgBodyItem struct { MsgType string MsgContent msgContentItem } type BodyStruct struct { GroupId string From_Account *string Random uint32 ForbidCallbackControl []string MsgBody []msgBodyItem CloudCustomData string } sig, err := getAdminSig() if err != nil { return 0, err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(reqUrl, "{appid}", strconv.Itoa(appId)), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := BodyStruct{ GroupId: txGroupId, Random: rand.Uint32(), ForbidCallbackControl: []string{"ForbidAfterSendMsgCallback"}, MsgBody: []msgBodyItem{{MsgType: "TIMCustomElem", MsgContent: msgContentItem{Data: content}}}, CloudCustomData: cloudCustomData, } if fromAccount != nil { body.From_Account = fromAccount } jsonStr, err := json.Marshal(body) if err != nil { return 0, err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return 0, err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return 0, err } type sendNormalMsgRsp struct { RespStruct MsgTime int64 MsgSeq uint } response := sendNormalMsgRsp{} if err := json.Unmarshal(result, &response); err != nil { return 0, err } endTime := time.Now() logger.Infof("SendCustomMsg taskes %d ms, result: %v", endTime.Sub(beginTime).Milliseconds(), response) if response.ErrorCode == 0 { return response.MsgSeq, nil } else { return 0, fmt.Errorf(fmt.Sprintf("tencentyun SendCustomMsg rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)) } } func SendSimpleAtMsg(txGroupId string, from string, account string, onLineOnly int, content string, cloudCustomData string) (uint, error) { if config.AppIsRelease() { return SendSimpleAtMsgBy(txGroupId, from, account, onLineOnly, content, cloudCustomData, overseaSendNormalMsgUrl, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } else { return SendSimpleAtMsgBy(txGroupId, from, account, onLineOnly, content, cloudCustomData, sendNormalMsgUrl, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } } // 向群内发送形如“@tommy xxx"的消息 func SendSimpleAtMsgBy(txGroupId string, from string, account string, onLineOnly int, content string, cloudCustomData string, reqUrl string, getAdminSig func() (string, error), appId int) (uint, error) { mylogrus.MyLog.Infof("SendSimpleAtMsg on Group %s, from %s, %s, content: %s", txGroupId, from, account, content) beginTime := time.Now() type msgContentItem struct { Text string } type msgBodyItem struct { MsgType string MsgContent msgContentItem } type groupAtItem struct { GroupAtAllFlag int GroupAt_Account string } type BodyStruct struct { GroupId string From_Account string Random uint32 ForbidCallbackControl []string OnlineOnlyFlag *int MsgBody []msgBodyItem GroupAtInfo []groupAtItem CloudCustomData string } sig, err := getAdminSig() if err != nil { return 0, err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(reqUrl, "{appid}", strconv.Itoa(appId)), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := BodyStruct{ GroupId: txGroupId, From_Account: from, Random: rand.Uint32(), ForbidCallbackControl: []string{"ForbidAfterSendMsgCallback"}, MsgBody: []msgBodyItem{{MsgType: "TIMTextElem", MsgContent: msgContentItem{Text: content}}}, GroupAtInfo: []groupAtItem{{GroupAtAllFlag: 0, GroupAt_Account: account}}, CloudCustomData: cloudCustomData, } if onLineOnly == 1 { body.OnlineOnlyFlag = &onLineOnly } jsonStr, err := json.Marshal(body) if err != nil { return 0, err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return 0, err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return 0, err } type sendNormalMsgRsp struct { RespStruct MsgTime int64 MsgSeq uint } response := sendNormalMsgRsp{} if err := json.Unmarshal(result, &response); err != nil { return 0, err } endTime := time.Now() mylogrus.MyLog.Info("tencentyun.SendSimpleAtMsg takes ", endTime.Sub(beginTime).Milliseconds(), "ms") mylogrus.MyLog.Infof("SendSimpleAtMsg on Group %s, result: %v", txGroupId, response) if response.ErrorCode == 0 { return response.MsgSeq, nil } else { return 0, fmt.Errorf(fmt.Sprintf("tencentyun SendSimpleAtMsg rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)) } } func SendSystemMsg(logger *logrus.Entry, groupId string, members []string, content string) error { if config.AppIsRelease() { return SendSystemMsgBy(logger, groupId, members, content, overseaSendSystemMsgUrl, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } else { return SendSystemMsgBy(logger, groupId, members, content, sendSystemMsgUrl, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } } func SendSystemMsgBy(logger *logrus.Entry, txGroupId string, members []string, content string, reqUrl string, getAdminSig func() (string, error), appId int) error { logger = logger.WithField("appId", appId).WithField("txGroupId", txGroupId) //logger.Infof("SendSystemMsg content: %s", content) beginTime := time.Now() type BodyStruct struct { GroupId string ToMembers_Account []string Content string } sig, err := getAdminSig() if err != nil { return err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(reqUrl, "{appid}", strconv.Itoa(appId)), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := BodyStruct{ GroupId: txGroupId, ToMembers_Account: members, Content: content, } jsonStr, err := json.Marshal(body) if err != nil { return err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return err } response := RespStruct{} if err := json.Unmarshal(result, &response); err != nil { return err } endTime := time.Now() logger.Infof("SendSystemMsg takes %dms: rsp: %+v", endTime.Sub(beginTime).Milliseconds(), response) if response.ErrorCode == 0 { return nil } else { return fmt.Errorf(fmt.Sprintf("tencentyun SendSystemMsg rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)) } } type MsgContentItem struct { Text string } type MsgBodyItem struct { MsgContent MsgContentItem MsgType string } type GroupMsgItem struct { From_Account string IsPlaceMsg int MsgBody []MsgBodyItem MsgPriority int MsgRandom uint MsgSeq uint MsgTimeStamp uint } func QueryState(model *domain.Model, users []string, isNeedDetail bool) (map[string]string, []string, error) { beginTime := time.Now() type BodyStruct struct { To_Account []string IsNeedDetail bool } sig, err := getAdminSig() if err != nil { return nil, nil, err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(queryStateUrl, "{appid}", strconv.Itoa(config.GetTencentyunAppId())), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := BodyStruct{ To_Account: users, IsNeedDetail: isNeedDetail, } jsonStr, err := json.Marshal(body) if err != nil { return nil, nil, err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return nil, nil, err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return nil, nil, err } type detailInfo struct { Platform string Status string } type stateInfo struct { To_Account string Status string Detail []detailInfo } type errorInfo struct { To_Account string ErrorCode uint } type queryStateRsp struct { RespStruct QueryResult []stateInfo ErrorList []errorInfo } response := queryStateRsp{} if err := json.Unmarshal(result, &response); err != nil { return nil, nil, err } endTime := time.Now() model.Log.Info("tencentyun.QueryState takes ", endTime.Sub(beginTime).Milliseconds(), "ms") model.Log.Info("tencentyun.QueryState: ", response) failed := make([]string, 0) if response.ErrorCode == 0 { result := make(map[string]string, 0) for _, i := range response.QueryResult { result[i.To_Account] = i.Status } for _, i := range response.ErrorList { failed = append(failed, i.To_Account) } return result, failed, nil } else { return nil, failed, nil //return nil, nil, fmt.Errorf(fmt.Sprintf("tencentyun QueryState rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)) } } func BatchQueryState(model *domain.Model, users []string) (map[string]uint, error) { result := make(map[string]uint, 0) for start := 0; start < len(users); start += IM_GET_BATCHSIZE { end := start + IM_GET_BATCHSIZE if end > len(users) { end = len(users) } u := users[start:end] s, missed, err := QueryState(model, u, false) mylogrus.MyLog.Info("tencentyun.BatchQueryState: ", start, ":", end, ", statusMap: ", s, ", missed: ", missed) if err == nil { for _, i := range missed { result[i] = IM_STATUS_OFF_LINE } for k, v := range s { if v == "Offline" { result[k] = IM_STATUS_OFF_LINE } else if v == "PushOnline" { result[k] = IM_STATUS_PUSH_ON_LINE } else if v == "Online" { result[k] = IM_STATUS_ON_LINE } } } else { return nil, err } } return result, nil } func DeleteAccount(userId string) ([]string, error) { type accountType struct { UserID string } type BodyStruct struct { DeleteItem []accountType } sig, err := getAdminSig() if err != nil { return nil, err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(deleteAccountUrl, "{appid}", strconv.Itoa(config.GetTencentyunAppId())), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := BodyStruct{ DeleteItem: []accountType{{UserID: userId}}, } jsonStr, err := json.Marshal(body) if err != nil { return nil, err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return nil, err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return nil, err } type resultItemType struct { ResultCode int ResultInfo string UserID string } type deleteAccountRsp struct { RespStruct ResultItem []resultItemType } response := deleteAccountRsp{} if err := json.Unmarshal(result, &response); err != nil { return nil, err } mylogrus.MyLog.Info("DeleteAccount: ", response) failed := make([]string, 0) if response.ErrorCode == 0 { for _, i := range response.ResultItem { mylogrus.MyLog.Info("DeleteAccount ResultItem", i) if i.ResultCode != 0 { failed = append(failed, i.UserID) } } return failed, nil } else { return nil, nil //return nil, nil, fmt.Errorf(fmt.Sprintf("tencentyun QueryState rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)) } } func GetUserLevel(userId string) (uint32, error) { type BodyStruct struct { To_Account []string TagList []string } sig, err := getAdminSig() if err != nil { return 0, err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(getProfileUrl, "{appid}", strconv.Itoa(config.GetTencentyunAppId())), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := BodyStruct{ To_Account: []string{userId}, TagList: []string{TAG_PROFILE_IM_LEVEL}, } jsonStr, err := json.Marshal(body) if err != nil { return 0, err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return 0, err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return 0, err } type ProfileItemType struct { Tag string Value uint32 } type UserProfileItemType struct { To_Account string ProfileItem []ProfileItemType ResultCode int ResultInfo string } type getUserLevelRsp struct { UserProfileItem []UserProfileItemType RespStruct ErrorDisplay string } response := getUserLevelRsp{} if err := json.Unmarshal(result, &response); err != nil { return 0, err } mylogrus.MyLog.Info("GetUserLevel: ", response) if response.ErrorCode == 0 { for _, i := range response.UserProfileItem { if i.To_Account == userId && i.ResultCode == 0 { for _, j := range i.ProfileItem { if j.Tag == TAG_PROFILE_IM_LEVEL { return j.Value, nil } } } } return 0, nil } else { return 0, fmt.Errorf(response.ErrorDisplay) } } // fixme: 可以去掉了 func SetUserLevel(userId string, level uint32) error { if !config.AppIsRelease() { return SetUserLevelBy(userId, level, setProfileUrl, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } return nil } func SetUserLevelBy(userId string, level uint32, reqUrl string, getAdminSig func() (string, error), appId int) error { mylogrus.MyLog.Infof("SetUserLevel: user %s, level = %x", userId, level) type ProfileItemType struct { Tag string Value uint32 } type BodyStruct struct { From_Account string ProfileItem []ProfileItemType } sig, err := getAdminSig() if err != nil { return err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(reqUrl, "{appid}", strconv.Itoa(appId)), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := BodyStruct{ From_Account: userId, ProfileItem: []ProfileItemType{{Tag: TAG_PROFILE_IM_LEVEL, Value: level}}, } jsonStr, err := json.Marshal(body) if err != nil { return err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return err } type setUserLevelRsp struct { RespStruct ErrorDisplay string } response := setUserLevelRsp{} if err := json.Unmarshal(result, &response); err != nil { return err } mylogrus.MyLog.Info("SetUserLevel: ", response) if response.ErrorCode == 0 { return nil } else { return fmt.Errorf(response.ErrorDisplay) } } func GetUserHiloInfo(userId string) (string, error) { type BodyStruct struct { To_Account []string TagList []string } sig, err := getOverseaAdminSig() if err != nil { return "", err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(overseaGetProfileUrl, "{appid}", strconv.Itoa(config.GetTxOverSeaAppId())), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := BodyStruct{ To_Account: []string{userId}, TagList: []string{TAG_PROFILE_IM_HILO}, } jsonStr, err := json.Marshal(body) if err != nil { return "", err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return "", err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return "", err } type ProfileItemType struct { Tag string Value string } type UserProfileItemType struct { To_Account string ProfileItem []ProfileItemType ResultCode int ResultInfo string } type getUserLevelRsp struct { UserProfileItem []UserProfileItemType RespStruct ErrorDisplay string } response := getUserLevelRsp{} if err := json.Unmarshal(result, &response); err != nil { return "", err } mylogrus.MyLog.Info("GetUserHiloInfo: ", response) if response.ErrorCode == 0 { for _, i := range response.UserProfileItem { if i.To_Account == userId && i.ResultCode == 0 { for _, j := range i.ProfileItem { if j.Tag == TAG_PROFILE_IM_HILO { return j.Value, nil } } } } return "", nil } else { return "", fmt.Errorf(response.ErrorDisplay) } } func SetUserHiloInfo(userId string, value string) error { /* err := SetUserHiloInfoBy(userId, value, overseaSetProfileUrl, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) if err != nil { return err } */ if !config.AppIsRelease() { return SetUserHiloInfoBy(userId, value, setProfileUrl, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } return nil } func SetUserHiloInfoBy(userId string, value string, reqUrl string, getAdminSig func() (string, error), appId int) error { mylogrus.MyLog.Infof("SetUserHiloInfo: user %s, value: %s", userId, value) type ProfileItemType struct { Tag string Value string } type BodyStruct struct { From_Account string ProfileItem []ProfileItemType } sig, err := getAdminSig() if err != nil { return err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(reqUrl, "{appid}", strconv.Itoa(appId)), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := BodyStruct{ From_Account: userId, ProfileItem: []ProfileItemType{{Tag: TAG_PROFILE_IM_HILO, Value: value}}, } jsonStr, err := json.Marshal(body) if err != nil { return err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return err } type setUserLevelRsp struct { RespStruct ErrorDisplay string } response := setUserLevelRsp{} if err := json.Unmarshal(result, &response); err != nil { return err } mylogrus.MyLog.Info("SetUserHiloInfo: ", response) if response.ErrorCode == 0 { return nil } else { return fmt.Errorf(response.ErrorDisplay) } } // 重置用户信息 // 头像+昵称 func ResetUserInfo(userId, nick string) error { if config.AppIsRelease() { return ResetUserInfoBy(userId, nick, overseaSetProfileUrl, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } return ResetUserInfoBy(userId, nick, setProfileUrl, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } func ResetUserInfoBy(userId, nick string, reqUrl string, getAdminSig func() (string, error), appId int) error { type ProfileItemType struct { Tag string Value string } type BodyStruct struct { From_Account string ProfileItem []ProfileItemType } sig, err := getAdminSig() if err != nil { return err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(reqUrl, "{appid}", strconv.Itoa(appId)), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := BodyStruct{ From_Account: userId, ProfileItem: []ProfileItemType{ {Tag: TAG_PROFILE_IM_NICK, Value: nick}, {Tag: TAG_PROFILE_IM_IMAGE, Value: utils.MakeFullUrl(utils.DefaultAvatarMan)}, }, } jsonStr, err := json.Marshal(body) if err != nil { return err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return err } type setUserNickRsp struct { RespStruct ErrorDisplay string } response := setUserNickRsp{} if err := json.Unmarshal(result, &response); err != nil { return err } mylogrus.MyLog.Info("SetUserNick: ", response) if response.ErrorCode == 0 { return nil } else { return fmt.Errorf(response.ErrorDisplay) } } func SetUserNick(userId string, nick string) error { if config.AppIsRelease() { return SetUserNickBy(userId, nick, overseaSetProfileUrl, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } return SetUserNickBy(userId, nick, setProfileUrl, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } func SetUserNickBy(userId string, nick string, reqUrl string, getAdminSig func() (string, error), appId int) error { type ProfileItemType struct { Tag string Value string } type BodyStruct struct { From_Account string ProfileItem []ProfileItemType } sig, err := getAdminSig() if err != nil { return err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(reqUrl, "{appid}", strconv.Itoa(appId)), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := BodyStruct{ From_Account: userId, ProfileItem: []ProfileItemType{{Tag: TAG_PROFILE_IM_NICK, Value: nick}}, } jsonStr, err := json.Marshal(body) if err != nil { return err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return err } type setUserNickRsp struct { RespStruct ErrorDisplay string } response := setUserNickRsp{} if err := json.Unmarshal(result, &response); err != nil { return err } mylogrus.MyLog.Info("SetUserNick: ", response) if response.ErrorCode == 0 { return nil } else { return fmt.Errorf(response.ErrorDisplay) } } func SetUserAttr(logger *logrus.Entry, externalId string, key string, value string) error { if config.AppIsRelease() { return SetUserAttrBy(logger, externalId, key, value, overseaSetAttrUrl, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } else { return SetUserAttrBy(logger, externalId, key, value, setAttrUrl, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } } func SetUserAttrBy(logger *logrus.Entry, externalId string, key string, value string, reqUrl string, getAdminSig func() (string, error), appId int) error { logger.Infof("SetUserAttrBy, externalId %s, (%s, %s)", externalId, key, value) type userAttrElem struct { ToAccount string `json:"To_Account"` Attrs map[string]string `json:"Attrs"` } type BodyStruct struct { UserAttrs []userAttrElem `json:"UserAttrs"` } sig, err := getAdminSig() if err != nil { return err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(reqUrl, "{appid}", strconv.Itoa(appId)), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := BodyStruct{UserAttrs: []userAttrElem{{ToAccount: externalId, Attrs: map[string]string{key: value}}}} logger.Info("SetUserAttrBy, body: ", body) jsonStr, err := json.Marshal(body) if err != nil { return err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return err } response := RespStruct{} if err := json.Unmarshal(result, &response); err != nil { return err } logger.Info("SetUserAttrBy, rsp: ", response) if response.ErrorCode == 0 { return nil } else { return fmt.Errorf("%v", response.ErrorInfo) } } func C2cSendMsg(model *domain.Model, syncOtherMachine int8, from_Account string, to_Account string, msgType string, text string) error { if config.AppIsRelease() { return C2cSendMsgBy(model, syncOtherMachine, from_Account, to_Account, msgType, text, overseaC2cSendMsg, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } else { return C2cSendMsgBy(model, syncOtherMachine, from_Account, to_Account, msgType, text, c2cSendMsg, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } } //服务器 单聊发消息 //1:把消息同步到 From_Account 在线终端和漫游上; 2:消息不同步至 From_Account; func C2cSendMsgBy(model *domain.Model, syncOtherMachine int8, from_Account string, to_Account string, msgType string, text string, reqUrl string, getAdminSig func() (string, error), appId int) error { type MsgContentStruct struct { Text string } type MsgBodyStruct struct { MsgType string MsgContent MsgContentStruct } type BodyStruct struct { SyncOtherMachine int8 From_Account string To_Account string MsgRandom int64 MsgTimeStamp int64 ForbidCallbackControl []string MsgBody []MsgBodyStruct } sig, err := getAdminSig() if err != nil { return err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(reqUrl, "{appid}", strconv.Itoa(appId)), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) msgBodys := []MsgBodyStruct{} msgBodys = append(msgBodys, MsgBodyStruct{ MsgType: msgType, MsgContent: MsgContentStruct{ Text: text, }, }) body := BodyStruct{ SyncOtherMachine: syncOtherMachine, From_Account: from_Account, To_Account: to_Account, MsgRandom: time.Now().Unix(), MsgTimeStamp: time.Now().Unix(), ForbidCallbackControl: []string{"ForbidAfterSendMsgCallback"}, MsgBody: msgBodys, } jsonStr, err := json.Marshal(body) if err != nil { return err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return err } response := RespStruct{} if err := json.Unmarshal(result, &response); err != nil { return err } model.Log.Infof("C2cSendMsg send:%v, resp:%v ", bytes.NewBuffer(jsonStr), response) if response.ErrorCode == 0 { return nil } else { return fmt.Errorf(fmt.Sprintf("tencentyun C2cSendMsg rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)) } } //服务器 查询单聊消息 func C2cGetMsg(model *domain.Model, fromAccount, toAccount string, maxCnt int, minTime, maxTime int64) (int64, error) { type BodyStruct struct { From_Account string To_Account string MaxCnt int MinTime int64 MaxTime int64 } sig, err := getOverseaAdminSig() if err != nil { return 0, err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(overseaC2cGetMsg, "{appid}", strconv.Itoa(config.GetTxOverSeaAppId())), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) body := BodyStruct{ From_Account: fromAccount, To_Account: toAccount, MaxCnt: maxCnt, MinTime: minTime, MaxTime: maxTime, } jsonStr, err := json.Marshal(body) if err != nil { return 0, err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return 0, err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return 0, err } type msgContentItem struct { Text string } type msgBodyItem struct { MsgType string MsgContent msgContentItem } type msgListType struct { FromAccount string `json:"From_Account"` ToAccount string `json:"To_Account"` MsgSeq uint64 MsgRandom int64 MsgTimeStamp int64 MsgFlagBits uint MsgKey string MsgBody []msgBodyItem CloudCustomData string } type c2cGetMsgRsp struct { RespStruct Complete int MsgCnt uint LastMsgTime int64 LastMsgKey string MsgList []msgListType } response := c2cGetMsgRsp{} if err := json.Unmarshal(result, &response); err != nil { return 0, err } model.Log.Infof("C2cGetMsg send:%v, resp:%+v ", bytes.NewBuffer(jsonStr), response) if response.ErrorCode == 0 { return response.LastMsgTime, nil } else { return 0, fmt.Errorf(fmt.Sprintf("tencentyun C2cGetMsg rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)) } } func C2cSendCustomMsg(model *domain.Model, syncOtherMachine int8, fromAccount *string, toAccount string, data string, pushContent string) error { if config.AppIsRelease() { return C2cSendCustomMsgBy(model, syncOtherMachine, fromAccount, toAccount, data, pushContent, overseaC2cSendMsg, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } else { return C2cSendCustomMsgBy(model, syncOtherMachine, fromAccount, toAccount, data, pushContent, c2cSendMsg, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } } //服务器 单聊发消息, 自定义消息 //1:把消息同步到 From_Account 在线终端和漫游上; 2:消息不同步至 From_Account; func C2cSendCustomMsgBy(model *domain.Model, syncOtherMachine int8, fromAccount *string, toAccount string, data string, pushContent string, reqUrl string, getAdminSig func() (string, error), appId int) error { type MsgContentStruct struct { Data string Desc string Ext string Sound string } type MsgBodyStruct struct { MsgType string MsgContent MsgContentStruct } type MsgOfflinePushInfo struct { PushFlag int Title string Desc string Ext string } type BodyStruct struct { SyncOtherMachine int8 From_Account *string To_Account string MsgRandom int64 ForbidCallbackControl []string MsgBody []MsgBodyStruct OfflinePushInfo MsgOfflinePushInfo } sig, err := getAdminSig() if err != nil { return err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(reqUrl, "{appid}", strconv.Itoa(appId)), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) msgBodys := []MsgBodyStruct{} msgBodys = append(msgBodys, MsgBodyStruct{ MsgType: "TIMCustomElem", MsgContent: MsgContentStruct{ Data: data, }, }) body := BodyStruct{ SyncOtherMachine: syncOtherMachine, To_Account: toAccount, From_Account: fromAccount, MsgRandom: time.Now().Unix(), ForbidCallbackControl: []string{"ForbidAfterSendMsgCallback"}, MsgBody: msgBodys, OfflinePushInfo: MsgOfflinePushInfo{ PushFlag: 0, Desc: pushContent, Ext: pushContent, }, } jsonStr, err := json.Marshal(body) if err != nil { return err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return err } type ErrorListItem struct { To_Account string ErrorCode int } type batchSendMsgRsp struct { RespStruct ErrorList []ErrorListItem } response := batchSendMsgRsp{} if err := json.Unmarshal(result, &response); err != nil { return err } model.Log.Infof("C2cSendCustomMsg send:%v, resp:%+v ", bytes.NewBuffer(jsonStr), response) if response.ErrorCode == 0 { // 存在部分错误的可能 if response.ActionStatus != "OK" { model.Log.Infof("C2cSendCustomMsg: user %s, err: %v", fromAccount, response.ErrorList) } return nil } else { return fmt.Errorf(fmt.Sprintf("tencentyun C2cSendCustomMsg rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)) } } // (分批)批量发自定义单聊消息 func BatchSendCustomMsg(model *domain.Model, syncOtherMachine int8, fromAccount string, toAccount []string, data string, pushContent string) error { for start := 0; start < len(toAccount); start += IM_GET_BATCHSIZE { end := start + IM_GET_BATCHSIZE if end > len(toAccount) { end = len(toAccount) } toAccounts := toAccount[start:end] model.Log.Infof("BatchSendCustomMsg, from %s, offset = %d, size = %d", fromAccount, start, len(toAccounts)) if err := c2cBatchSendCustomMsg(model, syncOtherMachine, fromAccount, toAccounts, data, pushContent); err != nil { return err } } return nil } func c2cBatchSendCustomMsg(model *domain.Model, syncOtherMachine int8, fromAccount string, toAccount []string, data string, pushContent string) error { if config.AppIsRelease() { return c2cBatchSendCustomMsgBy(model, syncOtherMachine, fromAccount, toAccount, data, pushContent, overseaC2cBatchSendMsg, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } else { return c2cBatchSendCustomMsgBy(model, syncOtherMachine, fromAccount, toAccount, data, pushContent, c2cBatchSendMsg, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } } //1:把消息同步到 From_Account 在线终端和漫游上; 2:消息不同步至 From_Account; func c2cBatchSendCustomMsgBy(model *domain.Model, syncOtherMachine int8, fromAccount string, toAccount []string, data string, pushContent string, reqUrl string, getAdminSig func() (string, error), appId int) error { type MsgContentStruct struct { Data string Desc string Ext string Sound string } type MsgBodyStruct struct { MsgType string MsgContent MsgContentStruct } type MsgOfflinePushInfo struct { PushFlag int Title string Desc string Ext string } type BodyStruct struct { SyncOtherMachine int8 From_Account string To_Account []string MsgRandom int64 MsgBody []MsgBodyStruct OfflinePushInfo MsgOfflinePushInfo } sig, err := getAdminSig() if err != nil { return err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(reqUrl, "{appid}", strconv.Itoa(appId)), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) msgBodys := []MsgBodyStruct{} msgBodys = append(msgBodys, MsgBodyStruct{ MsgType: "TIMCustomElem", MsgContent: MsgContentStruct{ Data: data, }, }) body := BodyStruct{ SyncOtherMachine: syncOtherMachine, From_Account: fromAccount, To_Account: toAccount, MsgRandom: time.Now().Unix(), MsgBody: msgBodys, OfflinePushInfo: MsgOfflinePushInfo{ PushFlag: 0, Desc: pushContent, Ext: pushContent, }, } jsonStr, err := json.Marshal(body) if err != nil { return err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return err } type ErrorListItem struct { To_Account string ErrorCode int } type batchSendMsgRsp struct { RespStruct ErrorList []ErrorListItem } response := batchSendMsgRsp{} if err := json.Unmarshal(result, &response); err != nil { return err } model.Log.Infof("C2cBatchSendMsg send:%v, resp:%v ", bytes.NewBuffer(jsonStr), response) if response.ErrorCode == 0 { // 存在部分错误的可能 if response.ActionStatus != "OK" { model.Log.Infof("c2cBatchSendCustomMsg: user %d, err: %v", fromAccount, response.ErrorList) } return nil } else { return fmt.Errorf(fmt.Sprintf("tencentyun C2cBatchSendMsg rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)) } } func BatchSendTextMsg(model *domain.Model, syncOtherMachine int8, fromAccount string, toAccount []string, text string, pushContent string) (int, error) { failedCount := 0 for start := 0; start < len(toAccount); start += IM_GET_BATCHSIZE { end := start + IM_GET_BATCHSIZE if end > len(toAccount) { end = len(toAccount) } fc, err := batchSendTextMsg(model, syncOtherMachine, fromAccount, toAccount[start:end], text, pushContent) if err != nil { return failedCount, err } failedCount += fc } return failedCount, nil } func batchSendTextMsg(model *domain.Model, syncOtherMachine int8, fromAccount string, toAccount []string, text string, pushContent string) (int, error) { if config.AppIsRelease() { return batchSendTextMsgBy(model, syncOtherMachine, fromAccount, toAccount, text, pushContent, overseaC2cBatchSendMsg, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } else { return batchSendTextMsgBy(model, syncOtherMachine, fromAccount, toAccount, text, pushContent, c2cBatchSendMsg, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } } // 批量发送单聊文字消息 func batchSendTextMsgBy(model *domain.Model, syncOtherMachine int8, fromAccount string, toAccount []string, text string, pushContent string, reqUrl string, getAdminSig func() (string, error), appId int) (int, error) { type MsgContentStruct struct { Text string } type MsgBodyStruct struct { MsgType string MsgContent MsgContentStruct } type MsgOfflinePushInfo struct { PushFlag int Title string Desc string Ext string } type BodyStruct struct { SyncOtherMachine int8 From_Account string To_Account []string MsgRandom int64 MsgBody []MsgBodyStruct OfflinePushInfo MsgOfflinePushInfo } sig, err := getAdminSig() if err != nil { return 0, err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(reqUrl, "{appid}", strconv.Itoa(appId)), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) msgBodys := []MsgBodyStruct{} msgBodys = append(msgBodys, MsgBodyStruct{ MsgType: "TIMTextElem", MsgContent: MsgContentStruct{ Text: text, }, }) body := BodyStruct{ SyncOtherMachine: syncOtherMachine, From_Account: fromAccount, To_Account: toAccount, MsgRandom: time.Now().Unix(), MsgBody: msgBodys, OfflinePushInfo: MsgOfflinePushInfo{ PushFlag: 0, Desc: pushContent, Ext: pushContent, }, } jsonStr, err := json.Marshal(body) if err != nil { return 0, err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return 0, err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return 0, err } type ErrorListItem struct { To_Account string ErrorCode int } type batchSendMsgRsp struct { RespStruct ErrorList []ErrorListItem } response := batchSendMsgRsp{} if err := json.Unmarshal(result, &response); err != nil { return 0, err } model.Log.Infof("batchSendTextMsgBy send:%v, resp:%v ", bytes.NewBuffer(jsonStr), response) if response.ErrorCode == 0 { // 存在部分错误的可能 if response.ActionStatus != "OK" { model.Log.Infof("batchSendTextMsgBy: user %s, err: %v", fromAccount, response.ErrorList) } return len(response.ErrorList), nil } else { return 0, fmt.Errorf(fmt.Sprintf("tencentyun batchSendTextMsgBy ErrorInfo:%v", response.ErrorInfo)) } } // 海外版和国内版之间交叉发消息 func C2cCrossSendMsg(model *domain.Model, appId int, syncOtherMachine int8, from_Account string, to_Account string, msgType string, msgContent json.RawMessage) error { if appId == config.GetTencentyunAppId() { return C2cCrossSendMsgBy(model, syncOtherMachine, from_Account, to_Account, msgType, msgContent, overseaC2cSendMsg, func() (string, error) { return getOverseaAdminSig() }, config.GetTxOverSeaAppId()) } else if appId == config.GetTxOverSeaAppId() { return C2cCrossSendMsgBy(model, syncOtherMachine, from_Account, to_Account, msgType, msgContent, c2cSendMsg, func() (string, error) { return getAdminSig() }, config.GetTencentyunAppId()) } return errors.New("illegal appid") } func C2cCrossSendMsgBy(model *domain.Model, syncOtherMachine int8, fromAccount, toAccount string, msgType string, msgContent json.RawMessage, reqUrl string, getAdminSig func() (string, error), appId int) error { type MsgBodyStruct struct { MsgType string MsgContent json.RawMessage } type MsgOfflinePushInfo struct { PushFlag int Title string Desc string Ext string } type BodyStruct struct { SyncOtherMachine int8 From_Account string To_Account string MsgRandom int64 ForbidCallbackControl []string MsgBody []MsgBodyStruct } sig, err := getAdminSig() if err != nil { return err } url := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(reqUrl, "{appid}", strconv.Itoa(appId)), "{userSig}", sig), "{random}", strconv.FormatUint(uint64(rand.Uint32()), 10)) msgBodys := []MsgBodyStruct{} msgBodys = append(msgBodys, MsgBodyStruct{ MsgType: msgType, MsgContent: msgContent, }) body := BodyStruct{ SyncOtherMachine: syncOtherMachine, To_Account: toAccount, From_Account: fromAccount, MsgRandom: time.Now().Unix(), ForbidCallbackControl: []string{"ForbidAfterSendMsgCallback"}, MsgBody: msgBodys, } model.Log.Infof("C2cCrossSendMsgBy url: %+v, body:%+v", url, body) jsonStr, err := json.Marshal(body) if err != nil { return err } rsp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr)) if err != nil { return err } defer rsp.Body.Close() result, err := ioutil.ReadAll(rsp.Body) if err != nil { return err } type ErrorListItem struct { To_Account string ErrorCode int } type batchSendMsgRsp struct { RespStruct ErrorList []ErrorListItem } response := batchSendMsgRsp{} if err := json.Unmarshal(result, &response); err != nil { return err } model.Log.Infof("C2cCrossSendMsgBy send:%+v, resp:%+v ", bytes.NewBuffer(jsonStr), response) if response.ErrorCode == 0 { // 存在部分错误的可能 if response.ActionStatus != "OK" { model.Log.Infof("C2cCrossSendMsgBy: user %s, err: %v", fromAccount, response.ErrorList) } return nil } else { return fmt.Errorf(fmt.Sprintf("tencentyun C2cCrossSendMsgBy rsp ErrorCode:%v, ErrorInfo:%v", response.ErrorCode, response.ErrorInfo)) } } const ( ImageFormatJpg = 1 ImageFormatGif = 2 ImageFormatPng = 3 ImageFormatBmp = 4 ImageFormatOther = 255 ) func GetImageFormat(format string) int { if format == "jpg" { return ImageFormatJpg } else if format == "gif" { return ImageFormatGif } else if format == "png" { return ImageFormatPng } else if format == "bmp" { return ImageFormatBmp } else { return ImageFormatOther } } const ( ImageTypeOriginal = 1 ImageTypeLarge = 2 ImageTypeSmall = 3 ) type ImageInfo struct { Type int Size uint Width uint Height uint URL string Download_Flag int }